diff --git a/CHANGELOG b/CHANGELOG index 65029d7..70dd54b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,9 +7,17 @@ iodine - IP over DNS is now easy CHANGES: -2006-11-06: 0.3.3 +2006-11-08: 0.3.4 + - Fixed handshake() buffer overflow + (Found by poplix, Secunia: SA22674 / FrSIRT/ADV-2006-4333) + - Added more tests + - More name parsing enhancements + - Now runs on Linux/AMD64 + - Added setting to change server port + +2006-11-05: 0.3.3 - Fixed possible buffer overflow - (Found by poplix) + (Found by poplix, Bugtraq ID: 20883) - Reworked dns hostname encoding 2006-09-11: 0.3.2 diff --git a/README b/README index 452e74c..0803fc1 100644 --- a/README +++ b/README @@ -73,13 +73,22 @@ can be max 63 chars. So your domain name and subdomain should be as short as possible to allow maximum throughput. +TIPS & TRICKS: + +If your port 53 is taken on a specific interface by an application that does +not use it, use -p on iodined to specify an alternate port (like -p 5353) and +use for instance iptables (on Linux) to forward the traffic: +iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to :5353 +(Sent in by Tom Schouten) + + PORTABILITY: -iodine has been tested on Linux (x86 and SPARC64), FreeBSD (x86), OpenBSD (x86), -NetBSD (x86) and MacOS X (10.3, ppc, with +iodine has been tested on Linux (x86, AMD64 and SPARC64), FreeBSD (x86), +OpenBSD (x86), NetBSD (x86) and MacOS X (10.3, ppc, with http://www-user.rhrk.uni-kl.de/~nissler/tuntap/). It should work on other -unix-like systems as well that has TUN/TAP tunneling support. Let us know if you -get it to run on other platforms. +unix-like systems as well that has TUN/TAP tunneling support (after some +patching). Let us know if you get it to run on other platforms. THE NAME: diff --git a/dns.c b/dns.c index 460932f..ade40de 100644 --- a/dns.c +++ b/dns.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,7 @@ open_dns(const char *domain, int localport, in_addr_t listen_ip) int flag; struct sockaddr_in addr; - bzero(&addr, sizeof(addr)); + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(localport); /* listen_ip already in network byte order from inet_addr, or 0 */ @@ -110,7 +111,7 @@ dns_settarget(const char *host) return -1; } - bzero(&peer, sizeof(peer)); + memset(&peer, 0, sizeof(peer)); peer.sin_family = AF_INET; peer.sin_port = htons(53); peer.sin_addr = *((struct in_addr *) h->h_addr); @@ -235,7 +236,7 @@ dns_write(int fd, int id, char *buf, int len, char flag) char *d; avail = 0xFF - strlen(topdomain) - 2; - bzero(data, sizeof(data)); + memset(data, 0, sizeof(data)); d = data; written = encode_data(buf, len, avail, d, flag); encoded = strlen(data); @@ -286,7 +287,7 @@ int dns_parse_reply(char *outbuf, int buflen, char *packet, int packetlen) { int rv; - long ttl; + uint32_t ttl; short rlen; short type; short class; @@ -309,12 +310,12 @@ dns_parse_reply(char *outbuf, int buflen, char *packet, int packetlen) rlen = 0; if(qdcount == 1) { - readname(packet, &data, name, sizeof(name)); + readname(packet, packetlen, &data, name, sizeof(name)); readshort(packet, &data, &type); readshort(packet, &data, &class); } if(ancount == 1) { - readname(packet, &data, name, sizeof(name)); + readname(packet, packetlen, &data, name, sizeof(name)); readshort(packet, &data, &type); readshort(packet, &data, &class); readlong(packet, &data, &ttl); @@ -455,7 +456,7 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen) qdcount = ntohs(header->qdcount); if(qdcount == 1) { - readname(packet, &data, name, sizeof(name) -1); + readname(packet, r, &data, name, sizeof(name) -1); name[256] = 0; readshort(packet, &data, &type); readshort(packet, &data, &class); @@ -469,9 +470,11 @@ dnsd_read(int fd, struct query *q, char *buf, int buflen) rv = decodepacket(name, buf, buflen); } } - } else { + } else if (r < 0) { // Error perror("recvfrom"); rv = 0; + } else { // Packet too small to be dns protocol + rv = 0; } return rv; diff --git a/encoding.c b/encoding.c index ef007fb..17092b0 100644 --- a/encoding.c +++ b/encoding.c @@ -127,6 +127,7 @@ encode_data(char *buf, int len, int space, char *dest, char flag) chunks = write / RAW_CHUNK; leftovers = write % RAW_CHUNK; + // flag is special character to be placed first in the encoded data if (flag != 0) { *dest = flag; } else { @@ -135,7 +136,7 @@ encode_data(char *buf, int len, int space, char *dest, char flag) } dest++; - bzero(encoded, sizeof(encoded)); + memset(encoded, 0, sizeof(encoded)); ep = encoded; dp = buf; for (i = 0; i < chunks; i++) { @@ -144,7 +145,7 @@ encode_data(char *buf, int len, int space, char *dest, char flag) dp += RAW_CHUNK; } realwrite = ENC_CHUNK * chunks; - bzero(padding, sizeof(padding)); + memset(padding, 0, sizeof(padding)); pp = padding; if (leftovers) { pp += RAW_CHUNK - leftovers; @@ -187,7 +188,7 @@ decode_data(char *dest, int size, const char *src, char *srcend) dest++; src++; - bzero(encoded, sizeof(encoded)); + memset(encoded, 0, sizeof(encoded)); ep = encoded; while(len < size && src < srcend) { if(*src == '.') { diff --git a/iodine.c b/iodine.c index 756dff1..8dddc32 100644 --- a/iodine.c +++ b/iodine.c @@ -112,8 +112,8 @@ static int handshake(int dns_fd) { struct timeval tv; - char server[128]; - char client[128]; + char server[65]; + char client[65]; char in[4096]; int timeout; fd_set fds; @@ -144,12 +144,20 @@ handshake(int dns_fd) } if (read > 0) { - if (sscanf(in, "%[^-]-%[^-]-%d", + if (sscanf(in, "%64[^-]-%64[^-]-%d", server, client, &mtu) == 3) { - if (tun_setip(client) == 0 && tun_setmtu(mtu) == 0) + + server[64] = 0; + client[64] = 0; + if (tun_setip(client) == 0 && + tun_setmtu(mtu) == 0) { + return 0; - else - warn("Received handshake but b0rk"); + } else { + warn("Received handshake with bad data"); + } + } else { + printf("Received bad handshake\n"); } } } @@ -190,7 +198,7 @@ help() { static void version() { printf("iodine IP over DNS tunneling client\n"); - printf("version: 0.3.3 from 2006-11-05\n"); + printf("version: 0.3.4 from 2006-11-08\n"); exit(0); } diff --git a/iodined.c b/iodined.c index 9bda8be..9321a53 100644 --- a/iodined.c +++ b/iodined.c @@ -111,7 +111,7 @@ tunnel(int tun_fd, int dns_fd) } if(FD_ISSET(dns_fd, &fds)) { read = dnsd_read(dns_fd, &q, in, sizeof(in)); - if (read < 0) + if (read <= 0) continue; if(in[0] == 'H' || in[0] == 'h') { @@ -168,8 +168,8 @@ static void usage() { extern char *__progname; - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] " - "tunnel_ip topdomain\n", __progname); + printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] [-p port]" + " tunnel_ip topdomain\n", __progname); exit(2); } @@ -178,8 +178,8 @@ help() { extern char *__progname; printf("iodine IP over DNS tunneling server\n"); - printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] " - "tunnel_ip topdomain\n", __progname); + printf("Usage: %s [-v] [-h] [-f] [-u user] [-t chrootdir] [-d device] [-m mtu] [-l ip address to listen on] [-p port]" + " tunnel_ip topdomain\n", __progname); printf(" -v to print version info and exit\n"); printf(" -h to print this help and exit\n"); printf(" -f to keep running in foreground\n"); @@ -188,6 +188,7 @@ help() { printf(" -d device to set tunnel device name\n"); printf(" -m mtu to set tunnel device mtu\n"); printf(" -l ip address to listen on for incoming dns traffic (default 0.0.0.0)\n"); + printf(" -p port to listen on for incoming dns traffic (default 53)\n"); printf("tunnel_ip is the IP number of the local tunnel interface.\n"); printf("topdomain is the FQDN that is delegated to this server.\n"); exit(0); @@ -196,7 +197,7 @@ help() { static void version() { printf("iodine IP over DNS tunneling server\n"); - printf("version: 0.3.3 from 2006-11-05\n"); + printf("version: 0.3.4 from 2006-11-08\n"); exit(0); } @@ -213,6 +214,7 @@ main(int argc, char **argv) int mtu; struct passwd *pw; in_addr_t listen_ip; + int port; username = NULL; newroot = NULL; @@ -220,13 +222,14 @@ main(int argc, char **argv) foreground = 0; mtu = 1024; listen_ip = INADDR_ANY; + port = 53; packetbuf.len = 0; packetbuf.offset = 0; outpacket.len = 0; q.id = 0; - while ((choice = getopt(argc, argv, "vfhu:t:d:m:l:")) != -1) { + while ((choice = getopt(argc, argv, "vfhu:t:d:m:l:p:")) != -1) { switch(choice) { case 'v': version(); @@ -252,6 +255,9 @@ main(int argc, char **argv) case 'l': listen_ip = inet_addr(optarg); break; + case 'p': + port = atoi(optarg); + break; default: usage(); break; @@ -291,7 +297,7 @@ main(int argc, char **argv) goto cleanup0; if (tun_setip(argv[0]) != 0 || tun_setmtu(mtu) != 0) goto cleanup1; - if ((dnsd_fd = open_dns(argv[1], 53, listen_ip)) == -1) + if ((dnsd_fd = open_dns(argv[1], port, listen_ip)) == -1) goto cleanup2; my_ip = inet_addr(argv[0]); diff --git a/read.c b/read.c index 07e3643..1e40dba 100644 --- a/read.c +++ b/read.c @@ -15,14 +15,16 @@ */ #include +#include static int -readname_loop(char *packet, char **src, char *dst, size_t length, size_t loop) +readname_loop(char *packet, int packetlen, char **src, char *dst, size_t length, size_t loop) { char *dummy; char *s; char *d; int len; + int offset; char c; if (loop <= 0) @@ -36,8 +38,18 @@ readname_loop(char *packet, char **src, char *dst, size_t length, size_t loop) /* is this a compressed label? */ if((c & 0xc0) == 0xc0) { - dummy = packet + (((s[-1] & 0x3f) << 8) | s[0]); - len += readname_loop(packet, &dummy, d, length - len, loop - 1); + offset = (((s[-1] & 0x3f) << 8) | (s[0] & 0xff)); + if (offset > packetlen) { + if (len == 0) { + // Bad jump first in packet + return 0; + } else { + // Bad jump after some data + break; + } + } + dummy = packet + offset; + len += readname_loop(packet, packetlen, &dummy, d, length - len, loop - 1); goto end; } @@ -65,9 +77,9 @@ end: } int -readname(char *packet, char **src, char *dst, size_t length) +readname(char *packet, int packetlen, char **src, char *dst, size_t length) { - return readname_loop(packet, src, dst, length, 10); + return readname_loop(packet, packetlen, src, dst, length, 10); } int @@ -83,19 +95,20 @@ readshort(char *packet, char **src, short *dst) } int -readlong(char *packet, char **src, long *dst) +readlong(char *packet, char **src, uint32_t *dst) { + // A long as described in dns protocol is always 32 bits unsigned char *p; p = *src; - *dst = ((long)p[0] << 24) - | ((long)p[1] << 16) - | ((long)p[2] << 8) - | ((long)p[3]); + *dst = ((uint32_t)p[0] << 24) + | ((uint32_t)p[1] << 16) + | ((uint32_t)p[2] << 8) + | ((uint32_t)p[3]); - (*src) += sizeof(long); - return sizeof(long); + (*src) += sizeof(uint32_t); + return sizeof(uint32_t); } int @@ -135,8 +148,9 @@ putshort(char **dst, short value) } int -putlong(char **dst, long value) +putlong(char **dst, uint32_t value) { + // A long as described in dns protocol is always 32 bits unsigned char *p; p = *dst; @@ -147,7 +161,7 @@ putlong(char **dst, long value) *p++ = (value); (*dst) = p; - return sizeof(long); + return sizeof(uint32_t); } int diff --git a/read.h b/read.h index 6117b7b..d695a7e 100644 --- a/read.h +++ b/read.h @@ -17,14 +17,14 @@ #ifndef _READ_H_ #define _READ_H_ -int readname(char *, char **, char *, size_t); +int readname(char *, int, char **, char *, size_t); int readshort(char *, char **, short *); -int readlong(char *, char **, long *); +int readlong(char *, char **, uint32_t *); int readdata(char *, char **, char *, size_t); int putbyte(char **, char); int putshort(char **, short); -int putlong(char **, long); +int putlong(char **, uint32_t); int putdata(char **, char *, size_t); #endif diff --git a/test.c b/test.c index 19050df..a2098cf 100644 --- a/test.c +++ b/test.c @@ -23,11 +23,13 @@ #include #endif #include +#include #include #include #include #include "structs.h" +#include "encoding.h" #include "dns.h" #include "read.h" @@ -73,10 +75,10 @@ static void test_readputlong() { char buf[4]; - long putint; - long tempi; - long tint; - long *l; + uint32_t putint; + uint32_t tempi; + uint32_t tint; + uint32_t *l; char* p; int i; @@ -89,13 +91,13 @@ test_readputlong() p = buf; putlong(&p, tint); l = &putint; - memcpy(l, buf, sizeof(int)); + memcpy(l, buf, sizeof(uint32_t)); if (putint != tempi) { printf("Bad value on putlong for %d\n", i); exit(2); } l = &tempi; - memcpy(buf, l, sizeof(int)); + memcpy(buf, l, sizeof(uint32_t)); p = buf; readlong(NULL, &p, &tempi); if (tempi != tint) { @@ -129,6 +131,13 @@ test_readname() char onejump[] = "AA\x81\x80\x00\x01\x00\x00\x00\x00\x00\x00" "\x02hh\xc0\x15\x00\x01\x00\x01\x05zBCDE\x00"; + char badjump[] = { + 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; + char badjump2[] = { + 'A', 'A', 0x81, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 'B', 'A', 0xfe, 0xcc, 0x00, 0x01, 0x00, 0x01 }; + char *jumper; char buf[1024]; char *data; int rv; @@ -136,28 +145,50 @@ test_readname() printf(" * Testing readname... "); fflush(stdout); - bzero(buf, sizeof(buf)); + memset(buf, 0, sizeof(buf)); data = emptyloop + sizeof(HEADER); buf[1023] = 'A'; - rv = readname(emptyloop, &data, buf, 1023); + rv = readname(emptyloop, sizeof(emptyloop), &data, buf, 1023); assert(buf[1023] == 'A'); - bzero(buf, sizeof(buf)); + memset(buf, 0, sizeof(buf)); data = infloop + sizeof(HEADER); buf[4] = '\a'; - rv = readname(infloop, &data, buf, 4); + rv = readname(infloop, sizeof(infloop), &data, buf, 4); assert(buf[4] == '\a'); - bzero(buf, sizeof(buf)); + memset(buf, 0, sizeof(buf)); data = longname + sizeof(HEADER); buf[256] = '\a'; - rv = readname(longname, &data, buf, 256); + rv = readname(longname, sizeof(longname), &data, buf, 256); assert(buf[256] == '\a'); - bzero(buf, sizeof(buf)); + memset(buf, 0, sizeof(buf)); data = onejump + sizeof(HEADER); - rv = readname(onejump, &data, buf, 256); + rv = readname(onejump, sizeof(onejump), &data, buf, 256); assert(rv == 9); + + // These two tests use malloc to cause segfault if jump is executed + memset(buf, 0, sizeof(buf)); + jumper = malloc(sizeof(badjump)); + if (jumper) { + memcpy(jumper, badjump, sizeof(badjump)); + data = jumper + sizeof(HEADER); + rv = readname(jumper, sizeof(badjump), &data, buf, 256); + assert(rv == 0); + } + free(jumper); + + memset(buf, 0, sizeof(buf)); + jumper = malloc(sizeof(badjump2)); + if (jumper) { + memcpy(jumper, badjump2, sizeof(badjump2)); + data = jumper + sizeof(HEADER); + rv = readname(jumper, sizeof(badjump2), &data, buf, 256); + assert(rv == 4); + assert(strcmp("BA.", buf) == 0); + } + free(jumper); printf("OK\n"); } @@ -170,6 +201,7 @@ test_encode_hostname() { len = 256; printf(" * Testing hostname encoding... "); + fflush(stdout); memset(buf, 0, 256); ret = dns_encode_hostname( // More than 63 chars between dots @@ -189,6 +221,31 @@ test_encode_hostname() { printf("OK\n"); } +static void +test_base32() { + char temp[256]; + char *start = "HELLOTEST"; + char *out = "1HELLOTEST"; + char *end; + char *tempend; + int codedlength; + + printf(" * Testing base32 encoding... "); + fflush(stdout); + + memset(temp, 0, sizeof(temp)); + end = malloc(16); + memset(end, 0, 16); + + codedlength = encode_data(start, 9, 256, temp, 0); + tempend = temp + strlen(temp); + decode_data(end, 16, temp, tempend); + assert(strcmp(out, end) == 0); + free(end); + + printf("OK\n"); +} + int main() { @@ -198,6 +255,7 @@ main() test_readputlong(); test_readname(); test_encode_hostname(); + test_base32(); printf("** All went well :)\n"); return 0; diff --git a/tun.c b/tun.c index d750362..0f5cef0 100644 --- a/tun.c +++ b/tun.c @@ -52,7 +52,7 @@ open_tun(const char *tun_device) return -1; } - bzero(&ifreq, sizeof(ifreq)); + memset(&ifreq, 0, sizeof(ifreq)); ifreq.ifr_flags = IFF_TUN;