iPXE - Open Source Boot Firmware

[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 }