From befdaa92d2c6ea36d6c3900b3882e7bf89934fb6 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sat, 4 Mar 2017 21:06:10 +0100 Subject: ripe-atlas-fw: imported version 4760 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- coreutils/condmv.c | 261 ++++++++++++++++++++++++++++++++++-- eperd/eooqd.c | 3 + eperd/eperd.c | 3 + eperd/evtdig.c | 341 +++++++++++++++++++++++++++++++++++------------ eperd/ping.c | 24 +++- eperd/sslgetcert.c | 12 ++ eperd/traceroute.c | 100 +++++++++----- include/libbb.h | 1 + libbb/atlas_check_addr.c | 2 + networking/httppost.c | 6 +- 10 files changed, 623 insertions(+), 130 deletions(-) diff --git a/coreutils/condmv.c b/coreutils/condmv.c index 0139779..2fd976f 100644 --- a/coreutils/condmv.c +++ b/coreutils/condmv.c @@ -6,23 +6,36 @@ #include "libbb.h" -#define SAFE_PREFIX_FROM ATLAS_DATA_NEW -#define SAFE_PREFIX_TO ATLAS_DATA_OUT +#define SAFE_PREFIX_FROM1 ATLAS_DATA_NEW +#define SAFE_PREFIX_FROM2 ATLAS_DATA_OUT +#define SAFE_PREFIX_TO1 ATLAS_DATA_OUT +#define SAFE_PREFIX_TO2 ATLAS_DATA_STORAGE #define A_FLAG (1 << 0) -#define F_FLAG (1 << 1) +#define a_FLAG (1 << 1) +#define D_FLAG (1 << 2) +#define f_FLAG (1 << 3) +#define t_FLAG (1 << 4) +#define x_FLAG (1 << 5) + +static time_t age_value; +static int cross_filesystems, append_timestamp; + +static int do_dir(char *from_dir, char *to_dir); +static int do_cprm(char *from_file, char *to_file); int condmv_main(int argc, char *argv[]) { - char *opt_add, *from, *to; + char *opt_add, *opt_age, *from, *to, *check; uint32_t opt; struct stat sb; FILE *file; time_t mytime; opt_add= NULL; + opt_age= NULL; opt_complementary= NULL; /* For when we are called by crond */ - opt= getopt32(argv, "!A:f", &opt_add); + opt= getopt32(argv, "!A:a:Dftx", &opt_add, &opt_age); if (opt == (uint32_t)-1) { @@ -39,19 +52,41 @@ int condmv_main(int argc, char *argv[]) from= argv[optind]; to= argv[optind+1]; - if (!validate_filename(from, SAFE_PREFIX_FROM)) + if (!validate_filename(from, SAFE_PREFIX_FROM1) && + !validate_filename(from, SAFE_PREFIX_FROM2)) { fprintf(stderr, "insecure from file '%s'\n", from); return 1; } - if (!validate_filename(to, SAFE_PREFIX_TO) && - !validate_filename(to, SAFE_PREFIX_FROM)) + if (!validate_filename(to, SAFE_PREFIX_TO1) && + !validate_filename(to, SAFE_PREFIX_TO2) && + !validate_filename(to, SAFE_PREFIX_FROM1)) { fprintf(stderr, "insecure to file '%s'\n", to); return 1; } - if (stat(to, &sb) == 0 && !(opt & F_FLAG)) + if (opt_age) + { + age_value= strtol(opt_age, &check, 0); + if (check[0] != '\0' || age_value <= 0) + { + fprintf(stderr, "bad age value '%s'\n", opt_age); + return 1; + } + } + else + age_value= 0; + + cross_filesystems= !!(opt & x_FLAG); + append_timestamp= !!(opt & t_FLAG); + + if (opt & D_FLAG) + { + return do_dir(from, to); + } + + if (stat(to, &sb) == 0 && !(opt & f_FLAG)) { /* Destination exists */ fprintf(stderr, "condmv: not moving, destination '%s' exists\n", @@ -98,3 +133,211 @@ int condmv_main(int argc, char *argv[]) return 0; } + +static int do_dir(char *from_dir, char *to_dir) +{ + int r, error; + size_t len, extra_len; + time_t now; + DIR *dir; + struct dirent *de; + char *from_file, *new_from_file; + char *to_file, *new_to_file; + size_t from_file_len, to_file_len; + struct stat sb; + + from_file= NULL; + from_file_len= 0; + to_file= NULL; + to_file_len= 0; + + dir= opendir(from_dir); + if (dir == NULL) + { + fprintf(stderr, "condmv: unable to open dir '%s': %s\n", + from_dir, strerror(errno)); + return 1; + } + + now= time (NULL); /* For age_value */ + + error= 0; /* Assume no failures */ + while (de= readdir(dir), de != NULL) + { + len= strlen(from_dir) + 1 + strlen(de->d_name) + 1; + if (len > from_file_len) + { + new_from_file= realloc(from_file, len); + if (new_from_file == NULL) + { + fprintf(stderr, + "condmv: out of memory (from_file)\n"); + error= 1; + break; + } + from_file= new_from_file; new_from_file= NULL; + from_file_len= len; + } + snprintf(from_file, from_file_len, "%s/%s", + from_dir, de->d_name); + r= stat(from_file, &sb); + if (r == -1) + { + fprintf(stderr, "condmv: stat %s failed: %sn", + from_file, strerror(errno)); + error= 1; + break; + } + if (!S_ISREG(sb.st_mode)) + { + /* Skip non-regular objects */ + continue; + } + + if (age_value) + { + if (sb.st_mtime + age_value > now) + continue; + } + + if (append_timestamp) + { + /* A unix timestamp is currently 10 characters. + * Allocate an extra 16 characters to have enough + * space, also for the separator. + */ + extra_len= 16; + } + else + extra_len= 0; + len= strlen(to_dir) + 1 + strlen(de->d_name) + extra_len + 1; + if (len > to_file_len) + { + new_to_file= realloc(to_file, len); + if (new_to_file == NULL) + { + fprintf(stderr, + "condmv: out of memory (to_file)\n"); + error= 1; + break; + } + to_file= new_to_file; new_to_file= NULL; + to_file_len= len; + } + if (append_timestamp) + { + snprintf(to_file, to_file_len, "%s/%s.%lu", + to_dir, de->d_name, (unsigned long)now); + } + else + { + snprintf(to_file, to_file_len, "%s/%s", + to_dir, de->d_name); + } + + /* Make sure to_file doesn't exist */ + r= stat(to_file, &sb); + if (r == 0 || (r == -1 && errno != ENOENT)) + { + /* Something wrong with to_file */ + continue; + } + + if (cross_filesystems) + { + r= do_cprm(from_file, to_file); + if (r == 0) + { + /* Okay, next one */ + continue; + } + error= 1; + break; + } + + r= rename(from_file, to_file); + if (r == -1) + { + fprintf(stderr, + "condmv: rename %s to %s failed: %s\n", + from_file, to_file, strerror(errno)); + error= 1; + break; + } + } + + closedir(dir); + + if (from_file) + { + free(from_file); + from_file= NULL; + } + if (to_file) + { + free(to_file); + to_file= NULL; + } + + return error; +} + +static int do_cprm(char *from_file, char *to_file) +{ + FILE *fp_in, *fp_out; + size_t len_in, len_out; + char buf[1024]; + + fp_in= fopen(from_file, "rb"); + if (fp_in == NULL) + { + fprintf(stderr, "condmv: cannot open '%s' for reading: %s\n", + from_file, strerror(errno)); + return 1; + } + + fp_out= fopen(to_file, "wb"); + if (fp_out == NULL) + { + fprintf(stderr, "condmv: cannot open '%s' for writing: %s\n", + to_file, strerror(errno)); + fclose(fp_in); fp_in= NULL; + return 1; + } + + for (;;) + { + len_in= fread(buf, 1, sizeof(buf), fp_in); + if (len_in == 0) + break; /* EOF or error */ + + len_out= fwrite(buf, 1, len_in, fp_out); + if (len_out != len_in) + { + fprintf(stderr, + "condmv: error writing to '%s': %s\n", + to_file, strerror(errno)); + fclose(fp_in); fp_in= NULL; + fclose(fp_out); fp_out= NULL; + unlink(to_file); + return 1; + } + } + + if (ferror(fp_in)) + { + fprintf(stderr, + "condmv: error reading from '%s': %s\n", + from_file, strerror(errno)); + fclose(fp_in); fp_in= NULL; + fclose(fp_out); fp_out= NULL; + unlink(to_file); + return 1; + } + + fclose(fp_in); fp_in= NULL; + fclose(fp_out); fp_out= NULL; + unlink(from_file); + + return 0; +} diff --git a/eperd/eooqd.c b/eperd/eooqd.c index 8c11f62..8f94614 100644 --- a/eperd/eooqd.c +++ b/eperd/eooqd.c @@ -185,6 +185,9 @@ int eooqd_main(int argc, char *argv[]) limit.rlim_max= RLIM_INFINITY; setrlimit(RLIMIT_CORE, &limit); + /* Ignore SIGPIPE, broken TCP sessions may trigger them */ + signal(SIGPIPE, SIG_IGN); + /* Create libevent event base */ EventBase= event_base_new(); if (!EventBase) diff --git a/eperd/eperd.c b/eperd/eperd.c index 83aff15..b126cce 100644 --- a/eperd/eperd.c +++ b/eperd/eperd.c @@ -336,6 +336,9 @@ int eperd_main(int argc UNUSED_PARAM, char **argv) limit.rlim_max= RLIM_INFINITY; setrlimit(RLIMIT_CORE, &limit); + /* Ignore SIGPIPE, broken TCP sessions may trigger them */ + signal(SIGPIPE, SIG_IGN); + /* Create libevent event base */ EventBase= event_base_new(); if (!EventBase) diff --git a/eperd/evtdig.c b/eperd/evtdig.c index c5879de..d09d489 100644 --- a/eperd/evtdig.c +++ b/eperd/evtdig.c @@ -71,7 +71,7 @@ /* Intervals and timeouts (all are in milliseconds unless otherwise specified) */ #define DEFAULT_NOREPLY_TIMEOUT 5000 /* 5000 msec - 0 is illegal */ -#define DEFAULT_LINE_LENGTH 80 +#define DEFAULT_LINE_LENGTH 256 #define DEFAULT_STATS_REPORT_INTERVEL 180 /* in seconds */ #define CONN_TO 5 /* TCP connection time out in seconds */ #define DEFAULT_RETRY_MAX 0 @@ -387,14 +387,14 @@ struct EDNS0_HEADER u_int8_t _edns_x; // combined rcode and edns version both zeros. u_int8_t _edns_y; // combined rcode and edns version both zeros. u_int16_t Z ; // first bit is the D0 bit. + uint16_t _edns_rdlen; // length of rdata }; // EDNS OPT pseudo-RR : eg NSID RFC 5001 struct EDNS_NSID { - uint16_t len; u_int16_t otype; - u_int16_t odata; + u_int16_t olength; // length of option data }; @@ -498,7 +498,7 @@ static char line[(DEFAULT_LINE_LENGTH+1)]; static void tdig_stats(int unused UNUSED_PARAM, const short event UNUSED_PARAM, void *h); static int tdig_delete(void *state); -static void ChangetoDnsNameFormat(u_char *dns, char * qry) ; +static int ChangetoDnsNameFormat(u_char *dns, size_t maxlen, char* qry); struct tdig_base *tdig_base_new(struct event_base *event_base); void tdig_start (void *qry); void printReply(struct query_state *qry, int wire_size, unsigned char *result); @@ -506,20 +506,20 @@ void printErrorQuick (struct query_state *qry); static void local_exit(void *state); static void *tdig_init(int argc, char *argv[], void (*done)(void *state)); static void process_reply(void * arg, int nrecv, struct timespec now); -static void mk_dns_buff(struct query_state *qry, u_char *packet); +static void mk_dns_buff(struct query_state *qry, u_char *packet, + size_t packetlen) ; int ip_addr_cmp (u_int16_t af_a, void *a, u_int16_t af_b, void *b); static void udp_dns_cb(int err, struct evutil_addrinfo *ev_res, void *arg); static void noreply_callback(int unused UNUSED_PARAM, const short event UNUSED_PARAM, void *h); static void free_qry_inst(struct query_state *qry); static void ready_callback (int unused, const short event, void * arg); -/* move the next functions from tdig.c */ u_int32_t get32b (char *p); void ldns_write_uint16(void *dst, uint16_t data); uint16_t ldns_read_uint16(const void *src); unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, int* count); -/* from tdig.c */ +int dns_namelen(unsigned char *base, size_t offset, size_t size); void print_txt_json(unsigned char *rdata, int txt_len,struct query_state *qry); @@ -673,14 +673,15 @@ int ip_addr_cmp (u_int16_t af_a, void *a, u_int16_t af_b, void *b) return 1; } -static void mk_dns_buff(struct query_state *qry, u_char *packet) +static void mk_dns_buff(struct query_state *qry, u_char *packet, + size_t packetlen) { struct DNS_HEADER *dns = NULL; - u_char *qname; + u_char *qname, *p; struct QUESTION *qinfo = NULL; struct EDNS0_HEADER *e; struct EDNS_NSID *n; - int r; + int r, qnamelen; struct buf pbuf; char *lookup_prepend; int probe_id; @@ -719,6 +720,20 @@ static void mk_dns_buff(struct query_state *qry, u_char *packet) //point to the query portion qname =(u_char *)&packet[sizeof(struct DNS_HEADER)]; + /* DNS limits the name lengths to 255 in DNS encoding. Verify that the + * buffer is big enough. Also include space for EDNS0 and NSID */ + qnamelen= 255; + + if (packetlen < + sizeof(struct DNS_HEADER) + + qnamelen + sizeof(struct QUESTION) + + 1 /* dummy dns name */ + sizeof(struct EDNS0_HEADER) + + sizeof(struct EDNS_NSID)) + { + crondlog(DIE9 "mk_dns_buff: packet size too small, got %d", + packetlen); + } + // should it be limited to clas C_IN ? if(qry->opt_prepend_probe_id ) { probe_id = get_probe_id(); @@ -731,21 +746,31 @@ static void mk_dns_buff(struct query_state *qry, u_char *packet) "%d.%lu.%s", probe_id, qry->xmit_time, qry->lookupname); - ChangetoDnsNameFormat(qname, lookup_prepend); // fill the query portion. + qnamelen= ChangetoDnsNameFormat(qname, qnamelen, + lookup_prepend); // fill the query portion. free(lookup_prepend); } else { - ChangetoDnsNameFormat(qname, qry->lookupname); // fill the query portion. + qnamelen= ChangetoDnsNameFormat(qname, qnamelen, + qry->lookupname); // fill the query portion. } - qinfo =(struct QUESTION*)&packet[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)]; + if (qnamelen == -1) + return; /* Do we need to tell anybody? */ + + qinfo =(struct QUESTION*)&packet[sizeof(struct DNS_HEADER) + qnamelen]; qinfo->qtype = htons(qry->qtype); qinfo->qclass = htons(qry->qclass); - qry->pktsize = (strlen((const char*)qname) + 1) + sizeof(struct DNS_HEADER) + sizeof(struct QUESTION) ; + qry->pktsize = (sizeof(struct DNS_HEADER) + qnamelen + + sizeof(struct QUESTION)) ; if(qry->opt_nsid || qry->opt_dnssec || (qry->opt_edns0 > 512)) { - e=(struct EDNS0_HEADER*)&packet[ qry->pktsize + 1 ]; + p= &packet[qry->pktsize]; + *p= 0; /* encoding of '.' */ + qry->pktsize++; + + e=(struct EDNS0_HEADER*)&packet[ qry->pktsize ]; e->otype = htons(ns_t_opt); e->_edns_udp_size = htons(qry->opt_edns0); if(qry->opt_dnssec) { @@ -754,6 +779,7 @@ static void mk_dns_buff(struct query_state *qry, u_char *packet) else { e->Z = 0x0; } + e->_edns_rdlen = htons(0); crondlog(LVL5 "opt header in hex | %02X %02X %02X %02X %02X %02X %02X %02X %02X | %02X", packet[qry->pktsize], packet[qry->pktsize + 1], @@ -767,15 +793,16 @@ static void mk_dns_buff(struct query_state *qry, u_char *packet) packet[qry->pktsize + 9]); qry->pktsize += sizeof(struct EDNS0_HEADER) ; + dns->add_count = htons(1); if(qry->opt_nsid ) { - dns->add_count = htons(1); - n=(struct EDNS_NSID*)&packet[ qry->pktsize + 1 ]; - n->len = htons(4); + n=(struct EDNS_NSID*)&packet[ qry->pktsize ]; + e->_edns_rdlen = htons(sizeof(struct EDNS_NSID)); n->otype = htons(3); + n->olength = htons(0); + qry->pktsize += sizeof(struct EDNS_NSID); } - qry->pktsize += sizeof(struct EDNS_NSID) + 1; - dns->add_count = htons(1); + /* Transmit the request over the network */ } buf_init(&pbuf, -1); @@ -821,7 +848,7 @@ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event //AA delete qry->outbuff = outbuff; qry->xmit_time= time(NULL); clock_gettime(CLOCK_MONOTONIC_RAW, &qry->xmit_time_ts); - mk_dns_buff(qry, outbuff); + mk_dns_buff(qry, outbuff, MAX_DNS_OUT_BUF_SIZE); do { if (qry->udp_fd != -1) { @@ -1091,9 +1118,9 @@ static void tcp_connected(struct tu_env *env, struct bufferevent *bev) getsockname(bufferevent_getfd(bev), &qry->loc_sin6, &qry->loc_socklen); qry->bev_tcp = bev; - outbuff = xzalloc(MAX_DNS_BUF_SIZE); + outbuff = xzalloc(MAX_DNS_OUT_BUF_SIZE); bzero(outbuff, MAX_DNS_OUT_BUF_SIZE); - mk_dns_buff(qry, outbuff); + mk_dns_buff(qry, outbuff, MAX_DNS_OUT_BUF_SIZE); payload_len = (uint16_t) qry->pktsize; wire = xzalloc (payload_len + 4); ldns_write_uint16(wire, qry->pktsize); @@ -1153,6 +1180,7 @@ static void tcp_readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr) if (qry->response_out) fwrite(b2, 2, 1, qry->resp_file); qry->wire_size = ldns_read_uint16(b2); +qry->wire_size= sizeof(struct DNS_HEADER); buf_init(&qry->packet, -1); } else { @@ -1161,6 +1189,18 @@ static void tcp_readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr) buf_add(&qry->err, line, strlen(line)); } } + + /* We need at least a header */ + if (qry->wire_size < sizeof(struct DNS_HEADER)) + { + snprintf(line, DEFAULT_LINE_LENGTH, "%s \"TCPREADSIZE\" : " + " \"reply too small, got %zu\"" + , qry->err.size ? ", " : "" + , (size_t)qry->wire_size); + buf_add(&qry->err, line, strlen(line)); + printReply (qry, 0, NULL); + return; + } for (;;) { if (qry->response_in) n= fread(line, 1, 1, qry->resp_file); @@ -1976,8 +2016,9 @@ void tdig_start (void *arg) if(qry->resolv_max ) { free(qry->server_name); qry->server_name = NULL; - qry->server_name = qry->nslist[qry->resolv_i]; - qry->nslist[qry->resolv_i]= NULL; + qry->server_name = + strdup(qry->nslist + [qry->resolv_i]); } else { crondlog(LVL5 "AAA RESOLV QUERY FREE %s resolv_max is zero %d i %d", qry->server_name, qry->resolv_max, qry->resolv_i); @@ -2179,23 +2220,68 @@ static void tdig_stats(int unusg_statsed UNUSED_PARAM, const short event UNUSED_ } -static void ChangetoDnsNameFormat(u_char * dns, char* qry) +/* Convert a string into DNS format. This is for a query so no compression. + * DNS format is a length byte followed by the contents of a label. We + * can assume that length of the DNS format is one larger than the original + * string because of the dots (except for just '.' where the length is the + * same). + */ +static int ChangetoDnsNameFormat(u_char *dns, size_t maxlen, char* qry) { - int lock = 0, i; + size_t qrylen, labellen; + char *src, *e; + u_char *dst; + + qrylen= strlen(qry); + + if (qrylen+1 > maxlen) + { + // printf("ChangetoDnsNameFormat: name too long\n"); + return -1; /* Doesn't fit */ + } + + if (strcmp(qry, ".") == 0) + { + /* This doesn't fit in our regular schedule */ + dns[0]= 0; + return 1; + } - for(i = 0 ; i < (int)strlen((char*)qry) ; i++) + src= qry; + dst= dns; + for (; src[0] != '\0' && dst < dns+maxlen;) { - //printf ("%c", qry[i] ); - if(qry[i]=='.') + e= strchr(src, '.'); + if (e == NULL) { - *dns++=i-lock; - for(;lock 63) + { + // printf("ChangetoDnsNameFormat: label too long\n"); + return -1; /* Can't do more than 63 */ + } + if (labellen == 0) + { + // printf("ChangetoDnsNameFormat: empty label\n"); + return -1; /* Take care of lonely '.' earlier */ } + + *dst= labellen; + dst++; + memcpy(dst, src, labellen); + src= e+1; + dst += labellen; } - *dns++=0; + + /* End, at a trailing null label */ + *dst= 0; + dst++; + + return dst-dns; } @@ -2254,6 +2340,10 @@ static void free_qry_inst(struct query_state *qry) free (qry->server_name); qry->server_name = NULL; } + if (qry->nslist[qry->resolv_i] == NULL) + { + crondlog(DIE9 "free_qry_inst: qry %p, no resolver at index %d, max %d", qry, qry->resolv_i, qry->resolv_max); + } qry->server_name = strdup(qry->nslist[qry->resolv_i]); qry->qst = STATUS_NEXT_QUERY; evtimer_add(&qry->next_qry_timer, &asap); @@ -2425,7 +2515,6 @@ void printErrorQuick (struct query_state *qry) void printReply(struct query_state *qry, int wire_size, unsigned char *result) { int i, stop=0; - unsigned char *qname, *reader; struct DNS_HEADER *dnsR = NULL; struct RES_RECORD answers[20]; //the replies from the DNS server void *ptr = NULL; @@ -2434,8 +2523,10 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) u_int32_t serial; int iMax ; int flagAnswer = 0; - int data_len, offset; + int data_len, len; int write_out = FALSE; + unsigned offset; + unsigned char *name1= NULL, *name2= NULL; int fw = get_atlas_fw_version(); int lts = get_timesync(); @@ -2526,19 +2617,26 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) if(result) { - dnsR = (struct DNS_HEADER*) result; - - //point to the query portion - qname =(unsigned char*)&result[sizeof(struct DNS_HEADER)]; - - //move ahead of the dns header and the query field - reader = &result[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)]; - snprintf(line, DEFAULT_LINE_LENGTH, ",\"result\" : { \"rt\" : %.3f,", qry->triptime); buf_add(&qry->result,line, strlen(line)); JD (size, wire_size); + + if(qry->opt_abuf) { + snprintf(line, DEFAULT_LINE_LENGTH, "\"abuf\" : \""); + buf_add(&qry->result,line, strlen(line)); + buf_add_b64(&qry->result, result, wire_size, 0); + AS("\""); + } + + if (wire_size < sizeof(struct DNS_HEADER)) + goto truncated; + + dnsR = (struct DNS_HEADER*) result; + + buf_add(&qry->result, ",", 1); JU (ID, ntohs(dnsR->id)); + /* fprintf (fh, " , \"RCODE\" : %d", dnsR->rcode); fprintf (fh, " , \"AA\" : %d", dnsR->aa); @@ -2549,12 +2647,17 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) JU (NSCOUNT, ntohs(dnsR->ns_count)); JU_NC (ARCOUNT, ntohs(dnsR->add_count)); - if(qry->opt_abuf) { - snprintf(line, DEFAULT_LINE_LENGTH, ",\"abuf\" : \""); - buf_add(&qry->result,line, strlen(line)); - buf_add_b64(&qry->result, result, wire_size, 0); - AS("\""); - } + /* Start just after header */ + offset= sizeof(struct DNS_HEADER); + + len= dns_namelen(result, offset, wire_size); + if (len == -1) + goto truncated; + + offset += len + sizeof(struct QUESTION); + + if (offset > wire_size) + goto truncated; stop=0; iMax = 0; @@ -2568,24 +2671,31 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) for(i=0;i - result + wire_size) + if (offset + sizeof(struct R_DATA) > + wire_size) { /* Report error? */ - break; + goto truncated; } - answers[i].resource = (struct R_DATA*)(reader); - reader = reader + sizeof(struct R_DATA); + answers[i].resource = + (struct R_DATA*)(result+offset); + offset += sizeof(struct R_DATA); answers[i].rdata = NULL; if(ntohs(answers[i].resource->type)==T_TXT) //txt { data_len = ntohs(answers[i].resource->data_len); + + if (offset+data_len > wire_size) + goto truncated; + if(flagAnswer == 0) { AS(",\"answers\" : [ {"); flagAnswer++; @@ -2596,17 +2706,37 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) flagAnswer++; JS (TYPE, "TXT"); JS (NAME, answers[i].name); - offset= reader-result; - if (offset+data_len > wire_size) - data_len= wire_size-offset; - print_txt_json(reader, + print_txt_json(result+offset, data_len, qry); - reader = reader + ntohs(answers[i].resource->data_len); + offset += data_len; AS("}"); } else if (ntohs(answers[i].resource->type)== T_SOA) { + name1= name2= NULL; + name1 = ReadName( + result,wire_size, + offset,&stop); + if (stop == -1) + goto truncated; + offset += stop; + name2 = ReadName( + result,wire_size, + offset,&stop); + if (stop == -1) + { + free(name1); name1= NULL; + goto truncated; + } + offset += stop; + if (offset+5*4 > wire_size) + { + free(name1); name1= NULL; + free(name2); name2= NULL; + goto truncated; + } + if(flagAnswer == 0) { AS(",\"answers\" : [ { "); } @@ -2618,26 +2748,27 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) JS(TYPE, "SOA"); JSDOT(NAME, answers[i].name); JU(TTL, ntohl(answers[i].resource->ttl)); - answers[i].rdata = ReadName( - result,wire_size, - reader-result,&stop); - JSDOT( MNAME, answers[i].rdata); - reader = reader + stop; - free(answers[i].rdata); - answers[i].rdata = ReadName( - result,wire_size, - reader-result,&stop); - JSDOT( RNAME, answers[i].rdata); - reader = reader + stop; - serial = get32b((char *)reader); + JSDOT( MNAME, name1); + free(name1); name1= NULL; + JSDOT( RNAME, name2); + free(name2); name2= NULL; + + serial = get32b(result+offset); JU_NC(SERIAL, serial); - reader = reader + 4; - reader = reader + 16; // skip REFRESH, RETRY, EXIPIRE, and MINIMUM + offset += 4; + + offset += 4*4; // skip REFRESH, RETRY, EXIPIRE, and MINIMUM AS(" } "); } else { - reader = reader + ntohs(answers[i].resource->data_len); + data_len = ntohs(answers[i]. + resource->data_len); + + if (offset+data_len > wire_size) + goto truncated; + + offset += data_len; } // free mem @@ -2653,6 +2784,7 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) free(answers[i].name); } +truncated: AS (" }"); //result { } if(qry->err.size) @@ -2729,7 +2861,8 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, snprintf((char *)name, sizeof(name), "format-error at %lu: value 0x%x", offset, len); - //abort(); + *count= -1; + free(name); name= NULL; return name; } @@ -2739,7 +2872,8 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, snprintf((char *)name, sizeof(name), "offset-error at %lu: offset %lu", offset, noffset); - //abort(); + *count= -1; + free(name); name= NULL; return name; } @@ -2749,7 +2883,8 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, snprintf((char *)name, sizeof(name), "too many redirects at %lu", offset); - //abort(); + *count= -1; + free(name); name= NULL; return name; } @@ -2770,7 +2905,8 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, snprintf((char *)name, sizeof(name), "buf-bounds-error at %lu: len %d", offset, len); - //abort(); + *count= -1; + free(name); name= NULL; return name; } @@ -2779,7 +2915,8 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, snprintf((char *)name, sizeof(name), "name-length-error at %lu: len %d", offset, p+len+1); - //abort(); + *count= -1; + free(name); name= NULL; return name; } memcpy(name+p, base+offset+1, len); @@ -2806,6 +2943,42 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, return name; } +int dns_namelen(unsigned char *base, size_t offset, size_t size) +{ + size_t start_offset; + unsigned int len; + + start_offset= offset; + + //figure out the length of a name in 3www6google3com format + while(offset < size) + { + len= base[offset]; + if (len & 0xc0) + { + if ((len & 0xc0) != 0xc0) + { + /* Bad format */ + return -1; + } + + offset++; + break; + } + if (offset+len+1 > size) + { + return -1; + } + + offset += len+1; + + if (len == 0) + break; + } + + return offset-start_offset; +} + /* get 4 bytes from memory * eg. used to extract serial number from soa packet */ diff --git a/eperd/ping.c b/eperd/ping.c index 583d512..a479312 100644 --- a/eperd/ping.c +++ b/eperd/ping.c @@ -87,7 +87,7 @@ struct pingbase void (*done)(void *state); /* Called when a ping is done */ - u_char packet [MAX_DATA_SIZE]; + u_char packet[MAX_DATA_SIZE]; }; struct pingstate @@ -720,7 +720,6 @@ static void ready_callback4 (int __attribute((unused)) unused, /* Pointer to relevant portions of the packet (IP, ICMP and user * data) */ ip = (struct ip *) base->packet; - data = (struct evdata *) (base->packet + IPHDR + ICMP_MINLEN); /* Time the packet has been received */ clock_gettime(CLOCK_MONOTONIC_RAW, &now); @@ -791,7 +790,8 @@ static void ready_callback4 (int __attribute((unused)) unused, hlen = ip->ip_hl * 4; /* Check the IP header */ - if (nrecv < hlen + ICMP_MINLEN || ip->ip_hl < 5) + if (nrecv < hlen + ICMP_MINLEN + sizeof (struct evdata) || + ip->ip_hl < 5) { /* One more too short packet */ goto done; @@ -811,8 +811,15 @@ static void ready_callback4 (int __attribute((unused)) unused, } /* Check the ICMP payload for legal values of the 'index' portion */ + data = (struct evdata *) (base->packet + hlen + ICMP_MINLEN); if (data->index >= base->tabsiz || base->table[data->index] == NULL) + { +#if 0 + printf("ready_callback4: bad index: got %d\n", + data->index); +#endif goto done; + } /* Get the pointer to the host descriptor in our internal table */ if (state != base->table[data->index]) @@ -899,6 +906,7 @@ static void ready_callback6 (int __attribute((unused)) unused, struct pingstate *state; int nrecv, isDup; + size_t icmp_len; struct sockaddr_in6 remote; /* responding internet address */ struct icmp6_hdr *icmp; @@ -918,8 +926,8 @@ static void ready_callback6 (int __attribute((unused)) unused, /* Pointer to relevant portions of the packet (IP, ICMP and user * data) */ icmp = (struct icmp6_hdr *) base->packet; - data = (struct evdata *) (base->packet + - offsetof(struct icmp6_hdr, icmp6_data16[2])); + icmp_len= offsetof(struct icmp6_hdr, icmp6_data16[2]); + data = (struct evdata *) (base->packet + icmp_len); /* Time the packet has been received */ clock_gettime(CLOCK_MONOTONIC_RAW, &now); @@ -995,6 +1003,12 @@ static void ready_callback6 (int __attribute((unused)) unused, fwrite(&remote, sizeof(remote), 1, state->resp_file_out); } + if (nrecv < icmp_len+sizeof(struct evdata)) + { + // printf("ready_callback6: short packet\n"); + goto done; + } + /* Check the ICMP header to drop unexpected packets due to * unrecognized id */ diff --git a/eperd/sslgetcert.c b/eperd/sslgetcert.c index 6660b40..cba0f42 100644 --- a/eperd/sslgetcert.c +++ b/eperd/sslgetcert.c @@ -410,6 +410,11 @@ static int msgbuf_read(struct state *state, struct msgbuf *msgbuf, int *typep, *majorp= p[1]; *minorp= p[2]; len= (p[3] << 8) + p[4]; + /* Note that buf_read may reallocate msgbuf->inbuf->buf, + * which invalidates p. For this reason, after buf_read + * either return to the caller, or use 'continue' to + * restart at the top of the loop. + */ if (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5 + len) { r= buf_read(state, msgbuf->inbuf); @@ -1264,6 +1269,13 @@ static int eat_certificate(struct state *state) return -1; } len= (p[1] << 16) + (p[2] << 8) + p[3]; + + /* Note that msgbuf_read may cause the buffer + * (msgbuf->buffer.buf) to be reallocated. This will make + * p a wild pointer. To counter that, after msgbuf_read, + * either return an error to the caller or use 'continue' + * to restart at the top of the loop. + */ if (msgbuf->buffer.size - msgbuf->buffer.offset < 4+len) { r= msgbuf_read(state, msgbuf, &type, diff --git a/eperd/traceroute.c b/eperd/traceroute.c index b335a9d..e29f174 100644 --- a/eperd/traceroute.c +++ b/eperd/traceroute.c @@ -74,7 +74,10 @@ struct trtbase */ void (*done)(void *state); - u_char packet[MAX_DATA_SIZE]; + /* Leave some space for headers. The various traceroute variations + * have to check that it fits. + */ + u_char packet[MAX_DATA_SIZE+128]; }; struct trtstate @@ -602,6 +605,12 @@ static void send_pkt(struct trtstate *state) tcphdr->doff= len / 4; tcphdr->syn= 1; + if (len+state->curpacksize > sizeof(base->packet)) + { + crondlog( + DIE9 "base->packet too small, need at least %d", + len+state->curpacksize); + } if (state->curpacksize > 0) { memset(&base->packet[len], '\0', @@ -709,6 +718,14 @@ static void send_pkt(struct trtstate *state) if (state->curpacksize < len) state->curpacksize= len; + if (ICMP6_HDR+state->curpacksize > + sizeof(base->packet)) + { + crondlog( + DIE9 "base->packet too small, need at least %d", + ICMP6_HDR+state->curpacksize); + } + if (state->curpacksize > len) { memset(&base->packet[ICMP6_HDR+len], '\0', @@ -981,6 +998,12 @@ static void send_pkt(struct trtstate *state) tcphdr->doff= len / 4; tcphdr->syn= 1; + if (len+state->curpacksize > sizeof(base->packet)) + { + crondlog( + DIE9 "base->packet too small, need at least %d", + len+state->curpacksize); + } if (state->curpacksize > 0) { memset(&base->packet[len], '\0', @@ -1094,14 +1117,26 @@ static void send_pkt(struct trtstate *state) len= offsetof(struct icmp, icmp_data[2]); - if (state->curpacksize+ICMP_MINLEN < len) + /* currpacksize is the amount of data after the + * ICMP header. len is the minimal amount of data + * including the ICMP header. Later len becomes + * the packet size including ICMP header. + */ + if (ICMP_MINLEN+state->curpacksize < len) state->curpacksize= len-ICMP_MINLEN; - if (state->curpacksize+ICMP_MINLEN > len) + if (ICMP_MINLEN+state->curpacksize > + sizeof(base->packet)) + { + crondlog( + DIE9 "base->packet too small, need at least %d", + ICMP_MINLEN+state->curpacksize); + } + if (ICMP_MINLEN+state->curpacksize > len) { memset(&base->packet[len], '\0', - state->curpacksize-ICMP_MINLEN-len); + ICMP_MINLEN+state->curpacksize-len); strcpy((char *)&base->packet[len], id); - len= state->curpacksize+ICMP_MINLEN; + len= ICMP_MINLEN+state->curpacksize; } if (state->parismod) @@ -2961,7 +2996,7 @@ static void ready_callback6(int __attribute((unused)) unused, ssize_t nrecv; int ind, rcvdttl, late, isDup, nxt, icmp_prefixlen, offset; unsigned nextmtu, seq, optlen, hbhoptsize, dstoptsize; - size_t ehdrsiz, v6info_siz, siz; + size_t v6info_siz, siz; struct trtbase *base; struct trtstate *state; struct ip6_hdr *eip; @@ -3136,7 +3171,8 @@ static void ready_callback6(int __attribute((unused)) unused, return; } - /* Make sure we have TCP, UDP, ICMP or a fragment header */ + /* Make sure we have TCP, UDP, ICMP, a fragment header or + * an options header */ if (eip->ip6_nxt == IPPROTO_FRAGMENT || eip->ip6_nxt == IPPROTO_HOPOPTS || eip->ip6_nxt == IPPROTO_DSTOPTS || @@ -3144,7 +3180,6 @@ static void ready_callback6(int __attribute((unused)) unused, eip->ip6_nxt == IPPROTO_UDP || eip->ip6_nxt == IPPROTO_ICMPV6) { - ehdrsiz= 0; frag= NULL; nxt= eip->ip6_nxt; ptr= &eip[1]; @@ -3153,12 +3188,12 @@ static void ready_callback6(int __attribute((unused)) unused, /* Make sure the options header is completely * there. */ - if (nrecv < sizeof(*icmp) + sizeof(*eip) - + sizeof(*opthdr)) + offset= (u_char *)ptr - base->packet; + if (offset + sizeof(*opthdr) > nrecv) { #if 0 printf( - "ready_callback6: too short %d (icmp+ip+opt)\n", + "ready_callback6: too short %d (HOPOPTS)\n", (int)nrecv); #endif return; @@ -3166,13 +3201,11 @@ static void ready_callback6(int __attribute((unused)) unused, opthdr= (struct ip6_ext *)ptr; hbhoptsize= 8*opthdr->ip6e_len; optlen= hbhoptsize+8; - if (nrecv < sizeof(*icmp) + sizeof(*eip) + - optlen) + if (offset + optlen > nrecv) { /* Does not contain the full header */ return; } - ehdrsiz += optlen; nxt= opthdr->ip6e_nxt; ptr= ((char *)opthdr)+optlen; } @@ -3181,12 +3214,12 @@ static void ready_callback6(int __attribute((unused)) unused, /* Make sure the fragment header is completely * there. */ - if (nrecv < sizeof(*icmp) + sizeof(*eip) - + sizeof(*frag)) + offset= (u_char *)ptr - base->packet; + if (offset + sizeof(*frag) > nrecv) { #if 0 printf( - "ready_callback6: too short %d (icmp+ip+frag)\n", + "ready_callback6: too short %d (FRAGMENT)\n", (int)nrecv); #endif return; @@ -3199,7 +3232,6 @@ static void ready_callback6(int __attribute((unused)) unused, */ return; } - ehdrsiz += sizeof(*frag); nxt= frag->ip6f_nxt; ptr= &frag[1]; } @@ -3208,12 +3240,12 @@ static void ready_callback6(int __attribute((unused)) unused, /* Make sure the options header is completely * there. */ - if (nrecv < sizeof(*icmp) + sizeof(*eip) - + sizeof(*opthdr)) + offset= (u_char *)ptr - base->packet; + if (offset + sizeof(*opthdr) > nrecv) { #if 0 printf( - "ready_callback6: too short %d (icmp+ip+opt)\n", + "ready_callback6: too short %d (DSTOPTS)\n", (int)nrecv); #endif return; @@ -3221,13 +3253,16 @@ static void ready_callback6(int __attribute((unused)) unused, opthdr= (struct ip6_ext *)ptr; dstoptsize= 8*opthdr->ip6e_len; optlen= dstoptsize+8; - if (nrecv < sizeof(*icmp) + sizeof(*eip) + - optlen) + if (offset + optlen > nrecv) { /* Does not contain the full header */ +#if 0 + printf( + "ready_callback6: too short %d (full DSTOPTS)\n", + (int)nrecv); +#endif return; } - ehdrsiz += optlen; nxt= opthdr->ip6e_nxt; ptr= ((char *)opthdr)+optlen; } @@ -3235,19 +3270,19 @@ static void ready_callback6(int __attribute((unused)) unused, v6info_siz= sizeof(*v6info); if (nxt == IPPROTO_TCP) { - ehdrsiz += sizeof(*etcp); + siz= sizeof(*etcp); v6info_siz= 0; } else if (nxt == IPPROTO_UDP) - ehdrsiz += sizeof(*eudp); + siz= sizeof(*eudp); else - ehdrsiz += sizeof(*eicmp); + siz= sizeof(*eicmp); /* Now check if there is also a header in the * packet. */ - if (nrecv < sizeof(*icmp) + sizeof(*eip) - + ehdrsiz + v6info_siz) + offset= (u_char *)ptr - base->packet; + if (offset + siz + v6info_siz > nrecv) { #if 0 printf( @@ -3885,8 +3920,11 @@ for (i= 0; argv[i] != NULL; i++) */ do_tcp= !!(opt & OPT_T); do_udp= !(do_icmp || do_tcp); - if (maxpacksize > sizeof(trt_base->packet)) - maxpacksize= sizeof(trt_base->packet); + if (maxpacksize > MAX_DATA_SIZE) + { + crondlog(LVL8 "max. packet size too big"); + return NULL; + } if (response_in) { diff --git a/include/libbb.h b/include/libbb.h index 7325ede..96c9bab 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -309,6 +309,7 @@ extern char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC; #define ATLAS_DATA_OUT ATLAS_HOME "/data/out" #define ATLAS_DATA_OOQ_OUT ATLAS_HOME "/data/ooq.out" #define ATLAS_DATA_NEW ATLAS_HOME "/data/new" +#define ATLAS_DATA_STORAGE ATLAS_HOME "/data/storage" #define ATLAS_TIMESYNC_FILE ATLAS_DATA_NEW "/timesync.vol" #define ATLAS_FUZZING ATLAS_HOME "/data" diff --git a/libbb/atlas_check_addr.c b/libbb/atlas_check_addr.c index a3a69f3..87245aa 100644 --- a/libbb/atlas_check_addr.c +++ b/libbb/atlas_check_addr.c @@ -30,6 +30,8 @@ static bad_ipv6[] = { { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001 }, 128 }, /* ::1 loopback */ + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0x0000, 0x0000 }, + 96 }, /* ::ffff:0:0/96 IPv4-mapped */ { { 0xE000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, 3 }, /* e000::/3 ULA, link local, multicast */ }; diff --git a/networking/httppost.c b/networking/httppost.c index b6e01da..edc88a4 100644 --- a/networking/httppost.c +++ b/networking/httppost.c @@ -19,6 +19,7 @@ #define SAFE_PREFIX_DATA_OUT ATLAS_DATA_OUT #define SAFE_PREFIX_DATA_OOQ_OUT ATLAS_DATA_OOQ_OUT #define SAFE_PREFIX_DATA_NEW ATLAS_DATA_NEW +#define SAFE_PREFIX_DATA_STORAGE ATLAS_DATA_STORAGE #define SAFE_PREFIX_STATUS ATLAS_STATUS /* Maximum number of files to post in one go with post-dir */ @@ -372,7 +373,10 @@ int httppost_main(int argc, char *argv[]) { fprintf(stderr, "posting file '%s'\n", p); if (!validate_filename(p, SAFE_PREFIX_DATA_OUT) && - !validate_filename(p, SAFE_PREFIX_DATA_OOQ_OUT)) + !validate_filename(p, + SAFE_PREFIX_DATA_OOQ_OUT) && + !validate_filename(p, + SAFE_PREFIX_DATA_STORAGE)) { report("protected file (post dir) '%s'", p); goto err; -- cgit v1.2.3