diff options
author | jsing <> | 2018-02-10 04:41:24 +0000 |
---|---|---|
committer | jsing <> | 2018-02-10 04:41:24 +0000 |
commit | ad2580ae7b71760c38ec88f34f360d5f1e6b3f13 (patch) | |
tree | d414866dbbe43d007a4873fb2dc7e6cb637f7bce /src | |
parent | 87264e9d7a6c2a965876fcf5e4b3dc46470e2562 (diff) | |
download | openbsd-ad2580ae7b71760c38ec88f34f360d5f1e6b3f13.tar.gz openbsd-ad2580ae7b71760c38ec88f34f360d5f1e6b3f13.tar.bz2 openbsd-ad2580ae7b71760c38ec88f34f360d5f1e6b3f13.zip |
Add support to libtls for client-side TLS session resumption.
A libtls client can specify a session file descriptor (a regular file
with appropriate ownership and permissions) and libtls will manage reading
and writing of session data across TLS handshakes.
Discussed at length with deraadt@ and tedu@.
Rides previous minor bump.
ok beck@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libtls/Symbols.list | 2 | ||||
-rw-r--r-- | src/lib/libtls/tls.h | 4 | ||||
-rw-r--r-- | src/lib/libtls/tls_client.c | 128 | ||||
-rw-r--r-- | src/lib/libtls/tls_config.c | 41 | ||||
-rw-r--r-- | src/lib/libtls/tls_conninfo.c | 21 | ||||
-rw-r--r-- | src/lib/libtls/tls_internal.h | 4 |
6 files changed, 195 insertions, 5 deletions
diff --git a/src/lib/libtls/Symbols.list b/src/lib/libtls/Symbols.list index 1e7538cfd4..923924fc40 100644 --- a/src/lib/libtls/Symbols.list +++ b/src/lib/libtls/Symbols.list | |||
@@ -42,6 +42,7 @@ tls_config_set_ocsp_staple_file | |||
42 | tls_config_set_protocols | 42 | tls_config_set_protocols |
43 | tls_config_set_session_id | 43 | tls_config_set_session_id |
44 | tls_config_set_session_lifetime | 44 | tls_config_set_session_lifetime |
45 | tls_config_set_session_fd | ||
45 | tls_config_set_verify_depth | 46 | tls_config_set_verify_depth |
46 | tls_config_skip_private_key_check | 47 | tls_config_skip_private_key_check |
47 | tls_config_verify | 48 | tls_config_verify |
@@ -51,6 +52,7 @@ tls_configure | |||
51 | tls_conn_alpn_selected | 52 | tls_conn_alpn_selected |
52 | tls_conn_cipher | 53 | tls_conn_cipher |
53 | tls_conn_servername | 54 | tls_conn_servername |
55 | tls_conn_session_resumed | ||
54 | tls_conn_version | 56 | tls_conn_version |
55 | tls_connect | 57 | tls_connect |
56 | tls_connect_cbs | 58 | tls_connect_cbs |
diff --git a/src/lib/libtls/tls.h b/src/lib/libtls/tls.h index cc8627f2af..8d66c2fbaa 100644 --- a/src/lib/libtls/tls.h +++ b/src/lib/libtls/tls.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls.h,v 1.51 2017/08/10 18:18:30 jsing Exp $ */ | 1 | /* $OpenBSD: tls.h,v 1.52 2018/02/10 04:41:24 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -128,6 +128,7 @@ int tls_config_set_ocsp_staple_mem(struct tls_config *_config, | |||
128 | int tls_config_set_ocsp_staple_file(struct tls_config *_config, | 128 | int tls_config_set_ocsp_staple_file(struct tls_config *_config, |
129 | const char *_staple_file); | 129 | const char *_staple_file); |
130 | int tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols); | 130 | int tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols); |
131 | int tls_config_set_session_fd(struct tls_config *_config, int _session_fd); | ||
131 | int tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth); | 132 | int tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth); |
132 | 133 | ||
133 | void tls_config_prefer_ciphers_client(struct tls_config *_config); | 134 | void tls_config_prefer_ciphers_client(struct tls_config *_config); |
@@ -188,6 +189,7 @@ const uint8_t *tls_peer_cert_chain_pem(struct tls *_ctx, size_t *_len); | |||
188 | const char *tls_conn_alpn_selected(struct tls *_ctx); | 189 | const char *tls_conn_alpn_selected(struct tls *_ctx); |
189 | const char *tls_conn_cipher(struct tls *_ctx); | 190 | const char *tls_conn_cipher(struct tls *_ctx); |
190 | const char *tls_conn_servername(struct tls *_ctx); | 191 | const char *tls_conn_servername(struct tls *_ctx); |
192 | int tls_conn_session_resumed(struct tls *_ctx); | ||
191 | const char *tls_conn_version(struct tls *_ctx); | 193 | const char *tls_conn_version(struct tls *_ctx); |
192 | 194 | ||
193 | uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password); | 195 | uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password); |
diff --git a/src/lib/libtls/tls_client.c b/src/lib/libtls/tls_client.c index c79f462a3a..14c716fa17 100644 --- a/src/lib/libtls/tls_client.c +++ b/src/lib/libtls/tls_client.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_client.c,v 1.43 2017/08/10 18:18:30 jsing Exp $ */ | 1 | /* $OpenBSD: tls_client.c,v 1.44 2018/02/10 04:41:24 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -17,10 +17,12 @@ | |||
17 | 17 | ||
18 | #include <sys/types.h> | 18 | #include <sys/types.h> |
19 | #include <sys/socket.h> | 19 | #include <sys/socket.h> |
20 | #include <sys/stat.h> | ||
20 | 21 | ||
21 | #include <arpa/inet.h> | 22 | #include <arpa/inet.h> |
22 | #include <netinet/in.h> | 23 | #include <netinet/in.h> |
23 | 24 | ||
25 | #include <limits.h> | ||
24 | #include <netdb.h> | 26 | #include <netdb.h> |
25 | #include <stdlib.h> | 27 | #include <stdlib.h> |
26 | #include <unistd.h> | 28 | #include <unistd.h> |
@@ -159,6 +161,118 @@ tls_connect_servername(struct tls *ctx, const char *host, const char *port, | |||
159 | } | 161 | } |
160 | 162 | ||
161 | static int | 163 | static int |
164 | tls_client_read_session(struct tls *ctx) | ||
165 | { | ||
166 | int sfd = ctx->config->session_fd; | ||
167 | uint8_t *session = NULL; | ||
168 | size_t session_len = 0; | ||
169 | SSL_SESSION *ss = NULL; | ||
170 | BIO *bio = NULL; | ||
171 | struct stat sb; | ||
172 | ssize_t n; | ||
173 | int rv = -1; | ||
174 | |||
175 | if (fstat(sfd, &sb) == -1) { | ||
176 | tls_set_error(ctx, "failed to stat session file"); | ||
177 | goto err; | ||
178 | } | ||
179 | if (sb.st_size < 0 || sb.st_size > INT_MAX) { | ||
180 | tls_set_errorx(ctx, "invalid session file size"); | ||
181 | goto err; | ||
182 | } | ||
183 | session_len = (size_t)sb.st_size; | ||
184 | |||
185 | /* A zero size file means that we do not yet have a valid session. */ | ||
186 | if (session_len == 0) | ||
187 | goto done; | ||
188 | |||
189 | if ((session = malloc(session_len)) == NULL) | ||
190 | goto err; | ||
191 | |||
192 | n = pread(sfd, session, session_len, 0); | ||
193 | if (n < 0 || (size_t)n != session_len) { | ||
194 | tls_set_error(ctx, "failed to read session file"); | ||
195 | goto err; | ||
196 | } | ||
197 | if ((bio = BIO_new_mem_buf(session, session_len)) == NULL) | ||
198 | goto err; | ||
199 | if ((ss = PEM_read_bio_SSL_SESSION(bio, NULL, tls_password_cb, | ||
200 | NULL)) == NULL) { | ||
201 | tls_set_errorx(ctx, "failed to parse session"); | ||
202 | goto err; | ||
203 | } | ||
204 | |||
205 | if (SSL_set_session(ctx->ssl_conn, ss) != 1) { | ||
206 | tls_set_errorx(ctx, "failed to set session"); | ||
207 | goto err; | ||
208 | } | ||
209 | |||
210 | done: | ||
211 | rv = 0; | ||
212 | |||
213 | err: | ||
214 | freezero(session, session_len); | ||
215 | SSL_SESSION_free(ss); | ||
216 | BIO_free(bio); | ||
217 | |||
218 | return rv; | ||
219 | } | ||
220 | |||
221 | static int | ||
222 | tls_client_write_session(struct tls *ctx) | ||
223 | { | ||
224 | int sfd = ctx->config->session_fd; | ||
225 | SSL_SESSION *ss = NULL; | ||
226 | BIO *bio = NULL; | ||
227 | long data_len; | ||
228 | char *data; | ||
229 | off_t offset; | ||
230 | size_t len; | ||
231 | ssize_t n; | ||
232 | int rv = -1; | ||
233 | |||
234 | if ((ss = SSL_get1_session(ctx->ssl_conn)) == NULL) { | ||
235 | if (ftruncate(sfd, 0) == -1) { | ||
236 | tls_set_error(ctx, "failed to truncate session file"); | ||
237 | goto err; | ||
238 | } | ||
239 | goto done; | ||
240 | } | ||
241 | |||
242 | if ((bio = BIO_new(BIO_s_mem())) == NULL) | ||
243 | goto err; | ||
244 | if (PEM_write_bio_SSL_SESSION(bio, ss) == 0) | ||
245 | goto err; | ||
246 | if ((data_len = BIO_get_mem_data(bio, &data)) <= 0) | ||
247 | goto err; | ||
248 | |||
249 | len = (size_t)data_len; | ||
250 | offset = 0; | ||
251 | |||
252 | if (ftruncate(sfd, len) == -1) { | ||
253 | tls_set_error(ctx, "failed to truncate session file"); | ||
254 | goto err; | ||
255 | } | ||
256 | while (len > 0) { | ||
257 | if ((n = pwrite(sfd, data + offset, len, offset)) == -1) { | ||
258 | tls_set_error(ctx, "failed to write session file"); | ||
259 | goto err; | ||
260 | } | ||
261 | offset += n; | ||
262 | len -= n; | ||
263 | } | ||
264 | |||
265 | done: | ||
266 | rv = 0; | ||
267 | |||
268 | err: | ||
269 | SSL_SESSION_free(ss); | ||
270 | BIO_free_all(bio); | ||
271 | |||
272 | return (rv); | ||
273 | } | ||
274 | |||
275 | static int | ||
162 | tls_connect_common(struct tls *ctx, const char *servername) | 276 | tls_connect_common(struct tls *ctx, const char *servername) |
163 | { | 277 | { |
164 | union tls_addr addrbuf; | 278 | union tls_addr addrbuf; |
@@ -221,6 +335,12 @@ tls_connect_common(struct tls *ctx, const char *servername) | |||
221 | goto err; | 335 | goto err; |
222 | } | 336 | } |
223 | 337 | ||
338 | if (ctx->config->session_fd != -1) { | ||
339 | SSL_clear_options(ctx->ssl_conn, SSL_OP_NO_TICKET); | ||
340 | if (tls_client_read_session(ctx) == -1) | ||
341 | goto err; | ||
342 | } | ||
343 | |||
224 | if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) { | 344 | if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) { |
225 | tls_set_errorx(ctx, "ssl OCSP extension setup failure"); | 345 | tls_set_errorx(ctx, "ssl OCSP extension setup failure"); |
226 | goto err; | 346 | goto err; |
@@ -336,6 +456,12 @@ tls_handshake_client(struct tls *ctx) | |||
336 | } | 456 | } |
337 | 457 | ||
338 | ctx->state |= TLS_HANDSHAKE_COMPLETE; | 458 | ctx->state |= TLS_HANDSHAKE_COMPLETE; |
459 | |||
460 | if (ctx->config->session_fd != -1) { | ||
461 | if (tls_client_write_session(ctx) == -1) | ||
462 | goto err; | ||
463 | } | ||
464 | |||
339 | rv = 0; | 465 | rv = 0; |
340 | 466 | ||
341 | err: | 467 | err: |
diff --git a/src/lib/libtls/tls_config.c b/src/lib/libtls/tls_config.c index 3db75dc62f..6dfebfaebf 100644 --- a/src/lib/libtls/tls_config.c +++ b/src/lib/libtls/tls_config.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_config.c,v 1.47 2018/02/08 05:56:49 jsing Exp $ */ | 1 | /* $OpenBSD: tls_config.c,v 1.48 2018/02/10 04:41:24 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
4 | * | 4 | * |
@@ -89,6 +89,7 @@ tls_config_new(void) | |||
89 | goto err; | 89 | goto err; |
90 | 90 | ||
91 | config->refcount = 1; | 91 | config->refcount = 1; |
92 | config->session_fd = -1; | ||
92 | 93 | ||
93 | /* | 94 | /* |
94 | * Default configuration. | 95 | * Default configuration. |
@@ -670,6 +671,44 @@ tls_config_set_protocols(struct tls_config *config, uint32_t protocols) | |||
670 | } | 671 | } |
671 | 672 | ||
672 | int | 673 | int |
674 | tls_config_set_session_fd(struct tls_config *config, int session_fd) | ||
675 | { | ||
676 | struct stat sb; | ||
677 | mode_t mugo; | ||
678 | |||
679 | if (session_fd == -1) { | ||
680 | config->session_fd = session_fd; | ||
681 | return (0); | ||
682 | } | ||
683 | |||
684 | if (fstat(session_fd, &sb) == -1) { | ||
685 | tls_config_set_error(config, "failed to stat session file"); | ||
686 | return (-1); | ||
687 | } | ||
688 | if (!S_ISREG(sb.st_mode)) { | ||
689 | tls_config_set_errorx(config, | ||
690 | "session file is not a regular file"); | ||
691 | return (-1); | ||
692 | } | ||
693 | |||
694 | if (sb.st_uid != getuid()) { | ||
695 | tls_config_set_errorx(config, "session file has incorrect " | ||
696 | "owner (uid %i != %i)", sb.st_uid, getuid()); | ||
697 | return (-1); | ||
698 | } | ||
699 | mugo = sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO); | ||
700 | if (mugo != (S_IRUSR|S_IWUSR)) { | ||
701 | tls_config_set_errorx(config, "session file has incorrect " | ||
702 | "permissions (%o != 600)", mugo); | ||
703 | return (-1); | ||
704 | } | ||
705 | |||
706 | config->session_fd = session_fd; | ||
707 | |||
708 | return (0); | ||
709 | } | ||
710 | |||
711 | int | ||
673 | tls_config_set_verify_depth(struct tls_config *config, int verify_depth) | 712 | tls_config_set_verify_depth(struct tls_config *config, int verify_depth) |
674 | { | 713 | { |
675 | config->verify_depth = verify_depth; | 714 | config->verify_depth = verify_depth; |
diff --git a/src/lib/libtls/tls_conninfo.c b/src/lib/libtls/tls_conninfo.c index 685ed194e4..34535b5668 100644 --- a/src/lib/libtls/tls_conninfo.c +++ b/src/lib/libtls/tls_conninfo.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_conninfo.c,v 1.17 2018/02/08 10:02:48 jsing Exp $ */ | 1 | /* $OpenBSD: tls_conninfo.c,v 1.18 2018/02/10 04:41:24 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> | 3 | * Copyright (c) 2015 Joel Sing <jsing@openbsd.org> |
4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> | 4 | * Copyright (c) 2015 Bob Beck <beck@openbsd.org> |
@@ -221,6 +221,14 @@ tls_conninfo_cert_pem(struct tls *ctx) | |||
221 | return rv; | 221 | return rv; |
222 | } | 222 | } |
223 | 223 | ||
224 | static int | ||
225 | tls_conninfo_session(struct tls *ctx) | ||
226 | { | ||
227 | ctx->conninfo->session_resumed = SSL_session_reused(ctx->ssl_conn); | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
224 | int | 232 | int |
225 | tls_conninfo_populate(struct tls *ctx) | 233 | tls_conninfo_populate(struct tls *ctx) |
226 | { | 234 | { |
@@ -260,6 +268,9 @@ tls_conninfo_populate(struct tls *ctx) | |||
260 | if (tls_conninfo_cert_pem(ctx) == -1) | 268 | if (tls_conninfo_cert_pem(ctx) == -1) |
261 | goto err; | 269 | goto err; |
262 | 270 | ||
271 | if (tls_conninfo_session(ctx) == -1) | ||
272 | goto err; | ||
273 | |||
263 | return (0); | 274 | return (0); |
264 | 275 | ||
265 | err: | 276 | err: |
@@ -313,6 +324,14 @@ tls_conn_servername(struct tls *ctx) | |||
313 | return (ctx->conninfo->servername); | 324 | return (ctx->conninfo->servername); |
314 | } | 325 | } |
315 | 326 | ||
327 | int | ||
328 | tls_conn_session_resumed(struct tls *ctx) | ||
329 | { | ||
330 | if (ctx->conninfo == NULL) | ||
331 | return (0); | ||
332 | return (ctx->conninfo->session_resumed); | ||
333 | } | ||
334 | |||
316 | const char * | 335 | const char * |
317 | tls_conn_version(struct tls *ctx) | 336 | tls_conn_version(struct tls *ctx) |
318 | { | 337 | { |
diff --git a/src/lib/libtls/tls_internal.h b/src/lib/libtls/tls_internal.h index eb08d47074..14265037eb 100644 --- a/src/lib/libtls/tls_internal.h +++ b/src/lib/libtls/tls_internal.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tls_internal.h,v 1.68 2018/02/08 10:19:31 jsing Exp $ */ | 1 | /* $OpenBSD: tls_internal.h,v 1.69 2018/02/10 04:41:24 jsing Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> | 3 | * Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org> |
4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> | 4 | * Copyright (c) 2014 Joel Sing <jsing@openbsd.org> |
@@ -95,6 +95,7 @@ struct tls_config { | |||
95 | int ocsp_require_stapling; | 95 | int ocsp_require_stapling; |
96 | uint32_t protocols; | 96 | uint32_t protocols; |
97 | unsigned char session_id[TLS_MAX_SESSION_ID_LENGTH]; | 97 | unsigned char session_id[TLS_MAX_SESSION_ID_LENGTH]; |
98 | int session_fd; | ||
98 | int session_lifetime; | 99 | int session_lifetime; |
99 | struct tls_ticket_key ticket_keys[TLS_NUM_TICKETS]; | 100 | struct tls_ticket_key ticket_keys[TLS_NUM_TICKETS]; |
100 | uint32_t ticket_keyrev; | 101 | uint32_t ticket_keyrev; |
@@ -111,6 +112,7 @@ struct tls_conninfo { | |||
111 | char *alpn; | 112 | char *alpn; |
112 | char *cipher; | 113 | char *cipher; |
113 | char *servername; | 114 | char *servername; |
115 | int session_resumed; | ||
114 | char *version; | 116 | char *version; |
115 | 117 | ||
116 | char *hash; | 118 | char *hash; |