1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <uv.h>
#include <gnutls/gnutls.h>
#include <libknot/packet/pkt.h>
#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);
|