]>
Commit | Line | Data |
---|---|---|
997358a6 MW |
1 | /* Support of the Online Certificate Status Protocol (OCSP) |
2 | * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen | |
7be566dc | 3 | * Copyright (C) 2009 Andreas Steffen - Hochschule fuer Technik Rapperswil |
997358a6 MW |
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. | |
997358a6 MW |
14 | */ |
15 | ||
16 | #include <unistd.h> | |
17 | #include <stdlib.h> | |
18 | #include <string.h> | |
19 | #include <time.h> | |
20 | #include <sys/types.h> | |
21 | #include <sys/stat.h> | |
22 | #include <fcntl.h> | |
23 | ||
24 | #include <freeswan.h> | |
997358a6 | 25 | |
bc2e33ca | 26 | #include <library.h> |
08b2d288 AS |
27 | #include <asn1/asn1.h> |
28 | #include <asn1/asn1_parser.h> | |
29 | #include <asn1/oid.h> | |
bc2e33ca | 30 | #include <crypto/rngs/rng.h> |
c08ae816 | 31 | #include <crypto/hashers/hasher.h> |
08b2d288 | 32 | |
997358a6 MW |
33 | #include "constants.h" |
34 | #include "defs.h" | |
35 | #include "log.h" | |
36 | #include "x509.h" | |
37 | #include "crl.h" | |
38 | #include "ca.h" | |
997358a6 MW |
39 | #include "certs.h" |
40 | #include "smartcard.h" | |
997358a6 | 41 | #include "whack.h" |
997358a6 MW |
42 | #include "keys.h" |
43 | #include "fetch.h" | |
44 | #include "ocsp.h" | |
45 | ||
3d7a244b | 46 | #define NONCE_LENGTH 16 |
997358a6 MW |
47 | |
48 | static const char *const cert_status_names[] = { | |
3d7a244b AS |
49 | "good", |
50 | "revoked", | |
51 | "unknown", | |
52 | "undefined" | |
997358a6 MW |
53 | }; |
54 | ||
55 | ||
56 | static const char *const response_status_names[] = { | |
3d7a244b AS |
57 | "successful", |
58 | "malformed request", | |
59 | "internal error", | |
60 | "try later", | |
61 | "status #4", | |
62 | "signature required", | |
63 | "unauthorized" | |
997358a6 MW |
64 | }; |
65 | ||
66 | /* response container */ | |
67 | typedef struct response response_t; | |
68 | ||
69 | struct response { | |
0354d570 AS |
70 | chunk_t tbs; |
71 | identification_t *responder_id_name; | |
72 | chunk_t responder_id_key; | |
73 | time_t produced_at; | |
74 | chunk_t responses; | |
75 | chunk_t nonce; | |
76 | int algorithm; | |
77 | chunk_t signature; | |
997358a6 MW |
78 | }; |
79 | ||
80 | const response_t empty_response = { | |
3d7a244b | 81 | { NULL, 0 } , /* tbs */ |
0354d570 | 82 | NULL , /* responder_id_name */ |
3d7a244b AS |
83 | { NULL, 0 } , /* responder_id_key */ |
84 | UNDEFINED_TIME, /* produced_at */ | |
85 | { NULL, 0 } , /* single_response */ | |
86 | { NULL, 0 } , /* nonce */ | |
87 | OID_UNKNOWN , /* signature_algorithm */ | |
88 | { NULL, 0 } /* signature */ | |
997358a6 MW |
89 | }; |
90 | ||
91 | /* single response container */ | |
92 | typedef struct single_response single_response_t; | |
93 | ||
94 | struct single_response { | |
3d7a244b AS |
95 | single_response_t *next; |
96 | int hash_algorithm; | |
97 | chunk_t issuer_name_hash; | |
98 | chunk_t issuer_key_hash; | |
99 | chunk_t serialNumber; | |
100 | cert_status_t status; | |
101 | time_t revocationTime; | |
102 | crl_reason_t revocationReason; | |
103 | time_t thisUpdate; | |
104 | time_t nextUpdate; | |
997358a6 MW |
105 | }; |
106 | ||
107 | const single_response_t empty_single_response = { | |
f561d6f2 AS |
108 | NULL , /* *next */ |
109 | OID_UNKNOWN , /* hash_algorithm */ | |
110 | { NULL, 0 } , /* issuer_name_hash */ | |
111 | { NULL, 0 } , /* issuer_key_hash */ | |
112 | { NULL, 0 } , /* serial_number */ | |
113 | CERT_UNDEFINED , /* status */ | |
114 | UNDEFINED_TIME , /* revocationTime */ | |
115 | CRL_REASON_UNSPECIFIED, /* revocationReason */ | |
116 | UNDEFINED_TIME , /* this_update */ | |
117 | UNDEFINED_TIME /* next_update */ | |
997358a6 MW |
118 | }; |
119 | ||
120 | ||
121 | /* list of single requests */ | |
122 | typedef struct request_list request_list_t; | |
123 | struct request_list { | |
3d7a244b AS |
124 | chunk_t request; |
125 | request_list_t *next; | |
997358a6 MW |
126 | }; |
127 | ||
128 | /* some OCSP specific prefabricated ASN.1 constants */ | |
3b878dae | 129 | static const chunk_t ASN1_nonce_oid = chunk_from_chars( |
3d7a244b | 130 | 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x02 |
3b878dae MW |
131 | ); |
132 | static const chunk_t ASN1_response_oid = chunk_from_chars( | |
3d7a244b | 133 | 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x04 |
3b878dae MW |
134 | ); |
135 | static const chunk_t ASN1_response_content = chunk_from_chars( | |
3d7a244b AS |
136 | 0x04, 0x0D, |
137 | 0x30, 0x0B, | |
138 | 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 | |
3b878dae | 139 | ); |
997358a6 MW |
140 | |
141 | /* default OCSP uri */ | |
142 | static chunk_t ocsp_default_uri; | |
143 | ||
144 | /* ocsp cache: pointer to first element */ | |
145 | static ocsp_location_t *ocsp_cache = NULL; | |
146 | ||
147 | /* static temporary storage for ocsp requestor information */ | |
f565d0c5 | 148 | static cert_t *ocsp_requestor_cert = NULL; |
997358a6 MW |
149 | |
150 | static smartcard_t *ocsp_requestor_sc = NULL; | |
151 | ||
8b799d55 | 152 | static private_key_t *ocsp_requestor_key = NULL; |
997358a6 | 153 | |
08b2d288 AS |
154 | /** |
155 | * ASN.1 definition of ocspResponse | |
156 | */ | |
997358a6 | 157 | static const asn1Object_t ocspResponseObjects[] = { |
08b2d288 AS |
158 | { 0, "OCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ |
159 | { 1, "responseStatus", ASN1_ENUMERATED, ASN1_BODY }, /* 1 */ | |
160 | { 1, "responseBytesContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 2 */ | |
161 | { 2, "responseBytes", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */ | |
162 | { 3, "responseType", ASN1_OID, ASN1_BODY }, /* 4 */ | |
163 | { 3, "response", ASN1_OCTET_STRING, ASN1_BODY }, /* 5 */ | |
164 | { 1, "end opt", ASN1_EOC, ASN1_END }, /* 6 */ | |
165 | { 0, "exit", ASN1_EOC, ASN1_EXIT } | |
997358a6 | 166 | }; |
08b2d288 AS |
167 | #define OCSP_RESPONSE_STATUS 1 |
168 | #define OCSP_RESPONSE_TYPE 4 | |
169 | #define OCSP_RESPONSE 5 | |
997358a6 | 170 | |
08b2d288 AS |
171 | /** |
172 | * ASN.1 definition of basicResponse | |
173 | */ | |
997358a6 | 174 | static const asn1Object_t basicResponseObjects[] = { |
08b2d288 AS |
175 | { 0, "BasicOCSPResponse", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ |
176 | { 1, "tbsResponseData", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ | |
177 | { 2, "versionContext", ASN1_CONTEXT_C_0, ASN1_NONE | | |
178 | ASN1_DEF }, /* 2 */ | |
179 | { 3, "version", ASN1_INTEGER, ASN1_BODY }, /* 3 */ | |
180 | { 2, "responderIdContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 4 */ | |
181 | { 3, "responderIdByName", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ | |
182 | { 2, "end choice", ASN1_EOC, ASN1_END }, /* 6 */ | |
183 | { 2, "responderIdContext", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 7 */ | |
184 | { 3, "responderIdByKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 8 */ | |
185 | { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ | |
186 | { 2, "producedAt", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 10 */ | |
187 | { 2, "responses", ASN1_SEQUENCE, ASN1_OBJ }, /* 11 */ | |
188 | { 2, "responseExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 12 */ | |
189 | { 3, "responseExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 13 */ | |
190 | { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 14 */ | |
191 | { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 15 */ | |
192 | { 5, "critical", ASN1_BOOLEAN, ASN1_BODY | | |
193 | ASN1_DEF }, /* 16 */ | |
194 | { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 17 */ | |
195 | { 4, "end loop", ASN1_EOC, ASN1_END }, /* 18 */ | |
196 | { 2, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ | |
197 | { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 20 */ | |
198 | { 1, "signature", ASN1_BIT_STRING, ASN1_BODY }, /* 21 */ | |
199 | { 1, "certsContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 22 */ | |
200 | { 2, "certs", ASN1_SEQUENCE, ASN1_LOOP }, /* 23 */ | |
201 | { 3, "certificate", ASN1_SEQUENCE, ASN1_RAW }, /* 24 */ | |
202 | { 2, "end loop", ASN1_EOC, ASN1_END }, /* 25 */ | |
203 | { 1, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ | |
204 | { 0, "exit", ASN1_EOC, ASN1_EXIT } | |
997358a6 | 205 | }; |
08b2d288 AS |
206 | #define BASIC_RESPONSE_TBS_DATA 1 |
207 | #define BASIC_RESPONSE_VERSION 3 | |
208 | #define BASIC_RESPONSE_ID_BY_NAME 5 | |
209 | #define BASIC_RESPONSE_ID_BY_KEY 8 | |
210 | #define BASIC_RESPONSE_PRODUCED_AT 10 | |
211 | #define BASIC_RESPONSE_RESPONSES 11 | |
212 | #define BASIC_RESPONSE_EXT_ID 15 | |
213 | #define BASIC_RESPONSE_CRITICAL 16 | |
214 | #define BASIC_RESPONSE_EXT_VALUE 17 | |
215 | #define BASIC_RESPONSE_ALGORITHM 20 | |
216 | #define BASIC_RESPONSE_SIGNATURE 21 | |
217 | #define BASIC_RESPONSE_CERTIFICATE 24 | |
218 | ||
219 | /** | |
220 | * ASN.1 definition of responses | |
221 | */ | |
997358a6 | 222 | static const asn1Object_t responsesObjects[] = { |
08b2d288 AS |
223 | { 0, "responses", ASN1_SEQUENCE, ASN1_LOOP }, /* 0 */ |
224 | { 1, "singleResponse", ASN1_EOC, ASN1_RAW }, /* 1 */ | |
225 | { 0, "end loop", ASN1_EOC, ASN1_END }, /* 2 */ | |
226 | { 0, "exit", ASN1_EOC, ASN1_EXIT } | |
997358a6 | 227 | }; |
08b2d288 | 228 | #define RESPONSES_SINGLE_RESPONSE 1 |
997358a6 | 229 | |
08b2d288 AS |
230 | /** |
231 | * ASN.1 definition of singleResponse | |
232 | */ | |
997358a6 | 233 | static const asn1Object_t singleResponseObjects[] = { |
08b2d288 AS |
234 | { 0, "singleResponse", ASN1_SEQUENCE, ASN1_BODY }, /* 0 */ |
235 | { 1, "certID", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */ | |
236 | { 2, "algorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ | |
237 | { 2, "issuerNameHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */ | |
238 | { 2, "issuerKeyHash", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */ | |
239 | { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 5 */ | |
240 | { 1, "certStatusGood", ASN1_CONTEXT_S_0, ASN1_OPT }, /* 6 */ | |
241 | { 1, "end opt", ASN1_EOC, ASN1_END }, /* 7 */ | |
242 | { 1, "certStatusRevoked", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 8 */ | |
243 | { 2, "revocationTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 9 */ | |
244 | { 2, "revocationReason", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 10 */ | |
245 | { 3, "crlReason", ASN1_ENUMERATED, ASN1_BODY }, /* 11 */ | |
246 | { 2, "end opt", ASN1_EOC, ASN1_END }, /* 12 */ | |
247 | { 1, "end opt", ASN1_EOC, ASN1_END }, /* 13 */ | |
248 | { 1, "certStatusUnknown", ASN1_CONTEXT_S_2, ASN1_OPT }, /* 14 */ | |
249 | { 1, "end opt", ASN1_EOC, ASN1_END }, /* 15 */ | |
250 | { 1, "thisUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 16 */ | |
251 | { 1, "nextUpdateContext", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 17 */ | |
252 | { 2, "nextUpdate", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 18 */ | |
253 | { 1, "end opt", ASN1_EOC, ASN1_END }, /* 19 */ | |
254 | { 1, "singleExtensionsContext", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 20 */ | |
255 | { 2, "singleExtensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 21 */ | |
256 | { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */ | |
257 | { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 23 */ | |
258 | { 4, "critical", ASN1_BOOLEAN, ASN1_BODY | | |
259 | ASN1_DEF }, /* 24 */ | |
260 | { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 25 */ | |
261 | { 2, "end loop", ASN1_EOC, ASN1_END }, /* 26 */ | |
262 | { 1, "end opt", ASN1_EOC, ASN1_END }, /* 27 */ | |
263 | { 0, "exit", ASN1_EOC, ASN1_EXIT } | |
997358a6 | 264 | }; |
08b2d288 AS |
265 | #define SINGLE_RESPONSE_ALGORITHM 2 |
266 | #define SINGLE_RESPONSE_ISSUER_NAME_HASH 3 | |
267 | #define SINGLE_RESPONSE_ISSUER_KEY_HASH 4 | |
268 | #define SINGLE_RESPONSE_SERIAL_NUMBER 5 | |
269 | #define SINGLE_RESPONSE_CERT_STATUS_GOOD 6 | |
270 | #define SINGLE_RESPONSE_CERT_STATUS_REVOKED 8 | |
271 | #define SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME 9 | |
272 | #define SINGLE_RESPONSE_CERT_STATUS_CRL_REASON 11 | |
273 | #define SINGLE_RESPONSE_CERT_STATUS_UNKNOWN 14 | |
274 | #define SINGLE_RESPONSE_THIS_UPDATE 16 | |
275 | #define SINGLE_RESPONSE_NEXT_UPDATE 18 | |
276 | #define SINGLE_RESPONSE_EXT_ID 23 | |
277 | #define SINGLE_RESPONSE_CRITICAL 24 | |
278 | #define SINGLE_RESPONSE_EXT_VALUE 25 | |
997358a6 | 279 | |
08b2d288 AS |
280 | /* |
281 | * Build an ocsp location from certificate information | |
997358a6 MW |
282 | * without unsharing its contents |
283 | */ | |
f565d0c5 | 284 | static bool build_ocsp_location(const cert_t *cert, ocsp_location_t *location) |
997358a6 | 285 | { |
0eff9f65 AS |
286 | certificate_t *certificate = cert->cert; |
287 | identification_t *issuer = certificate->get_issuer(certificate); | |
288 | x509_t *x509 = (x509_t*)certificate; | |
289 | chunk_t issuer_dn = issuer->get_encoding(issuer); | |
290 | chunk_t authKeyID = x509->get_authKeyIdentifier(x509); | |
8b799d55 | 291 | hasher_t *hasher; |
c08ae816 | 292 | static u_char digest[HASH_SIZE_SHA1]; /* temporary storage */ |
7daf5226 | 293 | |
0eff9f65 | 294 | enumerator_t *enumerator = x509->create_ocsp_uri_enumerator(x509); |
997358a6 | 295 | |
0eff9f65 AS |
296 | location->uri = NULL; |
297 | while (enumerator->enumerate(enumerator, &location->uri)) | |
9b91b818 | 298 | { |
0eff9f65 AS |
299 | break; |
300 | } | |
301 | enumerator->destroy(enumerator); | |
302 | ||
303 | if (location->uri == NULL) | |
304 | { | |
0354d570 AS |
305 | ca_info_t *ca = get_ca_info(issuer, authKeyID); |
306 | if (ca && ca->ocspuri) | |
3d7a244b | 307 | { |
0eff9f65 | 308 | location->uri = ca->ocspuri; |
3d7a244b AS |
309 | } |
310 | else | |
311 | { /* abort if no ocsp location uri is defined */ | |
312 | return FALSE; | |
313 | } | |
9b91b818 | 314 | } |
7daf5226 | 315 | |
8b799d55 | 316 | /* compute authNameID from as SHA-1 hash of issuer DN */ |
c08ae816 | 317 | location->authNameID = chunk_create(digest, HASH_SIZE_SHA1); |
8b799d55 AS |
318 | hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); |
319 | if (hasher == NULL) | |
320 | { | |
321 | return FALSE; | |
322 | } | |
0eff9f65 | 323 | hasher->get_hash(hasher, issuer_dn, digest); |
8b799d55 | 324 | hasher->destroy(hasher); |
3d7a244b AS |
325 | |
326 | location->next = NULL; | |
0354d570 | 327 | location->issuer = issuer; |
0eff9f65 | 328 | location->authKeyID = authKeyID; |
7daf5226 | 329 | |
0eff9f65 | 330 | if (authKeyID.ptr == NULL) |
997358a6 | 331 | { |
f565d0c5 | 332 | cert_t *authcert = get_authcert(issuer, authKeyID, X509_CA); |
3d7a244b | 333 | |
0354d570 | 334 | if (authcert) |
3d7a244b | 335 | { |
0eff9f65 AS |
336 | x509_t *x509 = (x509_t*)authcert->cert; |
337 | ||
338 | location->authKeyID = x509->get_subjectKeyIdentifier(x509); | |
3d7a244b | 339 | } |
997358a6 | 340 | } |
997358a6 | 341 | |
3d7a244b AS |
342 | location->nonce = chunk_empty; |
343 | location->certinfo = NULL; | |
997358a6 | 344 | |
3d7a244b | 345 | return TRUE; |
997358a6 MW |
346 | } |
347 | ||
08b2d288 AS |
348 | /** |
349 | * Compare two ocsp locations for equality | |
997358a6 | 350 | */ |
08b2d288 | 351 | static bool same_ocsp_location(const ocsp_location_t *a, const ocsp_location_t *b) |
997358a6 | 352 | { |
0354d570 | 353 | return ((a->authKeyID.ptr) |
3d7a244b | 354 | ? same_keyid(a->authKeyID, b->authKeyID) |
0354d570 | 355 | : a->issuer->equals(a->issuer, b->issuer)) |
0eff9f65 | 356 | && streq(a->uri, b->uri); |
997358a6 MW |
357 | } |
358 | ||
08b2d288 AS |
359 | /** |
360 | * Find an existing ocsp location in a chained list | |
997358a6 | 361 | */ |
08b2d288 | 362 | ocsp_location_t* get_ocsp_location(const ocsp_location_t * loc, ocsp_location_t *chain) |
997358a6 MW |
363 | { |
364 | ||
0354d570 | 365 | while (chain) |
3d7a244b AS |
366 | { |
367 | if (same_ocsp_location(loc, chain)) | |
368 | return chain; | |
369 | chain = chain->next; | |
370 | } | |
371 | return NULL; | |
997358a6 | 372 | } |
3d7a244b | 373 | |
08b2d288 AS |
374 | /** |
375 | * Retrieves the status of a cert from the ocsp cache | |
997358a6 MW |
376 | * returns CERT_UNDEFINED if no status is found |
377 | */ | |
08b2d288 AS |
378 | static cert_status_t get_ocsp_status(const ocsp_location_t *loc, |
379 | chunk_t serialNumber, | |
380 | time_t *nextUpdate, time_t *revocationTime, | |
381 | crl_reason_t *revocationReason) | |
997358a6 | 382 | { |
3d7a244b AS |
383 | ocsp_certinfo_t *certinfo, **certinfop; |
384 | int cmp = -1; | |
997358a6 | 385 | |
3d7a244b AS |
386 | /* find location */ |
387 | ocsp_location_t *location = get_ocsp_location(loc, ocsp_cache); | |
997358a6 | 388 | |
3d7a244b AS |
389 | if (location == NULL) |
390 | return CERT_UNDEFINED; | |
997358a6 | 391 | |
3d7a244b AS |
392 | /* traverse list of certinfos in increasing order */ |
393 | certinfop = &location->certinfo; | |
997358a6 | 394 | certinfo = *certinfop; |
997358a6 | 395 | |
0354d570 | 396 | while (certinfo) |
3d7a244b AS |
397 | { |
398 | cmp = chunk_compare(serialNumber, certinfo->serialNumber); | |
399 | if (cmp <= 0) | |
400 | break; | |
401 | certinfop = &certinfo->next; | |
402 | certinfo = *certinfop; | |
403 | } | |
404 | ||
405 | if (cmp == 0) | |
406 | { | |
407 | *nextUpdate = certinfo->nextUpdate; | |
408 | *revocationTime = certinfo->revocationTime; | |
409 | *revocationReason = certinfo->revocationReason; | |
410 | return certinfo->status; | |
411 | } | |
997358a6 | 412 | |
3d7a244b | 413 | return CERT_UNDEFINED; |
997358a6 MW |
414 | } |
415 | ||
08b2d288 AS |
416 | /** |
417 | * Verify the ocsp status of a certificate | |
997358a6 | 418 | */ |
f565d0c5 | 419 | cert_status_t verify_by_ocsp(const cert_t *cert, time_t *until, |
08b2d288 AS |
420 | time_t *revocationDate, |
421 | crl_reason_t *revocationReason) | |
997358a6 | 422 | { |
0eff9f65 AS |
423 | x509_t *x509 = (x509_t*)cert->cert; |
424 | chunk_t serialNumber = x509->get_serial(x509); | |
3d7a244b AS |
425 | cert_status_t status; |
426 | ocsp_location_t location; | |
ce2f2461 | 427 | time_t nextUpdate = UNDEFINED_TIME; |
3d7a244b AS |
428 | |
429 | *revocationDate = UNDEFINED_TIME; | |
f561d6f2 | 430 | *revocationReason = CRL_REASON_UNSPECIFIED; |
7daf5226 | 431 | |
3d7a244b AS |
432 | /* is an ocsp location defined? */ |
433 | if (!build_ocsp_location(cert, &location)) | |
0eff9f65 | 434 | { |
3d7a244b | 435 | return CERT_UNDEFINED; |
0eff9f65 | 436 | } |
997358a6 | 437 | |
3d7a244b | 438 | lock_ocsp_cache("verify_by_ocsp"); |
0eff9f65 | 439 | status = get_ocsp_status(&location, serialNumber, &nextUpdate |
3d7a244b AS |
440 | , revocationDate, revocationReason); |
441 | unlock_ocsp_cache("verify_by_ocsp"); | |
442 | ||
443 | if (status == CERT_UNDEFINED || nextUpdate < time(NULL)) | |
444 | { | |
445 | plog("ocsp status is stale or not in cache"); | |
0eff9f65 | 446 | add_ocsp_fetch_request(&location, serialNumber); |
3d7a244b AS |
447 | |
448 | /* inititate fetching of ocsp status */ | |
449 | wake_fetch_thread("verify_by_ocsp"); | |
450 | } | |
451 | *until = nextUpdate; | |
452 | return status; | |
997358a6 MW |
453 | } |
454 | ||
08b2d288 AS |
455 | /** |
456 | * Check if an ocsp status is about to expire | |
997358a6 | 457 | */ |
08b2d288 | 458 | void check_ocsp(void) |
997358a6 | 459 | { |
3d7a244b | 460 | ocsp_location_t *location; |
997358a6 | 461 | |
3d7a244b AS |
462 | lock_ocsp_cache("check_ocsp"); |
463 | location = ocsp_cache; | |
7daf5226 | 464 | |
0354d570 | 465 | while (location) |
997358a6 | 466 | { |
3d7a244b AS |
467 | char buf[BUF_LEN]; |
468 | bool first = TRUE; | |
469 | ocsp_certinfo_t *certinfo = location->certinfo; | |
997358a6 | 470 | |
0354d570 | 471 | while (certinfo) |
3d7a244b AS |
472 | { |
473 | if (!certinfo->once) | |
997358a6 | 474 | { |
3d7a244b AS |
475 | time_t time_left = certinfo->nextUpdate - time(NULL); |
476 | ||
477 | DBG(DBG_CONTROL, | |
478 | if (first) | |
479 | { | |
0354d570 AS |
480 | DBG_log("issuer: \"%Y\"", location->issuer); |
481 | if (location->authKeyID.ptr) | |
3d7a244b AS |
482 | { |
483 | datatot(location->authKeyID.ptr, location->authKeyID.len | |
484 | , ':', buf, BUF_LEN); | |
485 | DBG_log("authkey: %s", buf); | |
486 | } | |
487 | first = FALSE; | |
488 | } | |
489 | datatot(certinfo->serialNumber.ptr, certinfo->serialNumber.len | |
490 | , ':', buf, BUF_LEN); | |
491 | DBG_log("serial: %s, %ld seconds left", buf, time_left) | |
492 | ) | |
493 | ||
494 | if (time_left < 2*crl_check_interval) | |
495 | add_ocsp_fetch_request(location, certinfo->serialNumber); | |
997358a6 | 496 | } |
3d7a244b AS |
497 | certinfo = certinfo->next; |
498 | } | |
499 | location = location->next; | |
997358a6 | 500 | } |
3d7a244b | 501 | unlock_ocsp_cache("check_ocsp"); |
997358a6 MW |
502 | } |
503 | ||
08b2d288 | 504 | /** |
997358a6 MW |
505 | * frees the allocated memory of a certinfo struct |
506 | */ | |
08b2d288 | 507 | static void free_certinfo(ocsp_certinfo_t *certinfo) |
997358a6 | 508 | { |
3d7a244b AS |
509 | free(certinfo->serialNumber.ptr); |
510 | free(certinfo); | |
997358a6 MW |
511 | } |
512 | ||
08b2d288 | 513 | /** |
997358a6 MW |
514 | * frees all certinfos in a chained list |
515 | */ | |
08b2d288 | 516 | static void free_certinfos(ocsp_certinfo_t *chain) |
997358a6 | 517 | { |
3d7a244b | 518 | ocsp_certinfo_t *certinfo; |
997358a6 | 519 | |
0354d570 | 520 | while (chain) |
3d7a244b AS |
521 | { |
522 | certinfo = chain; | |
523 | chain = chain->next; | |
524 | free_certinfo(certinfo); | |
525 | } | |
997358a6 MW |
526 | } |
527 | ||
08b2d288 AS |
528 | /** |
529 | * Frees the memory allocated to an ocsp location including all certinfos | |
997358a6 | 530 | */ |
08b2d288 | 531 | static void free_ocsp_location(ocsp_location_t* location) |
997358a6 | 532 | { |
0354d570 | 533 | DESTROY_IF(location->issuer); |
3d7a244b AS |
534 | free(location->authNameID.ptr); |
535 | free(location->authKeyID.ptr); | |
0eff9f65 | 536 | free(location->uri); |
3d7a244b AS |
537 | free_certinfos(location->certinfo); |
538 | free(location); | |
997358a6 MW |
539 | } |
540 | ||
541 | /* | |
08b2d288 | 542 | * Free a chained list of ocsp locations |
997358a6 | 543 | */ |
08b2d288 | 544 | void free_ocsp_locations(ocsp_location_t **chain) |
997358a6 | 545 | { |
0354d570 | 546 | while (*chain) |
3d7a244b AS |
547 | { |
548 | ocsp_location_t *location = *chain; | |
549 | *chain = location->next; | |
550 | free_ocsp_location(location); | |
551 | } | |
997358a6 MW |
552 | } |
553 | ||
08b2d288 AS |
554 | /** |
555 | * Free the ocsp cache | |
997358a6 | 556 | */ |
08b2d288 | 557 | void free_ocsp_cache(void) |
997358a6 | 558 | { |
3d7a244b AS |
559 | lock_ocsp_cache("free_ocsp_cache"); |
560 | free_ocsp_locations(&ocsp_cache); | |
561 | unlock_ocsp_cache("free_ocsp_cache"); | |
997358a6 MW |
562 | } |
563 | ||
08b2d288 AS |
564 | /** |
565 | * Frees the ocsp cache and global variables | |
997358a6 | 566 | */ |
08b2d288 | 567 | void free_ocsp(void) |
997358a6 | 568 | { |
3d7a244b AS |
569 | free(ocsp_default_uri.ptr); |
570 | free_ocsp_cache(); | |
997358a6 MW |
571 | } |
572 | ||
08b2d288 AS |
573 | /** |
574 | * List a chained list of ocsp_locations | |
997358a6 | 575 | */ |
08b2d288 AS |
576 | void list_ocsp_locations(ocsp_location_t *location, bool requests, |
577 | bool utc, bool strict) | |
997358a6 | 578 | { |
3d7a244b | 579 | bool first = TRUE; |
997358a6 | 580 | |
0354d570 | 581 | while (location) |
997358a6 | 582 | { |
3d7a244b AS |
583 | ocsp_certinfo_t *certinfo = location->certinfo; |
584 | ||
0354d570 | 585 | if (certinfo) |
997358a6 | 586 | { |
3d7a244b AS |
587 | if (first) |
588 | { | |
589 | whack_log(RC_COMMENT, " "); | |
0354d570 AS |
590 | whack_log(RC_COMMENT, "List of OCSP %s:", requests ? |
591 | "Fetch Requests" : "Responses"); | |
3d7a244b AS |
592 | first = FALSE; |
593 | } | |
594 | whack_log(RC_COMMENT, " "); | |
0354d570 | 595 | if (location->issuer) |
3d7a244b | 596 | { |
0354d570 | 597 | whack_log(RC_COMMENT, " issuer: \"%Y\"", location->issuer); |
3d7a244b | 598 | } |
0354d570 | 599 | whack_log(RC_COMMENT, " uri: '%s'", location->uri); |
ce2f2461 | 600 | if (location->authNameID.ptr) |
3d7a244b | 601 | { |
0354d570 | 602 | whack_log(RC_COMMENT, " authname: %#B", &location->authNameID); |
3d7a244b | 603 | } |
ce2f2461 | 604 | if (location->authKeyID.ptr) |
3d7a244b | 605 | { |
0354d570 | 606 | whack_log(RC_COMMENT, " authkey: %#B", &location->authKeyID); |
3d7a244b | 607 | } |
ce2f2461 | 608 | while (certinfo) |
3d7a244b | 609 | { |
3d7a244b AS |
610 | if (requests) |
611 | { | |
0354d570 | 612 | whack_log(RC_COMMENT, " serial: %#B, %d trials", |
ce2f2461 | 613 | &certinfo->serialNumber, certinfo->trials); |
3d7a244b AS |
614 | } |
615 | else if (certinfo->once) | |
616 | { | |
0354d570 | 617 | whack_log(RC_COMMENT, " serial: %#B, %s, once%s", |
ce2f2461 AS |
618 | &certinfo->serialNumber, |
619 | cert_status_names[certinfo->status], | |
620 | (certinfo->nextUpdate < time(NULL))? " (expired)": ""); | |
3d7a244b AS |
621 | } |
622 | else | |
623 | { | |
0406eeaa | 624 | whack_log(RC_COMMENT, " serial: %#B, %s, until %T %s", |
ce2f2461 AS |
625 | &certinfo->serialNumber, |
626 | cert_status_names[certinfo->status], | |
627 | &certinfo->nextUpdate, utc, | |
628 | check_expiry(certinfo->nextUpdate, OCSP_WARNING_INTERVAL, strict)); | |
3d7a244b | 629 | } |
3d7a244b AS |
630 | certinfo = certinfo->next; |
631 | } | |
997358a6 | 632 | } |
3d7a244b | 633 | location = location->next; |
997358a6 | 634 | } |
997358a6 MW |
635 | } |
636 | ||
08b2d288 AS |
637 | /** |
638 | * List the ocsp cache | |
997358a6 | 639 | */ |
08b2d288 | 640 | void list_ocsp_cache(bool utc, bool strict) |
997358a6 | 641 | { |
3d7a244b AS |
642 | lock_ocsp_cache("list_ocsp_cache"); |
643 | list_ocsp_locations(ocsp_cache, FALSE, utc, strict); | |
644 | unlock_ocsp_cache("list_ocsp_cache"); | |
997358a6 MW |
645 | } |
646 | ||
08b2d288 | 647 | static bool get_ocsp_requestor_cert(ocsp_location_t *location) |
997358a6 | 648 | { |
f565d0c5 | 649 | cert_t *cert = NULL; |
997358a6 | 650 | |
3d7a244b AS |
651 | /* initialize temporary static storage */ |
652 | ocsp_requestor_cert = NULL; | |
653 | ocsp_requestor_sc = NULL; | |
8b799d55 | 654 | ocsp_requestor_key = NULL; |
997358a6 | 655 | |
3d7a244b | 656 | for (;;) |
997358a6 | 657 | { |
0eff9f65 | 658 | certificate_t *certificate; |
3d7a244b AS |
659 | |
660 | /* looking for a certificate from the same issuer */ | |
0eff9f65 | 661 | cert = get_x509cert(location->issuer, location->authKeyID, cert); |
3d7a244b | 662 | if (cert == NULL) |
0eff9f65 | 663 | { |
3d7a244b | 664 | break; |
0eff9f65 AS |
665 | } |
666 | certificate = cert->cert; | |
997358a6 | 667 | DBG(DBG_CONTROL, |
0eff9f65 | 668 | DBG_log("candidate: '%Y'", certificate->get_subject(certificate)); |
997358a6 | 669 | ) |
3d7a244b AS |
670 | |
671 | if (cert->smartcard) | |
997358a6 | 672 | { |
3d7a244b AS |
673 | /* look for a matching private key on a smartcard */ |
674 | smartcard_t *sc = scx_get(cert); | |
675 | ||
0354d570 | 676 | if (sc) |
3d7a244b AS |
677 | { |
678 | DBG(DBG_CONTROL, | |
679 | DBG_log("matching smartcard found") | |
680 | ) | |
681 | if (sc->valid) | |
682 | { | |
683 | ocsp_requestor_cert = cert; | |
684 | ocsp_requestor_sc = sc; | |
685 | return TRUE; | |
686 | } | |
687 | plog("unable to sign ocsp request without PIN"); | |
688 | } | |
997358a6 | 689 | } |
3d7a244b AS |
690 | else |
691 | { | |
692 | /* look for a matching private key in the chained list */ | |
8b799d55 | 693 | private_key_t *private = get_x509_private_key(cert); |
997358a6 | 694 | |
0354d570 | 695 | if (private) |
3d7a244b AS |
696 | { |
697 | DBG(DBG_CONTROL, | |
698 | DBG_log("matching private key found") | |
699 | ) | |
700 | ocsp_requestor_cert = cert; | |
8b799d55 | 701 | ocsp_requestor_key = private; |
3d7a244b AS |
702 | return TRUE; |
703 | } | |
704 | } | |
997358a6 | 705 | } |
3d7a244b | 706 | return FALSE; |
997358a6 MW |
707 | } |
708 | ||
8b799d55 | 709 | static chunk_t sc_build_sha1_signature(chunk_t tbs, smartcard_t *sc) |
997358a6 | 710 | { |
8b799d55 | 711 | hasher_t *hasher; |
3d7a244b | 712 | u_char *pos; |
3b878dae | 713 | chunk_t digest; |
8b799d55 | 714 | chunk_t digest_info, sigdata; |
3d7a244b | 715 | size_t siglen = 0; |
997358a6 | 716 | |
8b799d55 | 717 | if (!scx_establish_context(sc) || !scx_login(sc)) |
997358a6 | 718 | { |
8b799d55 AS |
719 | scx_release_context(sc); |
720 | return chunk_empty; | |
721 | } | |
997358a6 | 722 | |
8b799d55 | 723 | siglen = scx_get_keylength(sc); |
997358a6 | 724 | |
8b799d55 AS |
725 | if (siglen == 0) |
726 | { | |
727 | plog("failed to get keylength from smartcard"); | |
728 | scx_release_context(sc); | |
729 | return chunk_empty; | |
730 | } | |
997358a6 | 731 | |
8b799d55 AS |
732 | DBG(DBG_CONTROL | DBG_CRYPT, |
733 | DBG_log("signing hash with RSA key from smartcard (slot: %d, id: %s)" | |
734 | , (int)sc->slot, sc->id) | |
735 | ) | |
997358a6 | 736 | |
8b799d55 AS |
737 | hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); |
738 | if (hasher == NULL) | |
739 | { | |
740 | return chunk_empty; | |
3d7a244b | 741 | } |
3b878dae | 742 | hasher->allocate_hash(hasher, tbs, &digest); |
8b799d55 AS |
743 | hasher->destroy(hasher); |
744 | ||
745 | /* according to PKCS#1 v2.1 digest must be packaged into | |
746 | * an ASN.1 structure for encryption | |
747 | */ | |
eb73685d | 748 | digest_info = asn1_wrap(ASN1_SEQUENCE, "mm" |
8b799d55 | 749 | , asn1_algorithmIdentifier(OID_SHA1) |
3b878dae | 750 | , asn1_wrap(ASN1_OCTET_STRING, "m", digest)); |
8b799d55 AS |
751 | |
752 | pos = asn1_build_object(&sigdata, ASN1_BIT_STRING, 1 + siglen); | |
753 | *pos++ = 0x00; | |
754 | scx_sign_hash(sc, digest_info.ptr, digest_info.len, pos, siglen); | |
755 | free(digest_info.ptr); | |
756 | ||
757 | if (!pkcs11_keep_state) | |
3d7a244b | 758 | { |
8b799d55 | 759 | scx_release_context(sc); |
3d7a244b AS |
760 | } |
761 | return sigdata; | |
997358a6 MW |
762 | } |
763 | ||
08b2d288 AS |
764 | /** |
765 | * build signature into ocsp request gets built only if a request cert | |
766 | * with a corresponding private key is found | |
997358a6 | 767 | */ |
08b2d288 | 768 | static chunk_t build_signature(chunk_t tbsRequest) |
997358a6 | 769 | { |
0406eeaa | 770 | chunk_t sigdata, cert, certs = chunk_empty; |
3d7a244b | 771 | |
0354d570 | 772 | if (ocsp_requestor_sc) |
8b799d55 AS |
773 | { |
774 | /* RSA signature is done on smartcard */ | |
775 | sigdata = sc_build_sha1_signature(tbsRequest, ocsp_requestor_sc); | |
776 | } | |
777 | else | |
778 | { | |
779 | /* RSA signature is done in software */ | |
780 | sigdata = x509_build_signature(tbsRequest, OID_SHA1, ocsp_requestor_key, | |
781 | TRUE); | |
782 | } | |
3d7a244b | 783 | if (sigdata.ptr == NULL) |
8b799d55 | 784 | { |
3d7a244b | 785 | return chunk_empty; |
8b799d55 | 786 | } |
3d7a244b AS |
787 | |
788 | /* include our certificate */ | |
0406eeaa MW |
789 | if (ocsp_requestor_cert->cert->get_encoding(ocsp_requestor_cert->cert, |
790 | CERT_ASN1_DER, &cert)) | |
791 | { | |
792 | certs = asn1_wrap(ASN1_CONTEXT_C_0, "m", | |
793 | asn1_wrap(ASN1_SEQUENCE, "m", cert)); | |
794 | } | |
3d7a244b AS |
795 | /* build signature comprising algorithm, signature and cert */ |
796 | return asn1_wrap(ASN1_CONTEXT_C_0, "m" | |
eb73685d | 797 | , asn1_wrap(ASN1_SEQUENCE, "mmm" |
08b2d288 | 798 | , asn1_algorithmIdentifier(OID_SHA1_WITH_RSA) |
3d7a244b AS |
799 | , sigdata |
800 | , certs | |
801 | ) | |
802 | ); | |
997358a6 MW |
803 | } |
804 | ||
08b2d288 AS |
805 | /** |
806 | * Build request (into requestList) | |
997358a6 MW |
807 | * no singleRequestExtensions used |
808 | */ | |
08b2d288 | 809 | static chunk_t build_request(ocsp_location_t *location, ocsp_certinfo_t *certinfo) |
997358a6 | 810 | { |
eb73685d | 811 | chunk_t reqCert = asn1_wrap(ASN1_SEQUENCE, "mmmm" |
08b2d288 | 812 | , asn1_algorithmIdentifier(OID_SHA1) |
3d7a244b AS |
813 | , asn1_simple_object(ASN1_OCTET_STRING, location->authNameID) |
814 | , asn1_simple_object(ASN1_OCTET_STRING, location->authKeyID) | |
815 | , asn1_simple_object(ASN1_INTEGER, certinfo->serialNumber)); | |
997358a6 | 816 | |
3d7a244b | 817 | return asn1_wrap(ASN1_SEQUENCE, "m", reqCert); |
997358a6 MW |
818 | } |
819 | ||
08b2d288 | 820 | /** |
997358a6 MW |
821 | * build requestList (into TBSRequest) |
822 | */ | |
08b2d288 | 823 | static chunk_t build_request_list(ocsp_location_t *location) |
997358a6 | 824 | { |
3d7a244b AS |
825 | chunk_t requestList; |
826 | request_list_t *reqs = NULL; | |
827 | ocsp_certinfo_t *certinfo = location->certinfo; | |
828 | u_char *pos; | |
997358a6 | 829 | |
3d7a244b | 830 | size_t datalen = 0; |
997358a6 | 831 | |
3d7a244b | 832 | /* build content */ |
0354d570 | 833 | while (certinfo) |
3d7a244b AS |
834 | { |
835 | /* build request for every certificate in list | |
836 | * and store them in a chained list | |
837 | */ | |
838 | request_list_t *req = malloc_thing(request_list_t); | |
997358a6 | 839 | |
3d7a244b AS |
840 | req->request = build_request(location, certinfo); |
841 | req->next = reqs; | |
842 | reqs = req; | |
997358a6 | 843 | |
3d7a244b AS |
844 | datalen += req->request.len; |
845 | certinfo = certinfo->next; | |
846 | } | |
997358a6 | 847 | |
08b2d288 | 848 | pos = asn1_build_object(&requestList, ASN1_SEQUENCE, datalen); |
3d7a244b AS |
849 | |
850 | /* copy all in chained list, free list afterwards */ | |
0354d570 | 851 | while (reqs) |
3d7a244b AS |
852 | { |
853 | request_list_t *req = reqs; | |
854 | ||
855 | mv_chunk(&pos, req->request); | |
856 | reqs = reqs->next; | |
857 | free(req); | |
858 | } | |
997358a6 | 859 | |
3d7a244b | 860 | return requestList; |
997358a6 MW |
861 | } |
862 | ||
08b2d288 AS |
863 | /** |
864 | * Build requestorName (into TBSRequest) | |
997358a6 | 865 | */ |
08b2d288 | 866 | static chunk_t build_requestor_name(void) |
997358a6 | 867 | { |
0eff9f65 AS |
868 | certificate_t *certificate = ocsp_requestor_cert->cert; |
869 | identification_t *subject = certificate->get_subject(certificate); | |
870 | ||
3d7a244b AS |
871 | return asn1_wrap(ASN1_CONTEXT_C_1, "m" |
872 | , asn1_simple_object(ASN1_CONTEXT_C_4 | |
0eff9f65 | 873 | , subject->get_encoding(subject))); |
997358a6 MW |
874 | } |
875 | ||
08b2d288 | 876 | /** |
997358a6 MW |
877 | * build nonce extension (into requestExtensions) |
878 | */ | |
08b2d288 | 879 | static chunk_t build_nonce_extension(ocsp_location_t *location) |
997358a6 | 880 | { |
bc2e33ca AS |
881 | rng_t *rng; |
882 | ||
3d7a244b AS |
883 | /* generate a random nonce */ |
884 | location->nonce.ptr = malloc(NONCE_LENGTH), | |
885 | location->nonce.len = NONCE_LENGTH; | |
bc2e33ca AS |
886 | rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); |
887 | rng->get_bytes(rng, location->nonce.len, location->nonce.ptr); | |
888 | rng->destroy(rng); | |
3d7a244b AS |
889 | |
890 | return asn1_wrap(ASN1_SEQUENCE, "cm" | |
891 | , ASN1_nonce_oid | |
892 | , asn1_simple_object(ASN1_OCTET_STRING, location->nonce)); | |
997358a6 MW |
893 | } |
894 | ||
08b2d288 AS |
895 | /** |
896 | * Build requestExtensions (into TBSRequest) | |
997358a6 | 897 | */ |
08b2d288 | 898 | static chunk_t build_request_ext(ocsp_location_t *location) |
997358a6 | 899 | { |
3d7a244b AS |
900 | return asn1_wrap(ASN1_CONTEXT_C_2, "m" |
901 | , asn1_wrap(ASN1_SEQUENCE, "mm" | |
902 | , build_nonce_extension(location) | |
903 | , asn1_wrap(ASN1_SEQUENCE, "cc" | |
904 | , ASN1_response_oid | |
905 | , ASN1_response_content | |
906 | ) | |
907 | ) | |
908 | ); | |
997358a6 MW |
909 | } |
910 | ||
08b2d288 AS |
911 | /** |
912 | * Build TBSRequest (into OCSPRequest) | |
997358a6 | 913 | */ |
08b2d288 | 914 | static chunk_t build_tbs_request(ocsp_location_t *location, bool has_requestor_cert) |
997358a6 | 915 | { |
3d7a244b AS |
916 | /* version is skipped since the default is ok */ |
917 | return asn1_wrap(ASN1_SEQUENCE, "mmm" | |
918 | , (has_requestor_cert) | |
919 | ? build_requestor_name() | |
920 | : chunk_empty | |
921 | , build_request_list(location) | |
922 | , build_request_ext(location)); | |
997358a6 MW |
923 | } |
924 | ||
08b2d288 AS |
925 | /** |
926 | * Assembles an ocsp request to given location | |
997358a6 MW |
927 | * and sets nonce field in location to the sent nonce |
928 | */ | |
08b2d288 | 929 | chunk_t build_ocsp_request(ocsp_location_t *location) |
997358a6 | 930 | { |
3d7a244b AS |
931 | bool has_requestor_cert; |
932 | chunk_t tbsRequest, signature; | |
3d7a244b AS |
933 | |
934 | DBG(DBG_CONTROL, | |
935 | DBG_log("assembling ocsp request"); | |
0354d570 AS |
936 | DBG_log("issuer: \"%Y\"", location->issuer); |
937 | if (location->authKeyID.ptr) | |
3d7a244b | 938 | { |
0354d570 | 939 | DBG_log("authkey: %#B", &location->authKeyID); |
3d7a244b AS |
940 | } |
941 | ) | |
942 | lock_certs_and_keys("build_ocsp_request"); | |
997358a6 | 943 | |
3d7a244b AS |
944 | /* looks for requestor cert and matching private key */ |
945 | has_requestor_cert = get_ocsp_requestor_cert(location); | |
997358a6 | 946 | |
3d7a244b AS |
947 | /* build content */ |
948 | tbsRequest = build_tbs_request(location, has_requestor_cert); | |
997358a6 | 949 | |
3d7a244b AS |
950 | /* sign tbsReuqest */ |
951 | signature = (has_requestor_cert)? build_signature(tbsRequest) | |
952 | : chunk_empty; | |
997358a6 | 953 | |
3d7a244b | 954 | unlock_certs_and_keys("build_ocsp_request"); |
997358a6 | 955 | |
3d7a244b AS |
956 | return asn1_wrap(ASN1_SEQUENCE, "mm" |
957 | , tbsRequest | |
958 | , signature); | |
997358a6 MW |
959 | } |
960 | ||
08b2d288 AS |
961 | /** |
962 | * Check if the OCSP response has a valid signature | |
997358a6 | 963 | */ |
08b2d288 | 964 | static bool valid_ocsp_response(response_t *res) |
997358a6 | 965 | { |
4c68a85a | 966 | int pathlen, pathlen_constraint; |
f565d0c5 | 967 | cert_t *authcert; |
997358a6 | 968 | |
3d7a244b | 969 | lock_authcert_list("valid_ocsp_response"); |
997358a6 | 970 | |
0eff9f65 | 971 | authcert = get_authcert(res->responder_id_name, res->responder_id_key, |
fc12e3cd | 972 | X509_OCSP_SIGNER | X509_CA); |
997358a6 MW |
973 | if (authcert == NULL) |
974 | { | |
3d7a244b AS |
975 | plog("no matching ocsp signer cert found"); |
976 | unlock_authcert_list("valid_ocsp_response"); | |
977 | return FALSE; | |
997358a6 MW |
978 | } |
979 | DBG(DBG_CONTROL, | |
3d7a244b | 980 | DBG_log("ocsp signer cert found") |
997358a6 MW |
981 | ) |
982 | ||
0eff9f65 AS |
983 | if (!x509_check_signature(res->tbs, res->signature, res->algorithm, |
984 | authcert->cert)) | |
997358a6 | 985 | { |
3d7a244b AS |
986 | plog("signature of ocsp response is invalid"); |
987 | unlock_authcert_list("valid_ocsp_response"); | |
988 | return FALSE; | |
997358a6 MW |
989 | } |
990 | DBG(DBG_CONTROL, | |
3d7a244b | 991 | DBG_log("signature of ocsp response is valid") |
997358a6 MW |
992 | ) |
993 | ||
3d7a244b | 994 | |
4c68a85a | 995 | for (pathlen = -1; pathlen <= X509_MAX_PATH_LEN; pathlen++) |
997358a6 | 996 | { |
f565d0c5 | 997 | cert_t *cert = authcert; |
0eff9f65 AS |
998 | certificate_t *certificate = cert->cert; |
999 | x509_t *x509 = (x509_t*)certificate; | |
1000 | identification_t *subject = certificate->get_subject(certificate); | |
1001 | identification_t *issuer = certificate->get_issuer(certificate); | |
1002 | chunk_t authKeyID = x509->get_authKeyIdentifier(x509); | |
c4570d18 | 1003 | time_t not_before, not_after; |
3d7a244b AS |
1004 | |
1005 | DBG(DBG_CONTROL, | |
0eff9f65 AS |
1006 | DBG_log("subject: '%Y'", subject); |
1007 | DBG_log("issuer: '%Y'", issuer); | |
cf85e131 | 1008 | if (authKeyID.ptr) |
3d7a244b | 1009 | { |
0eff9f65 | 1010 | DBG_log("authkey: %#B", &authKeyID); |
3d7a244b AS |
1011 | } |
1012 | ) | |
1013 | ||
c4570d18 | 1014 | if (!certificate->get_validity(certificate, NULL, ¬_before, ¬_after)) |
3d7a244b | 1015 | { |
c4570d18 AS |
1016 | plog("certificate is invalid (valid from %T to %T)", |
1017 | ¬_before, FALSE, ¬_after, FALSE); | |
0406eeaa | 1018 | |
3d7a244b AS |
1019 | unlock_authcert_list("valid_ocsp_response"); |
1020 | return FALSE; | |
1021 | } | |
3d7a244b AS |
1022 | DBG(DBG_CONTROL, |
1023 | DBG_log("certificate is valid") | |
1024 | ) | |
7daf5226 | 1025 | |
0354d570 | 1026 | authcert = get_authcert(issuer, authKeyID, X509_CA); |
3d7a244b AS |
1027 | if (authcert == NULL) |
1028 | { | |
1029 | plog("issuer cacert not found"); | |
1030 | unlock_authcert_list("valid_ocsp_response"); | |
1031 | return FALSE; | |
1032 | } | |
1033 | DBG(DBG_CONTROL, | |
1034 | DBG_log("issuer cacert found") | |
1035 | ) | |
1036 | ||
0eff9f65 | 1037 | if (!certificate->issued_by(certificate, authcert->cert)) |
3d7a244b AS |
1038 | { |
1039 | plog("certificate signature is invalid"); | |
1040 | unlock_authcert_list("valid_ocsp_response"); | |
1041 | return FALSE; | |
1042 | } | |
1043 | DBG(DBG_CONTROL, | |
1044 | DBG_log("certificate signature is valid") | |
1045 | ) | |
1046 | ||
4c68a85a AS |
1047 | /* check path length constraint */ |
1048 | pathlen_constraint = x509->get_pathLenConstraint(x509); | |
1049 | if (pathlen_constraint != X509_NO_PATH_LEN_CONSTRAINT && | |
1050 | pathlen > pathlen_constraint) | |
1051 | { | |
1052 | plog("path length of %d violates constraint of %d", | |
1053 | pathlen, pathlen_constraint); | |
1054 | return FALSE; | |
1055 | } | |
1056 | ||
3d7a244b | 1057 | /* check if cert is self-signed */ |
0eff9f65 | 1058 | if (x509->get_flags(x509) & X509_SELF_SIGNED) |
3d7a244b AS |
1059 | { |
1060 | DBG(DBG_CONTROL, | |
4c68a85a AS |
1061 | DBG_log("reached self-signed root ca with a path length of %d", |
1062 | pathlen) | |
3d7a244b AS |
1063 | ) |
1064 | unlock_authcert_list("valid_ocsp_response"); | |
1065 | return TRUE; | |
1066 | } | |
997358a6 | 1067 | } |
4c68a85a | 1068 | plog("maximum path length of %d exceeded", X509_MAX_PATH_LEN); |
3d7a244b AS |
1069 | unlock_authcert_list("valid_ocsp_response"); |
1070 | return FALSE; | |
997358a6 MW |
1071 | } |
1072 | ||
08b2d288 AS |
1073 | /** |
1074 | * Parse a basic OCSP response | |
997358a6 | 1075 | */ |
08b2d288 | 1076 | static bool parse_basic_ocsp_response(chunk_t blob, int level0, response_t *res) |
997358a6 | 1077 | { |
08b2d288 | 1078 | asn1_parser_t *parser; |
3d7a244b | 1079 | chunk_t object; |
08b2d288 | 1080 | u_int version; |
08b2d288 | 1081 | int objectID; |
3d7a244b | 1082 | int extn_oid = OID_UNKNOWN; |
08b2d288 AS |
1083 | bool success = FALSE; |
1084 | bool critical; | |
3d7a244b | 1085 | |
08b2d288 AS |
1086 | parser = asn1_parser_create(basicResponseObjects, blob); |
1087 | parser->set_top_level(parser, level0); | |
3d7a244b | 1088 | |
08b2d288 | 1089 | while (parser->iterate(parser, &objectID, &object)) |
997358a6 | 1090 | { |
3d7a244b | 1091 | switch (objectID) |
997358a6 | 1092 | { |
3d7a244b AS |
1093 | case BASIC_RESPONSE_TBS_DATA: |
1094 | res->tbs = object; | |
1095 | break; | |
1096 | case BASIC_RESPONSE_VERSION: | |
1097 | version = (object.len)? (1 + (u_int)*object.ptr) : 1; | |
1098 | if (version != OCSP_BASIC_RESPONSE_VERSION) | |
1099 | { | |
1100 | plog("wrong ocsp basic response version (version= %i)", version); | |
08b2d288 | 1101 | goto end; |
3d7a244b AS |
1102 | } |
1103 | break; | |
1104 | case BASIC_RESPONSE_ID_BY_NAME: | |
0354d570 AS |
1105 | res->responder_id_name = identification_create_from_encoding( |
1106 | ID_DER_ASN1_DN, object); | |
3d7a244b | 1107 | DBG(DBG_PARSING, |
0354d570 | 1108 | DBG_log(" '%Y'", res->responder_id_name) |
3d7a244b AS |
1109 | ) |
1110 | break; | |
1111 | case BASIC_RESPONSE_ID_BY_KEY: | |
1112 | res->responder_id_key = object; | |
1113 | break; | |
1114 | case BASIC_RESPONSE_PRODUCED_AT: | |
08b2d288 | 1115 | res->produced_at = asn1_to_time(&object, ASN1_GENERALIZEDTIME); |
3d7a244b AS |
1116 | break; |
1117 | case BASIC_RESPONSE_RESPONSES: | |
1118 | res->responses = object; | |
1119 | break; | |
1120 | case BASIC_RESPONSE_EXT_ID: | |
1121 | extn_oid = asn1_known_oid(object); | |
1122 | break; | |
1123 | case BASIC_RESPONSE_CRITICAL: | |
1124 | critical = object.len && *object.ptr; | |
1125 | DBG(DBG_PARSING, | |
1126 | DBG_log(" %s",(critical)?"TRUE":"FALSE"); | |
1127 | ) | |
1128 | break; | |
1129 | case BASIC_RESPONSE_EXT_VALUE: | |
1130 | if (extn_oid == OID_NONCE) | |
1131 | res->nonce = object; | |
1132 | break; | |
1133 | case BASIC_RESPONSE_ALGORITHM: | |
08b2d288 AS |
1134 | res->algorithm = asn1_parse_algorithmIdentifier(object, |
1135 | parser->get_level(parser)+1, NULL); | |
3d7a244b AS |
1136 | break; |
1137 | case BASIC_RESPONSE_SIGNATURE: | |
1138 | res->signature = object; | |
1139 | break; | |
1140 | case BASIC_RESPONSE_CERTIFICATE: | |
1141 | { | |
f565d0c5 | 1142 | cert_t *cert = malloc_thing(cert_t); |
0eff9f65 | 1143 | x509_t *x509; |
3d7a244b | 1144 | |
f565d0c5 | 1145 | *cert = cert_empty; |
0eff9f65 AS |
1146 | cert->cert = lib->creds->create(lib->creds, |
1147 | CRED_CERTIFICATE, CERT_X509, | |
1148 | BUILD_BLOB_ASN1_DER, object, | |
1149 | BUILD_END); | |
1150 | if (cert->cert == NULL) | |
1151 | { | |
1152 | DBG(DBG_CONTROL | DBG_PARSING, | |
1153 | DBG_log("parsing of embedded ocsp certificate failed") | |
1154 | ) | |
f565d0c5 | 1155 | cert_free(cert); |
0eff9f65 AS |
1156 | break; |
1157 | } | |
0eff9f65 | 1158 | x509 = (x509_t*)cert->cert; |
0406eeaa | 1159 | |
0eff9f65 AS |
1160 | if ((x509->get_flags(x509) & X509_OCSP_SIGNER) && |
1161 | trust_authcert_candidate(cert, NULL)) | |
3d7a244b | 1162 | { |
fc12e3cd | 1163 | add_authcert(cert, X509_OCSP_SIGNER); |
3d7a244b AS |
1164 | } |
1165 | else | |
1166 | { | |
1167 | DBG(DBG_CONTROL | DBG_PARSING, | |
1168 | DBG_log("embedded ocsp certificate rejected") | |
1169 | ) | |
f565d0c5 | 1170 | cert_free(cert); |
3d7a244b AS |
1171 | } |
1172 | } | |
1173 | break; | |
997358a6 | 1174 | } |
997358a6 | 1175 | } |
08b2d288 AS |
1176 | success = parser->success(parser); |
1177 | ||
1178 | end: | |
1179 | parser->destroy(parser); | |
1180 | return success; | |
1181 | ||
997358a6 MW |
1182 | } |
1183 | ||
1184 | ||
08b2d288 AS |
1185 | /** |
1186 | * Parse an ocsp response and return the result as a response_t struct | |
997358a6 | 1187 | */ |
08b2d288 | 1188 | static response_status parse_ocsp_response(chunk_t blob, response_t * res) |
997358a6 | 1189 | { |
08b2d288 | 1190 | asn1_parser_t *parser; |
3d7a244b | 1191 | chunk_t object; |
08b2d288 | 1192 | int objectID; |
3d7a244b | 1193 | int ocspResponseType = OID_UNKNOWN; |
08b2d288 | 1194 | bool success = FALSE; |
3d7a244b AS |
1195 | response_status rStatus = STATUS_INTERNALERROR; |
1196 | ||
08b2d288 | 1197 | parser = asn1_parser_create(ocspResponseObjects, blob); |
3d7a244b | 1198 | |
08b2d288 | 1199 | while (parser->iterate(parser, &objectID, &object)) |
3d7a244b | 1200 | { |
3d7a244b AS |
1201 | switch (objectID) { |
1202 | case OCSP_RESPONSE_STATUS: | |
1203 | rStatus = (response_status) *object.ptr; | |
1204 | ||
1205 | switch (rStatus) | |
1206 | { | |
1207 | case STATUS_SUCCESSFUL: | |
1208 | break; | |
1209 | case STATUS_MALFORMEDREQUEST: | |
1210 | case STATUS_INTERNALERROR: | |
1211 | case STATUS_TRYLATER: | |
1212 | case STATUS_SIGREQUIRED: | |
1213 | case STATUS_UNAUTHORIZED: | |
1214 | plog("ocsp response: server said '%s'" | |
1215 | , response_status_names[rStatus]); | |
08b2d288 | 1216 | goto end; |
3d7a244b | 1217 | default: |
08b2d288 | 1218 | goto end; |
3d7a244b AS |
1219 | } |
1220 | break; | |
1221 | case OCSP_RESPONSE_TYPE: | |
1222 | ocspResponseType = asn1_known_oid(object); | |
1223 | break; | |
1224 | case OCSP_RESPONSE: | |
1225 | { | |
1226 | switch (ocspResponseType) { | |
1227 | case OID_BASIC: | |
08b2d288 AS |
1228 | success = parse_basic_ocsp_response(object, |
1229 | parser->get_level(parser)+1, res); | |
3d7a244b AS |
1230 | break; |
1231 | default: | |
1232 | DBG(DBG_CONTROL, | |
1233 | DBG_log("ocsp response is not of type BASIC"); | |
1234 | DBG_dump_chunk("ocsp response OID: ", object); | |
1235 | ) | |
08b2d288 | 1236 | goto end; |
3d7a244b AS |
1237 | } |
1238 | } | |
1239 | break; | |
997358a6 | 1240 | } |
997358a6 | 1241 | } |
08b2d288 AS |
1242 | success &= parser->success(parser); |
1243 | ||
1244 | end: | |
1245 | parser->destroy(parser); | |
3d7a244b | 1246 | return rStatus; |
997358a6 MW |
1247 | } |
1248 | ||
08b2d288 AS |
1249 | /** |
1250 | * Parse a basic OCSP response | |
997358a6 | 1251 | */ |
08b2d288 AS |
1252 | static bool parse_ocsp_single_response(chunk_t blob, int level0, |
1253 | single_response_t *sres) | |
997358a6 | 1254 | { |
08b2d288 | 1255 | asn1_parser_t *parser; |
3d7a244b | 1256 | chunk_t object; |
08b2d288 AS |
1257 | u_int extn_oid; |
1258 | int objectID; | |
1259 | bool critical; | |
1260 | bool success = FALSE; | |
997358a6 | 1261 | |
08b2d288 AS |
1262 | parser = asn1_parser_create(singleResponseObjects, blob); |
1263 | parser->set_top_level(parser, level0); | |
997358a6 | 1264 | |
08b2d288 | 1265 | while (parser->iterate(parser, &objectID, &object)) |
997358a6 | 1266 | { |
3d7a244b AS |
1267 | switch (objectID) |
1268 | { | |
1269 | case SINGLE_RESPONSE_ALGORITHM: | |
08b2d288 AS |
1270 | sres->hash_algorithm = asn1_parse_algorithmIdentifier(object, |
1271 | parser->get_level(parser)+1, NULL); | |
3d7a244b AS |
1272 | break; |
1273 | case SINGLE_RESPONSE_ISSUER_NAME_HASH: | |
1274 | sres->issuer_name_hash = object; | |
1275 | break; | |
1276 | case SINGLE_RESPONSE_ISSUER_KEY_HASH: | |
1277 | sres->issuer_key_hash = object; | |
1278 | break; | |
1279 | case SINGLE_RESPONSE_SERIAL_NUMBER: | |
1280 | sres->serialNumber = object; | |
1281 | break; | |
1282 | case SINGLE_RESPONSE_CERT_STATUS_GOOD: | |
1283 | sres->status = CERT_GOOD; | |
1284 | break; | |
1285 | case SINGLE_RESPONSE_CERT_STATUS_REVOKED: | |
1286 | sres->status = CERT_REVOKED; | |
1287 | break; | |
1288 | case SINGLE_RESPONSE_CERT_STATUS_REVOCATION_TIME: | |
08b2d288 | 1289 | sres->revocationTime = asn1_to_time(&object, ASN1_GENERALIZEDTIME); |
3d7a244b AS |
1290 | break; |
1291 | case SINGLE_RESPONSE_CERT_STATUS_CRL_REASON: | |
1292 | sres->revocationReason = (object.len == 1) | |
f561d6f2 | 1293 | ? *object.ptr : CRL_REASON_UNSPECIFIED; |
3d7a244b AS |
1294 | break; |
1295 | case SINGLE_RESPONSE_CERT_STATUS_UNKNOWN: | |
1296 | sres->status = CERT_UNKNOWN; | |
1297 | break; | |
1298 | case SINGLE_RESPONSE_THIS_UPDATE: | |
08b2d288 | 1299 | sres->thisUpdate = asn1_to_time(&object, ASN1_GENERALIZEDTIME); |
3d7a244b AS |
1300 | break; |
1301 | case SINGLE_RESPONSE_NEXT_UPDATE: | |
08b2d288 | 1302 | sres->nextUpdate = asn1_to_time(&object, ASN1_GENERALIZEDTIME); |
3d7a244b AS |
1303 | break; |
1304 | case SINGLE_RESPONSE_EXT_ID: | |
1305 | extn_oid = asn1_known_oid(object); | |
1306 | break; | |
1307 | case SINGLE_RESPONSE_CRITICAL: | |
1308 | critical = object.len && *object.ptr; | |
1309 | DBG(DBG_PARSING, | |
1310 | DBG_log(" %s",(critical)?"TRUE":"FALSE"); | |
1311 | ) | |
1312 | case SINGLE_RESPONSE_EXT_VALUE: | |
1313 | break; | |
1314 | } | |
997358a6 | 1315 | } |
08b2d288 AS |
1316 | success = parser->success(parser); |
1317 | parser->destroy(parser); | |
1318 | return success; | |
997358a6 MW |
1319 | } |
1320 | ||
08b2d288 AS |
1321 | /** |
1322 | * Add an ocsp location to a chained list | |
997358a6 | 1323 | */ |
08b2d288 AS |
1324 | ocsp_location_t* add_ocsp_location(const ocsp_location_t *loc, |
1325 | ocsp_location_t **chain) | |
997358a6 | 1326 | { |
3d7a244b | 1327 | ocsp_location_t *location = malloc_thing(ocsp_location_t); |
997358a6 | 1328 | |
3d7a244b | 1329 | /* unshare location fields */ |
0354d570 | 1330 | location->issuer = loc->issuer->clone(loc->issuer); |
3d7a244b AS |
1331 | location->authNameID = chunk_clone(loc->authNameID); |
1332 | location->authKeyID = chunk_clone(loc->authKeyID); | |
0eff9f65 | 1333 | location->uri = strdup(loc->uri); |
3d7a244b | 1334 | location->certinfo = NULL; |
997358a6 | 1335 | |
3d7a244b AS |
1336 | /* insert new ocsp location in front of chain */ |
1337 | location->next = *chain; | |
1338 | *chain = location; | |
997358a6 | 1339 | |
3d7a244b AS |
1340 | DBG(DBG_CONTROL, |
1341 | DBG_log("new ocsp location added") | |
1342 | ) | |
997358a6 | 1343 | |
3d7a244b | 1344 | return location; |
997358a6 MW |
1345 | } |
1346 | ||
08b2d288 | 1347 | /** |
997358a6 MW |
1348 | * add a certinfo struct to a chained list |
1349 | */ | |
08b2d288 AS |
1350 | void add_certinfo(ocsp_location_t *loc, ocsp_certinfo_t *info, |
1351 | ocsp_location_t **chain, bool request) | |
997358a6 | 1352 | { |
3d7a244b AS |
1353 | ocsp_location_t *location; |
1354 | ocsp_certinfo_t *certinfo, **certinfop; | |
1355 | char buf[BUF_LEN]; | |
1356 | time_t now; | |
1357 | int cmp = -1; | |
1358 | ||
1359 | location = get_ocsp_location(loc, *chain); | |
1360 | if (location == NULL) | |
08b2d288 | 1361 | { |
3d7a244b | 1362 | location = add_ocsp_location(loc, chain); |
08b2d288 | 1363 | } |
3d7a244b AS |
1364 | |
1365 | /* traverse list of certinfos in increasing order */ | |
1366 | certinfop = &location->certinfo; | |
997358a6 | 1367 | certinfo = *certinfop; |
3d7a244b | 1368 | |
0354d570 | 1369 | while (certinfo) |
3d7a244b AS |
1370 | { |
1371 | cmp = chunk_compare(info->serialNumber, certinfo->serialNumber); | |
1372 | if (cmp <= 0) | |
1373 | break; | |
1374 | certinfop = &certinfo->next; | |
1375 | certinfo = *certinfop; | |
1376 | } | |
1377 | ||
997358a6 | 1378 | if (cmp != 0) |
3d7a244b AS |
1379 | { |
1380 | /* add a new certinfo entry */ | |
1381 | ocsp_certinfo_t *cnew = malloc_thing(ocsp_certinfo_t); | |
1382 | ||
1383 | cnew->serialNumber = chunk_clone(info->serialNumber); | |
1384 | cnew->next = certinfo; | |
20afe5e9 | 1385 | cnew->trials = 0; |
3d7a244b AS |
1386 | *certinfop = cnew; |
1387 | certinfo = cnew; | |
1388 | } | |
7daf5226 | 1389 | |
3d7a244b AS |
1390 | DBG(DBG_CONTROL, |
1391 | datatot(info->serialNumber.ptr, info->serialNumber.len, ':' | |
1392 | , buf, BUF_LEN); | |
1393 | DBG_log("ocsp %s for serial %s %s" | |
1394 | , request?"fetch request":"certinfo" | |
1395 | , buf | |
1396 | , (cmp == 0)? (request?"already exists":"updated"):"added") | |
1397 | ) | |
1398 | ||
1399 | time(&now); | |
1400 | ||
1401 | if (request) | |
1402 | { | |
1403 | certinfo->status = CERT_UNDEFINED; | |
7daf5226 | 1404 | |
3d7a244b | 1405 | if (cmp != 0) |
08b2d288 | 1406 | { |
3d7a244b | 1407 | certinfo->thisUpdate = now; |
08b2d288 | 1408 | } |
3d7a244b AS |
1409 | certinfo->nextUpdate = UNDEFINED_TIME; |
1410 | } | |
1411 | else | |
1412 | { | |
1413 | certinfo->status = info->status; | |
1414 | certinfo->revocationTime = info->revocationTime; | |
1415 | certinfo->revocationReason = info->revocationReason; | |
7daf5226 | 1416 | |
3d7a244b AS |
1417 | certinfo->thisUpdate = (info->thisUpdate != UNDEFINED_TIME)? |
1418 | info->thisUpdate : now; | |
997358a6 | 1419 | |
3d7a244b | 1420 | certinfo->once = (info->nextUpdate == UNDEFINED_TIME); |
997358a6 | 1421 | |
3d7a244b AS |
1422 | certinfo->nextUpdate = (certinfo->once)? |
1423 | (now + OCSP_DEFAULT_VALID_TIME) : info->nextUpdate; | |
1424 | } | |
997358a6 MW |
1425 | } |
1426 | ||
08b2d288 AS |
1427 | /** |
1428 | * Process received ocsp single response and add it to ocsp cache | |
997358a6 | 1429 | */ |
08b2d288 AS |
1430 | static void process_single_response(ocsp_location_t *location, |
1431 | single_response_t *sres) | |
997358a6 | 1432 | { |
3d7a244b AS |
1433 | ocsp_certinfo_t *certinfo, **certinfop; |
1434 | int cmp = -1; | |
1435 | ||
1436 | if (sres->hash_algorithm != OID_SHA1) | |
1437 | { | |
1438 | plog("only SHA-1 hash supported in OCSP single response"); | |
1439 | return; | |
1440 | } | |
1441 | if (!(chunk_equals(sres->issuer_name_hash, location->authNameID) | |
1442 | && chunk_equals(sres->issuer_key_hash, location->authKeyID))) | |
1443 | { | |
1444 | plog("ocsp single response has wrong issuer"); | |
1445 | return; | |
1446 | } | |
7daf5226 | 1447 | |
3d7a244b AS |
1448 | /* traverse list of certinfos in increasing order */ |
1449 | certinfop = &location->certinfo; | |
997358a6 | 1450 | certinfo = *certinfop; |
3d7a244b | 1451 | |
0354d570 | 1452 | while (certinfo) |
3d7a244b AS |
1453 | { |
1454 | cmp = chunk_compare(sres->serialNumber, certinfo->serialNumber); | |
1455 | if (cmp <= 0) | |
1456 | break; | |
1457 | certinfop = &certinfo->next; | |
1458 | certinfo = *certinfop; | |
1459 | } | |
1460 | ||
1461 | if (cmp != 0) | |
1462 | { | |
1463 | plog("received unrequested cert status from ocsp server"); | |
1464 | return; | |
1465 | } | |
1466 | ||
1467 | /* unlink cert from ocsp fetch request list */ | |
1468 | *certinfop = certinfo->next; | |
7daf5226 | 1469 | |
3d7a244b AS |
1470 | /* update certinfo using the single response information */ |
1471 | certinfo->thisUpdate = sres->thisUpdate; | |
1472 | certinfo->nextUpdate = sres->nextUpdate; | |
1473 | certinfo->status = sres->status; | |
1474 | certinfo->revocationTime = sres->revocationTime; | |
1475 | certinfo->revocationReason = sres->revocationReason; | |
7daf5226 | 1476 | |
3d7a244b AS |
1477 | /* add or update certinfo in ocsp cache */ |
1478 | lock_ocsp_cache("process_single_response"); | |
1479 | add_certinfo(location, certinfo, &ocsp_cache, FALSE); | |
1480 | unlock_ocsp_cache("process_single_response"); | |
1481 | ||
1482 | /* free certinfo unlinked from ocsp fetch request list */ | |
1483 | free_certinfo(certinfo); | |
997358a6 MW |
1484 | } |
1485 | ||
f893bce3 AS |
1486 | /** |
1487 | * Destroy a response_t object | |
1488 | */ | |
1489 | static void free_response(response_t *res) | |
1490 | { | |
1491 | DESTROY_IF(res->responder_id_name); | |
1492 | } | |
1493 | ||
08b2d288 AS |
1494 | /** |
1495 | * Parse and verify ocsp response and update the ocsp cache | |
997358a6 | 1496 | */ |
08b2d288 | 1497 | void parse_ocsp(ocsp_location_t *location, chunk_t blob) |
997358a6 | 1498 | { |
3d7a244b | 1499 | response_t res = empty_response; |
997358a6 | 1500 | |
3d7a244b AS |
1501 | /* parse the ocsp response without looking at the single responses yet */ |
1502 | response_status status = parse_ocsp_response(blob, &res); | |
997358a6 | 1503 | |
3d7a244b AS |
1504 | if (status != STATUS_SUCCESSFUL) |
1505 | { | |
1506 | plog("error in ocsp response"); | |
f893bce3 | 1507 | goto free; |
3d7a244b AS |
1508 | } |
1509 | /* check if there was a nonce in the request */ | |
0354d570 | 1510 | if (location->nonce.ptr && res.nonce.ptr == NULL) |
3d7a244b AS |
1511 | { |
1512 | plog("ocsp response contains no nonce, replay attack possible"); | |
1513 | } | |
1514 | /* check if the nonce is identical */ | |
0354d570 | 1515 | if (res.nonce.ptr && !chunk_equals(res.nonce, location->nonce)) |
3d7a244b AS |
1516 | { |
1517 | plog("invalid nonce in ocsp response"); | |
f893bce3 | 1518 | goto free; |
3d7a244b AS |
1519 | } |
1520 | /* check if the response is signed by a trusted key */ | |
1521 | if (!valid_ocsp_response(&res)) | |
997358a6 | 1522 | { |
3d7a244b | 1523 | plog("invalid ocsp response"); |
f893bce3 | 1524 | goto free; |
3d7a244b AS |
1525 | } |
1526 | DBG(DBG_CONTROL, | |
1527 | DBG_log("valid ocsp response") | |
1528 | ) | |
1529 | ||
1530 | /* now parse the single responses one at a time */ | |
1531 | { | |
08b2d288 | 1532 | asn1_parser_t *parser; |
3d7a244b | 1533 | chunk_t object; |
08b2d288 | 1534 | int objectID; |
997358a6 | 1535 | |
08b2d288 | 1536 | parser = asn1_parser_create(responsesObjects, res.responses); |
3d7a244b | 1537 | |
08b2d288 | 1538 | while (parser->iterate(parser, &objectID, &object)) |
997358a6 | 1539 | { |
3d7a244b AS |
1540 | if (objectID == RESPONSES_SINGLE_RESPONSE) |
1541 | { | |
1542 | single_response_t sres = empty_single_response; | |
1543 | ||
08b2d288 | 1544 | if (!parse_ocsp_single_response(object, |
b9b8a98f | 1545 | parser->get_level(parser)+1, &sres)) |
3d7a244b | 1546 | { |
08b2d288 | 1547 | goto end; |
3d7a244b | 1548 | } |
08b2d288 | 1549 | process_single_response(location, &sres); |
3d7a244b | 1550 | } |
997358a6 | 1551 | } |
08b2d288 AS |
1552 | end: |
1553 | parser->destroy(parser); | |
997358a6 | 1554 | } |
f893bce3 AS |
1555 | |
1556 | free: | |
1557 | free_response(&res); | |
997358a6 | 1558 | } |