1 /* $Id$ */
2 /*
3  * Copyright (C) 2018-2018 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2014-2017 Savoir-faire Linux.
5  * (https://www.savoirfairelinux.com)
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 #include <pj/ssl_sock.h>
23 #include <pj/activesock.h>
24 #include <pj/compat/socket.h>
25 #include <pj/assert.h>
26 #include <pj/errno.h>
27 #include <pj/list.h>
28 #include <pj/lock.h>
29 #include <pj/log.h>
30 #include <pj/math.h>
31 #include <pj/os.h>
32 #include <pj/pool.h>
33 #include <pj/string.h>
34 #include <pj/timer.h>
35 #include <pj/file_io.h>
36 
37 #if GNUTLS_VERSION_NUMBER < 0x030306 && !defined(_MSC_VER)
38 #   include <dirent.h>
39 #endif
40 
41 #include <errno.h>
42 
43 /* Only build when PJ_HAS_SSL_SOCK and the implementation is GnuTLS. */
44 #if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0 && \
45     (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_GNUTLS)
46 
47 #define SSL_SOCK_IMP_USE_CIRC_BUF
48 
49 #include "ssl_sock_imp_common.h"
50 #include "ssl_sock_imp_common.c"
51 
52 #define THIS_FILE               "ssl_sock_gtls.c"
53 
54 /* Maximum ciphers */
55 #define MAX_CIPHERS             100
56 
57 /* Standard trust locations */
58 #define TRUST_STORE_FILE1 "/etc/ssl/certs/ca-certificates.crt"
59 #define TRUST_STORE_FILE2 "/etc/ssl/certs/ca-bundle.crt"
60 
61 /* Debugging output level for GnuTLS only */
62 #define GNUTLS_LOG_LEVEL 0
63 
64 /* GnuTLS includes */
65 #include <gnutls/gnutls.h>
66 #include <gnutls/x509.h>
67 #include <gnutls/abstract.h>
68 
69 #ifdef _MSC_VER
70 #  pragma comment( lib, "libgnutls")
71 #endif
72 
73 
74 /* Secure socket structure definition. */
75 typedef struct gnutls_sock_t {
76     pj_ssl_sock_t  	  base;
77 
78     gnutls_session_t      session;
79     gnutls_certificate_credentials_t xcred;
80 
81     int                   tls_init_count; /* library initialization counter */
82 } gnutls_sock_t;
83 
84 /* Last error reported somehow */
85 static int tls_last_error;
86 
87 
88 /*
89  *******************************************************************
90  * Static/internal functions.
91  *******************************************************************
92  */
93 
94 /* Convert from GnuTLS error to pj_status_t. */
tls_status_from_err(pj_ssl_sock_t * ssock,int err)95 static pj_status_t tls_status_from_err(pj_ssl_sock_t *ssock, int err)
96 {
97     pj_status_t status;
98 
99     switch (err) {
100     case GNUTLS_E_SUCCESS:
101         status = PJ_SUCCESS;
102         break;
103     case GNUTLS_E_MEMORY_ERROR:
104         status = PJ_ENOMEM;
105         break;
106     case GNUTLS_E_LARGE_PACKET:
107         status = PJ_ETOOBIG;
108         break;
109     case GNUTLS_E_NO_CERTIFICATE_FOUND:
110         status = PJ_ENOTFOUND;
111         break;
112     case GNUTLS_E_SESSION_EOF:
113         status = PJ_EEOF;
114         break;
115     case GNUTLS_E_HANDSHAKE_TOO_LARGE:
116         status = PJ_ETOOBIG;
117         break;
118     case GNUTLS_E_EXPIRED:
119         status = PJ_EGONE;
120         break;
121     case GNUTLS_E_TIMEDOUT:
122         status = PJ_ETIMEDOUT;
123         break;
124     case GNUTLS_E_PREMATURE_TERMINATION:
125         status = PJ_ECANCELLED;
126         break;
127     case GNUTLS_E_INTERNAL_ERROR:
128     case GNUTLS_E_UNIMPLEMENTED_FEATURE:
129         status = PJ_EBUG;
130         break;
131     case GNUTLS_E_AGAIN:
132     case GNUTLS_E_INTERRUPTED:
133     case GNUTLS_E_REHANDSHAKE:
134         status = PJ_EPENDING;
135         break;
136     case GNUTLS_E_TOO_MANY_EMPTY_PACKETS:
137     case GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS:
138     case GNUTLS_E_RECORD_LIMIT_REACHED:
139         status = PJ_ETOOMANY;
140         break;
141     case GNUTLS_E_UNSUPPORTED_VERSION_PACKET:
142     case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM:
143     case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE:
144     case GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE:
145     case GNUTLS_E_X509_UNSUPPORTED_EXTENSION:
146     case GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION:
147         status = PJ_ENOTSUP;
148         break;
149     case GNUTLS_E_INVALID_SESSION:
150     case GNUTLS_E_INVALID_REQUEST:
151     case GNUTLS_E_INVALID_PASSWORD:
152     case GNUTLS_E_ILLEGAL_PARAMETER:
153     case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION:
154     case GNUTLS_E_UNEXPECTED_PACKET:
155     case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
156     case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET:
157     case GNUTLS_E_UNWANTED_ALGORITHM:
158     case GNUTLS_E_USER_ERROR:
159         status = PJ_EINVAL;
160         break;
161     default:
162         status = PJ_EUNKNOWN;
163         break;
164     }
165 
166     /* Not thread safe */
167     tls_last_error = err;
168     if (ssock)
169         ssock->last_err = err;
170     return status;
171 }
172 
173 
174 /* Get error string from GnuTLS using tls_last_error */
tls_strerror(pj_status_t status,char * buf,pj_size_t bufsize)175 static pj_str_t tls_strerror(pj_status_t status,
176                              char *buf, pj_size_t bufsize)
177 {
178     pj_str_t errstr;
179     const char *tmp = gnutls_strerror(tls_last_error);
180 
181 #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
182     if (tmp) {
183         pj_ansi_strncpy(buf, tmp, bufsize);
184         errstr = pj_str(buf);
185         return errstr;
186     }
187 #endif /* PJ_HAS_ERROR_STRING */
188 
189     errstr.ptr = buf;
190     errstr.slen = pj_ansi_snprintf(buf, bufsize, "GnuTLS error %d: %s",
191                                    tls_last_error, tmp);
192     if (errstr.slen < 1 || errstr.slen >= (int) bufsize)
193         errstr.slen = bufsize - 1;
194 
195     return errstr;
196 }
197 
198 
199 /* GnuTLS way of reporting internal operations. */
tls_print_logs(int level,const char * msg)200 static void tls_print_logs(int level, const char* msg)
201 {
202     PJ_LOG(3, (THIS_FILE, "GnuTLS [%d]: %s", level, msg));
203 }
204 
205 
206 /* Initialize GnuTLS. */
tls_init(void)207 static pj_status_t tls_init(void)
208 {
209     /* Register error subsystem */
210     pj_status_t status = pj_register_strerror(PJ_ERRNO_START_USER +
211                                               PJ_ERRNO_SPACE_SIZE * 6,
212                                               PJ_ERRNO_SPACE_SIZE,
213                                               &tls_strerror);
214     pj_assert(status == PJ_SUCCESS);
215 
216     /* Init GnuTLS library */
217     int ret = gnutls_global_init();
218     if (ret < 0)
219         return tls_status_from_err(NULL, ret);
220 
221     gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
222     gnutls_global_set_log_function(tls_print_logs);
223 
224     /* Init available ciphers */
225     if (!ssl_cipher_num) {
226         unsigned int i;
227 
228         for (i = 0; i<PJ_ARRAY_SIZE(ssl_ciphers); i++) {
229             unsigned char id[2];
230             const char *suite;
231 
232             suite = gnutls_cipher_suite_info(i, (unsigned char *)id,
233                                              NULL, NULL, NULL, NULL);
234             ssl_ciphers[i].id = 0;
235             /* usually the array size is bigger than the number of available
236              * ciphers anyway, so by checking here we can exit the loop as soon
237              * as either all ciphers have been added or the array is full */
238             if (suite) {
239                 ssl_ciphers[i].id = (pj_ssl_cipher)
240                     (pj_uint32_t) ((id[0] << 8) | id[1]);
241                 ssl_ciphers[i].name = suite;
242             } else
243                 break;
244         }
245 
246         ssl_cipher_num = i;
247     }
248 
249     return PJ_SUCCESS;
250 }
251 
252 
253 /* Shutdown GnuTLS */
tls_deinit(void)254 static void tls_deinit(void)
255 {
256     gnutls_global_deinit();
257 }
258 
259 
260 /* Callback invoked every time a certificate has to be validated. */
tls_cert_verify_cb(gnutls_session_t session)261 static int tls_cert_verify_cb(gnutls_session_t session)
262 {
263     pj_ssl_sock_t *ssock;
264     unsigned int status;
265     int ret;
266 
267     /* Get SSL socket instance */
268     ssock = (pj_ssl_sock_t *)gnutls_session_get_ptr(session);
269     pj_assert(ssock);
270 
271     /* Support only x509 format */
272     ret = gnutls_certificate_type_get(session) != GNUTLS_CRT_X509;
273     if (ret < 0) {
274         ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
275         return GNUTLS_E_CERTIFICATE_ERROR;
276     }
277 
278     /* Store verification status */
279     ret = gnutls_certificate_verify_peers2(session, &status);
280     if (ret < 0) {
281         ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
282         return GNUTLS_E_CERTIFICATE_ERROR;
283     }
284     if (ssock->param.verify_peer) {
285     if (status & GNUTLS_CERT_INVALID) {
286         if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
287             ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND;
288         else if (status & GNUTLS_CERT_EXPIRED ||
289                  status & GNUTLS_CERT_NOT_ACTIVATED)
290             ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
291         else if (status & GNUTLS_CERT_SIGNER_NOT_CA ||
292                  status & GNUTLS_CERT_INSECURE_ALGORITHM)
293             ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
294         else if (status & GNUTLS_CERT_UNEXPECTED_OWNER ||
295                  status & GNUTLS_CERT_MISMATCH)
296             ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH;
297         else if (status & GNUTLS_CERT_REVOKED)
298             ssock->verify_status |= PJ_SSL_CERT_EREVOKED;
299         else
300             ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
301 
302         return GNUTLS_E_CERTIFICATE_ERROR;
303     }
304 
305     /* When verification is not requested just return ok here, however
306      * applications can still get the verification status. */
307         gnutls_x509_crt_t cert;
308         unsigned int cert_list_size;
309         const gnutls_datum_t *cert_list;
310         int ret;
311 
312         ret = gnutls_x509_crt_init(&cert);
313         if (ret < 0)
314             goto out;
315 
316         cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
317         if (cert_list == NULL) {
318             ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
319             goto out;
320         }
321 
322         /* TODO: verify whole chain perhaps? */
323         ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
324         if (ret < 0)
325             ret = gnutls_x509_crt_import(cert, &cert_list[0],
326                                          GNUTLS_X509_FMT_PEM);
327         if (ret < 0) {
328             ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
329             goto out;
330         }
331         ret = gnutls_x509_crt_check_hostname(cert,
332         				     ssock->param.server_name.ptr);
333         if (ret < 0)
334             goto out;
335 
336         gnutls_x509_crt_deinit(cert);
337 
338         /* notify GnuTLS to continue handshake normally */
339         return GNUTLS_E_SUCCESS;
340 
341 out:
342         tls_last_error = ret;
343         ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
344         return GNUTLS_E_CERTIFICATE_ERROR;
345     }
346 
347     return GNUTLS_E_SUCCESS;
348 }
349 
350 
351 /* gnutls_handshake() and gnutls_record_send() will call this function to
352  * send/write (encrypted) data */
tls_data_push(gnutls_transport_ptr_t ptr,const void * data,size_t len)353 static ssize_t tls_data_push(gnutls_transport_ptr_t ptr,
354                              const void *data, size_t len)
355 {
356     pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
357     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
358 
359     pj_lock_acquire(ssock->circ_buf_output_mutex);
360     if (circ_write(&ssock->circ_buf_output, data, len) != PJ_SUCCESS) {
361         pj_lock_release(ssock->circ_buf_output_mutex);
362 
363         gnutls_transport_set_errno(gssock->session, ENOMEM);
364         return -1;
365     }
366 
367     pj_lock_release(ssock->circ_buf_output_mutex);
368 
369     return len;
370 }
371 
372 
373 /* gnutls_handshake() and gnutls_record_recv() will call this function to
374  * receive/read (encrypted) data */
tls_data_pull(gnutls_transport_ptr_t ptr,void * data,pj_size_t len)375 static ssize_t tls_data_pull(gnutls_transport_ptr_t ptr,
376                              void *data, pj_size_t len)
377 {
378     pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
379     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
380 
381     pj_lock_acquire(ssock->circ_buf_input_mutex);
382 
383     if (circ_empty(&ssock->circ_buf_input)) {
384         pj_lock_release(ssock->circ_buf_input_mutex);
385 
386         /* Data buffers not yet filled */
387         gnutls_transport_set_errno(gssock->session, EAGAIN);
388         return -1;
389     }
390 
391     pj_size_t circ_buf_size = circ_size(&ssock->circ_buf_input);
392     pj_size_t read_size = PJ_MIN(circ_buf_size, len);
393 
394     circ_read(&ssock->circ_buf_input, data, read_size);
395 
396     pj_lock_release(ssock->circ_buf_input_mutex);
397 
398     return read_size;
399 }
400 
401 
402 /* Append a string to the priority string, only once. */
tls_str_append_once(pj_str_t * dst,pj_str_t * src)403 static pj_status_t tls_str_append_once(pj_str_t *dst, pj_str_t *src)
404 {
405     if (pj_strstr(dst, src) == NULL) {
406         /* Check buffer size */
407         if (dst->slen + src->slen + 3 > 1024)
408             return PJ_ETOOMANY;
409 
410         pj_strcat2(dst, ":+");
411         pj_strcat(dst, src);
412     }
413     return PJ_SUCCESS;
414 }
415 
416 
417 /* Generate priority string with user preference order. */
tls_priorities_set(pj_ssl_sock_t * ssock)418 static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock)
419 {
420     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
421     char buf[1024];
422     char priority_buf[256];
423     pj_str_t cipher_list;
424     pj_str_t compression = pj_str("COMP-NULL");
425     pj_str_t server = pj_str(":%SERVER_PRECEDENCE");
426     int i, j, ret;
427     pj_str_t priority;
428     const char *err;
429 
430     pj_strset(&cipher_list, buf, 0);
431     pj_strset(&priority, priority_buf, 0);
432 
433     /* For each level, enable only the requested protocol */
434     pj_strcat2(&priority, "NORMAL:");
435     if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_2) {
436         pj_strcat2(&priority, "+VERS-TLS1.2:");
437     }
438     if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1_1) {
439         pj_strcat2(&priority, "+VERS-TLS1.1:");
440     }
441     if (ssock->param.proto & PJ_SSL_SOCK_PROTO_TLS1) {
442         pj_strcat2(&priority, "+VERS-TLS1.0:");
443     }
444     pj_strcat2(&priority, "-VERS-SSL3.0:");
445     pj_strcat2(&priority, "%LATEST_RECORD_VERSION");
446 
447     pj_strcat(&cipher_list, &priority);
448     for (i = 0; i < ssock->param.ciphers_num; i++) {
449         for (j = 0; ; j++) {
450             pj_ssl_cipher c;
451             const char *suite;
452             unsigned char id[2];
453             gnutls_protocol_t proto;
454             gnutls_kx_algorithm_t kx;
455             gnutls_mac_algorithm_t mac;
456             gnutls_cipher_algorithm_t algo;
457 
458             suite = gnutls_cipher_suite_info(j, (unsigned char *)id,
459                                              &kx, &algo, &mac, &proto);
460             if (!suite)
461                 break;
462 
463             c = (pj_ssl_cipher) (pj_uint32_t) ((id[0] << 8) | id[1]);
464             if (ssock->param.ciphers[i] == c) {
465                 char temp[256];
466                 pj_str_t cipher_entry;
467 
468                 /* Protocol version */
469                 pj_strset(&cipher_entry, temp, 0);
470                 pj_strcat2(&cipher_entry, "VERS-");
471                 pj_strcat2(&cipher_entry, gnutls_protocol_get_name(proto));
472                 ret = tls_str_append_once(&cipher_list, &cipher_entry);
473                 if (ret != PJ_SUCCESS)
474                     return ret;
475 
476                 /* Cipher */
477                 pj_strset(&cipher_entry, temp, 0);
478                 pj_strcat2(&cipher_entry, gnutls_cipher_get_name(algo));
479                 ret = tls_str_append_once(&cipher_list, &cipher_entry);
480                 if (ret != PJ_SUCCESS)
481                     return ret;
482 
483                 /* Mac */
484                 pj_strset(&cipher_entry, temp, 0);
485                 pj_strcat2(&cipher_entry, gnutls_mac_get_name(mac));
486                 ret = tls_str_append_once(&cipher_list, &cipher_entry);
487                 if (ret != PJ_SUCCESS)
488                     return ret;
489 
490                 /* Key exchange */
491                 pj_strset(&cipher_entry, temp, 0);
492                 pj_strcat2(&cipher_entry, gnutls_kx_get_name(kx));
493                 ret = tls_str_append_once(&cipher_list, &cipher_entry);
494                 if (ret != PJ_SUCCESS)
495                     return ret;
496 
497                 /* Compression is always disabled */
498                 /* Signature is level-default */
499                 break;
500             }
501         }
502     }
503 
504     /* Disable compression, it's a TLS-only extension after all */
505     tls_str_append_once(&cipher_list, &compression);
506 
507     /* Server will be the one deciding which crypto to use */
508     if (ssock->is_server) {
509         if (cipher_list.slen + server.slen + 1 > sizeof(buf))
510             return PJ_ETOOMANY;
511         else
512             pj_strcat(&cipher_list, &server);
513     }
514 
515     /* End the string and print it */
516     cipher_list.ptr[cipher_list.slen] = '\0';
517     PJ_LOG(5, (ssock->pool->obj_name, "Priority string: %s", cipher_list.ptr));
518 
519     /* Set our priority string */
520     ret = gnutls_priority_set_direct(gssock->session,
521                                         cipher_list.ptr, &err);
522     if (ret < 0) {
523         tls_last_error = GNUTLS_E_INVALID_REQUEST;
524         return PJ_EINVAL;
525     }
526 
527     return PJ_SUCCESS;
528 }
529 
530 
531 /* Load root CA file or load the installed ones. */
tls_trust_set(pj_ssl_sock_t * ssock)532 static pj_status_t tls_trust_set(pj_ssl_sock_t *ssock)
533 {
534     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
535     int ntrusts = 0;
536     int err;
537 
538     err = gnutls_certificate_set_x509_system_trust(gssock->xcred);
539     if (err > 0)
540         ntrusts += err;
541     err = gnutls_certificate_set_x509_trust_file(gssock->xcred,
542                                                  TRUST_STORE_FILE1,
543                                                  GNUTLS_X509_FMT_PEM);
544     if (err > 0)
545         ntrusts += err;
546 
547     err = gnutls_certificate_set_x509_trust_file(gssock->xcred,
548                                                  TRUST_STORE_FILE2,
549                                                  GNUTLS_X509_FMT_PEM);
550     if (err > 0)
551         ntrusts += err;
552 
553     if (ntrusts > 0)
554         return PJ_SUCCESS;
555     else if (!ntrusts)
556         return PJ_ENOTFOUND;
557     else
558         return PJ_EINVAL;
559 }
560 
561 #if GNUTLS_VERSION_NUMBER < 0x030306
562 
563 #ifdef _POSIX_PATH_MAX
564 #   define GNUTLS_PATH_MAX _POSIX_PATH_MAX
565 #else
566 #   define GNUTLS_PATH_MAX 256
567 #endif
568 
gnutls_certificate_set_x509_trust_dir(gnutls_certificate_credentials_t cred,const char * dirname,unsigned type)569 static int gnutls_certificate_set_x509_trust_dir(
570 		gnutls_certificate_credentials_t cred,
571 		const char *dirname, unsigned type)
572 {
573     DIR *dirp;
574     struct dirent *d;
575     int ret;
576     int r = 0;
577     char path[GNUTLS_PATH_MAX];
578 #ifndef _WIN32
579     struct dirent e;
580 #endif
581 
582     dirp = opendir(dirname);
583     if (dirp != NULL) {
584         do {
585 #ifdef _WIN32
586             d = readdir(dirp);
587             if (d != NULL) {
588 #else
589             ret = readdir_r(dirp, &e, &d);
590             if (ret == 0 && d != NULL
591 #ifdef _DIRENT_HAVE_D_TYPE
592                 && (d->d_type == DT_REG || d->d_type == DT_LNK ||
593                     d->d_type == DT_UNKNOWN)
594 #endif
595             ) {
596 #endif
597                 snprintf(path, sizeof(path), "%s/%s",
598                      	 dirname, d->d_name);
599 
600                 ret = gnutls_certificate_set_x509_trust_file(cred, path, type);
601                 if (ret >= 0)
602                     r += ret;
603             }
604         } while (d != NULL);
605         closedir(dirp);
606     }
607 
608     return r;
609 }
610 
611 #endif
612 
613 static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool)
614 {
615     return (pj_ssl_sock_t *)PJ_POOL_ZALLOC_T(pool, gnutls_sock_t);
616 }
617 
618 /* Create and initialize new GnuTLS context and instance */
619 static pj_status_t ssl_create(pj_ssl_sock_t *ssock)
620 {
621     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
622     pj_ssl_cert_t *cert;
623     pj_status_t status;
624     int ret;
625 
626     pj_assert(ssock);
627 
628     cert = ssock->cert;
629 
630     /* Even if reopening is harmless, having one instance only simplifies
631      * deallocating it later on */
632     if (!gssock->tls_init_count) {
633         gssock->tls_init_count++;
634         ret = tls_init();
635         if (ret < 0)
636             return ret;
637     } else
638         return PJ_SUCCESS;
639 
640     /* Start this socket session */
641     ret = gnutls_init(&gssock->session, ssock->is_server ? GNUTLS_SERVER
642                                                         : GNUTLS_CLIENT);
643     if (ret < 0)
644         goto out;
645 
646     /* Set the ssock object to be retrieved by transport (send/recv) and by
647      * user data from this session */
648     gnutls_transport_set_ptr(gssock->session,
649                              (gnutls_transport_ptr_t) (uintptr_t) ssock);
650     gnutls_session_set_ptr(gssock->session,
651                            (gnutls_transport_ptr_t) (uintptr_t) ssock);
652 
653     /* Initialize input circular buffer */
654     status = circ_init(ssock->pool->factory, &ssock->circ_buf_input, 512);
655     if (status != PJ_SUCCESS)
656         return status;
657 
658     /* Initialize output circular buffer */
659     status = circ_init(ssock->pool->factory, &ssock->circ_buf_output, 512);
660     if (status != PJ_SUCCESS)
661         return status;
662 
663     /* Set the callback that allows GnuTLS to PUSH and PULL data
664      * TO and FROM the transport layer */
665     gnutls_transport_set_push_function(gssock->session, tls_data_push);
666     gnutls_transport_set_pull_function(gssock->session, tls_data_pull);
667 
668     /* Determine which cipher suite to support */
669     status = tls_priorities_set(ssock);
670     if (status != PJ_SUCCESS)
671         return status;
672 
673     /* Allocate credentials for handshaking and transmission */
674     ret = gnutls_certificate_allocate_credentials(&gssock->xcred);
675     if (ret < 0)
676         goto out;
677     gnutls_certificate_set_verify_function(gssock->xcred, tls_cert_verify_cb);
678 
679     /* Load system trust file(s) */
680     status = tls_trust_set(ssock);
681     if (status != PJ_SUCCESS)
682         return status;
683 
684     /* Load user-provided CA, certificate and key if available */
685     if (cert) {
686         /* Load CA if one is specified. */
687         if (cert->CA_file.slen) {
688             ret = gnutls_certificate_set_x509_trust_file(gssock->xcred,
689                                                          cert->CA_file.ptr,
690                                                          GNUTLS_X509_FMT_PEM);
691             if (ret < 0)
692                 ret = gnutls_certificate_set_x509_trust_file(
693                 		gssock->xcred,
694                                 cert->CA_file.ptr,
695                                 GNUTLS_X509_FMT_DER);
696             if (ret < 0)
697                 goto out;
698         }
699         if (cert->CA_path.slen) {
700             ret = gnutls_certificate_set_x509_trust_dir(gssock->xcred,
701                                                          cert->CA_path.ptr,
702                                                          GNUTLS_X509_FMT_PEM);
703             if (ret < 0)
704                 ret = gnutls_certificate_set_x509_trust_dir(
705                 		gssock->xcred,
706                                 cert->CA_path.ptr,
707                                 GNUTLS_X509_FMT_DER);
708             if (ret < 0)
709                 goto out;
710         }
711 
712         /* Load certificate, key and pass if one is specified */
713         if (cert->cert_file.slen && cert->privkey_file.slen) {
714             const char *prikey_file = cert->privkey_file.ptr;
715             const char *prikey_pass = cert->privkey_pass.slen
716                                     ? cert->privkey_pass.ptr
717                                     : NULL;
718             ret = gnutls_certificate_set_x509_key_file2(gssock->xcred,
719                                                         cert->cert_file.ptr,
720                                                         prikey_file,
721                                                         GNUTLS_X509_FMT_PEM,
722                                                         prikey_pass,
723                                                         0);
724             if (ret != GNUTLS_E_SUCCESS)
725                 ret = gnutls_certificate_set_x509_key_file2(gssock->xcred,
726                                                             cert->cert_file.ptr,
727                                                             prikey_file,
728                                                             GNUTLS_X509_FMT_DER,
729                                                             prikey_pass,
730                                                             0);
731             if (ret < 0)
732                 goto out;
733         }
734 
735         if (cert->CA_buf.slen) {
736             gnutls_datum_t ca;
737             ca.data = (unsigned char*)cert->CA_buf.ptr;
738             ca.size = cert->CA_buf.slen;
739             ret = gnutls_certificate_set_x509_trust_mem(gssock->xcred,
740                                                         &ca,
741                                                         GNUTLS_X509_FMT_PEM);
742             if (ret < 0)
743                 ret = gnutls_certificate_set_x509_trust_mem(
744                 		gssock->xcred, &ca, GNUTLS_X509_FMT_DER);
745             if (ret < 0)
746                 goto out;
747         }
748 
749         if (cert->cert_buf.slen && cert->privkey_buf.slen) {
750             gnutls_datum_t cert_buf;
751             gnutls_datum_t privkey_buf;
752 
753             cert_buf.data = (unsigned char*)cert->CA_buf.ptr;
754             cert_buf.size = cert->CA_buf.slen;
755             privkey_buf.data = (unsigned char*)cert->privkey_buf.ptr;
756             privkey_buf.size = cert->privkey_buf.slen;
757 
758             const char *prikey_pass = cert->privkey_pass.slen
759                                     ? cert->privkey_pass.ptr
760                                     : NULL;
761             ret = gnutls_certificate_set_x509_key_mem2(gssock->xcred,
762                                                        &cert_buf,
763                                                        &privkey_buf,
764                                                        GNUTLS_X509_FMT_PEM,
765                                                        prikey_pass,
766                                                        0);
767             /* Load DER format */
768             /*
769             if (ret != GNUTLS_E_SUCCESS)
770                 ret = gnutls_certificate_set_x509_key_mem2(gssock->xcred,
771                                                            &cert_buf,
772                                                            &privkey_buf,
773                                                            GNUTLS_X509_FMT_DER,
774                                                            prikey_pass,
775                                                            0);
776             */
777             if (ret < 0)
778                 goto out;
779         }
780     }
781 
782     /* Require client certificate if asked */
783     if (ssock->is_server && ssock->param.require_client_cert)
784         gnutls_certificate_server_set_request(gssock->session,
785                                               GNUTLS_CERT_REQUIRE);
786 
787     /* Finally set credentials for this session */
788     ret = gnutls_credentials_set(gssock->session,
789                                  GNUTLS_CRD_CERTIFICATE, gssock->xcred);
790     if (ret < 0)
791         goto out;
792 
793     ret = GNUTLS_E_SUCCESS;
794 out:
795     return tls_status_from_err(ssock, ret);
796 }
797 
798 
799 /* Destroy GnuTLS credentials and session. */
800 static void ssl_destroy(pj_ssl_sock_t *ssock)
801 {
802     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
803 
804     if (gssock->session) {
805         gnutls_bye(gssock->session, GNUTLS_SHUT_RDWR);
806         gnutls_deinit(gssock->session);
807         gssock->session = NULL;
808     }
809 
810     if (gssock->xcred) {
811         gnutls_certificate_free_credentials(gssock->xcred);
812         gssock->xcred = NULL;
813     }
814 
815     /* Free GnuTLS library */
816     if (gssock->tls_init_count) {
817         gssock->tls_init_count--;
818         tls_deinit();
819     }
820 
821     /* Destroy circular buffers */
822     circ_deinit(&ssock->circ_buf_input);
823     circ_deinit(&ssock->circ_buf_output);
824 }
825 
826 
827 /* Reset socket state. */
828 static void ssl_reset_sock_state(pj_ssl_sock_t *ssock)
829 {
830     pj_lock_acquire(ssock->circ_buf_output_mutex);
831     ssock->ssl_state = SSL_STATE_NULL;
832     pj_lock_release(ssock->circ_buf_output_mutex);
833 
834     ssl_close_sockets(ssock);
835 
836     ssock->last_err = tls_last_error = GNUTLS_E_SUCCESS;
837 }
838 
839 
840 static void ssl_ciphers_populate(void)
841 {
842      if (!ssl_cipher_num) {
843          tls_init();
844          tls_deinit();
845      }
846 }
847 
848 
849 static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock)
850 {
851     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
852     int i;
853     gnutls_cipher_algorithm_t lookup;
854     gnutls_cipher_algorithm_t cipher;
855 
856     /* Current cipher */
857     cipher = gnutls_cipher_get(gssock->session);
858     for (i = 0; ; i++) {
859         unsigned char id[2];
860         const char *suite;
861 
862         suite = gnutls_cipher_suite_info(i,(unsigned char *)id, NULL,
863                                          &lookup, NULL, NULL);
864         if (suite) {
865             if (lookup == cipher) {
866                 return (pj_uint32_t) ((id[0] << 8) | id[1]);
867             }
868         } else {
869             break;
870         }
871     }
872 
873     return PJ_TLS_UNKNOWN_CIPHER;
874 }
875 
876 
877 /* Get Common Name field string from a general name string */
878 static void tls_cert_get_cn(const pj_str_t *gen_name, pj_str_t *cn)
879 {
880     pj_str_t CN_sign = {"CN=", 3};
881     char *p, *q;
882 
883     pj_bzero(cn, sizeof(cn));
884 
885     p = pj_strstr(gen_name, &CN_sign);
886     if (!p)
887         return;
888 
889     p += 3; /* shift pointer to value part */
890     pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
891     q = pj_strchr(cn, ',');
892     if (q)
893         cn->slen = q - p;
894 }
895 
896 
897 /* Get certificate info; in case the certificate info is already populated,
898  * this function will check if the contents need updating by inspecting the
899  * issuer and the serial number. */
900 static void tls_cert_get_info(pj_pool_t *pool, pj_ssl_cert_info *ci,
901 			      gnutls_x509_crt_t cert)
902 {
903     pj_bool_t update_needed;
904     char buf[512] = { 0 };
905     size_t bufsize = sizeof(buf);
906     pj_uint8_t serial_no[64] = { 0 }; /* should be >= sizeof(ci->serial_no) */
907     size_t serialsize = sizeof(serial_no);
908     size_t len = sizeof(buf);
909     int i, ret, seq = 0;
910     pj_ssl_cert_name_type type;
911 
912     pj_assert(pool && ci && cert);
913 
914     /* Get issuer */
915     gnutls_x509_crt_get_issuer_dn(cert, buf, &bufsize);
916 
917     /* Get serial no */
918     gnutls_x509_crt_get_serial(cert, serial_no, &serialsize);
919 
920     /* Check if the contents need to be updated */
921     update_needed = pj_strcmp2(&ci->issuer.info, buf) ||
922                     pj_memcmp(ci->serial_no, serial_no, serialsize);
923     if (!update_needed)
924         return;
925 
926     /* Update cert info */
927 
928     pj_bzero(ci, sizeof(pj_ssl_cert_info));
929 
930     /* Version */
931     ci->version = gnutls_x509_crt_get_version(cert);
932 
933     /* Issuer */
934     pj_strdup2(pool, &ci->issuer.info, buf);
935     tls_cert_get_cn(&ci->issuer.info, &ci->issuer.cn);
936 
937     /* Serial number */
938     pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
939 
940     /* Subject */
941     bufsize = sizeof(buf);
942     gnutls_x509_crt_get_dn(cert, buf, &bufsize);
943     pj_strdup2(pool, &ci->subject.info, buf);
944     tls_cert_get_cn(&ci->subject.info, &ci->subject.cn);
945 
946     /* Validity */
947     ci->validity.end.sec = gnutls_x509_crt_get_expiration_time(cert);
948     ci->validity.start.sec = gnutls_x509_crt_get_activation_time(cert);
949     ci->validity.gmt = 0;
950 
951     /* Subject Alternative Name extension */
952     if (ci->version >= 3) {
953         char out[256] = { 0 };
954         /* Get the number of all alternate names so that we can allocate
955          * the correct number of bytes in subj_alt_name */
956         while (gnutls_x509_crt_get_subject_alt_name(cert, seq, out, &len,
957                                                     NULL) !=
958                GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
959         {
960             seq++;
961         }
962 
963         ci->subj_alt_name.entry = pj_pool_calloc(pool, seq,
964                                 	sizeof(*ci->subj_alt_name.entry));
965         if (!ci->subj_alt_name.entry) {
966             tls_last_error = GNUTLS_E_MEMORY_ERROR;
967             return;
968         }
969 
970         /* Now populate the alternative names */
971         for (i = 0; i < seq; i++) {
972             len = sizeof(out) - 1;
973             ret = gnutls_x509_crt_get_subject_alt_name(cert, i, out,
974             					       &len, NULL);
975 
976             switch (ret) {
977             case GNUTLS_SAN_IPADDRESS:
978                 type = PJ_SSL_CERT_NAME_IP;
979                 pj_inet_ntop2(len == sizeof(pj_in6_addr) ? pj_AF_INET6()
980                                                          : pj_AF_INET(),
981                               out, buf, sizeof(buf));
982                 break;
983             case GNUTLS_SAN_URI:
984                 type = PJ_SSL_CERT_NAME_URI;
985                 break;
986             case GNUTLS_SAN_RFC822NAME:
987                 type = PJ_SSL_CERT_NAME_RFC822;
988                 break;
989             case GNUTLS_SAN_DNSNAME:
990                 type = PJ_SSL_CERT_NAME_DNS;
991                 break;
992             default:
993                 type = PJ_SSL_CERT_NAME_UNKNOWN;
994                 break;
995             }
996 
997             if (len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
998                 ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
999                 pj_strdup2(pool,
1000                 	&ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
1001                         type == PJ_SSL_CERT_NAME_IP ? buf : out);
1002                 ci->subj_alt_name.cnt++;
1003             }
1004         }
1005         /* TODO: if no DNS alt. names were found, we could check against
1006          * the commonName as per RFC3280. */
1007     }
1008 }
1009 
1010 static void tls_cert_get_chain_raw(pj_pool_t *pool, pj_ssl_cert_info *ci,
1011 				   const gnutls_datum_t *certs,
1012 				   size_t certs_num)
1013 {
1014     size_t i=0;
1015     ci->raw_chain.cert_raw = pj_pool_calloc(pool, certs_num,
1016     					sizeof(*ci->raw_chain.cert_raw));
1017     ci->raw_chain.cnt = certs_num;
1018     for (i=0; i < certs_num; ++i) {
1019         const pj_str_t crt_raw = {(char*)certs[i].data,
1020         			  (pj_ssize_t)certs[i].size};
1021         pj_strdup(pool, ci->raw_chain.cert_raw+i, &crt_raw);
1022     }
1023 }
1024 
1025 /* Update local & remote certificates info. This function should be
1026  * called after handshake or renegotiation successfully completed. */
1027 static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
1028 {
1029     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
1030     gnutls_x509_crt_t cert = NULL;
1031     const gnutls_datum_t *us;
1032     const gnutls_datum_t *certs;
1033     unsigned int certslen = 0;
1034     int ret = GNUTLS_CERT_INVALID;
1035 
1036     pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED);
1037 
1038     /* Get active local certificate */
1039     us = gnutls_certificate_get_ours(gssock->session);
1040     if (!us)
1041         goto us_out;
1042 
1043     ret = gnutls_x509_crt_init(&cert);
1044     if (ret < 0)
1045         goto us_out;
1046     ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_DER);
1047     if (ret < 0)
1048         ret = gnutls_x509_crt_import(cert, us, GNUTLS_X509_FMT_PEM);
1049     if (ret < 0)
1050         goto us_out;
1051 
1052     tls_cert_get_info(ssock->pool, &ssock->local_cert_info, cert);
1053     pj_pool_reset(ssock->info_pool);
1054     tls_cert_get_chain_raw(ssock->info_pool, &ssock->local_cert_info, us, 1);
1055 
1056 us_out:
1057     tls_last_error = ret;
1058     if (cert)
1059         gnutls_x509_crt_deinit(cert);
1060     else
1061         pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info));
1062 
1063     cert = NULL;
1064 
1065     /* Get active remote certificate */
1066     certs = gnutls_certificate_get_peers(gssock->session, &certslen);
1067     if (certs == NULL || certslen == 0)
1068         goto peer_out;
1069 
1070     ret = gnutls_x509_crt_init(&cert);
1071     if (ret < 0)
1072         goto peer_out;
1073 
1074     ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_PEM);
1075     if (ret < 0)
1076         ret = gnutls_x509_crt_import(cert, certs, GNUTLS_X509_FMT_DER);
1077     if (ret < 0)
1078         goto peer_out;
1079 
1080     tls_cert_get_info(ssock->pool, &ssock->remote_cert_info, cert);
1081     pj_pool_reset(ssock->info_pool);
1082     tls_cert_get_chain_raw(ssock->info_pool, &ssock->remote_cert_info, certs,
1083     			   certslen);
1084 
1085 peer_out:
1086     tls_last_error = ret;
1087     if (cert)
1088         gnutls_x509_crt_deinit(cert);
1089     else
1090         pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
1091 }
1092 
1093 static void ssl_set_state(pj_ssl_sock_t *ssock, pj_bool_t is_server)
1094 {
1095     PJ_UNUSED_ARG(ssock);
1096     PJ_UNUSED_ARG(is_server);
1097 }
1098 
1099 static void ssl_set_peer_name(pj_ssl_sock_t *ssock)
1100 {
1101     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
1102 
1103     /* Set server name to connect */
1104     if (ssock->param.server_name.slen &&
1105         get_ip_addr_ver(&ssock->param.server_name) == 0)
1106     {
1107         int ret;
1108         /* Server name is null terminated already */
1109         ret = gnutls_server_name_set(gssock->session, GNUTLS_NAME_DNS,
1110                                      ssock->param.server_name.ptr,
1111                                      ssock->param.server_name.slen);
1112         if (ret < 0) {
1113             PJ_LOG(3, (ssock->pool->obj_name,
1114                        "gnutls_server_name_set() failed: %s",
1115                        gnutls_strerror(ret)));
1116         }
1117     }
1118 }
1119 
1120 /* Try to perform an asynchronous handshake */
1121 static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock)
1122 {
1123     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
1124     int ret;
1125     pj_status_t status;
1126 
1127     /* Perform SSL handshake */
1128     ret = gnutls_handshake(gssock->session);
1129 
1130     status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0);
1131     if (status != PJ_SUCCESS)
1132         return status;
1133 
1134     if (ret == GNUTLS_E_SUCCESS) {
1135         /* System are GO */
1136         ssock->ssl_state = SSL_STATE_ESTABLISHED;
1137         status = PJ_SUCCESS;
1138     } else if (!gnutls_error_is_fatal(ret)) {
1139         /* Non fatal error, retry later (busy or again) */
1140         status = PJ_EPENDING;
1141     } else {
1142         /* Fatal error invalidates session, no fallback */
1143         status = PJ_EINVAL;
1144     }
1145 
1146     tls_last_error = ret;
1147 
1148     return status;
1149 }
1150 
1151 static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size)
1152 {
1153     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
1154     int decrypted_size;
1155 
1156     /* Decrypt received data using GnuTLS (will read our input
1157      * circular buffer) */
1158     decrypted_size = gnutls_record_recv(gssock->session, data, *size);
1159     *size = 0;
1160     if (decrypted_size > 0) {
1161         *size = decrypted_size;
1162         return PJ_SUCCESS;
1163     } else if (decrypted_size == 0) {
1164         /* Nothing more to read */
1165         return PJ_SUCCESS;
1166     } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) {
1167     	return PJ_EEOF;
1168     } else if (decrypted_size == GNUTLS_E_AGAIN ||
1169                decrypted_size == GNUTLS_E_INTERRUPTED ||
1170                !gnutls_error_is_fatal(decrypted_size))
1171     {
1172     	/* non-fatal error, let's just continue */
1173         return PJ_SUCCESS;
1174     } else {
1175         return PJ_ECANCELLED;
1176     }
1177 }
1178 
1179 /*
1180  * Write the plain data to GnuTLS, it will be encrypted by gnutls_record_send()
1181  * and sent via tls_data_push. Note that re-negotitation may be on progress, so
1182  * sending data should be delayed until re-negotiation is completed.
1183  */
1184 static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data,
1185 			     pj_ssize_t size, int *nwritten)
1186 {
1187     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
1188     int nwritten_;
1189     pj_ssize_t total_written = 0;
1190 
1191     /* Ask GnuTLS to encrypt our plaintext now. GnuTLS will use the push
1192      * callback to actually write the encrypted bytes into our output circular
1193      * buffer. GnuTLS may refuse to "send" everything at once, but since we are
1194      * not really sending now, we will just call it again now until it succeeds
1195      * (or fails in a fatal way). */
1196     while (total_written < size) {
1197         /* Try encrypting using GnuTLS */
1198         nwritten_ = gnutls_record_send(gssock->session,
1199         			      ((read_data_t *)data) + total_written,
1200                                       size - total_written);
1201 
1202         if (nwritten_ > 0) {
1203             /* Good, some data was encrypted and written */
1204             total_written += nwritten_;
1205         } else {
1206             /* Normally we would have to retry record_send but our internal
1207              * state has not changed, so we have to ask for more data first.
1208              * We will just try again later, although this should never happen.
1209              */
1210             *nwritten = nwritten_;
1211             return tls_status_from_err(ssock, nwritten_);
1212         }
1213     }
1214 
1215     /* All encrypted data is written to the output circular buffer;
1216      * now send it on the socket (or notify problem). */
1217     *nwritten = total_written;
1218     return PJ_SUCCESS;
1219 }
1220 
1221 static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock)
1222 {
1223     gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
1224     int status;
1225 
1226     /* First call gnutls_rehandshake() to see if this is even possible */
1227     status = gnutls_rehandshake(gssock->session);
1228 
1229     if (status == GNUTLS_E_SUCCESS) {
1230         /* Rehandshake is possible, so try a GnuTLS handshake now. The eventual
1231          * gnutls_record_recv() calls could return a few specific values during
1232          * this state:
1233          *
1234          *   - GNUTLS_E_REHANDSHAKE: rehandshake message processing
1235          *   - GNUTLS_E_WARNING_ALERT_RECEIVED: client does not wish to
1236          *                                      renegotiate
1237          */
1238         return PJ_SUCCESS;
1239     } else {
1240         return tls_status_from_err(ssock, status);
1241     }
1242 }
1243 
1244 #endif /* PJ_HAS_SSL_SOCK */
1245