scepclient: Some code cleanup.
[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 <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 "../pluto/constants.h"
29 #include "../pluto/defs.h"
30 #include "../pluto/fetch.h"
31 #include "../pluto/log.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 DBG(DBG_CONTROL,
166 DBG_log("messageType: %s", msgType_names[attrs->msgType])
167 )
168 break;
169 }
170 case OID_PKI_STATUS:
171 {
172 pkiStatus_t s;
173
174 for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
175 {
176 if (strncmp(pkiStatus_values[s], object.ptr, object.len) == 0)
177 {
178 attrs->pkiStatus = s;
179 }
180 }
181 DBG(DBG_CONTROL,
182 DBG_log("pkiStatus: %s", pkiStatus_names[attrs->pkiStatus])
183 )
184 break;
185 }
186 case OID_PKI_FAIL_INFO:
187 {
188 if (object.len == 1 &&
189 *object.ptr >= '0' && *object.ptr <= '4')
190 {
191 attrs->failInfo = (failInfo_t)(*object.ptr - '0');
192 }
193 if (attrs->failInfo != SCEP_unknown_REASON)
194 {
195 plog("failInfo: %s", failInfo_reasons[attrs->failInfo]);
196 }
197 break;
198 }
199 case OID_PKI_SENDER_NONCE:
200 attrs->senderNonce = object;
201 break;
202 case OID_PKI_RECIPIENT_NONCE:
203 attrs->recipientNonce = object;
204 break;
205 case OID_PKI_TRANS_ID:
206 attrs->transID = object;
207 break;
208 }
209 return TRUE;
210 }
211
212 /**
213 * Parse X.501 attributes
214 */
215 bool parse_attributes(chunk_t blob, scep_attributes_t *attrs)
216 {
217 asn1_parser_t *parser;
218 chunk_t object;
219 int oid = OID_UNKNOWN;
220 int objectID;
221 bool success = FALSE;
222
223 parser = asn1_parser_create(attributesObjects, blob);
224 DBG(DBG_CONTROL | DBG_PARSING,
225 DBG_log("parsing attributes")
226 )
227
228 while (parser->iterate(parser, &objectID, &object))
229 {
230 switch (objectID)
231 {
232 case ATTRIBUTE_OBJ_TYPE:
233 oid = asn1_known_oid(object);
234 break;
235 case ATTRIBUTE_OBJ_VALUE:
236 {
237 if (!extract_attribute(oid, object, parser->get_level(parser), attrs))
238 {
239 goto end;
240 }
241 break;
242 }
243 }
244 }
245 success = parser->success(parser);
246
247 end:
248 parser->destroy(parser);
249 return success;
250 }
251
252 /**
253 * Generates a unique fingerprint of the pkcs10 request
254 * by computing an MD5 hash over it
255 */
256 chunk_t scep_generate_pkcs10_fingerprint(chunk_t pkcs10)
257 {
258 chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
259 hasher_t *hasher;
260
261 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
262 hasher->get_hash(hasher, pkcs10, digest.ptr);
263 hasher->destroy(hasher);
264
265 return chunk_to_hex(digest, NULL, FALSE);
266 }
267
268 /**
269 * Generate a transaction id as the MD5 hash of an public key
270 * the transaction id is also used as a unique serial number
271 */
272 void scep_generate_transaction_id(public_key_t *key, chunk_t *transID,
273 chunk_t *serialNumber)
274 {
275 chunk_t digest = chunk_alloca(HASH_SIZE_MD5);
276 chunk_t keyEncoding = chunk_empty, keyInfo;
277 hasher_t *hasher;
278 bool msb_set;
279 u_char *pos;
280
281 key->get_encoding(key, PUBKEY_ASN1_DER, &keyEncoding);
282
283 keyInfo = asn1_wrap(ASN1_SEQUENCE, "mm",
284 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION),
285 asn1_bitstring("m", keyEncoding));
286
287 hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
288 hasher->get_hash(hasher, keyInfo, digest.ptr);
289 hasher->destroy(hasher);
290 free(keyInfo.ptr);
291
292 /* is the most significant bit of the digest set? */
293 msb_set = (*digest.ptr & 0x80) == 0x80;
294
295 /* allocate space for the serialNumber */
296 serialNumber->len = msb_set + digest.len;
297 serialNumber->ptr = malloc(serialNumber->len);
298
299 /* the serial number as the two's complement of the digest */
300 pos = serialNumber->ptr;
301 if (msb_set)
302 {
303 *pos++ = 0x00;
304 }
305 memcpy(pos, digest.ptr, digest.len);
306
307 /* the transaction id is the serial number in hex format */
308 transID->len = 2*digest.len;
309 transID->ptr = malloc(transID->len + 1);
310 datatot(digest.ptr, digest.len, 16, transID->ptr, transID->len + 1);
311 }
312
313 /**
314 * Builds a transId attribute
315 */
316 chunk_t scep_transId_attribute(chunk_t transID)
317 {
318 return asn1_wrap(ASN1_SEQUENCE, "cm",
319 asn1_build_known_oid(OID_PKI_TRANS_ID),
320 asn1_wrap(ASN1_SET, "m",
321 asn1_simple_object(ASN1_PRINTABLESTRING, transID)));
322 }
323
324 /**
325 * Builds a messageType attribute
326 */
327 chunk_t scep_messageType_attribute(scep_msg_t m)
328 {
329 chunk_t msgType = {
330 (u_char*)msgType_values[m],
331 strlen(msgType_values[m])
332 };
333
334 return asn1_wrap(ASN1_SEQUENCE, "mm",
335 asn1_build_known_oid(OID_PKI_MESSAGE_TYPE),
336 asn1_wrap(ASN1_SET, "m",
337 asn1_simple_object(ASN1_PRINTABLESTRING, msgType)));
338 }
339
340 /**
341 * Builds a senderNonce attribute
342 */
343 chunk_t scep_senderNonce_attribute(void)
344 {
345 const size_t nonce_len = 16;
346 u_char nonce_buf[nonce_len];
347 chunk_t senderNonce = { nonce_buf, nonce_len };
348 rng_t *rng;
349
350 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
351 rng->get_bytes(rng, nonce_len, nonce_buf);
352 rng->destroy(rng);
353
354 return asn1_wrap(ASN1_SEQUENCE, "cm",
355 asn1_build_known_oid(OID_PKI_SENDER_NONCE),
356 asn1_wrap(ASN1_SET, "m",
357 asn1_simple_object(ASN1_OCTET_STRING, senderNonce)));
358 }
359
360 /**
361 * Builds a pkcs7 enveloped and signed scep request
362 */
363 chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg,
364 certificate_t *enc_cert, int enc_alg,
365 certificate_t *signer_cert, int digest_alg,
366 private_key_t *private_key)
367 {
368 chunk_t envelopedData, attributes, request;
369
370 envelopedData = pkcs7_build_envelopedData(data, enc_cert, enc_alg);
371
372 attributes = asn1_wrap(ASN1_SET, "mmmmm",
373 pkcs7_contentType_attribute(),
374 pkcs7_messageDigest_attribute(envelopedData, digest_alg),
375 scep_transId_attribute(transID),
376 scep_messageType_attribute(msg),
377 scep_senderNonce_attribute());
378
379 request = pkcs7_build_signedData(envelopedData, attributes,
380 signer_cert, digest_alg, private_key);
381 free(envelopedData.ptr);
382 free(attributes.ptr);
383 return request;
384 }
385
386 /**
387 * Converts a binary request to base64 with 64 characters per line
388 * newline and '+' characters are escaped by %0A and %2B, respectively
389 */
390 static char* escape_http_request(chunk_t req)
391 {
392 char *escaped_req = NULL;
393 char *p1, *p2;
394 int lines = 0;
395 int plus = 0;
396 int n = 0;
397
398 /* compute and allocate the size of the base64-encoded request */
399 int len = 1 + 4*((req.len + 2)/3);
400 char *encoded_req = malloc(len);
401
402 /* do the base64 conversion */
403 len = datatot(req.ptr, req.len, 64, encoded_req, len);
404
405 /* compute newline characters to be inserted every 64 characters */
406 lines = (len - 2) / 64;
407
408 /* count number of + characters to be escaped */
409 p1 = encoded_req;
410 while (*p1 != '\0')
411 {
412 if (*p1++ == '+')
413 {
414 plus++;
415 }
416 }
417
418 escaped_req = malloc(len + 3*(lines + plus));
419
420 /* escape special characters in the request */
421 p1 = encoded_req;
422 p2 = escaped_req;
423 while (*p1 != '\0')
424 {
425 if (n == 64)
426 {
427 memcpy(p2, "%0A", 3);
428 p2 += 3;
429 n = 0;
430 }
431 if (*p1 == '+')
432 {
433 memcpy(p2, "%2B", 3);
434 p2 += 3;
435 }
436 else
437 {
438 *p2++ = *p1;
439 }
440 p1++;
441 n++;
442 }
443 *p2 = '\0';
444 free(encoded_req);
445 return escaped_req;
446 }
447
448 /**
449 * Send a SCEP request via HTTP and wait for a response
450 */
451 bool scep_http_request(const char *url, chunk_t pkcs7, scep_op_t op,
452 bool http_get_request, chunk_t *response)
453 {
454 int len;
455 status_t status;
456 char *complete_url = NULL;
457
458 /* initialize response */
459 *response = chunk_empty;
460
461 DBG(DBG_CONTROL,
462 DBG_log("sending scep request to '%s'", url)
463 )
464
465 if (op == SCEP_PKI_OPERATION)
466 {
467 const char operation[] = "PKIOperation";
468
469 if (http_get_request)
470 {
471 char *escaped_req = escape_http_request(pkcs7);
472
473 /* form complete url */
474 len = strlen(url) + 20 + strlen(operation) + strlen(escaped_req) + 1;
475 complete_url = malloc(len);
476 snprintf(complete_url, len, "%s?operation=%s&message=%s"
477 , url, operation, escaped_req);
478 free(escaped_req);
479
480 status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
481 FETCH_HTTP_VERSION_1_0,
482 FETCH_REQUEST_HEADER, "Pragma:",
483 FETCH_REQUEST_HEADER, "Host:",
484 FETCH_REQUEST_HEADER, "Accept:",
485 FETCH_END);
486 }
487 else /* HTTP_POST */
488 {
489 /* form complete url */
490 len = strlen(url) + 11 + strlen(operation) + 1;
491 complete_url = malloc(len);
492 snprintf(complete_url, len, "%s?operation=%s", url, operation);
493
494 status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
495 FETCH_REQUEST_DATA, pkcs7,
496 FETCH_REQUEST_TYPE, "",
497 FETCH_REQUEST_HEADER, "Expect:",
498 FETCH_END);
499 }
500 }
501 else /* SCEP_GET_CA_CERT */
502 {
503 const char operation[] = "GetCACert";
504
505 /* form complete url */
506 len = strlen(url) + 32 + strlen(operation) + 1;
507 complete_url = malloc(len);
508 snprintf(complete_url, len, "%s?operation=%s&message=CAIdentifier",
509 url, operation);
510
511 status = lib->fetcher->fetch(lib->fetcher, complete_url, response,
512 FETCH_END);
513 }
514
515 free(complete_url);
516 return (status == SUCCESS);
517 }
518
519 err_t scep_parse_response(chunk_t response, chunk_t transID, contentInfo_t *data,
520 scep_attributes_t *attrs, certificate_t *signer_cert)
521 {
522 chunk_t attributes;
523
524 if (!pkcs7_parse_signedData(response, data, NULL, &attributes, signer_cert))
525 {
526 return "error parsing the scep response";
527 }
528 if (!parse_attributes(attributes, attrs))
529 {
530 return "error parsing the scep response attributes";
531 }
532 if (!chunk_equals(transID, attrs->transID))
533 {
534 return "transaction ID of scep response does not match";
535 }
536 return NULL;
537 }