diff options
Diffstat (limited to '')
-rw-r--r-- | src/include/tls-h | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/src/include/tls-h b/src/include/tls-h new file mode 100644 index 0000000..7bb994b --- /dev/null +++ b/src/include/tls-h @@ -0,0 +1,448 @@ +/* + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#ifndef FR_TLS_H +#define FR_TLS_H + +#ifdef WITH_TLS +/** + * $Id$ + * + * @file tls.h + * @brief Structures and prototypes for TLS wrappers + * + * @copyright 2010 Network RADIUS SARL <info@networkradius.com> + */ + +RCSIDH(tls_h, "$Id$") + +#include <freeradius-devel/conffile.h> + +/* + * For RH 9, which apparently needs this. + */ +#ifndef OPENSSL_NO_KRB5 +# define OPENSSL_NO_KRB5 +#endif +#include <openssl/err.h> +#ifdef HAVE_OPENSSL_ENGINE_H +# include <openssl/engine.h> +#endif +#include <openssl/ssl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct fr_tls_server_conf_t fr_tls_server_conf_t; + +typedef enum { + FR_TLS_INVALID = 0, //!< Invalid, don't reply. + FR_TLS_REQUEST, //!< Request, ok to send, invalid to receive. + FR_TLS_RESPONSE, //!< Response, ok to receive, invalid to send. + FR_TLS_SUCCESS, //!< Success, send success. + FR_TLS_FAIL, //!< Fail, send fail. + FR_TLS_NOOP, //!< Noop, continue. + + FR_TLS_START, //!< Start, ok to send, invalid to receive. + FR_TLS_OK, //!< Ok, continue. + FR_TLS_ACK, //!< Acknowledge, continue. + FR_TLS_FIRST_FRAGMENT, //!< First fragment. + FR_TLS_MORE_FRAGMENTS, //!< More fragments, to send/receive. + FR_TLS_LENGTH_INCLUDED, //!< Length included. + FR_TLS_MORE_FRAGMENTS_WITH_LENGTH, //!< More fragments with length. + FR_TLS_HANDLED //!< TLS code has handled it. +} fr_tls_status_t; +extern FR_NAME_NUMBER const fr_tls_status_table[]; + +#define MAX_RECORD_SIZE 16384 + +/* + * A single TLS record may be up to 16384 octets in length, but a + * TLS message may span multiple TLS records, and a TLS + * certificate message may in principle be as long as 16MB. + * + * However, note that in order to protect against reassembly + * lockup and denial of service attacks, it may be desirable for + * an implementation to set a maximum size for one such group of + * TLS messages. + * + * The TLS Message Length field is four octets, and provides the + * total length of the TLS message or set of messages that is + * being fragmented; this simplifies buffer allocation. + */ + +/* + * FIXME: Dynamic allocation of buffer to overcome MAX_RECORD_SIZE overflows. + * or configure TLS not to exceed MAX_RECORD_SIZE. + */ +typedef struct _record_t { + uint8_t data[MAX_RECORD_SIZE]; + size_t used; +} record_t; + +typedef struct _tls_info_t { + int origin; // 0 - received (from peer), 1 - sending (to peer) + int content_type; + uint8_t handshake_type; + uint8_t alert_level; + uint8_t alert_description; + bool initialized; + + char info_description[256]; + size_t record_len; +} tls_info_t; + +#if OPENSSL_VERSION_NUMBER < 0x10001000L +#define ssl_session ssl->session +#else +#define ssl_session session +#endif + +/** Contains EAP-REQUEST specific data (ie FR_TLS_DATA(fragment), EAPTLS-ALERT, EAPTLS-REQUEST ...) + * + * The tls_session_t Structure gets stored as opaque in eap_handler_t + */ +typedef struct _tls_session_t { + SSL_CTX *ctx; + SSL *ssl; +#if OPENSSL_VERSION_NUMBER >= 0x10001000L + SSL_SESSION *session; +#endif + tls_info_t info; + + BIO *into_ssl; + BIO *from_ssl; + record_t clean_in; //!< Data that needs to be sent but only after it is soiled. + record_t clean_out; //!< Data that is cleaned after receiving. + record_t dirty_in; //!< Data EAP server receives. + record_t dirty_out; //!< Data EAP server sends. + + void (*record_init)(record_t *buf); + void (*record_close)(record_t *buf); + unsigned int (*record_plus)(record_t *buf, void const *ptr, unsigned int size); + unsigned int (*record_minus)(record_t *buf, void *ptr, unsigned int size); + + bool invalid_hb_used; //!< Whether heartbleed attack was detected. + bool connected; //!< whether the outgoing socket is connected + bool is_init_finished; //!< whether or not init is finished + bool client_cert_ok; //!< whether or not we validated the client certificate + bool authentication_success; //!< whether or not the user was authenticated (cert or PW) + bool quick_session_tickets; //!< for EAP-TLS. + + /* + * Framed-MTU attribute in RADIUS, if present, can also be used to set this + */ + size_t mtu; //!< Current fragment size transmitted. + size_t tls_msg_len; //!< Actual/Total TLS message length. + bool fragment; //!< Flag, In fragment mode or not. + bool length_flag; //!< A flag to include length in every TLS Data/Alert packet. + //!< If set to no then only the first fragment contains length. + int peap_flag; + + size_t tls_record_in_total_len; //!< How long the peer indicated the complete tls record + //!< would be. + size_t tls_record_in_recvd_len; //!< How much of the record we've received so far. + + /* + * Used by TTLS & PEAP to keep track of other per-session data. + */ + void *opaque; + void (*free_opaque)(void *opaque); + + char const *label; + bool allow_session_resumption; //!< Whether session resumption is allowed. + bool session_not_resumed; //!< Whether our session was not resumed. + + fr_tls_server_conf_t const *conf; //! for better complaints +} tls_session_t; + +/* + * RFC 2716, Section 4.2: + * + * Flags + * + * 0 1 2 3 4 5 6 7 8 + * +-+-+-+-+-+-+-+-+ + * |L M S R R R R R| + * +-+-+-+-+-+-+-+-+ + * + * L = Length included + * M = More fragments + * S = EAP-TLS start + * R = Reserved + */ +#define TLS_START(x) (((x) & 0x20) != 0) +#define TLS_MORE_FRAGMENTS(x) (((x) & 0x40) != 0) +#define TLS_LENGTH_INCLUDED(x) (((x) & 0x80) != 0) + +#define TLS_CHANGE_CIPHER_SPEC(x) (((x) & 0x0014) == 0x0014) +#define TLS_ALERT(x) (((x) & 0x0015) == 0x0015) +#define TLS_HANDSHAKE(x) (((x) & 0x0016) == 0x0016) + +#define SET_START(x) ((x) | (0x20)) +#define SET_MORE_FRAGMENTS(x) ((x) | (0x40)) +#define SET_LENGTH_INCLUDED(x) ((x) | (0x80)) + +/* + * Following enums from rfc2246 + * + * Hmm... since we dpeend on OpenSSL, it would be smarter to + * use the OpenSSL names for these. + */ +enum ContentType { + change_cipher_spec = 20, + alert = 21, + handshake = 22, + application_data = 23 +}; + +enum AlertLevel { + warning = 1, + fatal = 2 +}; + +enum AlertDescription { + close_notify = 0, + unexpected_message = 10, + bad_record_mac = 20, + decryption_failed = 21, + record_overflow = 22, + decompression_failure = 30, + handshake_failure = 40, + bad_certificate = 42, + unsupported_certificate = 43, + certificate_revoked = 44, + certificate_expired = 45, + certificate_unknown = 46, + illegal_parameter = 47, + unknown_ca = 48, + access_denied = 49, + decode_error = 50, + decrypt_error = 51, + export_restriction = 60, + protocol_version = 70, + insufficient_security = 71, + internal_error = 80, + user_canceled = 90, + no_renegotiation = 100 +}; + +enum HandshakeType { + hello_request = 0, + client_hello = 1, + server_hello = 2, + certificate = 11, + server_key_exchange = 12, + certificate_request = 13, + server_hello_done = 14, + certificate_verify = 15, + client_key_exchange = 16, + handshake_finished = 20 +}; + + +/* + * From rfc + Flags + + 0 1 2 3 4 5 6 7 8 + +-+-+-+-+-+-+-+-+ + |L M S R R R R R| + +-+-+-+-+-+-+-+-+ + + L = Length included + M = More fragments + S = EAP-TLS start + R = Reserved + + The L bit (length included) is set to indicate the presence of the + four octet TLS Message Length field, and MUST be set for the first + fragment of a fragmented TLS message or set of messages. The M bit + (more fragments) is set on all but the last fragment. The S bit + (EAP-TLS start) is set in an EAP-TLS Start message. This + differentiates the EAP-TLS Start message from a fragment + acknowledgement. + + TLS Message Length + + The TLS Message Length field is four octets, and is present only + if the L bit is set. This field provides the total length of the + TLS message or set of messages that is being fragmented. + + TLS data + + The TLS data consists of the encapsulated TLS packet in TLS record + format. + * + * The data structures present here + * maps only to the typedata in the EAP packet + * + * Based on the L bit flag, first 4 bytes of data indicate the length + */ + +/* Callbacks */ +int cbtls_password(char *buf, int num, int rwflag, void *userdata); +void cbtls_info(SSL const *s, int where, int ret); +void cbtls_msg(int write_p, int msg_version, int content_type, void const *buf, size_t len, SSL *ssl, + void *arg); +int cbtls_verify(int ok, X509_STORE_CTX *ctx); + +/* threads.c */ +int tls_mutexes_init(void); + +/* TLS */ +int tls_global_init(bool spawn_flag, bool check); +#ifdef ENABLE_OPENSSL_VERSION_CHECK +int tls_global_version_check(char const *acknowledged); +#endif + +int tls_error_log(REQUEST *request, char const *msg, ...) CC_HINT(format (printf, 2, 3)); +int tls_error_io_log(REQUEST *request, tls_session_t *session, int ret, char const *msg, ...) + CC_HINT(format (printf, 4, 5)); + +void tls_global_cleanup(void); +tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQUEST *request, bool client_cert, bool allow_tls13); +tls_session_t *tls_new_client_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, int fd, VALUE_PAIR **certs); +fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs); +fr_tls_server_conf_t *tls_client_conf_parse(CONF_SECTION *cs); +fr_tls_server_conf_t *tls_server_conf_alloc(TALLOC_CTX *ctx); +SSL_CTX *tls_init_ctx(fr_tls_server_conf_t *conf, int client, char const *chain_file, char const *private_key_file); +int tls_handshake_recv(REQUEST *, tls_session_t *ssn); +int tls_handshake_send(REQUEST *, tls_session_t *ssn); +void tls_session_information(tls_session_t *ssn); +void tls_session_id(SSL_SESSION *ssn, char *buffer, size_t bufsize); +X509_STORE *fr_init_x509_store(fr_tls_server_conf_t *conf); + +/* + * Low-level TLS stuff + */ +int tls_success(tls_session_t *ssn, REQUEST *request); +void tls_fail(tls_session_t *ssn); +fr_tls_status_t tls_ack_handler(tls_session_t *tls_session, REQUEST *request); +fr_tls_status_t tls_application_data(tls_session_t *ssn, REQUEST *request); + +#define FR_TLS_EX_INDEX_HANDLER (10) +#define FR_TLS_EX_INDEX_CONF (11) +#define FR_TLS_EX_INDEX_REQUEST (12) +#define FR_TLS_EX_INDEX_IDENTITY (13) +#define FR_TLS_EX_INDEX_STORE (14) +#define FR_TLS_EX_INDEX_SSN (15) +#define FR_TLS_EX_INDEX_TALLOC (16) +#define FR_TLS_EX_INDEX_FIX_CERT_ORDER (17) + +extern int fr_tls_ex_index_certs; +extern int fr_tls_ex_index_vps; + +/* configured values goes right here */ +struct fr_tls_server_conf_t { + SSL_CTX *ctx; + CONF_SECTION *cs; + + char const *private_key_password; + char const *private_key_file; + char const *certificate_file; + char const *random_file; + char const *ca_path; + char const *ca_file; + char const *dh_file; + char const *rsa_file; + uint32_t verify_depth; + bool file_type; + bool include_length; + bool auto_chain; + bool disable_single_dh_use; + bool disable_tlsv1; + bool disable_tlsv1_1; + bool disable_tlsv1_2; + bool disallow_untrusted; //!< allow untrusted CAs to issue client certificates + + int min_version; + int max_version; + + char const *tls_min_version; + char const *tls_max_version; + + /* + * Always < 4096 (due to radius limit), 0 by default = 1024 + */ + uint32_t fragment_size; + bool check_crl; + bool check_all_crl; + bool allow_expired_crl; + uint32_t ca_path_reload_interval; + uint32_t ca_path_last_reload; + X509_STORE *old_x509_store; + char const *check_cert_cn; + char const *cipher_list; + bool cipher_server_preference; + char const *check_cert_issuer; + char const *sigalgs_list; + + bool session_cache_enable; + uint32_t session_lifetime; + uint32_t session_cache_size; + char const *session_id_name; + char const *session_cache_path; + char const *session_cache_server; + fr_hash_table_t *cache_ht; + char session_context_id[SSL_MAX_SSL_SESSION_ID_LENGTH]; + + bool verify_skip_if_ocsp_ok; + char const *verify_tmp_dir; + char const *verify_client_cert_cmd; + bool require_client_cert; + + bool fix_cert_order; + + pthread_mutex_t mutex; + +#ifdef HAVE_OPENSSL_OCSP_H + /* + * OCSP Configuration + */ + bool ocsp_enable; + bool ocsp_override_url; + char const *ocsp_url; + bool ocsp_use_nonce; + X509_STORE *ocsp_store; + uint32_t ocsp_timeout; + bool ocsp_softfail; +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#ifndef OPENSSL_NO_ECDH + char const *ecdh_curve; +#endif +#endif + +#ifdef PSK_MAX_IDENTITY_LEN + char const *psk_identity; + char const *psk_password; + char const *psk_query; +#endif + + char const *realm_dir; + fr_hash_table_t *realms; + + char const *client_hostname; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* WITH_TLS */ +#endif /* FR_TLS_H */ |