[ocsp] Centralise test for whether or not an OCSP check is required
[ipxe.git] / src / net / validator.c
1 /*
2 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <string.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <ipxe/refcnt.h>
30 #include <ipxe/malloc.h>
31 #include <ipxe/interface.h>
32 #include <ipxe/xfer.h>
33 #include <ipxe/open.h>
34 #include <ipxe/iobuf.h>
35 #include <ipxe/xferbuf.h>
36 #include <ipxe/process.h>
37 #include <ipxe/x509.h>
38 #include <ipxe/settings.h>
39 #include <ipxe/dhcp.h>
40 #include <ipxe/base64.h>
41 #include <ipxe/crc32.h>
42 #include <ipxe/ocsp.h>
43 #include <ipxe/validator.h>
44 #include <config/crypto.h>
45
46 /** @file
47 *
48 * Certificate validator
49 *
50 */
51
52 /** A certificate validator */
53 struct validator {
54 /** Reference count */
55 struct refcnt refcnt;
56 /** Job control interface */
57 struct interface job;
58 /** Data transfer interface */
59 struct interface xfer;
60
61 /** Process */
62 struct process process;
63
64 /** X.509 certificate chain */
65 struct x509_chain *chain;
66 /** OCSP check */
67 struct ocsp_check *ocsp;
68 /** Data buffer */
69 struct xfer_buffer buffer;
70 /** Action to take upon completed transfer */
71 int ( * done ) ( struct validator *validator, const void *data,
72 size_t len );
73 };
74
75 /**
76 * Free certificate validator
77 *
78 * @v refcnt Reference count
79 */
80 static void validator_free ( struct refcnt *refcnt ) {
81 struct validator *validator =
82 container_of ( refcnt, struct validator, refcnt );
83
84 DBGC2 ( validator, "VALIDATOR %p freed\n", validator );
85 x509_chain_put ( validator->chain );
86 ocsp_put ( validator->ocsp );
87 xferbuf_free ( &validator->buffer );
88 free ( validator );
89 }
90
91 /**
92 * Mark certificate validation as finished
93 *
94 * @v validator Certificate validator
95 * @v rc Reason for finishing
96 */
97 static void validator_finished ( struct validator *validator, int rc ) {
98
99 /* Remove process */
100 process_del ( &validator->process );
101
102 /* Close all interfaces */
103 intf_shutdown ( &validator->xfer, rc );
104 intf_shutdown ( &validator->job, rc );
105 }
106
107 /****************************************************************************
108 *
109 * Job control interface
110 *
111 */
112
113 /** Certificate validator job control interface operations */
114 static struct interface_operation validator_job_operations[] = {
115 INTF_OP ( intf_close, struct validator *, validator_finished ),
116 };
117
118 /** Certificate validator job control interface descriptor */
119 static struct interface_descriptor validator_job_desc =
120 INTF_DESC ( struct validator, job, validator_job_operations );
121
122 /****************************************************************************
123 *
124 * Cross-signing certificates
125 *
126 */
127
128 /** Cross-signed certificate source setting */
129 const struct setting crosscert_setting __setting ( SETTING_CRYPTO, crosscert )={
130 .name = "crosscert",
131 .description = "Cross-signed certificate source",
132 .tag = DHCP_EB_CROSS_CERT,
133 .type = &setting_type_string,
134 };
135
136 /** Default cross-signed certificate source */
137 static const char crosscert_default[] = CROSSCERT;
138
139 /**
140 * Append cross-signing certificates to certificate chain
141 *
142 * @v validator Certificate validator
143 * @v data Raw cross-signing certificate data
144 * @v len Length of raw data
145 * @ret rc Return status code
146 */
147 static int validator_append ( struct validator *validator,
148 const void *data, size_t len ) {
149 struct asn1_cursor cursor;
150 struct x509_chain *certs;
151 struct x509_certificate *cert;
152 struct x509_certificate *last;
153 int rc;
154
155 /* Allocate certificate list */
156 certs = x509_alloc_chain();
157 if ( ! certs ) {
158 rc = -ENOMEM;
159 goto err_alloc_certs;
160 }
161
162 /* Initialise cursor */
163 cursor.data = data;
164 cursor.len = len;
165
166 /* Enter certificateSet */
167 if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) {
168 DBGC ( validator, "VALIDATOR %p could not enter "
169 "certificateSet: %s\n", validator, strerror ( rc ) );
170 goto err_certificateset;
171 }
172
173 /* Add each certificate to list */
174 while ( cursor.len ) {
175
176 /* Add certificate to chain */
177 if ( ( rc = x509_append_raw ( certs, cursor.data,
178 cursor.len ) ) != 0 ) {
179 DBGC ( validator, "VALIDATOR %p could not append "
180 "certificate: %s\n",
181 validator, strerror ( rc) );
182 DBGC_HDA ( validator, 0, cursor.data, cursor.len );
183 return rc;
184 }
185 cert = x509_last ( certs );
186 DBGC ( validator, "VALIDATOR %p found certificate %s\n",
187 validator, x509_name ( cert ) );
188
189 /* Move to next certificate */
190 asn1_skip_any ( &cursor );
191 }
192
193 /* Append certificates to chain */
194 last = x509_last ( validator->chain );
195 if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) {
196 DBGC ( validator, "VALIDATOR %p could not append "
197 "certificates: %s\n", validator, strerror ( rc ) );
198 goto err_auto_append;
199 }
200
201 /* Check that at least one certificate has been added */
202 if ( last == x509_last ( validator->chain ) ) {
203 DBGC ( validator, "VALIDATOR %p failed to append any "
204 "applicable certificates\n", validator );
205 rc = -EACCES;
206 goto err_no_progress;
207 }
208
209 /* Drop reference to certificate list */
210 x509_chain_put ( certs );
211
212 return 0;
213
214 err_no_progress:
215 err_auto_append:
216 err_certificateset:
217 x509_chain_put ( certs );
218 err_alloc_certs:
219 return rc;
220 }
221
222 /**
223 * Start download of cross-signing certificate
224 *
225 * @v validator Certificate validator
226 * @v issuer Required issuer
227 * @ret rc Return status code
228 */
229 static int validator_start_download ( struct validator *validator,
230 const struct asn1_cursor *issuer ) {
231 const char *crosscert;
232 char *crosscert_copy;
233 char *uri_string;
234 size_t uri_string_len;
235 uint32_t crc;
236 int len;
237 int rc;
238
239 /* Determine cross-signed certificate source */
240 fetch_string_setting_copy ( NULL, &crosscert_setting, &crosscert_copy );
241 crosscert = ( crosscert_copy ? crosscert_copy : crosscert_default );
242 if ( ! crosscert[0] ) {
243 rc = -EINVAL;
244 goto err_check_uri_string;
245 }
246
247 /* Allocate URI string */
248 uri_string_len = ( strlen ( crosscert ) + 22 /* "/%08x.der?subject=" */
249 + base64_encoded_len ( issuer->len ) + 1 /* NUL */ );
250 uri_string = zalloc ( uri_string_len );
251 if ( ! uri_string ) {
252 rc = -ENOMEM;
253 goto err_alloc_uri_string;
254 }
255
256 /* Generate CRC32 */
257 crc = crc32_le ( 0xffffffffUL, issuer->data, issuer->len );
258
259 /* Generate URI string */
260 len = snprintf ( uri_string, uri_string_len, "%s/%08x.der?subject=",
261 crosscert, crc );
262 base64_encode ( issuer->data, issuer->len, ( uri_string + len ),
263 ( uri_string_len - len ) );
264 DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate "
265 "from %s\n", validator, uri_string );
266
267 /* Set completion handler */
268 validator->done = validator_append;
269
270 /* Open URI */
271 if ( ( rc = xfer_open_uri_string ( &validator->xfer,
272 uri_string ) ) != 0 ) {
273 DBGC ( validator, "VALIDATOR %p could not open %s: %s\n",
274 validator, uri_string, strerror ( rc ) );
275 goto err_open_uri_string;
276 }
277
278 /* Success */
279 rc = 0;
280
281 err_open_uri_string:
282 free ( uri_string );
283 err_alloc_uri_string:
284 err_check_uri_string:
285 free ( crosscert_copy );
286 return rc;
287 }
288
289 /****************************************************************************
290 *
291 * OCSP checks
292 *
293 */
294
295 /**
296 * Validate OCSP response
297 *
298 * @v validator Certificate validator
299 * @v data Raw OCSP response
300 * @v len Length of raw data
301 * @ret rc Return status code
302 */
303 static int validator_ocsp_validate ( struct validator *validator,
304 const void *data, size_t len ) {
305 time_t now;
306 int rc;
307
308 /* Record OCSP response */
309 if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) {
310 DBGC ( validator, "VALIDATOR %p could not record OCSP "
311 "response: %s\n", validator, strerror ( rc ) );
312 return rc;
313 }
314
315 /* Validate OCSP response */
316 now = time ( NULL );
317 if ( ( rc = ocsp_validate ( validator->ocsp, now ) ) != 0 ) {
318 DBGC ( validator, "VALIDATOR %p could not validate OCSP "
319 "response: %s\n", validator, strerror ( rc ) );
320 return rc;
321 }
322
323 /* Drop reference to OCSP check */
324 ocsp_put ( validator->ocsp );
325 validator->ocsp = NULL;
326
327 return 0;
328 }
329
330 /**
331 * Start OCSP check
332 *
333 * @v validator Certificate validator
334 * @v cert Certificate to check
335 * @v issuer Issuing certificate
336 * @ret rc Return status code
337 */
338 static int validator_start_ocsp ( struct validator *validator,
339 struct x509_certificate *cert,
340 struct x509_certificate *issuer ) {
341 const char *uri_string;
342 int rc;
343
344 /* Create OCSP check */
345 assert ( validator->ocsp == NULL );
346 if ( ( rc = ocsp_check ( cert, issuer, &validator->ocsp ) ) != 0 ) {
347 DBGC ( validator, "VALIDATOR %p could not create OCSP check: "
348 "%s\n", validator, strerror ( rc ) );
349 return rc;
350 }
351
352 /* Set completion handler */
353 validator->done = validator_ocsp_validate;
354
355 /* Open URI */
356 uri_string = validator->ocsp->uri_string;
357 DBGC ( validator, "VALIDATOR %p performing OCSP check at %s\n",
358 validator, uri_string );
359 if ( ( rc = xfer_open_uri_string ( &validator->xfer,
360 uri_string ) ) != 0 ) {
361 DBGC ( validator, "VALIDATOR %p could not open %s: %s\n",
362 validator, uri_string, strerror ( rc ) );
363 return rc;
364 }
365
366 return 0;
367 }
368
369 /****************************************************************************
370 *
371 * Data transfer interface
372 *
373 */
374
375 /**
376 * Close data transfer interface
377 *
378 * @v validator Certificate validator
379 * @v rc Reason for close
380 */
381 static void validator_xfer_close ( struct validator *validator, int rc ) {
382
383 /* Close data transfer interface */
384 intf_restart ( &validator->xfer, rc );
385
386 /* Check for errors */
387 if ( rc != 0 ) {
388 DBGC ( validator, "VALIDATOR %p transfer failed: %s\n",
389 validator, strerror ( rc ) );
390 goto err_transfer;
391 }
392 DBGC2 ( validator, "VALIDATOR %p transfer complete\n", validator );
393
394 /* Process completed download */
395 assert ( validator->done != NULL );
396 if ( ( rc = validator->done ( validator, validator->buffer.data,
397 validator->buffer.len ) ) != 0 )
398 goto err_append;
399
400 /* Free downloaded data */
401 xferbuf_free ( &validator->buffer );
402
403 /* Resume validation process */
404 process_add ( &validator->process );
405
406 return;
407
408 err_append:
409 err_transfer:
410 validator_finished ( validator, rc );
411 }
412
413 /**
414 * Receive data
415 *
416 * @v validator Certificate validator
417 * @v iobuf I/O buffer
418 * @v meta Data transfer metadata
419 * @ret rc Return status code
420 */
421 static int validator_xfer_deliver ( struct validator *validator,
422 struct io_buffer *iobuf,
423 struct xfer_metadata *meta ) {
424 int rc;
425
426 /* Add data to buffer */
427 if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ),
428 meta ) ) != 0 ) {
429 DBGC ( validator, "VALIDATOR %p could not receive data: %s\n",
430 validator, strerror ( rc ) );
431 validator_finished ( validator, rc );
432 return rc;
433 }
434
435 return 0;
436 }
437
438 /** Certificate validator data transfer interface operations */
439 static struct interface_operation validator_xfer_operations[] = {
440 INTF_OP ( xfer_deliver, struct validator *, validator_xfer_deliver ),
441 INTF_OP ( intf_close, struct validator *, validator_xfer_close ),
442 };
443
444 /** Certificate validator data transfer interface descriptor */
445 static struct interface_descriptor validator_xfer_desc =
446 INTF_DESC ( struct validator, xfer, validator_xfer_operations );
447
448 /****************************************************************************
449 *
450 * Validation process
451 *
452 */
453
454 /**
455 * Certificate validation process
456 *
457 * @v validator Certificate validator
458 */
459 static void validator_step ( struct validator *validator ) {
460 struct x509_link *link;
461 struct x509_certificate *cert;
462 struct x509_certificate *issuer = NULL;
463 struct x509_certificate *last;
464 time_t now;
465 int rc;
466
467 /* Try validating chain. Try even if the chain is incomplete,
468 * since certificates may already have been validated
469 * previously.
470 */
471 now = time ( NULL );
472 if ( ( rc = x509_validate_chain ( validator->chain, now, NULL,
473 NULL ) ) == 0 ) {
474 validator_finished ( validator, 0 );
475 return;
476 }
477
478 /* If there is a certificate that could be validated using
479 * OCSP, try it.
480 */
481 list_for_each_entry ( link, &validator->chain->links, list ) {
482 cert = issuer;
483 issuer = link->cert;
484 if ( ! cert )
485 continue;
486 if ( ! x509_is_valid ( issuer ) )
487 continue;
488 /* The issuer is valid, but this certificate is not
489 * yet valid. If OCSP is applicable, start it.
490 */
491 if ( ocsp_required ( cert ) ) {
492 /* Start OCSP */
493 if ( ( rc = validator_start_ocsp ( validator, cert,
494 issuer ) ) != 0 ) {
495 validator_finished ( validator, rc );
496 return;
497 }
498 return;
499 }
500 /* Otherwise, this is a permanent failure */
501 validator_finished ( validator, rc );
502 return;
503 }
504
505 /* If chain ends with a self-issued certificate, then there is
506 * nothing more to do.
507 */
508 last = x509_last ( validator->chain );
509 if ( asn1_compare ( &last->issuer.raw, &last->subject.raw ) == 0 ) {
510 validator_finished ( validator, rc );
511 return;
512 }
513
514 /* Otherwise, try to download a suitable cross-signing
515 * certificate.
516 */
517 if ( ( rc = validator_start_download ( validator,
518 &last->issuer.raw ) ) != 0 ) {
519 validator_finished ( validator, rc );
520 return;
521 }
522 }
523
524 /** Certificate validator process descriptor */
525 static struct process_descriptor validator_process_desc =
526 PROC_DESC_ONCE ( struct validator, process, validator_step );
527
528 /****************************************************************************
529 *
530 * Instantiator
531 *
532 */
533
534 /**
535 * Instantiate a certificate validator
536 *
537 * @v job Job control interface
538 * @v chain X.509 certificate chain
539 * @ret rc Return status code
540 */
541 int create_validator ( struct interface *job, struct x509_chain *chain ) {
542 struct validator *validator;
543 int rc;
544
545 /* Sanity check */
546 if ( ! chain ) {
547 rc = -EINVAL;
548 goto err_sanity;
549 }
550
551 /* Allocate and initialise structure */
552 validator = zalloc ( sizeof ( *validator ) );
553 if ( ! validator ) {
554 rc = -ENOMEM;
555 goto err_alloc;
556 }
557 ref_init ( &validator->refcnt, validator_free );
558 intf_init ( &validator->job, &validator_job_desc,
559 &validator->refcnt );
560 intf_init ( &validator->xfer, &validator_xfer_desc,
561 &validator->refcnt );
562 process_init ( &validator->process, &validator_process_desc,
563 &validator->refcnt );
564 validator->chain = x509_chain_get ( chain );
565 xferbuf_malloc_init ( &validator->buffer );
566
567 /* Attach parent interface, mortalise self, and return */
568 intf_plug_plug ( &validator->job, job );
569 ref_put ( &validator->refcnt );
570 DBGC2 ( validator, "VALIDATOR %p validating X509 chain %p\n",
571 validator, validator->chain );
572 return 0;
573
574 validator_finished ( validator, rc );
575 ref_put ( &validator->refcnt );
576 err_alloc:
577 err_sanity:
578 return rc;
579 }