Skip to content

Instantly share code, notes, and snippets.

@dimgrav
Last active August 28, 2017 11:21
Embed
What would you like to do?
All the code patches produced for the requirements of my GSoC 2017 project with Samba (improve libcli/dns)
0001-libcli-dns-added-README.patch
0002-libcli-dns-cli_dns-library-for-TCP-UDP-async-request.patch
0003-libcli-dns-libtcp-TCP-definitions-for-cli_dns.patch
0004-libcli-dns-libudp-UDP-definitions-for-cli_dns.patch
0005-libcli-dns-libtsig-GSS-TSIG-definitions-for-cli_dns.patch
0006-libcli-dns-wrap_cli-provides-wrap-functionality-for-.patch
0007-libcli-dns-libwrap-wrapper-definitions-for-wrap_cli.patch
0008-cli-fn-client_crypto-GSS-TSIG-signing-feature-librar.patch
0009-cli-fn-dns_tcp-TCP-send-recv-request-feature-library.patch
0010-cli-fn-dns_udp-UDP-send-recv-request-feature-library.patch
0011-cli-fn-README-feature-libraries-information.patch
0012-cmocka-tests-cli_tests-test-suite-for-cli_dns-using-.patch
0013-cmocka-tests-wscript_build-integrate-recursive-build.patch
0014-cmocka-tests-README-test-suite-information.patch
0015-cmocka-tests-test-fn-cli_crypto_test-individual-test.patch
0016-cmocka-tests-test-fn-dns_tcp_test-individual-tests-f.patch
0017-cmocka-tests-test-fn-dns_udp_test-individual-tests-f.patch
0018-cmocka-tests-test-fn-wscript-waf-script-to-build-ind.patch
0019-source4-dns_server-dns_query-replaced-libdns.h-with-.patch
0020-wscript_build-added-recursive-building-for-libcli-dn.patch
0021-libcli-dns-libdns-renamed-to-libudp.patch
0022-libcli-dns-wscript_build-modified-to-build-clidns-su.patch
0023-libcli-dns-dns.c-replaced-by-cli_dns.patch
From 6fe1afd181635b46c97619530915353cec1135ae Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:11:01 +0300
Subject: [PATCH 01/23] libcli/dns: added README
---
libcli/dns/README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 libcli/dns/README.md
diff --git a/libcli/dns/README.md b/libcli/dns/README.md
new file mode 100644
index 0000000..f5a1c53
--- /dev/null
+++ b/libcli/dns/README.md
@@ -0,0 +1,76 @@
+# Client-side DNS call handling with GSS-TSIG
+### Unix SMB/CIFS implementation
+### Dimitrios Gravanis (C) 2017
+### Based on the existing work by Samba Team
+
+--------------------------------------------------------
+About
+--------------------------------------------------------
+
+For the Samba AD DC, libcli/dns is a library that allows the handling of DNS
+calls (send/receive requests) and generates GSS-TSIG type encryption signature
+for signed packets, to accomodate encrypted client-server communication.
+
+It consists of its respective function and structure libraries, that provide
+definitions for client-side functionality.
+
+Test suites are also available, that inspect individual features of cli_dns.c
+
+For more information on the project goals, read the GSoC proposal [here](https://summerofcode.withgoogle.com/projects/#6642229069217792).
+
+The project timeline and development journal is documented in its dedicated [blogspot](https://dimgrav.blogspot.gr/).
+
+--------------------------------------------------------
+Content listing and descriptions
+--------------------------------------------------------
+
+1. cli-fn
+
+ * client_crypto.c: GSS-TSIG client-side handling for signed packets
+ * dns_tcp.c: TCP client-side DNS call handling
+ * dns_udp.c: Small async DNS library for Samba with socketwrapper support (pre-existing dns.c)
+
+2. cmocka-tests
+
+ * cli_crypto_test.c: Tests GSS-TSIG client-side handling for signed $
+ * dns_tcp_test.c: Tests TCP client-side DNS call handling
+ * dns_udp_test.c: Tests UDP client-side DNS call handling
+ * cli_tests: Complete test suite for libcli/dns/cli_dns.c
+
+3. cli_dns.c: DNS UDP/TCP call handler with socketwrapper support and TSIG generation (replaces dns.c)
+
+4. dns.h: Internal DNS query structures
+
+5. libtcp.h: TCP client-side DNS structures
+
+6. libtsig.h: GSS-TSIG client-side DNS structures and utilites
+
+7. libudp.h: Small async DNS library for Samba with socketwrapper support (pre-existing libdns.h)
+
+8. libwrap.h: DNS UDP/TCP send/recv wrap library with TSIG generation
+
+9. wrap_cli.c: DNS UDP/TCP send/recv wrapping with TSIG generation
+
+--------------------------------------------------------
+DNS Client (with wrapper support)
+--------------------------------------------------------
+
+Handles TCP and UDP requests.
+
+The client may use either TCP or UDP protocols to send a DNS name request to
+the server, then handle the reception of the appropriate server response.
+
+Features:
+
+* UDP request send/receive
+* TCP request send/receive
+* GSS-TSIG generation
+* DNS name packet parsing and signing
+
+The library consists of cli_dns.c, that includes functions, and dns.h, libtcp.h,
+libtsig.h, libudp.h, that provide declarations, definitions and structures.
+
+### Wrapping
+wrap_cli.c provides multiple wrapping of the above functionality, to hide buffer
+creation, DNS packet parsing and signature generation. Definitions of the wrapped
+functions are provided in libwrap.h.
--
2.7.4
From be0bc659c08a3e6706b616cee92817889781cec5 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:12:20 +0300
Subject: [PATCH 02/23] libcli/dns/cli_dns: library for TCP/UDP async requests
using GSS-TSIG
---
libcli/dns/cli_dns.c | 541 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 541 insertions(+)
create mode 100644 libcli/dns/cli_dns.c
diff --git a/libcli/dns/cli_dns.c b/libcli/dns/cli_dns.c
new file mode 100644
index 0000000..ab41e09
--- /dev/null
+++ b/libcli/dns/cli_dns.c
@@ -0,0 +1,541 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS UDP/TCP call handler with socketwrapper support and TSIG generation
+
+ Copyright (C) 2017 Dimitrios Gravanis <dimgrav@gmail.com>
+
+ Based on:
+
+ DNS server startup
+ DNS structures
+ Small async DNS library for Samba with socketwrapper support
+
+ Copyright (C) 2010 Kai Blin <kai@samba.org>
+
+ 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/>.
+*/
+
+/* TSIG generation */
+#include "includes.h"
+#include "lib/crypto/hmacmd5.h"
+#include "libcli/util/ntstatus.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "source4/dns_server/dns_server.h"
+#include "libcli/dns/libtsig.h"
+
+/* DNS call send/recv() */
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "source4/smbd/service_task.h"
+#include "source4/smbd/service_stream.h"
+#include "source4/lib/stream/packet.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/dns/libudp.h"
+#include "libcli/dns/libtcp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_werror.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+
+#define DNS_REQUEST_TIMEOUT 2
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DNS
+
+/*** UDP Requests ***/
+
+/* UDP state struct */
+struct dns_udp_request_state {
+ struct tevent_context *ev;
+ struct tdgram_context *dgram;
+ size_t query_len;
+ uint8_t *reply;
+ size_t reply_len;
+};
+
+/* UDP callbacks */
+void dns_udp_request_get_reply(struct tevent_req *subreq);
+void dns_udp_request_done(struct tevent_req *subreq);
+
+/* udp request to send */
+struct tevent_req *dns_udp_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ const uint8_t *query,
+ size_t query_len)
+{
+ struct tevent_req *req, *subreq;
+ struct dns_udp_request_state *state;
+ struct tsocket_address *local_addr, *server_addr;
+ struct tdgram_context *dgram;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct dns_udp_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ /* Use connected UDP sockets */
+ ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+ &local_addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
+ DNS_SERVICE_PORT, &server_addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tdgram_inet_udp_socket(local_addr, server_addr, state, &dgram);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ state->dgram = dgram;
+ state->query_len = query_len;
+
+ dump_data(10, query, query_len);
+
+ subreq = tdgram_sendto_send(state, ev, dgram, query, query_len, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (!tevent_req_set_endtime(req, ev,
+ timeval_current_ofs(DNS_REQUEST_TIMEOUT, 0))) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq, dns_udp_request_get_reply, req);
+ return req;
+}
+
+/* wait for server reply */
+void dns_udp_request_get_reply(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_sendto_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ if (len != state->query_len) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ subreq = tdgram_recvfrom_send(state, state->ev, state->dgram);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, dns_udp_request_done, req);
+}
+
+/* callback status */
+void dns_udp_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_recvfrom_recv(subreq, &err, state, &state->reply, NULL);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ state->reply_len = len;
+ dump_data(10, state->reply, state->reply_len);
+ tevent_req_done(req);
+}
+
+/* receiver */
+int dns_udp_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len)
+{
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *reply = talloc_move(mem_ctx, &state->reply);
+ *reply_len = state->reply_len;
+ tevent_req_received(req);
+
+ return 0;
+}
+
+/*** TCP Requests ***/
+
+/* TCP callbacks */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq);
+void dns_tcp_req_done(struct tevent_req *subreq);
+
+/* tcp request to send */
+struct tevent_req *dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ struct iovec *vector,
+ size_t count)
+{
+ struct tevent_req *req, *subreq, *socreq;
+ struct dns_tcp_request_state *state;
+ struct tsocket_address *local_address, *remote_address;
+ struct tstream_context *stream;
+ int req_ret, soc_ret, err;
+
+ req = tevent_req_create(mem_ctx, &state, struct dns_tcp_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ /* check for connected sockets and use if any */
+ req_ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+ &local_address);
+ if (req_ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ req_ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
+ DNS_SERVICE_PORT, &remote_address);
+ if (req_ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ /* must be reviewed! */
+ soc_ret = tstream_inet_tcp_connect_recv(socreq, err, mem_ctx, stream, NULL);
+ TALLOC_FREE(socreq);
+ if (soc_ret == -1 && err != 0) {
+ tevent_req_error(socreq, err);
+ return tevent_req_post(req, ev);
+ }
+
+ socreq = tstream_inet_tcp_connect_send(mem_ctx, ev, local_address, remote_address);
+ if (tevent_req_nomem(socreq, req)) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(socreq, dns_tcp_req_send, req);
+
+ state->tstream = stream;
+ state->v_count = count;
+
+ subreq = tstream_writev_send(mem_ctx, ev, stream, vector, count);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (!tevent_req_set_endtime(req, ev,
+ timeval_current_ofs(DNS_REQUEST_TIMEOUT, 0))) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ /* associate callback */
+ tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, req);
+
+ return req;
+}
+
+/* get buffer and wait to receive server response */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_tcp_request_state *state = tevent_req_data(req,
+ struct dns_tcp_request_state);
+ ssize_t stream_len;
+ int err = 0;
+ NTSTATUS status;
+
+ stream_len = tstream_writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+
+ if (stream_len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ if (stream_len != state->v_count) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ /* response loop */
+ struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+ struct dns_tcp_connection);
+ struct tsocket_address *local_address, *server_address;
+ struct dns_client *dns = dns_conn->dns_socket->dns; // uses server iface
+ struct dns_tcp_call *call;
+
+ call = talloc(dns_conn, struct dns_tcp_call);
+ if (call == NULL) {
+ DEBUG(1, ("dns_tcp_req_recv_reply: NULL call\n"));
+ return;
+ }
+ call->dns_conn = dns_conn;
+
+ status = tstream_read_pdu_blob_recv(subreq, call, &call->in);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("tstream_read_pdu_blob_recv: error %s\n", nt_errstr(status)));
+ return;
+ }
+
+ if (subreq == NULL) {
+ DEBUG(1, ("dns_tcp_req_recv_reply: NULL subreq\n"));
+ return;
+ }
+ tevent_req_set_callback(subreq, dns_tcp_req_done, call);
+ TALLOC_FREE(subreq);
+
+ subreq = tstream_read_pdu_blob_send(dns_conn,
+ dns_conn->conn->event.ctx,
+ dns_conn->tstream,
+ 2,
+ packet_full_request_u16,
+ dns_conn);
+ /* loop callback */
+ tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, dns_conn);
+}
+
+/* callback status */
+void dns_tcp_req_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+ struct dns_tcp_connection);
+ struct dns_tcp_call *call;
+
+ WERROR err;
+
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(1, ("dns_req_done error: %s\n", win_errstr(err)));
+ return;
+ }
+
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+}
+
+/* receiver */
+int dns_tcp_req_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len)
+{
+ struct dns_tcp_request_state *state = tevent_req_data(req,
+ struct dns_tcp_request_state);
+ int err;
+
+ /* tevent_req_is_unix_error defined in tevent_unix.h */
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *reply = talloc_move(mem_ctx, &state->reply);
+ *reply_len = state->reply_len;
+ tevent_req_received(req);
+
+ return 0;
+}
+
+/*** TSIG generation ***/
+
+/* identify tkey in record */
+struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+ const char *name)
+{
+ struct dns_client_tkey *tkey = NULL;
+ uint16_t i = 0;
+
+ do {
+ struct dns_client_tkey *tmp_key = store->tkeys[i];
+
+ i++;
+ i %= TKEY_BUFFER_SIZE;
+
+ if (tmp_key == NULL) {
+ continue;
+ }
+ if (strcmp(name, tmp_key->name) == 0) {
+ tkey = tmp_key;
+ break;
+ }
+ } while (i != 0);
+
+ return tkey;
+}
+
+/* generate signature and rebuild packet with TSIG */
+WERROR dns_cli_generate_tsig(struct dns_client *dns,
+ TALLOC_CTX *mem_ctx,
+ struct dns_request_cli_state *state,
+ struct dns_name_packet *packet,
+ DATA_BLOB *in)
+{
+ int tsig_flag = 0;
+ struct dns_client_tkey *tkey = NULL;
+ uint16_t i, arcount = 0;
+ DATA_BLOB tsig_blob, fake_tsig_blob;
+ uint8_t *buffer = NULL;
+ size_t buffer_len = 0, packet_len = 0;
+
+ NTSTATUS gen_sig;
+ DATA_BLOB sig = (DATA_BLOB) {.data = NULL, .length = 0};
+ struct dns_res_rec *tsig = NULL;
+ time_t current_time = time(NULL);
+
+ /* find TSIG record in inbound packet */
+ for (i=0; i < packet->arcount; i++) {
+ if (packet->additional[i].rr_type == DNS_QTYPE_TSIG) {
+ tsig_flag = 1;
+ break;
+ }
+ }
+ if (tsig_flag != 1) {
+ return WERR_OK;
+ }
+
+ /* check TSIG record format consistency */
+ if (tsig_flag == 1 && i + 1 != packet->arcount) {
+ DEBUG(1, ("TSIG format inconsistent!\n"));
+ return DNS_ERR(FORMAT_ERROR);
+ }
+
+ /* save the keyname from the TSIG request to add MAC later */
+ tkey = dns_find_cli_tkey(dns->tkeys, state->tsig->name);
+ if (tkey == NULL) {
+ state->key_name = talloc_strdup(state->mem_ctx,
+ state->tsig->name);
+ if (state->key_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->tsig_error = DNS_RCODE_BADKEY;
+ return DNS_ERR(NOTAUTH);
+ }
+ state->key_name = talloc_strdup(state->mem_ctx, tkey->name);
+ if (state->key_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /*
+ * preserve input packet but remove TSIG record bytes
+ * then count down the arcount field in the packet
+ */
+ packet_len = in->length - tsig_blob.length;
+ packet->arcount--;
+ arcount = RSVAL(buffer, 10);
+ RSSVAL(buffer, 10, arcount-1);
+
+ /* append fake_tsig_blob to buffer */
+ buffer_len = packet_len + fake_tsig_blob.length;
+ buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
+ if (buffer == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ memcpy(buffer, in->data, packet_len);
+ memcpy(buffer + packet_len, fake_tsig_blob.data, fake_tsig_blob.length);
+
+ /* generate signature */
+ gen_sig = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len,
+ buffer, buffer_len, &sig);
+
+ /* get MAC size and save MAC to sig*/
+ sig.length = state->tsig->rdata.tsig_record.mac_size;
+ sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length);
+ if (sig.data == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* rebuild packet with MAC from gensec_sign_packet() */
+ tsig = talloc_zero(mem_ctx, struct dns_res_rec);
+
+ tsig->name = talloc_strdup(tsig, state->key_name);
+ if (tsig->name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ tsig->rr_class = DNS_QCLASS_ANY;
+ tsig->rr_type = DNS_QTYPE_TSIG;
+ tsig->ttl = 0;
+ tsig->length = UINT16_MAX;
+ tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, "gss-tsig");
+ tsig->rdata.tsig_record.time_prefix = 0;
+ tsig->rdata.tsig_record.time = current_time;
+ tsig->rdata.tsig_record.fudge = 300;
+ tsig->rdata.tsig_record.error = state->tsig_error;
+ tsig->rdata.tsig_record.original_id = packet->id;
+ tsig->rdata.tsig_record.other_size = 0;
+ tsig->rdata.tsig_record.other_data = NULL;
+ if (sig.length > 0) {
+ tsig->rdata.tsig_record.mac_size = sig.length;
+ tsig->rdata.tsig_record.mac = talloc_memdup(tsig, sig.data, sig.length);
+ }
+
+ packet->additional = talloc_realloc(mem_ctx, packet->additional,
+ struct dns_res_rec,
+ packet->arcount + 1);
+ if (packet->additional == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ packet->arcount++;
+
+ return WERR_OK;
+}
\ No newline at end of file
--
2.7.4
From 3538e38751deb01942aa49e96fcd69e05688762a Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:13:09 +0300
Subject: [PATCH 03/23] libcli/dns/libtcp: TCP definitions for cli_dns
---
libcli/dns/libtcp.h | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 libcli/dns/libtcp.h
diff --git a/libcli/dns/libtcp.h b/libcli/dns/libtcp.h
new file mode 100644
index 0000000..50cc9ec
--- /dev/null
+++ b/libcli/dns/libtcp.h
@@ -0,0 +1,94 @@
+/* TCP client-side DNS structures.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ *
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ *
+ * 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/>.
+ */
+
+#ifndef __LIBTCP_H__
+#define __LIBTCP_H__
+
+#include "source4/dns_server/dns_server.h"
+#include "source4/dns_server/dnsserver_common.h"
+
+
+/** DNS TCP definitions **/
+struct tsocket_address;
+
+struct dns_socket {
+ struct dns_server *dns;
+ struct tsocket_address *local_address;
+};
+
+struct dns_tcp_request_state {
+ struct tevent_context *ev;
+ struct tstream_context **tstream;
+ size_t v_count;
+ uint32_t *reply;
+ size_t reply_len;
+};
+
+struct dns_tcp_connection {
+ struct stream_connection *conn;
+ struct dns_socket *dns_socket;
+ struct tstream_context *tstream;
+ struct tevent_queue *send_queue;
+};
+
+struct dns_tcp_call {
+ struct dns_tcp_connection *dns_conn;
+ DATA_BLOB in;
+ DATA_BLOB out;
+ uint8_t out_hdr[4];
+ struct iovec out_iov[2];
+};
+
+/** DNS TCP functions **/
+
+/* Send an DNS request to a DNS server via TCP
+ *
+ *@param mem_ctx talloc memory context to use
+ *@param ev tevent context to use
+ *@param server_addr_string address of the server as a string
+ *@param query dns query to send
+ *@param count length of the iovector
+ *@return tevent_req with the active request or NULL on out-of-memory
+ */
+struct tevent_req *dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ struct iovec *vector,
+ size_t count);
+
+/* Receive the DNS response from the DNS server via TCP
+ *
+ *@param req tevent_req struct returned from dns_request_send
+ *@param mem_ctx talloc memory context to use for the reply string
+ *@param reply buffer that will be allocated and filled with the dns reply
+ *@param reply_len length of the reply buffer
+ *@return 0/errno
+ */
+int dns_tcp_req_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len);
+
+/* Callbacks */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq);
+void dns_tcp_req_done(struct tevent_req *subreq);
+
+#endif /*__LIBTCP_H__*/
\ No newline at end of file
--
2.7.4
From 0ca1174209c9393ca01febb158b93faad73298d8 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:13:54 +0300
Subject: [PATCH 04/23] libcli/dns/libudp: UDP definitions for cli_dns
---
libcli/dns/libudp.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 libcli/dns/libudp.h
diff --git a/libcli/dns/libudp.h b/libcli/dns/libudp.h
new file mode 100644
index 0000000..57ab85f
--- /dev/null
+++ b/libcli/dns/libudp.h
@@ -0,0 +1,53 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Small async DNS library for Samba with socketwrapper support
+
+ Copyright (C) 2012 Kai Blin <kai@samba.org>
+
+ 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/>.
+*/
+
+#ifndef __LIBUDP_H__
+#define __LIBUDP_H__
+
+/** Send an dns request to a dns server using UDP
+ *
+ *@param mem_ctx talloc memory context to use
+ *@param ev tevent context to use
+ *@param server_address address of the server as a string
+ *@param query dns query to send
+ *@param query_len length of the query
+ *@return tevent_req with the active request or NULL on out-of-memory
+ */
+struct tevent_req *dns_udp_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_address,
+ const uint8_t *query,
+ size_t query_len);
+
+/** Get the dns response from a dns server via UDP
+ *
+ *@param req tevent_req struct returned from dns_request_send
+ *@param mem_ctx talloc memory context to use for the reply string
+ *@param reply buffer that will be allocated and filled with the dns reply
+ *@param reply_len length of the reply buffer
+ *@return 0/errno
+ */
+int dns_udp_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len);
+
+#endif /*__LIBUDP_H__*/
--
2.7.4
From 073aca58f436d3b152a21a98e9a20bda61ae0f86 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:14:26 +0300
Subject: [PATCH 05/23] libcli/dns/libtsig: GSS-TSIG definitions for cli_dns
---
libcli/dns/libtsig.h | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
create mode 100644 libcli/dns/libtsig.h
diff --git a/libcli/dns/libtsig.h b/libcli/dns/libtsig.h
new file mode 100644
index 0000000..0f7ec2f
--- /dev/null
+++ b/libcli/dns/libtsig.h
@@ -0,0 +1,114 @@
+/* GSS-TSIG client-side DNS structures and utilites.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ *
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ *
+ * 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/>.
+ */
+
+#ifndef __LIBTSIG_H__
+#define __LIBTSIG_H__
+
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+
+
+/** error definitions **/
+uint8_t werr_to_dns_err(WERROR werr);
+#define DNS_ERR(err_str) WERR_DNS_ERROR_RCODE_##err_str
+
+/** client structures **/
+struct dns_client_zone {
+ struct dns_client_zone *prev, *next;
+ const char *name;
+ struct ldb_dn *dn;
+};
+
+struct dns_client {
+ struct task_server *task;
+ struct ldb_context *samdb;
+ struct dns_client_zone *zones;
+ struct dns_client_tkey_store *tkeys;
+ struct cli_credentials *client_credentials;
+ uint16_t max_payload;
+};
+
+struct dns_request_cli_state {
+ TALLOC_CTX *mem_ctx;
+ uint16_t flags;
+ bool authenticated;
+ bool sign;
+ char *key_name;
+ struct dns_res_rec *tsig;
+ uint16_t tsig_error;
+};
+
+/** transaction key definitions **/
+#define TKEY_BUFFER_SIZE 128
+
+struct dns_client_tkey {
+ const char *name;
+ enum dns_tkey_mode mode;
+ const char *algorithm;
+ struct auth_session_info *session_info;
+ struct gensec_security *gensec;
+ bool complete;
+};
+
+struct dns_client_tkey_store {
+ struct dns_client_tkey **tkeys;
+ uint16_t next_idx;
+ uint16_t size;
+};
+
+/** functions **/
+
+/* Search for DNS key name in record to the expected name
+ *
+ *@param store dns_client_tkey_store to use for name search
+ *@param name name to match
+ *@return tkey
+ */
+struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+ const char *name);
+
+/* Make a record copy with empty TSIG rdata
+ *
+ *@param mem_ctx talloc memory context to use
+ *@param orig_record dns_res_rec struct to duplicate
+ *@param empty_record dns_res_rec struct with empty RDATA
+ *@return WERR_OK/WERR_NOT_ENOUGH_MEMORY
+ */
+WERROR dns_empty_tsig(TALLOC_CTX *mem_ctx,
+ struct dns_res_rec *orig_record,
+ struct dns_res_rec *empty_record);
+
+/* Sign packet and rebuild with TSIG
+ *
+ *@param dns dns_client structure with client internals
+ *@param mem_ctx talloc memory context to use
+ *@param packet dns_name_packet that is used
+ *@param state packet state
+ *@param in data and length of packet
+ *@return WERR_OK/_NOT_ENOUGH_MEMORY/_FORMAT_ERROR/_NOTAUTH
+ */
+WERROR dns_cli_generate_sig(struct dns_client *dns,
+ TALLOC_CTX *mem_ctx,
+ struct dns_name_packet *packet,
+ struct dns_request_cli_state *state,
+ DATA_BLOB *in);
+
+#endif /* __LIBTSIG_H__ */
\ No newline at end of file
--
2.7.4
From b38eb95bda4b232abb422bdf570136361ee70ab6 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:15:16 +0300
Subject: [PATCH 06/23] libcli/dns/wrap_cli: provides wrap functionality for
cli_dns
---
libcli/dns/wrap_cli.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 libcli/dns/wrap_cli.c
diff --git a/libcli/dns/wrap_cli.c b/libcli/dns/wrap_cli.c
new file mode 100644
index 0000000..f82ad91
--- /dev/null
+++ b/libcli/dns/wrap_cli.c
@@ -0,0 +1,83 @@
+/* DNS UDP/TCP send/recv wrapping with TSIG generation.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ *
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ *
+ * 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/>.
+ */
+
+#include "libcli/dns/libwrap.h"
+
+/* wrap dns udp/tcp req send/recv() and tsig generation functions
+ * see libcli/dns/lib*.h for wrapped function declarations
+ */
+
+/* udp */
+struct tevent_req *__wrap_udp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *server_addr_string, const uint8_t *query, size_t query_len)
+{
+ return dns_udp_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ const uint8_t *query,
+ size_t query_len);
+}
+
+int __wrap_udp_req_recv(struct tevent_req *subreq, struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, uint8_t **reply, size_t *reply_len)
+{
+ void dns_udp_request_get_reply(tevent_req *subreq);
+
+ void dns_udp_request_done(tevent_req *subreq);
+
+ return dns_udp_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **reply, size_t *reply_len);
+}
+
+/* tcp */
+struct tevent_req *__wrap_tcp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *server_addr_string, struct iovec *vector, size_t count)
+{
+ return dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ struct iovec *vector,
+ size_t count);
+}
+
+int __wrap_tcp_req_recv(struct tevent_req *subreq, struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, uint8_t **reply, size_t *reply_len)
+{
+ void dns_tcp_req_recv_reply(tevent_req *subreq);
+
+ void dns_tcp_req_done(tevent_req *subreq);
+
+ return dns_tcp_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **reply, size_t *reply_len);
+}
+
+/* tsig gen */
+WERROR __wrap_tcp_cli_tsig_gen(struct dns_client_tkey_store *store, const char *name,
+ struct dns_client *dns, TALLOC_CTX *mem_ctx, struct dns_request_state *state,
+ struct dns_name_packet *packet, DATA_BLOB *in)
+{
+ struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+ const char *name);
+
+ return dns_cli_generate_tsig(struct dns_client *dns, TALLOC_CTX *mem_ctx,
+ struct dns_request_state *state, struct dns_name_packet *packet,
+ DATA_BLOB *in);
+}
\ No newline at end of file
--
2.7.4
From abb0aa0ba8c8364c54b298a24bc47cf5bd189557 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:15:58 +0300
Subject: [PATCH 07/23] libcli/dns/libwrap: wrapper definitions for wrap_cli
---
libcli/dns/libwrap.h | 44 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 libcli/dns/libwrap.h
diff --git a/libcli/dns/libwrap.h b/libcli/dns/libwrap.h
new file mode 100644
index 0000000..071d55c
--- /dev/null
+++ b/libcli/dns/libwrap.h
@@ -0,0 +1,44 @@
+/* DNS UDP/TCP send/recv wrap library with TSIG generation.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ *
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ *
+ * 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/>.
+ */
+
+#ifndef __LIBWRAP_H__
+#define __LIBWRAP_H__
+
+/* udp */
+struct tevent_req *udp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *server_addr_string, const uint8_t *query, size_t query_len);
+
+int udp_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **reply, size_t *reply_len);
+
+/* tcp */
+struct tevent_req *tcp_req_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *server_addr_string, struct iovec *vector, size_t count);
+
+int tcp_req_recv(struct tevent_req *subreq, struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, uint8_t **reply, size_t *reply_len);
+
+/* tsig gen */
+WERROR tcp_cli_tsig_gen(struct dns_client_tkey_store *store, const char *name,
+ struct dns_client *dns, TALLOC_CTX *mem_ctx,v struct dns_request_state *state,
+ struct dns_name_packet *packet, DATA_BLOB *in);
+
+#endif /* __LIBWRAP_H__ */
\ No newline at end of file
--
2.7.4
From 2e12732efc492ffe6e3cb09b78feaab6d6819cb2 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:17:10 +0300
Subject: [PATCH 08/23] cli-fn/client_crypto: GSS-TSIG signing feature library
---
libcli/dns/cli-fn/client_crypto.c | 201 ++++++++++++++++++++++++++++++++++++++
1 file changed, 201 insertions(+)
create mode 100644 libcli/dns/cli-fn/client_crypto.c
diff --git a/libcli/dns/cli-fn/client_crypto.c b/libcli/dns/cli-fn/client_crypto.c
new file mode 100644
index 0000000..5976af8
--- /dev/null
+++ b/libcli/dns/cli-fn/client_crypto.c
@@ -0,0 +1,201 @@
+/* GSS-TSIG client-side handling for signed packets.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ *
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014.
+ *
+ * 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/>.
+ */
+
+#include "includes.h"
+#include "lib/crypto/hmacmd5.h"
+#include "libcli/util/ntstatus.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "source4/dns_server/dns_server.h"
+#include "libcli/dns/libtsig.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DNS
+
+/*
+ * make a copy of the original tsig record
+ * with null rdata values
+ */
+static WERROR dns_empty_tsig(TALLOC_CTX *mem_ctx,
+ struct dns_res_rec *orig_record,
+ struct dns_res_rec *empty_record)
+{
+ /* see /libprc/idl/dns.idl for PIDL tsig definition */
+ empty_record->name = talloc_strdup(mem_ctx, orig_record->name);
+ W_ERROR_HAVE_NO_MEMORY(empty_record->name);
+ empty_record->rr_type = orig_record->rr_type;
+ empty_record->rr_class = orig_record->rr_class;
+ empty_record->ttl = orig_record->ttl;
+ empty_record->length = orig_record->length;
+
+ /* empty tsig rdata field in the new record */
+ /* the smooth way! */
+ empty_record->rdata.tsig_record.algorithm_name = talloc_strdup(mem_ctx,
+ orig_record->rdata.tsig_record.algorithm_name);
+ W_ERROR_HAVE_NO_MEMORY(empty_record->rdata.tsig_record.algorithm_name);
+ ZERO_STRUCT(empty_record->rdata.tsig_record);
+
+ return WERR_OK;
+}
+
+/* identify tkey in record */
+struct dns_client_tkey *dns_find_cli_tkey(struct dns_client_tkey_store *store,
+ const char *name)
+{
+ struct dns_client_tkey *tkey = NULL;
+ uint16_t i = 0;
+
+ do {
+ struct dns_client_tkey *tmp_key = store->tkeys[i];
+
+ i++;
+ i %= TKEY_BUFFER_SIZE;
+
+ if (tmp_key == NULL) {
+ continue;
+ }
+ if (strcmp(name, tmp_key->name) == 0) {
+ tkey = tmp_key;
+ break;
+ }
+ } while (i != 0);
+
+ return tkey;
+}
+
+/* generate signature and rebuild packet with TSIG */
+WERROR dns_cli_generate_tsig(struct dns_client *dns,
+ TALLOC_CTX *mem_ctx,
+ struct dns_request_cli_state *state,
+ struct dns_name_packet *packet,
+ DATA_BLOB *in)
+{
+ int tsig_flag = 0;
+ struct dns_client_tkey *tkey = NULL;
+ uint16_t i, arcount = 0;
+ DATA_BLOB tsig_blob, fake_tsig_blob;
+ uint8_t *buffer = NULL;
+ size_t buffer_len = 0, packet_len = 0;
+
+ NTSTATUS gen_sig;
+ DATA_BLOB sig = (DATA_BLOB) {.data = NULL, .length = 0};
+ struct dns_res_rec *tsig = NULL;
+ time_t current_time = time(NULL);
+
+ /* find TSIG record in inbound packet */
+ for (i=0; i < packet->arcount; i++) {
+ if (packet->additional[i].rr_type == DNS_QTYPE_TSIG) {
+ tsig_flag = 1;
+ break;
+ }
+ }
+ if (tsig_flag != 1) {
+ return WERR_OK;
+ }
+
+ /* check TSIG record format consistency */
+ if (tsig_flag == 1 && i + 1 != packet->arcount) {
+ DEBUG(1, ("TSIG format inconsistent!\n"));
+ return DNS_ERR(FORMAT_ERROR);
+ }
+
+ /* save the keyname from the TSIG request to add MAC later */
+ tkey = dns_find_cli_tkey(dns->tkeys, state->tsig->name);
+ if (tkey == NULL) {
+ state->key_name = talloc_strdup(state->mem_ctx,
+ state->tsig->name);
+ if (state->key_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ state->tsig_error = DNS_RCODE_BADKEY;
+ return DNS_ERR(NOTAUTH);
+ }
+ state->key_name = talloc_strdup(state->mem_ctx, tkey->name);
+ if (state->key_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /*
+ * preserve input packet but remove TSIG record bytes
+ * then count down the arcount field in the packet
+ */
+ packet_len = in->length - tsig_blob.length;
+ packet->arcount--;
+ arcount = RSVAL(buffer, 10);
+ RSSVAL(buffer, 10, arcount-1);
+
+ /* append fake_tsig_blob to buffer */
+ buffer_len = packet_len + fake_tsig_blob.length;
+ buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
+ if (buffer == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ memcpy(buffer, in->data, packet_len);
+ memcpy(buffer + packet_len, fake_tsig_blob.data, fake_tsig_blob.length);
+
+ /* generate signature */
+ gen_sig = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len,
+ buffer, buffer_len, &sig);
+
+ /* get MAC size and save MAC to sig*/
+ sig.length = state->tsig->rdata.tsig_record.mac_size;
+ sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length);
+ if (sig.data == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* rebuild packet with MAC from gensec_sign_packet() */
+ tsig = talloc_zero(mem_ctx, struct dns_res_rec);
+
+ tsig->name = talloc_strdup(tsig, state->key_name);
+ if (tsig->name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ tsig->rr_class = DNS_QCLASS_ANY;
+ tsig->rr_type = DNS_QTYPE_TSIG;
+ tsig->ttl = 0;
+ tsig->length = UINT16_MAX;
+ tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, "gss-tsig");
+ tsig->rdata.tsig_record.time_prefix = 0;
+ tsig->rdata.tsig_record.time = current_time;
+ tsig->rdata.tsig_record.fudge = 300;
+ tsig->rdata.tsig_record.error = state->tsig_error;
+ tsig->rdata.tsig_record.original_id = packet->id;
+ tsig->rdata.tsig_record.other_size = 0;
+ tsig->rdata.tsig_record.other_data = NULL;
+ if (sig.length > 0) {
+ tsig->rdata.tsig_record.mac_size = sig.length;
+ tsig->rdata.tsig_record.mac = talloc_memdup(tsig, sig.data, sig.length);
+ }
+
+ packet->additional = talloc_realloc(mem_ctx, packet->additional,
+ struct dns_res_rec,
+ packet->arcount + 1);
+ if (packet->additional == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ packet->arcount++;
+
+ return WERR_OK;
+}
--
2.7.4
From d0fdff708f2cca609dc5b5a273e74423d7ee5bd3 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:17:43 +0300
Subject: [PATCH 09/23] cli-fn/dns_tcp: TCP send/recv request feature library
---
libcli/dns/cli-fn/dns_tcp.c | 221 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 221 insertions(+)
create mode 100644 libcli/dns/cli-fn/dns_tcp.c
diff --git a/libcli/dns/cli-fn/dns_tcp.c b/libcli/dns/cli-fn/dns_tcp.c
new file mode 100644
index 0000000..4e94e0e
--- /dev/null
+++ b/libcli/dns/cli-fn/dns_tcp.c
@@ -0,0 +1,221 @@
+/* TCP client-side DNS call handling.
+ *
+ * Copyright (C) 2017 Dimitrios Gravanis
+ *
+ * Based on the existing work on Samba Unix SMB/CIFS implementation by
+ * Kai Blin Copyright (C) 2011, Stefan Metzmacher Copyright (C) 2014
+ *
+ * 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/>.
+ */
+
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "source4/smbd/service_task.h"
+#include "source4/smbd/service_stream.h"
+#include "source4/lib/stream/packet.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/dns/libudp.h"
+#include "libcli/dns/libtcp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_werror.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+
+#define DNS_REQUEST_TIMEOUT 2
+
+/*** TCP Requests ***/
+
+/* TCP callbacks */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq);
+void dns_tcp_req_done(struct tevent_req *subreq);
+
+/* tcp request to send */
+struct tevent_req *dns_tcp_req_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ struct iovec *vector,
+ size_t count)
+{
+ struct tevent_req *req, *subreq, *socreq;
+ struct dns_tcp_request_state *state;
+ struct tsocket_address *local_address, *remote_address;
+ struct tstream_context *stream;
+ int req_ret, soc_ret, err;
+
+ req = tevent_req_create(mem_ctx, &state, struct dns_tcp_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ /* check for connected sockets and use if any */
+ req_ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+ &local_address);
+ if (req_ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ req_ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
+ DNS_SERVICE_PORT, &remote_address);
+ if (req_ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ /* must be reviewed! */
+ soc_ret = tstream_inet_tcp_connect_recv(socreq, err, mem_ctx, stream, NULL);
+ TALLOC_FREE(socreq);
+ if (soc_ret == -1 && err != 0) {
+ tevent_req_error(socreq, err);
+ return tevent_req_post(req, ev);
+ }
+
+ socreq = tstream_inet_tcp_connect_send(mem_ctx, ev, local_address, remote_address);
+ if (tevent_req_nomem(socreq, req)) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(socreq, dns_tcp_req_send, req);
+
+ state->tstream = stream;
+ state->v_count = count;
+
+ subreq = tstream_writev_send(mem_ctx, ev, stream, vector, count);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (!tevent_req_set_endtime(req, ev,
+ timeval_current_ofs(DNS_REQUEST_TIMEOUT, 0))) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ /* associate callback */
+ tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, req);
+
+ return req;
+}
+
+/* get buffer and wait to receive server response */
+void dns_tcp_req_recv_reply(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_tcp_request_state *state = tevent_req_data(req,
+ struct dns_tcp_request_state);
+ ssize_t stream_len;
+ int err = 0;
+ NTSTATUS status;
+
+ stream_len = tstream_writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+
+ if (stream_len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ if (stream_len != state->v_count) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ /* response loop */
+ struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+ struct dns_tcp_connection);
+ struct tsocket_address *local_address, *server_address;
+ struct dns_client *dns = dns_conn->dns_socket->dns; // uses server iface
+ struct dns_tcp_call *call;
+
+ call = talloc(dns_conn, struct dns_tcp_call);
+ if (call == NULL) {
+ DEBUG(1, ("dns_tcp_req_recv_reply: NULL call\n"));
+ return;
+ }
+ call->dns_conn = dns_conn;
+
+ status = tstream_read_pdu_blob_recv(subreq, call, &call->in);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("tstream_read_pdu_blob_recv: error %s\n", nt_errstr(status)));
+ return;
+ }
+
+ if (subreq == NULL) {
+ DEBUG(1, ("dns_tcp_req_recv_reply: NULL subreq\n"));
+ return;
+ }
+ tevent_req_set_callback(subreq, dns_tcp_req_done, call);
+ TALLOC_FREE(subreq);
+
+ subreq = tstream_read_pdu_blob_send(dns_conn,
+ dns_conn->conn->event.ctx,
+ dns_conn->tstream,
+ 2,
+ packet_full_request_u16,
+ dns_conn);
+ /* loop callback */
+ tevent_req_set_callback(subreq, dns_tcp_req_recv_reply, dns_conn);
+}
+
+/* callback status */
+void dns_tcp_req_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+ struct dns_tcp_connection *dns_conn = tevent_req_callback_data(subreq,
+ struct dns_tcp_connection);
+ struct dns_tcp_call *call;
+
+ WERROR err;
+
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(1, ("dns_req_done error: %s\n", win_errstr(err)));
+ return;
+ }
+
+ TALLOC_FREE(subreq);
+ tevent_req_done(req);
+}
+
+/* receiver */
+int dns_tcp_req_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len)
+{
+ struct dns_tcp_request_state *state = tevent_req_data(req,
+ struct dns_tcp_request_state);
+ int err;
+
+ /* tevent_req_is_unix_error defined in tevent_unix.h */
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *reply = talloc_move(mem_ctx, &state->reply);
+ *reply_len = state->reply_len;
+ tevent_req_received(req);
+
+ return 0;
+}
\ No newline at end of file
--
2.7.4
From a01c0c1dbeca73e4bbab08d3055a30b8dfceed3e Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:18:13 +0300
Subject: [PATCH 10/23] cli-fn/dns_udp: UDP send/recv request feature library
---
libcli/dns/cli-fn/dns_udp.c | 178 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 178 insertions(+)
create mode 100644 libcli/dns/cli-fn/dns_udp.c
diff --git a/libcli/dns/cli-fn/dns_udp.c b/libcli/dns/cli-fn/dns_udp.c
new file mode 100644
index 0000000..f8a5495
--- /dev/null
+++ b/libcli/dns/cli-fn/dns_udp.c
@@ -0,0 +1,178 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Small async DNS library for Samba with socketwrapper support
+
+ Copyright (C) 2010 Kai Blin <kai@samba.org>
+
+ 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/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/dns/libudp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+#include "librpc/gen_ndr/dns.h"
+
+struct dns_udp_request_state {
+ struct tevent_context *ev;
+ struct tdgram_context *dgram;
+ size_t query_len;
+ uint8_t *reply;
+ size_t reply_len;
+};
+
+#define DNS_REQUEST_TIMEOUT 2
+
+/* Declare callback functions used below. */
+void dns_udp_request_get_reply(struct tevent_req *subreq);
+void dns_udp_request_done(struct tevent_req *subreq);
+
+struct tevent_req *dns_udp_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *server_addr_string,
+ const uint8_t *query,
+ size_t query_len)
+{
+ struct tevent_req *req, *subreq;
+ struct dns_udp_request_state *state;
+ struct tsocket_address *local_addr, *server_addr;
+ struct tdgram_context *dgram;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct dns_udp_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+
+ /* Use connected UDP sockets */
+ ret = tsocket_address_inet_from_strings(state, "ip", NULL, 0,
+ &local_addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_inet_from_strings(state, "ip", server_addr_string,
+ DNS_SERVICE_PORT, &server_addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tdgram_inet_udp_socket(local_addr, server_addr, state, &dgram);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ return tevent_req_post(req, ev);
+ }
+
+ state->dgram = dgram;
+ state->query_len = query_len;
+
+ dump_data(10, query, query_len);
+
+ subreq = tdgram_sendto_send(state, ev, dgram, query, query_len, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (!tevent_req_set_endtime(req, ev,
+ timeval_current_ofs(DNS_REQUEST_TIMEOUT, 0))) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq, dns_udp_request_get_reply, req);
+ return req;
+}
+
+void dns_udp_request_get_reply(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_sendto_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ if (len != state->query_len) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ subreq = tdgram_recvfrom_send(state, state->ev, state->dgram);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, dns_udp_request_done, req);
+}
+
+void dns_udp_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+
+ ssize_t len;
+ int err = 0;
+
+ len = tdgram_recvfrom_recv(subreq, &err, state, &state->reply, NULL);
+ TALLOC_FREE(subreq);
+
+ if (len == -1 && err != 0) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ state->reply_len = len;
+ dump_data(10, state->reply, state->reply_len);
+ tevent_req_done(req);
+}
+
+int dns_udp_request_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **reply,
+ size_t *reply_len)
+{
+ struct dns_udp_request_state *state = tevent_req_data(req,
+ struct dns_udp_request_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ tevent_req_received(req);
+ return err;
+ }
+
+ *reply = talloc_move(mem_ctx, &state->reply);
+ *reply_len = state->reply_len;
+ tevent_req_received(req);
+
+ return 0;
+}
--
2.7.4
From b58d2eed4552646491c45ab05e860fe69b1dc2aa Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:18:54 +0300
Subject: [PATCH 11/23] cli-fn/README: feature libraries information
---
libcli/dns/cli-fn/README.md | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 libcli/dns/cli-fn/README.md
diff --git a/libcli/dns/cli-fn/README.md b/libcli/dns/cli-fn/README.md
new file mode 100644
index 0000000..bdd66ba
--- /dev/null
+++ b/libcli/dns/cli-fn/README.md
@@ -0,0 +1,17 @@
+## README: features
+
+The individual function libraries that are incorporated in cli_dns.c,
+to provide client-side DNS call features. Each library comes with a
+corresponding test suite in libcli/dns/cmocka-tests/ directory.
+
+Descriptions
+
+* client_crypto.c: GSS-TSIG client-side handling for signed packets
+* dns_tcp.c: TCP client-side DNS call handling
+* dns_udp.c: Small async DNS library with socketwrapper support
+
+It is highly recommended that the above libraries will be used for
+adding and testing features in libcli/dns individually, **BEFORE**
+implementing any changes in libcli/cli_dns.c library.
+
+*Associated headers are found in libcli/dns/*
--
2.7.4
From a4d01bcb5d7f8a39621f96e089cd0ab25234da83 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:19:47 +0300
Subject: [PATCH 12/23] cmocka-tests/cli_tests: test suite for cli_dns using
Cmocka
---
libcli/dns/cmocka-tests/cli_tests.c | 305 ++++++++++++++++++++++++++++++++++++
1 file changed, 305 insertions(+)
create mode 100644 libcli/dns/cmocka-tests/cli_tests.c
diff --git a/libcli/dns/cmocka-tests/cli_tests.c b/libcli/dns/cmocka-tests/cli_tests.c
new file mode 100644
index 0000000..090b953
--- /dev/null
+++ b/libcli/dns/cmocka-tests/cli_tests.c
@@ -0,0 +1,305 @@
+/* Unix SMB/CIFS implementation.
+ *
+ * Test suite for:
+ * DNS UDP/TCP call handler with socketwrapper support and TSIG generation
+ *
+ * Copyright 2017 (c) Dimitrios Gravanis
+ *
+ * Uses cmocka C testing API.
+ * Copyright 2013 (c) Andreas Schneider <asn@cynapses.org>
+ * Jakub Hrozek <jakub.hrozek@gmail.com>
+ *
+ * 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/>.
+ */
+
+/* test requirements */
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "libcli/dns/cli_dns.c"
+
+/* TSIG generation */
+#include "includes.h"
+#include "lib/crypto/hmacmd5.h"
+#include "libcli/util/ntstatus.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "source4/dns_server/dns_server.h"
+#include "libcli/dns/libtsig.h"
+
+/* DNS call send/recv() */
+#include "replace.h"
+#include "system/network.h"
+#include <tevent.h>
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "source4/smbd/service_task.h"
+#include "source4/smbd/service_stream.h"
+#include "source4/lib/stream/packet.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/dns.h"
+#include "librpc/gen_ndr/ndr_dns.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/dns/libudp.h"
+#include "libcli/dns/libtcp.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_werror.h"
+#include "lib/util/samba_util.h"
+#include "libcli/util/error.h"
+
+#define DNS_REQUEST_TIMEOUT 2
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_DNS
+
+
+/** test tcp send/recv functionality **/
+
+/* calls fail() if TCP test_req is NULL */
+static void test_req_send(void **state)
+{
+ /* incomplete */
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *test_ev;
+ const char *test_server_addr_string = "TEST_SRVR_ADDR";
+ struct iovec *test_vector;
+ size_t test_count = SIZE_MAX;
+
+ struct tevent_req *test_req = dns_tcp_req_send(mem_ctx, test_ev,
+ test_server_addr_string, test_vector, test_count);
+
+ assert_non_null(test_req);
+ TALLOC_FREE(mem_ctx);
+ return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_req_recv_reply(void **state)
+{
+ struct tevent_req *test_subreq;
+ assert_non_null(test_subreq);
+ dns_tcp_req_recv_reply(test_subreq);
+ return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_req_done(void **state)
+{
+ struct tevent_req *test_subreq;
+ assert_non_null(test_subreq);
+ dns_tcp_req_done(test_subreq);
+ return;
+}
+
+/* calls fail() if test_rcv is not 0 */
+static void test_req_recv(void **state)
+{
+ TALLOC_CTX *mem_ctx;
+ struct tevent_req *test_req;
+ uint8_t **test_reply = UINT8_MAX;
+ size_t *test_reply_len = SIZE_MAX;
+
+ int test_rcv = dns_tcp_req_recv(test_req, mem_ctx, test_reply, test_reply_len);
+
+ assert_int_equal(test_rcv, 0);
+ TALLOC_FREE(mem_ctx);
+ return;
+}
+
+/** test udp send/recv functionality **/
+
+/* calls fail() if UDP test_req is NULL */
+static void test_request_send(void **state)
+{
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *test_ev;
+ const char *test_server_addr_string = "TEST_SRVR_ADDR";
+ const uint8_t *test_query = UINT8_MAX;
+ size_t test_query_len = SIZE_MAX;
+
+ struct tevent_req *test_req = dns_udp_request_send(mem_ctx, test_ev,
+ test_server_addr_string, test_query, test_query_len);
+
+ assert_non_null(test_req);
+ TALLOC_FREE(mem_ctx);
+ return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_request_get_reply(void **state)
+{
+ struct tevent_req *test_subreq;
+ assert_non_null(test_subreq);
+ dns_udp_request_get_reply(test_subreq);
+ return;
+}
+
+/* calls fail() if test_subreq is NULL */
+static void test_request_done(void **state)
+{
+ struct tevent_req *test_subreq;
+ assert_non_null(test_subreq);
+ dns_udp_request_done(test_subreq);
+ return;
+}
+
+/* calls fail() if test_rcv is not 0 */
+static void test_request_recv(void **state)
+{
+ struct tevent_req *test_req;
+ TALLOC_CTX *mem_ctx;
+ uint8_t **test_reply = UINT8_MAX;
+ size_t *test_reply_len = SIZE_MAX;
+
+ int test_rcv = dns_udp_request_recv(test_req, mem_ctx, test_reply, test_reply_len);
+
+ assert_int_equal(test_rcv, 0);
+ TALLOC_FREE(mem_ctx);
+ return;
+}
+
+/** test gss-tsig functionality **/
+
+/* helper struct functions */
+static struct dns_res_rec *test_record(void) {
+
+ TALLOC_CTX *mem_ctx;
+ struct dns_res_rec *test_rec;
+ test_rec->name = "TEST_RECORD";
+ test_rec->rr_type = DNS_QTYPE_TSIG;
+ test_rec->rr_class = DNS_QCLASS_ANY;
+ test_rec->ttl = 0;
+ test_rec->length = UINT16_MAX;
+ /* rdata */
+ test_rec->rdata.tsig_record.algorithm_name = "gss-tsig";
+ test_rec->rdata.tsig_record.time_prefix = 0;
+ test_rec->rdata.tsig_record.time = 0;
+ test_rec->rdata.tsig_record.fudge = 300;
+ test_rec->rdata.tsig_record.mac_size = UINT16_MAX;
+ test_rec->rdata.tsig_record.mac = NULL;
+ test_rec->rdata.tsig_record.original_id = UINT16_MAX;
+ test_rec->rdata.tsig_record.error = UINT16_MAX;
+ test_rec->rdata.tsig_record.other_size = 0;
+ test_rec->rdata.tsig_record.other_data = NULL;
+
+ return test_rec;
+};
+
+static struct dns_client_tkey *test_tkey_name(void) {
+
+ struct dns_client_tkey *test_tkey = NULL;
+ test_tkey->name = "TEST_TKEY";
+
+ return test_tkey;
+};
+
+/* calls fail() if assertions are false */
+static void tkey_test(void **state)
+{
+ struct dns_client_tkey_store *test_store;
+ const char *test_name = "TEST_TKEY";
+
+ struct dns_client_tkey *testing;
+ struct dns_client_tkey *verifier;
+
+ testing = test_tkey_name();
+ verifier = dns_find_cli_tkey(test_store, test_name);
+
+ assert_non_null(testing);
+ assert_non_null(verifier);
+ assert_string_equal(testing->name, verifier->name);
+
+ TALLOC_FREE(testing);
+ TALLOC_FREE(verifier);
+ return;
+}
+
+/* calls fail() if test_werr not in werr_set */
+static void gen_tsig_test(void **state)
+{
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB *in_test = {NULL, SIZE_MAX};
+
+ struct dns_client *test_client;
+ test_client->samdb = NULL;
+ test_client->zones = NULL;
+ test_client->tkeys = NULL;
+ test_client->client_credentials = NULL;
+ test_client->max_payload = UINT16_MAX;
+
+ struct dns_request_cli_state *test_state;
+ test_state->flags = UINT16_MAX;
+ test_state->authenticated = true;
+ test_state->sign = true;
+ test_state->key_name = "TKEY_NAME";
+ test_state->tsig->name = "TSIG_RECORD";
+ test_state->tsig->rr_type = DNS_QTYPE_TSIG;
+ test_state->tsig->rr_class = DNS_QCLASS_ANY;
+ test_state->tsig->ttl = 0;
+ test_state->tsig->length = UINT16_MAX;
+ test_state->tsig_error = UINT16_MAX;
+
+ struct dns_name_packet *test_packet;
+ test_packet->id = UINT16_MAX;
+ test_packet->qdcount = UINT16_MAX;
+ test_packet->ancount = UINT16_MAX;
+ test_packet->nscount = UINT16_MAX;
+ test_packet->arcount = UINT16_MAX;
+
+ /* test error codes */
+ WERROR test_werr = dns_cli_generate_tsig(test_client, mem_ctx,
+ test_state, test_packet, in_test);
+
+ /* expected WERROR output */
+ assert_true(W_ERROR_IS_OK(test_werr));
+ assert_true(W_ERROR_EQUAL(WERR_NOT_ENOUGH_MEMORY, test_werr));
+ assert_true(W_ERROR_EQUAL(DNS_ERR(FORMAT_ERROR), test_werr));
+ assert_true(W_ERROR_EQUAL(DNS_ERR(NOTAUTH), test_werr));
+
+ TALLOC_FREE(mem_ctx);
+ return;
+}
+
+/* run test suite */
+int main(void)
+{
+ /* test structure */
+ const struct CMUnitTest tests[] = {
+ /* tcp */
+ cmocka_unit_test(test_req_send),
+ cmocka_unit_test(test_req_recv_reply),
+ cmocka_unit_test(test_req_done),
+ cmocka_unit_test(test_req_recv),
+ /* udp */
+ cmocka_unit_test(test_request_send),
+ cmocka_unit_test(test_request_get_reply),
+ cmocka_unit_test(test_request_done),
+ cmocka_unit_test(test_request_recv),
+ /* gss-tsig */
+ cmocka_unit_test(tkey_test),
+ cmocka_unit_test(gen_tsig_test),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
\ No newline at end of file
--
2.7.4
From 5ab2b0943eaf60fee6502032f9c6d0b7c1d5e83f Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:20:30 +0300
Subject: [PATCH 13/23] cmocka-tests/wscript_build: integrate recursive build
for cli_tests
---
libcli/dns/cmocka-tests/wscript_build | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100755 libcli/dns/cmocka-tests/wscript_build
diff --git a/libcli/dns/cmocka-tests/wscript_build b/libcli/dns/cmocka-tests/wscript_build
new file mode 100755
index 0000000..8cf4219
--- /dev/null
+++ b/libcli/dns/cmocka-tests/wscript_build
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+# cli_dns test suite
+bld.SAMBA_BINARY('client_tests',
+ source='cli_tests.c',
+ deps='LIBTSOCKET tevent-util cmocka gensec auth samba_server_gensec dnsserver_common',
+ install=False)
--
2.7.4
From 2c4b9f5333bd2ea3a8374db598317687ed8cc68e Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:21:03 +0300
Subject: [PATCH 14/23] cmocka-tests/README: test suite information
---
libcli/dns/cmocka-tests/README.md | 50 +++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 libcli/dns/cmocka-tests/README.md
diff --git a/libcli/dns/cmocka-tests/README.md b/libcli/dns/cmocka-tests/README.md
new file mode 100644
index 0000000..4c4a5b8
--- /dev/null
+++ b/libcli/dns/cmocka-tests/README.md
@@ -0,0 +1,50 @@
+## README: test suites
+
+
+*Test suite cli_tests.c is functional and currently incorporated in Samba/wscript/*
+
+*Individual test suites are functional, but stand alone waf built is not yet supported.*
+
+Tests for the client features are divided in four different test suites:
+
+* cli_crypto_test: transaction key name search and GSS-TSIG signature generation
+* dns_tcp_test: individual DNS TCP send/receive request packet test
+* dns_udp_test: individual DNS UDP send/receive request packet test
+* cli_tests: complete test suite for libcli/dns/cli_dns.c
+
+*See cli-fn for corresponding libraries*
+
+### Configure and Build complete test suite
+
+The Samba top-level wscript and wscript_build have been modified to recursively implement test suites in
+Samba builds. Running `$ waf configure && waf` in Samba top-level directory, takes care of creating the
+test executable and incorporating it during the building process.
+
+### Configure and Build individual test suites
+
+You can build and incorporate individual tests in Samba builds, by configuring Samba with the "ENABLE_SELFTEST"
+option:
+
+`$ ./configure --enable-selftest`
+
+### Configure and Build individual test suites (standalone)
+
+Samba contributors, or anyone interested in the specific code, may wish to build the individual tests for
+feature testing and/or other development purposes. To do so:
+
+In dns/cmocka-tests/test-fn/:
+```
+$ waf configure
+
+$ waf
+```
+*default build directory is set to cmocka-tests/build-tests*
+
+To clean "leftovers":
+
+```
+$ waf clean
+
+$ waf distclean
+```
+
--
2.7.4
<
From 21057db243a576e3bae217c302c66788ef101a15 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:22:03 +0300
Subject: [PATCH 15/23] cmocka-tests/test-fn/cli_crypto_test: individual tests
for TSIG crypto feature
---
libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c | 149 ++++++++++++++++++++++
1 file changed, 149 insertions(+)
create mode 100644 libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c
diff --git a/libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c b/libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c
new file mode 100644
index 0000000..412c6c1
--- /dev/null
+++ b/libcli/dns/cmocka-tests/test-fn/cli_crypto_test.c
@@ -0,0 +1,149 @@
+/* Tests GSS-TSIG client-side handling for signed packets.
+ *
+ * Copyright 2017 (c) Dimitrios Gravanis
+ *
+ * Uses cmocka C testing API.
+ * Copyright 2013 (c) Andreas Schneider <asn@cynapses.org>
+ * Jakub Hrozek <jakub.hrozek@gmail.com>
+ *
+ * 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/>.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include "libcli/dns/cli-fn/client_crypto.c"
+
+
+/** test gss-tsig functionality **/
+
+/* helper struct functions */
+static struct dns_res_rec *test_record(void) {
+
+ TALLOC_CTX *mem_ctx;
+ struct dns_res_rec *test_rec;
+ test_rec->name = "TEST_RECORD";
+ test_rec->rr_type = DNS_QTYPE_TSIG;
+ test_rec->rr_class = DNS_QCLASS_ANY;
+ test_rec->ttl = 0;
+ test_rec->length = UINT16_MAX;
+ /* rdata */
+ test_rec->rdata.tsig_record.algorithm_name = "gss-tsig";
+ test_rec->rdata.tsig_record.time_prefix = 0;
+ test_rec->rdata.tsig_record.time = 0;
+ test_rec->rdata.tsig_record.fudge = 300;
+ test_rec->rdata.tsig_record.mac_size = UINT16_MAX;
+ test_rec->rdata.tsig_record.mac = NULL;
+ test_rec->rdata.tsig_record.original_id = UINT16_MAX;
+ test_rec->rdata.tsig_record.error = UINT16_MAX;
+ test_rec->rdata.tsig_record.other_size = 0;
+ test_rec->rdata.tsig_record.other_data = NULL;
+
+ return test_rec;
+};
+
+static struct dns_client_tkey *test_tkey_name(void) {
+
+ struct dns_client_tkey *test_tkey = NULL;
+ test_tkey->name = "TEST_TKEY";
+
+ return test_tkey;
+};
+
+/* calls fail() if assertions are false */
+static void tkey_test(void **state)
+{
+ struct dns_client_tkey_store *test_store;
+ const char *test_name = "TEST_TKEY";
+
+ struct dns_client_tkey *testing;
+ struct dns_client_tkey *verifier;
+
+ testing = test_tkey_name();
+ verifier = dns_find_cli_tkey(test_store, test_name);
+
+ assert_non_null(testing);
+ assert_non_null(verifier);
+ assert_string_equal(testing->name, verifier->name);
+
+ TALLOC_FREE(testing);
+ TALLOC_FREE(verifier);
+ return;
+}
+
+/* calls fail() if test_werr not in werr_set */
+static void gen_tsig_test(void **state)
+{
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB *in_test = {NULL, SIZE_MAX};
+
+ struct dns_client *test_client;
+ test_client->samdb = NULL;
+ test_client->zones = NULL;
+ test_client->tkeys = NULL;
+ test_client->client_credentials = NULL;
+ test_client->max_payload = UINT16_MAX;
+
+ struct dns_request_cli_state *test_state;
+ test_state->flags = UINT16_MAX;
+ test_state->authenticated = true;
+ test_state->sign = true;
+ test_state->key_name = "TKEY_NAME";
+ test_state->tsig->name = "TSIG_RECORD";
+ test_state->tsig->rr_type = DNS_QTYPE_TSIG;
+ test_state->tsig->rr_class = DNS_QCLASS_ANY;