aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Ingebrigtsen2016-02-16 15:56:56 +1100
committerLars Ingebrigtsen2016-02-16 15:56:56 +1100
commitac6e085cf6b26257cfe181f17828432d414cb3a6 (patch)
tree9d382588ce869a2b816373e278b2805ffbb90f59 /src
parentc43bb7f136ab9e9439a4b5c045040a12cbe8bda0 (diff)
downloademacs-ac6e085cf6b26257cfe181f17828432d414cb3a6.tar.gz
emacs-ac6e085cf6b26257cfe181f17828432d414cb3a6.zip
Implement asynch TLS negotiation
* src/gnutls.c (gnutls_try_handshake): Factor out into its own function. (emacs_gnutls_handshake): Use it. (emacs_gnutls_read): Just return instead of retrying the handshake. * src/process.c (finish_after_tls_connection): Factor out into its own function. (connect_network_socket): Use it. (wait_reading_process_output): Retry TLS handshakes. (wait_reading_process_output): Defer sentinel until TLS completes.
Diffstat (limited to 'src')
-rw-r--r--src/gnutls.c71
-rw-r--r--src/gnutls.h1
-rw-r--r--src/process.c131
3 files changed, 119 insertions, 84 deletions
diff --git a/src/gnutls.c b/src/gnutls.c
index 948a0c56f14..6573c87cf78 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -397,11 +397,42 @@ gnutls_log_function2i (int level, const char *string, int extra)
397 message ("gnutls.c: [%d] %s %d", level, string, extra); 397 message ("gnutls.c: [%d] %s %d", level, string, extra);
398} 398}
399 399
400int
401gnutls_try_handshake (struct Lisp_Process *proc)
402{
403 gnutls_session_t state = proc->gnutls_state;
404 int ret;
405
406 do
407 {
408 ret = gnutls_handshake (state);
409 emacs_gnutls_handle_error (state, ret);
410 QUIT;
411 }
412 while (ret < 0 && gnutls_error_is_fatal (ret) == 0 &&
413 ! proc->is_non_blocking_client);
414
415 proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
416
417 if (proc->is_non_blocking_client)
418 proc->gnutls_p = 1;
419
420 if (ret == GNUTLS_E_SUCCESS)
421 {
422 /* Here we're finally done. */
423 proc->gnutls_initstage = GNUTLS_STAGE_READY;
424 }
425 else
426 {
427 //check_memory_full (gnutls_alert_send_appropriate (state, ret));
428 }
429 return ret;
430}
431
400static int 432static int
401emacs_gnutls_handshake (struct Lisp_Process *proc) 433emacs_gnutls_handshake (struct Lisp_Process *proc)
402{ 434{
403 gnutls_session_t state = proc->gnutls_state; 435 gnutls_session_t state = proc->gnutls_state;
404 int ret;
405 436
406 if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO) 437 if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO)
407 return -1; 438 return -1;
@@ -443,26 +474,7 @@ emacs_gnutls_handshake (struct Lisp_Process *proc)
443 proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET; 474 proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET;
444 } 475 }
445 476
446 do 477 return gnutls_try_handshake (proc);
447 {
448 ret = gnutls_handshake (state);
449 emacs_gnutls_handle_error (state, ret);
450 QUIT;
451 }
452 while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
453
454 proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
455
456 if (ret == GNUTLS_E_SUCCESS)
457 {
458 /* Here we're finally done. */
459 proc->gnutls_initstage = GNUTLS_STAGE_READY;
460 }
461 else
462 {
463 check_memory_full (gnutls_alert_send_appropriate (state, ret));
464 }
465 return ret;
466} 478}
467 479
468ptrdiff_t 480ptrdiff_t
@@ -531,23 +543,8 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte)
531 int log_level = proc->gnutls_log_level; 543 int log_level = proc->gnutls_log_level;
532 544
533 if (proc->gnutls_initstage != GNUTLS_STAGE_READY) 545 if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
534 { 546 return -1;
535 /* If the handshake count is under the limit, try the handshake
536 again and increment the handshake count. This count is kept
537 per process (connection), not globally. */
538 if (proc->gnutls_handshakes_tried < GNUTLS_EMACS_HANDSHAKES_LIMIT)
539 {
540 proc->gnutls_handshakes_tried++;
541 emacs_gnutls_handshake (proc);
542 GNUTLS_LOG2i (5, log_level, "Retried handshake",
543 proc->gnutls_handshakes_tried);
544 return -1;
545 }
546 547
547 GNUTLS_LOG (2, log_level, "Giving up on handshake; resetting retries");
548 proc->gnutls_handshakes_tried = 0;
549 return 0;
550 }
551 rtnval = gnutls_record_recv (state, buf, nbyte); 548 rtnval = gnutls_record_recv (state, buf, nbyte);
552 if (rtnval >= 0) 549 if (rtnval >= 0)
553 return rtnval; 550 return rtnval;
diff --git a/src/gnutls.h b/src/gnutls.h
index 8e879c168bd..cb521350b9d 100644
--- a/src/gnutls.h
+++ b/src/gnutls.h
@@ -84,6 +84,7 @@ extern void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err);
84#endif 84#endif
85extern Lisp_Object emacs_gnutls_deinit (Lisp_Object); 85extern Lisp_Object emacs_gnutls_deinit (Lisp_Object);
86extern Lisp_Object emacs_gnutls_global_init (void); 86extern Lisp_Object emacs_gnutls_global_init (void);
87extern int gnutls_try_handshake (struct Lisp_Process *p);
87 88
88#endif 89#endif
89 90
diff --git a/src/process.c b/src/process.c
index 9a3bcaed389..ec31ea87c7b 100644
--- a/src/process.c
+++ b/src/process.c
@@ -281,9 +281,7 @@ static int max_input_desc;
281 281
282/* Indexed by descriptor, gives the process (if any) for that descriptor. */ 282/* Indexed by descriptor, gives the process (if any) for that descriptor. */
283static Lisp_Object chan_process[FD_SETSIZE]; 283static Lisp_Object chan_process[FD_SETSIZE];
284#ifdef HAVE_GETADDRINFO_A
285static void wait_for_socket_fds (Lisp_Object process, char *name); 284static void wait_for_socket_fds (Lisp_Object process, char *name);
286#endif
287 285
288/* Alist of elements (NAME . PROCESS). */ 286/* Alist of elements (NAME . PROCESS). */
289static Lisp_Object Vprocess_alist; 287static Lisp_Object Vprocess_alist;
@@ -3038,7 +3036,45 @@ void set_network_socket_coding_system (Lisp_Object proc)
3038 = !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system); 3036 = !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system);
3039} 3037}
3040 3038
3041void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) 3039#ifdef HAVE_GNUTLS
3040void
3041finish_after_tls_connection (Lisp_Object proc)
3042{
3043 struct Lisp_Process *p = XPROCESS (proc);
3044 Lisp_Object contact = p->childp;
3045 Lisp_Object result = Qt;
3046
3047 if (!NILP (Ffboundp (Qnsm_verify_connection)))
3048 result = call3 (Qnsm_verify_connection,
3049 proc,
3050 Fplist_get (contact, QChost),
3051 Fplist_get (contact, QCservice));
3052
3053 if (NILP (result))
3054 {
3055 pset_status (p, list2 (Qfailed,
3056 build_string ("The Network Security Manager stopped the connections")));
3057 deactivate_process (proc);
3058 }
3059 else
3060 {
3061 /* If we cleared the connection wait mask before we did
3062 the TLS setup, then we have to say that the process
3063 is finally "open" here. */
3064 if (! FD_ISSET (p->outfd, &connect_wait_mask))
3065 {
3066 pset_status (p, Qrun);
3067 /* Execute the sentinel here. If we had relied on
3068 status_notify to do it later, it will read input
3069 from the process before calling the sentinel. */
3070 exec_sentinel (proc, build_string ("open\n"));
3071 }
3072 }
3073}
3074#endif
3075
3076void
3077connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
3042{ 3078{
3043 ptrdiff_t count = SPECPDL_INDEX (); 3079 ptrdiff_t count = SPECPDL_INDEX ();
3044 ptrdiff_t count1; 3080 ptrdiff_t count1;
@@ -3359,8 +3395,10 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
3359 boot = Fgnutls_boot (proc, XCAR (params), XCDR (params)); 3395 boot = Fgnutls_boot (proc, XCAR (params), XCDR (params));
3360 p->gnutls_boot_parameters = Qnil; 3396 p->gnutls_boot_parameters = Qnil;
3361 3397
3362 if (NILP (boot) || STRINGP (boot) || 3398 if (p->gnutls_initstage == GNUTLS_STAGE_READY)
3363 p->gnutls_initstage != GNUTLS_STAGE_READY) 3399 /* Run sentinels, etc. */
3400 finish_after_tls_connection (proc);
3401 else if (p->gnutls_initstage != GNUTLS_STAGE_HANDSHAKE_TRIED)
3364 { 3402 {
3365 deactivate_process (proc); 3403 deactivate_process (proc);
3366 if (NILP (boot)) 3404 if (NILP (boot))
@@ -3369,37 +3407,6 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
3369 else 3407 else
3370 pset_status (p, list2 (Qfailed, boot)); 3408 pset_status (p, list2 (Qfailed, boot));
3371 } 3409 }
3372 else
3373 {
3374 Lisp_Object result = Qt;
3375
3376 if (!NILP (Ffboundp (Qnsm_verify_connection)))
3377 result = call3 (Qnsm_verify_connection,
3378 proc,
3379 Fplist_get (contact, QChost),
3380 Fplist_get (contact, QCservice));
3381
3382 if (NILP (result))
3383 {
3384 pset_status (p, list2 (Qfailed,
3385 build_string ("The Network Security Manager stopped the connections")));
3386 deactivate_process (proc);
3387 }
3388 else
3389 {
3390 /* If we cleared the connection wait mask before we did
3391 the TLS setup, then we have to say that the process
3392 is finally "open" here. */
3393 if (! FD_ISSET (p->outfd, &connect_wait_mask))
3394 {
3395 pset_status (p, Qrun);
3396 /* Execute the sentinel here. If we had relied on
3397 status_notify to do it later, it will read input
3398 from the process before calling the sentinel. */
3399 exec_sentinel (proc, build_string ("open\n"));
3400 }
3401 }
3402 }
3403 } 3410 }
3404#endif 3411#endif
3405 3412
@@ -4747,8 +4754,8 @@ static void
4747wait_for_tls_negotiation (Lisp_Object process) 4754wait_for_tls_negotiation (Lisp_Object process)
4748{ 4755{
4749#ifdef HAVE_GNUTLS 4756#ifdef HAVE_GNUTLS
4750 while (EQ (XPROCESS (process)->status, Qconnect) && 4757 while (XPROCESS (process)->gnutls_p &&
4751 !NILP (XPROCESS (process)->gnutls_boot_parameters)) 4758 XPROCESS (process)->gnutls_initstage != GNUTLS_STAGE_READY)
4752 { 4759 {
4753 printf("Waiting for TLS...\n"); 4760 printf("Waiting for TLS...\n");
4754 wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0); 4761 wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0);
@@ -4881,7 +4888,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
4881 if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) 4888 if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell)))
4882 break; 4889 break;
4883 4890
4884#ifdef HAVE_GETADDRINFO_A 4891#if defined (HAVE_GETADDRINFO_A) || defined (HAVE_GNUTLS)
4885 { 4892 {
4886 Lisp_Object ip_addresses; 4893 Lisp_Object ip_addresses;
4887 Lisp_Object process_list_head, aproc; 4894 Lisp_Object process_list_head, aproc;
@@ -4891,17 +4898,41 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
4891 { 4898 {
4892 p = XPROCESS (aproc); 4899 p = XPROCESS (aproc);
4893 4900
4894 if (p->dns_requests && 4901 if (! wait_proc || p == wait_proc)
4895 (! wait_proc || p == wait_proc))
4896 { 4902 {
4897 ip_addresses = check_for_dns (aproc); 4903#ifdef HAVE_GETADDRINFO_A
4898 if (!NILP (ip_addresses) && 4904 /* Check for pending DNS requests. */
4899 !EQ (ip_addresses, Qt)) 4905 if (p->dns_requests)
4900 connect_network_socket (aproc, ip_addresses); 4906 {
4907 ip_addresses = check_for_dns (aproc);
4908 if (!NILP (ip_addresses) &&
4909 !EQ (ip_addresses, Qt))
4910 connect_network_socket (aproc, ip_addresses);
4911 }
4912#endif
4913#ifdef HAVE_GNUTLS
4914 /* Continue TLS negotiation. */
4915 if (p->gnutls_initstage == GNUTLS_STAGE_HANDSHAKE_TRIED &&
4916 p->is_non_blocking_client)
4917 {
4918 gnutls_try_handshake (p);
4919 p->gnutls_handshakes_tried++;
4920
4921 if (p->gnutls_initstage == GNUTLS_STAGE_READY)
4922 finish_after_tls_connection (aproc);
4923 else if (p->gnutls_handshakes_tried >
4924 GNUTLS_EMACS_HANDSHAKES_LIMIT)
4925 {
4926 deactivate_process (proc);
4927 pset_status (p, list2 (Qfailed,
4928 build_string ("TLS negotiation failed")));
4929 }
4930 }
4931#endif
4901 } 4932 }
4902 } 4933 }
4903 } 4934 }
4904#endif /* HAVE_GETADDRINFO_A */ 4935#endif /* GETADDRINFO_A or GNUTLS */
4905 4936
4906 /* Compute time from now till when time limit is up. */ 4937 /* Compute time from now till when time limit is up. */
4907 /* Exit if already run out. */ 4938 /* Exit if already run out. */
@@ -5522,7 +5553,13 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
5522 } 5553 }
5523 else 5554 else
5524 { 5555 {
5525 if (NILP (p->gnutls_boot_parameters)) 5556#ifdef HAVE_GNUTLS
5557 /* If we have an incompletely set up TLS connection,
5558 then defer the sentinel signalling until
5559 later. */
5560 if (NILP (p->gnutls_boot_parameters) &&
5561 !p->gnutls_p)
5562#endif
5526 { 5563 {
5527 pset_status (p, Qrun); 5564 pset_status (p, Qrun);
5528 /* Execute the sentinel here. If we had relied on 5565 /* Execute the sentinel here. If we had relied on