714d3e9297998a161dd18c58ed83c46fb85c3e4e
[strongswan.git] / src / scepclient / scep.c
1 /*
2 * Copyright (C) 2005 Jan Hutter, Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <string.h>
17 #include <stdlib.h>
18
19 #include <freeswan.h>
20
21 #include <library.h>
22 #include <debug.h>
23 #include <asn1/asn1.h>
24 #include <asn1/asn1_parser.h>
25 #include <asn1/oid.h>
26 #include <crypto/rngs/rng.h>
27 #include <crypto/hashers/hasher.h>
28
29 #include "../pluto/constants.h"
30 #include "../pluto/defs.h"
31 #include "../pluto/fetch.h"
32
33 #include "scep.h"
34
35 static const char *pkiStatus_values[] = { "0", "2", "3" };
36
37 static const char *pkiStatus_names[] = {
38 "SUCCESS",
39 "FAILURE",
40 "PENDING",
41 "UNKNOWN"
42 };
43
44 static const char *msgType_values[] = { "3", "19", "20", "21", "22" };
45
46 static const char *msgType_names[] = {
47 "CertRep",
48 "PKCSReq",
49 "GetCertInitial",
50 "GetCert",
51 "GetCRL",
52 "Unknown"
53 };
54
55 static const char *failInfo_reasons[] = {
56 "badAlg - unrecognized or unsupported algorithm identifier",
57 "badMessageCheck - integrity check failed",
58 "badRequest - transaction not permitted or supported",
59 "badTime - Message time field was not sufficiently close to the system time",
60 "badCertId - No certificate could be identified matching the provided criteria"
61 };
62
63 const scep_attributes_t empty_scep_attributes = {
64 SCEP_Unknown_MSG , /* msgType */
65 SCEP_UNKNOWN , /* pkiStatus */
66 SCEP_unknown_REASON, /* failInfo */
67 { NULL, 0 } , /* transID */
68 { NULL, 0 } , /* senderNonce */
69 { NULL, 0 } , /* recipientNonce */
70 };
71
72 /* ASN.1 definition of the X.501 atttribute type */
73
74 static const asn1Object_t attributesObjects[] = {
75 { 0, "attributes", ASN1_SET, ASN1_LOOP }, /* 0 */
76 { 1, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
77 { 2, "type", ASN1_OID, ASN1_BODY }, /* 2 */
78 { 2, "values", ASN1_SET, ASN1_LOOP }, /* 3 */
79 { 3, "value", ASN1_EOC, ASN1_RAW }, /* 4 */
80 { 2, "end loop", ASN1_EOC, ASN1_END }, /* 5 */
81 { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */
82 { 0, "exit", ASN1_EOC, ASN1_EXIT }
83 };
84 #define ATTRIBUTE_OBJ_TYPE 2
85 #define ATTRIBUTE_OBJ_VALUE 4
86
87 /**
88 * Extract and store an attribute
89 */
90 static bool extract_attribute(int oid, chunk_t object, u_int level,
91 scep_attributes_t *attrs)
92 {
93 asn1_t type = ASN1_EOC;
94 const char *name = "none";
95
96 switch (oid)
97 {
98 case OID_PKCS9_CONTENT_TYPE:
99 type = ASN1_OID;
100 name = "contentType";
101 break;
102 case OID_PKCS9_SIGNING_TIME:
103 type = ASN1_UTCTIME;
104 name = "signingTime";
105 break;
106 case OID_PKCS9_MESSAGE_DIGEST:
107 type = ASN1_OCTET_STRING;
108 name = "messageDigest";
109 break;
110 case OID_PKI_MESSAGE_TYPE:
111 type = ASN1_PRINTABLESTRING;
112 name = "messageType";
113 break;
114 case OID_PKI_STATUS:
115 type = ASN1_PRINTABLESTRING;
116 name = "pkiStatus";
117 break;
118 case OID_PKI_FAIL_INFO:
119 type = ASN1_PRINTABLESTRING;
120 name = "failInfo";
121 break;
122 case OID_PKI_SENDER_NONCE:
123 type = ASN1_OCTET_STRING;
124 name = "senderNonce";
125 break;
126 case OID_PKI_RECIPIENT_NONCE:
127 type = ASN1_OCTET_STRING;
128 name = "recipientNonce";
129 break;
130 case OID_PKI_TRANS_ID:
131 type = ASN1_PRINTABLESTRING;
132 name = "transID";
133 break;
134 default:
135 break;
136 }
137
138 if (type == ASN1_EOC)
139 {
140 return TRUE;
141 }
142
143 if (!asn1_parse_simple_object(&object, type, level+1, name))
144 {
145 return FALSE;
146 }
147
148 switch (oid)
149 {
150 case OID_PKCS9_CONTENT_TYPE:
151 break;
152 case OID_PKCS9_SIGNING_TIME:
153 break;
154 case OID_PKCS9_MESSAGE_DIGEST:
155 break;
156 case OID_PKI_MESSAGE_TYPE:
157 {
158 scep_msg_t m;
159
160 for (m = SCEP_CertRep_MSG; m < SCEP_Unknown_MSG; m++)
161 {
162 if (strncmp(msgType_values[m], object.ptr, object.len) == 0)
163 attrs->msgType = m;
164 }
165 DBG2(DBG_APP, "messageType: %s", msgType_names[attrs->msgType]);
166 break;
167 }
168 case OID_PKI_STATUS:
169 {
170 pkiStatus_t s;
171
172 for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
173 {
174 if (strncmp(pkiStatus_values[s], object.ptr, object.len) == 0)
175 {
176 attrs->pkiStatus = s;
177 }
178 }
179 DBG2(DBG_APP, "pkiStatus: %s", pkiStatus_names[attrs->pkiStatus]);
180 break;
181 }
182 case OID_PKI_FAIL_INFO:
183 {
184 if (object.len == 1 &&
185 *object.ptr >= '0' && *object.ptr <= '4')
186 {
187 attrs->failInfo = (failInfo_t)(*object.ptr - '0');
188 }
189 if (attrs->failInfo != SCEP_unknown_REASON)
190 {
191 DBG1(DBG_APP, "failInfo: %s", failInfo_reasons[attrs->failInfo]);
192 }
193 break;
194 }
195 case OID_PKI_SENDER_NONCE:
196 attrs->senderNonce = object;
197 break;
198 case OID_PKI_RECIPIENT_NONCE:
199 attrs->recipientNonce = object;
200 break;
201 case OID_PKI_TRANS_ID:
202 attrs->transID = object;
203 break;
204 }
205 return TRUE;
206 }
207
208 /**
209 * Parse X.501 attributes
210 */
211 bool parse_attributes(chunk_t blob, scep_attributes_t *attrs)
212 {
213 asn1_parser_t *parser;
214 chunk_t object;
215 int oid = OID_UNKNOWN;
216 int objectID;
217 bool success = FALSE;
218
219 parser = asn1_parser_create(attributesObjects, blob);
220 DBG3(DBG_APP, "parsing attributes");
221
222 while (parser->iterate(parser, &objectID, &object))
223 {
224 switch (objectID)
225 {
226 case ATTRIBUTE_OBJ_TYPE:
227 oid = asn1_known_oid(object);
228 break;
229 case ATTRIBUTE_OBJ_VALUE:
230 {
231 if (!extract_attribute(oid, object, parser->get_level(parser), attrs))
232 {
233 goto end;
234 }
235 break;
236 }
237 }
238 }
239 success = parser->success(parser);
240
241 end:
242 parser->destroy(parser);
243 return success;
244 }
245
246 /**
247 * Generates a unique fingerprint of the pkcs10 request
248 * by computing an MD5 hash over it
249 */
250 chunk_t scep_generate_pkcs10_fingerprint(chunk_t pkcs10)
251 {
252 chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
253 hasher_t *hasher;
254
255 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
256 hasher->get_hash(hasher, pkcs10, digest.ptr);
257 hasher->destroy(hasher);
258
259 return chunk_to_hex(digest, NULL, FALSE);
260 }
261
262 /**
263 * Generate a transaction id as the MD5 hash of an public key
264 * the transaction id is also used as a unique serial number
265 */
266 void scep_generate_transaction_id(public_key_t *key, chunk_t *transID,
267 chunk_t *serialNumber)
268 {
269 chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
270 chunk_t keyEncoding = chunk_empty, keyInfo;
271 hasher_t *hasher;
272 bool msb_set;
273 u_char *pos;
274
275 key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding);
276
277 keyInfo = asn1_wrap(ASN1_SEQUENCE, "mm",
278 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
279 asn1_bitstring("m", keyEncoding));
280
281 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
282 hasher->get_hash(hasher, keyInfo, digest.ptr);
283 hasher->destroy(hasher);
284 free(keyInfo.ptr);
285
286 /* is the most significant bit of the digest set? */
287 msb_set = (*digest.ptr & 0x80) == 0x80;
288
289 /* allocate space for the serialNumber */
290 serialNumber->len = msb_set + digest.len;
291 serialNumber->ptr = malloc(serialNumber->len);
292
293 /* the serial number as the two's complement of the digest */
294 pos = serialNumber->ptr;
295 if (msb_set)
296 {
297 *pos++ = 0x00;
298 }
299 memcpy(pos, digest.ptr, digest.len);
300
301 /* the transaction id is the serial number in hex format */
302 transID->len = 2*digest.len;
303 transID->ptr = malloc(transID->len + 1);
304 datatot(digest.ptr, digest.len, 16, transID->ptr, transID->len + 1);
305 }
306
307 /**
308 * Builds a transId attribute
309 */
310 chunk_t scep_transId_attribute(chunk_t transID)
311 {
312 return asn1_wrap(ASN1_SEQUENCE, "cm",
313 asn1_build_known_oid(OID_PKI_TRANS_ID),
314 asn1_wrap(ASN1_SET, "m",
315 asn1_simple_object(ASN1_PRINTABLESTRING, transID)));
316 }
317
318 /**
319 * Builds a messageType attribute
320 */
321 chunk_t scep_messageType_attribute(scep_msg_t m)
322 {
323 chunk_t msgType = {
324 (u_char*)msgType_values[m],
325 strlen(msgType_values[m])
326 };
327
328 return asn1_wrap(ASN1_SEQUENCE, "mm",
329 asn1_build_known_oid(OID_PKI_MESSAGE_TYPE),
330 asn1_wrap(ASN1_SET, "m",
331 asn1_simple_object(ASN1_PRINTABLESTRING, msgType)));
332 }
333
334 /**
335 * Builds a senderNonce attribute
336 */
337 chunk_t scep_senderNonce_attribute(void)
338 {
339 const size_t nonce_len = 16;
340 u_char nonce_buf[nonce_len];
341 chunk_t senderNonce = { nonce_buf, nonce_len };
342 rng_t *rng;
343
344 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
345 rng->get_bytes(rng, nonce_len, nonce_buf);
346 rng->destroy(rng);
347
348 return asn1_wrap(ASN1_SEQUENCE, "cm",
349 asn1_build_known_oid(OID_PKI_SENDER_NONCE),
350 asn1_wrap(ASN1_SET, "m",
351 asn1_simple_object(ASN1_OCTET_STRING, senderNonce)));
352 }
353
354 /**
355 * Builds a pkcs7 enveloped and signed scep request
356 */
357 chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
358 certificate_t *enc_cert, int enc_alg,
359 certificate_t *signer_cert, int digest_alg,
360 private_key_t *private_key)
361 {
362 chunk_t envelopedData, attributes, request;
363
364 envelopedData = pkcs7_build_envelopedData(data, enc_cert, enc_alg);
365
366 attributes = asn1_wrap(ASN1_SET, "mmmmm",
367 pkcs7_contentType_attribute(),
368 pkcs7_messageDigest_attribute(envelopedData, digest_alg),
369 scep_transId_attribute(transID),
370 scep_messageType_attribute(msg),
371 scep_senderNonce_attribute());
372
373 request = pkcs7_build_signedData(envelopedData, attributes,
374 signer_cert, digest_alg, private_key);
375 free(envelopedData.ptr);
376 free(attributes.ptr);
377 return request;
378 }
379
380 /**
381 * Converts a binary request to base64 with 64 characters per line
382 * newline and '+' characters are escaped by %0A and %2B, respectively
383 */
384 static char* escape_http_request(chunk_t req)
385 {
386 char *escaped_req = NULL;
387 char *p1, *p2;
388 int lines = 0;
389 int plus = 0;
390 int n = 0;
391
392 /* compute and allocate the size of the base64-encoded request */
393 int len = 1 + 4*((req.len + 2)/3);
394 char *encoded_req = malloc(len);
395
396 /* do the base64 conversion */
397 len = datatot(req.ptr, req.len, 64, encoded_req, len);
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 }