Commit ac6e085c authored by Lars Ingebrigtsen's avatar Lars Ingebrigtsen
Browse files

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.
parent c43bb7f1
...@@ -397,11 +397,42 @@ gnutls_log_function2i (int level, const char *string, int extra) ...@@ -397,11 +397,42 @@ gnutls_log_function2i (int level, const char *string, int extra)
message ("gnutls.c: [%d] %s %d", level, string, extra); message ("gnutls.c: [%d] %s %d", level, string, extra);
} }
int
gnutls_try_handshake (struct Lisp_Process *proc)
{
gnutls_session_t state = proc->gnutls_state;
int ret;
do
{
ret = gnutls_handshake (state);
emacs_gnutls_handle_error (state, ret);
QUIT;
}
while (ret < 0 && gnutls_error_is_fatal (ret) == 0 &&
! proc->is_non_blocking_client);
proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
if (proc->is_non_blocking_client)
proc->gnutls_p = 1;
if (ret == GNUTLS_E_SUCCESS)
{
/* Here we're finally done. */
proc->gnutls_initstage = GNUTLS_STAGE_READY;
}
else
{
//check_memory_full (gnutls_alert_send_appropriate (state, ret));
}
return ret;
}
static int static int
emacs_gnutls_handshake (struct Lisp_Process *proc) emacs_gnutls_handshake (struct Lisp_Process *proc)
{ {
gnutls_session_t state = proc->gnutls_state; gnutls_session_t state = proc->gnutls_state;
int ret;
if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO) if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO)
return -1; return -1;
...@@ -443,26 +474,7 @@ emacs_gnutls_handshake (struct Lisp_Process *proc) ...@@ -443,26 +474,7 @@ emacs_gnutls_handshake (struct Lisp_Process *proc)
proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET; proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET;
} }
do return gnutls_try_handshake (proc);
{
ret = gnutls_handshake (state);
emacs_gnutls_handle_error (state, ret);
QUIT;
}
while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
if (ret == GNUTLS_E_SUCCESS)
{
/* Here we're finally done. */
proc->gnutls_initstage = GNUTLS_STAGE_READY;
}
else
{
check_memory_full (gnutls_alert_send_appropriate (state, ret));
}
return ret;
} }
ptrdiff_t ptrdiff_t
...@@ -531,23 +543,8 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte) ...@@ -531,23 +543,8 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte)
int log_level = proc->gnutls_log_level; int log_level = proc->gnutls_log_level;
if (proc->gnutls_initstage != GNUTLS_STAGE_READY) if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
{ return -1;
/* If the handshake count is under the limit, try the handshake
again and increment the handshake count. This count is kept
per process (connection), not globally. */
if (proc->gnutls_handshakes_tried < GNUTLS_EMACS_HANDSHAKES_LIMIT)
{
proc->gnutls_handshakes_tried++;
emacs_gnutls_handshake (proc);
GNUTLS_LOG2i (5, log_level, "Retried handshake",
proc->gnutls_handshakes_tried);
return -1;
}
GNUTLS_LOG (2, log_level, "Giving up on handshake; resetting retries");
proc->gnutls_handshakes_tried = 0;
return 0;
}
rtnval = gnutls_record_recv (state, buf, nbyte); rtnval = gnutls_record_recv (state, buf, nbyte);
if (rtnval >= 0) if (rtnval >= 0)
return rtnval; return rtnval;
......
...@@ -84,6 +84,7 @@ extern void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err); ...@@ -84,6 +84,7 @@ extern void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err);
#endif #endif
extern Lisp_Object emacs_gnutls_deinit (Lisp_Object); extern Lisp_Object emacs_gnutls_deinit (Lisp_Object);
extern Lisp_Object emacs_gnutls_global_init (void); extern Lisp_Object emacs_gnutls_global_init (void);
extern int gnutls_try_handshake (struct Lisp_Process *p);
#endif #endif
......
...@@ -281,9 +281,7 @@ static int max_input_desc; ...@@ -281,9 +281,7 @@ static int max_input_desc;
/* Indexed by descriptor, gives the process (if any) for that descriptor. */ /* Indexed by descriptor, gives the process (if any) for that descriptor. */
static Lisp_Object chan_process[FD_SETSIZE]; static Lisp_Object chan_process[FD_SETSIZE];
#ifdef HAVE_GETADDRINFO_A
static void wait_for_socket_fds (Lisp_Object process, char *name); static void wait_for_socket_fds (Lisp_Object process, char *name);
#endif
/* Alist of elements (NAME . PROCESS). */ /* Alist of elements (NAME . PROCESS). */
static Lisp_Object Vprocess_alist; static Lisp_Object Vprocess_alist;
...@@ -3038,7 +3036,45 @@ void set_network_socket_coding_system (Lisp_Object proc) ...@@ -3038,7 +3036,45 @@ void set_network_socket_coding_system (Lisp_Object proc)
= !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system); = !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system);
} }
void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) #ifdef HAVE_GNUTLS
void
finish_after_tls_connection (Lisp_Object proc)
{
struct Lisp_Process *p = XPROCESS (proc);
Lisp_Object contact = p->childp;
Lisp_Object result = Qt;
if (!NILP (Ffboundp (Qnsm_verify_connection)))
result = call3 (Qnsm_verify_connection,
proc,
Fplist_get (contact, QChost),
Fplist_get (contact, QCservice));
if (NILP (result))
{
pset_status (p, list2 (Qfailed,
build_string ("The Network Security Manager stopped the connections")));
deactivate_process (proc);
}
else
{
/* If we cleared the connection wait mask before we did
the TLS setup, then we have to say that the process
is finally "open" here. */
if (! FD_ISSET (p->outfd, &connect_wait_mask))
{
pset_status (p, Qrun);
/* Execute the sentinel here. If we had relied on
status_notify to do it later, it will read input
from the process before calling the sentinel. */
exec_sentinel (proc, build_string ("open\n"));
}
}
}
#endif
void
connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
{ {
ptrdiff_t count = SPECPDL_INDEX (); ptrdiff_t count = SPECPDL_INDEX ();
ptrdiff_t count1; ptrdiff_t count1;
...@@ -3359,8 +3395,10 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) ...@@ -3359,8 +3395,10 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
boot = Fgnutls_boot (proc, XCAR (params), XCDR (params)); boot = Fgnutls_boot (proc, XCAR (params), XCDR (params));
p->gnutls_boot_parameters = Qnil; p->gnutls_boot_parameters = Qnil;
if (NILP (boot) || STRINGP (boot) || if (p->gnutls_initstage == GNUTLS_STAGE_READY)
p->gnutls_initstage != GNUTLS_STAGE_READY) /* Run sentinels, etc. */
finish_after_tls_connection (proc);
else if (p->gnutls_initstage != GNUTLS_STAGE_HANDSHAKE_TRIED)
{ {
deactivate_process (proc); deactivate_process (proc);
if (NILP (boot)) if (NILP (boot))
...@@ -3369,37 +3407,6 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) ...@@ -3369,37 +3407,6 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
else else
pset_status (p, list2 (Qfailed, boot)); pset_status (p, list2 (Qfailed, boot));
} }
else
{
Lisp_Object result = Qt;
if (!NILP (Ffboundp (Qnsm_verify_connection)))
result = call3 (Qnsm_verify_connection,
proc,
Fplist_get (contact, QChost),
Fplist_get (contact, QCservice));
if (NILP (result))
{
pset_status (p, list2 (Qfailed,
build_string ("The Network Security Manager stopped the connections")));
deactivate_process (proc);
}
else
{
/* If we cleared the connection wait mask before we did
the TLS setup, then we have to say that the process
is finally "open" here. */
if (! FD_ISSET (p->outfd, &connect_wait_mask))
{
pset_status (p, Qrun);
/* Execute the sentinel here. If we had relied on
status_notify to do it later, it will read input
from the process before calling the sentinel. */
exec_sentinel (proc, build_string ("open\n"));
}
}
}
} }
#endif #endif
...@@ -4747,8 +4754,8 @@ static void ...@@ -4747,8 +4754,8 @@ static void
wait_for_tls_negotiation (Lisp_Object process) wait_for_tls_negotiation (Lisp_Object process)
{ {
#ifdef HAVE_GNUTLS #ifdef HAVE_GNUTLS
while (EQ (XPROCESS (process)->status, Qconnect) && while (XPROCESS (process)->gnutls_p &&
!NILP (XPROCESS (process)->gnutls_boot_parameters)) XPROCESS (process)->gnutls_initstage != GNUTLS_STAGE_READY)
{ {
printf("Waiting for TLS...\n"); printf("Waiting for TLS...\n");
wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0); 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,7 +4888,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell)))
break; break;
#ifdef HAVE_GETADDRINFO_A #if defined (HAVE_GETADDRINFO_A) || defined (HAVE_GNUTLS)
{ {
Lisp_Object ip_addresses; Lisp_Object ip_addresses;
Lisp_Object process_list_head, aproc; Lisp_Object process_list_head, aproc;
...@@ -4891,17 +4898,41 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, ...@@ -4891,17 +4898,41 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
{ {
p = XPROCESS (aproc); p = XPROCESS (aproc);
if (p->dns_requests && if (! wait_proc || p == wait_proc)
(! wait_proc || p == wait_proc))
{ {
ip_addresses = check_for_dns (aproc); #ifdef HAVE_GETADDRINFO_A
if (!NILP (ip_addresses) && /* Check for pending DNS requests. */
!EQ (ip_addresses, Qt)) if (p->dns_requests)
connect_network_socket (aproc, ip_addresses); {
ip_addresses = check_for_dns (aproc);
if (!NILP (ip_addresses) &&
!EQ (ip_addresses, Qt))
connect_network_socket (aproc, ip_addresses);
}
#endif
#ifdef HAVE_GNUTLS
/* Continue TLS negotiation. */
if (p->gnutls_initstage == GNUTLS_STAGE_HANDSHAKE_TRIED &&
p->is_non_blocking_client)
{
gnutls_try_handshake (p);
p->gnutls_handshakes_tried++;
if (p->gnutls_initstage == GNUTLS_STAGE_READY)
finish_after_tls_connection (aproc);
else if (p->gnutls_handshakes_tried >
GNUTLS_EMACS_HANDSHAKES_LIMIT)
{
deactivate_process (proc);
pset_status (p, list2 (Qfailed,
build_string ("TLS negotiation failed")));
}
}
#endif
} }
} }
} }
#endif /* HAVE_GETADDRINFO_A */ #endif /* GETADDRINFO_A or GNUTLS */
/* Compute time from now till when time limit is up. */ /* Compute time from now till when time limit is up. */
/* Exit if already run out. */ /* Exit if already run out. */
...@@ -5522,7 +5553,13 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, ...@@ -5522,7 +5553,13 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
} }
else else
{ {
if (NILP (p->gnutls_boot_parameters)) #ifdef HAVE_GNUTLS
/* If we have an incompletely set up TLS connection,
then defer the sentinel signalling until
later. */
if (NILP (p->gnutls_boot_parameters) &&
!p->gnutls_p)
#endif
{ {
pset_status (p, Qrun); pset_status (p, Qrun);
/* Execute the sentinel here. If we had relied on /* Execute the sentinel here. If we had relied on
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment