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
, 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
, &errpos
);
309 errout (s
, "Cannot set GNUTLS session priority: %s\n",
310 gnutls_strerror (ret
));
314 gnutls_session_set_ptr (s
->session
, (void *) s
);
316 ret
= gnutls_credentials_set (s
->session
, GNUTLS_CRD_CERTIFICATE
, s
->creds
);
319 errout (s
, "Cannot set session GNUTL credentials: %s\n",
320 gnutls_strerror (ret
));
326 /* requests but does not check a client certificate */
327 gnutls_certificate_server_set_request (s
->session
, GNUTLS_CERT_REQUEST
);
335 gnutls_deinit (s
->session
);
341 tlssession_close (tlssession_t
* s
)
344 gnutls_deinit (s
->session
);
352 return gnutls_global_init ();
357 tlssession_mainloop (int cryptfd
, int plainfd
, tlssession_t
* s
)
362 int tls_wr_interrupted
= 0;
363 int plainEOF
= FALSE
;
364 int cryptEOF
= FALSE
;
367 buffer_t
*plainToCrypt
= bufNew (BUF_SIZE
, BUF_HWM
);
368 buffer_t
*cryptToPlain
= bufNew (BUF_SIZE
, BUF_HWM
);
370 if (socksetnonblock (cryptfd
, 0) < 0)
372 errout (s
, "Could not turn on blocking: %m");
376 /* set it up to work with our FD */
377 gnutls_transport_set_ptr (s
->session
,
378 (gnutls_transport_ptr_t
) (intptr_t) cryptfd
);
381 /* Now do the handshake */
382 ret
= gnutls_handshake (s
->session
);
385 errout (s
, "TLS handshake failed: %s\n", gnutls_strerror (ret
));
389 if (socksetnonblock (cryptfd
, 1) < 0)
391 errout (s
, "Could not turn on non-blocking on crypt FD: %m");
395 if (socksetnonblock (plainfd
, 1) < 0)
397 errout (s
, "Could not turn on non-blocking on plain FD: %m");
401 maxfd
= (plainfd
> cryptfd
) ? plainfd
+ 1 : cryptfd
+ 1;
403 while ((!plainEOF
|| !cryptEOF
) && !quit (s
))
405 struct timeval timeout
;
413 size_t buffered
= gnutls_record_check_pending (s
->session
);
415 wait
= FALSE
; /* do not wait for select to return if we have buffered data */
419 /* plain text end has closed, but me may still have
420 * data yet to write to the crypt end */
421 if (bufIsEmpty (plainToCrypt
) && !tls_wr_interrupted
)
429 if (!bufIsEmpty (cryptToPlain
))
430 FD_SET (plainfd
, &writefds
);
431 if (!bufIsOverHWM (plainToCrypt
))
432 FD_SET (plainfd
, &readfds
);
437 /* crypt end has closed, but me way still have data to
438 * write from the crypt buffer */
439 if (bufIsEmpty (cryptToPlain
) && !buffered
)
447 if (!bufIsEmpty (plainToCrypt
) || tls_wr_interrupted
)
448 FD_SET (cryptfd
, &writefds
);
449 if (!bufIsOverHWM (cryptToPlain
))
450 FD_SET (cryptfd
, &readfds
);
453 /* Repeat select whilst EINTR happens */
456 timeout
.tv_sec
= wait
? 1 : 0;
458 result
= select (maxfd
, &readfds
, &writefds
, NULL
, &timeout
);
462 while ((result
== -1) && (selecterrno
== EINTR
) && !quit (s
));
466 if (FD_ISSET (plainfd
, &readfds
))
468 /* we can read at least one byte */
470 /* get a span of characters to write to the
471 * buffer. As the empty portion may wrap the end of the
472 * circular buffer this might not be all we could read.
474 ssize_t len
= bufGetWriteSpan (plainToCrypt
, &addr
);
480 ret
= read (plainfd
, addr
, (size_t) len
);
482 while ((ret
< 0) && (errno
== EINTR
) && !quit (s
));
487 errout (s
, "Error on read from plain socket: %m\n");
496 bufDoneWrite (plainToCrypt
, ret
); /* mark ret bytes as written to the buffer */
501 if (FD_ISSET (plainfd
, &writefds
))
503 /* we can write at least one byte */
505 /* get a span of characters to read from the buffer
506 * as the full portion may wrap the end of the circular buffer
507 * this might not be all we have to write.
509 ssize_t len
= bufGetReadSpan (cryptToPlain
, &addr
);
515 ret
= write (plainfd
, addr
, (size_t) len
);
517 while ((ret
< 0) && (errno
== EINTR
) && !quit (s
));
522 errout (s
, "Error on write to plain socket: %m\n");
525 bufDoneRead (cryptToPlain
, ret
); /* mark ret bytes as read from the buffer */
529 if (FD_ISSET (cryptfd
, &readfds
) || buffered
)
531 /* we can read at least one byte */
533 /* get a span of characters to write to the
534 * buffer. As the empty portion may wrap the end of the
535 * circular buffer this might not be all we could read.
537 ssize_t len
= bufGetWriteSpan (cryptToPlain
, &addr
);
543 ret
= gnutls_record_recv (s
->session
, addr
, (size_t) len
);
545 while (ret
== GNUTLS_E_INTERRUPTED
&& !quit (s
));
546 /* do not loop on GNUTLS_E_AGAIN - this means we'd block so we'd loop for
551 if (ret
< 0 && ret
!= GNUTLS_E_AGAIN
)
553 errout (s
, "Error on read from crypt socket: %s\n",
554 gnutls_strerror (ret
));
563 bufDoneWrite (cryptToPlain
, ret
); /* mark ret bytes as written to the buffer */
568 if (FD_ISSET (cryptfd
, &writefds
))
570 /* we can write at least one byte */
572 /* get a span of characters to read from the buffer
573 * as the full portion may wrap the end of the circular buffer
574 * this might not be all we have to write.
576 ssize_t len
= bufGetReadSpan (plainToCrypt
, &addr
);
582 if (tls_wr_interrupted
)
584 ret
= gnutls_record_send (s
->session
, NULL
, 0);
588 ret
= gnutls_record_send (s
->session
, addr
, len
);
591 while (ret
== GNUTLS_E_INTERRUPTED
&& !quit (s
));
594 if (ret
== GNUTLS_E_AGAIN
)
596 /* we need to call this again with NULL parameters
599 tls_wr_interrupted
= TRUE
;
603 errout (s
, "Error on write to crypto socket: %s\n",
604 gnutls_strerror (ret
));
609 bufDoneRead (plainToCrypt
, ret
); /* mark ret bytes as read from the buffer */
622 gnutls_bye (s
->session
, GNUTLS_SHUT_RDWR
);
623 shutdown (plainfd
, SHUT_RDWR
);
624 bufFree (plainToCrypt
);
625 bufFree (cryptToPlain
);