1 /***************************************
2   WWWOFFLE - World Wide Web Offline Explorer - Version 2.9j.
3 
4   Functions for file input and output using gnutls.
5   ******************/ /******************
6   Written by Andrew M. Bishop
7 
8   This file Copyright 2005-2016 Andrew M. Bishop
9   It may be distributed under the GNU Public License, version 2, or
10   any higher version.  See section COPYING of the GNU Public license
11   for conditions under which this file may be redistributed.
12   ***************************************/
13 
14 
15 #include "autoconfig.h"
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #if TIME_WITH_SYS_TIME
22 # include <sys/time.h>
23 # include <time.h>
24 #else
25 # if HAVE_SYS_TIME_H
26 #  include <sys/time.h>
27 # else
28 #  include <time.h>
29 # endif
30 #endif
31 
32 #include <errno.h>
33 
34 #include <setjmp.h>
35 #include <signal.h>
36 
37 #if USE_GNUTLS
38 #include <gnutls/gnutls.h>
39 #include <gnutls/x509.h>
40 #endif
41 
42 #include "io.h"
43 #include "iopriv.h"
44 #include "errors.h"
45 
46 
47 #if USE_GNUTLS
48 
49 #include "certificates.h"
50 
51 
52 /*+ A longjump context for write timeouts. +*/
53 static jmp_buf write_jmp_env;
54 
55 
56 /* Local functions */
57 
58 static int pull_with_timeout(gnutls_transport_ptr_t ptr,       void* data, size_t size);
59 static int push_with_timeout(gnutls_transport_ptr_t ptr, const void* data, size_t size);
60 static int handshake_sni_callback(gnutls_session_t session);
61 
62 static int set_credentials(io_gnutls *context);
63 
64 static void sigalarm(int signum);
65 
66 static ssize_t write_all(gnutls_session_t session,const char *data,size_t n);
67 
68 static void set_gnutls_error(int err,gnutls_session_t session);
69 
70 
71 /*++++++++++++++++++++++++++++++++++++++
72   Initialise the gnutls context information.
73 
74   io_gnutls *io_init_gnutls Returns a new gnutls io context.
75 
76   int fd The file descriptor for the session.
77 
78   const char *host The name of the server to serve as or NULL for a client.
79 
80   int type A flag set to 0 for client connection, 1 for built-in server or 2 for a fake server.
81 
82   unsigned timeout_r The read timeout or 0 for none.
83 
84   unsigned timeout_w The write timeout or 0 for none.
85   ++++++++++++++++++++++++++++++++++++++*/
86 
io_init_gnutls(int fd,const char * host,int type,unsigned timeout_r,unsigned timeout_w)87 io_gnutls *io_init_gnutls(int fd,const char *host,int type,unsigned timeout_r,unsigned timeout_w)
88 {
89  io_gnutls *context=(io_gnutls*)calloc(1,sizeof(io_gnutls));
90 
91  /* Initialise the gnutls session. */
92 
93  if(type)
94     io_errno=gnutls_init(&context->session,GNUTLS_SERVER);
95  else
96     io_errno=gnutls_init(&context->session,GNUTLS_CLIENT);
97 
98  if(io_errno!=GNUTLS_E_SUCCESS)
99    {
100     set_gnutls_error(io_errno,context->session);
101 
102     PrintMessage(Warning,"GNUTLS Failed to initialise session [%s].",io_strerror);
103 
104     free(context);
105     return(NULL);
106    }
107 
108  io_errno=gnutls_set_default_priority(context->session);
109 
110  if(io_errno!=GNUTLS_E_SUCCESS)
111    {
112     set_gnutls_error(io_errno,context->session);
113     gnutls_deinit(context->session);
114 
115     PrintMessage(Warning,"GNUTLS Failed to set session priority [%s].",io_strerror);
116 
117     free(context);
118     return(NULL);
119    }
120 
121  /* Set the server credentials (in callback for server mode, now for client) */
122 
123  if(type)
124    {
125     context->type=type;
126     context->host=host;
127 
128     gnutls_handshake_set_post_client_hello_function(context->session,
129                                                     (gnutls_handshake_post_client_hello_func)handshake_sni_callback);
130    }
131  else /* if(type==0) */
132    {
133     gnutls_server_name_set(context->session,GNUTLS_NAME_DNS,host,strlen(host));
134 
135     context->cred=GetClientCredentials();
136 
137     if(set_credentials(context))
138       {
139        free(context);
140        return(NULL);
141       }
142    }
143 
144  /* Store the file descriptor and timeout and set the push and pull functions. */
145 
146  context->fd=fd;
147 
148  context->r_timeout=timeout_r;
149  context->w_timeout=timeout_w;
150 
151  gnutls_transport_set_ptr(context->session,context);
152 
153  gnutls_transport_set_pull_function(context->session,(gnutls_pull_func)pull_with_timeout);
154  gnutls_transport_set_push_function(context->session,(gnutls_push_func)push_with_timeout);
155 
156  /* Handshake the session on the socket */
157 
158  do
159    {
160     io_errno=gnutls_handshake(context->session);
161    }
162  while(io_errno!=GNUTLS_E_SUCCESS && !gnutls_error_is_fatal(io_errno));
163 
164  if(io_errno!=GNUTLS_E_SUCCESS)
165    {
166     set_gnutls_error(io_errno,context->session);
167     gnutls_bye(context->session,GNUTLS_SHUT_WR);
168     gnutls_deinit(context->session);
169 
170     PrintMessage(Warning,"GNUTLS handshake has failed [%s].",io_strerror);
171 
172     free(context);
173     return(NULL);
174    }
175 
176  /* Save the server credentials */
177 
178  if(type==0)
179     PutRealCertificate(context->session,host);
180 
181  return(context);
182 }
183 
184 
185 /*++++++++++++++++++++++++++++++++++++++
186   A function to be called by gnutles to read data from the socket.
187 
188   int pull_with_timeout Return the number of bytes read or a negative number for an error.
189 
190   gnutls_transport_ptr_t ptr The gnutls per-session transport pointer.
191 
192   void* data The buffer to fill with data read from the sockeet.
193 
194   size_t size The amount of data to read.
195 
196   This is necessary because the SNI callback only passes the gnutls session but we
197   need the WWWOFFLE IO context so we have to put that into the gnutls_transport_ptr.
198   Therefore the timeout function is moved into here so that it applies to all gnutls
199   data transfers.
200   ++++++++++++++++++++++++++++++++++++++*/
201 
pull_with_timeout(gnutls_transport_ptr_t ptr,void * data,size_t size)202 static int pull_with_timeout(gnutls_transport_ptr_t ptr, void* data, size_t size)
203 {
204  io_gnutls *context=(io_gnutls*)ptr;
205  int n;
206 
207  if(context->r_timeout)
208    {
209     fd_set readfd;
210     struct timeval tv;
211 
212     while(1)
213       {
214        FD_ZERO(&readfd);
215 
216        FD_SET(context->fd,&readfd);
217 
218        tv.tv_sec=context->r_timeout;
219        tv.tv_usec=0;
220 
221        n=select(context->fd+1,&readfd,NULL,NULL,&tv);
222 
223        if(n>0)
224           break;
225        else if(n==0)
226           return(0);
227        else if(errno!=EINTR)
228           return(-1);
229       }
230    }
231 
232  n=read(context->fd,data,size);
233 
234  return(n);
235 }
236 
237 
238 /*++++++++++++++++++++++++++++++++++++++
239   The signal handler for the alarm signal to timeout the socket write.
240 
241   int signum The signal number.
242   ++++++++++++++++++++++++++++++++++++++*/
243 
sigalarm(int signum)244 static void sigalarm(/*@unused@*/ int signum)
245 {
246  longjmp(write_jmp_env,1);
247 }
248 
249 
250 /*++++++++++++++++++++++++++++++++++++++
251   A function to be called by gnutls to write data to the socket.
252 
253   int push_with_timeout Return the number of bytes written or a negative number for an error.
254 
255   gnutls_transport_ptr_t ptr The gnutls per-session transport pointer.
256 
257   const void* data The buffer of data to write to the sockeet.
258 
259   size_t size The amount of data to write.
260 
261   This is necessary because the SNI callback only passes the gnutls session but we
262   need the WWWOFFLE IO context so we have to put that into the gnutls_transport_ptr.
263   Therefore the timeout function is moved into here so that it applies to all gnutls
264   data transfers.
265   ++++++++++++++++++++++++++++++++++++++*/
266 
push_with_timeout(gnutls_transport_ptr_t ptr,const void * data,size_t size)267 static int push_with_timeout(gnutls_transport_ptr_t ptr, const void* data, size_t size)
268 {
269  io_gnutls *context=(io_gnutls*)ptr;
270  struct sigaction action;
271  int n;
272 
273  start:
274 
275  if(!context->w_timeout)
276    {
277     n=write(context->fd,data,size);
278 
279     return(n);
280    }
281 
282  action.sa_handler = sigalarm;
283  sigemptyset(&action.sa_mask);
284  action.sa_flags = 0;
285  if(sigaction(SIGALRM, &action, NULL) != 0)
286    {
287     PrintMessage(Warning, "Failed to set SIGALRM; cancelling timeout for writing.");
288     context->w_timeout=0;
289     goto start;
290    }
291 
292  alarm(context->w_timeout);
293 
294  if(setjmp(write_jmp_env))
295    {
296     n=-1;
297     errno=ETIMEDOUT;
298    }
299  else
300     n=write(context->fd,data,size);
301 
302  alarm(0);
303  action.sa_handler = SIG_IGN;
304  sigemptyset(&action.sa_mask);
305  action.sa_flags = 0;
306  if(sigaction(SIGALRM, &action, NULL) != 0)
307     PrintMessage(Warning, "Failed to clear SIGALRM.");
308 
309  return(n);
310 }
311 
312 
313 /*++++++++++++++++++++++++++++++++++++++
314   A callback to handle the TLS SNI extension.
315 
316   int handshake_sni_callback Returns 0 if OK, something else otherwise.
317 
318   gnutls_session_t session The GNUTLS session information.
319   ++++++++++++++++++++++++++++++++++++++*/
320 
handshake_sni_callback(gnutls_session_t session)321 static int handshake_sni_callback(gnutls_session_t session)
322 {
323  int err;
324  size_t sni_len=40;
325  unsigned int sni_type;
326  char *sni_name=NULL;
327  io_gnutls *context;
328 
329  context=gnutls_transport_get_ptr(session);
330 
331  /* Work out the server requested (using SNI) */
332 
333  sni_name=(char*)malloc(sni_len);
334 
335  err=gnutls_server_name_get(context->session,sni_name,&sni_len,&sni_type,0);
336 
337  if(err==GNUTLS_E_SHORT_MEMORY_BUFFER)
338    {
339     sni_name=(char*)realloc((void*)sni_name,sni_len);
340 
341     err=gnutls_server_name_get(context->session,sni_name,&sni_len,&sni_type,0);
342    }
343 
344  if(err==GNUTLS_E_SUCCESS && sni_type==GNUTLS_NAME_DNS)
345     PrintMessage(Inform,"GNUTLS SNI server name was '%s'.",sni_name);
346  else
347    {
348     if(err==GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
349        PrintMessage(Inform,"GNUTLS No SNI server name found, using '%s'.",context->host);
350     else if(err==GNUTLS_E_SUCCESS && sni_type!=GNUTLS_NAME_DNS)
351        PrintMessage(Warning,"GNUTLS Requested SNI server name was not a DNS name [type=%d], using '%s'.",sni_type,context->host);
352     else
353        PrintMessage(Warning,"GNUTLS Request for SNI server name returned an error [%s], using '%s'.",gnutls_strerror(err),context->host);
354 
355     free(sni_name);
356     sni_name=NULL;
357    }
358 
359  /* Set the server credentials */
360 
361  if(context->type==1)
362    {
363     if(sni_name)
364       {
365        context->cred=GetServerCredentials(sni_name);
366        free(sni_name);
367       }
368     else
369        context->cred=GetServerCredentials(context->host);
370    }
371  else /* if(context->type==2) */
372    {
373     if(sni_name)
374       {
375        context->cred=GetFakeCredentials(sni_name);
376        free(sni_name);
377       }
378     else
379        context->cred=GetFakeCredentials(context->host);
380    }
381 
382  return(set_credentials(context));
383 }
384 
385 
386 /*++++++++++++++++++++++++++++++++++++++
387   Set the GNUTLS credentials.
388 
389   int set_credentials Return 0 if OK or something else otherwise.
390 
391   io_gnutls *context The gnutls context information.
392 
393   This is only in a separate function because it needs to be called both
394   from the SNI callback function and from the main io_init_gnutls function.
395   ++++++++++++++++++++++++++++++++++++++*/
396 
set_credentials(io_gnutls * context)397 static int set_credentials(io_gnutls *context)
398 {
399  if(!context->cred)
400    {
401     if(io_strerror)
402        free(io_strerror);
403     io_strerror=(char*)malloc(40);
404 
405     strcpy(io_strerror,"IO(gnutls): Failed to get credentials");
406 
407     PrintMessage(Warning,"GNUTLS Failed to get server credentials [%s].",io_strerror);
408 
409     return(-1);
410    }
411 
412  io_errno=gnutls_credentials_set(context->session,GNUTLS_CRD_CERTIFICATE,context->cred);
413 
414  if(io_errno!=GNUTLS_E_SUCCESS)
415    {
416     set_gnutls_error(io_errno,context->session);
417 
418     PrintMessage(Warning,"GNUTLS Failed to set session credentials [%s].",io_strerror);
419 
420     return(-1);
421    }
422 
423  return(0);
424 }
425 
426 
427 /*++++++++++++++++++++++++++++++++++++++
428   Finalise the gnutls data stream.
429 
430   int io_finish_gnutls Returns 0 on completion, negative if error.
431 
432   io_gnutls *context The gnutls context information.
433   ++++++++++++++++++++++++++++++++++++++*/
434 
io_finish_gnutls(io_gnutls * context)435 int io_finish_gnutls(io_gnutls *context)
436 {
437  gnutls_bye(context->session,GNUTLS_SHUT_WR);
438 
439  gnutls_deinit(context->session);
440 
441  FreeCredentials(context->cred);
442 
443  free(context);
444 
445  return(0);
446 }
447 
448 
449 /*++++++++++++++++++++++++++++++++++++++
450   Read data from a gnutls session and buffer it with a timeout.
451 
452   ssize_t io_gnutls_read_with_timeout Returns the number of bytes read.
453 
454   io_gnutls *context The gnutls context information.
455 
456   io_buffer *out The IO buffer to output the data.
457 
458   unsigned timeout The maximum time to wait for data to be read (or 0 for no timeout).
459   ++++++++++++++++++++++++++++++++++++++*/
460 
io_gnutls_read_with_timeout(io_gnutls * context,io_buffer * out,unsigned timeout)461 ssize_t io_gnutls_read_with_timeout(io_gnutls *context,io_buffer *out,unsigned timeout)
462 {
463  int n;
464 
465  context->r_timeout=timeout;
466 
467  do
468    {
469     n=gnutls_record_recv(context->session,out->data+out->length,out->size-out->length);
470    }
471  while(n==GNUTLS_E_INTERRUPTED || n==GNUTLS_E_AGAIN);
472 
473  if(n==GNUTLS_E_REHANDSHAKE)
474     gnutls_alert_send(context->session,GNUTLS_AL_WARNING,GNUTLS_A_NO_RENEGOTIATION);
475 
476  if(n==GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
477     n==GNUTLS_E_PREMATURE_TERMINATION ||
478     n==GNUTLS_E_INVALID_SESSION) /* Seems to happen at the end of the data. */
479     n=0;
480 
481  if(n>0)
482     out->length+=n;
483 
484  if(n<0)
485     set_gnutls_error(n,context->session);
486 
487  return(n);
488 }
489 
490 
491 /*++++++++++++++++++++++++++++++++++++++
492   Write some data to a gnutls session from a buffer with a timeout.
493 
494   ssize_t io_gnutls_write_with_timeout Returns the number of bytes written or negative on error.
495 
496   io_gnutls *context The gnutls context information.
497 
498   io_buffer *in The IO buffer with the input data.
499 
500   unsigned timeout The maximum time to wait for data to be written (or 0 for no timeout).
501   ++++++++++++++++++++++++++++++++++++++*/
502 
io_gnutls_write_with_timeout(io_gnutls * context,io_buffer * in,unsigned timeout)503 ssize_t io_gnutls_write_with_timeout(io_gnutls *context,io_buffer *in,unsigned timeout)
504 {
505  int n;
506 
507  if(in->length==0)
508     return(0);
509 
510  context->w_timeout=timeout;
511 
512  if(in->length>(4*IO_BUFFER_SIZE))
513    {
514     size_t offset;
515     io_buffer temp;
516 
517     temp.size=in->size;
518 
519     for(offset=0;offset<in->length;offset+=IO_BUFFER_SIZE)
520       {
521        temp.data=in->data+offset;
522 
523        temp.length=in->length-offset;
524        if(temp.length>IO_BUFFER_SIZE)
525           temp.length=IO_BUFFER_SIZE;
526 
527        n=io_gnutls_write_with_timeout(context,&temp,timeout);
528 
529        if(n<0)
530          {
531           in->length=0;
532           return(n);
533          }
534       }
535 
536     in->length=0;
537     return(in->length);
538    }
539 
540  n=write_all(context->session,in->data,in->length);
541 
542  in->length=0;
543  return(n);
544 }
545 
546 
547 /*++++++++++++++++++++++++++++++++++++++
548   A function to write all of a buffer of data to a gnutls session.
549 
550   ssize_t write_all Returns the number of bytes written.
551 
552   gnutls_session_t session The gnutls session.
553 
554   const char *data The data buffer to write.
555 
556   size_t n The number of bytes to write.
557   ++++++++++++++++++++++++++++++++++++++*/
558 
write_all(gnutls_session_t session,const char * data,size_t n)559 static ssize_t write_all(gnutls_session_t session,const char *data,size_t n)
560 {
561  int nn=0;
562 
563  /* Unroll the first loop to optimise the obvious case. */
564 
565  do
566    {
567     nn=gnutls_record_send(session,data,n);
568    }
569  while(nn==GNUTLS_E_INTERRUPTED || nn==GNUTLS_E_AGAIN);
570 
571  if(nn<0 || nn==n)
572     return(nn);
573 
574  /* Loop around until the data is finished. */
575 
576  do
577    {
578     int m;
579 
580     do
581       {
582        m=gnutls_record_send(session,data+nn,n-nn);
583       }
584     while(m==GNUTLS_E_INTERRUPTED || m==GNUTLS_E_AGAIN);
585 
586     if(m<0)
587       {n=m;break;}
588     else
589        nn+=m;
590    }
591  while(nn<n);
592 
593  return(n);
594 }
595 
596 
597 /*++++++++++++++++++++++++++++++++++++++
598   Set the error status when there is a gnutls error.
599 
600   int err The error number.
601 
602   gnutls_session_t session The session information if one is active.
603   ++++++++++++++++++++++++++++++++++++++*/
604 
set_gnutls_error(int err,gnutls_session_t session)605 static void set_gnutls_error(int err,gnutls_session_t session)
606 {
607  const char *type,*msg;
608 
609  if(err==GNUTLS_E_WARNING_ALERT_RECEIVED)
610     type="Warning Alert:";
611  else if(err==GNUTLS_E_FATAL_ALERT_RECEIVED)
612     type="Fatal Alert:";
613  else
614     type="Error:";
615 
616  if(err==GNUTLS_E_WARNING_ALERT_RECEIVED || err==GNUTLS_E_FATAL_ALERT_RECEIVED)
617    {
618     if(session)
619        msg=gnutls_alert_get_name(gnutls_alert_get(session));
620     else
621        msg="No session info";
622    }
623  else
624     msg=gnutls_strerror(err);
625 
626  errno=ERRNO_USE_IO_ERRNO;
627 
628  if(io_strerror)
629     free(io_strerror);
630  io_strerror=(char*)malloc(16+strlen(type)+strlen(msg)+1);
631 
632  sprintf(io_strerror,"IO(gnutls): %s %s",type,msg);
633 }
634 
635 #endif /* USE_GNUTLS */
636