diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gnutls.c | 269 | ||||
| -rw-r--r-- | src/gnutls.h | 1 | ||||
| -rw-r--r-- | src/process.c | 5 |
3 files changed, 150 insertions, 125 deletions
diff --git a/src/gnutls.c b/src/gnutls.c index 6573c87cf78..ce4fbf9b7ef 100644 --- a/src/gnutls.c +++ b/src/gnutls.c | |||
| @@ -540,8 +540,6 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte) | |||
| 540 | ssize_t rtnval; | 540 | ssize_t rtnval; |
| 541 | gnutls_session_t state = proc->gnutls_state; | 541 | gnutls_session_t state = proc->gnutls_state; |
| 542 | 542 | ||
| 543 | int log_level = proc->gnutls_log_level; | ||
| 544 | |||
| 545 | if (proc->gnutls_initstage != GNUTLS_STAGE_READY) | 543 | if (proc->gnutls_initstage != GNUTLS_STAGE_READY) |
| 546 | return -1; | 544 | return -1; |
| 547 | 545 | ||
| @@ -1032,7 +1030,7 @@ The return value is a property list with top-level keys :warnings and | |||
| 1032 | 1030 | ||
| 1033 | CHECK_PROCESS (proc); | 1031 | CHECK_PROCESS (proc); |
| 1034 | 1032 | ||
| 1035 | if (GNUTLS_INITSTAGE (proc) < GNUTLS_STAGE_INIT) | 1033 | if (GNUTLS_INITSTAGE (proc) != GNUTLS_STAGE_READY) |
| 1036 | return Qnil; | 1034 | return Qnil; |
| 1037 | 1035 | ||
| 1038 | /* Then collect any warnings already computed by the handshake. */ | 1036 | /* Then collect any warnings already computed by the handshake. */ |
| @@ -1176,6 +1174,149 @@ boot_error (struct Lisp_Process *p, const char *m, ...) | |||
| 1176 | verror (m, ap); | 1174 | verror (m, ap); |
| 1177 | } | 1175 | } |
| 1178 | 1176 | ||
| 1177 | Lisp_Object | ||
| 1178 | gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) | ||
| 1179 | { | ||
| 1180 | int ret; | ||
| 1181 | struct Lisp_Process *p = XPROCESS (proc); | ||
| 1182 | gnutls_session_t state = p->gnutls_state; | ||
| 1183 | unsigned int peer_verification; | ||
| 1184 | Lisp_Object warnings; | ||
| 1185 | int max_log_level = p->gnutls_log_level; | ||
| 1186 | Lisp_Object hostname, verify_error; | ||
| 1187 | bool verify_error_all = 0; | ||
| 1188 | char *c_hostname; | ||
| 1189 | |||
| 1190 | if (NILP (proplist)) | ||
| 1191 | proplist = Fcdr (Fplist_get (p->childp, QCtls_parameters)); | ||
| 1192 | |||
| 1193 | verify_error = Fplist_get (proplist, QCgnutls_bootprop_verify_error); | ||
| 1194 | hostname = Fplist_get (proplist, QCgnutls_bootprop_hostname); | ||
| 1195 | |||
| 1196 | if (EQ (verify_error, Qt)) | ||
| 1197 | { | ||
| 1198 | verify_error_all = 1; | ||
| 1199 | } | ||
| 1200 | else if (NILP (Flistp (verify_error))) | ||
| 1201 | { | ||
| 1202 | boot_error (p, "gnutls-boot: invalid :verify_error parameter (not a list)"); | ||
| 1203 | return Qnil; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | if (!STRINGP (hostname)) | ||
| 1207 | { | ||
| 1208 | boot_error (p, "gnutls-boot: invalid :hostname parameter (not a string)"); | ||
| 1209 | return Qnil; | ||
| 1210 | } | ||
| 1211 | c_hostname = SSDATA (hostname); | ||
| 1212 | |||
| 1213 | /* Now verify the peer, following | ||
| 1214 | http://www.gnu.org/software/gnutls/manual/html_node/Verifying-peer_0027s-certificate.html. | ||
| 1215 | The peer should present at least one certificate in the chain; do a | ||
| 1216 | check of the certificate's hostname with | ||
| 1217 | gnutls_x509_crt_check_hostname against :hostname. */ | ||
| 1218 | |||
| 1219 | ret = gnutls_certificate_verify_peers2 (state, &peer_verification); | ||
| 1220 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1221 | return gnutls_make_error (ret); | ||
| 1222 | |||
| 1223 | XPROCESS (proc)->gnutls_peer_verification = peer_verification; | ||
| 1224 | |||
| 1225 | warnings = Fplist_get (Fgnutls_peer_status (proc), intern (":warnings")); | ||
| 1226 | if (!NILP (warnings)) | ||
| 1227 | { | ||
| 1228 | Lisp_Object tail; | ||
| 1229 | for (tail = warnings; CONSP (tail); tail = XCDR (tail)) | ||
| 1230 | { | ||
| 1231 | Lisp_Object warning = XCAR (tail); | ||
| 1232 | Lisp_Object message = Fgnutls_peer_status_warning_describe (warning); | ||
| 1233 | if (!NILP (message)) | ||
| 1234 | GNUTLS_LOG2 (1, max_log_level, "verification:", SSDATA (message)); | ||
| 1235 | } | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | if (peer_verification != 0) | ||
| 1239 | { | ||
| 1240 | if (verify_error_all | ||
| 1241 | || !NILP (Fmember (QCgnutls_bootprop_trustfiles, verify_error))) | ||
| 1242 | { | ||
| 1243 | emacs_gnutls_deinit (proc); | ||
| 1244 | boot_error (p, "Certificate validation failed %s, verification code %x", | ||
| 1245 | c_hostname, peer_verification); | ||
| 1246 | return Qnil; | ||
| 1247 | } | ||
| 1248 | else | ||
| 1249 | { | ||
| 1250 | GNUTLS_LOG2 (1, max_log_level, "certificate validation failed:", | ||
| 1251 | c_hostname); | ||
| 1252 | } | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | /* Up to here the process is the same for X.509 certificates and | ||
| 1256 | OpenPGP keys. From now on X.509 certificates are assumed. This | ||
| 1257 | can be easily extended to work with openpgp keys as well. */ | ||
| 1258 | if (gnutls_certificate_type_get (state) == GNUTLS_CRT_X509) | ||
| 1259 | { | ||
| 1260 | gnutls_x509_crt_t gnutls_verify_cert; | ||
| 1261 | const gnutls_datum_t *gnutls_verify_cert_list; | ||
| 1262 | unsigned int gnutls_verify_cert_list_size; | ||
| 1263 | |||
| 1264 | ret = gnutls_x509_crt_init (&gnutls_verify_cert); | ||
| 1265 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1266 | return gnutls_make_error (ret); | ||
| 1267 | |||
| 1268 | gnutls_verify_cert_list = | ||
| 1269 | gnutls_certificate_get_peers (state, &gnutls_verify_cert_list_size); | ||
| 1270 | |||
| 1271 | if (gnutls_verify_cert_list == NULL) | ||
| 1272 | { | ||
| 1273 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1274 | emacs_gnutls_deinit (proc); | ||
| 1275 | boot_error (p, "No x509 certificate was found\n"); | ||
| 1276 | return Qnil; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | /* We only check the first certificate in the given chain. */ | ||
| 1280 | ret = gnutls_x509_crt_import (gnutls_verify_cert, | ||
| 1281 | &gnutls_verify_cert_list[0], | ||
| 1282 | GNUTLS_X509_FMT_DER); | ||
| 1283 | |||
| 1284 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1285 | { | ||
| 1286 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1287 | return gnutls_make_error (ret); | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | XPROCESS (proc)->gnutls_certificate = gnutls_verify_cert; | ||
| 1291 | |||
| 1292 | int err = gnutls_x509_crt_check_hostname (gnutls_verify_cert, | ||
| 1293 | c_hostname); | ||
| 1294 | check_memory_full (err); | ||
| 1295 | if (!err) | ||
| 1296 | { | ||
| 1297 | XPROCESS (proc)->gnutls_extra_peer_verification |= | ||
| 1298 | CERTIFICATE_NOT_MATCHING; | ||
| 1299 | if (verify_error_all | ||
| 1300 | || !NILP (Fmember (QCgnutls_bootprop_hostname, verify_error))) | ||
| 1301 | { | ||
| 1302 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1303 | emacs_gnutls_deinit (proc); | ||
| 1304 | boot_error (p, "The x509 certificate does not match \"%s\"", c_hostname); | ||
| 1305 | return Qnil; | ||
| 1306 | } | ||
| 1307 | else | ||
| 1308 | { | ||
| 1309 | GNUTLS_LOG2 (1, max_log_level, "x509 certificate does not match:", | ||
| 1310 | c_hostname); | ||
| 1311 | } | ||
| 1312 | } | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | /* Set this flag only if the whole initialization succeeded. */ | ||
| 1316 | XPROCESS (proc)->gnutls_p = 1; | ||
| 1317 | |||
| 1318 | return gnutls_make_error (ret); | ||
| 1319 | } | ||
| 1179 | 1320 | ||
| 1180 | DEFUN ("gnutls-boot", Fgnutls_boot, Sgnutls_boot, 3, 3, 0, | 1321 | DEFUN ("gnutls-boot", Fgnutls_boot, Sgnutls_boot, 3, 3, 0, |
| 1181 | doc: /* Initialize GnuTLS client for process PROC with TYPE+PROPLIST. | 1322 | doc: /* Initialize GnuTLS client for process PROC with TYPE+PROPLIST. |
| @@ -1235,14 +1376,12 @@ one trustfile (usually a CA bundle). */) | |||
| 1235 | { | 1376 | { |
| 1236 | int ret = GNUTLS_E_SUCCESS; | 1377 | int ret = GNUTLS_E_SUCCESS; |
| 1237 | int max_log_level = 0; | 1378 | int max_log_level = 0; |
| 1238 | bool verify_error_all = 0; | ||
| 1239 | 1379 | ||
| 1240 | gnutls_session_t state; | 1380 | gnutls_session_t state; |
| 1241 | gnutls_certificate_credentials_t x509_cred = NULL; | 1381 | gnutls_certificate_credentials_t x509_cred = NULL; |
| 1242 | gnutls_anon_client_credentials_t anon_cred = NULL; | 1382 | gnutls_anon_client_credentials_t anon_cred = NULL; |
| 1243 | Lisp_Object global_init; | 1383 | Lisp_Object global_init; |
| 1244 | char const *priority_string_ptr = "NORMAL"; /* default priority string. */ | 1384 | char const *priority_string_ptr = "NORMAL"; /* default priority string. */ |
| 1245 | unsigned int peer_verification; | ||
| 1246 | char *c_hostname; | 1385 | char *c_hostname; |
| 1247 | 1386 | ||
| 1248 | /* Placeholders for the property list elements. */ | 1387 | /* Placeholders for the property list elements. */ |
| @@ -1253,9 +1392,7 @@ one trustfile (usually a CA bundle). */) | |||
| 1253 | /* Lisp_Object callbacks; */ | 1392 | /* Lisp_Object callbacks; */ |
| 1254 | Lisp_Object loglevel; | 1393 | Lisp_Object loglevel; |
| 1255 | Lisp_Object hostname; | 1394 | Lisp_Object hostname; |
| 1256 | Lisp_Object verify_error; | ||
| 1257 | Lisp_Object prime_bits; | 1395 | Lisp_Object prime_bits; |
| 1258 | Lisp_Object warnings; | ||
| 1259 | struct Lisp_Process *p = XPROCESS (proc); | 1396 | struct Lisp_Process *p = XPROCESS (proc); |
| 1260 | 1397 | ||
| 1261 | CHECK_PROCESS (proc); | 1398 | CHECK_PROCESS (proc); |
| @@ -1280,19 +1417,8 @@ one trustfile (usually a CA bundle). */) | |||
| 1280 | keylist = Fplist_get (proplist, QCgnutls_bootprop_keylist); | 1417 | keylist = Fplist_get (proplist, QCgnutls_bootprop_keylist); |
| 1281 | crlfiles = Fplist_get (proplist, QCgnutls_bootprop_crlfiles); | 1418 | crlfiles = Fplist_get (proplist, QCgnutls_bootprop_crlfiles); |
| 1282 | loglevel = Fplist_get (proplist, QCgnutls_bootprop_loglevel); | 1419 | loglevel = Fplist_get (proplist, QCgnutls_bootprop_loglevel); |
| 1283 | verify_error = Fplist_get (proplist, QCgnutls_bootprop_verify_error); | ||
| 1284 | prime_bits = Fplist_get (proplist, QCgnutls_bootprop_min_prime_bits); | 1420 | prime_bits = Fplist_get (proplist, QCgnutls_bootprop_min_prime_bits); |
| 1285 | 1421 | ||
| 1286 | if (EQ (verify_error, Qt)) | ||
| 1287 | { | ||
| 1288 | verify_error_all = 1; | ||
| 1289 | } | ||
| 1290 | else if (NILP (Flistp (verify_error))) | ||
| 1291 | { | ||
| 1292 | boot_error (p, "gnutls-boot: invalid :verify_error parameter (not a list)"); | ||
| 1293 | return Qnil; | ||
| 1294 | } | ||
| 1295 | |||
| 1296 | if (!STRINGP (hostname)) | 1422 | if (!STRINGP (hostname)) |
| 1297 | { | 1423 | { |
| 1298 | boot_error (p, "gnutls-boot: invalid :hostname parameter (not a string)"); | 1424 | boot_error (p, "gnutls-boot: invalid :hostname parameter (not a string)"); |
| @@ -1521,112 +1647,7 @@ one trustfile (usually a CA bundle). */) | |||
| 1521 | if (ret < GNUTLS_E_SUCCESS) | 1647 | if (ret < GNUTLS_E_SUCCESS) |
| 1522 | return gnutls_make_error (ret); | 1648 | return gnutls_make_error (ret); |
| 1523 | 1649 | ||
| 1524 | /* Now verify the peer, following | 1650 | return gnutls_verify_boot (proc, proplist); |
| 1525 | http://www.gnu.org/software/gnutls/manual/html_node/Verifying-peer_0027s-certificate.html. | ||
| 1526 | The peer should present at least one certificate in the chain; do a | ||
| 1527 | check of the certificate's hostname with | ||
| 1528 | gnutls_x509_crt_check_hostname against :hostname. */ | ||
| 1529 | |||
| 1530 | ret = gnutls_certificate_verify_peers2 (state, &peer_verification); | ||
| 1531 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1532 | return gnutls_make_error (ret); | ||
| 1533 | |||
| 1534 | XPROCESS (proc)->gnutls_peer_verification = peer_verification; | ||
| 1535 | |||
| 1536 | warnings = Fplist_get (Fgnutls_peer_status (proc), intern (":warnings")); | ||
| 1537 | if (!NILP (warnings)) | ||
| 1538 | { | ||
| 1539 | Lisp_Object tail; | ||
| 1540 | for (tail = warnings; CONSP (tail); tail = XCDR (tail)) | ||
| 1541 | { | ||
| 1542 | Lisp_Object warning = XCAR (tail); | ||
| 1543 | Lisp_Object message = Fgnutls_peer_status_warning_describe (warning); | ||
| 1544 | if (!NILP (message)) | ||
| 1545 | GNUTLS_LOG2 (1, max_log_level, "verification:", SSDATA (message)); | ||
| 1546 | } | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | if (peer_verification != 0) | ||
| 1550 | { | ||
| 1551 | if (verify_error_all | ||
| 1552 | || !NILP (Fmember (QCgnutls_bootprop_trustfiles, verify_error))) | ||
| 1553 | { | ||
| 1554 | emacs_gnutls_deinit (proc); | ||
| 1555 | boot_error (p, "Certificate validation failed %s, verification code %x", | ||
| 1556 | c_hostname, peer_verification); | ||
| 1557 | return Qnil; | ||
| 1558 | } | ||
| 1559 | else | ||
| 1560 | { | ||
| 1561 | GNUTLS_LOG2 (1, max_log_level, "certificate validation failed:", | ||
| 1562 | c_hostname); | ||
| 1563 | } | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | /* Up to here the process is the same for X.509 certificates and | ||
| 1567 | OpenPGP keys. From now on X.509 certificates are assumed. This | ||
| 1568 | can be easily extended to work with openpgp keys as well. */ | ||
| 1569 | if (gnutls_certificate_type_get (state) == GNUTLS_CRT_X509) | ||
| 1570 | { | ||
| 1571 | gnutls_x509_crt_t gnutls_verify_cert; | ||
| 1572 | const gnutls_datum_t *gnutls_verify_cert_list; | ||
| 1573 | unsigned int gnutls_verify_cert_list_size; | ||
| 1574 | |||
| 1575 | ret = gnutls_x509_crt_init (&gnutls_verify_cert); | ||
| 1576 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1577 | return gnutls_make_error (ret); | ||
| 1578 | |||
| 1579 | gnutls_verify_cert_list = | ||
| 1580 | gnutls_certificate_get_peers (state, &gnutls_verify_cert_list_size); | ||
| 1581 | |||
| 1582 | if (gnutls_verify_cert_list == NULL) | ||
| 1583 | { | ||
| 1584 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1585 | emacs_gnutls_deinit (proc); | ||
| 1586 | boot_error (p, "No x509 certificate was found\n"); | ||
| 1587 | return Qnil; | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | /* We only check the first certificate in the given chain. */ | ||
| 1591 | ret = gnutls_x509_crt_import (gnutls_verify_cert, | ||
| 1592 | &gnutls_verify_cert_list[0], | ||
| 1593 | GNUTLS_X509_FMT_DER); | ||
| 1594 | |||
| 1595 | if (ret < GNUTLS_E_SUCCESS) | ||
| 1596 | { | ||
| 1597 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1598 | return gnutls_make_error (ret); | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | XPROCESS (proc)->gnutls_certificate = gnutls_verify_cert; | ||
| 1602 | |||
| 1603 | int err = gnutls_x509_crt_check_hostname (gnutls_verify_cert, | ||
| 1604 | c_hostname); | ||
| 1605 | check_memory_full (err); | ||
| 1606 | if (!err) | ||
| 1607 | { | ||
| 1608 | XPROCESS (proc)->gnutls_extra_peer_verification |= | ||
| 1609 | CERTIFICATE_NOT_MATCHING; | ||
| 1610 | if (verify_error_all | ||
| 1611 | || !NILP (Fmember (QCgnutls_bootprop_hostname, verify_error))) | ||
| 1612 | { | ||
| 1613 | gnutls_x509_crt_deinit (gnutls_verify_cert); | ||
| 1614 | emacs_gnutls_deinit (proc); | ||
| 1615 | boot_error (p, "The x509 certificate does not match \"%s\"", c_hostname); | ||
| 1616 | return Qnil; | ||
| 1617 | } | ||
| 1618 | else | ||
| 1619 | { | ||
| 1620 | GNUTLS_LOG2 (1, max_log_level, "x509 certificate does not match:", | ||
| 1621 | c_hostname); | ||
| 1622 | } | ||
| 1623 | } | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | /* Set this flag only if the whole initialization succeeded. */ | ||
| 1627 | XPROCESS (proc)->gnutls_p = 1; | ||
| 1628 | |||
| 1629 | return gnutls_make_error (ret); | ||
| 1630 | } | 1651 | } |
| 1631 | 1652 | ||
| 1632 | DEFUN ("gnutls-bye", Fgnutls_bye, | 1653 | DEFUN ("gnutls-bye", Fgnutls_bye, |
diff --git a/src/gnutls.h b/src/gnutls.h index cb521350b9d..d03332ec2b6 100644 --- a/src/gnutls.h +++ b/src/gnutls.h | |||
| @@ -85,6 +85,7 @@ extern void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err); | |||
| 85 | extern Lisp_Object emacs_gnutls_deinit (Lisp_Object); | 85 | extern Lisp_Object emacs_gnutls_deinit (Lisp_Object); |
| 86 | extern Lisp_Object emacs_gnutls_global_init (void); | 86 | extern Lisp_Object emacs_gnutls_global_init (void); |
| 87 | extern int gnutls_try_handshake (struct Lisp_Process *p); | 87 | extern int gnutls_try_handshake (struct Lisp_Process *p); |
| 88 | extern Lisp_Object gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist); | ||
| 88 | 89 | ||
| 89 | #endif | 90 | #endif |
| 90 | 91 | ||
diff --git a/src/process.c b/src/process.c index d78b04f9770..4a11e7f8b8f 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -4919,7 +4919,10 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 4919 | p->gnutls_handshakes_tried++; | 4919 | p->gnutls_handshakes_tried++; |
| 4920 | 4920 | ||
| 4921 | if (p->gnutls_initstage == GNUTLS_STAGE_READY) | 4921 | if (p->gnutls_initstage == GNUTLS_STAGE_READY) |
| 4922 | finish_after_tls_connection (aproc); | 4922 | { |
| 4923 | gnutls_verify_boot (proc, Qnil); | ||
| 4924 | finish_after_tls_connection (aproc); | ||
| 4925 | } | ||
| 4923 | else if (p->gnutls_handshakes_tried > | 4926 | else if (p->gnutls_handshakes_tried > |
| 4924 | GNUTLS_EMACS_HANDSHAKES_LIMIT) | 4927 | GNUTLS_EMACS_HANDSHAKES_LIMIT) |
| 4925 | { | 4928 | { |