diff options
| author | Lars Ingebrigtsen | 2018-06-24 21:17:37 +0200 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2018-06-24 21:17:46 +0200 |
| commit | c8745d95cffc348da7ae1e7f6a6c07ec2f4b2f3f (patch) | |
| tree | 8b51f860eca6020f9db662db9d7109a5e11d26db /src | |
| parent | cd5bb4bf3dbad8941d25823f398b595b8f0edbb9 (diff) | |
| download | emacs-c8745d95cffc348da7ae1e7f6a6c07ec2f4b2f3f.tar.gz emacs-c8745d95cffc348da7ae1e7f6a6c07ec2f4b2f3f.zip | |
Return the entire TLS certificate chain back to the caller
* src/gnutls.c (gnutls_deinit_certificates): New function.
(Fgnutls_peer_status): Return all certificates in the chain back
to Lisp land.
(gnutls_verify_boot): Compute all the x509 certificates in the
chain.
* src/process.h (struct Lisp_Process): Adjust gnutls fields so
that we can keep tracks of all certificates in the chain instead
of just the host certificate.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gnutls.c | 108 | ||||
| -rw-r--r-- | src/process.h | 3 |
2 files changed, 77 insertions, 34 deletions
diff --git a/src/gnutls.c b/src/gnutls.c index 903393fed18..5a178472ceb 100644 --- a/src/gnutls.c +++ b/src/gnutls.c | |||
| @@ -819,6 +819,19 @@ gnutls_make_error (int err) | |||
| 819 | return make_number (err); | 819 | return make_number (err); |
| 820 | } | 820 | } |
| 821 | 821 | ||
| 822 | static void | ||
| 823 | gnutls_deinit_certificates (struct Lisp_Process *p) | ||
| 824 | { | ||
| 825 | if (! p->gnutls_certificates) | ||
| 826 | return; | ||
| 827 | |||
| 828 | for (int i = 0; i < p->gnutls_certificates_length; i++) | ||
| 829 | gnutls_x509_crt_deinit (p->gnutls_certificates[i]); | ||
| 830 | |||
| 831 | xfree (p->gnutls_certificates); | ||
| 832 | p->gnutls_certificates = NULL; | ||
| 833 | } | ||
| 834 | |||
| 822 | Lisp_Object | 835 | Lisp_Object |
| 823 | emacs_gnutls_deinit (Lisp_Object proc) | 836 | emacs_gnutls_deinit (Lisp_Object proc) |
| 824 | { | 837 | { |
| @@ -853,6 +866,9 @@ emacs_gnutls_deinit (Lisp_Object proc) | |||
| 853 | GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT - 1; | 866 | GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT - 1; |
| 854 | } | 867 | } |
| 855 | 868 | ||
| 869 | if (XPROCESS (proc)->gnutls_certificates) | ||
| 870 | gnutls_deinit_certificates (XPROCESS (proc)); | ||
| 871 | |||
| 856 | XPROCESS (proc)->gnutls_p = false; | 872 | XPROCESS (proc)->gnutls_p = false; |
| 857 | return Qt; | 873 | return Qt; |
| 858 | } | 874 | } |
| @@ -1238,9 +1254,9 @@ The return value is a property list with top-level keys :warnings and | |||
| 1238 | 1254 | ||
| 1239 | /* This could get called in the INIT stage, when the certificate is | 1255 | /* This could get called in the INIT stage, when the certificate is |
| 1240 | not yet set. */ | 1256 | not yet set. */ |
| 1241 | if (XPROCESS (proc)->gnutls_certificate != NULL && | 1257 | if (XPROCESS (proc)->gnutls_certificates != NULL && |
| 1242 | gnutls_x509_crt_check_issuer(XPROCESS (proc)->gnutls_certificate, | 1258 | gnutls_x509_crt_check_issuer(XPROCESS (proc)->gnutls_certificates[0], |
| 1243 | XPROCESS (proc)->gnutls_certificate)) | 1259 | XPROCESS (proc)->gnutls_certificates[0])) |
| 1244 | warnings = Fcons (intern (":self-signed"), warnings); | 1260 | warnings = Fcons (intern (":self-signed"), warnings); |
| 1245 | 1261 | ||
| 1246 | if (!NILP (warnings)) | 1262 | if (!NILP (warnings)) |
| @@ -1248,10 +1264,23 @@ The return value is a property list with top-level keys :warnings and | |||
| 1248 | 1264 | ||
| 1249 | /* This could get called in the INIT stage, when the certificate is | 1265 | /* This could get called in the INIT stage, when the certificate is |
| 1250 | not yet set. */ | 1266 | not yet set. */ |
| 1251 | if (XPROCESS (proc)->gnutls_certificate != NULL) | 1267 | if (XPROCESS (proc)->gnutls_certificates != NULL) |
| 1252 | result = nconc2 (result, list2 | 1268 | { |
| 1253 | (intern (":certificate"), | 1269 | Lisp_Object certs = Qnil; |
| 1254 | gnutls_certificate_details (XPROCESS (proc)->gnutls_certificate))); | 1270 | |
| 1271 | /* Return the host certificate in its own element for | ||
| 1272 | compatibility reasons. */ | ||
| 1273 | result = nconc2 (result, list2 | ||
| 1274 | (intern (":certificate"), | ||
| 1275 | gnutls_certificate_details (XPROCESS (proc)->gnutls_certificates[0]))); | ||
| 1276 | |||
| 1277 | /* Return all the certificates in a list. */ | ||
| 1278 | for (int i = 0; i < XPROCESS (proc)->gnutls_certificates_length; i++) | ||
| 1279 | certs = nconc2 (certs, list1 (gnutls_certificate_details | ||
| 1280 | (XPROCESS (proc)->gnutls_certificates[i]))); | ||
| 1281 | |||
| 1282 | result = nconc2 (result, list2 (intern (":certificates"), certs)); | ||
| 1283 | } | ||
| 1255 | 1284 | ||
| 1256 | state = XPROCESS (proc)->gnutls_state; | 1285 | state = XPROCESS (proc)->gnutls_state; |
| 1257 | 1286 | ||
| @@ -1394,7 +1423,7 @@ gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) | |||
| 1394 | if (ret < GNUTLS_E_SUCCESS) | 1423 | if (ret < GNUTLS_E_SUCCESS) |
| 1395 | return gnutls_make_error (ret); | 1424 | return gnutls_make_error (ret); |
| 1396 | 1425 | ||
| 1397 | XPROCESS (proc)->gnutls_peer_verification = peer_verification; | 1426 | p->gnutls_peer_verification = peer_verification; |
| 1398 | 1427 | ||
| 1399 | warnings = Fplist_get (Fgnutls_peer_status (proc), intern (":warnings")); | 1428 | warnings = Fplist_get (Fgnutls_peer_status (proc), intern (":warnings")); |
| 1400 | if (!NILP (warnings)) | 1429 | if (!NILP (warnings)) |
| @@ -1431,49 +1460,61 @@ gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) | |||
| 1431 | can be easily extended to work with openpgp keys as well. */ | 1460 | can be easily extended to work with openpgp keys as well. */ |
| 1432 | if (gnutls_certificate_type_get (state) == GNUTLS_CRT_X509) | 1461 | if (gnutls_certificate_type_get (state) == GNUTLS_CRT_X509) |
| 1433 | { | 1462 | { |
| 1434 | gnutls_x509_crt_t gnutls_verify_cert; | 1463 | const gnutls_datum_t *cert_list; |
| 1435 | const gnutls_datum_t *gnutls_verify_cert_list; | 1464 | unsigned int cert_list_length; |
| 1436 | unsigned int gnutls_verify_cert_list_size; | 1465 | int failed_import = 0; |
| 1437 | |||
| 1438 | ret = gnutls_x509_crt_init (&gnutls_verify_cert); | ||
| 1439 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1440 | return gnutls_make_error (ret); | ||
| 1441 | 1466 | ||
| 1442 | gnutls_verify_cert_list | 1467 | cert_list = gnutls_certificate_get_peers (state, &cert_list_length); |
| 1443 | = gnutls_certificate_get_peers (state, &gnutls_verify_cert_list_size); | ||
| 1444 | 1468 | ||
| 1445 | if (gnutls_verify_cert_list == NULL) | 1469 | if (cert_list == NULL) |
| 1446 | { | 1470 | { |
| 1447 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1448 | emacs_gnutls_deinit (proc); | 1471 | emacs_gnutls_deinit (proc); |
| 1449 | boot_error (p, "No x509 certificate was found\n"); | 1472 | boot_error (p, "No x509 certificate was found\n"); |
| 1450 | return Qnil; | 1473 | return Qnil; |
| 1451 | } | 1474 | } |
| 1452 | 1475 | ||
| 1453 | /* Check only the first certificate in the given chain. */ | 1476 | /* Check only the first certificate in the given chain, but |
| 1454 | ret = gnutls_x509_crt_import (gnutls_verify_cert, | 1477 | store them all. */ |
| 1455 | &gnutls_verify_cert_list[0], | 1478 | p->gnutls_certificates = |
| 1456 | GNUTLS_X509_FMT_DER); | 1479 | xmalloc (cert_list_length * sizeof (gnutls_x509_crt_t)); |
| 1480 | p->gnutls_certificates_length = cert_list_length; | ||
| 1457 | 1481 | ||
| 1458 | if (ret < GNUTLS_E_SUCCESS) | 1482 | for (int i = cert_list_length - 1; i >= 0; i--) |
| 1459 | { | 1483 | { |
| 1460 | gnutls_x509_crt_deinit (gnutls_verify_cert); | 1484 | gnutls_x509_crt_t cert; |
| 1461 | return gnutls_make_error (ret); | 1485 | |
| 1486 | gnutls_x509_crt_init (&cert); | ||
| 1487 | |||
| 1488 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1489 | failed_import = ret; | ||
| 1490 | else | ||
| 1491 | { | ||
| 1492 | ret = gnutls_x509_crt_import (cert, &cert_list[i], | ||
| 1493 | GNUTLS_X509_FMT_DER); | ||
| 1494 | |||
| 1495 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1496 | failed_import = ret; | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | p->gnutls_certificates[i] = cert; | ||
| 1462 | } | 1500 | } |
| 1463 | 1501 | ||
| 1464 | XPROCESS (proc)->gnutls_certificate = gnutls_verify_cert; | 1502 | if (failed_import != 0) |
| 1503 | { | ||
| 1504 | gnutls_deinit_certificates (p); | ||
| 1505 | p->gnutls_certificates = NULL; | ||
| 1506 | return gnutls_make_error (failed_import); | ||
| 1507 | } | ||
| 1465 | 1508 | ||
| 1466 | int err = gnutls_x509_crt_check_hostname (gnutls_verify_cert, | 1509 | int err = gnutls_x509_crt_check_hostname (p->gnutls_certificates[0], |
| 1467 | c_hostname); | 1510 | c_hostname); |
| 1468 | check_memory_full (err); | 1511 | check_memory_full (err); |
| 1469 | if (!err) | 1512 | if (!err) |
| 1470 | { | 1513 | { |
| 1471 | XPROCESS (proc)->gnutls_extra_peer_verification | 1514 | p->gnutls_extra_peer_verification |= CERTIFICATE_NOT_MATCHING; |
| 1472 | |= CERTIFICATE_NOT_MATCHING; | ||
| 1473 | if (verify_error_all | 1515 | if (verify_error_all |
| 1474 | || !NILP (Fmember (QChostname, verify_error))) | 1516 | || !NILP (Fmember (QChostname, verify_error))) |
| 1475 | { | 1517 | { |
| 1476 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1477 | emacs_gnutls_deinit (proc); | 1518 | emacs_gnutls_deinit (proc); |
| 1478 | boot_error (p, "The x509 certificate does not match \"%s\"", | 1519 | boot_error (p, "The x509 certificate does not match \"%s\"", |
| 1479 | c_hostname); | 1520 | c_hostname); |
| @@ -1486,7 +1527,7 @@ gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) | |||
| 1486 | } | 1527 | } |
| 1487 | 1528 | ||
| 1488 | /* Set this flag only if the whole initialization succeeded. */ | 1529 | /* Set this flag only if the whole initialization succeeded. */ |
| 1489 | XPROCESS (proc)->gnutls_p = true; | 1530 | p->gnutls_p = true; |
| 1490 | 1531 | ||
| 1491 | return gnutls_make_error (ret); | 1532 | return gnutls_make_error (ret); |
| 1492 | } | 1533 | } |
| @@ -1855,7 +1896,8 @@ This function may also return `gnutls-e-again', or | |||
| 1855 | 1896 | ||
| 1856 | state = XPROCESS (proc)->gnutls_state; | 1897 | state = XPROCESS (proc)->gnutls_state; |
| 1857 | 1898 | ||
| 1858 | gnutls_x509_crt_deinit (XPROCESS (proc)->gnutls_certificate); | 1899 | if (XPROCESS (proc)->gnutls_certificates) |
| 1900 | gnutls_deinit_certificates (XPROCESS (proc)); | ||
| 1859 | 1901 | ||
| 1860 | ret = gnutls_bye (state, NILP (cont) ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR); | 1902 | ret = gnutls_bye (state, NILP (cont) ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR); |
| 1861 | 1903 | ||
diff --git a/src/process.h b/src/process.h index 42cc66ec560..6bc22146a72 100644 --- a/src/process.h +++ b/src/process.h | |||
| @@ -194,7 +194,8 @@ struct Lisp_Process | |||
| 194 | gnutls_session_t gnutls_state; | 194 | gnutls_session_t gnutls_state; |
| 195 | gnutls_certificate_client_credentials gnutls_x509_cred; | 195 | gnutls_certificate_client_credentials gnutls_x509_cred; |
| 196 | gnutls_anon_client_credentials_t gnutls_anon_cred; | 196 | gnutls_anon_client_credentials_t gnutls_anon_cred; |
| 197 | gnutls_x509_crt_t gnutls_certificate; | 197 | gnutls_x509_crt_t *gnutls_certificates; |
| 198 | int gnutls_certificates_length; | ||
| 198 | unsigned int gnutls_peer_verification; | 199 | unsigned int gnutls_peer_verification; |
| 199 | unsigned int gnutls_extra_peer_verification; | 200 | unsigned int gnutls_extra_peer_verification; |
| 200 | int gnutls_log_level; | 201 | int gnutls_log_level; |