diff --git a/NEWS b/NEWS index 4157d57..7126517 100644 --- a/NEWS +++ b/NEWS @@ -1,31 +1,36 @@ -Noteworthy changes in version 0.1.3 (unreleased) [C0/A0/R3] +Noteworthy changes in version 0.2.0 (unreleased) [C0/A0/R3] ------------------------------------------------ + * Interface changes relative to version 0.1.2 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ntbtls_get_last_alert NEW function. + + Noteworthy changes in version 0.1.2 (2017-09-19) [C0/A0/R2] ------------------------------------------------ * Add support for secp384r1, secp521r1, and 3 Brainpool curves. Noteworthy changes in version 0.1.1 (2017-03-16) [C0/A0/R1] ------------------------------------------------ * Now supports ECDHE-RSA key exchange. Noteworthy changes in version 0.1.0 (2017-02-21) [C0/A0/R0] ------------------------------------------------ * Initial experimental release with only the client code. See the file AUTHORS for copying conditions. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/src/context.h b/src/context.h index 66f7356..9cd0dc9 100644 --- a/src/context.h +++ b/src/context.h @@ -1,453 +1,458 @@ /* context.h - the context object * Copyright (C) 2006-2014, Brainspark B.V. * Copyright (C) 2014 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef NTBTLS_CONTEXT_H #define NTBTLS_CONTEXT_H #include typedef enum gcry_md_algos md_algo_t; typedef enum gcry_mac_algos mac_algo_t; typedef enum gcry_cipher_algos cipher_algo_t; typedef enum gcry_cipher_modes cipher_mode_t; typedef enum gcry_pk_algos pk_algo_t; /* * TLS states (note that the order of the states is important) */ typedef enum { TLS_HELLO_REQUEST, TLS_CLIENT_HELLO, TLS_SERVER_HELLO, TLS_SERVER_CERTIFICATE, TLS_SERVER_KEY_EXCHANGE, TLS_CERTIFICATE_REQUEST, TLS_SERVER_HELLO_DONE, TLS_CLIENT_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_CERTIFICATE_VERIFY, TLS_CLIENT_CHANGE_CIPHER_SPEC, TLS_CLIENT_FINISHED, TLS_SERVER_CHANGE_CIPHER_SPEC, TLS_SERVER_FINISHED, TLS_FLUSH_BUFFERS, TLS_HANDSHAKE_WRAPUP, TLS_HANDSHAKE_OVER, TLS_SERVER_NEW_SESSION_TICKET } tls_state_t; /* * Renegotiation states */ typedef enum { TLS_INITIAL_HANDSHAKE = 0, TLS_RENEGOTIATION, /* In progress */ TLS_RENEGOTIATION_DONE, /* Done */ TLS_RENEGOTIATION_PENDING /* Requested (server only) */ } tls_renegotiation_state_t; /* * Key exchange protocols * * Reminder: Update premaster_secret_u when adding a new key exchange. */ typedef enum { KEY_EXCHANGE_NONE = 0, KEY_EXCHANGE_RSA, KEY_EXCHANGE_DHE_RSA, KEY_EXCHANGE_ECDHE_RSA, KEY_EXCHANGE_ECDHE_ECDSA, KEY_EXCHANGE_PSK, KEY_EXCHANGE_DHE_PSK, KEY_EXCHANGE_RSA_PSK, KEY_EXCHANGE_ECDHE_PSK, KEY_EXCHANGE_ECDH_RSA, KEY_EXCHANGE_ECDH_ECDSA } key_exchange_type_t; /* * Object to hold an X.509 CRL. */ struct x509_crl_s; typedef struct x509_crl_s *x509_crl_t; /* * Object to hold an X.509 private key. */ struct x509_privkey_s; typedef struct x509_privkey_s *x509_privkey_t; /* * Object to hold an DHM context. */ struct dhm_context_s; typedef struct dhm_context_s *dhm_context_t; /* * Object to hold an ECDH context. */ struct ecdh_context_s; typedef struct ecdh_context_s *ecdh_context_t; /* * This structure is used for storing current session data. */ struct _ntbtls_session_s { time_t start; /*!< starting time */ int ciphersuite; /*!< chosen ciphersuite */ int compression; /*!< chosen compression */ size_t length; /*!< session id length */ unsigned char id[32]; /*!< session identifier */ unsigned char master[48]; /*!< the master secret */ x509_cert_t peer_chain; /*!< peer X.509 cert chain */ int verify_result; /*!< verification result */ unsigned char *ticket; /*!< RFC 5077 session ticket */ size_t ticket_len; /*!< session ticket length */ uint32_t ticket_lifetime; /*!< ticket lifetime hint */ unsigned char mfl_code; /*!< MaxFragmentLength negotiated by peer */ int use_trunc_hmac; /* Flag for truncated hmac activation. */ }; typedef struct _ntbtls_session_s *session_t; /* * This structure is used for storing ciphersuite information */ struct _ntbtls_ciphersuite_s; typedef const struct _ntbtls_ciphersuite_s *ciphersuite_t; /* * This structure contains a full set of runtime transform parameters * either in negotiation or active. */ struct _ntbtls_transform_s { /* * Session specific crypto layer */ ciphersuite_t ciphersuite; /*!< Chosen cipersuite_info */ unsigned int keylen; /*!< symmetric key length */ size_t minlen; /*!< min. ciphertext length */ size_t ivlen; /*!< IV length */ size_t fixed_ivlen; /*!< Fixed part of IV (AEAD) */ size_t maclen; /* MAC length in bytes */ unsigned char iv_enc[16]; /*!< IV (encryption) */ unsigned char iv_dec[16]; /*!< IV (decryption) */ gcry_mac_hd_t mac_ctx_enc; /* MAC (encryption) */ gcry_mac_hd_t mac_ctx_dec; /* MAC (decryption) */ gcry_cipher_hd_t cipher_ctx_enc; /* Encryption context. */ cipher_mode_t cipher_mode_enc;/* Mode for encryption. */ gcry_cipher_hd_t cipher_ctx_dec; /* Decryption context. */ cipher_mode_t cipher_mode_dec;/* Mode for encryption. */ /* * Session specific compression layer */ z_stream ctx_deflate; /*!< compression context */ z_stream ctx_inflate; /*!< decompression context */ }; typedef struct _ntbtls_transform_s *transform_t; /* * List of certificate + private key pairs */ struct _ntbtls_key_cert_s { struct _ntbtls_key_cert_s *next; x509_cert_t cert; x509_privkey_t key; }; typedef struct _ntbtls_key_cert_s *key_cert_t; /* * This structure contains the parameters only needed during handshake. */ struct _ntbtls_handshake_params_s { /* * Handshake specific crypto variables */ int sig_alg; /*!< Hash algorithm for signature */ int cert_type; /*!< Requested cert type */ int verify_sig_alg; /*!< Signature algorithm for verify */ dhm_context_t dhm_ctx; /* DHM key exchange info. */ ecdh_context_t ecdh_ctx; /* ECDH key exchange info. */ const /*ecp_curve_info*/void **curves;/*!< Supported elliptic curves */ /** * //FIXME: Better explain this * Current key/cert or key/cert list. * On client: pointer to ssl->key_cert, only the first entry used. * On server: starts as a pointer to ssl->key_cert, then becomes * a pointer to the chosen key from this list or the SNI list. */ key_cert_t key_cert; key_cert_t sni_key_cert; /*!< key/cert list from SNI */ /* * Checksum contexts */ gcry_md_hd_t fin_sha256; /* Checksum of all handshake messages. */ gcry_md_hd_t fin_sha512; /* Ditto. */ void (*update_checksum) (ntbtls_t, const unsigned char *, size_t); void (*calc_verify) (ntbtls_t, unsigned char *); void (*calc_finished) (ntbtls_t, unsigned char *, int); gpg_error_t (*tls_prf) (const unsigned char *, size_t, const char *, const unsigned char *, size_t, unsigned char *, size_t); size_t pmslen; /*!< premaster length */ unsigned char randbytes[64]; /*!< random bytes */ unsigned char premaster[TLS_PREMASTER_SIZE]; /*!< premaster secret */ int resume; /*!< session resume indicator */ int max_major_ver; /*!< max. major version client */ int max_minor_ver; /*!< max. minor version client */ int cli_exts; /*!< client extension presence */ int new_session_ticket; /*!< use NewSessionTicket? */ }; typedef struct _ntbtls_handshake_params_s *handshake_params_t; /* * Parameters needed to secure session tickets */ struct _ntbtls_ticket_keys_s { unsigned char key_name[16]; /*!< name to quickly discard bad tickets */ gcry_cipher_hd_t enc; /*!< encryption context */ gcry_cipher_hd_t dec; /*!< decryption context */ unsigned char mac_key[16]; /*!< authentication key */ }; typedef struct _ntbtls_ticket_keys_s *ticket_keys_t; #if SIZEOF_UNSIGNED_LONG == 8 # define NTBTLS_CONTEXT_MAGIC 0x6e7462746c736378 /* "ntbtlscx" */ #else # define NTBTLS_CONTEXT_MAGIC 0x6e746243 /* "ntbC" */ #endif /* * The TLS context object. */ struct _ntbtls_context_s { unsigned long magic; /* * Miscellaneous */ int major_ver; /*!< equal to SSL_MAJOR_VERSION_3 */ int minor_ver; /*!< either 0 (SSL3) or 1 (TLS1.0) */ int max_major_ver; /*!< max. major version used */ int max_minor_ver; /*!< max. minor version used */ int min_major_ver; /*!< min. major version used */ int min_minor_ver; /*!< min. minor version used */ tls_state_t state; /* Current state of the handshake. */ tls_renegotiation_state_t renegotiation; /*!< Initial or renegotiation */ int renego_records_seen; /*!< Records since renego request */ + struct { + unsigned char any; + unsigned char level; + unsigned char type; + } last_alert; /* Info about the last received alert. */ /* * Callbacks (RNG, debug, I/O, verification) */ void (*f_dbg) (void *, int, const char *); int (*f_recv) (void *, unsigned char *, size_t); int (*f_send) (void *, const unsigned char *, size_t); int (*f_get_cache) (void *, session_t); int (*f_set_cache) (void *, const session_t); void *p_dbg; /*!< context for the debug function */ void *p_recv; /*!< context for reading operations */ void *p_send; /*!< context for writing operations */ void *p_get_cache; /*!< context for cache retrieval */ void *p_set_cache; /*!< context for cache store */ void *p_hw_data; /*!< context for HW acceleration */ int (*f_sni) (void *, ntbtls_t, const unsigned char *, size_t); void *p_sni; /*!< context for SNI extension */ int (*f_vrfy) (void *, x509_cert_t, int, int *); void *p_vrfy; /*!< context for verification */ int (*f_psk) (void *, ntbtls_t, const unsigned char *, size_t); void *p_psk; /*!< context for PSK retrieval */ /* * Session layer */ session_t session_in; /*!< current session data (in) */ session_t session_out; /*!< current session data (out) */ session_t session; /*!< negotiated session data */ session_t session_negotiate; /* Session data in negotiation. */ handshake_params_t handshake; /* Params required only during the handshake process. */ /* * Record layer transformations */ transform_t transform_in; /*!< current transform params (in) */ transform_t transform_out; /*!< current transform params (in) */ transform_t transform; /*!< negotiated transform params */ transform_t transform_negotiate; /* Transform params in negotiation. */ /* * Record layer (incoming data) */ estream_t inbound; /* Stream used to receive TLS data. */ unsigned char *in_ctr; /*!< 64-bit incoming message counter */ unsigned char *in_hdr; /*!< 5-byte record header (in_ctr+8) */ unsigned char *in_iv; /*!< ivlen-byte IV (in_hdr+5) */ unsigned char *in_msg; /*!< message contents (in_iv+ivlen) */ unsigned char *in_offt; /*!< read offset in application data */ int in_msgtype; /*!< record header: message type */ size_t in_msglen; /*!< record header: message length */ size_t in_left; /* Amount of data read so far. */ size_t in_hslen; /*!< current handshake message length */ int nb_zero; /*!< # of 0-length encrypted messages */ int record_read; /*!< record is already present */ /* * Record layer (outgoing data) */ estream_t outbound; /* Stream used to send TLS data. */ unsigned char *out_ctr; /*!< 64-bit outgoing message counter */ unsigned char *out_hdr; /*!< 5-byte record header (out_ctr+8) */ unsigned char *out_iv; /*!< ivlen-byte IV (out_hdr+5) */ unsigned char *out_msg; /*!< message contents (out_iv+ivlen) */ int out_msgtype; /*!< record header: message type */ size_t out_msglen; /* Record header: message length. */ size_t out_left; /* Amount of data not yet written. */ unsigned char *compress_buf; /*!< zlib data buffer */ unsigned char mfl_code; /*!< MaxFragmentLength chosen by us */ /* * Layer to the TLS encrypted data */ estream_t readfp; /* Estream to read from the peer. */ estream_t writefp; /* Estream to write to the peer. */ /* * PKI layer */ key_cert_t key_cert; /*!< own certificate(s)/key(s) */ ntbtls_verify_cb_t verify_cb; /*!< the verify callback */ void *verify_cb_value;; /*!< the first arg passed to this cb */ /* * Support for generating and checking session tickets */ ticket_keys_t ticket_keys; /*!< keys for ticket encryption */ /* * User settings */ int is_client; /* True if we are in client mode. */ unsigned int flags; /* All flags from ntbtls_new. */ int authmode; /*!< verification mode */ int client_auth; /*!< flag for client auth. */ int verify_result; /*!< verification result */ int disable_renegotiation; /*!< enable/disable renegotiation */ int allow_legacy_renegotiation; /*!< allow legacy renegotiation */ int renego_max_records; /*!< grace period for renegotiation */ const int *ciphersuite_list[4]; /*!< allowed ciphersuites / version */ const /*ecp_group_id*/ void *curve_list; /*!< allowed curves */ int use_trunc_hmac; /* Use truncated HMAC flag. */ int use_session_tickets; /* Use session tickets flag. */ int ticket_lifetime; /*!< session ticket lifetime */ gcry_mpi_t dhm_P; /*!< prime modulus for DHM */ gcry_mpi_t dhm_G; /*!< generator for DHM */ char *hostname; /*!< expected peer CN for verification and SNI */ /* * PSK values */ unsigned char *psk; size_t psk_len; unsigned char *psk_identity; size_t psk_identity_len; /* * ALPN extension */ const char **alpn_list; /*!< ordered list of supported protocols */ const char *alpn_chosen; /*!< negotiated protocol */ /* * Secure renegotiation */ int secure_renegotiation; /*!< does peer support legacy or secure renegotiation */ size_t verify_data_len; /*!< length of verify data stored */ char own_verify_data[36]; /*!< previous handshake verify data */ char peer_verify_data[36]; /*!< previous handshake verify data */ }; #endif /*NTBTLS_CONTEXT_H*/ diff --git a/src/libntbtls.def b/src/libntbtls.def index fe6e0fd..5431533 100644 --- a/src/libntbtls.def +++ b/src/libntbtls.def @@ -1,42 +1,44 @@ ; libtnbtls.def - List of symbols to export. ; Copyright (C) 2014 g10 Code GmbH ; ; This file is part of NTBTLS ; ; NTBTLS is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 3 of the License, or ; (at your option) any later version. ; ; NTBTLS is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, see . ; ; NOTE: When adding new functions, please make sure to add them also ; to visibility.{c,h} and libntbtls.vers. EXPORTS ntbtls_check_version @1 ntbtls_set_debug @2 ntbtls_set_log_handler @3 ntbtls_new @4 _ntbtls_check_context @5 ntbtls_release @6 ntbtls_set_transport @7 ntbtls_get_stream @8 ntbtls_set_hostname @9 ntbtls_get_hostname @10 ntbtls_set_verify_cb @11 ntbtls_handshake @12 ntbtls_x509_get_peer_cert @13 + ntbtls_get_last_alert @14 + ; END diff --git a/src/libntbtls.vers b/src/libntbtls.vers index 48d7662..d1f9c88 100644 --- a/src/libntbtls.vers +++ b/src/libntbtls.vers @@ -1,44 +1,46 @@ # libntbtls.vers - What symbols to export -*- std -*- # Copyright (C) 2014 g10 Code GmbH # # This file is part of NTBTLS # # NTBTLS is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # NTBTLS is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # NOTE: When adding new functions, please make sure to add them also # to visibility.{c,h} and libntbtls.def. NTBTLS_1.0 { global: ntbtls_check_version; ntbtls_set_debug; ntbtls_set_log_handler; ntbtls_new; _ntbtls_check_context; ntbtls_release; ntbtls_set_transport; ntbtls_get_stream; ntbtls_set_hostname; ntbtls_get_hostname; ntbtls_set_verify_cb; ntbtls_handshake; ntbtls_x509_get_peer_cert; + ntbtls_get_last_alert; + local: *; }; diff --git a/src/ntbtls-cli.c b/src/ntbtls-cli.c index 555961b..bbfcd3c 100644 --- a/src/ntbtls-cli.c +++ b/src/ntbtls-cli.c @@ -1,465 +1,469 @@ /* ntbtls-cli.h - NTBTLS client test program * Copyright (C) 2014 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM # define WIN32_LEAN_AND_MEAN # ifdef HAVE_WINSOCK2_H # include # endif # include #else # include # include # include # include #endif #include "ntbtls.h" #define PGMNAME "ntbtls-cli" static int verbose; static int errorcount; static char *opt_hostname; static int opt_head; /* * Reporting functions. */ static void die (const char *format, ...) { va_list arg_ptr ; fflush (stdout); #ifdef HAVE_FLOCKFILE flockfile (stderr); #endif fprintf (stderr, "%s: ", PGMNAME); va_start (arg_ptr, format) ; vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); if (*format && format[strlen(format)-1] != '\n') putc ('\n', stderr); #ifdef HAVE_FLOCKFILE funlockfile (stderr); #endif exit (1); } static void fail (const char *format, ...) { va_list arg_ptr; fflush (stdout); #ifdef HAVE_FLOCKFILE flockfile (stderr); #endif fprintf (stderr, "%s: ", PGMNAME); va_start (arg_ptr, format); vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); if (*format && format[strlen(format)-1] != '\n') putc ('\n', stderr); #ifdef HAVE_FLOCKFILE funlockfile (stderr); #endif errorcount++; if (errorcount >= 50) die ("stopped after 50 errors."); } static void info (const char *format, ...) { va_list arg_ptr; if (!verbose) return; #ifdef HAVE_FLOCKFILE flockfile (stderr); #endif fprintf (stderr, "%s: ", PGMNAME); va_start (arg_ptr, format); vfprintf (stderr, format, arg_ptr); if (*format && format[strlen(format)-1] != '\n') putc ('\n', stderr); va_end (arg_ptr); #ifdef HAVE_FLOCKFILE funlockfile (stderr); #endif } /* Until we support send/recv in estream we need to use es_fopencookie * under Windows. */ #ifdef HAVE_W32_SYSTEM static gpgrt_ssize_t w32_cookie_read (void *cookie, void *buffer, size_t size) { int sock = (int)cookie; int nread; do { /* Under Windows we need to use recv for a socket. */ nread = recv (sock, buffer, size, 0); } while (nread == -1 && errno == EINTR); return (gpgrt_ssize_t)nread; } static gpg_error_t w32_write_server (int sock, const char *data, size_t length) { int nleft; int nwritten; nleft = length; while (nleft > 0) { nwritten = send (sock, data, nleft, 0); if ( nwritten == SOCKET_ERROR ) { info ("network write failed: ec=%d\n", (int)WSAGetLastError ()); return gpg_error (GPG_ERR_NETWORK); } nleft -= nwritten; data += nwritten; } return 0; } /* Write handler for estream. */ static gpgrt_ssize_t w32_cookie_write (void *cookie, const void *buffer_arg, size_t size) { int sock = (int)cookie; const char *buffer = buffer_arg; int nwritten = 0; if (w32_write_server (sock, buffer, size)) { gpg_err_set_errno (EIO); nwritten = -1; } else nwritten = size; return (gpgrt_ssize_t)nwritten; } static es_cookie_io_functions_t w32_cookie_functions = { w32_cookie_read, w32_cookie_write, NULL, NULL }; #endif /*HAVE_W32_SYSTEM*/ static int connect_server (const char *server, unsigned short port) { gpg_error_t err; int sock = -1; struct sockaddr_in addr; struct hostent *host; union { char *addr; struct in_addr *in_addr; } addru; addr.sin_family = AF_INET; addr.sin_port = htons (port); host = gethostbyname ((char*)server); if (!host) { err = gpg_error_from_syserror (); fail ("host '%s' not found: %s\n", server, gpg_strerror (err)); return -1; } addru.addr = host->h_addr; addr.sin_addr = *addru.in_addr; sock = socket (AF_INET, SOCK_STREAM, 0); if (sock == -1) { err = gpg_error_from_syserror (); die ("error creating socket: %s\n", gpg_strerror (err)); return -1; } if (connect (sock, (struct sockaddr *)&addr, sizeof addr) == -1) { err = gpg_error_from_syserror (); fail ("error connecting '%s': %s\n", server, gpg_strerror (err)); close (sock); return -1; } info ("connected to '%s' port %hu\n", server, port); return sock; } static int connect_estreams (const char *server, int port, estream_t *r_in, estream_t *r_out) { gpg_error_t err; int sock; *r_in = *r_out = NULL; sock = connect_server (server, port); if (sock == -1) return gpg_error (GPG_ERR_GENERAL); #ifdef HAVE_W32_SYSTEM *r_in = es_fopencookie ((void*)(unsigned int)sock, "rb", w32_cookie_functions); #else *r_in = es_fdopen (sock, "rb"); #endif if (!*r_in) { err = gpg_error_from_syserror (); close (sock); return err; } #ifdef HAVE_W32_SYSTEM *r_out = es_fopencookie ((void*)(unsigned int)sock, "wb", w32_cookie_functions); #else *r_out = es_fdopen (sock, "wb"); #endif if (!*r_out) { err = gpg_error_from_syserror (); es_fclose (*r_in); *r_in = NULL; close (sock); return err; } return 0; } static void simple_client (const char *server, int port) { gpg_error_t err; ntbtls_t tls; estream_t inbound, outbound; estream_t readfp, writefp; int c; err = ntbtls_new (&tls, NTBTLS_CLIENT); if (err) die ("ntbtls_init failed: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); err = connect_estreams (server, port, &inbound, &outbound); if (err) die ("error connecting server: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); err = ntbtls_set_transport (tls, inbound, outbound); if (err) die ("ntbtls_set_transport failed: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); err = ntbtls_get_stream (tls, &readfp, &writefp); if (err) die ("ntbtls_get_stream failed: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); if (opt_hostname) { err = ntbtls_set_hostname (tls, opt_hostname); if (err) die ("ntbtls_set_hostname failed: %s <%s>\n", gpg_strerror (err), gpg_strsource (err)); } info ("starting handshake"); while ((err = ntbtls_handshake (tls))) { + const char *s; + + if ((s = ntbtls_get_last_alert (tls, NULL, NULL))) + info ("received alert: %s", s); info ("handshake error: %s <%s>", gpg_strerror (err),gpg_strsource (err)); switch (gpg_err_code (err)) { default: break; } die ("handshake failed"); } info ("handshake done"); do { es_fprintf (writefp, "%s / HTTP/1.0\r\n", opt_head? "HEAD":"GET"); if (opt_hostname) es_fprintf (writefp, "Host: %s\r\n", opt_hostname); es_fprintf (writefp, "X-ntbtls: %s\r\n", ntbtls_check_version (PACKAGE_VERSION)); es_fputs ("\r\n", writefp); es_fflush (writefp); while (/*es_pending (readfp) &&*/ (c = es_fgetc (readfp)) != EOF) putchar (c); } while (c != EOF); ntbtls_release (tls); es_fclose (inbound); es_fclose (outbound); } int main (int argc, char **argv) { int last_argc = -1; int debug_level = 0; int port = 443; char *host; if (argc) { argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--")) { argc--; argv++; break; } else if (!strcmp (*argv, "--help")) { fputs ("Usage: " PGMNAME " [OPTIONS] HOST\n" "Connect via TLS to HOST\n" "Options:\n" " --version print the library version\n" " --verbose show more diagnostics\n" " --debug LEVEL enable debugging at LEVEL\n" " --port N connect to port N (default is 443)\n" " --hostname NAME use NAME instead of HOST for SNI\n" " --head send a HEAD and not a GET request\n" "\n", stdout); return 0; } else if (!strcmp (*argv, "--version")) { printf ("%s\n", ntbtls_check_version (NULL)); if (verbose) printf ("%s", ntbtls_check_version ("\001\001")); return 0; } else if (!strcmp (*argv, "--verbose")) { verbose = 1; argc--; argv++; } else if (!strcmp (*argv, "--debug")) { verbose = 1; argc--; argv++; if (argc) { debug_level = atoi (*argv); argc--; argv++; } else debug_level = 1; } else if (!strcmp (*argv, "--port")) { argc--; argv++; if (argc) { port = atoi (*argv); argc--; argv++; } else port = 8443; } else if (!strcmp (*argv, "--hostname")) { if (argc < 2) die ("argument missing for option '%s'\n", *argv); argc--; argv++; opt_hostname = *argv; argc--; argv++; } else if (!strcmp (*argv, "--head")) { opt_head = 1; argc--; argv++; } else if (!strncmp (*argv, "--", 2) && (*argv)[2]) die ("Invalid option '%s'\n", *argv); } host = argc? *argv : "localhost"; if (!opt_hostname) opt_hostname = host; if (!*opt_hostname) opt_hostname = NULL; #ifdef HAVE_W32_SYSTEM { WSADATA wsadat; WSAStartup (0x202, &wsadat); } #endif if (!ntbtls_check_version (PACKAGE_VERSION)) die ("NTBTLS library too old (need %s, have %s)\n", PACKAGE_VERSION, ntbtls_check_version (NULL)); if (debug_level) ntbtls_set_debug (debug_level, NULL, NULL); simple_client (host, port); return 0; } diff --git a/src/ntbtls-int.h b/src/ntbtls-int.h index cb20ccc..c431152 100644 --- a/src/ntbtls-int.h +++ b/src/ntbtls-int.h @@ -1,420 +1,422 @@ /* ntbtls-int.h - Internal version of ntbtls.h * Copyright (C) 2014 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef NTBTLS_NTBTLS_INT_H #define NTBTLS_NTBTLS_INT_H #ifdef _NTBTLS_H #error ntbtls.h already included #endif #include #include "ntbtls.h" #include "util.h" /* * Various constants */ #define TLS_MAJOR_VERSION_3 3 #define TLS_MINOR_VERSION_0 0 /* SSL v3.0 */ #define TLS_MINOR_VERSION_1 1 /* TLS v1.0 */ #define TLS_MINOR_VERSION_2 2 /* TLS v1.1 */ #define TLS_MINOR_VERSION_3 3 /* TLS v1.2 */ /* Define minimum and maximum supported versions. This is currently TLS v1.2 only but we may support newer versions of TLS as soon as they are standardized. */ #define TLS_MIN_MAJOR_VERSION TLS_MAJOR_VERSION_3 #define TLS_MIN_MINOR_VERSION TLS_MINOR_VERSION_3 #define TLS_MAX_MAJOR_VERSION TLS_MAJOR_VERSION_3 #define TLS_MAX_MINOR_VERSION TLS_MINOR_VERSION_3 #define TLS_RENEGO_MAX_RECORDS_DEFAULT 16 #define TLS_RENEGOTIATION_DISABLED 0 #define TLS_RENEGOTIATION_ENABLED 1 #define TLS_RENEGOTIATION_NOT_ENFORCED -1 #define TLS_COMPRESS_NULL 0 #define TLS_COMPRESS_DEFLATE 1 #define TLS_VERIFY_NONE 0 #define TLS_VERIFY_OPTIONAL 1 #define TLS_VERIFY_REQUIRED 2 #define TLS_LEGACY_RENEGOTIATION 0 #define TLS_SECURE_RENEGOTIATION 1 #define TLS_LEGACY_NO_RENEGOTIATION 0 #define TLS_LEGACY_ALLOW_RENEGOTIATION 1 #define TLS_LEGACY_BREAK_HANDSHAKE 2 #define TLS_TRUNCATED_HMAC_LEN 10 /* 80 bits, rfc 6066 section 7 */ /* Lifetime of session tickets in seconds. */ #define TLS_DEFAULT_TICKET_LIFETIME 86400 /* Maximum size of a MAC. */ #define TLS_MAX_MAC_SIZE 48 /* * Size of the input/output buffer. Note: the RFC defines the default * size of TLS messages. If you change the value here, other * clients/servers may not be able to communicate with you anymore. * Only change this value if you control both sides of the connection * and have it reduced at both sides, or if you're using the Max * Fragment Length extension and you know all your peers are using it * too! */ #define TLS_MAX_CONTENT_LEN 16384 /* * Allow extra bytes for record, authentication and encryption overhead: * counter (8) + header (5) + IV(16) + MAC (16-48) + padding (0-256) * and allow for a maximum of 1024 of compression expansion if * enabled. */ #define TLS_COMPRESSION_ADD 1024 #define TLS_MAC_ADD 48 /* SHA-384 used for HMAC */ #define TLS_PADDING_ADD 256 #define TLS_BUFFER_LEN (TLS_MAX_CONTENT_LEN \ + TLS_COMPRESSION_ADD \ + 29 /* counter + header + IV */ \ + TLS_MAC_ADD \ + TLS_PADDING_ADD \ ) /* * The size of the premaster secret. */ #define TLS_MPI_MAX_SIZE 512 /* 4096 bits */ #define TLS_ECP_MAX_BITS 521 #define TLS_ECP_MAX_BYTES ((TLS_ECP_MAX_BITS + 7)/8) #define TLS_PSK_MAX_LEN 32 /* 256 bits */ /* Dummy type used only for its size */ union premaster_secret_u { unsigned char _pms_rsa[48]; /* RFC 5246 8.1.1 */ unsigned char _pms_dhm[TLS_MPI_MAX_SIZE]; /* RFC 5246 8.1.2 */ unsigned char _pms_ecdh[TLS_ECP_MAX_BYTES]; /* RFC 4492 5.10 */ unsigned char _pms_psk[4 + 2 * TLS_PSK_MAX_LEN]; /* RFC 4279 2 */ unsigned char _pms_dhe_psk[4 + TLS_MPI_MAX_SIZE + TLS_PSK_MAX_LEN]; /* RFC 4279 3 */ unsigned char _pms_rsa_psk[52 + TLS_PSK_MAX_LEN]; /* RFC 4279 4 */ unsigned char _pms_ecdhe_psk[4 + TLS_ECP_MAX_BYTES + TLS_PSK_MAX_LEN]; /* RFC 5489 2 */ }; #define TLS_PREMASTER_SIZE sizeof( union premaster_secret_u ) /* RFC 6066 section 4, see also mfl_code_to_length in protocol.c. * NONE must be zero so that memset()ing structure to zero works. */ #define TLS_MAX_FRAG_LEN_NONE 0 /*!< don't use this extension */ #define TLS_MAX_FRAG_LEN_512 1 /*!< MaxFragmentLength 2^9 */ #define TLS_MAX_FRAG_LEN_1024 2 /*!< MaxFragmentLength 2^10 */ #define TLS_MAX_FRAG_LEN_2048 3 /*!< MaxFragmentLength 2^11 */ #define TLS_MAX_FRAG_LEN_4096 4 /*!< MaxFragmentLength 2^12 */ /* * Supported hash and signature algorithms (for TLS 1.2). * RFC 5246 section 7.4.1.4.1 */ #define TLS_HASH_NONE 0 #define TLS_HASH_SHA1 2 #define TLS_HASH_SHA224 3 #define TLS_HASH_SHA256 4 #define TLS_HASH_SHA384 5 #define TLS_HASH_SHA512 6 #define TLS_SIG_ANON 0 #define TLS_SIG_RSA 1 #define TLS_SIG_ECDSA 3 /* * Client Certificate Types * RFC 5246 section 7.4.4 plus RFC 4492 section 5.5 */ #define TLS_CERT_TYPE_RSA_SIGN 1 #define TLS_CERT_TYPE_ECDSA_SIGN 64 /* * Message, alert and handshake types */ #define TLS_MSG_CHANGE_CIPHER_SPEC 20 #define TLS_MSG_ALERT 21 #define TLS_MSG_HANDSHAKE 22 #define TLS_MSG_APPLICATION_DATA 23 #define TLS_ALERT_LEVEL_WARNING 1 #define TLS_ALERT_LEVEL_FATAL 2 #define TLS_ALERT_MSG_CLOSE_NOTIFY 0 /* 0x00 */ #define TLS_ALERT_MSG_UNEXPECTED_MESSAGE 10 /* 0x0A */ #define TLS_ALERT_MSG_BAD_RECORD_MAC 20 /* 0x14 */ #define TLS_ALERT_MSG_DECRYPTION_FAILED 21 /* 0x15 */ #define TLS_ALERT_MSG_RECORD_OVERFLOW 22 /* 0x16 */ #define TLS_ALERT_MSG_DECOMPRESSION_FAILURE 30 /* 0x1E */ #define TLS_ALERT_MSG_HANDSHAKE_FAILURE 40 /* 0x28 */ #define TLS_ALERT_MSG_NO_CERT 41 /* 0x29 */ #define TLS_ALERT_MSG_BAD_CERT 42 /* 0x2A */ #define TLS_ALERT_MSG_UNSUPPORTED_CERT 43 /* 0x2B */ #define TLS_ALERT_MSG_CERT_REVOKED 44 /* 0x2C */ #define TLS_ALERT_MSG_CERT_EXPIRED 45 /* 0x2D */ #define TLS_ALERT_MSG_CERT_UNKNOWN 46 /* 0x2E */ #define TLS_ALERT_MSG_ILLEGAL_PARAMETER 47 /* 0x2F */ #define TLS_ALERT_MSG_UNKNOWN_CA 48 /* 0x30 */ #define TLS_ALERT_MSG_ACCESS_DENIED 49 /* 0x31 */ #define TLS_ALERT_MSG_DECODE_ERROR 50 /* 0x32 */ #define TLS_ALERT_MSG_DECRYPT_ERROR 51 /* 0x33 */ #define TLS_ALERT_MSG_EXPORT_RESTRICTION 60 /* 0x3C */ #define TLS_ALERT_MSG_PROTOCOL_VERSION 70 /* 0x46 */ #define TLS_ALERT_MSG_INSUFFICIENT_SECURITY 71 /* 0x47 */ #define TLS_ALERT_MSG_INTERNAL_ERROR 80 /* 0x50 */ #define TLS_ALERT_MSG_USER_CANCELED 90 /* 0x5A */ #define TLS_ALERT_MSG_NO_RENEGOTIATION 100 /* 0x64 */ #define TLS_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ #define TLS_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */ #define TLS_ALERT_MSG_UNKNOWN_PSK_IDENTITY 115 /* 0x73 */ #define TLS_ALERT_MSG_NO_APPLICATION_PROTOCOL 120 /* 0x78 */ #define TLS_HS_HELLO_REQUEST 0 #define TLS_HS_CLIENT_HELLO 1 #define TLS_HS_SERVER_HELLO 2 #define TLS_HS_NEW_SESSION_TICKET 4 #define TLS_HS_CERTIFICATE 11 #define TLS_HS_SERVER_KEY_EXCHANGE 12 #define TLS_HS_CERTIFICATE_REQUEST 13 #define TLS_HS_SERVER_HELLO_DONE 14 #define TLS_HS_CERTIFICATE_VERIFY 15 #define TLS_HS_CLIENT_KEY_EXCHANGE 16 #define TLS_HS_FINISHED 20 /* * TLS extensions */ #define TLS_EXT_SERVERNAME 0 #define TLS_EXT_MAX_FRAGMENT_LENGTH 1 #define TLS_EXT_TRUNCATED_HMAC 4 #define TLS_EXT_SUPPORTED_ELLIPTIC_CURVES 10 #define TLS_EXT_SUPPORTED_POINT_FORMATS 11 #define TLS_EXT_SIG_ALG 13 #define TLS_EXT_ALPN 16 #define TLS_EXT_SESSION_TICKET 35 #define TLS_EXT_RENEGOTIATION_INFO 0xFF01 /* TLS extension flags (for extensions with outgoing ServerHello * content that need it (e.g. for RENEGOTIATION_INFO the server * already knows because of state of the renegotiation flag, so no * indicator is required). */ #define TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT (1 << 0) /* * Signaling ciphersuite values (SCSV) */ #define TLS_EMPTY_RENEGOTIATION_INFO 0xFF /* * The structure definitions are in a separate file. */ #include "context.h" /* * Inline functions etc. */ /* Return the private key object from the context object or NULL if there is none. */ static inline x509_privkey_t tls_own_key (ntbtls_t tls) { return tls->handshake->key_cert? tls->handshake->key_cert->key : NULL; } /* Return the certifciate key object from the context object or NULL if there is none. */ static inline x509_cert_t tls_own_cert (ntbtls_t tls) { return tls->handshake->key_cert? tls->handshake->key_cert->cert : NULL; } /* * Prototypes */ /*-- util.c --*/ const char *_ntbtls_check_version (const char *req_version); char *_ntbtls_trim_trailing_spaces (char *string); int _ntbtls_ascii_strcasecmp (const char *a, const char *b); /*-- protocol.c --*/ const char *_ntbtls_state2str (tls_state_t state); gpg_error_t _ntbtls_fetch_input (ntbtls_t tls, size_t nb_want); gpg_error_t _ntbtls_flush_output (ntbtls_t tls); gpg_error_t _ntbtls_write_record (ntbtls_t tls); gpg_error_t _ntbtls_read_record (ntbtls_t tls); gpg_error_t _ntbtls_send_fatal_handshake_failure (ntbtls_t tls); gpg_error_t _ntbtls_send_alert_message (ntbtls_t tls, unsigned char level, unsigned char message); pk_algo_t _ntbtls_pk_alg_from_sig (unsigned char sig); md_algo_t _ntbtls_md_alg_from_hash (unsigned char hash); gpg_error_t _ntbtls_derive_keys (ntbtls_t tls); void _ntbtls_optimize_checksum (ntbtls_t tls, const ciphersuite_t ciphersuite_info); gpg_error_t _ntbtls_psk_derive_premaster (ntbtls_t tls, key_exchange_type_t kex); gpg_error_t _ntbtls_write_certificate (ntbtls_t tls); gpg_error_t _ntbtls_read_certificate (ntbtls_t tls); gpg_error_t _ntbtls_write_change_cipher_spec (ntbtls_t tls); gpg_error_t _ntbtls_read_change_cipher_spec (ntbtls_t tls); gpg_error_t _ntbtls_write_finished (ntbtls_t tls); gpg_error_t _ntbtls_read_finished (ntbtls_t tls); void _ntbtls_handshake_wrapup (ntbtls_t tls); /* Functions directly used by the public API. */ gpg_error_t _ntbtls_new (ntbtls_t *r_tls, unsigned int flags); void _ntbtls_release (ntbtls_t tls); +const char *_ntbtls_get_last_alert (ntbtls_t tls, unsigned int *r_level, + unsigned int *r_type); gpg_error_t _ntbtls_set_transport (ntbtls_t tls, gpgrt_stream_t inbound, gpgrt_stream_t outbound); gpg_error_t _ntbtls_get_stream (ntbtls_t tls, gpgrt_stream_t *r_readfp, gpgrt_stream_t *r_writefp); gpg_error_t _ntbtls_set_verify_cb (ntbtls_t tls, ntbtls_verify_cb_t cb, void *cb_value); gpg_error_t _ntbtls_set_hostname (ntbtls_t tls, const char *hostname); const char *_ntbtls_get_hostname (ntbtls_t tls); gpg_error_t _ntbtls_handshake (ntbtls_t tls); /*-- protocol-srv.c --*/ gpg_error_t _ntbtls_handshake_server_step (ntbtls_t tls); /*-- protocol-cli.c --*/ gpg_error_t _ntbtls_handshake_client_step (ntbtls_t tls); /*-- pkglue.c --*/ gpg_error_t _ntbtls_pk_verify (x509_cert_t chain, pk_algo_t pk_alg, md_algo_t md_alg, const unsigned char *hash, size_t hashlen, const unsigned char *sig, size_t siglen); gpg_error_t _ntbtls_pk_encrypt (x509_cert_t chain, const unsigned char *input, size_t ilen, unsigned char *output, size_t *olen, size_t osize); /*-- x509.c --*/ /* *X509 Verify codes - FIXME: Replace them by ksba stuff. */ #define BADCERT_EXPIRED 0x01 /* The certificate validity has expired. */ #define BADCERT_REVOKED 0x02 /* The certificate has been revoked (is on a CRL). */ #define BADCERT_CN_MISMATCH 0x04 /* The certificate Common Name (CN) does not match with the expected CN. */ #define BADCERT_NOT_TRUSTED 0x08 /* The certificate is not correctly signed by the trusted CA. */ #define BADCRL_NOT_TRUSTED 0x10 /* CRL is not correctly signed by the trusted CA. */ #define BADCRL_EXPIRED 0x20 /* CRL is expired. */ #define BADCERT_MISSING 0x40 /* Certificate was missing. */ #define BADCERT_SKIP_VERIFY 0x80 /* Certificate verification was skipped. */ #define BADCERT_OTHER 0x0100 /* Other reason (can be used by verify callback) */ #define BADCERT_FUTURE 0x0200 /* The certificate validity starts in the future. */ #define BADCRL_FUTURE 0x0400 /* The CRL is from the future */ gpg_error_t _ntbtls_x509_cert_new (x509_cert_t *r_cert); void _ntbtls_x509_cert_release (x509_cert_t crt); gpg_error_t _ntbtls_x509_append_cert (x509_cert_t cert, const void *der, size_t derlen); void _ntbtls_x509_log_cert (const char *text, x509_cert_t chain, int full); const unsigned char *_ntbtls_x509_get_cert (x509_cert_t cert, int idx, size_t *r_derlen); ksba_cert_t _ntbtls_x509_get_peer_cert (ntbtls_t tls, int idx); gpg_error_t _ntbtls_x509_get_pk (x509_cert_t cert, int idx, gcry_sexp_t *r_pk); int _ntbtls_x509_can_do (x509_privkey_t privkey, pk_algo_t pkalgo); gpg_error_t _ntbtls_x509_check_hostname (x509_cert_t cert, const char *hostname); /*-- dhm.c --*/ gpg_error_t _ntbtls_dhm_new (dhm_context_t *r_dhm); void _ntbtls_dhm_release (dhm_context_t dhm); gpg_error_t _ntbtls_dhm_read_params (dhm_context_t dhm, const void *der, size_t derlen, size_t *r_nparsed); unsigned int _ntbtls_dhm_get_nbits (dhm_context_t dhm); gpg_error_t _ntbtls_dhm_make_public (dhm_context_t dhm, unsigned char *outbuf, size_t outbufsize, size_t *r_outbuflen); gpg_error_t _ntbtls_dhm_calc_secret (dhm_context_t dhm, unsigned char *outbuf, size_t outbufsize, size_t *r_outbuflen); /*-- ecdh.c --*/ gpg_error_t _ntbtls_ecdh_new (ecdh_context_t *r_ecdh); void _ntbtls_ecdh_release (ecdh_context_t ecdh); gpg_error_t _ntbtls_ecdh_read_params (ecdh_context_t ecdh, const void *der, size_t derlen, size_t *r_nparsed); gpg_error_t _ntbtls_ecdh_make_public (ecdh_context_t ecdh, unsigned char *outbuf, size_t outbufsize, size_t *r_outbuflen); gpg_error_t _ntbtls_ecdh_calc_secret (ecdh_context_t ecdh, unsigned char *outbuf, size_t outbufsize, size_t *r_outbuflen); #endif /*NTBTLS_NTBTLS_INT_H*/ diff --git a/src/ntbtls.h.in b/src/ntbtls.h.in index 899e8b2..254f817 100644 --- a/src/ntbtls.h.in +++ b/src/ntbtls.h.in @@ -1,161 +1,167 @@ /* ntbtls.h - Not Too Bad TLS -*- c -*- * Copyright (C) 2014-2017 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * File: @configure_input@ */ #ifndef _NTBTLS_H #define _NTBTLS_H #include #include #ifdef __cplusplus extern "C" { #if 0 /* (Keep Emacsens' auto-indent happy.) */ } #endif #endif /* * The version of this header file. * * A program should better the fucntion ntbtls_check_version() which * will return the actual version of the library. The sole purpose of * this macro is to let autoconf (using the AM_PATH_NTBTLS macro) * check that this header matches the installed library. */ #define NTBTLS_VERSION "@VERSION@" /* * The version number of this header. * * It may be used to handle minor API incompatibilities. */ #define NTBTLS_VERSION_NUMBER @VERSION_NUMBER@ /* Flags used by ntbtls_new. */ #define NTBTLS_SERVER 0 #define NTBTLS_CLIENT 1 #define NTBTLS_SAMETRHEAD (1<<4) /* The TLS context object. */ struct _ntbtls_context_s; typedef struct _ntbtls_context_s *ntbtls_t; /* * The type of the verification callback. * * This must be registered prior to the handshake and will be called * by ntbltls when a peer's certificate needs to be verified. OPAQUE * is the vale set when the callback has been set. TLS is the * respective TLS context. VERIFY_FLAGS are not yet defined flags. */ typedef gpg_error_t (*ntbtls_verify_cb_t) (void *opaque, ntbtls_t tls, unsigned int verify_flags); /* * The type of an optional log handler. * * OPAQUE is the value supplied to the set function. LEVEL is the * debug level for that message; it might be -1 for always log or any * value less than the limit set with ntbtls_set_debug. FMT is the * format string. Unless FMT starts with a '\b' the log function is * expected to append a missing final linefeed. */ typedef void (*ntbtls_log_handler_t)(void *opaque, int level, const char *fmt, va_list argv); /* Check that the library fulfills the version requirement. */ const char *ntbtls_check_version (const char *req_version); /* Create a new TLS context. */ gpg_error_t ntbtls_new (ntbtls_t *r_tls, unsigned int flags); /* Destroy a TLS context. */ void ntbtls_release (ntbtls_t tls); /* Check that TLS is not NULL and valid. (Use only the macro). */ gpg_error_t _ntbtls_check_context (ntbtls_t tls, const char *file, int line); #define ntbtls_check_context(t) _ntbtls_check_context ((t), __FILE__, __LINE__) /* Setup the transport streams (usually connected to one socket). */ gpg_error_t ntbtls_set_transport (ntbtls_t tls, gpgrt_stream_t inbound, gpgrt_stream_t outbound); /* Get the read and write stream for the plaintext. */ gpg_error_t ntbtls_get_stream (ntbtls_t tls, gpgrt_stream_t *r_readfp, gpgrt_stream_t *r_writefp); /* Set the data required to verify peer certificate. */ gpg_error_t ntbtls_set_verify_cb (ntbtls_t tls, ntbtls_verify_cb_t cb, void *cb_value); /* Set the hostname to check against the received server certificate. It is used for SNI, too. */ gpg_error_t ntbtls_set_hostname (ntbtls_t tls, const char *hostname); /* Return the hostname which has been set with ntbtls_set_hostname. * The returned value is valid as long as TLS is valid and * ntbtls_set_hostname has not been used again. */ const char *ntbtls_get_hostname (ntbtls_t tls); /* Perform the handshake with the peer. The transport streams must be connected before starting this handshake. */ gpg_error_t ntbtls_handshake (ntbtls_t tls); /* Return the peer's certificate. */ ksba_cert_t ntbtls_x509_get_peer_cert (ntbtls_t tls, int idx); +/* Return a string with the last received alert message. NULL is + * return if no alert has yet been received. If R_LEVEL or R_TYPE are + * not NULL the level of the alert and its type are stored tehre. A + * level of 1 is a warning alert and a level of 2 is fatal alert. */ +const char *ntbtls_get_last_alert (ntbtls_t tls, + unsigned int *r_level, unsigned int *r_type); /* * Support functions */ /* Enable debugging at LEVEL (> 0) using an optional PREFIX (default: * "ntbtls") and an optional debug stream STREAM (default: es_stderr). * This function is not thread-safe and shall thus be called only once * before any extra threads have been started. */ void ntbtls_set_debug (int level, const char *prefix, gpgrt_stream_t stream); /* Set a dedicated log handler. See the description of * ntbtls_log_handler_t for details. This is not thread-safe. */ void ntbtls_set_log_handler (ntbtls_log_handler_t cb, void *cb_value); #if 0 /* (Keep Emacsens' auto-indent happy.) */ { #endif #ifdef __cplusplus } #endif #endif /* _NTBTLS_H */ /* @emacs_local_vars_begin@ @emacs_local_vars_read_only@ @emacs_local_vars_end@ */ diff --git a/src/protocol.c b/src/protocol.c index 9499878..8ffaf70 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -1,4200 +1,4226 @@ /* protocol.c - TLS 1.2 protocol implementation * Copyright (C) 2006-2014, Brainspark B.V. * Copyright (C) 2014, 2017 g10 code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * This file was part of PolarSSL (http://www.polarssl.org). Former * Lead Maintainer: Paul Bakker . * Please do not file bug reports to them but to the address given in * the file AUTHORS in the top directory of NTBTLS. */ #include #include #include #include "ntbtls-int.h" #include "ciphersuites.h" static void transform_deinit (transform_t transform); static void session_deinit (session_t session); static void handshake_params_deinit (handshake_params_t handshake); static void ticket_keys_deinit (ticket_keys_t tkeys); static void update_checksum_sha256 (ntbtls_t, const unsigned char *, size_t); static void calc_verify_tls_sha256 (ntbtls_t, unsigned char *); static void calc_finished_tls_sha256 (ntbtls_t, unsigned char *, int); static void calc_verify_tls_sha384 (ntbtls_t, unsigned char *); static void calc_finished_tls_sha384 (ntbtls_t, unsigned char *, int); static const char * alert_msg_to_string (int msgno) { switch (msgno) { case TLS_ALERT_MSG_CLOSE_NOTIFY: return "close notify"; case TLS_ALERT_MSG_UNEXPECTED_MESSAGE: return "unexpected msg"; case TLS_ALERT_MSG_BAD_RECORD_MAC: return "bad record mac "; case TLS_ALERT_MSG_DECRYPTION_FAILED: return "decryption failed"; case TLS_ALERT_MSG_RECORD_OVERFLOW: return "record overflow"; case TLS_ALERT_MSG_DECOMPRESSION_FAILURE:return "decompression failure"; case TLS_ALERT_MSG_HANDSHAKE_FAILURE: return "handshake failure"; case TLS_ALERT_MSG_NO_CERT: return "no cert"; case TLS_ALERT_MSG_BAD_CERT: return "bad cert"; case TLS_ALERT_MSG_UNSUPPORTED_CERT: return "unsupported cert"; case TLS_ALERT_MSG_CERT_REVOKED: return "cert revoked"; case TLS_ALERT_MSG_CERT_EXPIRED: return "cert expired"; case TLS_ALERT_MSG_CERT_UNKNOWN: return "cert unknown"; case TLS_ALERT_MSG_ILLEGAL_PARAMETER: return "illegal param"; case TLS_ALERT_MSG_UNKNOWN_CA: return "unknown CA"; case TLS_ALERT_MSG_ACCESS_DENIED: return "access denied"; case TLS_ALERT_MSG_DECODE_ERROR: return "decode error"; case TLS_ALERT_MSG_DECRYPT_ERROR: return "decrypt error"; case TLS_ALERT_MSG_EXPORT_RESTRICTION: return "export restriction"; case TLS_ALERT_MSG_PROTOCOL_VERSION: return "protocol version"; case TLS_ALERT_MSG_INSUFFICIENT_SECURITY:return "insufficient security"; case TLS_ALERT_MSG_INTERNAL_ERROR: return "internal error"; case TLS_ALERT_MSG_USER_CANCELED: return "user canceled"; case TLS_ALERT_MSG_NO_RENEGOTIATION: return "no renegotiation"; case TLS_ALERT_MSG_UNSUPPORTED_EXT: return "unsupported extenstion"; case TLS_ALERT_MSG_UNRECOGNIZED_NAME: return "unsupported name"; case TLS_ALERT_MSG_UNKNOWN_PSK_IDENTITY: return "unknown PSK identify"; case TLS_ALERT_MSG_NO_APPLICATION_PROTOCOL:return "no application protocol"; default: return "[?]"; } } /* * Convert max_fragment_length codes to length. * RFC 6066 says: * enum{ * 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255) * } MaxFragmentLength; * and we add 0 -> extension unused */ static unsigned int mfl_code_to_length[] = { TLS_MAX_CONTENT_LEN, /* TLS_MAX_FRAG_LEN_NONE */ 512, /* TLS_MAX_FRAG_LEN_512 */ 1024, /* TLS_MAX_FRAG_LEN_1024 */ 2048, /* TLS_MAX_FRAG_LEN_2048 */ 4096 /* TLS_MAX_FRAG_LEN_4096 */ }; /* Return true is MODE is an AEAD mode. */ static int is_aead_mode (cipher_mode_t mode) { switch (mode) { case GCRY_CIPHER_MODE_GCM: case GCRY_CIPHER_MODE_CCM: return 1; default: return 0; } } const char * _ntbtls_state2str (tls_state_t state) { const char *s = "?"; switch (state) { case TLS_HELLO_REQUEST: s = "hello_request"; break; case TLS_CLIENT_HELLO: s = "client_hello"; break; case TLS_SERVER_HELLO: s = "server_hello"; break; case TLS_SERVER_CERTIFICATE: s = "server_certificate"; break; case TLS_SERVER_KEY_EXCHANGE: s = "server_key_exchange"; break; case TLS_CERTIFICATE_REQUEST: s = "certificate_request"; break; case TLS_SERVER_HELLO_DONE: s = "server_hello_done"; break; case TLS_CLIENT_CERTIFICATE: s = "client_certificate"; break; case TLS_CLIENT_KEY_EXCHANGE: s = "client_key_exchange"; break; case TLS_CERTIFICATE_VERIFY: s = "certificate_verify"; break; case TLS_CLIENT_CHANGE_CIPHER_SPEC: s = "client_change_cipher_spec"; break; case TLS_CLIENT_FINISHED: s = "client_finished"; break; case TLS_SERVER_CHANGE_CIPHER_SPEC: s = "server_change_cipher_spec"; break; case TLS_SERVER_FINISHED: s = "server_finished"; break; case TLS_FLUSH_BUFFERS: s = "flush_buffers"; break; case TLS_HANDSHAKE_WRAPUP: s = "handshake_wrapup"; break; case TLS_HANDSHAKE_OVER: s = "handshake_over"; break; case TLS_SERVER_NEW_SESSION_TICKET: s = "server_new_session_tickets"; break; } return s; } static gpg_error_t session_copy (session_t dst, const session_t src) { session_deinit (dst); memcpy (dst, src, sizeof *src); if (src->peer_chain) { /* int ret; */ //FIXME: Use libksba /* dst->peer_cert = malloc (sizeof *dst->peer_cert); */ /* if (!dst->peer_cert) */ /* return gpg_error_from_syserror (); */ /* x509_crt_init (dst->peer_cert); */ /* if ((ret = x509_crt_parse_der (dst->peer_cert, src->peer_cert->raw.p, */ /* src->peer_cert->raw.len)) != 0) */ /* { */ /* free (dst->peer_cert); */ /* dst->peer_cert = NULL; */ /* return (ret); */ /* } */ } if (src->ticket) { dst->ticket = malloc (src->ticket_len); if (!dst->ticket) return gpg_error_from_syserror (); memcpy (dst->ticket, src->ticket, src->ticket_len); } return 0; } /* * output = HMAC-SHA-NNN( hmac key, input buffer ) * * The used algorithm depends on OUTPUTSIZE which is expected in bytes. */ static gpg_error_t sha_hmac (const unsigned char *key, size_t keylen, const unsigned char *input, size_t inputlen, unsigned char *output, int outputsize) { gpg_error_t err; gcry_mac_hd_t hd; size_t macoutlen; int algo; switch (outputsize) { case 32: algo = GCRY_MAC_HMAC_SHA256; break; case 48: algo = GCRY_MAC_HMAC_SHA384; break; case 64: algo = GCRY_MAC_HMAC_SHA512; break; default: return gpg_error (GPG_ERR_MAC_ALGO); } err = gcry_mac_open (&hd, algo, 0, NULL); if (!err) { err = gcry_mac_setkey (hd, key, keylen); if (!err) { err = gcry_mac_write (hd, input, inputlen); if (!err) { macoutlen = outputsize; err = gcry_mac_read (hd, output, &macoutlen); } } gcry_mac_close (hd); } return err; } /* * Key material generation */ static gpg_error_t do_tls_prf (const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen, size_t hashlen) { gpg_error_t err; size_t nb; size_t i, j, k; unsigned char tmp[128]; unsigned char h_i[64]; if (sizeof (tmp) < hashlen + strlen (label) + rlen) return gpg_error (GPG_ERR_INV_ARG); nb = strlen (label); memcpy (tmp + hashlen, label, nb); memcpy (tmp + hashlen + nb, random, rlen); nb += rlen; /* * Compute P_(secret, label + random)[0..dlen] */ err = sha_hmac (secret, slen, tmp + hashlen, nb, tmp, hashlen); if (err) return err; for (i = 0; i < dlen; i += hashlen) { err = sha_hmac (secret, slen, tmp, hashlen + nb, h_i, hashlen); if (err) return err; err = sha_hmac (secret, slen, tmp, hashlen, tmp, hashlen); if (err) return err; k = (i + hashlen > dlen) ? dlen % hashlen : hashlen; for (j = 0; j < k; j++) dstbuf[i + j] = h_i[j]; } wipememory (tmp, sizeof (tmp)); wipememory (h_i, hashlen); return 0; } static gpg_error_t tls_prf_sha256 (const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen) { return do_tls_prf (secret, slen, label, random, rlen, dstbuf, dlen, 32); } static gpg_error_t tls_prf_sha384 (const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen) { return do_tls_prf (secret, slen, label, random, rlen, dstbuf, dlen, 48); } gpg_error_t _ntbtls_derive_keys (ntbtls_t tls) { gpg_error_t err; unsigned char tmp[64]; unsigned char keyblk[256]; unsigned char *key1; unsigned char *key2; unsigned char *mac_enc; unsigned char *mac_dec; size_t iv_copy_len; cipher_algo_t cipher; cipher_mode_t ciphermode; mac_algo_t mac; session_t session = tls->session_negotiate; transform_t transform = tls->transform_negotiate; handshake_params_t handshake = tls->handshake; debug_msg (2, "derive keys"); if (tls->minor_ver != TLS_MINOR_VERSION_3) { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } cipher = _ntbtls_ciphersuite_get_cipher (transform->ciphersuite, &ciphermode); if (!cipher || !ciphermode) { debug_msg (1, "cipher algo not found"); return gpg_error (GPG_ERR_INV_ARG); } mac = _ntbtls_ciphersuite_get_mac (transform->ciphersuite); if (!mac) { debug_msg (1, "mac algo not found"); return gpg_error (GPG_ERR_INV_ARG); } /* * Set appropriate PRF function and other TLS functions */ if (mac == GCRY_MAC_HMAC_SHA384) { handshake->tls_prf = tls_prf_sha384; handshake->calc_verify = calc_verify_tls_sha384; handshake->calc_finished = calc_finished_tls_sha384; } else { handshake->tls_prf = tls_prf_sha256; handshake->calc_verify = calc_verify_tls_sha256; handshake->calc_finished = calc_finished_tls_sha256; } /* * TLSv1+: * master = PRF( premaster, "master secret", randbytes )[0..47] */ if (!handshake->resume) { debug_buf (3, "premaster secret", handshake->premaster, handshake->pmslen); handshake->tls_prf (handshake->premaster, handshake->pmslen, "master secret", handshake->randbytes, 64, session->master, 48); wipememory (handshake->premaster, sizeof (handshake->premaster)); } else debug_msg (3, "no premaster (session resumed)"); /* * Swap the client and server random values. */ memcpy (tmp, handshake->randbytes, 64); memcpy (handshake->randbytes, tmp + 32, 32); memcpy (handshake->randbytes + 32, tmp, 32); wipememory (tmp, sizeof (tmp)); /* * TLSv1: * key block = PRF( master, "key expansion", randbytes ) */ handshake->tls_prf (session->master, 48, "key expansion", handshake->randbytes, 64, keyblk, 256); debug_msg (3, "ciphersuite = %s", _ntbtls_ciphersuite_get_name (session->ciphersuite)); debug_buf (3, "master secret", session->master, 48); debug_buf (4, "random bytes", handshake->randbytes, 64); debug_buf (4, "key block", keyblk, 256); wipememory (handshake->randbytes, sizeof (handshake->randbytes)); /* * Determine the appropriate key, IV and MAC length. */ transform->keylen = gcry_cipher_get_algo_keylen (cipher); /* FIXME: Check that KEYLEN has an upper bound. 2015-06-23 wk: Why? */ if (is_aead_mode (ciphermode)) { transform->maclen = 0; transform->ivlen = 12; transform->fixed_ivlen = 4; /* Minimum length is expicit IV + tag */ transform->minlen = (transform->ivlen - transform->fixed_ivlen + ((_ntbtls_ciphersuite_get_flags (transform->ciphersuite) & CIPHERSUITE_FLAG_SHORT_TAG)? 8 : 16)); } else { size_t blklen = gcry_cipher_get_algo_blklen (cipher); /* Initialize HMAC contexts */ /* Fixme: Check whether the context may really be open. */ gcry_mac_close (transform->mac_ctx_enc); err = gcry_mac_open (&transform->mac_ctx_enc, mac, 0, NULL); if (!err) { gcry_mac_close (transform->mac_ctx_dec); err = gcry_mac_open (&transform->mac_ctx_dec, mac, 0, NULL); } if (err) { debug_ret (1, "gcry_mac_open", err); return err; } /* Get MAC length */ transform->maclen = gcry_mac_get_algo_maclen (mac); if (transform->maclen < TLS_TRUNCATED_HMAC_LEN) { debug_bug (); return gpg_error (GPG_ERR_BUG); } /* * If HMAC is to be truncated, we shall keep the leftmost bytes, * (rfc 6066 page 13 or rfc 2104 section 4), * so we only need to adjust the length here. */ if (session->use_trunc_hmac) transform->maclen = TLS_TRUNCATED_HMAC_LEN; /* IV length. According to RFC-5246, Appendix C, we shall use the block length of the IV length. */ transform->ivlen = blklen; /* Minimum length for GenericBlockCipher: * First multiple of blocklen greater than maclen + IV. */ transform->minlen = (transform->maclen + blklen - (transform->maclen % blklen) + transform->ivlen); } debug_msg (3, "keylen: %d, minlen: %zu, ivlen: %zu, maclen: %zu", transform->keylen, transform->minlen, transform->ivlen, transform->maclen); /* * Finally setup the cipher contexts, IVs and MAC secrets. */ if (tls->is_client) { key1 = keyblk + transform->maclen * 2; key2 = keyblk + transform->maclen * 2 + transform->keylen; mac_enc = keyblk; mac_dec = keyblk + transform->maclen; /* * This is not used in TLS v1.1. FIXME: Check and remove. */ iv_copy_len = (transform->fixed_ivlen ? transform->fixed_ivlen : transform->ivlen); memcpy (transform->iv_enc, key2 + transform->keylen, iv_copy_len); memcpy (transform->iv_dec, key2 + transform->keylen + iv_copy_len, iv_copy_len); } else { key1 = keyblk + transform->maclen * 2 + transform->keylen; key2 = keyblk + transform->maclen * 2; mac_enc = keyblk + transform->maclen; mac_dec = keyblk; /* * This is not used in TLS v1.1. FIXME: Check and remove */ iv_copy_len = (transform->fixed_ivlen ? transform->fixed_ivlen : transform->ivlen); memcpy (transform->iv_dec, key1 + transform->keylen, iv_copy_len); memcpy (transform->iv_enc, key1 + transform->keylen + iv_copy_len, iv_copy_len); } if (!is_aead_mode (ciphermode)) { err = gcry_mac_setkey (transform->mac_ctx_enc, mac_enc, transform->maclen); if (!err) err = gcry_mac_setkey (transform->mac_ctx_dec, mac_dec, transform->maclen); if (err) { debug_ret (1, "gcry_mac_setkey", err); return err; } } gcry_cipher_close (transform->cipher_ctx_enc); err = gcry_cipher_open (&transform->cipher_ctx_enc, cipher, ciphermode, 0); if (!err) { gcry_cipher_close (transform->cipher_ctx_dec); err = gcry_cipher_open (&transform->cipher_ctx_dec, cipher, ciphermode,0); } if (err) { debug_ret (1, "gcry_cipher_open", err); return err; } transform->cipher_mode_enc = ciphermode; transform->cipher_mode_dec = ciphermode; err = gcry_cipher_setkey (transform->cipher_ctx_enc, key1, transform->keylen); if (!err) err = gcry_cipher_setkey (transform->cipher_ctx_dec, key2, transform->keylen); if (err) { debug_ret (1, "cipher_setkey", err); return err; } wipememory (keyblk, sizeof (keyblk)); /* Initialize compression. */ if (session->compression == TLS_COMPRESS_DEFLATE) { /* if (tls->compress_buf == NULL) */ /* { */ /* deboug_msg (3, "Allocating compression buffer"); */ /* ssl->compress_buf = malloc (SSL_BUFFER_LEN); */ /* if (!ssl->compress_buf) */ /* { */ /* err = gpg_error_from_syserror (); */ /* debug_msg (1, "malloc(%d bytes) failed", SSL_BUFFER_LEN); */ /* return err; */ /* } */ /* } */ /* debug_msg (3, "Initializing zlib states"); */ /* memset (&transform->ctx_deflate, 0, sizeof (transform->ctx_deflate));*/ /* memset (&transform->ctx_inflate, 0, sizeof (transform->ctx_inflate));*/ /* if (deflateInit (&transform->ctx_deflate, */ /* Z_DEFAULT_COMPRESSION) != Z_OK || */ /* inflateInit (&transform->ctx_inflate) != Z_OK) */ { debug_msg (1, "Failed to initialize compression"); return gpg_error (GPG_ERR_COMPR_FAILED); } } return 0; } static void calc_verify_tls (gcry_md_hd_t md_input, md_algo_t md_alg, unsigned char *hash, size_t hashlen) { gpg_error_t err; gcry_md_hd_t md; char *p; debug_msg (2, "calc_verify_tls sha%zu", hashlen*8); err = gcry_md_copy (&md, md_input); if (err) { debug_ret (1, "calc_verify_tls", err); memset (hash, 0, hashlen); return; } p = gcry_md_read (md, md_alg); if (!p) { debug_bug (); memset (hash, 0, hashlen); gcry_md_close (md); return; } memcpy (hash, p, hashlen); gcry_md_close (md); debug_buf (3, "calculated verify result", hash, hashlen); debug_msg (3, "calc_verify_tls sha%zu", hashlen*8); } static void calc_verify_tls_sha256 (ntbtls_t tls, unsigned char hash[32]) { calc_verify_tls (tls->handshake->fin_sha256, GCRY_MD_SHA256, hash, 32); } static void calc_verify_tls_sha384 (ntbtls_t tls, unsigned char hash[48]) { calc_verify_tls (tls->handshake->fin_sha512, GCRY_MD_SHA384, hash, 48); } gpg_error_t _ntbtls_psk_derive_premaster (ntbtls_t tls, key_exchange_type_t kex) { gpg_error_t err; unsigned char *p = tls->handshake->premaster; unsigned char *end = p + sizeof (tls->handshake->premaster); /* * PMS = struct { * opaque other_secret<0..2^16-1>; * opaque psk<0..2^16-1>; * }; * with "other_secret" depending on the particular key exchange */ if (kex == KEY_EXCHANGE_PSK) { if (end - p < 2 + (int) tls->psk_len) return gpg_error (GPG_ERR_INV_ARG); *(p++) = (unsigned char) (tls->psk_len >> 8); *(p++) = (unsigned char) (tls->psk_len); p += tls->psk_len; } else if (kex == KEY_EXCHANGE_RSA_PSK) { /* * other_secret already set by the ClientKeyExchange message, * and is 48 bytes long */ *p++ = 0; *p++ = 48; p += 48; } else if (kex == KEY_EXCHANGE_DHE_PSK) { size_t len = end - (p + 2); /* Write length only when we know the actual value. */ /* err = dhm_calc_secret (&tls->handshake->dhm_ctx, p + 2, &len); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "dhm_calc_secret", err); return err; } *(p++) = (unsigned char) (len >> 8); *(p++) = (unsigned char) (len); p += len; /* SSL_DEBUG_MPI (3, "DHM: K ", &tls->handshake->dhm_ctx.K); */ } else if (kex == KEY_EXCHANGE_ECDHE_PSK) { size_t zlen; /* err = ecdh_calc_secret (&tls->handshake->ecdh_ctx, &zlen, */ /* p + 2, end - (p + 2)); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "ecdh_calc_secret", err); return err; } *(p++) = (unsigned char) (zlen >> 8); *(p++) = (unsigned char) (zlen); p += zlen; /* SSL_DEBUG_MPI (3, "ECDH: z", &tls->handshake->ecdh_ctx.z); */ } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } /* opaque psk<0..2^16-1>; */ if (end - p < 2 + (int) tls->psk_len) return gpg_error (GPG_ERR_INV_ARG); *(p++) = (unsigned char) (tls->psk_len >> 8); *(p++) = (unsigned char) (tls->psk_len); memcpy (p, tls->psk, tls->psk_len); p += tls->psk_len; tls->handshake->pmslen = p - tls->handshake->premaster; return 0; } /* * Encryption/decryption functions */ static gpg_error_t encrypt_buf (ntbtls_t tls) { gpg_error_t err; size_t tmplen, i; cipher_mode_t mode = tls->transform_out->cipher_mode_enc; debug_msg (2, "encrypt buf"); if (tls->minor_ver < TLS_MINOR_VERSION_3) { debug_bug (); return gpg_error (GPG_ERR_BUG); } /* * Add MAC before encrypt, except for AEAD modes */ if (!is_aead_mode (mode)) { err = gcry_mac_write (tls->transform_out->mac_ctx_enc, tls->out_ctr, 13); if (!err) err = gcry_mac_write (tls->transform_out->mac_ctx_enc, tls->out_msg, tls->out_msglen); tmplen = tls->transform_out->maclen; if (!err) err = gcry_mac_read (tls->transform_out->mac_ctx_enc, tls->out_msg + tls->out_msglen, &tmplen); if (!err) err = gcry_mac_reset (tls->transform_out->mac_ctx_enc); if (err) { debug_ret (1, "encrypt_buf: MACing failed", err); return err; } debug_buf (4, "computed mac", tls->out_msg + tls->out_msglen, tls->transform_out->maclen); tls->out_msglen += tls->transform_out->maclen; } /* * Encrypt */ if (is_aead_mode (mode)) { size_t enc_msglen; unsigned char *enc_msg; unsigned char add_data[13]; unsigned char taglen; unsigned char iv[12]; taglen = (_ntbtls_ciphersuite_get_flags (tls->transform_out->ciphersuite) & CIPHERSUITE_FLAG_SHORT_TAG)? 8 : 16; memcpy (add_data, tls->out_ctr, 8); add_data[8] = tls->out_msgtype; add_data[9] = tls->major_ver; add_data[10] = tls->minor_ver; add_data[11] = (tls->out_msglen >> 8) & 0xFF; add_data[12] = tls->out_msglen & 0xFF; debug_buf (4, "additional data used for AEAD", add_data, 13); /* * Generate IV */ memcpy (iv, tls->transform_out->iv_enc, tls->transform_out->fixed_ivlen); memcpy (iv + tls->transform_out->fixed_ivlen, tls->out_ctr, 8); memcpy (tls->out_iv, tls->out_ctr, 8); debug_buf (4, "IV used (internal)", iv, tls->transform_out->ivlen); debug_buf (4, "IV used (transmitted)", tls->out_iv, tls->transform_out->ivlen - tls->transform_out->fixed_ivlen); /* * Fix pointer positions and message length with added IV */ enc_msg = tls->out_msg; enc_msglen = tls->out_msglen; tls->out_msglen += (tls->transform_out->ivlen - tls->transform_out->fixed_ivlen); debug_msg (3, "before encrypt: msglen = %zu, " "including %d bytes of padding", enc_msglen, 0); debug_buf (4, "before encrypt: output payload", tls->out_msg, enc_msglen); err = gcry_cipher_reset (tls->transform_out->cipher_ctx_enc); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_out->cipher_ctx_enc, iv, tls->transform_out->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } /* * Encrypt and authenticate */ err = gcry_cipher_authenticate (tls->transform_out->cipher_ctx_enc, add_data, 13); if (err) { debug_ret (1, "cipher_authenticate", err); return err; } err = gcry_cipher_encrypt (tls->transform_out->cipher_ctx_enc, enc_msg, enc_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_encrypt", err); return err; } err = gcry_cipher_gettag (tls->transform_out->cipher_ctx_enc, enc_msg + enc_msglen, taglen); if (err) { debug_ret (1, "cipher_gettag", err); return err; } tls->out_msglen += taglen; debug_buf (4, "after encrypt: payload", enc_msg, enc_msglen); debug_buf (4, "after encrypt: tag", enc_msg + enc_msglen, taglen); } else if (mode == GCRY_CIPHER_MODE_CBC) { unsigned char *enc_msg; size_t enc_msglen, padlen; padlen = (tls->transform_out->ivlen - ((tls->out_msglen + 1) % tls->transform_out->ivlen)); if (padlen == tls->transform_out->ivlen) padlen = 0; for (i = 0; i <= padlen; i++) tls->out_msg[tls->out_msglen + i] = (unsigned char) padlen; tls->out_msglen += padlen + 1; enc_msglen = tls->out_msglen; enc_msg = tls->out_msg; /* * Prepend per-record IV for block cipher in TLS v1.1 and up as per * Method 1 (RFC-5246, 6.2.3.2) */ /* Generate IV. */ gcry_create_nonce (tls->transform_out->iv_enc, tls->transform_out->ivlen); memcpy (tls->out_iv, tls->transform_out->iv_enc, tls->transform_out->ivlen); /* Fix pointer positions and message length with added IV. */ enc_msg = tls->out_msg; enc_msglen = tls->out_msglen; tls->out_msglen += tls->transform_out->ivlen; debug_msg (3, "before encrypt: msglen = %zu, " "including %zu bytes of IV and %zu bytes of padding", tls->out_msglen, tls->transform_out->ivlen, padlen + 1); debug_buf (4, "before encrypt: output payload", tls->out_iv, tls->out_msglen); err = gcry_cipher_reset (tls->transform_out->cipher_ctx_enc); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_out->cipher_ctx_enc, tls->transform_out->iv_enc, tls->transform_out->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } err = gcry_cipher_encrypt (tls->transform_out->cipher_ctx_enc, enc_msg, enc_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_encrypt", err); return err; } } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } for (i = 8; i > 0; i--) if (++tls->out_ctr[i - 1] != 0) break; /* The loops goes to its end iff the counter is wrapping */ if (!i) { debug_msg (1, "outgoing message counter would wrap"); return gpg_error (GPG_ERR_WOULD_WRAP); } return 0; } static int decrypt_buf (ntbtls_t tls) { gpg_error_t err; cipher_mode_t mode = tls->transform_out->cipher_mode_dec; size_t padlen = 0; size_t correct = 1; size_t tmplen, i; debug_msg (2, "decrypt buf"); if (tls->minor_ver < TLS_MINOR_VERSION_3) { debug_bug (); return gpg_error (GPG_ERR_BUG); } if (tls->in_msglen < tls->transform_in->minlen) { debug_msg (1, "in_msglen (%zu) < minlen (%zu)", tls->in_msglen, tls->transform_in->minlen); return gpg_error (GPG_ERR_INV_MAC); } if (is_aead_mode (mode)) { size_t dec_msglen; unsigned char *dec_msg; unsigned char add_data[13]; unsigned char taglen, explicit_iv_len; unsigned char iv[12]; taglen = (_ntbtls_ciphersuite_get_flags (tls->transform_in->ciphersuite) & CIPHERSUITE_FLAG_SHORT_TAG)? 8 : 16; explicit_iv_len = (tls->transform_in->ivlen - tls->transform_in->fixed_ivlen); if (tls->in_msglen < explicit_iv_len + taglen) { debug_msg (1, "msglen (%zud) < explicit_iv_len (%d) " "+ taglen (%d)", tls->in_msglen, explicit_iv_len, taglen); return gpg_error (GPG_ERR_INV_MAC); } dec_msglen = tls->in_msglen - explicit_iv_len - taglen; dec_msg = tls->in_msg; tls->in_msglen = dec_msglen; memcpy (add_data, tls->in_ctr, 8); add_data[8] = tls->in_msgtype; add_data[9] = tls->major_ver; add_data[10] = tls->minor_ver; add_data[11] = (tls->in_msglen >> 8) & 0xFF; add_data[12] = tls->in_msglen & 0xFF; debug_buf (4, "additional data used for AEAD", add_data, 13); memcpy (iv, tls->transform_in->iv_dec, tls->transform_in->fixed_ivlen); memcpy (iv + tls->transform_in->fixed_ivlen, tls->in_iv, 8); debug_buf (4, "IV used", iv, 12); debug_buf (4, "TAG used", dec_msg + dec_msglen, taglen); /* * Decrypt and authenticate */ err = gcry_cipher_reset (tls->transform_in->cipher_ctx_dec); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_in->cipher_ctx_dec, iv, tls->transform_in->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } err = gcry_cipher_authenticate (tls->transform_in->cipher_ctx_dec, add_data, 13); if (err) { debug_ret (1, "cipher_authenticate", err); return err; } err = gcry_cipher_decrypt (tls->transform_in->cipher_ctx_dec, dec_msg, dec_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_decrypt", err); return err; } err = gcry_cipher_checktag (tls->transform_in->cipher_ctx_dec, dec_msg + dec_msglen, taglen); if (err) { debug_ret (1, "cipher_checktag", err); return err; } } else if (mode == GCRY_CIPHER_MODE_CBC) { /* * Decrypt and check the padding */ unsigned char *dec_msg; size_t pad_count, real_count, padding_idx; size_t dec_msglen; size_t minlen = 0; /* * Check immediate ciphertext sanity */ if ((tls->in_msglen % tls->transform_in->ivlen)) { debug_msg (1, "msglen (%zu) %% ivlen (%zu) != 0", tls->in_msglen, tls->transform_in->ivlen); return gpg_error (GPG_ERR_INV_MAC); } minlen += tls->transform_in->ivlen; if (tls->in_msglen < minlen + tls->transform_in->ivlen || tls->in_msglen < minlen + tls->transform_in->maclen + 1) { debug_msg (1, "msglen (%zu) < max( ivlen(%zu), maclen (%zu) " "+ 1 ) ( + expl IV )", tls->in_msglen, tls->transform_in->ivlen, tls->transform_in->maclen); return gpg_error (GPG_ERR_INV_MAC); } dec_msglen = tls->in_msglen; dec_msg = tls->in_msg; /* * Initialize for prepended IV. */ dec_msglen -= tls->transform_in->ivlen; tls->in_msglen -= tls->transform_in->ivlen; for (i = 0; i < tls->transform_in->ivlen; i++) tls->transform_in->iv_dec[i] = tls->in_iv[i]; err = gcry_cipher_reset (tls->transform_out->cipher_ctx_dec); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_out->cipher_ctx_dec, tls->transform_out->iv_dec, tls->transform_out->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } err = gcry_cipher_decrypt (tls->transform_out->cipher_ctx_dec, dec_msg, dec_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_decrypt", err); return err; } padlen = 1 + tls->in_msg[tls->in_msglen - 1]; if (tls->in_msglen < tls->transform_in->maclen + padlen) { debug_msg (1, "msglen (%zu) < maclen (%zu) + padlen (%zu)", tls->in_msglen, tls->transform_in->maclen, padlen); padlen = 0; correct = 0; } /* * Always check the padding up to the first failure and fake * check up to 256 bytes of padding */ pad_count = 0; real_count = 1; padding_idx = tls->in_msglen - padlen - 1; /* * Padding is guaranteed to be incorrect if: * 1. padlen >= tls->in_msglen * * 2. padding_idx >= TLS_MAX_CONTENT_LEN + * tls->transform_in->maclen * * In both cases we reset padding_idx to a safe value (0) to * prevent out-of-buffer reads. */ correct &= (tls->in_msglen >= padlen + 1); correct &= (padding_idx < TLS_MAX_CONTENT_LEN + tls->transform_in->maclen); padding_idx *= correct; for (i = 1; i <= 256; i++) { real_count &= (i <= padlen); pad_count += real_count * (tls->in_msg[padding_idx + i] == padlen-1); } correct &= (pad_count == padlen); /* Only 1 on correct padding */ if (padlen > 0 && !correct) debug_msg (1, "bad padding byte detected"); padlen &= correct * 0x1FF; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } debug_buf (4, "raw buffer after decryption", tls->in_msg, tls->in_msglen); /* * Always compute the MAC (RFC4346, CBCTIME), except for AEAD of course */ if (!is_aead_mode (mode)) { unsigned char tmp[TLS_MAX_MAC_SIZE]; size_t extra_run; tls->in_msglen -= (tls->transform_in->maclen + padlen); tls->in_hdr[3] = (unsigned char) (tls->in_msglen >> 8); tls->in_hdr[4] = (unsigned char) (tls->in_msglen); memcpy (tmp, tls->in_msg + tls->in_msglen, tls->transform_in->maclen); /* * Process MAC and always update for padlen afterwards to make * total time independent of padlen * * extra_run compensates MAC check for padlen * * Known timing attacks: * - Lucky Thirteen (http://www.isg.rhul.ac.uk/tls/TLStiming.pdf) * * We use ( ( Lx + 8 ) / 64 ) to handle 'negative Lx' values * correctly. (We round down instead of up, so -56 is the correct * value for our calculations instead of -55). * * Fixme: Get the transform block size from Libgcrypt instead of * assuming 64. */ extra_run = ((13 + tls->in_msglen + padlen + 8) / 64 - (13 + tls->in_msglen + 8) / 64); extra_run &= correct * 0xFF; err = gcry_mac_write (tls->transform_in->mac_ctx_dec, tls->in_ctr, 13); if (!err) err = gcry_mac_write (tls->transform_in->mac_ctx_dec, tls->in_msg, tls->in_msglen); tmplen = tls->transform_in->maclen; if (!err) err = gcry_mac_read (tls->transform_in->mac_ctx_dec, tls->in_msg + tls->in_msglen, &tmplen); /* Keep on hashing dummy blocks if needed. gcry_mac_write explictly declares this as a valid modus operandi. */ if (!err && extra_run) { int j; for (j = 0; j < extra_run && !err; j++) err = gcry_mac_write (tls->transform_in->mac_ctx_dec, tls->in_msg, 64); if (!err) err = gcry_mac_write (tls->transform_in->mac_ctx_dec, NULL, 0); } if (!err) err = gcry_mac_reset (tls->transform_in->mac_ctx_dec); if (err) { /* Note that such an error is due to a bug in the code, a missing algorithm, or an out of core case. It is highly unlikely that a side channel attack can be constructed based on such an error. In any case, with failing MAC functions we are anyway not able to guarantee a constant time behavior. */ debug_ret (1, "decrypt_buf: MACing failed", err); return err; } debug_buf (4, "message mac", tmp, tls->transform_in->maclen); debug_buf (4, "computed mac", tls->in_msg + tls->in_msglen, tls->transform_in->maclen); if (memcmpct (tmp, tls->in_msg + tls->in_msglen, tls->transform_in->maclen)) { debug_msg (1, "message mac does not match"); correct = 0; } /* * Finally check the correct flag */ if (!correct) return gpg_error (GPG_ERR_BAD_MAC); } if (!tls->in_msglen) { tls->nb_zero++; /* * Three or more empty messages may be a DoS attack * (excessive CPU consumption). */ if (tls->nb_zero > 3) { debug_msg (1, "received four consecutive empty " "messages, possible DoS attack"); return gpg_error (GPG_ERR_INV_MAC); } } else tls->nb_zero = 0; for (i = 8; i > 0; i--) if (++tls->in_ctr[i - 1] != 0) break; /* The loops goes to its end iff the counter is wrapping */ if (!i) { debug_msg (1, "incoming message counter would wrap"); return gpg_error (GPG_ERR_WOULD_WRAP); } return 0; } /* * Compression/decompression functions */ static int ssl_compress_buf (ntbtls_t ssl) { int ret; unsigned char *msg_post = ssl->out_msg; size_t len_pre = ssl->out_msglen; unsigned char *msg_pre = ssl->compress_buf; debug_msg (2, "compress buf"); if (len_pre == 0) return (0); memcpy (msg_pre, ssl->out_msg, len_pre); debug_msg (3, "before compression: msglen = %zu, ", ssl->out_msglen); debug_buf (4, "before compression: output payload", ssl->out_msg, ssl->out_msglen); ssl->transform_out->ctx_deflate.next_in = msg_pre; ssl->transform_out->ctx_deflate.avail_in = len_pre; ssl->transform_out->ctx_deflate.next_out = msg_post; ssl->transform_out->ctx_deflate.avail_out = TLS_BUFFER_LEN; /* ret = deflate (&ssl->transform_out->ctx_deflate, Z_SYNC_FLUSH); */ ret = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (ret != Z_OK) { debug_msg (1, "failed to perform compression (%d)", ret); return gpg_error (GPG_ERR_COMPR_FAILED); } ssl->out_msglen = (TLS_BUFFER_LEN - ssl->transform_out->ctx_deflate.avail_out); debug_msg (3, "after compression: msglen = %zu, ", ssl->out_msglen); debug_buf (4, "after compression: output payload", ssl->out_msg, ssl->out_msglen); return (0); } static int ssl_decompress_buf (ntbtls_t ssl) { int ret; unsigned char *msg_post = ssl->in_msg; size_t len_pre = ssl->in_msglen; unsigned char *msg_pre = ssl->compress_buf; debug_msg (2, "decompress buf"); if (len_pre == 0) return (0); memcpy (msg_pre, ssl->in_msg, len_pre); debug_msg (3, "before decompression: msglen = %zu, ", ssl->in_msglen); debug_buf (4, "before decompression: input payload", ssl->in_msg, ssl->in_msglen); ssl->transform_in->ctx_inflate.next_in = msg_pre; ssl->transform_in->ctx_inflate.avail_in = len_pre; ssl->transform_in->ctx_inflate.next_out = msg_post; ssl->transform_in->ctx_inflate.avail_out = TLS_MAX_CONTENT_LEN; /* ret = inflate (&ssl->transform_in->ctx_inflate, Z_SYNC_FLUSH); */ ret = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (ret != Z_OK) { debug_msg (1, "failed to perform decompression (%d)", ret); return gpg_error (GPG_ERR_COMPR_FAILED); } ssl->in_msglen = (TLS_MAX_CONTENT_LEN - ssl->transform_in->ctx_inflate.avail_out); debug_msg (3, "after decompression: msglen = %zu, ", ssl->in_msglen); debug_buf (4, "after decompression: input payload", ssl->in_msg, ssl->in_msglen); return (0); } /* Fill the input message buffer with NB_WANT bytes. The function * returns an error if the numer of requested bytes do not fit into * the record buffer, there is a read problem, or on EOF. */ gpg_error_t _ntbtls_fetch_input (ntbtls_t tls, size_t nb_want) { gpg_error_t err; size_t len, nread; debug_msg (3, "fetch input"); if (!tls->inbound) return gpg_error (GPG_ERR_NOT_INITIALIZED); if (nb_want > TLS_BUFFER_LEN - 8) { debug_msg (1, "requesting more data than fits"); return gpg_error (GPG_ERR_REQUEST_TOO_LONG); } err = 0; while (tls->in_left < nb_want) { len = nb_want - tls->in_left; if (es_read (tls->inbound, tls->in_hdr + tls->in_left, len, &nread)) err = gpg_error_from_syserror (); else if (!nread) /*ie. EOF*/ err = gpg_error (GPG_ERR_EOF); debug_msg (3, "in_left: %zu, nb_want: %zu", tls->in_left, nb_want); debug_ret (3, "es_read", err); if (err) break; tls->in_left += nread; } return err; } /* * Flush any data not yet written */ gpg_error_t _ntbtls_flush_output (ntbtls_t tls) { gpg_error_t err; unsigned char *buf; size_t nwritten; debug_msg (3, "flush output"); if (!tls->outbound) return gpg_error (GPG_ERR_NOT_INITIALIZED); err = 0; while (tls->out_left > 0) { debug_msg (3, "message length: %zu, out_left: %zu", 5 + tls->out_msglen, tls->out_left); buf = tls->out_hdr + 5 + tls->out_msglen - tls->out_left; if (es_write (tls->outbound, buf, tls->out_left, &nwritten)) err = gpg_error_from_syserror (); debug_ret (3, "es_write", err); if (err) break; tls->out_left -= nwritten; } return err; } /* * Record layer functions */ gpg_error_t _ntbtls_write_record (ntbtls_t tls) { gpg_error_t err; int done = 0; size_t len = tls->out_msglen; debug_msg (3, "write record"); if (tls->out_msgtype == TLS_MSG_HANDSHAKE) { tls->out_msg[1] = (unsigned char) ((len - 4) >> 16); tls->out_msg[2] = (unsigned char) ((len - 4) >> 8); tls->out_msg[3] = (unsigned char) ((len - 4)); if (tls->out_msg[0] != TLS_HS_HELLO_REQUEST) tls->handshake->update_checksum (tls, tls->out_msg, len); } if (tls->transform_out && tls->session_out->compression == TLS_COMPRESS_DEFLATE) { err = ssl_compress_buf (tls); if (err) { debug_ret (1, "ssl_compress_buf", err); return err; } len = tls->out_msglen; } if (!done) { tls->out_hdr[0] = (unsigned char) tls->out_msgtype; tls->out_hdr[1] = (unsigned char) tls->major_ver; tls->out_hdr[2] = (unsigned char) tls->minor_ver; tls->out_hdr[3] = (unsigned char) (len >> 8); tls->out_hdr[4] = (unsigned char) (len); if (tls->transform_out) { err = encrypt_buf (tls); if (err) { debug_ret (1, "encrypt_buf", err); return err; } len = tls->out_msglen; tls->out_hdr[3] = (unsigned char) (len >> 8); tls->out_hdr[4] = (unsigned char) (len); } tls->out_left = 5 + tls->out_msglen; debug_msg (3, "output record: msgtype = %d, " "version = [%d:%d], msglen = %u", tls->out_hdr[0], tls->out_hdr[1], tls->out_hdr[2], buf16_to_uint (tls->out_hdr + 3)); debug_buf (4, "output record sent to network", tls->out_hdr, 5 + tls->out_msglen); } err = _ntbtls_flush_output (tls); if (err) debug_ret (1, "_ntbtls_flush_output", err); return err; } gpg_error_t _ntbtls_read_record (ntbtls_t tls) { gpg_error_t err; int done = 0; debug_msg (3, "read record"); if (tls->in_hslen != 0 && tls->in_hslen < tls->in_msglen) { /* * Get next Handshake message in the current record */ tls->in_msglen -= tls->in_hslen; memmove (tls->in_msg, tls->in_msg + tls->in_hslen, tls->in_msglen); tls->in_hslen = 4; tls->in_hslen += buf16_to_size_t (tls->in_msg + 2); debug_msg (3, "handshake message: msglen =" " %zu, type = %u, hslen = %zu", tls->in_msglen, tls->in_msg[0], tls->in_hslen); if (tls->in_msglen < 4 || tls->in_msg[1] != 0) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->in_msglen < tls->in_hslen) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->state != TLS_HANDSHAKE_OVER) tls->handshake->update_checksum (tls, tls->in_msg, tls->in_hslen); return 0; } tls->in_hslen = 0; read_record_header: /* * Read the record header and validate it */ err = _ntbtls_fetch_input (tls, 5); if (err) { debug_ret (1, "fetch_input", err); return err; } //FIXME: Handle EOF tls->in_msgtype = tls->in_hdr[0]; tls->in_msglen = buf16_to_size_t (tls->in_hdr + 3); debug_msg (3, "input record: msgtype = %d, " "version = [%d:%d], msglen = %u", tls->in_hdr[0], tls->in_hdr[1], tls->in_hdr[2], buf16_to_uint (tls->in_hdr + 3)); if (tls->in_hdr[1] != tls->major_ver) { debug_msg (1, "major version mismatch"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->in_hdr[2] > tls->max_minor_ver) { debug_msg (1, "minor version mismatch"); return gpg_error (GPG_ERR_INV_RECORD); } /* Sanity check (outer boundaries) */ if (tls->in_msglen < 1 || tls->in_msglen > TLS_BUFFER_LEN - 13) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } /* * Make sure the message length is acceptable for the current transform * and protocol version. */ if (!tls->transform_in) { if (tls->in_msglen > TLS_MAX_CONTENT_LEN) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } } else { if (tls->in_msglen < tls->transform_in->minlen) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } /* * TLS encrypted messages can have up to 256 bytes of padding */ if (tls->minor_ver >= TLS_MINOR_VERSION_1 && tls->in_msglen > (tls->transform_in->minlen + TLS_MAX_CONTENT_LEN + 256)) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } } /* * Read and optionally decrypt the message contents */ err = _ntbtls_fetch_input (tls, 5 + tls->in_msglen); if (err) { debug_ret (1, "fetch_input", err); return err; } //FIXME: Handle EOF debug_buf (4, "input record from network", tls->in_hdr, 5 + tls->in_msglen); if (!done && tls->transform_in) { err = decrypt_buf (tls); if (err) { if (gpg_err_code (err) == GPG_ERR_INV_MAC || gpg_err_code (err) == GPG_ERR_BAD_MAC || gpg_err_code (err) == GPG_ERR_CHECKSUM) { _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_BAD_RECORD_MAC); } debug_ret (1, "decrypt_buf", err); return err; } debug_buf (4, "input payload after decrypt", tls->in_msg, tls->in_msglen); if (tls->in_msglen > TLS_MAX_CONTENT_LEN) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } } if (tls->transform_in && tls->session_in->compression == TLS_COMPRESS_DEFLATE) { err = ssl_decompress_buf (tls); if (err) { debug_ret (1, "decompress_buf", err); return err; } tls->in_hdr[3] = (unsigned char) (tls->in_msglen >> 8); tls->in_hdr[4] = (unsigned char) (tls->in_msglen); } if ( tls->in_msgtype != TLS_MSG_HANDSHAKE && tls->in_msgtype != TLS_MSG_ALERT && tls->in_msgtype != TLS_MSG_CHANGE_CIPHER_SPEC && tls->in_msgtype != TLS_MSG_APPLICATION_DATA) { debug_msg (1, "unknown record type"); err = _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_UNEXPECTED_MESSAGE); if (!err) err = gpg_error (GPG_ERR_INV_RECORD); return err; } if (tls->in_msgtype == TLS_MSG_HANDSHAKE) { tls->in_hslen = 4; tls->in_hslen += buf16_to_size_t (tls->in_msg + 2); debug_msg (3, "handshake message: msglen =" " %zu, type = %u, hslen = %zu", tls->in_msglen, tls->in_msg[0], tls->in_hslen); /* * Additional checks to validate the handshake header */ if (tls->in_msglen < 4 || tls->in_msg[1] != 0) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->in_msglen < tls->in_hslen) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->state != TLS_HANDSHAKE_OVER) tls->handshake->update_checksum (tls, tls->in_msg, tls->in_hslen); } if (tls->in_msgtype == TLS_MSG_ALERT) { + tls->last_alert.any = 1; + tls->last_alert.level = tls->in_msg[0]; + tls->last_alert.type = tls->in_msg[1]; + if (tls->in_msg[0] == TLS_ALERT_LEVEL_FATAL) debug_msg (1, "got fatal alert message %d: %s", tls->in_msg[1], alert_msg_to_string (tls->in_msg[1])); else if (tls->in_msg[0] == TLS_ALERT_LEVEL_WARNING) debug_msg (2, "got warning alert message %d: %s", tls->in_msg[1], alert_msg_to_string (tls->in_msg[1])); else debug_msg (2, "got alert message of unknown level %d type %d: %s", tls->in_msg[0], tls->in_msg[1], alert_msg_to_string (tls->in_msg[1])); /* * Ignore non-fatal alerts, except close_notify */ if (tls->in_msg[0] == TLS_ALERT_LEVEL_FATAL) { return gpg_error (GPG_ERR_FATAL_ALERT); } if (tls->in_msg[0] == TLS_ALERT_LEVEL_WARNING && tls->in_msg[1] == TLS_ALERT_MSG_CLOSE_NOTIFY) { return gpg_error (GPG_ERR_CLOSE_NOTIFY); } tls->in_left = 0; goto read_record_header; } tls->in_left = 0; return (0); } gpg_error_t _ntbtls_send_fatal_handshake_failure (ntbtls_t tls) { return _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_HANDSHAKE_FAILURE); } gpg_error_t _ntbtls_send_alert_message (ntbtls_t tls, unsigned char level, unsigned char message) { gpg_error_t err; debug_msg (2, "send alert message"); tls->out_msgtype = TLS_MSG_ALERT; tls->out_msglen = 2; tls->out_msg[0] = level; tls->out_msg[1] = message; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } /* * Handshake functions */ gpg_error_t _ntbtls_write_certificate (ntbtls_t tls) { gpg_error_t err; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); x509_cert_t cert; int idx; const unsigned char *der; size_t derlen; size_t i; if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK || (tls->is_client && !tls->client_auth)) { debug_msg (2, "skipping write certificate"); tls->state++; return 0; } debug_msg (2, "write certificate"); if (!tls->is_client && !tls_own_cert (tls)) { debug_msg (1, "got no certificate to send"); return gpg_error (GPG_ERR_MISSING_CERT); } /* SSL_DEBUG_CRT (3, "own certificate", tls_own_cert (tls)); */ /* * 0 . 0 handshake type * 1 . 3 handshake length * 4 . 6 length of all certs * 7 . 9 length of cert. 1 * 10 . n-1 peer certificate * n . n+2 length of cert. 2 * n+3 . ... upper level cert, etc. */ i = 7; cert = tls_own_cert (tls); for (idx = 0; (der = _ntbtls_x509_get_cert (cert, idx, &derlen)); idx++) { if (derlen > TLS_MAX_CONTENT_LEN - 3 - i) { debug_msg (1, "certificate too large, %zu > %d", i + 3 + derlen, TLS_MAX_CONTENT_LEN); return gpg_error (GPG_ERR_CERT_TOO_LARGE); } tls->out_msg[i] = (unsigned char) (derlen >> 16); tls->out_msg[i + 1] = (unsigned char) (derlen >> 8); tls->out_msg[i + 2] = (unsigned char) (derlen); i += 3; memcpy (tls->out_msg + i, der, derlen); i += derlen; } tls->out_msg[4] = (unsigned char) ((i - 7) >> 16); tls->out_msg[5] = (unsigned char) ((i - 7) >> 8); tls->out_msg[6] = (unsigned char) ((i - 7)); tls->out_msglen = i; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CERTIFICATE; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return err; } gpg_error_t _ntbtls_read_certificate (ntbtls_t tls) { gpg_error_t err; size_t i, n; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { debug_msg (2, "skipping read certificate"); tls->state++; return 0; } if (!tls->is_client && (tls->authmode == TLS_VERIFY_NONE || kex == KEY_EXCHANGE_RSA_PSK)) { tls->session_negotiate->verify_result = BADCERT_SKIP_VERIFY; debug_msg (2, "skipping read certificate"); tls->state++; return 0; } debug_msg (3, "read certificate"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } tls->state++; if (!tls->is_client && tls->minor_ver != TLS_MINOR_VERSION_0) { if (tls->in_hslen == 7 && tls->in_msgtype == TLS_MSG_HANDSHAKE && tls->in_msg[0] == TLS_HS_CERTIFICATE && !memcmp (tls->in_msg + 4, "\0\0\0", 3)) { debug_msg (1, "TLSv1 client has no certificate"); tls->session_negotiate->verify_result = BADCERT_MISSING; if (tls->authmode == TLS_VERIFY_REQUIRED) return gpg_error (GPG_ERR_MISSING_CLIENT_CERT); else return 0; } } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } if (tls->in_msg[0] != TLS_HS_CERTIFICATE || tls->in_hslen < 10) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } /* * Same message structure as in _ntbtls_write_certificate() */ n = buf16_to_size_t (tls->in_msg + 5); if (tls->in_msg[4] != 0 || tls->in_hslen != 7 + n) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } /* In case we tried to reuse a session but it failed. */ if (tls->session_negotiate->peer_chain) { _ntbtls_x509_cert_release (tls->session_negotiate->peer_chain); tls->session_negotiate->peer_chain = NULL; } err = _ntbtls_x509_cert_new (&tls->session_negotiate->peer_chain); if (err) { debug_msg (1, "allocating X.509 cert object failed"); return err; } for (i = 7; i < tls->in_hslen; ) { if (tls->in_msg[i] != 0) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } n = buf16_to_size_t (tls->in_msg + i + 1); i += 3; if (n < 128 || i + n > tls->in_hslen) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } err = _ntbtls_x509_append_cert (tls->session_negotiate->peer_chain, tls->in_msg + i, n); if (err) { debug_ret (1, "x509_append_cert", err); return err; } i += n; } debug_crt (1, "peer certificate", tls->session_negotiate->peer_chain); /* * On client, make sure the server cert doesn't change during renego to * avoid "triple handshake" attack: https://secure-resumption.com/ */ if (tls->is_client && tls->renegotiation == TLS_RENEGOTIATION) { if (!tls->session->peer_chain) { debug_msg (1, "new server cert during renegotiation"); return gpg_error (GPG_ERR_BAD_HS_CERT); } //FIXME: Need to implement this in x509.c IMPORTANT! /* if (tls->session->peer_chain->raw.len != */ /* tls->session_negotiate->peer_chain->raw.len */ /* || memcmp (tls->session->peer_chain->raw.p, */ /* tls->session_negotiate->peer_chain->raw.p, */ /* tls->session->peer_chain->raw.len)) */ /* { */ /* debug_msg (1, "server cert changed during renegotiation"); */ /* return gpg_error (GPG_ERR_BAD_HS_CERT); */ /* } */ } if (tls->authmode != TLS_VERIFY_NONE) { /* * Verify hostname */ if (tls->hostname) { if (!tls->session_negotiate) err = gpg_error (GPG_ERR_MISSING_CERT); else err = _ntbtls_x509_check_hostname (tls->session_negotiate->peer_chain, tls->hostname); if (err) { debug_ret (1, "x509_check_hostname", err); } } else err = 0; /* * Verify certificate. We don't do this if the hostname check * already failed. */ if (!err) { if (!tls->verify_cb) { debug_msg (1, "verify callback not set"); return gpg_error (GPG_ERR_NOT_INITIALIZED); } err = tls->verify_cb (tls->verify_cb_value, tls, 0); if (err) { debug_ret (1, "error from the verify callback", err); } if (tls->authmode != TLS_VERIFY_REQUIRED) err = 0; } } return err; } gpg_error_t _ntbtls_write_change_cipher_spec (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "write change cipher spec"); tls->out_msgtype = TLS_MSG_CHANGE_CIPHER_SPEC; tls->out_msglen = 1; tls->out_msg[0] = 1; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } gpg_error_t _ntbtls_read_change_cipher_spec (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "read change_cipher_spec"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_CHANGE_CIPHER_SPEC) { debug_msg (1, "bad change_cipher_spec message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } if (tls->in_msglen != 1 || tls->in_msg[0] != 1) { debug_msg (1, "bad change_cipher_spec message"); return gpg_error (GPG_ERR_BAD_HS_CHANGE_CIPHER); } tls->state++; return 0; } static void update_checksum_sha256 (ntbtls_t tls, const unsigned char *buf, size_t len) { gcry_md_write (tls->handshake->fin_sha256, buf, len); } static void update_checksum_sha384 (ntbtls_t tls, const unsigned char *buf, size_t len) { gcry_md_write (tls->handshake->fin_sha512, buf, len); } void _ntbtls_optimize_checksum (ntbtls_t tls, const ciphersuite_t suite) { if (_ntbtls_ciphersuite_get_mac (suite) == GCRY_MAC_HMAC_SHA384) tls->handshake->update_checksum = update_checksum_sha384; else if (_ntbtls_ciphersuite_get_mac (suite) != GCRY_MAC_HMAC_SHA384) tls->handshake->update_checksum = update_checksum_sha256; else { debug_bug (); return; } } static void update_checksum_start (ntbtls_t tls, const unsigned char *buf, size_t len) { gcry_md_write (tls->handshake->fin_sha256, buf, len); gcry_md_write (tls->handshake->fin_sha512, buf, len); } static void calc_finished_tls (ntbtls_t tls, int is_sha384, unsigned char *buf, int is_client) { gpg_error_t err; gcry_md_hd_t md; int len = 12; const char *sender; unsigned char padbuf[48]; size_t hashlen = is_sha384? 48 : 32; session_t session; char *p; session = tls->session_negotiate; if (!session) session = tls->session; debug_msg (2, "calc finished tls sha%d", is_sha384? 384 : 256); err = gcry_md_copy (&md, (is_sha384 ? tls->handshake->fin_sha512 /* */ : tls->handshake->fin_sha256)); if (err) { debug_ret (1, "calc_finished_tls", err); memset (buf, 0, len); return; } /* * TLSv1.2: * hash = PRF( master, finished_label, * Hash( handshake ) )[0.11] */ sender = is_client ? "client finished" : "server finished"; p = gcry_md_read (md, is_sha384? GCRY_MD_SHA384 : GCRY_MD_SHA256); if (p) memcpy (padbuf, p, hashlen); gcry_md_close (md); if (!p) { debug_bug (); memset (buf, 0, len); return; } tls->handshake->tls_prf (session->master, 48, sender, padbuf, hashlen, buf, len); debug_buf (3, "calc finished result", buf, len); wipememory (padbuf, hashlen); } static void calc_finished_tls_sha256 (ntbtls_t tls, unsigned char *buf, int is_client) { calc_finished_tls (tls, 0, buf, is_client); } static void calc_finished_tls_sha384 (ntbtls_t tls, unsigned char *buf, int is_client) { calc_finished_tls (tls, 1, buf, is_client); } void _ntbtls_handshake_wrapup (ntbtls_t tls) { int resume = tls->handshake->resume; debug_msg (3, "handshake wrapup"); /* * Free our handshake params */ handshake_params_deinit (tls->handshake); free (tls->handshake); tls->handshake = NULL; if (tls->renegotiation == TLS_RENEGOTIATION) { tls->renegotiation = TLS_RENEGOTIATION_DONE; tls->renego_records_seen = 0; } /* * Switch in our now active transform context */ if (tls->transform) { transform_deinit (tls->transform); free (tls->transform); } tls->transform = tls->transform_negotiate; tls->transform_negotiate = NULL; if (tls->session) { session_deinit (tls->session); free (tls->session); } tls->session = tls->session_negotiate; tls->session_negotiate = NULL; /* * Add cache entry */ if (tls->f_set_cache && tls->session->length && !resume) { if (tls->f_set_cache (tls->p_set_cache, tls->session)) debug_msg (1, "cache did not store session"); } tls->state++; debug_msg (3, "handshake wrapup ready "); } gpg_error_t _ntbtls_write_finished (ntbtls_t tls) { gpg_error_t err; int hashlen; debug_msg (2, "write finished"); /* * Set the out_msg pointer to the correct location based on IV length */ if (tls->minor_ver >= TLS_MINOR_VERSION_2) { tls->out_msg = (tls->out_iv + tls->transform_negotiate->ivlen - tls->transform_negotiate->fixed_ivlen); } else tls->out_msg = tls->out_iv; tls->handshake->calc_finished (tls, tls->out_msg + 4, tls->is_client); /* TODO TLS/1.2 Hash length is determined by cipher suite (Page 63) but all currently defined cipher suite keep it at 12. */ hashlen = 12; tls->verify_data_len = hashlen; memcpy (tls->own_verify_data, tls->out_msg + 4, hashlen); tls->out_msglen = 4 + hashlen; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_FINISHED; /* * In case of session resuming, invert the client and server * ChangeCipherSpec messages order. */ if (tls->handshake->resume) { if (tls->is_client) tls->state = TLS_HANDSHAKE_WRAPUP; else tls->state = TLS_CLIENT_CHANGE_CIPHER_SPEC; } else tls->state++; /* * Switch to our negotiated transform and session parameters for outbound * data. */ debug_msg (3, "switching to new transform spec for outbound data"); tls->transform_out = tls->transform_negotiate; tls->session_out = tls->session_negotiate; memset (tls->out_ctr, 0, 8); err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } gpg_error_t _ntbtls_read_finished (ntbtls_t tls) { gpg_error_t err; unsigned int hashlen; unsigned char buf[36]; debug_msg (2, "read finished"); tls->handshake->calc_finished (tls, buf, !tls->is_client); /* * Switch to our negotiated transform and session parameters for inbound * data. */ debug_msg (3, "switching to new transform spec for inbound data"); tls->transform_in = tls->transform_negotiate; tls->session_in = tls->session_negotiate; memset (tls->in_ctr, 0, 8); /* * Set the in_msg pointer to the correct location based on IV length */ if (tls->minor_ver >= TLS_MINOR_VERSION_2) { tls->in_msg = (tls->in_iv + tls->transform_negotiate->ivlen - tls->transform_negotiate->fixed_ivlen); } else tls->in_msg = tls->in_iv; err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad finished message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } /* TODO TLS/1.2 Hash length is determined by cipher suite (Page 63). */ hashlen = 12; if (tls->in_msg[0] != TLS_HS_FINISHED || tls->in_hslen != 4 + hashlen) { debug_msg (1, "bad finished message"); return gpg_error (GPG_ERR_BAD_HS_FINISHED); } if (memcmpct (tls->in_msg + 4, buf, hashlen)) { debug_msg (1, "bad finished message"); debug_buf (2, "want", buf, hashlen); debug_buf (2, " got", tls->in_msg+4, hashlen); return gpg_error (GPG_ERR_BAD_HS_FINISHED); } tls->verify_data_len = hashlen; memcpy (tls->peer_verify_data, buf, hashlen); if (tls->handshake->resume) { if (tls->is_client) tls->state = TLS_CLIENT_CHANGE_CIPHER_SPEC; else tls->state = TLS_HANDSHAKE_WRAPUP; } else tls->state++; return 0; } static gpg_error_t transform_init (transform_t transform) { gpg_error_t err = 0; (void)transform; //FIXME: /* cipher_init (&transform->cipher_ctx_enc); */ /* cipher_init (&transform->cipher_ctx_dec); */ /* md_init (&transform->mac_ctx_enc); */ /* md_init (&transform->mac_ctx_dec); */ return err; } static void transform_deinit (transform_t transform) { if (!transform) return; //FIXME: /* deflateEnd (&transform->ctx_deflate); */ /* inflateEnd (&transform->ctx_inflate); */ /* cipher_free (&transform->cipher_ctx_enc); */ /* cipher_free (&transform->cipher_ctx_dec); */ /* md_free (&transform->mac_ctx_enc); */ /* md_free (&transform->mac_ctx_dec); */ wipememory (transform, sizeof *transform); } static gpg_error_t session_init (session_t session) { (void)session; return 0; } static void session_deinit (session_t session) { if (!session) return; _ntbtls_x509_cert_release (session->peer_chain); free (session->ticket); wipememory (session, sizeof *session); } static gpg_error_t handshake_params_init (handshake_params_t handshake) { gpg_error_t err; err = gcry_md_open (&handshake->fin_sha256, GCRY_MD_SHA256, 0); if (err) return err; err = gcry_md_open (&handshake->fin_sha512, GCRY_MD_SHA384, 0); if (err) { gcry_md_close (handshake->fin_sha256); handshake->fin_sha256 = NULL; return err; } err = _ntbtls_dhm_new (&handshake->dhm_ctx); if (err) { gcry_md_close (handshake->fin_sha256); handshake->fin_sha256 = NULL; gcry_md_close (handshake->fin_sha512); handshake->fin_sha512 = NULL; return err; } err = _ntbtls_ecdh_new (&handshake->ecdh_ctx); if (err) { _ntbtls_dhm_release (handshake->dhm_ctx); handshake->dhm_ctx = NULL; gcry_md_close (handshake->fin_sha256); handshake->fin_sha256 = NULL; gcry_md_close (handshake->fin_sha512); handshake->fin_sha512 = NULL; return err; } handshake->update_checksum = update_checksum_start; handshake->sig_alg = TLS_HASH_SHA256; return 0; } static void handshake_params_deinit (handshake_params_t handshake) { if (!handshake) return; _ntbtls_dhm_release (handshake->dhm_ctx); handshake->dhm_ctx = NULL; _ntbtls_ecdh_release (handshake->ecdh_ctx); handshake->ecdh_ctx = NULL; free (handshake->curves); /* Free only the linked list wrapper, not the keys themselves since the belong to the SNI callback. */ if (handshake->sni_key_cert) { //FIXME: /* ssl_key_cert *cur, *next; */ /* cur = handshake->sni_key_cert; */ /* while (cur) */ /* { */ /* next = cur->next; */ /* free (cur); */ /* cur = next; */ /* } */ } wipememory (handshake, sizeof *handshake); } static gpg_error_t handshake_init (ntbtls_t tls) { gpg_error_t err; /* Clear old handshake information if present. */ transform_deinit (tls->transform_negotiate); session_deinit (tls->session_negotiate); handshake_params_deinit (tls->handshake); /* * Either the pointers are now NULL or cleared properly and can be freed. * Now allocate missing structures. */ if (!tls->transform_negotiate) { tls->transform_negotiate = calloc (1, sizeof *tls->transform_negotiate); if (!tls->transform_negotiate) { err = gpg_error_from_syserror (); goto leave; } } if (!tls->session_negotiate) { tls->session_negotiate = calloc (1, sizeof *tls->session_negotiate); if (!tls->session_negotiate) { err = gpg_error_from_syserror (); goto leave; } } if (!tls->handshake) { tls->handshake = calloc (1, sizeof *tls->handshake); if (!tls->handshake) { err = gpg_error_from_syserror (); goto leave; } } /* Initialize structures */ err = transform_init (tls->transform_negotiate); if (err) goto leave; err = session_init (tls->session_negotiate); if (err) goto leave; err = handshake_params_init (tls->handshake); if (err) goto leave; /* Fixme: Document the owner of KEY_CERT or use a ref counter. */ tls->handshake->key_cert = tls->key_cert; leave: if (err) { transform_deinit (tls->transform_negotiate); free (tls->transform_negotiate); tls->transform_negotiate = NULL; session_deinit (tls->session_negotiate); free (tls->session_negotiate); tls->session_negotiate = NULL; handshake_params_deinit (tls->handshake); free (tls->handshake); tls->handshake = NULL; } return err; } /* * Create a new TLS context. Valid values for FLAGS are: * * NTBTLS_SERVER - This endpoint is a server (default). * NTBTLS_CLIENT - This endpoint is a client. * * On success a context object is returned at R_TLS. One error NULL * is stored at R_TLS and an error code is returned. */ gpg_error_t _ntbtls_new (ntbtls_t *r_tls, unsigned int flags) { gpg_error_t err; ntbtls_t tls; int buffer_len = TLS_BUFFER_LEN; *r_tls = NULL; /* Note: NTBTLS_SERVER has value 0, thus we can't check for it. */ if ((flags & ~(NTBTLS_CLIENT|NTBTLS_SAMETRHEAD))) return gpg_error (GPG_ERR_EINVAL); tls = calloc (1, sizeof *tls); if (!tls) return gpg_error_from_syserror (); /* Return immediately. */ tls->magic = NTBTLS_CONTEXT_MAGIC; tls->min_major_ver = TLS_MIN_MAJOR_VERSION; tls->min_minor_ver = TLS_MIN_MINOR_VERSION; tls->max_major_ver = TLS_MAX_MAJOR_VERSION; tls->max_minor_ver = TLS_MAX_MINOR_VERSION; tls->flags = flags; if ((flags & NTBTLS_CLIENT)) { tls->is_client = 1; tls->use_session_tickets = 1; } /* We only support TLS 1.2 and thus we set the list for the other TLS versions to NULL. */ tls->ciphersuite_list[TLS_MINOR_VERSION_0] = NULL; tls->ciphersuite_list[TLS_MINOR_VERSION_1] = NULL; tls->ciphersuite_list[TLS_MINOR_VERSION_2] = NULL; tls->ciphersuite_list[TLS_MINOR_VERSION_3] = _ntbtls_ciphersuite_list (); tls->renego_max_records = TLS_RENEGO_MAX_RECORDS_DEFAULT; /* FIXME */ /* if ((ret = mpi_read_string (&tls->dhm_P, 16, */ /* POLARSSL_DHM_RFC5114_MODP_1024_P)) != 0 || */ /* (ret = mpi_read_string (&tls->dhm_G, 16, */ /* POLARSSL_DHM_RFC5114_MODP_1024_G)) != 0) */ /* { */ /* debug_ret (1, "mpi_read_string", ret); */ /* return (ret); */ /* } */ /* * Prepare base structures */ tls->in_ctr = malloc (buffer_len); if (!tls->in_ctr) { err = gpg_error_from_syserror (); goto leave; } tls->in_hdr = tls->in_ctr + 8; tls->in_iv = tls->in_ctr + 13; tls->in_msg = tls->in_ctr + 13; tls->out_ctr = malloc (buffer_len); if (!tls->out_ctr) { err = gpg_error_from_syserror (); goto leave; } tls->out_hdr = tls->out_ctr + 8; tls->out_iv = tls->out_ctr + 13; tls->out_msg = tls->out_ctr + 13; memset (tls->in_ctr, 0, buffer_len); memset (tls->out_ctr, 0, buffer_len); tls->ticket_lifetime = TLS_DEFAULT_TICKET_LIFETIME; // FIXME: tls->curve_list = ecp_grp_id_list (); err = handshake_init (tls); if (err) goto leave; if (tls->is_client) tls->use_session_tickets = 1; leave: if (err) { free (tls->in_ctr); free (tls); } else *r_tls = tls; return err; } /* * Release an TLS context. */ void _ntbtls_release (ntbtls_t tls) { if (!tls) return; debug_msg (2, "release"); if (tls->magic != NTBTLS_CONTEXT_MAGIC) debug_bug (); if (tls->out_ctr) { /* FIXME: At some points we are using a variable for the length. Either do that always or use always this constant. */ wipememory (tls->out_ctr, TLS_BUFFER_LEN); free (tls->out_ctr); } if (tls->in_ctr) { wipememory (tls->in_ctr, TLS_BUFFER_LEN); free (tls->in_ctr); } if (tls->compress_buf) { wipememory (tls->compress_buf, TLS_BUFFER_LEN); free (tls->compress_buf); } //FIXME: /* mpi_free (&tls->dhm_P); */ /* mpi_free (&tls->dhm_G); */ if (tls->transform) { transform_deinit (tls->transform); free (tls->transform); } if (tls->handshake) { handshake_params_deinit (tls->handshake); free (tls->handshake); transform_deinit (tls->transform_negotiate); free (tls->transform_negotiate); session_deinit (tls->session_negotiate); free (tls->session_negotiate); } if (tls->session) { session_deinit (tls->session); free (tls->session); } if (tls->ticket_keys) { ticket_keys_deinit (tls->ticket_keys); free (tls->ticket_keys); } free (tls->hostname); if (tls->psk) { wipememory (tls->psk, tls->psk_len); wipememory (tls->psk_identity, tls->psk_identity_len); free (tls->psk); free (tls->psk_identity); tls->psk_len = 0; tls->psk_identity_len = 0; } //FIXME: /* ssl_key_cert_free (tls->key_cert); */ /* Actually clear after last debug message */ wipememory (tls, sizeof *tls); free (tls); } +/* Return info about the last rceeived alert. */ +const char * +_ntbtls_get_last_alert (ntbtls_t tls, + unsigned int *r_level, unsigned int *r_type) +{ + if (!tls || !tls->last_alert.any) + { + if (r_level) + *r_level = 0; + if (r_type) + *r_type = 0; + return NULL; + } + + if (r_level) + *r_level = tls->last_alert.level; + if (r_type) + *r_type = tls->last_alert.type; + return alert_msg_to_string (tls->last_alert.type); +} + + /* Set the transport stream for the context TLS. This needs to be called right after init and may not be changed later. INBOUND and OUTBOUND are usually connected to the same socket. The caller must ensure that the streams are not closed as long as the context TLS is valid. However, after destroying the context the streams may be closed. This behavior allows to setup a TLS connection on an existing stream, shutdown the TLS and continue unencrypted. Whether the latter is of any real use in practice is a different question. Using separate streams allow to run TLS over a pair of half-duplex connections. */ gpg_error_t _ntbtls_set_transport (ntbtls_t tls, estream_t inbound, estream_t outbound) { if (!tls || !inbound || !outbound) return gpg_error (GPG_ERR_INV_ARG); if (tls->inbound || tls->outbound) return gpg_error (GPG_ERR_CONFLICT); /* We do our own buffering thus we disable buffer of the transport streams. */ if (es_setvbuf (inbound, NULL, _IONBF, 0)) return gpg_error_from_syserror (); if (es_setvbuf (outbound, NULL, _IONBF, 0)) return gpg_error_from_syserror (); tls->inbound = inbound; tls->outbound = outbound; return 0; } /* * Reset an initialized and used SSL context for re-use while retaining * all application-set variables, function pointers and data. */ int ssl_session_reset (ntbtls_t ssl) { int ret; ssl->state = TLS_HELLO_REQUEST; ssl->renegotiation = TLS_INITIAL_HANDSHAKE; ssl->secure_renegotiation = TLS_LEGACY_RENEGOTIATION; ssl->verify_data_len = 0; memset (ssl->own_verify_data, 0, 36); memset (ssl->peer_verify_data, 0, 36); ssl->in_offt = NULL; ssl->in_msg = ssl->in_ctr + 13; ssl->in_msgtype = 0; ssl->in_msglen = 0; ssl->in_left = 0; ssl->in_hslen = 0; ssl->nb_zero = 0; ssl->record_read = 0; ssl->out_msg = ssl->out_ctr + 13; ssl->out_msgtype = 0; ssl->out_msglen = 0; ssl->out_left = 0; ssl->transform_in = NULL; ssl->transform_out = NULL; ssl->renego_records_seen = 0; memset (ssl->out_ctr, 0, TLS_BUFFER_LEN); memset (ssl->in_ctr, 0, TLS_BUFFER_LEN); if (ssl->transform) { transform_deinit (ssl->transform); free (ssl->transform); ssl->transform = NULL; } if (ssl->session) { session_deinit (ssl->session); free (ssl->session); ssl->session = NULL; } ssl->alpn_chosen = NULL; if ((ret = handshake_init (ssl)) != 0) return (ret); return (0); } static void ticket_keys_deinit (ticket_keys_t tkeys) { //FIXME: /* aes_free (&tkeys->enc); */ /* aes_free (&tkeys->dec); */ wipememory (tkeys, sizeof *tkeys); } /* * Allocate and initialize ticket keys in TLS if not yet done. */ static gpg_error_t ticket_keys_setup (ntbtls_t tls) { ticket_keys_t tkeys; unsigned char buf[16]; if (tls->ticket_keys) return 0; tkeys = malloc (sizeof *tkeys); if (!tkeys) return gpg_error_from_syserror (); //FIXME: /* aes_init (&tkeys->enc); */ /* aes_init (&tkeys->dec); */ gcry_randomize (tkeys->key_name, 16, GCRY_STRONG_RANDOM); gcry_randomize (buf, 16, GCRY_STRONG_RANDOM); //FIXME: /* if ((ret = aes_setkey_enc (&tkeys->enc, buf, 128)) != 0 || */ /* (ret = aes_setkey_dec (&tkeys->dec, buf, 128)) != 0) */ /* { */ /* ssl_ticket_keys_free (tkeys); */ /* polarssl_free (tkeys); */ /* return (ret); */ /* } */ gcry_randomize (tkeys->mac_key, 16, GCRY_STRONG_RANDOM); tls->ticket_keys = tkeys; return 0; } /* * SSL set accessors */ void ssl_set_authmode (ntbtls_t ssl, int authmode) { ssl->authmode = authmode; } #if defined(POLARSSL_X509_CRT_PARSE_C) void ssl_set_verify (ntbtls_t ssl, int (*f_vrfy) (void *, x509_crt *, int, int *), void *p_vrfy) { ssl->f_vrfy = f_vrfy; ssl->p_vrfy = p_vrfy; } #endif /* POLARSSL_X509_CRT_PARSE_C */ void ssl_set_session_cache (ntbtls_t ssl, int (*f_get_cache) (void *, session_t), void *p_get_cache, int (*f_set_cache) (void *, const session_t), void *p_set_cache) { ssl->f_get_cache = f_get_cache; ssl->p_get_cache = p_get_cache; ssl->f_set_cache = f_set_cache; ssl->p_set_cache = p_set_cache; } /* Request resumption of session (client-side only). Session data is copied from presented session structure. */ gpg_error_t _ntbtls_set_session (ntbtls_t tls, const session_t session) { gpg_error_t err; if (!tls || !session || !tls->session_negotiate || !tls->is_client) return gpg_error (GPG_ERR_INV_ARG); err = session_copy (tls->session_negotiate, session); if (err) return err; tls->handshake->resume = 1; return 0; } /* Add a new (empty) key_cert entry an return a pointer to it */ /* static ssl_key_cert * */ /* ssl_add_key_cert (ntbtls_t ssl) */ /* { */ /* ssl_key_cert *key_cert, *last; */ /* key_cert = calloc (1, sizeof *key_cert); */ /* if (!key_cert) */ /* return NULL; */ /* /\* Append the new key_cert to the (possibly empty) current list *\/ */ /* if (ssl->key_cert == NULL) */ /* { */ /* ssl->key_cert = key_cert; */ /* if (ssl->handshake != NULL) */ /* ssl->handshake->key_cert = key_cert; */ /* } */ /* else */ /* { */ /* last = ssl->key_cert; */ /* while (last->next != NULL) */ /* last = last->next; */ /* last->next = key_cert; */ /* } */ /* return (key_cert); */ /* } */ /* Set a certificate verify callback for the session TLS. */ gpg_error_t _ntbtls_set_verify_cb (ntbtls_t tls, ntbtls_verify_cb_t cb, void *cb_value) { if (!tls) return gpg_error (GPG_ERR_INV_ARG); tls->verify_cb = cb; tls->verify_cb_value = cb_value; /* Make sure we have an authmode set. Right now, there is no API to * change thye authmode. */ tls->authmode = cb ? TLS_VERIFY_REQUIRED : TLS_VERIFY_NONE; return 0; } /* int */ /* ssl_set_own_cert (ntbtls_t ssl, x509_crt * own_cert, pk_context * pk_key) */ /* { */ /* ssl_key_cert *key_cert; */ /* key_cert = ssl_add_key_cert (ssl); */ /* if (!key_cert) */ /* return gpg_error_from_syserror (); */ /* key_cert->cert = own_cert; */ /* key_cert->key = pk_key; */ /* return 0; */ /* } */ /* int */ /* ssl_set_own_cert_rsa (ntbtls_t ssl, x509_crt * own_cert, */ /* rsa_context * rsa_key) */ /* { */ /* int ret; */ /* ssl_key_cert *key_cert; */ /* key_cert = ssl_add_key_cert (ssl); */ /* if (!key_cert) */ /* return gpg_error_from_syserror (); */ /* key_cert->key = malloc (sizeof (pk_context)); */ /* if (!key_cert->key) */ /* return gpg_error_from_syserror (); */ /* pk_init (key_cert->key); */ /* ret = pk_init_ctx (key_cert->key, pk_info_from_type (POLARSSL_PK_RSA)); */ /* if (ret != 0) */ /* return (ret); */ /* if ((ret = rsa_copy (pk_rsa (*key_cert->key), rsa_key)) != 0) */ /* return (ret); */ /* key_cert->cert = own_cert; */ /* key_cert->key_own_alloc = 1; */ /* return (0); */ /* } */ /* int */ /* ssl_set_own_cert_alt (ntbtls_t ssl, x509_crt * own_cert, */ /* void *rsa_key, */ /* rsa_decrypt_func rsa_decrypt, */ /* rsa_sign_func rsa_sign, rsa_key_len_func rsa_key_len) */ /* { */ /* int ret; */ /* ssl_key_cert *key_cert; */ /* key_cert = ssl_add_key_cert (ssl); */ /* if (!key_cert) */ /* return gpg_error_from_syserror (); */ /* key_cert->key = malloc (sizeof (pk_context)); */ /* if (!key_cert->key) */ /* { */ /* err = gpg_error_from_syserror (); */ /* free (key_cert); */ /* return err; */ /* } */ /* pk_init (key_cert->key); */ /* if ((ret = pk_init_ctx_rsa_alt (key_cert->key, rsa_key, */ /* rsa_decrypt, rsa_sign, rsa_key_len)) != 0) */ /* return (ret); */ /* key_cert->cert = own_cert; */ /* key_cert->key_own_alloc = 1; */ /* return 0; */ /* } */ /* int */ /* ssl_set_psk (ntbtls_t ssl, const unsigned char *psk, size_t psk_len, */ /* const unsigned char *psk_identity, size_t psk_identity_len) */ /* { */ /* if (psk == NULL || psk_identity == NULL) */ /* return gpg_error (GPG_ERR_INV_ARG); */ /* if (psk_len > POLARSSL_PSK_MAX_LEN) */ /* return gpg_error (GPG_ERR_INV_ARG); */ /* if (ssl->psk != NULL) */ /* { */ /* free (ssl->psk); */ /* ssl->psk = NULL; */ /* free (ssl->psk_identity); */ /* ssl->psk_identity = NULL; */ /* } */ /* ssl->psk_len = psk_len; */ /* ssl->psk_identity_len = psk_identity_len; */ /* ssl->psk = malloc (ssl->psk_len); */ /* if (!ssl->psk) */ /* return gpg_error_from_syserror (); */ /* ssl->psk_identity = malloc (ssl->psk_identity_len); */ /* if (!ssl->psk_identity) */ /* { */ /* err = gpg_error_from_syserror (); */ /* free (ssl->psk); */ /* ssl->psk = NULL; */ /* return err; */ /* } */ /* memcpy (ssl->psk, psk, ssl->psk_len); */ /* memcpy (ssl->psk_identity, psk_identity, ssl->psk_identity_len); */ /* return (0); */ /* } */ /* void */ /* ssl_set_psk_cb (ntbtls_t ssl, */ /* int (*f_psk) (void *, ssl_context *, const unsigned char *, */ /* size_t), void *p_psk) */ /* { */ /* ssl->f_psk = f_psk; */ /* ssl->p_psk = p_psk; */ /* } */ /* int */ /* ssl_set_dh_param (ntbtls_t ssl, const char *dhm_P, const char *dhm_G) */ /* { */ /* int ret; */ /* if ((ret = mpi_read_string (&ssl->dhm_P, 16, dhm_P)) != 0) */ /* { */ /* debug_ret (1, "mpi_read_string", ret); */ /* return (ret); */ /* } */ /* if ((ret = mpi_read_string (&ssl->dhm_G, 16, dhm_G)) != 0) */ /* { */ /* debug_ret (1, "mpi_read_string", ret); */ /* return (ret); */ /* } */ /* return (0); */ /* } */ /* int */ /* ssl_set_dh_param_ctx (ntbtls_t ssl, dhm_context * dhm_ctx) */ /* { */ /* int ret; */ /* if ((ret = mpi_copy (&ssl->dhm_P, &dhm_ctx->P)) != 0) */ /* { */ /* debug_ret (1, "mpi_copy", ret); */ /* return (ret); */ /* } */ /* if ((ret = mpi_copy (&ssl->dhm_G, &dhm_ctx->G)) != 0) */ /* { */ /* debug_ret (1, "mpi_copy", ret); */ /* return (ret); */ /* } */ /* return (0); */ /* } */ /* * Set the allowed elliptic curves */ /* void */ /* ssl_set_curves (ntbtls_t ssl, const ecp_group_id * curve_list) */ /* { */ /* ssl->curve_list = curve_list; */ /* } */ gpg_error_t _ntbtls_set_hostname (ntbtls_t tls, const char *hostname) { size_t len; if (!tls) return gpg_error (GPG_ERR_INV_ARG); if (!hostname) { free (tls->hostname); tls->hostname = NULL; } len = strlen (hostname); if ( len + 1 < len ) return gpg_error (GPG_ERR_EOVERFLOW); tls->hostname = malloc (len + 1); if (!tls->hostname) return gpg_error_from_syserror (); strcpy (tls->hostname, hostname); return 0; } /* Return the hostname which has been set with ntbtls_set_hostname. * The returned value is valid as long as TLS is valid and * ntbtls_set_hostname has not been used again. */ const char * _ntbtls_get_hostname (ntbtls_t tls) { return tls ? tls->hostname : NULL; } /* void */ /* ssl_set_sni (ntbtls_t ssl, */ /* int (*f_sni) (void *, ntbtls_t, */ /* const unsigned char *, size_t), void *p_sni) */ /* { */ /* ssl->f_sni = f_sni; */ /* ssl->p_sni = p_sni; */ /* } */ /* int */ /* ssl_set_alpn_protocols (ntbtls_t ssl, const char **protos) */ /* { */ /* size_t cur_len, tot_len; */ /* const char **p; */ /* /\* */ /* * "Empty strings MUST NOT be included and byte strings MUST NOT be */ /* * truncated". Check lengths now rather than later. */ /* *\/ */ /* tot_len = 0; */ /* for (p = protos; *p != NULL; p++) */ /* { */ /* cur_len = strlen (*p); */ /* tot_len += cur_len; */ /* if (cur_len == 0 || cur_len > 255 || tot_len > 65535) */ /* return gpg_error (GPG_ERR_INV_ARG); */ /* } */ /* ssl->alpn_list = protos; */ /* return (0); */ /* } */ /* const char * */ /* ssl_get_alpn_protocol (const ntbtls_t ssl) */ /* { */ /* return (ssl->alpn_chosen); */ /* } */ /* void */ /* ssl_set_max_version (ntbtls_t ssl, int major, int minor) */ /* { */ /* if ( major >= TLS_MIN_MAJOR_VERSION && major <= TLS_MAX_MAJOR_VERSION */ /* && minor >= TLS_MIN_MINOR_VERSION && minor <= TLS_MAX_MINOR_VERSION) */ /* { */ /* ssl->max_major_ver = major; */ /* ssl->max_minor_ver = minor; */ /* } */ /* } */ /* void */ /* ssl_set_min_version (ntbtls_t ssl, int major, int minor) */ /* { */ /* if ( major >= TLS_MIN_MAJOR_VERSION && major <= TLS_MAX_MAJOR_VERSION */ /* && minor >= TLS_MIN_MINOR_VERSION && minor <= TLS_MAX_MINOR_VERSION) */ /* { */ /* ssl->min_major_ver = major; */ /* ssl->min_minor_ver = minor; */ /* } */ /* } */ int ssl_set_max_frag_len (ntbtls_t ssl, unsigned char mfl_code) { if (mfl_code >= DIM(mfl_code_to_length) || mfl_code_to_length[mfl_code] > TLS_MAX_CONTENT_LEN) { return gpg_error (GPG_ERR_INV_ARG); } ssl->mfl_code = mfl_code; return (0); } int ssl_set_truncated_hmac (ntbtls_t ssl, int truncate) { if (!ssl->is_client) return gpg_error (GPG_ERR_INV_ARG); ssl->use_trunc_hmac = !!truncate; return 0; } void ssl_set_renegotiation (ntbtls_t ssl, int renegotiation) { ssl->disable_renegotiation = renegotiation; } void ssl_legacy_renegotiation (ntbtls_t ssl, int allow_legacy) { ssl->allow_legacy_renegotiation = allow_legacy; } void ssl_set_renegotiation_enforced (ntbtls_t ssl, int max_records) { ssl->renego_max_records = max_records; } int _ntbtls_set_session_tickets (ntbtls_t tls, int use_tickets) { tls->use_session_tickets = !!use_tickets; if (tls->is_client) return 0; return ticket_keys_setup (tls); } void ssl_set_session_ticket_lifetime (ntbtls_t ssl, int lifetime) { ssl->ticket_lifetime = lifetime; } /* * SSL get accessors */ size_t ssl_get_bytes_avail (const ntbtls_t ssl) { return (ssl->in_offt == NULL ? 0 : ssl->in_msglen); } int ssl_get_verify_result (const ntbtls_t ssl) { return (ssl->session->verify_result); } /* * Return the name of the current ciphersuite */ const char * _ntbtls_get_ciphersuite (const ntbtls_t tls) { if (!tls || !tls->session) return NULL; return _ntbtls_ciphersuite_get_name (tls->session->ciphersuite); } /* const x509_crt * */ /* ssl_get_peer_chain (const ntbtls_t ssl) */ /* { */ /* if (ssl == NULL || ssl->session == NULL) */ /* return (NULL); */ /* return (ssl->session->peer_chain); */ /* } */ /* Save session in order to resume it later (client-side only). Session data is copied to presented session structure. */ gpg_error_t _ntbtls_get_session (const ntbtls_t tls, session_t dst) { if (!tls || !dst || !tls->session || !tls->is_client) { return gpg_error (GPG_ERR_INV_ARG); } return session_copy (dst, tls->session); } /* * Perform a single step of the SSL handshake */ static gpg_error_t handshake_step (ntbtls_t tls) { gpg_error_t err; if (tls->is_client) err = _ntbtls_handshake_client_step (tls); else err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*_ntbtls_handshake_server_step (tls);*/ return err; } /* * Perform the SSL handshake */ gpg_error_t _ntbtls_handshake (ntbtls_t tls) { gpg_error_t err = 0; debug_msg (2, "handshake"); while (tls->state != TLS_HANDSHAKE_OVER) { err = handshake_step (tls); if (err) break; } debug_msg (2, "handshake ready"); return err; } /* * Write HelloRequest to request renegotiation on server */ static int write_hello_request (ntbtls_t ssl) { int ret; debug_msg (2, "write hello_request"); ssl->out_msglen = 4; ssl->out_msgtype = TLS_MSG_HANDSHAKE; ssl->out_msg[0] = TLS_HS_HELLO_REQUEST; ret = _ntbtls_write_record (ssl); if (ret) { debug_ret (1, "write_record", ret); return ret; } ssl->renegotiation = TLS_RENEGOTIATION_PENDING; return (0); } /* * Actually renegotiate current connection, triggered by either: * - calling ssl_renegotiate() on client, * - receiving a HelloRequest on client during ssl_read(), * - receiving any handshake message on server during ssl_read() after the * initial handshake is completed * If the handshake doesn't complete due to waiting for I/O, it will continue * during the next calls to ssl_renegotiate() or ssl_read() respectively. */ static gpg_error_t start_renegotiation (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "renegotiate"); err = handshake_init (tls); if (err) return err; tls->state = TLS_HELLO_REQUEST; tls->renegotiation = TLS_RENEGOTIATION; err = _ntbtls_handshake (tls); if (err) { debug_ret (1, "handshake", err); return err; } return 0; } /* * Renegotiate current connection on client, * or request renegotiation on server */ int ssl_renegotiate (ntbtls_t ssl) { int ret = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* On server, just send the request */ if (!ssl->is_client) { if (ssl->state != TLS_HANDSHAKE_OVER) return gpg_error (GPG_ERR_INV_ARG); return write_hello_request (ssl); } /* * On client, either start the renegotiation process or, * if already in progress, continue the handshake */ if (ssl->renegotiation != TLS_RENEGOTIATION) { if (ssl->state != TLS_HANDSHAKE_OVER) return gpg_error (GPG_ERR_INV_ARG); if ((ret = start_renegotiation (ssl)) != 0) { debug_ret (1, "start_renegotiation", ret); return (ret); } } else { if ((ret = _ntbtls_handshake (ssl)) != 0) { debug_ret (1, "handshake", ret); return (ret); } } return (ret); } /* * Notify the peer that the connection is being closed */ gpg_error_t _ntbtls_close_notify (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "write close_notify"); err = _ntbtls_flush_output (tls); if (err) { debug_ret (1, "flush_output", err); return err; } if (tls->state == TLS_HANDSHAKE_OVER) { err = _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_WARNING, TLS_ALERT_MSG_CLOSE_NOTIFY); if (err) return err; } return err; } /* static void */ /* ssl_key_cert_free (ssl_key_cert * key_cert) */ /* { */ /* ssl_key_cert *cur = key_cert, *next; */ /* while (cur != NULL) */ /* { */ /* next = cur->next; */ /* if (cur->key_own_alloc) */ /* { */ /* pk_free (cur->key); */ /* polarssl_free (cur->key); */ /* } */ /* polarssl_free (cur); */ /* cur = next; */ /* } */ /* } */ /* * Map gcrypt algo number to TLS algo number, return ANON if the algo * is not supported. */ //FIXME: // unsigned char // ssl_sig_from_pk (pk_context * pk) // { // if (pk_can_do (pk, POLARSSL_PK_RSA)) // return (SSL_SIG_RSA); // #endif // #if defined(POLARSSL_ECDSA_C) // if (pk_can_do (pk, POLARSSL_PK_ECDSA)) // return (SSL_SIG_ECDSA); // #endif // return (SSL_SIG_ANON); // } /* * Map TLS signature algorithm number to a gcrypt algo number. */ pk_algo_t _ntbtls_pk_alg_from_sig (unsigned char sig) { switch (sig) { case TLS_SIG_ANON: return 0; case TLS_SIG_RSA: return GCRY_PK_RSA; case TLS_SIG_ECDSA: return GCRY_PK_ECC; } return 0; } /* * Map TLS hash algorithm number to a gcrypt algo number. */ md_algo_t _ntbtls_md_alg_from_hash (unsigned char hash) { switch (hash) { case TLS_HASH_SHA1: return GCRY_MD_SHA1; case TLS_HASH_SHA224: return GCRY_MD_SHA224; case TLS_HASH_SHA256: return GCRY_MD_SHA256; case TLS_HASH_SHA384: return GCRY_MD_SHA384; case TLS_HASH_SHA512: return GCRY_MD_SHA512; } return 0; } /* * Check is a curve proposed by the peer is in our list. * Return 1 if we're willing to use it, 0 otherwise. */ /* int */ /* ssl_curve_is_acceptable (const ntbtls_t ssl, ecp_group_id grp_id) */ /* { */ /* const ecp_group_id *gid; */ /* for (gid = ssl->curve_list; *gid != POLARSSL_ECP_DP_NONE; gid++) */ /* if (*gid == grp_id) */ /* return (1); */ /* return (0); */ /* } */ /* int */ /* ssl_check_cert_usage (const x509_crt * cert, */ /* const ssl_ciphersuite_t * ciphersuite, */ /* int is_client) */ /* { */ /* int usage = 0; */ /* const char *ext_oid; */ /* size_t ext_len; */ /* if (!is_client) */ /* { */ /* /\* Server part of the key exchange *\/ */ /* switch (ciphersuite->key_exchange) */ /* { */ /* case KEY_EXCHANGE_RSA: */ /* case KEY_EXCHANGE_RSA_PSK: */ /* usage = KU_KEY_ENCIPHERMENT; */ /* break; */ /* case KEY_EXCHANGE_DHE_RSA: */ /* case KEY_EXCHANGE_ECDHE_RSA: */ /* case KEY_EXCHANGE_ECDHE_ECDSA: */ /* usage = KU_DIGITAL_SIGNATURE; */ /* break; */ /* case KEY_EXCHANGE_ECDH_RSA: */ /* case KEY_EXCHANGE_ECDH_ECDSA: */ /* usage = KU_KEY_AGREEMENT; */ /* break; */ /* /\* Don't use default: we want warnings when adding new values *\/ */ /* case KEY_EXCHANGE_NONE: */ /* case KEY_EXCHANGE_PSK: */ /* case KEY_EXCHANGE_DHE_PSK: */ /* case KEY_EXCHANGE_ECDHE_PSK: */ /* usage = 0; */ /* break; */ /* } */ /* } */ /* else */ /* { */ /* /\* Client auth: we only implement rsa_sign and ecdsa_sign for now *\/ */ /* usage = KU_DIGITAL_SIGNATURE; */ /* } */ /* if (x509_crt_check_key_usage (cert, usage) != 0) */ /* return (-1); */ /* if (!is_client) */ /* { */ /* ext_oid = OID_SERVER_AUTH; */ /* ext_len = OID_SIZE (OID_SERVER_AUTH); */ /* } */ /* else */ /* { */ /* ext_oid = OID_CLIENT_AUTH; */ /* ext_len = OID_SIZE (OID_CLIENT_AUTH); */ /* } */ /* if (x509_crt_check_extended_key_usage (cert, ext_oid, ext_len) != 0) */ /* return (-1); */ /* return (0); */ /* } */ /* * Receive application data decrypted from the SSL layer */ static gpg_error_t tls_read (ntbtls_t tls, unsigned char *buf, size_t len, size_t *nread) { gpg_error_t err; size_t n; *nread = 0; debug_msg (2, "tls read"); if (tls->state != TLS_HANDSHAKE_OVER) { err = _ntbtls_handshake (tls); if (err) { debug_ret (1, "handshake", err); return err; } } if (!tls->in_offt) { err = _ntbtls_read_record (tls); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) return 0; debug_ret (1, "read_record", err); return err; } if (!tls->in_msglen && tls->in_msgtype == TLS_MSG_APPLICATION_DATA) { /* * OpenSSL sends empty messages to randomize the IV */ err = _ntbtls_read_record (tls); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) return 0; debug_ret (1, "read_record", err); return err; } } if (tls->in_msgtype == TLS_MSG_HANDSHAKE) { debug_msg (1, "received handshake message"); if (tls->is_client && (tls->in_msg[0] != TLS_HS_HELLO_REQUEST || tls->in_hslen != 4)) { debug_msg (1, "handshake received (not HelloRequest)"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } if (tls->disable_renegotiation == TLS_RENEGOTIATION_DISABLED || (tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && (tls->allow_legacy_renegotiation == TLS_LEGACY_NO_RENEGOTIATION))) { debug_msg (3, "ignoring renegotiation, sending alert"); if (tls->minor_ver >= TLS_MINOR_VERSION_1) { err = _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_WARNING, TLS_ALERT_MSG_NO_RENEGOTIATION); if (err) { return err; } } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } } else { err = start_renegotiation (tls); if (err) { debug_ret (1, "start_renegotiation", err); return err; } return gpg_error (GPG_ERR_EAGAIN); } } else if (tls->renegotiation == TLS_RENEGOTIATION_PENDING) { tls->renego_records_seen++; if (tls->renego_max_records >= 0 && tls->renego_records_seen > tls->renego_max_records) { debug_msg (1, "renegotiation requested, " "but not honored by client"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } } else if (tls->in_msgtype != TLS_MSG_APPLICATION_DATA) { debug_msg (1, "bad application data message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } tls->in_offt = tls->in_msg; } if (!len) /* Check only for pending bytes. */ { return tls->in_msglen? 0 : gpg_error (GPG_ERR_EOF); } n = (len < tls->in_msglen) ? len : tls->in_msglen; memcpy (buf, tls->in_offt, n); tls->in_msglen -= n; if (!tls->in_msglen) /* All bytes consumed. */ tls->in_offt = NULL; else /* More data available. */ tls->in_offt += n; debug_msg (2, "tls read ready"); *nread = n; return 0; } /* * Send application data to be encrypted by the TLS layer. */ static gpg_error_t tls_write (ntbtls_t tls, const unsigned char *buf, size_t len, size_t *nwritten) { gpg_error_t err; size_t n; unsigned int max_len = TLS_MAX_CONTENT_LEN; *nwritten = 0; debug_msg (2, "tls write"); if (tls->state != TLS_HANDSHAKE_OVER) { err = _ntbtls_handshake (tls); if (err) { debug_ret (1, "handshake", err); return err; } } /* * Assume mfl_code is correct since it was checked when set */ max_len = mfl_code_to_length[tls->mfl_code]; /* * Check if a smaller max length was negotiated */ if (tls->session_out && mfl_code_to_length[tls->session_out->mfl_code] < max_len) { max_len = mfl_code_to_length[tls->session_out->mfl_code]; } n = (len < max_len) ? len : max_len; if (tls->out_left) { err = _ntbtls_flush_output (tls); if (err) { debug_ret (1, "flush_output", err); return err; } } else { tls->out_msglen = n; tls->out_msgtype = TLS_MSG_APPLICATION_DATA; memcpy (tls->out_msg, buf, n); err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } } debug_msg (2, "tls write ready"); *nwritten = n; return 0; } /* Read handler for estream. */ static gpgrt_ssize_t cookie_read (void *cookie, void *buffer, size_t size) { ntbtls_t tls = cookie; gpg_error_t err; size_t nread; again: err = tls_read (tls, buffer, size, &nread); if (err) { if (gpg_err_code (err) == GPG_ERR_EAGAIN && gpg_err_source (err) == GPG_ERR_SOURCE_TLS) goto again; /* I.e. renegotiation. */ if (!size && gpg_err_code (err) == GPG_ERR_EOF) return -1; /* Nope, no pending bytes. */ debug_ret (1, "tls_read", err); /* Fixme: We shoud extend estream to allow setting extended errors. */ gpg_err_set_errno (EIO); return -1; } else if (!size) nread = 0; /* Yep, there are pending bytes. */ return nread; } /* Write handler for estream. */ static gpgrt_ssize_t cookie_write (void *cookie, const void *buffer_arg, size_t size) { ntbtls_t tls = cookie; const char *buffer = buffer_arg; gpg_error_t err; size_t nwritten = 0; int nleft = size; again: while (nleft > 0) { err = tls_write (tls, buffer, nleft, &nwritten); if (err) { if (gpg_err_code (err) == GPG_ERR_EAGAIN && gpg_err_source (err) == GPG_ERR_SOURCE_TLS) goto again; /* I.e. renegotiation. */ debug_ret (1, "tls_write", err); gpg_err_set_errno (EIO); return -1; } nleft -= nwritten; buffer += nwritten; } return nwritten; } static gpgrt_cookie_io_functions_t cookie_functions = { cookie_read, cookie_write, NULL, NULL }; /* Return the two streams used to read and write the plaintext. The streams are valid as long as TLS is valid and may thus not be used after TLS has been destroyed. Note: After adding a "fullduplex" feature to estream we will allow to pass NULL for r_writefp to make use of that feature. */ gpg_error_t _ntbtls_get_stream (ntbtls_t tls, estream_t *r_readfp, estream_t *r_writefp) { gpg_error_t err; if (!tls || !r_readfp || !r_writefp) return gpg_error (GPG_ERR_INV_ARG); *r_readfp = NULL; *r_writefp = NULL; if ((!tls->readfp ^ !tls->writefp)) return gpg_error (GPG_ERR_INTERNAL); if (!tls->readfp) { if ((tls->flags & NTBTLS_SAMETRHEAD)) tls->readfp = es_fopencookie (tls, "r,samethread", cookie_functions); else tls->readfp = es_fopencookie (tls, "r", cookie_functions); if (!tls->readfp) { err = gpg_error_from_syserror (); return err; } } if (!tls->writefp) { if ((tls->flags & NTBTLS_SAMETRHEAD)) tls->writefp = es_fopencookie (tls, "w,samethread", cookie_functions); else tls->writefp = es_fopencookie (tls, "w", cookie_functions); if (!tls->writefp) { err = gpg_error_from_syserror (); es_fclose (tls->readfp); tls->readfp = NULL; return err; } } *r_readfp = tls->readfp; *r_writefp = tls->writefp; return 0; } diff --git a/src/visibility.c b/src/visibility.c index 7f0e50e..7929662 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -1,124 +1,132 @@ /* visibility.c - Public API * Copyright (C) 2014 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #define _NTBTLS_INCLUDED_BY_VISIBILITY_C #include "visibility.h" const char * ntbtls_check_version (const char *req_version) { return _ntbtls_check_version (req_version); } void ntbtls_set_debug (int level, const char *prefix, gpgrt_stream_t stream) { _ntbtls_set_debug (level, prefix, stream); } void ntbtls_set_log_handler (ntbtls_log_handler_t cb, void *cb_value) { _ntbtls_set_log_handler (cb, cb_value); } gpg_error_t ntbtls_new (ntbtls_t *r_tls, unsigned int flags) { return _ntbtls_new (r_tls, flags); } /* Check that TLS is valid. FILE and LINE are printed in case of a * failure. Returns True on failure. This should be called using the * corresponding macro. */ gpg_error_t _ntbtls_check_context (ntbtls_t tls, const char *file, int line) { if (!tls || tls->magic != NTBTLS_CONTEXT_MAGIC) { _ntbtls_debug_bug (file, line); return gpg_error (GPG_ERR_BUG); } return 0; } void ntbtls_release (ntbtls_t tls) { _ntbtls_release (tls); } gpg_error_t ntbtls_set_transport (ntbtls_t tls, gpgrt_stream_t inbound, gpgrt_stream_t outbound) { return _ntbtls_set_transport (tls, inbound, outbound); } gpg_error_t ntbtls_get_stream (ntbtls_t tls, gpgrt_stream_t *r_readfp, gpgrt_stream_t *r_writefp) { return _ntbtls_get_stream (tls, r_readfp, r_writefp); } gpg_error_t ntbtls_set_hostname (ntbtls_t tls, const char *hostname) { return _ntbtls_set_hostname (tls, hostname); } const char * ntbtls_get_hostname (ntbtls_t tls) { return _ntbtls_get_hostname (tls); } gpg_error_t ntbtls_handshake (ntbtls_t tls) { return _ntbtls_handshake (tls); } +const char * +ntbtls_get_last_alert (ntbtls_t tls, + unsigned int *r_level, unsigned int *r_type) +{ + return _ntbtls_get_last_alert (tls, r_level, r_type); +} + + gpg_error_t ntbtls_set_verify_cb (ntbtls_t tls, ntbtls_verify_cb_t cb, void *cb_value) { return _ntbtls_set_verify_cb (tls, cb, cb_value); } ksba_cert_t ntbtls_x509_get_peer_cert (ntbtls_t tls, int idx) { return _ntbtls_x509_get_peer_cert (tls, idx); } diff --git a/src/visibility.h b/src/visibility.h index d411126..8bedeed 100644 --- a/src/visibility.h +++ b/src/visibility.h @@ -1,80 +1,82 @@ /* visibility.c - Public API * Copyright (C) 2014 g10 Code GmbH * * This file is part of NTBTLS * * NTBTLS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * NTBTLS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef NTBTLS_VISIBILITY_H #define NTBTLS_VISIBILITY_H #ifdef _NTBTLS_INCLUDED_BY_VISIBILITY_C # include "ntbtls-int.h" #endif /* Our use of the ELF visibility feature works by passing -fvisibiliy=hidden on the command line and by explicitly marking all exported functions as visible. NOTE: When adding new functions, please make sure to add them to libntbtls.vers and libntbtls.def as well. */ #ifdef _NTBTLS_INCLUDED_BY_VISIBILITY_C /* A macro to flag a function as visible. */ #ifdef NTBTLS_USE_VISIBILITY # define MARK_VISIBLE(name) \ extern __typeof__ (name) name __attribute__ ((visibility("default"))); #else # define MARK_VISIBLE(name) /* */ #endif MARK_VISIBLE (ntbtls_check_version) MARK_VISIBLE (ntbtls_set_debug) MARK_VISIBLE (ntbtls_set_log_handler) MARK_VISIBLE (ntbtls_new) MARK_VISIBLE (_ntbtls_check_context) MARK_VISIBLE (ntbtls_release) MARK_VISIBLE (ntbtls_set_transport) MARK_VISIBLE (ntbtls_get_stream) MARK_VISIBLE (ntbtls_set_hostname) MARK_VISIBLE (ntbtls_get_hostname) MARK_VISIBLE (ntbtls_handshake) MARK_VISIBLE (ntbtls_set_verify_cb) MARK_VISIBLE (ntbtls_x509_get_peer_cert) +MARK_VISIBLE (ntbtls_get_last_alert) #undef MARK_VISIBLE #else /*!_NTBTLS_INCLUDED_BY_VISIBILITY_C*/ /* To avoid accidental use of the public functions inside ntbtls, we redefine them to catch such errors. */ #define ntbtls_check_version _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_set_debug _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_set_log_handler _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_new _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_released _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_set_transport _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_get_stream _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_set_hostname _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_get_hostname _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_handshake _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_set_verify_cb _ntbtls_USE_THE_UNDERSCORED_FUNCTION #define ntbtls_x509_get_peer_cert _ntbtls_USE_THE_UNDERSCORED_FUNCTION +#define ntbtls_get_last_alert _ntbtls_USE_THE_UNDERSCORED_FUNCTION #endif /*!_NTBTLS_INCLUDED_BY_VISIBILITY_C*/ #endif /*NTBTLS_VISIBILITY_H*/