e2535f7d3e8cc6fa8467b45d6b6849f3b49b3d34
[strongswan.git] / src / libstrongswan / crypto / crl.c
1 /**
2 * @file crl.c
3 *
4 * @brief Implementation of crl_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2006 Andreas Steffen
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdio.h>
27
28 #include <library.h>
29 #include <debug.h>
30 #include <asn1/oid.h>
31 #include <asn1/asn1.h>
32 #include <asn1/pem.h>
33 #include <utils/linked_list.h>
34 #include <utils/identification.h>
35
36 #include "certinfo.h"
37 #include "x509.h"
38 #include "crl.h"
39
40 #define CRL_WARNING_INTERVAL 7 /* days */
41
42 /* access structure for a revoked certificate */
43
44 typedef struct revokedCert_t revokedCert_t;
45
46 struct revokedCert_t {
47 chunk_t userCertificate;
48 time_t revocationDate;
49 crl_reason_t revocationReason;
50 };
51
52 typedef struct private_crl_t private_crl_t;
53
54 /**
55 * Private data of a crl_t object.
56 */
57 struct private_crl_t {
58 /**
59 * Public interface for this crl.
60 */
61 crl_t public;
62
63 /**
64 * Time when crl was installed
65 */
66 time_t installed;
67
68 /**
69 * List of crlDistributionPoints
70 */
71 linked_list_t *crlDistributionPoints;
72
73 /**
74 * X.509 crl in DER format
75 */
76 chunk_t certificateList;
77
78 /**
79 * X.509 crl body over which signature is computed
80 */
81 chunk_t tbsCertList;
82
83 /**
84 * Version of the X.509 crl
85 */
86 u_int version;
87
88 /**
89 * Signature algorithm
90 */
91 int sigAlg;
92
93 /**
94 * ID representing the crl issuer
95 */
96 identification_t *issuer;
97
98 /**
99 * Time when the crl was generated
100 */
101 time_t thisUpdate;
102
103 /**
104 * Time when an update crl will be available
105 */
106 time_t nextUpdate;
107
108 /**
109 * List of identification_t's representing subjectAltNames
110 */
111 linked_list_t *revokedCertificates;
112
113 /**
114 * Authority Key Identifier
115 */
116 chunk_t authKeyID;
117
118 /**
119 * Authority Key Serial Number
120 */
121 chunk_t authKeySerialNumber;
122
123 /**
124 * Signature algorithm (must be identical to sigAlg)
125 */
126 int algorithm;
127
128 /**
129 * Signature
130 */
131 chunk_t signature;
132 };
133
134 /**
135 * ASN.1 definition of an X.509 certificate revocation list
136 */
137 static const asn1Object_t crlObjects[] = {
138 { 0, "certificateList", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */
139 { 1, "tbsCertList", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */
140 { 2, "version", ASN1_INTEGER, ASN1_OPT |
141 ASN1_BODY }, /* 2 */
142 { 2, "end opt", ASN1_EOC, ASN1_END }, /* 3 */
143 { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 4 */
144 { 2, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */
145 { 2, "thisUpdate", ASN1_EOC, ASN1_RAW }, /* 6 */
146 { 2, "nextUpdate", ASN1_EOC, ASN1_RAW }, /* 7 */
147 { 2, "revokedCertificates", ASN1_SEQUENCE, ASN1_OPT |
148 ASN1_LOOP }, /* 8 */
149 { 3, "certList", ASN1_SEQUENCE, ASN1_NONE }, /* 9 */
150 { 4, "userCertificate", ASN1_INTEGER, ASN1_BODY }, /* 10 */
151 { 4, "revocationDate", ASN1_EOC, ASN1_RAW }, /* 11 */
152 { 4, "crlEntryExtensions", ASN1_SEQUENCE, ASN1_OPT |
153 ASN1_LOOP }, /* 12 */
154 { 5, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 13 */
155 { 6, "extnID", ASN1_OID, ASN1_BODY }, /* 14 */
156 { 6, "critical", ASN1_BOOLEAN, ASN1_DEF |
157 ASN1_BODY }, /* 15 */
158 { 6, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 16 */
159 { 4, "end opt or loop", ASN1_EOC, ASN1_END }, /* 17 */
160 { 2, "end opt or loop", ASN1_EOC, ASN1_END }, /* 18 */
161 { 2, "optional extensions", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 19 */
162 { 3, "crlExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 20 */
163 { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 21 */
164 { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 22 */
165 { 5, "critical", ASN1_BOOLEAN, ASN1_DEF |
166 ASN1_BODY }, /* 23 */
167 { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 24 */
168 { 3, "end loop", ASN1_EOC, ASN1_END }, /* 25 */
169 { 2, "end opt", ASN1_EOC, ASN1_END }, /* 26 */
170 { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 27 */
171 { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 28 */
172 };
173
174 #define CRL_OBJ_CERTIFICATE_LIST 0
175 #define CRL_OBJ_TBS_CERT_LIST 1
176 #define CRL_OBJ_VERSION 2
177 #define CRL_OBJ_SIG_ALG 4
178 #define CRL_OBJ_ISSUER 5
179 #define CRL_OBJ_THIS_UPDATE 6
180 #define CRL_OBJ_NEXT_UPDATE 7
181 #define CRL_OBJ_USER_CERTIFICATE 10
182 #define CRL_OBJ_REVOCATION_DATE 11
183 #define CRL_OBJ_CRL_ENTRY_EXTN_ID 14
184 #define CRL_OBJ_CRL_ENTRY_CRITICAL 15
185 #define CRL_OBJ_CRL_ENTRY_EXTN_VALUE 16
186 #define CRL_OBJ_EXTN_ID 22
187 #define CRL_OBJ_CRITICAL 23
188 #define CRL_OBJ_EXTN_VALUE 24
189 #define CRL_OBJ_ALGORITHM 27
190 #define CRL_OBJ_SIGNATURE 28
191 #define CRL_OBJ_ROOF 29
192
193 /**
194 * Parses a CRL revocation reason code
195 */
196 static crl_reason_t parse_crl_reasonCode(chunk_t object)
197 {
198 crl_reason_t reason = REASON_UNSPECIFIED;
199
200 if (*object.ptr == ASN1_ENUMERATED && asn1_length(&object) == 1)
201 {
202 reason = *object.ptr;
203 }
204 DBG2(" '%N'", crl_reason_names, reason);
205
206 return reason;
207 }
208
209 /**
210 * Parses an X.509 Certificate Revocation List (CRL)
211 */
212 bool parse_x509crl(chunk_t blob, u_int level0, private_crl_t *crl)
213 {
214 asn1_ctx_t ctx;
215 bool critical;
216 chunk_t extnID;
217 chunk_t userCertificate = chunk_empty;
218 revokedCert_t *revokedCert = NULL;
219 chunk_t object;
220 u_int level;
221 int objectID = 0;
222
223 asn1_init(&ctx, blob, level0, FALSE, FALSE);
224
225 while (objectID < CRL_OBJ_ROOF)
226 {
227 if (!extract_object(crlObjects, &objectID, &object, &level, &ctx))
228 return FALSE;
229
230 /* those objects which will parsed further need the next higher level */
231 level++;
232
233 switch (objectID)
234 {
235 case CRL_OBJ_CERTIFICATE_LIST:
236 crl->certificateList = object;
237 break;
238 case CRL_OBJ_TBS_CERT_LIST:
239 crl->tbsCertList = object;
240 break;
241 case CRL_OBJ_VERSION:
242 crl->version = (object.len) ? (1+(u_int)*object.ptr) : 1;
243 DBG2(" v%d", crl->version);
244 break;
245 case CRL_OBJ_SIG_ALG:
246 crl->sigAlg = parse_algorithmIdentifier(object, level, NULL);
247 break;
248 case CRL_OBJ_ISSUER:
249 crl->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object);
250 DBG2(" '%D'", crl->issuer);
251 break;
252 case CRL_OBJ_THIS_UPDATE:
253 crl->thisUpdate = parse_time(object, level);
254 break;
255 case CRL_OBJ_NEXT_UPDATE:
256 crl->nextUpdate = parse_time(object, level);
257 break;
258 case CRL_OBJ_USER_CERTIFICATE:
259 userCertificate = object;
260 break;
261 case CRL_OBJ_REVOCATION_DATE:
262 revokedCert = malloc_thing(revokedCert_t);
263 revokedCert->userCertificate = userCertificate;
264 revokedCert->revocationDate = parse_time(object, level);
265 revokedCert->revocationReason = REASON_UNSPECIFIED;
266 crl->revokedCertificates->insert_last(crl->revokedCertificates, (void *)revokedCert);
267 break;
268 case CRL_OBJ_CRL_ENTRY_EXTN_ID:
269 case CRL_OBJ_EXTN_ID:
270 extnID = object;
271 break;
272 case CRL_OBJ_CRL_ENTRY_CRITICAL:
273 case CRL_OBJ_CRITICAL:
274 critical = object.len && *object.ptr;
275 DBG2(" %s",(critical)?"TRUE":"FALSE");
276 break;
277 case CRL_OBJ_CRL_ENTRY_EXTN_VALUE:
278 case CRL_OBJ_EXTN_VALUE:
279 {
280 int extn_oid = known_oid(extnID);
281
282 if (revokedCert && extn_oid == OID_CRL_REASON_CODE)
283 {
284 revokedCert->revocationReason = parse_crl_reasonCode(object);
285 }
286 else if (extn_oid == OID_AUTHORITY_KEY_ID)
287 {
288 parse_authorityKeyIdentifier(object, level, &crl->authKeyID, &crl->authKeySerialNumber);
289 }
290 }
291 break;
292 case CRL_OBJ_ALGORITHM:
293 crl->algorithm = parse_algorithmIdentifier(object, level, NULL);
294 break;
295 case CRL_OBJ_SIGNATURE:
296 crl->signature = object;
297 break;
298 default:
299 break;
300 }
301 objectID++;
302 }
303 time(&crl->installed);
304 return TRUE;
305 }
306
307 /**
308 * Implements crl_t.is_valid
309 */
310 static bool is_valid(const private_crl_t *this)
311 {
312 time_t current_time = time(NULL);
313
314 DBG2(" this update : %T", &this->thisUpdate);
315 DBG2(" current time: %T", &current_time);
316 DBG2(" next update: %T", &this->nextUpdate);
317
318 return current_time < this->nextUpdate;
319 }
320
321 /**
322 * Implements crl_t.get_issuer
323 */
324 static identification_t *get_issuer(const private_crl_t *this)
325 {
326 return this->issuer;
327 }
328
329 /**
330 * Implements crl_t.equals_issuer
331 */
332 static bool equals_issuer(const private_crl_t *this, const private_crl_t *other)
333 {
334 return (this->authKeyID.ptr)
335 ? chunk_equals(this->authKeyID, other->authKeyID)
336 : (this->issuer->equals(this->issuer, other->issuer)
337 && chunk_equals_or_null(this->authKeySerialNumber, other->authKeySerialNumber));
338 }
339
340 /**
341 * Implements crl_t.is_issuer
342 */
343 static bool is_issuer(const private_crl_t *this, const x509_t *issuer)
344 {
345 return (this->authKeyID.ptr)
346 ? chunk_equals(this->authKeyID, issuer->get_subjectKeyID(issuer))
347 : (this->issuer->equals(this->issuer, issuer->get_subject(issuer))
348 && chunk_equals_or_null(this->authKeySerialNumber, issuer->get_serialNumber(issuer)));
349 }
350
351 /**
352 * Implements crl_t.is_newer
353 */
354 static bool is_newer(const private_crl_t *this, const private_crl_t *other)
355 {
356 return (this->nextUpdate > other->nextUpdate);
357 }
358
359 /**
360 * Implements crl_t.verify
361 */
362 static bool verify(const private_crl_t *this, const rsa_public_key_t *signer)
363 {
364 return signer->verify_emsa_pkcs1_signature(signer, this->tbsCertList, this->signature) == SUCCESS;
365 }
366
367 /**
368 * Implements crl_t.get_status
369 */
370 static void get_status(const private_crl_t *this, certinfo_t *certinfo)
371 {
372 chunk_t serialNumber = certinfo->get_serialNumber(certinfo);
373 iterator_t *iterator;
374 revokedCert_t *revokedCert;
375
376 certinfo->set_nextUpdate(certinfo, this->nextUpdate);
377 certinfo->set_status(certinfo, CERT_GOOD);
378
379 iterator = this->revokedCertificates->create_iterator(this->revokedCertificates, TRUE);
380 while (iterator->iterate(iterator, (void**)&revokedCert))
381 {
382 if (chunk_equals(serialNumber, revokedCert->userCertificate))
383 {
384 certinfo->set_status(certinfo, CERT_REVOKED);
385 certinfo->set_revocationTime(certinfo, revokedCert->revocationDate);
386 certinfo->set_revocationReason(certinfo, revokedCert->revocationReason);
387 break;
388 }
389 }
390 iterator->destroy(iterator);
391 }
392
393 /**
394 * Implements crl_t.write_to_file.
395 */
396 static bool write_to_file(private_crl_t *this, const char *path, mode_t mask, bool force)
397 {
398 return chunk_write(this->certificateList, path, "crl", mask, force);
399 }
400
401 /**
402 * Implements crl_t.destroy
403 */
404 static void destroy(private_crl_t *this)
405 {
406 this->revokedCertificates->destroy_function(this->revokedCertificates, free);
407 this->crlDistributionPoints->destroy_offset(this->crlDistributionPoints,
408 offsetof(identification_t, destroy));
409 DESTROY_IF(this->issuer);
410 free(this->certificateList.ptr);
411 free(this);
412 }
413
414 /**
415 * Implementation of crl_t.list.
416 */
417 static void list(private_crl_t *this, FILE* out, bool utc)
418 {
419 time_t now;
420
421 now = time(NULL);
422
423 fprintf(out, "%#T, revoked certs: %d\n", &this->installed, utc,
424 this->revokedCertificates->get_count(this->revokedCertificates));
425 fprintf(out, " issuer: '%D'\n", this->issuer);
426 fprintf(out, " updates: this %#T\n", &this->thisUpdate, utc);
427 fprintf(out, " next %#T ", &this->nextUpdate, utc);
428 if (this->nextUpdate == UNDEFINED_TIME)
429 {
430 fprintf(out, "ok (expires never)\n");
431 }
432 else if (now > this->nextUpdate)
433 {
434 fprintf(out, "expired (%V ago)\n", &now, &this->nextUpdate);
435 }
436 else if (now > this->nextUpdate - CRL_WARNING_INTERVAL * 60 * 60 * 24)
437 {
438 fprintf(out, "ok (expires in %V)\n", &now, &this->nextUpdate);
439 }
440 else
441 {
442 fprintf(out, "ok\n");
443 }
444 if (this->authKeyID.ptr)
445 {
446 fprintf(out, " authkey: %#B\n", &this->authKeyID);
447 }
448 if (this->authKeySerialNumber.ptr)
449 {
450 fprintf(out, " aserial: %#B\n", &this->authKeySerialNumber);
451 }
452 }
453
454 /*
455 * Described in header.
456 */
457 crl_t *crl_create_from_chunk(chunk_t chunk)
458 {
459 private_crl_t *this = malloc_thing(private_crl_t);
460
461 /* initialize */
462 this->crlDistributionPoints = linked_list_create();
463 this->tbsCertList = chunk_empty;
464 this->issuer = NULL;
465 this->revokedCertificates = linked_list_create();
466 this->authKeyID = chunk_empty;
467 this->authKeySerialNumber = chunk_empty;
468
469 /* public functions */
470 this->public.get_issuer = (identification_t* (*) (const crl_t*))get_issuer;
471 this->public.equals_issuer = (bool (*) (const crl_t*,const crl_t*))equals_issuer;
472 this->public.is_issuer = (bool (*) (const crl_t*,const x509_t*))is_issuer;
473 this->public.is_valid = (bool (*) (const crl_t*))is_valid;
474 this->public.is_newer = (bool (*) (const crl_t*,const crl_t*))is_newer;
475 this->public.verify = (bool (*) (const crl_t*,const rsa_public_key_t*))verify;
476 this->public.get_status = (void (*) (const crl_t*,certinfo_t*))get_status;
477 this->public.write_to_file = (bool (*) (const crl_t*,const char*,mode_t,bool))write_to_file;
478 this->public.list = (void(*)(crl_t*, FILE* out, bool utc))list;
479 this->public.destroy = (void (*) (crl_t*))destroy;
480
481 if (!parse_x509crl(chunk, 0, this))
482 {
483 destroy(this);
484 return NULL;
485 }
486
487 return &this->public;
488 }
489
490 /*
491 * Described in header.
492 */
493 crl_t *crl_create_from_file(const char *filename)
494 {
495 bool pgp = FALSE;
496 chunk_t chunk = chunk_empty;
497 crl_t *crl = NULL;
498
499 if (!pem_asn1_load_file(filename, NULL, "crl", &chunk, &pgp))
500 return NULL;
501
502 crl = crl_create_from_chunk(chunk);
503
504 if (crl == NULL)
505 free(chunk.ptr);
506 return crl;
507 }