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;