diff --git a/doc/Changelog b/doc/Changelog index e85b4c1d2..eb8031aa1 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,9 @@ 1 May 2019: Wouter - Update makedist for git. - Nicer travis output for clang analysis. + - PR #16: XoT support, AXFR over TLS, turn it on with + master: # in unbound.conf. This uses TLS to + download the AXFR (or IXFR). 25 April 2019: Wouter - Fix wrong query name in local zone redirect answers with a CNAME, diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index eff757e67..c14ee2785 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1675,6 +1675,7 @@ Name of the authority zone. .B master: \fI Where to download a copy of the zone from, with AXFR and IXFR. Multiple masters can be specified. They are all tried if one fails. +With the "ip#name" notation a AXFR over TLS can be used. .TP .B url: \fI Where to download a zonefile for the zone. With http or https. An example diff --git a/services/authzone.c b/services/authzone.c index 225cb0a7a..1426f423a 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -5059,6 +5059,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env) struct sockaddr_storage addr; socklen_t addrlen = 0; struct auth_master* master = xfr->task_transfer->master; + char *auth_name = NULL; struct timeval t; int timeout; if(!master) return 0; @@ -5069,7 +5070,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env) addrlen = xfr->task_transfer->scan_addr->addrlen; memmove(&addr, &xfr->task_transfer->scan_addr->addr, addrlen); } else { - if(!extstrtoaddr(master->host, &addr, &addrlen)) { + if(!authextstrtoaddr(master->host, &addr, &addrlen, &auth_name)) { /* the ones that are not in addr format are supposed * to be looked up. The lookup has failed however, * so skip them */ @@ -5139,7 +5140,8 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env) /* connect on fd */ xfr->task_transfer->cp = outnet_comm_point_for_tcp(env->outnet, auth_xfer_transfer_tcp_callback, xfr, &addr, addrlen, - env->scratch_buffer, -1); + env->scratch_buffer, -1, + auth_name != NULL, auth_name); if(!xfr->task_transfer->cp) { char zname[255+1], as[256]; dname_str(xfr->name, zname); @@ -5938,6 +5940,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env, struct timeval t; /* pick master */ struct auth_master* master = xfr_probe_current_master(xfr); + char *auth_name = NULL; if(!master) return 0; if(master->allow_notify) return 0; /* only for notify */ if(master->http) return 0; /* only masters get SOA UDP probe, @@ -5948,7 +5951,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env, addrlen = xfr->task_probe->scan_addr->addrlen; memmove(&addr, &xfr->task_probe->scan_addr->addr, addrlen); } else { - if(!extstrtoaddr(master->host, &addr, &addrlen)) { + if(!authextstrtoaddr(master->host, &addr, &addrlen, &auth_name)) { /* the ones that are not in addr format are supposed * to be looked up. The lookup has failed however, * so skip them */ @@ -5958,6 +5961,18 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env, zname, master->host); return 0; } + if (auth_name != NULL) { + if (addr.ss_family == AF_INET + && ntohs(((struct sockaddr_in *)&addr)->sin_port) + == env->cfg->ssl_port) + ((struct sockaddr_in *)&addr)->sin_port + = htons(env->cfg->port); + else if (addr.ss_family == AF_INET6 + && ntohs(((struct sockaddr_in6 *)&addr)->sin6_port) + == env->cfg->ssl_port) + ((struct sockaddr_in6 *)&addr)->sin6_port + = htons(env->cfg->port); + } } /* create packet */ diff --git a/services/outside_network.c b/services/outside_network.c index 3347c38e7..0323f1b30 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -2281,11 +2281,60 @@ outnet_comm_point_for_udp(struct outside_network* outnet, return cp; } +/** setup SSL for comm point */ +static int +setup_comm_ssl(struct comm_point* cp, struct outside_network* outnet, + int fd, char* host) +{ + cp->ssl = outgoing_ssl_fd(outnet->sslctx, fd); + if(!cp->ssl) { + log_err("cannot create SSL object"); + return 0; + } +#ifdef USE_WINSOCK + comm_point_tcp_win_bio_cb(cp, cp->ssl); +#endif + cp->ssl_shake_state = comm_ssl_shake_write; + /* https verification */ +#ifdef HAVE_SSL_SET1_HOST + if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) { + /* because we set SSL_VERIFY_PEER, in netevent in + * ssl_handshake, it'll check if the certificate + * verification has succeeded */ + /* SSL_VERIFY_PEER is set on the sslctx */ + /* and the certificates to verify with are loaded into + * it with SSL_load_verify_locations or + * SSL_CTX_set_default_verify_paths */ + /* setting the hostname makes openssl verify the + * host name in the x509 certificate in the + * SSL connection*/ + if(!SSL_set1_host(cp->ssl, host)) { + log_err("SSL_set1_host failed"); + return 0; + } + } +#elif defined(HAVE_X509_VERIFY_PARAM_SET1_HOST) + /* openssl 1.0.2 has this function that can be used for + * set1_host like verification */ + if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) { + X509_VERIFY_PARAM* param = SSL_get0_param(cp->ssl); + X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + if(!X509_VERIFY_PARAM_set1_host(param, host, strlen(host))) { + log_err("X509_VERIFY_PARAM_set1_host failed"); + return 0; + } + } +#else + (void)host; +#endif /* HAVE_SSL_SET1_HOST */ + return 1; +} + struct comm_point* outnet_comm_point_for_tcp(struct outside_network* outnet, comm_point_callback_type* cb, void* cb_arg, struct sockaddr_storage* to_addr, socklen_t to_addrlen, - sldns_buffer* query, int timeout) + sldns_buffer* query, int timeout, int ssl, char* host) { struct comm_point* cp; int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss); @@ -2305,6 +2354,16 @@ outnet_comm_point_for_tcp(struct outside_network* outnet, } cp->repinfo.addrlen = to_addrlen; memcpy(&cp->repinfo.addr, to_addr, to_addrlen); + + /* setup for SSL (if needed) */ + if(ssl) { + if(!setup_comm_ssl(cp, outnet, fd, host)) { + log_err("cannot setup XoT"); + comm_point_delete(cp); + return NULL; + } + } + /* set timeout on TCP connection */ comm_point_start_listening(cp, fd, timeout); /* copy scratch buffer to cp->buffer */ @@ -2361,48 +2420,11 @@ outnet_comm_point_for_http(struct outside_network* outnet, /* setup for SSL (if needed) */ if(ssl) { - cp->ssl = outgoing_ssl_fd(outnet->sslctx, fd); - if(!cp->ssl) { + if(!setup_comm_ssl(cp, outnet, fd, host)) { log_err("cannot setup https"); comm_point_delete(cp); return NULL; } -#ifdef USE_WINSOCK - comm_point_tcp_win_bio_cb(cp, cp->ssl); -#endif - cp->ssl_shake_state = comm_ssl_shake_write; - /* https verification */ -#ifdef HAVE_SSL_SET1_HOST - if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) { - /* because we set SSL_VERIFY_PEER, in netevent in - * ssl_handshake, it'll check if the certificate - * verification has succeeded */ - /* SSL_VERIFY_PEER is set on the sslctx */ - /* and the certificates to verify with are loaded into - * it with SSL_load_verify_locations or - * SSL_CTX_set_default_verify_paths */ - /* setting the hostname makes openssl verify the - * host name in the x509 certificate in the - * SSL connection*/ - if(!SSL_set1_host(cp->ssl, host)) { - log_err("SSL_set1_host failed"); - comm_point_delete(cp); - return NULL; - } - } -#elif defined(HAVE_X509_VERIFY_PARAM_SET1_HOST) - /* openssl 1.0.2 has this function that can be used for - * set1_host like verification */ - if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) { - X509_VERIFY_PARAM* param = SSL_get0_param(cp->ssl); - X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - if(!X509_VERIFY_PARAM_set1_host(param, host, strlen(host))) { - log_err("X509_VERIFY_PARAM_set1_host failed"); - comm_point_delete(cp); - return NULL; - } - } -#endif /* HAVE_SSL_SET1_HOST */ } /* set timeout on TCP connection */ diff --git a/services/outside_network.h b/services/outside_network.h index 48ef03edb..3456a3da3 100644 --- a/services/outside_network.h +++ b/services/outside_network.h @@ -570,12 +570,14 @@ struct comm_point* outnet_comm_point_for_udp(struct outside_network* outnet, * @param timeout: timeout for the TCP connection. * timeout in milliseconds, or -1 for no (change to the) timeout. * So seconds*1000. + * @param ssl: set to true for TLS. + * @param host: hostname for host name verification of TLS (or NULL if no TLS). * @return tcp_out commpoint, or NULL. */ struct comm_point* outnet_comm_point_for_tcp(struct outside_network* outnet, comm_point_callback_type* cb, void* cb_arg, struct sockaddr_storage* to_addr, socklen_t to_addrlen, - struct sldns_buffer* query, int timeout); + struct sldns_buffer* query, int timeout, int ssl, char* host); /** * Create http commpoint suitable for communication to the destination. diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 4fb9bc8ed..713e24759 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1629,7 +1629,8 @@ struct comm_point* outnet_comm_point_for_udp(struct outside_network* outnet, struct comm_point* outnet_comm_point_for_tcp(struct outside_network* outnet, comm_point_callback_type* cb, void* cb_arg, struct sockaddr_storage* to_addr, socklen_t to_addrlen, - struct sldns_buffer* query, int timeout) + struct sldns_buffer* query, int timeout, int ATTR_UNUSED(ssl), + char* ATTR_UNUSED(host)) { struct replay_runtime* runtime = (struct replay_runtime*) outnet->base;