Browse Source

[crypto] Automatically download cross-signed certificates

Automatically attempt to download any required cross-signing
certificates from http://ca.ipxe.org/auto, in order to enable the use
of standard SSL certificates issued by public CAs.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 12 years ago
parent
commit
3e6e0078e0
2 changed files with 287 additions and 11 deletions
  1. 3
    0
      src/include/ipxe/dhcp.h
  2. 284
    11
      src/net/validator.c

+ 3
- 0
src/include/ipxe/dhcp.h View File

@@ -364,6 +364,9 @@ struct dhcp_client_uuid {
364 364
 /** Client private key */
365 365
 #define DHCP_EB_KEY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x5c )
366 366
 
367
+/** Cross-signed certificate source */
368
+#define DHCP_EB_CROSS_CERT DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x5d )
369
+
367 370
 /** Skip PXE DHCP protocol extensions such as ProxyDHCP
368 371
  *
369 372
  * If set to a non-zero value, iPXE will not wait for ProxyDHCP offers

+ 284
- 11
src/net/validator.c View File

@@ -20,12 +20,21 @@
20 20
 FILE_LICENCE ( GPL2_OR_LATER );
21 21
 
22 22
 #include <string.h>
23
+#include <stdio.h>
23 24
 #include <errno.h>
24 25
 #include <ipxe/refcnt.h>
25 26
 #include <ipxe/malloc.h>
26 27
 #include <ipxe/interface.h>
28
+#include <ipxe/xfer.h>
29
+#include <ipxe/open.h>
30
+#include <ipxe/iobuf.h>
31
+#include <ipxe/xferbuf.h>
27 32
 #include <ipxe/process.h>
28 33
 #include <ipxe/x509.h>
34
+#include <ipxe/settings.h>
35
+#include <ipxe/dhcp.h>
36
+#include <ipxe/base64.h>
37
+#include <ipxe/crc32.h>
29 38
 #include <ipxe/validator.h>
30 39
 
31 40
 /** @file
@@ -40,10 +49,14 @@ struct validator {
40 49
 	struct refcnt refcnt;
41 50
 	/** Job control interface */
42 51
 	struct interface job;
52
+	/** Data transfer interface */
53
+	struct interface xfer;
43 54
 	/** Process */
44 55
 	struct process process;
45 56
 	/** X.509 certificate chain */
46 57
 	struct x509_chain *chain;
58
+	/** Data buffer */
59
+	struct xfer_buffer buffer;
47 60
 };
48 61
 
49 62
 /**
@@ -57,6 +70,7 @@ static void validator_free ( struct refcnt *refcnt ) {
57 70
 
58 71
 	DBGC ( validator, "VALIDATOR %p freed\n", validator );
59 72
 	x509_chain_put ( validator->chain );
73
+	xferbuf_done ( &validator->buffer );
60 74
 	free ( validator );
61 75
 }
62 76
 
@@ -72,6 +86,7 @@ static void validator_finished ( struct validator *validator, int rc ) {
72 86
 	process_del ( &validator->process );
73 87
 
74 88
 	/* Close all interfaces */
89
+	intf_shutdown ( &validator->xfer, rc );
75 90
 	intf_shutdown ( &validator->job, rc );
76 91
 }
77 92
 
@@ -90,6 +105,250 @@ static struct interface_operation validator_job_operations[] = {
90 105
 static struct interface_descriptor validator_job_desc =
91 106
 	INTF_DESC ( struct validator, job, validator_job_operations );
92 107
 
108
+/****************************************************************************
109
+ *
110
+ * Cross-signing certificates
111
+ *
112
+ */
113
+
114
+/** Cross-signed certificate source setting */
115
+struct setting crosscert_setting __setting ( SETTING_CRYPTO ) = {
116
+	.name = "crosscert",
117
+	.description = "Cross-signed certificate source",
118
+	.tag = DHCP_EB_CROSS_CERT,
119
+	.type = &setting_type_string,
120
+};
121
+
122
+/** Default cross-signed certificate source */
123
+static const char crosscert_default[] = "http://ca.ipxe.org/auto";
124
+
125
+/**
126
+ * Start download of cross-signing certificate
127
+ *
128
+ * @v validator		Certificate validator
129
+ * @v issuer		Required issuer
130
+ * @ret rc		Return status code
131
+ */
132
+static int validator_start_download ( struct validator *validator,
133
+				      const struct asn1_cursor *issuer ) {
134
+	const char *crosscert;
135
+	char *crosscert_copy;
136
+	char *uri_string;
137
+	size_t uri_string_len;
138
+	uint32_t crc;
139
+	int len;
140
+	int rc;
141
+
142
+	/* Determine cross-signed certificate source */
143
+	len = fetch_string_setting_copy ( NULL, &crosscert_setting,
144
+					  &crosscert_copy );
145
+	if ( len < 0 ) {
146
+		rc = len;
147
+		DBGC ( validator, "VALIDATOR %p could not fetch crosscert "
148
+		       "setting: %s\n", validator, strerror ( rc ) );
149
+		goto err_fetch_crosscert;
150
+	}
151
+	crosscert = ( crosscert_copy ? crosscert_copy : crosscert_default );
152
+
153
+	/* Allocate URI string */
154
+	uri_string_len = ( strlen ( crosscert ) + 14 /* "/%08x.der?" */ +
155
+			   base64_encoded_len ( issuer->len ) + 1 /* NUL */ );
156
+	uri_string = zalloc ( uri_string_len );
157
+	if ( ! uri_string ) {
158
+		rc = -ENOMEM;
159
+		goto err_alloc_uri_string;
160
+	}
161
+
162
+	/* Generate CRC32 */
163
+	crc = crc32_le ( 0xffffffffUL, issuer->data, issuer->len );
164
+
165
+	/* Generate URI string */
166
+	len = snprintf ( uri_string, uri_string_len, "%s/%08x.der?",
167
+			 crosscert, crc );
168
+	base64_encode ( issuer->data, issuer->len, ( uri_string + len ) );
169
+	DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate "
170
+	       "from %s\n", validator, uri_string );
171
+
172
+	/* Open URI */
173
+	if ( ( rc = xfer_open_uri_string ( &validator->xfer,
174
+					   uri_string ) ) != 0 ) {
175
+		DBGC ( validator, "VALIDATOR %p could not open %s: %s\n",
176
+		       validator, uri_string, strerror ( rc ) );
177
+		goto err_open_uri_string;
178
+	}
179
+
180
+	/* Success */
181
+	rc = 0;
182
+
183
+ err_open_uri_string:
184
+	free ( uri_string );
185
+ err_alloc_uri_string:
186
+	free ( crosscert_copy );
187
+ err_fetch_crosscert:
188
+	return rc;
189
+}
190
+
191
+/**
192
+ * Append cross-signing certificates to certificate chain
193
+ *
194
+ * @v validator		Certificate validator
195
+ * @v data		Raw cross-signing certificate data
196
+ * @v len		Length of raw data
197
+ * @ret rc		Return status code
198
+ */
199
+static int validator_append ( struct validator *validator,
200
+			      const void *data, size_t len ) {
201
+	struct asn1_cursor cursor;
202
+	struct x509_chain *certs;
203
+	struct x509_certificate *cert;
204
+	struct x509_certificate *last;
205
+	int rc;
206
+
207
+	/* Allocate certificate list */
208
+	certs = x509_alloc_chain();
209
+	if ( ! certs ) {
210
+		rc = -ENOMEM;
211
+		goto err_alloc_certs;
212
+	}
213
+
214
+	/* Initialise cursor */
215
+	cursor.data = data;
216
+	cursor.len = len;
217
+
218
+	/* Enter certificateSet */
219
+	if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) {
220
+		DBGC ( validator, "VALIDATOR %p could not enter "
221
+		       "certificateSet: %s\n", validator, strerror ( rc ) );
222
+		goto err_certificateset;
223
+	}
224
+
225
+	/* Add each certificate to list */
226
+	while ( cursor.len ) {
227
+
228
+		/* Add certificate to chain */
229
+		if ( ( rc = x509_append_raw ( certs, cursor.data,
230
+					      cursor.len ) ) != 0 ) {
231
+			DBGC ( validator, "VALIDATOR %p could not append "
232
+			       "certificate: %s\n",
233
+			       validator, strerror ( rc) );
234
+			DBGC_HDA ( validator, 0, cursor.data, cursor.len );
235
+			return rc;
236
+		}
237
+		cert = x509_last ( certs );
238
+		DBGC ( validator, "VALIDATOR %p found certificate %s\n",
239
+		       validator, cert->subject.name );
240
+
241
+		/* Move to next certificate */
242
+		asn1_skip_any ( &cursor );
243
+	}
244
+
245
+	/* Append certificates to chain */
246
+	last = x509_last ( validator->chain );
247
+	if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) {
248
+		DBGC ( validator, "VALIDATOR %p could not append "
249
+		       "certificates: %s\n", validator, strerror ( rc ) );
250
+		goto err_auto_append;
251
+	}
252
+
253
+	/* Check that at least one certificate has been added */
254
+	if ( last == x509_last ( validator->chain ) ) {
255
+		DBGC ( validator, "VALIDATOR %p failed to append any "
256
+		       "applicable certificates\n", validator );
257
+		rc = -EACCES;
258
+		goto err_no_progress;
259
+	}
260
+
261
+	/* Drop reference to certificate list */
262
+	x509_chain_put ( certs );
263
+
264
+	return 0;
265
+
266
+ err_no_progress:
267
+ err_auto_append:
268
+ err_certificateset:
269
+	x509_chain_put ( certs );
270
+ err_alloc_certs:
271
+	return rc;
272
+}
273
+
274
+/****************************************************************************
275
+ *
276
+ * Data transfer interface
277
+ *
278
+ */
279
+
280
+/**
281
+ * Close data transfer interface
282
+ *
283
+ * @v validator		Certificate validator
284
+ * @v rc		Reason for close
285
+ */
286
+static void validator_xfer_close ( struct validator *validator, int rc ) {
287
+
288
+	/* Close data transfer interface */
289
+	intf_restart ( &validator->xfer, rc );
290
+
291
+	/* Check for errors */
292
+	if ( rc != 0 ) {
293
+		DBGC ( validator, "VALIDATOR %p download failed: %s\n",
294
+		       validator, strerror ( rc ) );
295
+		goto err_download;
296
+	}
297
+	DBGC ( validator, "VALIDATOR %p download complete\n", validator );
298
+
299
+	/* Append downloaded certificates */
300
+	if ( ( rc = validator_append ( validator, validator->buffer.data,
301
+				       validator->buffer.len ) ) != 0 )
302
+		goto err_append;
303
+
304
+	/* Free downloaded data */
305
+	xferbuf_done ( &validator->buffer );
306
+
307
+	/* Resume validation process */
308
+	process_add ( &validator->process );
309
+
310
+	return;
311
+
312
+ err_append:
313
+ err_download:
314
+	validator_finished ( validator, rc );
315
+}
316
+
317
+/**
318
+ * Receive data
319
+ *
320
+ * @v validator		Certificate validator
321
+ * @v iobuf		I/O buffer
322
+ * @v meta		Data transfer metadata
323
+ * @ret rc		Return status code
324
+ */
325
+static int validator_xfer_deliver ( struct validator *validator,
326
+				    struct io_buffer *iobuf,
327
+				    struct xfer_metadata *meta ) {
328
+	int rc;
329
+
330
+	/* Add data to buffer */
331
+	if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ),
332
+				      meta ) ) != 0 ) {
333
+		DBGC ( validator, "VALIDATOR %p could not receive data: %s\n",
334
+		       validator, strerror ( rc ) );
335
+		validator_finished ( validator, rc );
336
+		return rc;
337
+	}
338
+
339
+	return 0;
340
+}
341
+
342
+/** Certificate validator data transfer interface operations */
343
+static struct interface_operation validator_xfer_operations[] = {
344
+	INTF_OP ( xfer_deliver, struct validator *, validator_xfer_deliver ),
345
+	INTF_OP ( intf_close, struct validator *, validator_xfer_close ),
346
+};
347
+
348
+/** Certificate validator data transfer interface descriptor */
349
+static struct interface_descriptor validator_xfer_desc =
350
+	INTF_DESC ( struct validator, xfer, validator_xfer_operations );
351
+
93 352
 /****************************************************************************
94 353
  *
95 354
  * Validation process
@@ -102,25 +361,37 @@ static struct interface_descriptor validator_job_desc =
102 361
  * @v validator		Certificate validator
103 362
  */
104 363
 static void validator_step ( struct validator *validator ) {
364
+	struct x509_certificate *last = x509_last ( validator->chain );
105 365
 	time_t now;
106 366
 	int rc;
107 367
 
108
-	/* Attempt to validate certificate chain */
368
+	/* Try validating chain.  Try even if the chain is incomplete,
369
+	 * since certificates may already have been validated
370
+	 * previously.
371
+	 */
109 372
 	now = time ( NULL );
110 373
 	if ( ( rc = x509_validate_chain ( validator->chain, now,
111
-					  NULL ) ) != 0 ) {
112
-		DBGC ( validator, "VALIDATOR %p could not validate chain: %s\n",
113
-		       validator, strerror ( rc ) );
114
-		goto err_validate;
374
+					  NULL ) ) == 0 ) {
375
+		validator_finished ( validator, 0 );
376
+		return;
115 377
 	}
116 378
 
117
-	/* Mark validation as complete */
118
-	validator_finished ( validator, 0 );
119
-
120
-	return;
379
+	/* If chain ends with a self-issued certificate, then there is
380
+	 * nothing more to do.
381
+	 */
382
+	if ( asn1_compare ( &last->issuer.raw, &last->subject.raw ) == 0 ) {
383
+		validator_finished ( validator, rc );
384
+		return;
385
+	}
121 386
 
122
- err_validate:
123
-	validator_finished ( validator, rc );
387
+	/* Otherwise, try to download a suitable cross-signing
388
+	 * certificate.
389
+	 */
390
+	if ( ( rc = validator_start_download ( validator,
391
+					       &last->issuer.raw ) ) != 0 ) {
392
+		validator_finished ( validator, rc );
393
+		return;
394
+	}
124 395
 }
125 396
 
126 397
 /** Certificate validator process descriptor */
@@ -159,6 +430,8 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) {
159 430
 	ref_init ( &validator->refcnt, validator_free );
160 431
 	intf_init ( &validator->job, &validator_job_desc,
161 432
 		    &validator->refcnt );
433
+	intf_init ( &validator->xfer, &validator_xfer_desc,
434
+		    &validator->refcnt );
162 435
 	process_init ( &validator->process, &validator_process_desc,
163 436
 		       &validator->refcnt );
164 437
 	validator->chain = x509_chain_get ( chain );

Loading…
Cancel
Save