5 Copyright (c) 2016 Wrymouth Innovation Ltd
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 and/or sell copies of the Software, and to permit persons to whom the
12 Software is furnished to do so, subject to the following conditions:
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
35 #include <sys/select.h>
36 #include <sys/socket.h>
38 #include <sys/types.h>
41 #include <gnutls/gnutls.h>
42 #include <gnutls/crypto.h>
43 #include <gnutls/x509.h>
44 #include <gnutls/abstract.h>
46 #include "crypto-gnutls.h"
54 #define PRIORITY "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2"
58 gnutls_certificate_credentials_t creds
;
59 gnutls_session_t session
;
61 int (*quitfn
) (void *opaque
);
62 int (*erroutfn
) (void *opaque
, const char *format
, va_list ap
);
67 #define BUF_SIZE 65536
68 #define BUF_HWM ((BUF_SIZE*3)/4)
71 falsequit (void *opaque
)
77 quit (tlssession_t
* s
)
79 return s
->quitfn (s
->opaque
);
84 stderrout (void *opaque
, const char *format
, va_list ap
)
86 return vfprintf (stderr
, format
, ap
);
90 errout (tlssession_t
* s
, const char *format
, ...)
94 va_start (ap
, format
);
95 ret
= s
->erroutfn (s
->opaque
, format
, ap
);
101 debugout (tlssession_t
* s
, const char *format
, ...)
105 va_start (ap
, format
);
107 ret
= s
->erroutfn (s
->opaque
, format
, ap
);
113 socksetnonblock (int fd
, int nb
)
115 int sf
= fcntl (fd
, F_GETFL
, 0);
118 return fcntl (fd
, F_SETFL
, nb
? (sf
| O_NONBLOCK
) : (sf
& ~O_NONBLOCK
));
121 /* From (public domain) example file in GNUTLS
123 * This function will try to verify the peer's certificate, and
124 * also check if the hostname matches, and the activation, expiration dates.
127 verify_certificate_callback (gnutls_session_t session
)
130 const gnutls_datum_t
*cert_list
;
131 unsigned int cert_list_size
;
133 gnutls_x509_crt_t cert
;
136 /* read session pointer */
137 s
= (tlssession_t
*) gnutls_session_get_ptr (session
);
139 /* This verification function uses the trusted CAs in the credentials
140 * structure. So you must have installed one or more CA certificates.
142 ret
= gnutls_certificate_verify_peers2 (session
, &status
);
145 debugout (s
, "Could not verfify peer certificate due to an error\n");
146 return GNUTLS_E_CERTIFICATE_ERROR
;
149 if (status
& GNUTLS_CERT_INVALID
)
150 debugout (s
, "The certificate is not trusted.\n");
152 if (status
& GNUTLS_CERT_SIGNER_NOT_FOUND
)
153 debugout (s
, "The certificate hasn't got a known issuer.\n");
155 if (status
& GNUTLS_CERT_REVOKED
)
156 debugout (s
, "The certificate has been revoked.\n");
158 if (status
& GNUTLS_CERT_EXPIRED
)
159 debugout (s
, "The certificate has expired\n");
161 if (status
& GNUTLS_CERT_NOT_ACTIVATED
)
162 debugout (s
, "The certificate is not yet activated\n");
165 return GNUTLS_E_CERTIFICATE_ERROR
;
167 if (gnutls_certificate_type_get (session
) != GNUTLS_CRT_X509
)
168 return GNUTLS_E_CERTIFICATE_ERROR
;
170 if (gnutls_x509_crt_init (&cert
) < 0)
172 debugout (s
, "error in initialization\n");
173 return GNUTLS_E_CERTIFICATE_ERROR
;
176 cert_list
= gnutls_certificate_get_peers (session
, &cert_list_size
);
177 if (cert_list
== NULL
)
179 debugout (s
, "No certificate was found!\n");
180 return GNUTLS_E_CERTIFICATE_ERROR
;
183 /* check only the first certificate - seems to be what curl does */
184 if (gnutls_x509_crt_import (cert
, &cert_list
[0], GNUTLS_X509_FMT_DER
) < 0)
186 debugout (s
, "error parsing certificate\n");
187 return GNUTLS_E_CERTIFICATE_ERROR
;
190 if (s
->hostname
&& *s
->hostname
)
192 if (!gnutls_x509_crt_check_hostname (cert
, s
->hostname
))
195 "The certificate's owner does not match hostname '%s'\n",
197 return GNUTLS_E_CERTIFICATE_ERROR
;
201 gnutls_x509_crt_deinit (cert
);
203 debugout (s
, "Peer passed certificate verification\n");
205 /* notify gnutls to continue handshake normally */
210 tlssession_new (int isserver
,
211 char *keyfile
, char *certfile
, char *cacertfile
,
212 char *hostname
, char *priority
, int insecure
, int debug
,
213 int (*quitfn
) (void *opaque
),
214 int (*erroutfn
) (void *opaque
, const char *format
,
215 va_list ap
), void *opaque
)
218 tlssession_t
*s
= calloc (1, sizeof (tlssession_t
));
223 s
->quitfn
= falsequit
;
226 s
->erroutfn
= erroutfn
;
228 s
->erroutfn
= stderrout
;
231 s
->hostname
= strdup (hostname
);
235 if (gnutls_certificate_allocate_credentials (&s
->creds
) < 0)
237 errout (s
, "Certificate allocation memory error\n");
241 if (cacertfile
!= NULL
)
244 gnutls_certificate_set_x509_trust_file (s
->creds
, cacertfile
,
245 GNUTLS_X509_FMT_PEM
);
248 errout (s
, "Error setting the x509 trust file: %s\n",
249 gnutls_strerror (ret
));
255 gnutls_certificate_set_verify_function (s
->creds
,
256 verify_certificate_callback
);
257 gnutls_certificate_set_verify_flags (s
->creds
,
258 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT
);
262 if (keyfile
&& !certfile
)
265 if (certfile
!= NULL
&& keyfile
!= NULL
)
268 gnutls_certificate_set_x509_key_file (s
->creds
, certfile
, keyfile
,
269 GNUTLS_X509_FMT_PEM
);
274 "Error loading certificate or key file (%s, %s): %s\n",
275 certfile
, keyfile
, gnutls_strerror (ret
));
282 ret
= gnutls_init (&s
->session
, GNUTLS_SERVER
);
286 ret
= gnutls_init (&s
->session
, GNUTLS_CLIENT
);
290 errout (s
, "Cannot initialize GNUTLS session: %s\n",
291 gnutls_strerror (ret
));
295 gnutls_session_set_ptr (s
->session
, (void *) s
);
297 ret
= gnutls_set_default_priority (s
->session
);
300 errout (s
, "Cannot set default GNUTLS session priority: %s\n",
301 gnutls_strerror (ret
));
305 const char *errpos
= NULL
;
306 ret
= gnutls_priority_set_direct (s
->session
, priority
? priority
: PRIORITY
,
310 errout (s
, "Cannot set GNUTLS session priority: %s\n",
311 gnutls_strerror (ret
));
315 gnutls_session_set_ptr (s
->session
, (void *) s
);
317 ret
= gnutls_credentials_set (s
->session
, GNUTLS_CRD_CERTIFICATE
, s
->creds
);
320 errout (s
, "Cannot set session GNUTL credentials: %s\n",
321 gnutls_strerror (ret
));
327 /* requests but does not check a client certificate */
328 gnutls_certificate_server_set_request (s
->session
, GNUTLS_CERT_REQUEST
);
336 gnutls_deinit (s
->session
);
342 tlssession_close (tlssession_t
* s
)
345 gnutls_deinit (s
->session
);
353 return gnutls_global_init ();
358 tlssession_mainloop (int cryptfd
, int plainfd
, tlssession_t
* s
)
363 int tls_wr_interrupted
= 0;
364 int plainEOF
= FALSE
;
365 int cryptEOF
= FALSE
;
368 buffer_t
*plainToCrypt
= bufNew (BUF_SIZE
, BUF_HWM
);
369 buffer_t
*cryptToPlain
= bufNew (BUF_SIZE
, BUF_HWM
);
371 if (socksetnonblock (cryptfd
, 0) < 0)
373 errout (s
, "Could not turn on blocking: %m");
377 /* set it up to work with our FD */
378 gnutls_transport_set_ptr (s
->session
,
379 (gnutls_transport_ptr_t
) (intptr_t) cryptfd
);
382 /* Now do the handshake */
383 ret
= gnutls_handshake (s
->session
);
386 errout (s
, "TLS handshake failed: %s\n", gnutls_strerror (ret
));
390 if (socksetnonblock (cryptfd
, 1) < 0)
392 errout (s
, "Could not turn on non-blocking on crypt FD: %m");
396 if (socksetnonblock (plainfd
, 1) < 0)
398 errout (s
, "Could not turn on non-blocking on plain FD: %m");
402 maxfd
= (plainfd
> cryptfd
) ? plainfd
+ 1 : cryptfd
+ 1;
404 while ((!plainEOF
|| !cryptEOF
) && !quit (s
))
406 struct timeval timeout
;
414 size_t buffered
= gnutls_record_check_pending (s
->session
);
416 wait
= FALSE
; /* do not wait for select to return if we have buffered data */
420 /* plain text end has closed, but me may still have
421 * data yet to write to the crypt end */
422 if (bufIsEmpty (plainToCrypt
) && !tls_wr_interrupted
)
430 if (!bufIsEmpty (cryptToPlain
))
431 FD_SET (plainfd
, &writefds
);
432 if (!bufIsOverHWM (plainToCrypt
))
433 FD_SET (plainfd
, &readfds
);
438 /* crypt end has closed, but me way still have data to
439 * write from the crypt buffer */
440 if (bufIsEmpty (cryptToPlain
) && !buffered
)
448 if (!bufIsEmpty (plainToCrypt
) || tls_wr_interrupted
)
449 FD_SET (cryptfd
, &writefds
);
450 if (!bufIsOverHWM (cryptToPlain
))
451 FD_SET (cryptfd
, &readfds
);
454 /* Repeat select whilst EINTR happens */
457 timeout
.tv_sec
= wait
? 1 : 0;
459 result
= select (maxfd
, &readfds
, &writefds
, NULL
, &timeout
);
463 while ((result
== -1) && (selecterrno
== EINTR
) && !quit (s
));
467 if (FD_ISSET (plainfd
, &readfds
))
469 /* we can read at least one byte */
471 /* get a span of characters to write to the
472 * buffer. As the empty portion may wrap the end of the
473 * circular buffer this might not be all we could read.
475 ssize_t len
= bufGetWriteSpan (plainToCrypt
, &addr
);
481 ret
= read (plainfd
, addr
, (size_t) len
);
483 while ((ret
< 0) && (errno
== EINTR
) && !quit (s
));
488 errout (s
, "Error on read from plain socket: %m\n");
497 bufDoneWrite (plainToCrypt
, ret
); /* mark ret bytes as written to the buffer */
502 if (FD_ISSET (plainfd
, &writefds
))
504 /* we can write at least one byte */
506 /* get a span of characters to read from the buffer
507 * as the full portion may wrap the end of the circular buffer
508 * this might not be all we have to write.
510 ssize_t len
= bufGetReadSpan (cryptToPlain
, &addr
);
516 ret
= write (plainfd
, addr
, (size_t) len
);
518 while ((ret
< 0) && (errno
== EINTR
) && !quit (s
));
523 errout (s
, "Error on write to plain socket: %m\n");
526 bufDoneRead (cryptToPlain
, ret
); /* mark ret bytes as read from the buffer */
530 if (FD_ISSET (cryptfd
, &readfds
) || buffered
)
532 /* we can read at least one byte */
534 /* get a span of characters to write to the
535 * buffer. As the empty portion may wrap the end of the
536 * circular buffer this might not be all we could read.
538 ssize_t len
= bufGetWriteSpan (cryptToPlain
, &addr
);
544 ret
= gnutls_record_recv (s
->session
, addr
, (size_t) len
);
546 while (ret
== GNUTLS_E_INTERRUPTED
&& !quit (s
));
547 /* do not loop on GNUTLS_E_AGAIN - this means we'd block so we'd loop for
552 if (ret
< 0 && ret
!= GNUTLS_E_AGAIN
)
554 errout (s
, "Error on read from crypt socket: %s\n",
555 gnutls_strerror (ret
));
564 bufDoneWrite (cryptToPlain
, ret
); /* mark ret bytes as written to the buffer */
569 if (FD_ISSET (cryptfd
, &writefds
))
571 /* we can write at least one byte */
573 /* get a span of characters to read from the buffer
574 * as the full portion may wrap the end of the circular buffer
575 * this might not be all we have to write.
577 ssize_t len
= bufGetReadSpan (plainToCrypt
, &addr
);
583 if (tls_wr_interrupted
)
585 ret
= gnutls_record_send (s
->session
, NULL
, 0);
589 ret
= gnutls_record_send (s
->session
, addr
, len
);
592 while (ret
== GNUTLS_E_INTERRUPTED
&& !quit (s
));
595 if (ret
== GNUTLS_E_AGAIN
)
597 /* we need to call this again with NULL parameters
600 tls_wr_interrupted
= TRUE
;
604 errout (s
, "Error on write to crypto socket: %s\n",
605 gnutls_strerror (ret
));
610 bufDoneRead (plainToCrypt
, ret
); /* mark ret bytes as read from the buffer */
623 gnutls_bye (s
->session
, GNUTLS_SHUT_RDWR
);
624 shutdown (plainfd
, SHUT_RDWR
);
625 bufFree (plainToCrypt
);
626 bufFree (cryptToPlain
);