Hi,
I have a patch for TSIG authentication in libasr. It is enabled by the "tsig"
keyword in /etc/resolv.conf. My /etc/resolv.conf looks like this:
search centroid.eu
#nameserver 192.168.34.1
nameserver 200.46.208.61
tsig secret-key.:DONTTRY
lookup file bind
The HMAC over the TSIG is SHA256-HMAC by default but this can be expanded if
willed.
I have tested this by relinking ping and running chrome on websites that I
never visited before, it works. If you need help setting this up on a BIND
nameserver so that it can recurse a TSIG authenticated lookup, let me know
I can share my example.
The code follows. If there is an interest for this, I'll clean it up and
write a manpage, I worked on getting this working as a start.
? asr.patch
Index: asr.c
===================================================================
RCS file: /cvs/src/lib/libc/asr/asr.c,v
retrieving revision 1.50
diff -u -p -u -r1.50 asr.c
--- asr.c 16 Dec 2015 16:32:30 -0000 1.50
+++ asr.c 27 Feb 2016 10:26:33 -0000
@@ -527,6 +527,7 @@ pass0(char **tok, int n, struct asr_ctx
{
int i, j, d;
const char *e;
+ char *p;
struct sockaddr_storage ss;
if (!strcmp(tok[0], "nameserver")) {
@@ -548,6 +549,32 @@ pass0(char **tok, int n, struct asr_ctx
return;
ac->ac_domain = strdup(tok[1]);
+ } else if (!strcmp(tok[0], "tsig")) {
+ if (n != 2)
+ return;
+ if (ac->ac_use_tsig)
+ return;
+
+ p = strchr(tok[1], ':');
+ if (p == NULL)
+ return;
+
+ *p = '\0';
+ if (_asr_dname_from_fqdn(tok[1], ac->ac_tsig_key,
sizeof(ac->ac_tsig_key)) == -1)
+ return;
+
+ /* RFC 4635 */
+ if (_asr_dname_from_fqdn("hmac-sha256.", ac->ac_dn_algorithm,
sizeof(ac->ac_dn_algorithm)) == -1)
+ return;
+
+ p++;
+ ac->ac_use_tsig = 1;
+
+ ac->ac_dn_algorithm_len = strlen(ac->ac_dn_algorithm);
+ ac->ac_algorithm_size = 32;
+
+ ac->ac_tsig_password = strdup(p);
+
} else if (!strcmp(tok[0], "lookup")) {
/* ensure that each lookup is only given once */
for (i = 1; i < n; i++)
Index: asr_private.h
===================================================================
RCS file: /cvs/src/lib/libc/asr/asr_private.h,v
retrieving revision 1.38
diff -u -p -u -r1.38 asr_private.h
--- asr_private.h 16 Dec 2015 16:32:30 -0000 1.38
+++ asr_private.h 27 Feb 2016 10:26:33 -0000
@@ -135,7 +135,13 @@ struct asr_ctx {
int ac_nstimeout;
int ac_nsretries;
struct sockaddr *ac_ns[ASR_MAXNS];
-
+ /* pjp below */
+ int ac_use_tsig;
+ char *ac_tsig_password;
+ char ac_tsig_key[MAXDNAME];
+ char ac_dn_algorithm[MAXDNAME];
+ int ac_dn_algorithm_len;
+ int ac_algorithm_size;
};
struct asr {
@@ -301,6 +307,7 @@ __BEGIN_HIDDEN_DECLS
void _asr_pack_init(struct asr_pack *, char *, size_t);
int _asr_pack_header(struct asr_pack *, const struct asr_dns_header *);
int _asr_pack_query(struct asr_pack *, uint16_t, uint16_t, const char *);
+int _asr_tsig_query(struct asr_pack *, const struct asr_dns_header *, struct
asr_ctx *, const char *, uint16_t);
void _asr_unpack_init(struct asr_unpack *, const char *, size_t);
int _asr_unpack_header(struct asr_unpack *, struct asr_dns_header *);
int _asr_unpack_query(struct asr_unpack *, struct asr_dns_query *);
@@ -308,6 +315,7 @@ int _asr_unpack_rr(struct asr_unpack *,
int _asr_sockaddr_from_str(struct sockaddr *, int, const char *);
ssize_t _asr_dname_from_fqdn(const char *, char *, size_t);
ssize_t _asr_addr_as_fqdn(const char *, int, char *, size_t);
+void _asr_hmac_sha256(u_char *text, int text_len, u_char *key, int key_len,
char *digest);
/* asr.c */
static void *_asr_resolver(void);
Index: asr_utils.c
===================================================================
RCS file: /cvs/src/lib/libc/asr/asr_utils.c,v
retrieving revision 1.13
diff -u -p -u -r1.13 asr_utils.c
--- asr_utils.c 9 Sep 2015 15:49:34 -0000 1.13
+++ asr_utils.c 27 Feb 2016 10:26:34 -0000
@@ -32,6 +32,8 @@
#include <string.h>
#include <unistd.h>
+#include <sha2.h>
+
#include "asr_private.h"
static int dname_check_label(const char *, size_t);
@@ -373,6 +375,14 @@ pack_data(struct asr_pack *p, const void
}
static int
+pack_u32(struct asr_pack *p, uint32_t v)
+{
+ v = htonl(v);
+
+ return (pack_data(p, &v, 4));
+}
+
+static int
pack_u16(struct asr_pack *p, uint16_t v)
{
v = htons(v);
@@ -415,6 +425,89 @@ _asr_pack_query(struct asr_pack *p, uint
}
int
+_asr_tsig_query(struct asr_pack *p, const struct asr_dns_header *h, struct
asr_ctx *ac, const char *dname, uint16_t class)
+{
+ char buf[512];
+ int buflen, hlen;
+ int i;
+ uint32_t now;
+ char mac[32];
+ struct asr_pack pseudo, *pp;
+ struct dns_header {
+ uint16_t id;
+ uint16_t query;
+ uint16_t question;
+ uint16_t answer;
+ uint16_t nsrr;
+ uint16_t additional;
+ } *dhdr;
+
+
+ memset(&buf, 0, sizeof(buf));
+ if (p->offset < sizeof(buf))
+ memcpy(&buf, p->buf, p->offset);
+ else
+ return -1;
+
+ hlen = p->offset;
+ now = time(NULL);
+
+ pack_dname(p, ac->ac_tsig_key); /* key name */
+ pack_u16(p, T_TSIG); /* transactional signature */
+ pack_u16(p, C_ANY); /* class */
+ pack_u32(p, 0); /* ttl */
+ pack_u16(p, ac->ac_dn_algorithm_len + 2 + 4 + 2 + 2 +
ac->ac_algorithm_size + 2 + 2 + 2 + 1); /* rdata len
*/
+ pack_dname(p, ac->ac_dn_algorithm); /* algorithm name */
+ pack_u16(p, 0); /* time 1 */
+ pack_u32(p, now); /* time 2 */
+ pack_u16(p, 300); /* fudge */
+ pack_u16(p, ac->ac_algorithm_size); /* mac size */
+
+ /* start pseudo packet */
+ buflen = p->offset + sizeof(mac) + 6;
+ _asr_pack_init(&pseudo, buf, buflen);
+ pp = &pseudo;
+ pp->offset = hlen;
+
+ /*
+ * libasr has already incremented this in the header but we must
+ * take it out for digesting
+ */
+
+ dhdr = (struct dns_header *)&buf;
+ dhdr->additional = 0;
+
+ pack_dname(pp, ac->ac_tsig_key); /* key name */
+ pack_u16(pp, C_ANY); /* class */
+ pack_u32(pp, 0); /* ttl */
+ pack_dname(pp, ac->ac_dn_algorithm); /* algorithm name */
+ pack_u16(pp, 0); /* time 1 */
+ pack_u32(pp, now); /* time 2 */
+ pack_u16(pp, 300); /* fudge */
+ pack_u16(pp, 0); /* error */
+ pack_u16(pp, 0); /* other len */
+ /* end of pseudo packet */
+
+#if DEBUG
+ printf("\n");
+ for (i = 0; i < pp->offset; i++) {
+ printf("%02x", buf[i] & 0xff);
+ }
+ printf("\n");
+#endif
+
+ _asr_hmac_sha256(pp->buf, pp->offset, ac->ac_tsig_password,
strlen(ac->ac_tsig_password), (char *)&mac);
+
+ pack_data(p, mac, ac->ac_algorithm_size); /* mac */
+ pack_u16(p, ntohs(h->id)); /* original id */
+ pack_u16(p, 0); /* error */
+ pack_u16(p, 0); /* other len */
+
+
+ return (p->err) ? (-1) : (0);
+}
+
+int
_asr_sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
{
struct in_addr ina;
@@ -543,4 +636,56 @@ _asr_addr_as_fqdn(const char *addr, int
return (-1);
}
return (0);
+}
+
+void
+_asr_hmac_sha256(u_char *text, int text_len, u_char *key, int key_len, char
*digest)
+{
+ SHA2_CTX context;
+ unsigned char k_ipad[65]; /* inner padding -
+ * key XORd with ipad
+ */
+ unsigned char k_opad[65]; /* outer padding -
+ * key XORd with opad
+ */
+ unsigned char tk[32];
+ int i;
+
+ if (key_len > 64) {
+
+ SHA2_CTX tctx;
+
+ SHA256Init(&tctx);
+ SHA256Update(&tctx, key, key_len);
+ SHA256Final(tk, &tctx);
+
+ key = tk;
+ key_len = 32;
+ }
+
+ /* start out by storing key in pads */
+ bzero( k_ipad, sizeof k_ipad);
+ bzero( k_opad, sizeof k_opad);
+ bcopy( key, k_ipad, key_len);
+ bcopy( key, k_opad, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ /*
+ * perform inner SHA256
+ */
+ SHA256Init(&context);
+ SHA256Update(&context, k_ipad, 64);
+ SHA256Update(&context, text, text_len);
+ SHA256Final(digest, &context);
+ /*
+ * perform outer SHA256
+ */
+ SHA256Init(&context);
+ SHA256Update(&context, k_opad, 64);
+ SHA256Update(&context, digest, 32);
+ SHA256Final(digest, &context);
}
Index: res_mkquery.c
===================================================================
RCS file: /cvs/src/lib/libc/asr/res_mkquery.c,v
retrieving revision 1.9
diff -u -p -u -r1.9 res_mkquery.c
--- res_mkquery.c 9 Sep 2015 15:49:34 -0000 1.9
+++ res_mkquery.c 27 Feb 2016 10:26:34 -0000
@@ -62,9 +62,16 @@ res_mkquery(int op, const char *dname, i
h.flags |= RD_MASK;
h.qdcount = 1;
+ if (ac->ac_use_tsig) {
+ h.arcount = 1;
+ }
+
_asr_pack_init(&p, buf, buflen);
_asr_pack_header(&p, &h);
_asr_pack_query(&p, type, class, dn);
+
+ if (ac->ac_use_tsig)
+ _asr_tsig_query(&p, &h, ac, dn, class);
_asr_ctx_unref(ac);
Index: res_send_async.c
===================================================================
RCS file: /cvs/src/lib/libc/asr/res_send_async.c,v
retrieving revision 1.29
diff -u -p -u -r1.29 res_send_async.c
--- res_send_async.c 23 Oct 2015 00:52:09 -0000 1.29
+++ res_send_async.c 27 Feb 2016 10:26:34 -0000
@@ -378,6 +378,10 @@ setup_query(struct asr_query *as, const
h.flags |= RD_MASK;
h.qdcount = 1;
+ if (as->as_ctx->ac_use_tsig) {
+ h.arcount = 1;
+ }
+
_asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize);
_asr_pack_header(&p, &h);
_asr_pack_query(&p, type, class, dname);
@@ -386,6 +390,9 @@ setup_query(struct asr_query *as, const
errno = EINVAL;
return (-1);
}
+
+ if (as->as_ctx->ac_use_tsig)
+ _asr_tsig_query(&p, &h, as->as_ctx, dname, class);
/* Remember the parameters. */
as->as.dns.reqid = h.id;