From: Witold Kręcicki Date: Fri, 2 Nov 2018 20:30:56 +0000 (+0000) Subject: WiP1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e58bf7bb58d3e0bbe4db2b134182c329fc3d9b95;p=thirdparty%2Fbind9.git WiP1 --- diff --git a/aclocal.m4 b/aclocal.m4 index 5a16166751a..4d850813d6b 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.16.1 -*- Autoconf -*- +# generated automatically by aclocal 1.15.1 -*- Autoconf -*- -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2017 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -12,9 +12,9 @@ # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) -# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -# serial 12 (pkg-config-0.29.2) - +dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +dnl serial 11 (pkg-config-0.29.1) +dnl dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl @@ -55,7 +55,7 @@ dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], -[m4_define([PKG_MACROS_VERSION], [0.29.2]) +[m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ @@ -156,7 +156,7 @@ AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no -AC_MSG_CHECKING([for $2]) +AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) @@ -166,11 +166,11 @@ and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` - else + else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs @@ -187,7 +187,7 @@ installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full diff --git a/bin/dig/dig.c b/bin/dig/dig.c index 47d18ae331b..6926dd4d48e 100644 --- a/bin/dig/dig.c +++ b/bin/dig/dig.c @@ -263,6 +263,9 @@ received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { else printf(";; Query time: %ld msec\n", (long) diff / 1000); printf(";; SERVER: %s(%s)\n", fromtext, query->servname); + if (query->servssldigest != NULL) { + printf(";; DNSoTLS cert digest: %s\n", query->servssldigest); + } time(&tnow); #if !defined(WIN32) (void)localtime_r(&tnow, &tmnow); @@ -920,6 +923,18 @@ plus_option(char *option, bool is_batchfile, case 'o': /* domain ... but treat "do" as synonym for dnssec */ if (cmd[2] == '\0') goto dnssec; + if (cmd[2] == 't') { + FULLCHECK("dot"); + if (!is_batchfile) { + lookup->tcp_mode = true; + lookup->dot_mode = state; + lookup->dot_mode_set = true; + if (!explicit_port) { + port = 853; + } + } + break; + } FULLCHECK("domain"); if (value == NULL) goto need_value; @@ -1692,6 +1707,7 @@ dash_option(char *option, char *next, dig_lookup_t **lookup, result = parse_uint(&num, value, MAXPORT, "port number"); if (result != ISC_R_SUCCESS) fatal("Couldn't parse port number"); + explicit_port = true; port = num; return (value_from_next); case 'q': diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 4daddbda7e3..b1344886354 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -114,7 +114,8 @@ bool showsearch = false, is_dst_up = false, keep_open = false, - verbose = false; + verbose = false, + explicit_port = false; in_port_t port = 53; unsigned int timeout = 0; unsigned int extrabytes; @@ -649,6 +650,8 @@ make_empty_lookup(void) { looknew->nsfound = 0; looknew->tcp_mode = false; looknew->tcp_mode_set = false; + looknew->dot_mode = false; + looknew->dot_mode_set = false; looknew->comments = true; looknew->stats = true; looknew->section_question = true; @@ -787,6 +790,8 @@ clone_lookup(dig_lookup_t *lookold, bool servers) { looknew->ns_search_only = lookold->ns_search_only; looknew->tcp_mode = lookold->tcp_mode; looknew->tcp_mode_set = lookold->tcp_mode_set; + looknew->dot_mode = lookold->dot_mode; + looknew->dot_mode_set = lookold->dot_mode_set; looknew->comments = lookold->comments; looknew->stats = lookold->stats; looknew->section_question = lookold->section_question; @@ -1541,7 +1546,11 @@ clear_query(dig_query_t *query) { isc_mempool_put(commctx, query->tmpsendspace); isc_buffer_invalidate(&query->recvbuf); isc_buffer_invalidate(&query->lengthbuf); - if (query->waiting_senddone) + if (query->servssldigest != NULL) { + isc_mem_free(mctx, query->servssldigest); + } + + if (query->waiting_senddone) { query->pending_free = true; else isc_mem_free(mctx, query); @@ -2474,6 +2483,7 @@ setup_lookup(dig_lookup_t *lookup) { query->first_rr_serial = 0; query->second_rr_serial = 0; query->servname = serv->servername; + query->servssldigest = NULL; query->userarg = serv->userarg; query->rr_count = 0; query->msg_count = 0; @@ -2717,6 +2727,7 @@ send_tcp_connect(dig_query_t *query) { result = isc_socket_create(socketmgr, isc_sockaddr_pf(&query->sockaddr), + query->lookup->dot_mode ? isc_sockettype_tls : isc_sockettype_tcp, &query->sock); check_result(result, "isc_socket_create"); sockcount++; @@ -3163,6 +3174,11 @@ connect_done(isc_task_t *task, isc_event_t *event) { return; } exitcode = 0; + query->servssldigest = isc_mem_allocate(mctx, 4096); + if (isc_socket_getsslhexdigest(query->sock, query->servssldigest, 4096) != ISC_R_SUCCESS) { + isc_mem_free(mctx, query->servssldigest); + query->servssldigest = NULL; + } if (keep_open) { if (keep != NULL) isc_socket_detach(&keep); diff --git a/bin/dig/include/dig/dig.h b/bin/dig/include/dig/dig.h index a224c705a31..d901aaedaa5 100644 --- a/bin/dig/include/dig/dig.h +++ b/bin/dig/include/dig/dig.h @@ -101,6 +101,8 @@ struct dig_lookup { zflag, trace, /*% dig +trace */ trace_root, /*% initial query for either +trace or +nssearch */ + dot_mode, + dot_mode_set, tcp_mode, tcp_mode_set, comments, @@ -201,6 +203,7 @@ struct dig_query { uint32_t rr_count; bool ixfr_axfr; char *servname; + char *servssldigest; char *userarg; isc_buffer_t recvbuf, lengthbuf, @@ -242,7 +245,7 @@ extern dig_searchlistlist_t search_list; extern unsigned int extrabytes; extern bool check_ra, have_ipv4, have_ipv6, specified_source, - usesearch, showsearch; + usesearch, showsearch, explicit_port; extern in_port_t port; extern unsigned int timeout; extern isc_mem_t *mctx; diff --git a/configure b/configure index b6a780f78f8..a258f9213bc 100755 --- a/configure +++ b/configure @@ -15407,8 +15407,8 @@ else # then use that information and don't search ssldirs pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypto" >&5 -$as_echo_n "checking for crypto... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 +$as_echo_n "checking for OPENSSL... " >&6; } if test -n "$OPENSSL_CFLAGS"; then pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" @@ -15448,7 +15448,7 @@ fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -15466,7 +15466,7 @@ fi ssldirs="$default_ssldirs" elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ssldirs="$default_ssldirs" else @@ -15495,7 +15495,7 @@ $as_echo_n "checking for openssl/ssl.h in $ssldir... " >&6; } if test -f "$ssldir/include/openssl/ssl.h"; then : OPENSSL_INCLUDES="-I$ssldir/include" - OPENSSL_LIBS="-L$ssldir/lib -lcrypto" + OPENSSL_LIBS="-L$ssldir/lib -lcrypto -lssl" found=true { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -19420,8 +19420,8 @@ case $with_libidn2 in #( yes) : pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libidn2" >&5 -$as_echo_n "checking for libidn2... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBIDN2" >&5 +$as_echo_n "checking for LIBIDN2... " >&6; } if test -n "$LIBIDN2_CFLAGS"; then pkg_cv_LIBIDN2_CFLAGS="$LIBIDN2_CFLAGS" @@ -19461,7 +19461,7 @@ fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -19488,7 +19488,7 @@ Alternatively, you may set the environment variables LIBIDN2_CFLAGS and LIBIDN2_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} @@ -19628,8 +19628,8 @@ case $with_cmocka in #( yes) : pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cmocka >= 1.0.0" >&5 -$as_echo_n "checking for cmocka >= 1.0.0... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CMOCKA" >&5 +$as_echo_n "checking for CMOCKA... " >&6; } if test -n "$CMOCKA_CFLAGS"; then pkg_cv_CMOCKA_CFLAGS="$CMOCKA_CFLAGS" @@ -19669,7 +19669,7 @@ fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -19696,7 +19696,7 @@ Alternatively, you may set the environment variables CMOCKA_CFLAGS and CMOCKA_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} diff --git a/lib/isc/include/isc/socket.h b/lib/isc/include/isc/socket.h index 98fa51bfd6c..8a3c96b14d2 100644 --- a/lib/isc/include/isc/socket.h +++ b/lib/isc/include/isc/socket.h @@ -243,7 +243,8 @@ typedef enum { isc_sockettype_udp = 1, isc_sockettype_tcp = 2, isc_sockettype_unix = 3, - isc_sockettype_raw = 4 + isc_sockettype_raw = 4, + isc_sockettype_tls = 5 } isc_sockettype_t; /*@{*/ @@ -1040,4 +1041,7 @@ typedef isc_result_t ISC_LANG_ENDDECLS +isc_result_t +isc_socket_getsslhexdigest(isc_socket_t *sock0, char *dest, unsigned int len); + #endif /* ISC_SOCKET_H */ diff --git a/lib/isc/tests/socket_test.c b/lib/isc/tests/socket_test.c index 1bfe65f3a2a..68ebffbe7f2 100644 --- a/lib/isc/tests/socket_test.c +++ b/lib/isc/tests/socket_test.c @@ -879,6 +879,96 @@ ATF_TC_BODY(udp_trunc, tc) { isc_test_end(); } +/* Test TCP ssl sendto/recv */ +ATF_TC(tls); +ATF_TC_HEAD(tls, tc) { + atf_tc_set_md_var(tc, "descr", "tls"); +} +ATF_TC_BODY(tls, tc) { + isc_result_t result; + isc_sockaddr_t addr1; + struct in_addr in; + isc_socket_t *s1 = NULL, *s2 = NULL, *s3 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ], recvbuf[BUFSIZ]; + completion_t completion, completion2; + isc_region_t r; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in.s_addr = inet_addr("127.0.0.1"); + isc_sockaddr_fromin(&addr1, &in, 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_tls, &s1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_socket_bind(s1, &addr1, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_listen(s1, 3); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_tls, &s2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + completion_init(&completion2); + result = isc_socket_accept(s1, task, accept_done, &completion2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + completion_init(&completion); + result = isc_socket_connect(s2, &addr1, task, event_done, &completion); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + waitfor2(&completion, &completion2); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK(completion2.done); + ATF_CHECK_EQ(completion2.result, ISC_R_SUCCESS); + s3 = completion2.socket; + + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + recv_dscp = false; + recv_dscp_value = 0; + + completion_init(&completion); + result = isc_socket_sendto(s2, &r, task, event_done, &completion, + NULL, NULL); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s3, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + isc_socket_detach(&s3); + + isc_test_end(); +} + /* * Main */ @@ -891,6 +981,7 @@ ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, udp_dscp_v6); ATF_TP_ADD_TC(tp, net_probedscp); ATF_TP_ADD_TC(tp, udp_trunc); + ATF_TP_ADD_TC(tp, tls); return (atf_no_error()); } diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c index 6bad341a2e0..6d2d78f4451 100644 --- a/lib/isc/unix/socket.c +++ b/lib/isc/unix/socket.c @@ -31,6 +31,8 @@ #include #endif +#include + #include #include #include @@ -41,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -331,6 +334,20 @@ typedef struct isc__socketthread isc__socketthread_t; #define NEWCONNSOCK(ev) ((isc__socket_t *)(ev)->newsocket) +/* + * When using TLS read() might require write() on the underlying + * socket, and write() might require read() on the underlying socket. + * To trace this properly we have a 'tlsstate' field in the socket + * that's a combination of the fields below. + * 'write' here also includes SSL_connect, and 'read' includes SSL_accept. + */ + +#define TLSSTATE_RWR 0x0001 /* Read Wants to Read */ +#define TLSSTATE_RWW 0x0002 /* Read Wants to Write */ +#define TLSSTATE_WWR 0x0004 /* Write Wants to Read */ +#define TLSSTATE_WWW 0x0008 /* Write Wants to Write */ + + struct isc__socket { /* Not locked. */ isc_socket_t common; @@ -353,15 +370,21 @@ struct isc__socket { ISC_LIST(isc_socket_newconnev_t) accept_list; ISC_LIST(isc_socket_connev_t) connect_list; - isc_sockaddr_t peer_address; /* remote address */ + SSL * ssl; + + isc_sockaddr_t peer_address; /* remote address */ - unsigned int listener : 1, /* listener socket */ + unsigned int listener : 1, /* listener socket */ connected : 1, - connecting : 1, /* connect pending */ - bound : 1, /* bound to local addr */ + connecting : 1, /* connect pending */ + bound : 1, /* bound to local addr */ dupped : 1, - active : 1, /* currently active */ - pktdscp : 1; /* per packet dscp */ + active : 1, /* currently active */ + pktdscp : 1, /* per packet dscp */ + tlsconnecting : 1, /* waiting for TLS conn */ + tlsaccepting : 1; /* waiting for TLS accept */ + + int tlsstate; #ifdef ISC_PLATFORM_RECVOVERFLOW unsigned char overflow; /* used for MSG_TRUNC fake */ @@ -464,6 +487,10 @@ static void build_msghdr_recv(isc__socket_t *, char *, isc_socketevent_t *, struct msghdr *, struct iovec *, size_t *); static bool process_ctlfd(isc__socketthread_t *thread); static void setdscp(isc__socket_t *sock, isc_dscp_t dscp); +static void internal_tls_accept(isc__socket_t *); +static void internal_tls_connect(isc__socket_t *); +static void internal_tls_recv(isc__socket_t *); +static void internal_tls_send(isc__socket_t *); #define SELECT_POKE_SHUTDOWN (-1) #define SELECT_POKE_NOTHING (-2) @@ -1776,6 +1803,104 @@ doio_send(isc__socket_t *sock, isc_socketevent_t *dev) { return (DOIO_SUCCESS); } +static int +doio_tls_recv(isc__socket_t *sock, isc_socketevent_t *dev) { + int cc; + size_t read_count; + void *read_base; + + read_count = dev->region.length - dev->n; + read_base = (void *)(dev->region.base + dev->n); + dev->address = sock->peer_address; + + cc = SSL_read(sock->ssl, read_base, read_count); + printf("SSL read res %d\n", cc); + if (cc <= 0) { + int err = SSL_get_error(sock->ssl, cc); + printf("err %d\n", err); + if (err == SSL_ERROR_WANT_READ) { + sock->tlsstate |= TLSSTATE_RWR; + dev->result = ISC_R_WOULDBLOCK; + return (DOIO_SOFT); + } else if (err == SSL_ERROR_WANT_WRITE) { + sock->tlsstate |= TLSSTATE_RWW; + dev->result = ISC_R_WOULDBLOCK; + return (DOIO_SOFT); + } else { + printf("Hard err in read %d\n", cc); + return (DOIO_HARD); + } + } + if (cc == 0) { + return (DOIO_EOF); + } + + dev->n += cc; + /* + * If we have a partial read we need to watch the socket + */ + if (((size_t)cc != read_count) && (dev->n < dev->minimum)) { + sock->tlsstate |= TLSSTATE_RWR; + return (DOIO_SOFT); + } + + /* + * Full reads are posted, or partials if partials are ok. + */ + dev->result = ISC_R_SUCCESS; + return (DOIO_SUCCESS); +} + +/* + * Returns: + * DOIO_SUCCESS The operation succeeded. dev->result contains + * ISC_R_SUCCESS. + * + * DOIO_HARD A hard or unexpected I/O error was encountered. + * dev->result contains the appropriate error. + * + * DOIO_SOFT A soft I/O error was encountered. No senddone + * event was sent. The operation should be retried. + * + * No other return values are possible. + */ +static int +doio_tls_send(isc__socket_t *sock, isc_socketevent_t *dev) { + int cc; + size_t write_count; + char *send_base; + + write_count = dev->region.length - dev->n; + send_base = (void *) (dev->region.base + dev->n); + + cc = SSL_write(sock->ssl, send_base, write_count); + printf("SSL write res %d\n", cc); + if (cc <= 0) { + int err = SSL_get_error(sock->ssl, cc); + printf("err %d\n", err); + if (err == SSL_ERROR_WANT_READ) { + sock->tlsstate |= TLSSTATE_WWR; + dev->result = ISC_R_WOULDBLOCK; + return (DOIO_SOFT); + } else if (err == SSL_ERROR_WANT_WRITE) { + sock->tlsstate |= TLSSTATE_WWW; + dev->result = ISC_R_WOULDBLOCK; + return (DOIO_SOFT); + } else { + /* XXXWPK TODO log specific error */ + return (DOIO_HARD); + } + } + /* + * With SSL with no SSL_MODE_ENABLE_PARTIAL_WRITE writes are + * always complete. + */ + dev->n += cc; + + dev->result = ISC_R_SUCCESS; + return (DOIO_SUCCESS); +} + /* * Kill. * @@ -1892,6 +2017,11 @@ allocate_socket(isc__socketmgr_t *manager, isc_sockettype_t type, sock->statsindex = NULL; sock->active = 0; + sock->tlsconnecting = 0; + sock->tlsaccepting = 0; + sock->tlsstate = 0; + sock->ssl = NULL; + ISC_LINK_INIT(sock, link); @@ -2171,6 +2301,7 @@ opensocket(isc__socketmgr_t *manager, isc__socket_t *sock, sock->fd = socket(sock->pf, SOCK_DGRAM, IPPROTO_UDP); break; case isc_sockettype_tcp: + case isc_sockettype_tls: sock->fd = socket(sock->pf, SOCK_STREAM, IPPROTO_TCP); break; case isc_sockettype_unix: @@ -2561,6 +2692,7 @@ socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, sock->pktdscp = (isc_net_probedscp() & DCSPPKT(pf)) != 0; break; case isc_sockettype_tcp: + case isc_sockettype_tls: /* XXXWPK TODO */ sock->statsindex = (pf == AF_INET) ? tcp4statsindex : tcp6statsindex; break; @@ -3113,6 +3245,8 @@ internal_accept(isc__socket_t *sock) { return; } +static void internal_tls_accept(isc__socket_t *sock) { UNUSED(sock); abort(); }; + static void internal_recv(isc__socket_t *sock) { isc_socketevent_t *dev; @@ -3207,6 +3341,124 @@ internal_send(isc__socket_t *sock) { UNLOCK(&sock->lock); } +static void +watch_unwatch(isc__socket_t *sock, bool wanted_read, bool wanted_write) { + if (wanted_read && !(sock->tlsstate & (TLSSTATE_RWR | TLSSTATE_WWR))) { + unwatch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_READ); + } else if (!wanted_read && (sock->tlsstate & (TLSSTATE_RWR | TLSSTATE_WWR))) { + watch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_READ); + } + + if (wanted_write && !(sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW))) { + unwatch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_WRITE); + } else if (!wanted_write && (sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW))) { + watch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_READ); + } +} + +static void +internal_tls_recv(isc__socket_t *sock) { + isc_socketevent_t *dev = NULL; + + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + bool wanted_read = sock->tlsstate & (TLSSTATE_RWR | TLSSTATE_WWR); + bool wanted_write = sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW); + sock->tlsstate &= ~(TLSSTATE_RWR | TLSSTATE_RWW); + + dev = ISC_LIST_HEAD(sock->recv_list); + + if (dev == NULL) { + goto finish; + return; + } + + socket_log(sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALRECV, + "internal_recv: event %p -> task %p", dev, dev->ev_sender); + + /* + * Try to do as much I/O as possible on this socket. There are no + * limits here, currently. + */ + while (dev != NULL) { + switch (doio_tls_recv(sock, dev)) { + case DOIO_SOFT: + goto finish; + + case DOIO_EOF: + /* + * read of 0 means the remote end was closed. + * Run through the event queue and dispatch all + * the events with an EOF result code. + */ + do { + dev->result = ISC_R_EOF; + send_recvdone_event(sock, &dev); + dev = ISC_LIST_HEAD(sock->recv_list); + } while (dev != NULL); + goto finish; + + case DOIO_SUCCESS: + case DOIO_HARD: + send_recvdone_event(sock, &dev); + break; + } + + dev = ISC_LIST_HEAD(sock->recv_list); + } + + finish: + watch_unwatch(sock, wanted_read, wanted_write); + UNLOCK(&sock->lock); +} + +static void +internal_tls_send(isc__socket_t *sock) { + isc_socketevent_t *dev; + + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + bool wanted_read = sock->tlsstate & (TLSSTATE_RWR | TLSSTATE_WWR); + bool wanted_write = sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW); + sock->tlsstate &= ~(TLSSTATE_WWR | TLSSTATE_WWW); + dev = ISC_LIST_HEAD(sock->send_list); + if (dev == NULL) { + goto finish; + } + socket_log(sock, NULL, EVENT, NULL, 0, 0, + "internal_send: event %p -> task %p", + dev, dev->ev_sender); + + /* + * Try to do as much I/O as possible on this socket. There are no + * limits here, currently. + */ + while (dev != NULL) { + switch (doio_tls_send(sock, dev)) { + case DOIO_SOFT: + goto finish; + + case DOIO_HARD: + case DOIO_SUCCESS: + send_senddone_event(sock, &dev); + break; + } + + dev = ISC_LIST_HEAD(sock->send_list); + } + + finish: + watch_unwatch(sock, wanted_read, wanted_write); + UNLOCK(&sock->lock); +} + /* * Process read/writes on each fd here. Avoid locking * and unlocking twice if both reads and writes are possible. @@ -3241,18 +3493,49 @@ process_fd(isc__socketthread_t *thread, int fd, bool readable, isc_refcount_increment(&sock->references); + printf("process_fd sock->type %d readable %d writeable %d connecting %d\n", sock->type, readable, writeable, sock->connecting); + if (!sock->listener && !sock->connecting && sock->type == isc_sockettype_tls) { + if (readable) { + if (sock->tlsstate & TLSSTATE_RWR) { + if (sock->tlsaccepting) { + internal_tls_accept(sock); + } else { + internal_tls_recv(sock); + } + } + if (sock->tlsstate & TLSSTATE_WWR) { + if (sock->tlsconnecting) { + internal_tls_connect(sock); + } else { + internal_tls_send(sock); + } + } + } + if (writeable) { + if (sock->tlsstate & TLSSTATE_RWW) { + internal_tls_recv(sock); + } + if (sock->tlsstate & TLSSTATE_WWW) { + internal_tls_send(sock); + } + } + goto unlock_fd; + } + if (readable) { - if (sock->listener) + if (sock->listener) { internal_accept(sock); - else + } else { internal_recv(sock); + } } if (writeable) { - if (sock->connecting) + if (sock->connecting) { internal_connect(sock); - else + } else { internal_send(sock); + } } unlock_fd: @@ -4075,6 +4358,7 @@ static isc_result_t socket_recv(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, unsigned int flags) { + printf("socket recv\n"); int io_state; bool have_lock = false; isc_task_t *ntask = NULL; @@ -4088,14 +4372,21 @@ socket_recv(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, LOCK(&sock->lock); have_lock = true; - if (ISC_LIST_EMPTY(sock->recv_list)) - io_state = doio_recv(sock, dev); - else + if (ISC_LIST_EMPTY(sock->recv_list)) { + if (sock->type == isc_sockettype_tls) { + printf("Direct recv\n"); + io_state = doio_tls_recv(sock, dev); + } else { + io_state = doio_recv(sock, dev); + } + } else { io_state = DOIO_SOFT; + } } switch (io_state) { case DOIO_SOFT: + printf("Soft\n"); /* * We couldn't read all or part of the request right now, so * queue it. @@ -4114,12 +4405,19 @@ socket_recv(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, * Enqueue the request. If the socket was previously not being * watched, poke the watcher to start paying attention to it. */ - bool do_poke = ISC_LIST_EMPTY(sock->recv_list); + bool do_poke_read = sock->type == isc_sockettype_tls ? + sock->tlsstate & (TLSSTATE_RWR | TLSSTATE_WWR) : + ISC_LIST_EMPTY(sock->recv_list); + bool do_poke_write = sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW); ISC_LIST_ENQUEUE(sock->recv_list, dev, ev_link); - if (do_poke) { + if (do_poke_read) { select_poke(sock->manager, sock->threadid, sock->fd, SELECT_POKE_READ); } + if (do_poke_write) { + select_poke(sock->manager, sock->threadid, sock->fd, + SELECT_POKE_WRITE); + } socket_log(sock, NULL, EVENT, NULL, 0, 0, "socket_recv: event %p -> task %p", @@ -4135,6 +4433,7 @@ socket_recv(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, case DOIO_HARD: case DOIO_SUCCESS: + printf("Succ\n"); if ((flags & ISC_SOCKFLAG_IMMEDIATE) == 0) send_recvdone_event(sock, &dev); break; @@ -4232,16 +4531,21 @@ socket_send(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, } } - if (sock->type == isc_sockettype_udp) + if (sock->type == isc_sockettype_udp) { io_state = doio_send(sock, dev); - else { + } else { LOCK(&sock->lock); have_lock = true; - if (ISC_LIST_EMPTY(sock->send_list)) - io_state = doio_send(sock, dev); - else + if (ISC_LIST_EMPTY(sock->send_list)) { + if (sock->type == isc_sockettype_tls) { + io_state = doio_tls_send(sock, dev); + } else { + io_state = doio_send(sock, dev); + } + } else { io_state = DOIO_SOFT; + } } switch (io_state) { @@ -4263,14 +4567,26 @@ socket_send(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, * Enqueue the request. If the socket was previously * not being watched, poke the watcher to start * paying attention to it. + * For TLS sockets it's the TLS code that handles + * poking, as we don't know whether TLS wants to read + * or write. */ - bool do_poke = ISC_LIST_EMPTY(sock->send_list); + bool do_poke_write = sock->type == isc_sockettype_tls ? + sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW) : + ISC_LIST_EMPTY(sock->send_list); + bool do_poke_read = sock->tlsstate & (TLSSTATE_WWR | TLSSTATE_RWR); + ISC_LIST_ENQUEUE(sock->send_list, dev, ev_link); - if (do_poke) { + if (do_poke_write) { select_poke(sock->manager, sock->threadid, sock->fd, SELECT_POKE_WRITE); } + if (do_poke_read) { + select_poke(sock->manager, sock->threadid, + sock->fd, + SELECT_POKE_READ); + } socket_log(sock, NULL, EVENT, NULL, 0, 0, "socket_send: event %p -> task %p", dev, ntask); @@ -4743,7 +5059,8 @@ isc_socket_listen(isc_socket_t *sock0, unsigned int backlog) { REQUIRE(!sock->listener); REQUIRE(sock->bound); REQUIRE(sock->type == isc_sockettype_tcp || - sock->type == isc_sockettype_unix); + sock->type == isc_sockettype_unix || + sock->type == isc_sockettype_tls); if (backlog == 0) backlog = SOMAXCONN; @@ -4964,14 +5281,25 @@ isc_socket_connect(isc_socket_t *sock0, const isc_sockaddr_t *addr, if (cc == 0) { sock->connected = 1; sock->bound = 1; - dev->result = ISC_R_SUCCESS; - isc_task_sendto(task, ISC_EVENT_PTR(&dev), sock->threadid); + /* + * If socket is TLS we need to negiotate TLS before + * returning the socket as connected + */ + if (sock->type == isc_sockettype_tls) { + isc_task_attach(task, &ntask); + dev->ev_sender = ntask; + ISC_LIST_ENQUEUE(sock->connect_list, dev, ev_link); + internal_tls_connect(sock); + } else { + dev->result = ISC_R_SUCCESS; + isc_task_sendto(task, ISC_EVENT_PTR(&dev), sock->threadid); + } UNLOCK(&sock->lock); - inc_stats(sock->manager->stats, sock->statsindex[STATID_CONNECT]); + return (ISC_R_SUCCESS); } @@ -5024,6 +5352,8 @@ internal_connect(isc__socket_t *sock) { dev = ISC_LIST_HEAD(sock->connect_list); if (dev == NULL) { INSIST(!sock->connecting); + unwatch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_CONNECT); goto finish; } @@ -5047,8 +5377,7 @@ internal_connect(isc__socket_t *sock) { */ if (SOFT_ERROR(errno) || errno == EINPROGRESS) { sock->connecting = 1; - UNLOCK(&sock->lock); - return; + goto finish; } inc_stats(sock->manager->stats, @@ -5091,17 +5420,84 @@ internal_connect(isc__socket_t *sock) { sock->bound = 1; } + unwatch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_CONNECT); + + if (sock->type == isc_sockettype_tls) { + internal_tls_connect(sock); + } else { + do { + dev->result = result; + send_connectdone_event(sock, &dev); + dev = ISC_LIST_HEAD(sock->connect_list); + } while (dev != NULL); + } + + finish: + UNLOCK(&sock->lock); +} + +static void +internal_tls_connect(isc__socket_t *sock) { + isc_socket_connev_t *dev; + isc_result_t result; + sock->tlsconnecting = 1; + bool wanted_read = sock->tlsstate & (TLSSTATE_RWR | TLSSTATE_WWR); + bool wanted_write = sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW); + sock->tlsstate &= ~(TLSSTATE_RWR | TLSSTATE_RWW); + + if (sock->ssl == NULL) { + const SSL_METHOD *meth; + SSL_CTX* ctx; + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + meth = TLS_client_method(); + ctx = SSL_CTX_new(meth); + sock->ssl = SSL_new(ctx); + SSL_set_fd(sock->ssl, sock->fd); + SSL_set_connect_state(sock->ssl); + } + dev = ISC_LIST_HEAD(sock->connect_list); + if (dev == NULL) { + abort(); + } + int cc = SSL_connect(sock->ssl); + printf("SSL_Connect returned %d\n", cc); + if (cc < 0) { + int err = SSL_get_error(sock->ssl, cc); + if (err == SSL_ERROR_WANT_READ) { + printf("Want read\n"); + if (!wanted_read) { + watch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_READ); + } + sock->tlsstate |= TLSSTATE_WWR; + goto finish; + } else if (err == SSL_ERROR_WANT_WRITE) { + printf("Want write\n"); + if (!wanted_write) { + watch_fd(&sock->manager->threads[sock->threadid], sock->fd, + SELECT_POKE_WRITE); + } + sock->tlsstate |= TLSSTATE_WWW; + goto finish; + } else { + printf("Other SSL error in connect %d %d\n", cc, err); + result = ISC_R_CONNECTIONRESET; + } + } else { + result = ISC_R_SUCCESS; + } do { + printf("Send connectdone\n"); + sock->tlsconnecting = 0; dev->result = result; send_connectdone_event(sock, &dev); dev = ISC_LIST_HEAD(sock->connect_list); } while (dev != NULL); finish: - unwatch_fd(&sock->manager->threads[sock->threadid], sock->fd, - SELECT_POKE_CONNECT); - - UNLOCK(&sock->lock); + watch_unwatch(sock, wanted_read, wanted_write); } isc_result_t @@ -5715,3 +6111,40 @@ isc_socketmgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, return (result); } + +isc_result_t +isc_socket_getsslhexdigest(isc_socket_t *sock0, char *dest, unsigned int len) { + isc__socket_t *sock = (isc__socket_t*) sock0; + isc_result_t result; + isc_region_t r; + isc_buffer_t buf; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen; + X509* x509; + if (sock->ssl == NULL) { + return (ISC_R_UNSET); + } + x509 = SSL_get_peer_certificate(sock->ssl); + if (x509 == NULL) { + return (ISC_R_UNEXPECTED); + } + + if (X509_pubkey_digest(x509, EVP_sha256(), digest, &dlen) != 1) { + return (ISC_R_UNEXPECTED); + } + + if (len < 2*dlen + 1) { + return (ISC_R_NOSPACE); + } + + r.base = digest; + r.length = dlen; + isc_buffer_init(&buf, dest, len); + result = isc_hex_totext(&r, 4096, "", &buf); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_putuint8(&buf, 0); + return (ISC_R_SUCCESS); +} + diff --git a/m4/ax_check_openssl.m4 b/m4/ax_check_openssl.m4 index fd308476e4f..c7db8e0f60f 100644 --- a/m4/ax_check_openssl.m4 +++ b/m4/ax_check_openssl.m4 @@ -68,7 +68,7 @@ AC_DEFUN([AX_CHECK_OPENSSL], [ AS_IF([test -f "$ssldir/include/openssl/ssl.h"], [ OPENSSL_INCLUDES="-I$ssldir/include" - OPENSSL_LIBS="-L$ssldir/lib -lcrypto" + OPENSSL_LIBS="-L$ssldir/lib -lcrypto -lssl" found=true AC_MSG_RESULT([yes]) break