#endif
#include <openssl/ssl.h>
+#include <openssl/err.h>
#include <errno.h>
#include <fcntl.h>
#define TLSSTATE_RWW 0x0002 /* Read Wants to Write */
#define TLSSTATE_WWR 0x0004 /* Write Wants to Read */
#define TLSSTATE_WWW 0x0008 /* Write Wants to Write */
+#define TLSSTATE_DEAD 0x0010 /* Delayed error */
struct isc__socket {
ISC_LIST(isc_socket_newconnev_t) accept_list;
ISC_LIST(isc_socket_connev_t) connect_list;
- SSL * ssl;
-
isc_sockaddr_t peer_address; /* remote address */
unsigned int listener : 1, /* listener socket */
bound : 1, /* bound to local addr */
dupped : 1,
active : 1, /* currently active */
- pktdscp : 1, /* per packet dscp */
- tlsconnecting : 1, /* waiting for TLS conn */
- tlsaccepting : 1; /* waiting for TLS accept */
+ pktdscp : 1; /* per packet dscp */
+
+ SSL *ssl;
+ /* server ctx for accepting socket */
+ SSL_CTX *ssl_ctx;
+ unsigned int tlsconnecting : 1, /* waiting for TLS conn */
+ tlsaccepting : 1; /* waiting for TLS accept */
int tlsstate;
#ifdef ISC_PLATFORM_RECVOVERFLOW
int reserved; /* unlocked */
isc_condition_t shutdown_ok;
int maxudp;
+
+ /* client ctx for created sockets */
+ SSL_CTX *ssl_ctx; /* XXXWPK TODO verify thread-safety of that (we're not modifying it, only creating clients) */
};
struct isc__socketthread {
sock->tlsaccepting = 0;
sock->tlsstate = 0;
sock->ssl = NULL;
+ sock->ssl_ctx = NULL;
ISC_LINK_INIT(sock, link);
/*
* Fill in the done event details and send it off.
*/
- dev->result = result;
- task = dev->ev_sender;
- dev->ev_sender = sock;
-
- isc_task_sendtoanddetach(&task, ISC_EVENT_PTR(&dev), sock->threadid);
+ printf("Accept done %d %p\n", result, sock->ssl_ctx);
+ if (result == ISC_R_SUCCESS && sock->ssl_ctx != NULL) {
+ /*
+ * This socket might be handled by different FD, we can't
+ * launch internal_accept directly
+ */
+ isc__socket_t *ns = NEWCONNSOCK(dev);
+ ns->ssl_ctx = sock->ssl_ctx;
+ ns->tlsstate = TLSSTATE_RWR;
+ ns->tlsaccepting = 1;
+ ns->type = isc_sockettype_tls;
+ printf("Pushing TLS ACCEPT to %p\n", ns);
+ ISC_LIST_APPEND(ns->accept_list, dev, ev_link);
+ select_poke(ns->manager, ns->threadid, ns->fd,
+ SELECT_POKE_READ);
+ } else {
+ dev->result = result;
+ task = dev->ev_sender;
+ dev->ev_sender = sock;
+ isc_task_sendtoanddetach(&task, ISC_EVENT_PTR(&dev), sock->threadid);
+ }
return;
soft_error:
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;
isc_refcount_increment(&sock->references);
- printf("process_fd sock->type %d readable %d writeable %d connecting %d\n", sock->type, readable, writeable, sock->connecting);
+ printf("process_fd %d sock->type %d readable %d writeable %d connecting %d\n", sock->fd, sock->type, readable, writeable, sock->connecting);
if (!sock->listener && !sock->connecting && sock->type == isc_sockettype_tls) {
if (readable) {
if (sock->tlsstate & TLSSTATE_RWR) {
return (ISC_R_UNEXPECTED);
}
+ const SSL_METHOD *meth;
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+ meth = TLS_client_method();
+ manager->ssl_ctx = SSL_CTX_new(meth);
/*
* Start up the select/poll thread.
dev->ev_sender = task;
+ if ((sock->type == isc_sockettype_tls) && (sock->tlsstate & TLSSTATE_DEAD) == TLSSTATE_DEAD) {
+ dev->result = ISC_R_TLSERROR;
+ if ((flags & ISC_SOCKFLAG_IMMEDIATE) == 0)
+ send_recvdone_event(sock, &dev);
+ return (ISC_R_SUCCESS);
+ }
+
if (sock->type == isc_sockettype_udp) {
io_state = doio_recv(sock, dev);
} else {
}
}
+ if ((sock->type == isc_sockettype_tls) && (sock->tlsstate & TLSSTATE_DEAD) == TLSSTATE_DEAD) {
+ dev->result = ISC_R_TLSERROR;
+ if ((flags & ISC_SOCKFLAG_IMMEDIATE) == 0)
+ send_recvdone_event(sock, &dev);
+ return (ISC_R_SUCCESS);
+ }
+
if (sock->type == isc_sockettype_udp) {
io_state = doio_send(sock, dev);
} else {
bool wanted_write = sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW);
sock->tlsstate &= ~(TLSSTATE_RWR | TLSSTATE_RWW);
+ /*
+ * internal_tls_connect can be called multiple times, if that's the
+ * first time we need to create SSL object
+ */
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);
+ sock->ssl = SSL_new(sock->manager->ssl_ctx);
SSL_set_fd(sock->ssl, sock->fd);
SSL_set_connect_state(sock->ssl);
}
goto finish;
} else {
printf("Other SSL error in connect %d %d\n", cc, err);
+ ERR_print_errors_fp(stderr);
result = ISC_R_CONNECTIONRESET;
}
} else {
watch_unwatch(sock, wanted_read, wanted_write);
}
+static void
+internal_tls_accept(isc__socket_t *sock) {
+ isc_socket_newconnev_t *dev;
+ isc_result_t result;
+ int threadid = sock->threadid;
+ sock->tlsaccepting = 1;
+ bool wanted_read = sock->tlsstate & (TLSSTATE_RWR | TLSSTATE_WWR);
+ bool wanted_write = sock->tlsstate & (TLSSTATE_RWW | TLSSTATE_WWW);
+ sock->tlsstate &= ~(TLSSTATE_WWR | TLSSTATE_WWW);
+
+ printf("TLS ACCEPT SOCK %p\n", sock);
+ dev = ISC_LIST_HEAD(sock->accept_list);
+ if (dev == NULL) {
+ abort();
+ }
+
+ /*
+ * internal_tls_accept can be called multiple times, if that's the
+ * first time we need to create SSL object
+ */
+ if (sock->ssl == NULL) {
+ sock->ssl = SSL_new(sock->ssl_ctx);
+ SSL_set_fd(sock->ssl, sock->fd);
+ SSL_set_accept_state(sock->ssl);
+// SSL_set_connect_state(sock->ssl);
+ }
+ int cc = SSL_accept(sock->ssl);
+ printf("SSL_Accept 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_RWR;
+ watch_unwatch(sock, wanted_read, wanted_write);
+ return;
+ } 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_RWW;
+ watch_unwatch(sock, wanted_read, wanted_write);
+ return;
+ } else {
+ printf("Other SSL error in connect %d %d\n", cc, err);
+ result = ISC_R_CONNECTIONRESET;
+
+ }
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+ /*
+ * Since regular accept failures are odd, but SSL negotiation
+ * failures are quite common, we delay the error to first read/write
+ * happening on the socket - accept always return SUCCESS at this
+ * stage.
+ */
+ if (result != ISC_R_SUCCESS) {
+ sock->tlsstate |= TLSSTATE_DEAD;
+ }
+ dev->result = ISC_R_SUCCESS;
+ sock->tlsaccepting = 0;
+ isc_task_t *task = dev->ev_sender;
+ ISC_LIST_UNLINK(sock->accept_list, dev, ev_link);
+ watch_unwatch(sock, wanted_read, wanted_write);
+ dev->ev_sender = sock;
+ isc_task_sendtoanddetach(&task, ISC_EVENT_PTR(&dev), threadid);
+
+ INSIST(ISC_LIST_EMPTY(sock->accept_list));
+}
+
isc_result_t
isc_socket_getpeername(isc_socket_t *sock0, isc_sockaddr_t *addressp) {
isc__socket_t *sock = (isc__socket_t *)sock0;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int dlen;
X509* x509;
- if (sock->ssl == NULL) {
+ 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 (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);
isc_buffer_putuint8(&buf, 0);
return (ISC_R_SUCCESS);
}
-
+
+isc_result_t
+isc_socket_maketls(isc_socket_t *sock0, const char* cert_path, const char* key_path) {
+ printf("Maketls\n");
+ isc__socket_t *sock = (isc__socket_t*) sock0;
+ const SSL_METHOD *meth;
+
+ REQUIRE(VALID_SOCKET(sock));
+ REQUIRE(!sock->connected);
+ REQUIRE(sock->listener);
+
+ REQUIRE(sock->ssl == NULL);
+ REQUIRE(sock->ssl_ctx == NULL);
+
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+ meth = TLS_server_method();
+ INSIST(meth != NULL);
+ sock->ssl_ctx = SSL_CTX_new(meth);
+ INSIST(sock->ssl_ctx != NULL);
+// SSL_set_msg_callback(sock->ssl_ctx,SSL_trace); SSL_set_msg_callback_arg(sock->ssl_ctx,BIO_new_fp(stdout,0));
+ if (SSL_CTX_use_certificate_file(sock->ssl_ctx, cert_path, SSL_FILETYPE_PEM) <= 0) {
+ abort();
+ }
+ if (SSL_CTX_use_PrivateKey_file(sock->ssl_ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
+ abort();
+ }
+
+ sock->type = isc_sockettype_tls;
+
+ return (ISC_R_SUCCESS);
+}
static cfg_type_t cfg_type_optional_allow;
static cfg_type_t cfg_type_optional_class;
static cfg_type_t cfg_type_optional_dscp;
+static cfg_type_t cfg_type_optional_cert;
+static cfg_type_t cfg_type_optional_key;
static cfg_type_t cfg_type_optional_facility;
static cfg_type_t cfg_type_optional_keyref;
static cfg_type_t cfg_type_optional_port;
static cfg_tuplefielddef_t listenon_fields[] = {
{ "port", &cfg_type_optional_port, 0 },
{ "dscp", &cfg_type_optional_dscp, 0 },
+ { "cert", &cfg_type_optional_cert, 0 },
+ { "key", &cfg_type_optional_key, 0 },
{ "acl", &cfg_type_bracketed_aml, 0 },
{ NULL, NULL, 0 }
};
doc_optional_keyvalue, &cfg_rep_uint32, &port_kw
};
+static keyword_type_t tlskey_kw = { "key", &cfg_type_qstring };
+
+static cfg_type_t cfg_type_optional_key = {
+ "optional_key", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_string, &tlskey_kw
+};
+
+static keyword_type_t tlscert_kw = { "cert", &cfg_type_qstring };
+
+static cfg_type_t cfg_type_optional_cert = {
+ "optional_cert", parse_optional_keyvalue, print_keyvalue,
+ doc_optional_keyvalue, &cfg_rep_string, &tlscert_kw
+};
+
/*% A list of keys, as in the "key" clause of the controls statement. */
static cfg_type_t cfg_type_keylist = {
"keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
static isc_result_t
ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
- const char *name, ns_interface_t **ifpret)
+ const char *name, const char *certpath,
+ const char *keypath, ns_interface_t **ifpret)
{
ns_interface_t *ifp;
isc_result_t result;
ifp->mgr = NULL;
ifp->generation = mgr->generation;
ifp->addr = *addr;
+ ifp->certpath = certpath;
+ ifp->keypath = keypath;
ifp->flags = 0;
strlcpy(ifp->name, name, sizeof(ifp->name));
ifp->clientmgr = NULL;
isc_result_totext(result));
goto tcp_listen_failure;
}
-
+ printf("XXX %s\n", ifp->certpath);
+ if (ifp->certpath != NULL) {
+ result = isc_socket_maketls(ifp->tcpsocket, ifp->certpath,
+ ifp->keypath);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "setting TLS on TCP socket: %s",
+ isc_result_totext(result));
+ goto tcp_listen_failure;
+ }
+ }
/*
* If/when there a multiple filters listen to the
* result.
ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
const char *name, ns_interface_t **ifpret,
bool accept_tcp, isc_dscp_t dscp,
+ const char *certpath, const char *keypath,
bool *addr_in_use)
{
isc_result_t result;
REQUIRE(ifpret != NULL && *ifpret == NULL);
REQUIRE(addr_in_use == NULL || *addr_in_use == false);
- result = ns_interface_create(mgr, addr, name, &ifp);
+ result = ns_interface_create(mgr, addr, name, certpath, keypath, &ifp);
if (result != ISC_R_SUCCESS)
return (result);
ifp->dscp = dscp;
-
- result = ns_interface_listenudp(ifp);
- if (result != ISC_R_SUCCESS) {
- if ((result == ISC_R_ADDRINUSE) && (addr_in_use != NULL))
- *addr_in_use = true;
- goto cleanup_interface;
+ if (certpath == NULL) {
+ result = ns_interface_listenudp(ifp);
+ if (result != ISC_R_SUCCESS) {
+ if ((result == ISC_R_ADDRINUSE) && (addr_in_use != NULL))
+ *addr_in_use = true;
+ goto cleanup_interface;
+ }
}
if (((mgr->sctx->options & NS_SERVER_NOTCP) == 0) &&
"<any>", &ifp,
true,
le->dscp,
+ le->certpath,
+ le->keypath,
NULL);
if (result == ISC_R_SUCCESS)
ifp->flags |= NS_INTERFACEFLAG_ANYADDR;
(adjusting == true) ?
false : true,
le->dscp,
+ le->certpath,
+ le->keypath,
&addr_in_use);
tried_listening = true;