/* Copyright (C) 2016 American Civil Liberties Union (ACLU)
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 3 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, see .
*/
#pragma once
#include
#include
#include
#include "lib/defines.h"
#include "lib/generic/array.h"
#include "lib/generic/map.h"
#define MAX_TLS_PADDING KR_EDNS_PAYLOAD
#define TLS_MAX_UNCORK_RETRIES 100
/* rfc 5476, 7.3 - handshake Protocol overview
* https://tools.ietf.org/html/rfc5246#page-33
* Message flow for a full handshake (only mandatory messages)
* ClientHello -------->
ServerHello
<-------- ServerHelloDone
ClientKeyExchange
Finished -------->
<-------- Finished
*
* See also https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/
* So it takes 2 RTT.
* As we use session tickets, there are additional messages, add one RTT mode.
*/
#define TLS_MAX_HANDSHAKE_TIME (KR_CONN_RTT_MAX * 3)
/** Transport session (opaque). */
struct session;
struct tls_ctx_t;
struct tls_client_ctx_t;
struct tls_credentials {
int count;
char *tls_cert;
char *tls_key;
gnutls_certificate_credentials_t credentials;
time_t valid_until;
char *ephemeral_servicename;
};
struct tls_client_paramlist_entry {
array_t(const char *) ca_files;
array_t(const char *) hostnames;
array_t(const char *) pins;
gnutls_certificate_credentials_t credentials;
gnutls_datum_t session_data;
uint32_t refs;
};
struct worker_ctx;
struct qr_task;
typedef enum tls_client_hs_state {
TLS_HS_NOT_STARTED = 0,
TLS_HS_IN_PROGRESS,
TLS_HS_DONE,
TLS_HS_CLOSING,
TLS_HS_LAST
} tls_hs_state_t;
typedef int (*tls_handshake_cb) (struct session *session, int status);
typedef enum tls_client_param {
TLS_CLIENT_PARAM_NONE = 0,
TLS_CLIENT_PARAM_PIN,
TLS_CLIENT_PARAM_HOSTNAME,
TLS_CLIENT_PARAM_CA,
} tls_client_param_t;
struct tls_common_ctx {
bool client_side;
gnutls_session_t tls_session;
tls_hs_state_t handshake_state;
struct session *session;
/* for reading from the network */
const uint8_t *buf;
ssize_t nread;
ssize_t consumed;
uint8_t recv_buf[16384];
tls_handshake_cb handshake_cb;
struct worker_ctx *worker;
size_t write_queue_size;
};
struct tls_ctx_t {
/*
* Since pointer to tls_ctx_t needs to be casted
* to tls_ctx_common in some functions,
* this field must be always at first position
*/
struct tls_common_ctx c;
struct tls_credentials *credentials;
};
struct tls_client_ctx_t {
/*
* Since pointer to tls_client_ctx_t needs to be casted
* to tls_ctx_common in some functions,
* this field must be always at first position
*/
struct tls_common_ctx c;
struct tls_client_paramlist_entry *params;
};
/*! Create an empty TLS context in query context */
struct tls_ctx_t* tls_new(struct worker_ctx *worker);
/*! Close a TLS context (call gnutls_bye()) */
void tls_close(struct tls_common_ctx *ctx);
/*! Release a TLS context */
void tls_free(struct tls_ctx_t* tls);
/*! Push new data to TLS context for sending */
int tls_write(uv_write_t *req, uv_handle_t* handle, knot_pkt_t * pkt, uv_write_cb cb);
/*! Unwrap incoming data from a TLS stream and pass them to TCP session.
* @return the number of newly-completed requests (>=0) or an error code
*/
ssize_t tls_process_input_data(struct session *s, const uint8_t *buf, ssize_t nread);
/*! Set TLS certificate and key from files. */
int tls_certificate_set(struct network *net, const char *tls_cert, const char *tls_key);
/*! Borrow TLS credentials for context. */
struct tls_credentials *tls_credentials_reserve(struct tls_credentials *tls_credentials);
/*! Release TLS credentials for context (decrements refcount or frees). */
int tls_credentials_release(struct tls_credentials *tls_credentials);
/*! Free TLS credentials, must not be called if it holds positive refcount. */
void tls_credentials_free(struct tls_credentials *tls_credentials);
/*! Log DNS-over-TLS OOB key-pin form of current credentials:
* https://tools.ietf.org/html/rfc7858#appendix-A */
void tls_credentials_log_pins(struct tls_credentials *tls_credentials);
/*! Generate new ephemeral TLS credentials. */
struct tls_credentials * tls_get_ephemeral_credentials(struct engine *engine);
/*! Get TLS handshake state. */
tls_hs_state_t tls_get_hs_state(const struct tls_common_ctx *ctx);
/*! Set TLS handshake state. */
int tls_set_hs_state(struct tls_common_ctx *ctx, tls_hs_state_t state);
/*! Find TLS parameters for given address. Attempt opportunistic upgrade for port 53 to 853,
* if the address is configured with a working DoT on port 853.
*/
struct tls_client_paramlist_entry *tls_client_try_upgrade(map_t *tls_client_paramlist,
const struct sockaddr *addr);
/*! Clear (remove) TLS parameters for given address. */
int tls_client_params_clear(map_t *tls_client_paramlist, const char *addr, uint16_t port);
/*! Set TLS authentication parameters for given address.
* Note: hostnames must be imported before ca files,
* otherwise ca files will not be imported at all.
*/
int tls_client_params_set(map_t *tls_client_paramlist,
const char *addr, uint16_t port,
const char *param, tls_client_param_t param_type);
/*! Free TLS authentication parameters. */
int tls_client_params_free(map_t *tls_client_paramlist);
/*! Allocate new client TLS context */
struct tls_client_ctx_t *tls_client_ctx_new(struct tls_client_paramlist_entry *entry,
struct worker_ctx *worker);
/*! Free client TLS context */
void tls_client_ctx_free(struct tls_client_ctx_t *ctx);
int tls_client_connect_start(struct tls_client_ctx_t *client_ctx,
struct session *session,
tls_handshake_cb handshake_cb);
int tls_client_ctx_set_session(struct tls_client_ctx_t *ctx, struct session *session);
/* Session tickets, server side. Implementation in ./tls_session_ticket-srv.c */
/*! Opaque struct used by tls_session_ticket_* functions. */
struct tls_session_ticket_ctx;
/*! Suggested maximum reasonable secret length. */
#define TLS_SESSION_TICKET_SECRET_MAX_LEN 1024
/*! Create a session ticket context and initialize it (secret gets copied inside).
*
* Passing zero-length secret implies using a random key, i.e. not synchronized
* between multiple instances.
*
* Beware that knowledge of the secret (if nonempty) breaks forward secrecy,
* so you should rotate the secret regularly and securely erase all past secrets.
* With TLS < 1.3 it's probably too risky to set nonempty secret.
*/
struct tls_session_ticket_ctx * tls_session_ticket_ctx_create(
uv_loop_t *loop, const char *secret, size_t secret_len);
/*! Try to enable session tickets for a server session. */
void tls_session_ticket_enable(struct tls_session_ticket_ctx *ctx, gnutls_session_t session);
/*! Free all resources of the session ticket context. NULL is accepted as well. */
void tls_session_ticket_ctx_destroy(struct tls_session_ticket_ctx *ctx);