Skip to content

Instantly share code, notes, and snippets.

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;
+ 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)
+{
+ /* tests structure */
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(empty_sig_test),
+ 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 2f1784cfb0df574b9d7ff9198bf752c2e71e3c83 Mon Sep 17 00:00:00 2001
From: Dimitrios Gravanis <dimgrav@gmail.com>
Date: Sun, 27 Aug 2017 14:22:35 +0300
Subject: [PATCH 16/23] cmocka-tests/test-fn/dns_tcp_test: individual tests for
TCP requests feature
---
libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c | 100 +++++++++++++++++++++++++
1 file changed, 100 insertions(+)
create mode 100644 libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c
diff --git a/libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c b/libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c
new file mode 100644
index 0000000..72f1848
--- /dev/null
+++ b/libcli/dns/cmocka-tests/test-fn/dns_tcp_test.c
@@ -0,0 +1,100 @@
+/* Tests TCP client-side DNS call handling.
+ *
+ * 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/dns_tcp.c"
+
+
+/** test tcp send/recv functionality **/
+
+/* calls fail() if TCP test_req is NULL */
+static void test_req_send(void **state)
+{
+ 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)