|
@@ -43,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
43
|
43
|
#include <ipxe/x509.h>
|
44
|
44
|
#include <ipxe/clientcert.h>
|
45
|
45
|
#include <ipxe/rbg.h>
|
|
46
|
+#include <ipxe/validator.h>
|
46
|
47
|
#include <ipxe/tls.h>
|
47
|
48
|
|
48
|
49
|
/* Disambiguate the various error causes */
|
|
@@ -93,6 +94,16 @@ static void tls_set_uint24 ( uint8_t field24[3], unsigned long value ) {
|
93
|
94
|
*field32 |= cpu_to_be32 ( value << 8 );
|
94
|
95
|
}
|
95
|
96
|
|
|
97
|
+/**
|
|
98
|
+ * Determine if TLS session is ready for application data
|
|
99
|
+ *
|
|
100
|
+ * @v tls TLS session
|
|
101
|
+ * @ret is_ready TLS session is ready
|
|
102
|
+ */
|
|
103
|
+static int tls_ready ( struct tls_session *tls ) {
|
|
104
|
+ return ( tls->client_finished && tls->server_finished );
|
|
105
|
+}
|
|
106
|
+
|
96
|
107
|
/******************************************************************************
|
97
|
108
|
*
|
98
|
109
|
* Hybrid MD5+SHA1 hash as used by TLSv1.1 and earlier
|
|
@@ -196,10 +207,11 @@ static void tls_close ( struct tls_session *tls, int rc ) {
|
196
|
207
|
|
197
|
208
|
/* Remove process */
|
198
|
209
|
process_del ( &tls->process );
|
199
|
|
-
|
200
|
|
- /* Close ciphertext and plaintext streams */
|
|
210
|
+
|
|
211
|
+ /* Close all interfaces */
|
201
|
212
|
intf_shutdown ( &tls->cipherstream, rc );
|
202
|
213
|
intf_shutdown ( &tls->plainstream, rc );
|
|
214
|
+ intf_shutdown ( &tls->validator, rc );
|
203
|
215
|
}
|
204
|
216
|
|
205
|
217
|
/******************************************************************************
|
|
@@ -1111,7 +1123,9 @@ static int tls_send_finished ( struct tls_session *tls ) {
|
1111
|
1123
|
uint8_t verify_data[12];
|
1112
|
1124
|
} __attribute__ (( packed )) finished;
|
1113
|
1125
|
uint8_t digest_out[ digest->digestsize ];
|
|
1126
|
+ int rc;
|
1114
|
1127
|
|
|
1128
|
+ /* Construct record */
|
1115
|
1129
|
memset ( &finished, 0, sizeof ( finished ) );
|
1116
|
1130
|
finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) |
|
1117
|
1131
|
htonl ( sizeof ( finished ) -
|
|
@@ -1121,7 +1135,15 @@ static int tls_send_finished ( struct tls_session *tls ) {
|
1121
|
1135
|
finished.verify_data, sizeof ( finished.verify_data ),
|
1122
|
1136
|
"client finished", digest_out, sizeof ( digest_out ) );
|
1123
|
1137
|
|
1124
|
|
- return tls_send_handshake ( tls, &finished, sizeof ( finished ) );
|
|
1138
|
+ /* Transmit record */
|
|
1139
|
+ if ( ( rc = tls_send_handshake ( tls, &finished,
|
|
1140
|
+ sizeof ( finished ) ) ) != 0 )
|
|
1141
|
+ return rc;
|
|
1142
|
+
|
|
1143
|
+ /* Mark client as finished */
|
|
1144
|
+ tls->client_finished = 1;
|
|
1145
|
+
|
|
1146
|
+ return 0;
|
1125
|
1147
|
}
|
1126
|
1148
|
|
1127
|
1149
|
/**
|
|
@@ -1354,10 +1376,6 @@ static int tls_new_certificate ( struct tls_session *tls,
|
1354
|
1376
|
} __attribute__ (( packed )) *certificate = data;
|
1355
|
1377
|
size_t certificates_len = tls_uint24 ( certificate->length );
|
1356
|
1378
|
const void *end = ( certificate->certificates + certificates_len );
|
1357
|
|
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
|
1358
|
|
- struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
|
1359
|
|
- struct x509_certificate *cert;
|
1360
|
|
- time_t now;
|
1361
|
1379
|
int rc;
|
1362
|
1380
|
|
1363
|
1381
|
/* Sanity check */
|
|
@@ -1373,35 +1391,6 @@ static int tls_new_certificate ( struct tls_session *tls,
|
1373
|
1391
|
certificates_len ) ) != 0 )
|
1374
|
1392
|
return rc;
|
1375
|
1393
|
|
1376
|
|
- /* Validate certificate chain */
|
1377
|
|
- now = time ( NULL );
|
1378
|
|
- if ( ( rc = x509_validate_chain ( tls->chain, now, NULL ) ) != 0 ) {
|
1379
|
|
- DBGC ( tls, "TLS %p could not validate certificate chain: %s\n",
|
1380
|
|
- tls, strerror ( rc ) );
|
1381
|
|
- return rc;
|
1382
|
|
- }
|
1383
|
|
-
|
1384
|
|
- /* Extract first certificate */
|
1385
|
|
- cert = x509_first ( tls->chain );
|
1386
|
|
- assert ( cert != NULL );
|
1387
|
|
-
|
1388
|
|
- /* Verify server name */
|
1389
|
|
- if ( ( cert->subject.name == NULL ) ||
|
1390
|
|
- ( strcmp ( cert->subject.name, tls->name ) != 0 ) ) {
|
1391
|
|
- DBGC ( tls, "TLS %p server name incorrect (expected %s, got "
|
1392
|
|
- "%s)\n", tls, tls->name, cert->subject.name );
|
1393
|
|
- return -EACCES_WRONG_NAME;
|
1394
|
|
- }
|
1395
|
|
-
|
1396
|
|
- /* Initialise public key algorithm */
|
1397
|
|
- if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
1398
|
|
- cert->subject.public_key.raw.data,
|
1399
|
|
- cert->subject.public_key.raw.len ) ) != 0 ) {
|
1400
|
|
- DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
1401
|
|
- tls, strerror ( rc ) );
|
1402
|
|
- return rc;
|
1403
|
|
- }
|
1404
|
|
-
|
1405
|
1394
|
return 0;
|
1406
|
1395
|
}
|
1407
|
1396
|
|
|
@@ -1442,6 +1431,7 @@ static int tls_new_server_hello_done ( struct tls_session *tls,
|
1442
|
1431
|
char next[0];
|
1443
|
1432
|
} __attribute__ (( packed )) *hello_done = data;
|
1444
|
1433
|
const void *end = hello_done->next;
|
|
1434
|
+ int rc;
|
1445
|
1435
|
|
1446
|
1436
|
/* Sanity check */
|
1447
|
1437
|
if ( end != ( data + len ) ) {
|
|
@@ -1451,11 +1441,12 @@ static int tls_new_server_hello_done ( struct tls_session *tls,
|
1451
|
1441
|
return -EINVAL;
|
1452
|
1442
|
}
|
1453
|
1443
|
|
1454
|
|
- /* Schedule Client Key Exchange, Change Cipher, and Finished */
|
1455
|
|
- tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
|
1456
|
|
- TLS_TX_CHANGE_CIPHER |
|
1457
|
|
- TLS_TX_FINISHED );
|
1458
|
|
- tls_tx_resume ( tls );
|
|
1444
|
+ /* Begin certificate validation */
|
|
1445
|
+ if ( ( rc = create_validator ( &tls->validator, tls->chain ) ) != 0 ) {
|
|
1446
|
+ DBGC ( tls, "TLS %p could not start certificate validation: "
|
|
1447
|
+ "%s\n", tls, strerror ( rc ) );
|
|
1448
|
+ return rc;
|
|
1449
|
+ }
|
1459
|
1450
|
|
1460
|
1451
|
return 0;
|
1461
|
1452
|
}
|
|
@@ -1497,8 +1488,8 @@ static int tls_new_finished ( struct tls_session *tls,
|
1497
|
1488
|
return -EPERM;
|
1498
|
1489
|
}
|
1499
|
1490
|
|
1500
|
|
- /* Mark session as ready to transmit plaintext data */
|
1501
|
|
- tls->tx_ready = 1;
|
|
1491
|
+ /* Mark server as finished */
|
|
1492
|
+ tls->server_finished = 1;
|
1502
|
1493
|
|
1503
|
1494
|
/* Send notification of a window change */
|
1504
|
1495
|
xfer_window_changed ( &tls->plainstream );
|
|
@@ -1601,6 +1592,8 @@ static int tls_new_record ( struct tls_session *tls, unsigned int type,
|
1601
|
1592
|
case TLS_TYPE_HANDSHAKE:
|
1602
|
1593
|
return tls_new_handshake ( tls, data, len );
|
1603
|
1594
|
case TLS_TYPE_DATA:
|
|
1595
|
+ if ( ! tls_ready ( tls ) )
|
|
1596
|
+ return -ENOTCONN;
|
1604
|
1597
|
return xfer_deliver_raw ( &tls->plainstream, data, len );
|
1605
|
1598
|
default:
|
1606
|
1599
|
/* RFC4346 says that we should just ignore unknown
|
|
@@ -2015,7 +2008,7 @@ static int tls_new_ciphertext ( struct tls_session *tls,
|
2015
|
2008
|
static size_t tls_plainstream_window ( struct tls_session *tls ) {
|
2016
|
2009
|
|
2017
|
2010
|
/* Block window unless we are ready to accept data */
|
2018
|
|
- if ( ! tls->tx_ready )
|
|
2011
|
+ if ( ! tls_ready ( tls ) )
|
2019
|
2012
|
return 0;
|
2020
|
2013
|
|
2021
|
2014
|
return xfer_window ( &tls->cipherstream );
|
|
@@ -2035,7 +2028,7 @@ static int tls_plainstream_deliver ( struct tls_session *tls,
|
2035
|
2028
|
int rc;
|
2036
|
2029
|
|
2037
|
2030
|
/* Refuse unless we are ready to accept data */
|
2038
|
|
- if ( ! tls->tx_ready ) {
|
|
2031
|
+ if ( ! tls_ready ( tls ) ) {
|
2039
|
2032
|
rc = -ENOTCONN;
|
2040
|
2033
|
goto done;
|
2041
|
2034
|
}
|
|
@@ -2192,6 +2185,79 @@ static struct interface_descriptor tls_cipherstream_desc =
|
2192
|
2185
|
INTF_DESC_PASSTHRU ( struct tls_session, cipherstream,
|
2193
|
2186
|
tls_cipherstream_ops, plainstream );
|
2194
|
2187
|
|
|
2188
|
+/******************************************************************************
|
|
2189
|
+ *
|
|
2190
|
+ * Certificate validator
|
|
2191
|
+ *
|
|
2192
|
+ ******************************************************************************
|
|
2193
|
+ */
|
|
2194
|
+
|
|
2195
|
+/**
|
|
2196
|
+ * Handle certificate validation completion
|
|
2197
|
+ *
|
|
2198
|
+ * @v tls TLS session
|
|
2199
|
+ * @v rc Reason for completion
|
|
2200
|
+ */
|
|
2201
|
+static void tls_validator_done ( struct tls_session *tls, int rc ) {
|
|
2202
|
+ struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
|
|
2203
|
+ struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
|
|
2204
|
+ struct x509_certificate *cert;
|
|
2205
|
+
|
|
2206
|
+ /* Close validator interface */
|
|
2207
|
+ intf_restart ( &tls->validator, rc );
|
|
2208
|
+
|
|
2209
|
+ /* Check for validation failure */
|
|
2210
|
+ if ( rc != 0 ) {
|
|
2211
|
+ DBGC ( tls, "TLS %p certificate validation failed: %s\n",
|
|
2212
|
+ tls, strerror ( rc ) );
|
|
2213
|
+ goto err;
|
|
2214
|
+ }
|
|
2215
|
+ DBGC ( tls, "TLS %p certificate validation succeeded\n", tls );
|
|
2216
|
+
|
|
2217
|
+ /* Extract first certificate */
|
|
2218
|
+ cert = x509_first ( tls->chain );
|
|
2219
|
+ assert ( cert != NULL );
|
|
2220
|
+
|
|
2221
|
+ /* Verify server name */
|
|
2222
|
+ if ( ( cert->subject.name == NULL ) ||
|
|
2223
|
+ ( strcmp ( cert->subject.name, tls->name ) != 0 ) ) {
|
|
2224
|
+ DBGC ( tls, "TLS %p server name incorrect (expected %s, got "
|
|
2225
|
+ "%s)\n", tls, tls->name, cert->subject.name );
|
|
2226
|
+ rc = -EACCES_WRONG_NAME;
|
|
2227
|
+ goto err;
|
|
2228
|
+ }
|
|
2229
|
+
|
|
2230
|
+ /* Initialise public key algorithm */
|
|
2231
|
+ if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
|
|
2232
|
+ cert->subject.public_key.raw.data,
|
|
2233
|
+ cert->subject.public_key.raw.len ) ) != 0 ) {
|
|
2234
|
+ DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
|
|
2235
|
+ tls, strerror ( rc ) );
|
|
2236
|
+ goto err;
|
|
2237
|
+ }
|
|
2238
|
+
|
|
2239
|
+ /* Schedule Client Key Exchange, Change Cipher, and Finished */
|
|
2240
|
+ tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
|
|
2241
|
+ TLS_TX_CHANGE_CIPHER |
|
|
2242
|
+ TLS_TX_FINISHED );
|
|
2243
|
+ tls_tx_resume ( tls );
|
|
2244
|
+
|
|
2245
|
+ return;
|
|
2246
|
+
|
|
2247
|
+ err:
|
|
2248
|
+ tls_close ( tls, rc );
|
|
2249
|
+ return;
|
|
2250
|
+}
|
|
2251
|
+
|
|
2252
|
+/** TLS certificate validator interface operations */
|
|
2253
|
+static struct interface_operation tls_validator_ops[] = {
|
|
2254
|
+ INTF_OP ( intf_close, struct tls_session *, tls_validator_done ),
|
|
2255
|
+};
|
|
2256
|
+
|
|
2257
|
+/** TLS certificate validator interface descriptor */
|
|
2258
|
+static struct interface_descriptor tls_validator_desc =
|
|
2259
|
+ INTF_DESC ( struct tls_session, validator, tls_validator_ops );
|
|
2260
|
+
|
2195
|
2261
|
/******************************************************************************
|
2196
|
2262
|
*
|
2197
|
2263
|
* Controlling process
|
|
@@ -2307,6 +2373,8 @@ int add_tls ( struct interface *xfer, const char *name,
|
2307
|
2373
|
tls->name = name;
|
2308
|
2374
|
intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt );
|
2309
|
2375
|
intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt );
|
|
2376
|
+ intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt );
|
|
2377
|
+ process_init ( &tls->process, &tls_process_desc, &tls->refcnt );
|
2310
|
2378
|
tls->version = TLS_VERSION_TLS_1_2;
|
2311
|
2379
|
tls_clear_cipher ( tls, &tls->tx_cipherspec );
|
2312
|
2380
|
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
|
|
@@ -2327,7 +2395,6 @@ int add_tls ( struct interface *xfer, const char *name,
|
2327
|
2395
|
tls->handshake_digest = &sha256_algorithm;
|
2328
|
2396
|
tls->handshake_ctx = tls->handshake_sha256_ctx;
|
2329
|
2397
|
tls->tx_pending = TLS_TX_CLIENT_HELLO;
|
2330
|
|
- process_init ( &tls->process, &tls_process_desc, &tls->refcnt );
|
2331
|
2398
|
|
2332
|
2399
|
/* Attach to parent interface, mortalise self, and return */
|
2333
|
2400
|
intf_plug_plug ( &tls->plainstream, xfer );
|