/* * Copyright (c) 2003, 2004 Niels Provos * All rights reserved. * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "tagging.h" #include "untagging.h" extern struct evbuffer *_buf; static int __inline decode_int_internal(uint32_t *pnumber, struct evbuffer *evbuf, int dodrain) { uint32_t number = 0; uint8_t *data = EVBUFFER_DATA(evbuf); int len = EVBUFFER_LENGTH(evbuf); int nibbles = 0, off; if (!len) return (-1); nibbles = ((data[0] & 0xf0) >> 4) + 1; if (nibbles > 8 || (nibbles >> 1) > len - 1) return (-1); off = nibbles; while (off > 0) { number <<= 4; if (off & 0x1) number |= data[off >> 1] & 0x0f; else number |= (data[off >> 1] & 0xf0) >> 4; off--; } len = (nibbles >> 1) + 1; if (dodrain) evbuffer_drain(evbuf, len); *pnumber = number; return (len); } int decode_int(uint32_t *pnumber, struct evbuffer *evbuf) { return (decode_int_internal(pnumber, evbuf, 1) == -1 ? -1 : 0); } int tag_peek(struct evbuffer *evbuf, uint8_t *ptag) { if (EVBUFFER_LENGTH(evbuf) < 2) return (-1); *ptag = EVBUFFER_DATA(evbuf)[0]; return (0); } int tag_peek_length(struct evbuffer *evbuf, uint32_t *plength) { struct evbuffer tmp; int res; if (EVBUFFER_LENGTH(evbuf) < 2) return (-1); tmp = *evbuf; tmp.buffer += 1; tmp.off -= 1; res = decode_int_internal(plength, &tmp, 0); if (res == -1) return (-1); *plength += res + 1; return (0); } int tag_consume(struct evbuffer *evbuf) { uint32_t len; evbuffer_drain(evbuf, 1); if (decode_int(&len, evbuf) == -1) return (-1); evbuffer_drain(evbuf, len); return (0); } /* Reads the data type from an event buffer */ int tag_unmarshal(struct evbuffer *src, uint8_t *ptag, struct evbuffer *dst) { uint8_t tag; uint16_t len; uint32_t integer; if (evbuffer_remove(src, &tag, sizeof(tag)) != sizeof(tag)) return (-1); if (decode_int(&integer, src) == -1) return (-1); len = integer; if (EVBUFFER_LENGTH(src) < len) return (-1); if (evbuffer_add(dst, EVBUFFER_DATA(src), len) == -1) return (-1); evbuffer_drain(src, len); *ptag = tag; return (len); } /* Marshaling for integers */ int tag_unmarshal_int(struct evbuffer *evbuf, uint8_t need_tag, uint32_t *pinteger) { uint8_t tag; uint16_t len; uint32_t integer; if (evbuffer_remove(evbuf, &tag, sizeof(tag)) != sizeof(tag) || tag != need_tag) return (-1); if (decode_int(&integer, evbuf) == -1) return (-1); len = integer; if (EVBUFFER_LENGTH(evbuf) < len) return (-1); evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); if (evbuffer_add(_buf, EVBUFFER_DATA(evbuf), len) == -1) return (-1); evbuffer_drain(evbuf, len); return (decode_int(pinteger, _buf)); } /* Unmarshal a fixed length tag */ int tag_unmarshal_fixed(struct evbuffer *src, uint8_t need_tag, void *data, size_t len) { uint8_t tag; /* Initialize this event buffer so that we can read into it */ evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); /* Now unmarshal a tag and check that it matches the tag we want */ if (tag_unmarshal(src, &tag, _buf) == -1 || tag != need_tag) return (-1); if (EVBUFFER_LENGTH(_buf) != len) return (-1); memcpy(data, EVBUFFER_DATA(_buf), len); return (0); } int tag_unmarshal_string(struct evbuffer *evbuf, uint8_t need_tag, char **pstring) { uint8_t tag; evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); if (tag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) return (-1); *pstring = calloc(EVBUFFER_LENGTH(_buf) + 1, 1); if (*pstring == NULL) err(1, "%s: calloc", __func__); evbuffer_remove(_buf, *pstring, EVBUFFER_LENGTH(_buf)); return (0); } int tag_unmarshal_timeval(struct evbuffer *evbuf, uint8_t need_tag, struct timeval *ptv) { uint8_t tag; uint32_t integer; evbuffer_drain(_buf, EVBUFFER_LENGTH(_buf)); if (tag_unmarshal(evbuf, &tag, _buf) == -1 || tag != need_tag) return (-1); if (decode_int(&integer, _buf) == -1) return (-1); ptv->tv_sec = integer; if (decode_int(&integer, _buf) == -1) return (-1); ptv->tv_usec = integer; return (0); } int tag_unmarshal_record(struct evbuffer *evbuf, uint8_t need_tag, struct record *record) { uint8_t tag; struct evbuffer *tmp = evbuffer_new(); if (tag_unmarshal(evbuf, &tag, tmp) == -1 || tag != need_tag) goto error; if (record_unmarshal(record, tmp) == -1) goto error; evbuffer_free(tmp); return (0); error: evbuffer_free(tmp); return (-1); } /* * Functions for un/marshaling dnet's struct addr; we create a tagged * stream to save space. Otherwise, we would have pay the overhead of * IPv6 address sizes for every kind of address. */ #define UNMARSHAL(tag, what) \ tag_unmarshal_fixed(evbuf, tag, &(what), sizeof(what)) int addr_unmarshal(struct addr* addr, struct evbuffer *evbuf) { uint32_t tmp_int; memset(addr, 0, sizeof(struct addr)); if (tag_unmarshal_int(evbuf, ADDR_TYPE, &tmp_int) == -1) return (-1); addr->addr_type = tmp_int; if (tag_unmarshal_int(evbuf, ADDR_BITS, &tmp_int) == -1) return (-1); addr->addr_bits = tmp_int; switch (addr->addr_type) { case ADDR_TYPE_ETH: tag_unmarshal_fixed(evbuf, ADDR_ADDR, &addr->addr_eth, sizeof(addr->addr_eth)); break; case ADDR_TYPE_IP: tag_unmarshal_fixed(evbuf, ADDR_ADDR, &addr->addr_ip, sizeof(addr->addr_ip)); break; case ADDR_TYPE_IP6: tag_unmarshal_fixed(evbuf, ADDR_ADDR, &addr->addr_ip6, sizeof(addr->addr_ip6)); break; default: return (-1); } return (0); } /* * Functions to un/marshal records. */ int record_unmarshal(struct record *record, struct evbuffer *evbuf) { struct evbuffer *tmp = evbuffer_new(); uint32_t integer; uint8_t tag; memset(record, 0, sizeof(struct record)); TAILQ_INIT(&record->hashes); /* The timevals are optional, so we need to check their presence */ if (tag_peek(evbuf, &tag) != -1 && tag == REC_TV_START) { if (tag_unmarshal_timeval(evbuf, REC_TV_START, &record->tv_start) == -1) goto error; } if (tag_peek(evbuf, &tag) != -1 && tag == REC_TV_END) { if (tag_unmarshal_timeval(evbuf, REC_TV_END, &record->tv_end) == -1) goto error; } evbuffer_drain(tmp, EVBUFFER_LENGTH(tmp)); if (tag_unmarshal(evbuf, &tag, tmp) == -1 || tag != REC_SRC) goto error; if (addr_unmarshal(&record->src, tmp) == -1) goto error; evbuffer_drain(tmp, EVBUFFER_LENGTH(tmp)); if (tag_unmarshal(evbuf, &tag, tmp) == -1 || tag != REC_DST) goto error; if (addr_unmarshal(&record->dst, tmp) == -1) goto error; if (tag_unmarshal_int(evbuf, REC_SRC_PORT, &integer) == -1) goto error; record->src_port = integer; if (tag_unmarshal_int(evbuf, REC_DST_PORT, &integer) == -1) goto error; record->dst_port = integer; if (tag_unmarshal_int(evbuf, REC_PROTO, &integer) == -1) goto error; record->proto = integer; if (tag_unmarshal_int(evbuf, REC_STATE, &integer) == -1) goto error; record->state = integer; while (tag_peek(evbuf, &tag) != -1) { switch(tag) { case REC_OS_FP: if (tag_unmarshal_string(evbuf, tag, &record->os_fp) == -1) goto error; break; case REC_HASH: { struct hash *tmp; if ((tmp = calloc(1, sizeof(struct hash))) == NULL) err(1, "%s: calloc", __func__); if (tag_unmarshal_fixed(evbuf, REC_HASH, tmp->digest, sizeof(tmp->digest)) == -1) { free(tmp); goto error; } TAILQ_INSERT_TAIL(&record->hashes, tmp, next); } break; case REC_BYTES: if (tag_unmarshal_int(evbuf, tag,&record->bytes) == -1) goto error; break; case REC_FLAGS: if (tag_unmarshal_int(evbuf, tag,&record->flags) == -1) goto error; break; default: syslog(LOG_DEBUG, "Ignoring unknown record tag %d", tag); tag_consume(evbuf); break; } } evbuffer_free(tmp); return (0); error: evbuffer_free(tmp); return (-1); } #define TEST_MAX_INT 6 void tagging_int_test(void) { struct evbuffer *tmp = evbuffer_new(); uint32_t integers[TEST_MAX_INT] = { 0xaf0, 0x1000, 0x1, 0xdeadbeef, 0x00, 0xbef000 }; uint32_t integer; int i; for (i = 0; i < TEST_MAX_INT; i++) { int oldlen, newlen; oldlen = EVBUFFER_LENGTH(tmp); encode_int(tmp, integers[i]); newlen = EVBUFFER_LENGTH(tmp); fprintf(stderr, "\t\tencoded 0x%08x with %d bytes\n", integers[i], newlen - oldlen); } for (i = 0; i < TEST_MAX_INT; i++) { if (decode_int(&integer, tmp) == -1) errx(1, "decode %d failed", i); if (integer != integers[i]) errx(1, "got %x, wanted %x", integer, integers[i]); } if (EVBUFFER_LENGTH(tmp) != 0) errx(1, "trailing data"); evbuffer_free(tmp); fprintf(stderr, "\t%s: OK\n", __func__); } void tagging_addr_test(void) { struct evbuffer *tmp = evbuffer_new(); struct addr one, two; addr_pton("192.168.1.16/28", &one); addr_marshal(tmp, &one); if (addr_unmarshal(&two, tmp) == -1) errx(1, "addr unmarshal failed."); if (addr_cmp(&one, &two) != 0) errx(1, "addr %s != %s", addr_ntoa(&one), addr_ntoa(&two)); evbuffer_free(tmp); fprintf(stderr, "\t%s: OK\n", __func__); } void tagging_record_test(void) { struct evbuffer *tmp = evbuffer_new(); struct record one, two; uint32_t length = 0; memset(&one, 0, sizeof(one)); memset(&two, 0, sizeof(two)); TAILQ_INIT(&one.hashes); addr_pton("127.0.0.1", &one.src); addr_pton("192.168.0.1", &one.dst); gettimeofday(&one.tv_start, NULL); one.proto = IP_PROTO_TCP; one.os_fp = "Honeyd Machine"; one.bytes = 100; record_marshal(tmp, &one); if (tag_peek_length(tmp, &length) == -1 || length == 0) errx(1, "tag_peek_length failed."); if (record_unmarshal(&two, tmp) == -1) errx(1, "record unmarshal failed."); if (strcmp(one.os_fp, two.os_fp) != 0) errx(1, "fingerprints not the same"); /* Equal out the variable fields */ free(two.os_fp); two.os_fp = one.os_fp; two.hashes = one.hashes; if (memcmp(&one, &two, sizeof(one)) != 0) errx(1, "records not the same"); evbuffer_free(tmp); fprintf(stderr, "\t%s: OK\n", __func__); } void tagging_fuzz() { u_char buffer[4096]; struct evbuffer *tmp = evbuffer_new(); rand_t *rand = rand_open(); struct record record; struct addr addr; int i, j; for (j = 0; j < 100; j++) { for (i = 0; i < sizeof(buffer); i++) buffer[i] = rand_uint8(rand); evbuffer_drain(tmp, -1); evbuffer_add(tmp, buffer, sizeof(buffer)); if (tag_unmarshal_record(tmp, 1, &record) != -1) errx(1, "tag_unmarshal_record should have failed"); if (addr_unmarshal(&addr, tmp) != -1) errx(1, "addr_unmarshal should have failed"); if (record_unmarshal(&record, tmp) != -1) errx(1, "record_unmarshal should have failed"); } /* Now insert some corruption into the tag length field */ evbuffer_drain(tmp, -1); addr_pton("127.0.0.0/20", &addr); addr_marshal(tmp, &addr); evbuffer_add(tmp, buffer, sizeof(buffer)); EVBUFFER_DATA(tmp)[1] = 0xff; if (addr_unmarshal(&addr, tmp) != -1) errx(1, "addr_unmarshal should have failed"); evbuffer_free(tmp); rand_close(rand); fprintf(stderr, "\t%s: OK\n", __func__); } void tagging_test(void) { tagging_init(); tagging_int_test(); tagging_addr_test(); tagging_record_test(); tagging_fuzz(); }