diff options
| author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2016-09-27 21:11:07 +0000 |
|---|---|---|
| committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2016-09-27 21:11:07 +0000 |
| commit | 27c2fff0f2fef695b0599fc3931cacfc16376e88 (patch) | |
| tree | b2599c622858ea78bd8237ce2ee38b62725dabf9 /daemon | |
| parent | a6533d88996e7570cf04db0d99b6012d25a953d3 (diff) | |
Notes
Diffstat (limited to 'daemon')
| -rw-r--r-- | daemon/acl_list.c | 250 | ||||
| -rw-r--r-- | daemon/acl_list.h | 29 | ||||
| -rw-r--r-- | daemon/daemon.c | 23 | ||||
| -rw-r--r-- | daemon/remote.c | 50 | ||||
| -rw-r--r-- | daemon/remote.h | 4 | ||||
| -rw-r--r-- | daemon/unbound.c | 15 | ||||
| -rw-r--r-- | daemon/worker.c | 12 |
7 files changed, 350 insertions, 33 deletions
diff --git a/daemon/acl_list.c b/daemon/acl_list.c index 84d099ca5092..d09b46e5e046 100644 --- a/daemon/acl_list.c +++ b/daemon/acl_list.c @@ -45,6 +45,8 @@ #include "util/log.h" #include "util/config_file.h" #include "util/net_help.h" +#include "services/localzone.h" +#include "sldns/str2wire.h" struct acl_list* acl_list_create(void) @@ -71,21 +73,21 @@ acl_list_delete(struct acl_list* acl) } /** insert new address into acl_list structure */ -static int +static struct acl_addr* acl_list_insert(struct acl_list* acl, struct sockaddr_storage* addr, socklen_t addrlen, int net, enum acl_access control, int complain_duplicates) { - struct acl_addr* node = regional_alloc(acl->region, + struct acl_addr* node = regional_alloc_zero(acl->region, sizeof(struct acl_addr)); if(!node) - return 0; + return NULL; node->control = control; if(!addr_tree_insert(&acl->tree, &node->node, addr, addrlen, net)) { if(complain_duplicates) verbose(VERB_QUERY, "duplicate acl address ignored."); } - return 1; + return node; } /** apply acl_list string */ @@ -125,6 +127,156 @@ acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2, return 1; } +/** find or create node (NULL on parse or error) */ +static struct acl_addr* +acl_find_or_create(struct acl_list* acl, const char* str) +{ + struct acl_addr* node; + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) { + log_err("cannot parse netblock: %s", str); + return NULL; + } + /* find or create node */ + if(!(node=(struct acl_addr*)addr_tree_find(&acl->tree, &addr, + addrlen, net))) { + /* create node, type 'allow' since otherwise tags are + * pointless, can override with specific access-control: cfg */ + if(!(node=(struct acl_addr*)acl_list_insert(acl, &addr, + addrlen, net, acl_allow, 1))) { + log_err("out of memory"); + return NULL; + } + } + return node; +} + +/** apply acl_tag string */ +static int +acl_list_tags_cfg(struct acl_list* acl, const char* str, uint8_t* bitmap, + size_t bitmaplen) +{ + struct acl_addr* node; + if(!(node=acl_find_or_create(acl, str))) + return 0; + node->taglen = bitmaplen; + node->taglist = regional_alloc_init(acl->region, bitmap, bitmaplen); + if(!node->taglist) { + log_err("out of memory"); + return 0; + } + return 1; +} + +/** apply acl_tag_action string */ +static int +acl_list_tag_action_cfg(struct acl_list* acl, struct config_file* cfg, + const char* str, const char* tag, const char* action) +{ + struct acl_addr* node; + int tagid; + enum localzone_type t; + if(!(node=acl_find_or_create(acl, str))) + return 0; + /* allocate array if not yet */ + if(!node->tag_actions) { + node->tag_actions = (uint8_t*)regional_alloc_zero(acl->region, + sizeof(*node->tag_actions)*cfg->num_tags); + if(!node->tag_actions) { + log_err("out of memory"); + return 0; + } + node->tag_actions_size = (size_t)cfg->num_tags; + } + /* parse tag */ + if((tagid=find_tag_id(cfg, tag)) == -1) { + log_err("cannot parse tag (define-tag it): %s %s", str, tag); + return 0; + } + if((size_t)tagid >= node->tag_actions_size) { + log_err("tagid too large for array %s %s", str, tag); + return 0; + } + if(!local_zone_str2type(action, &t)) { + log_err("cannot parse access control action type: %s %s %s", + str, tag, action); + return 0; + } + node->tag_actions[tagid] = (uint8_t)t; + return 1; +} + +/** check wire data parse */ +static int +check_data(const char* data) +{ + char buf[65536]; + uint8_t rr[LDNS_RR_BUF_SIZE]; + size_t len = sizeof(rr); + int res; + snprintf(buf, sizeof(buf), "%s %s", "example.com.", data); + res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL, 0, + NULL, 0); + if(res == 0) + return 1; + log_err("rr data [char %d] parse error %s", + (int)LDNS_WIREPARSE_OFFSET(res)-13, + sldns_get_errorstr_parse(res)); + return 0; +} + +/** apply acl_tag_data string */ +static int +acl_list_tag_data_cfg(struct acl_list* acl, struct config_file* cfg, + const char* str, const char* tag, const char* data) +{ + struct acl_addr* node; + int tagid; + char* dupdata; + if(!(node=acl_find_or_create(acl, str))) + return 0; + /* allocate array if not yet */ + if(!node->tag_datas) { + node->tag_datas = (struct config_strlist**)regional_alloc_zero( + acl->region, sizeof(*node->tag_datas)*cfg->num_tags); + if(!node->tag_datas) { + log_err("out of memory"); + return 0; + } + node->tag_datas_size = (size_t)cfg->num_tags; + } + /* parse tag */ + if((tagid=find_tag_id(cfg, tag)) == -1) { + log_err("cannot parse tag (define-tag it): %s %s", str, tag); + return 0; + } + if((size_t)tagid >= node->tag_datas_size) { + log_err("tagid too large for array %s %s", str, tag); + return 0; + } + + /* check data? */ + if(!check_data(data)) { + log_err("cannot parse access-control-tag data: %s %s '%s'", + str, tag, data); + return 0; + } + + dupdata = regional_strdup(acl->region, data); + if(!dupdata) { + log_err("out of memory"); + return 0; + } + if(!cfg_region_strlist_insert(acl->region, + &(node->tag_datas[tagid]), dupdata)) { + log_err("out of memory"); + return 0; + } + return 1; +} + /** read acl_list config */ static int read_acl_list(struct acl_list* acl, struct config_file* cfg) @@ -138,6 +290,77 @@ read_acl_list(struct acl_list* acl, struct config_file* cfg) return 1; } +/** read acl tags config */ +static int +read_acl_tags(struct acl_list* acl, struct config_file* cfg) +{ + struct config_strbytelist* np, *p = cfg->acl_tags; + cfg->acl_tags = NULL; + while(p) { + log_assert(p->str && p->str2); + if(!acl_list_tags_cfg(acl, p->str, p->str2, p->str2len)) { + config_del_strbytelist(p); + return 0; + } + /* free the items as we go to free up memory */ + np = p->next; + free(p->str); + free(p->str2); + free(p); + p = np; + } + return 1; +} + +/** read acl tag actions config */ +static int +read_acl_tag_actions(struct acl_list* acl, struct config_file* cfg) +{ + struct config_str3list* p, *np; + p = cfg->acl_tag_actions; + cfg->acl_tag_actions = NULL; + while(p) { + log_assert(p->str && p->str2 && p->str3); + if(!acl_list_tag_action_cfg(acl, cfg, p->str, p->str2, + p->str3)) { + config_deltrplstrlist(p); + return 0; + } + /* free the items as we go to free up memory */ + np = p->next; + free(p->str); + free(p->str2); + free(p->str3); + free(p); + p = np; + } + return 1; +} + +/** read acl tag datas config */ +static int +read_acl_tag_datas(struct acl_list* acl, struct config_file* cfg) +{ + struct config_str3list* p, *np; + p = cfg->acl_tag_datas; + cfg->acl_tag_datas = NULL; + while(p) { + log_assert(p->str && p->str2 && p->str3); + if(!acl_list_tag_data_cfg(acl, cfg, p->str, p->str2, p->str3)) { + config_deltrplstrlist(p); + return 0; + } + /* free the items as we go to free up memory */ + np = p->next; + free(p->str); + free(p->str2); + free(p->str3); + free(p); + p = np; + } + return 1; +} + int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg) { @@ -145,6 +368,12 @@ acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg) addr_tree_init(&acl->tree); if(!read_acl_list(acl, cfg)) return 0; + if(!read_acl_tags(acl, cfg)) + return 0; + if(!read_acl_tag_actions(acl, cfg)) + return 0; + if(!read_acl_tag_datas(acl, cfg)) + return 0; /* insert defaults, with '0' to ignore them if they are duplicates */ if(!acl_list_str_cfg(acl, "0.0.0.0/0", "refuse", 0)) return 0; @@ -163,13 +392,18 @@ acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg) } enum acl_access -acl_list_lookup(struct acl_list* acl, struct sockaddr_storage* addr, +acl_get_control(struct acl_addr* acl) +{ + if(acl) return acl->control; + return acl_deny; +} + +struct acl_addr* +acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr, socklen_t addrlen) { - struct acl_addr* r = (struct acl_addr*)addr_tree_lookup(&acl->tree, + return (struct acl_addr*)addr_tree_lookup(&acl->tree, addr, addrlen); - if(r) return r->control; - return acl_deny; } size_t diff --git a/daemon/acl_list.h b/daemon/acl_list.h index 2323697d5b84..fc0e9cabf3df 100644 --- a/daemon/acl_list.h +++ b/daemon/acl_list.h @@ -87,6 +87,19 @@ struct acl_addr { struct addr_tree_node node; /** access control on this netblock */ enum acl_access control; + /** tag bitlist */ + uint8_t* taglist; + /** length of the taglist (in bytes) */ + size_t taglen; + /** array per tagnumber of localzonetype(in one byte). NULL if none. */ + uint8_t* tag_actions; + /** size of the tag_actions_array */ + size_t tag_actions_size; + /** array per tagnumber, with per tag a list of rdata strings. + * NULL if none. strings are like 'A 127.0.0.1' 'AAAA ::1' */ + struct config_strlist** tag_datas; + /** size of the tag_datas array */ + size_t tag_datas_size; }; /** @@ -110,14 +123,22 @@ void acl_list_delete(struct acl_list* acl); int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg); /** - * Lookup address to see its access control status. + * Lookup access control status for acl structure. + * @param acl: structure for acl storage. + * @return: what to do with message from this address. + */ +enum acl_access acl_get_control(struct acl_addr* acl); + +/** + * Lookup address to see its acl structure * @param acl: structure for address storage. * @param addr: address to check * @param addrlen: length of addr. - * @return: what to do with message from this address. + * @return: acl structure from this address. */ -enum acl_access acl_list_lookup(struct acl_list* acl, - struct sockaddr_storage* addr, socklen_t addrlen); +struct acl_addr* +acl_addr_lookup(struct acl_list* acl, struct sockaddr_storage* addr, + socklen_t addrlen); /** * Get memory used by acl structure. diff --git a/daemon/daemon.c b/daemon/daemon.c index 1036fcde2001..2ed9af8fe66f 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -204,17 +204,29 @@ daemon_init(void) signal_handling_record(); checklock_start(); #ifdef HAVE_SSL +# ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS ERR_load_crypto_strings(); +# endif ERR_load_SSL_strings(); # ifdef USE_GOST (void)sldns_key_EVP_load_gost_id(); # endif +# if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) OpenSSL_add_all_algorithms(); +# else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS + | OPENSSL_INIT_ADD_ALL_DIGESTS + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +# endif # if HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS /* grab the COMP method ptr because openssl leaks it */ comp_meth = (void*)SSL_COMP_get_compression_methods(); # endif +# if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) (void)SSL_library_init(); +# else + (void)OPENSSL_init_ssl(0, NULL); +# endif # if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED) if(!ub_openssl_lock_init()) fatal_exit("could not init openssl locks"); @@ -404,6 +416,8 @@ daemon_create_workers(struct daemon* daemon) } daemon->workers = (struct worker**)calloc((size_t)daemon->num, sizeof(struct worker*)); + if(!daemon->workers) + fatal_exit("out of memory during daemon init"); if(daemon->cfg->dnstap) { #ifdef USE_DNSTAP daemon->dtenv = dt_create(daemon->cfg->dnstap_socket_path, @@ -586,13 +600,12 @@ daemon_cleanup(struct daemon* daemon) log_thread_set(NULL); /* clean up caches because * a) RRset IDs will be recycled after a reload, causing collisions - * b) validation config can change, thus rrset, msg, keycache clear - * The infra cache is kept, the timing and edns info is still valid */ + * b) validation config can change, thus rrset, msg, keycache clear */ slabhash_clear(&daemon->env->rrset_cache->table); slabhash_clear(daemon->env->msg_cache); local_zones_delete(daemon->local_zones); daemon->local_zones = NULL; - /* key cache is cleared by module desetup during next daemon_init() */ + /* key cache is cleared by module desetup during next daemon_fork() */ daemon_remote_clear(daemon->rc); for(i=0; i<daemon->num; i++) worker_delete(daemon->workers[i]); @@ -656,8 +669,12 @@ daemon_delete(struct daemon* daemon) # endif CONF_modules_free(); # endif +# ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA CRYPTO_cleanup_all_ex_data(); /* safe, no more threads right now */ +# endif +# ifdef HAVE_ERR_FREE_STRINGS ERR_free_strings(); +# endif # if OPENSSL_VERSION_NUMBER < 0x10100000 RAND_cleanup(); # endif diff --git a/daemon/remote.c b/daemon/remote.c index 7690ee8b1875..3fe6650b4ba6 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -46,9 +46,12 @@ #ifdef HAVE_OPENSSL_ERR_H #include <openssl/err.h> #endif -#ifndef HEADER_DH_H +#ifdef HAVE_OPENSSL_DH_H #include <openssl/dh.h> #endif +#ifdef HAVE_OPENSSL_BN_H +#include <openssl/bn.h> +#endif #include <ctype.h> #include "daemon/remote.h" @@ -144,7 +147,7 @@ timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d) * (some openssl versions reject DH that is 'too small', eg. 512). */ #ifndef S_SPLINT_S -DH *get_dh2048() +static DH *get_dh2048(void) { static unsigned char dh2048_p[]={ 0xE7,0x36,0x28,0x3B,0xE4,0xC3,0x32,0x1C,0x01,0xC3,0x67,0xD6, @@ -173,14 +176,31 @@ DH *get_dh2048() static unsigned char dh2048_g[]={ 0x02, }; - DH *dh; - - if ((dh=DH_new()) == NULL) return(NULL); - dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); - dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); - if ((dh->p == NULL) || (dh->g == NULL)) - { DH_free(dh); return(NULL); } - return(dh); + DH *dh = NULL; + BIGNUM *p = NULL, *g = NULL; + + dh = DH_new(); + p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL); + g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL); + if (!dh || !p || !g) + goto err; + +#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL) + dh->p = p; + dh->g = g; +#else + if (!DH_set0_pqg(dh, p, NULL, g)) + goto err; +#endif + return dh; +err: + if (p) + BN_free(p); + if (g) + BN_free(g); + if (dh) + DH_free(dh); + return NULL; } #endif /* SPLINT */ @@ -225,6 +245,7 @@ daemon_remote_create(struct config_file* cfg) /* No certificates are requested */ if(!SSL_CTX_set_cipher_list(rc->ctx, "aNULL")) { log_crypto_err("Failed to set aNULL cipher list"); + daemon_remote_delete(rc); return NULL; } @@ -233,6 +254,7 @@ daemon_remote_create(struct config_file* cfg) */ if(!SSL_CTX_set_tmp_dh(rc->ctx,get_dh2048())) { log_crypto_err("Wanted to set DH param, but failed"); + daemon_remote_delete(rc); return NULL; } return rc; @@ -359,8 +381,12 @@ add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err, if(fd != -1) { #ifdef HAVE_CHOWN if (cfg->username && cfg->username[0] && - cfg_uid != (uid_t)-1) - chown(ip, cfg_uid, cfg_gid); + cfg_uid != (uid_t)-1) { + if(chown(ip, cfg_uid, cfg_gid) == -1) + log_err("cannot chown %u.%u %s: %s", + (unsigned)cfg_uid, (unsigned)cfg_gid, + ip, strerror(errno)); + } chmod(ip, (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)); #else (void)cfg; diff --git a/daemon/remote.h b/daemon/remote.h index b25bfb1af611..190286d474a4 100644 --- a/daemon/remote.h +++ b/daemon/remote.h @@ -56,8 +56,8 @@ struct comm_reply; struct comm_point; struct daemon_remote; -/** number of seconds timeout on incoming remote control handshake */ -#define REMOTE_CONTROL_TCP_TIMEOUT 120 +/** number of milliseconds timeout on incoming remote control handshake */ +#define REMOTE_CONTROL_TCP_TIMEOUT 120000 /** * a busy control command connection, SSL state diff --git a/daemon/unbound.c b/daemon/unbound.c index d1de67369827..73e9fcbb6234 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -93,10 +93,13 @@ void* unbound_start_brk = 0; #endif /** print usage. */ -static void usage() +static void usage(void) { const char** m; const char *evnm="event", *evsys="", *evmethod=""; + time_t t; + struct timeval now; + struct ub_event_base* base; printf("usage: unbound [options]\n"); printf(" start unbound daemon DNS resolver.\n"); printf("-h this help\n"); @@ -110,11 +113,16 @@ static void usage() printf(" service - used to start from services control panel\n"); #endif printf("Version %s\n", PACKAGE_VERSION); - ub_get_event_sys(NULL, &evnm, &evsys, &evmethod); + base = ub_default_event_base(0,&t,&now); + ub_get_event_sys(base, &evnm, &evsys, &evmethod); printf("linked libs: %s %s (it uses %s), %s\n", evnm, evsys, evmethod, #ifdef HAVE_SSL +# ifdef SSLEAY_VERSION SSLeay_version(SSLEAY_VERSION) +# else + OpenSSL_version(OPENSSL_VERSION) +# endif #elif defined(HAVE_NSS) NSS_GetVersion() #elif defined(HAVE_NETTLE) @@ -127,6 +135,7 @@ static void usage() printf("\n"); printf("BSD licensed, see LICENSE in source package for details.\n"); printf("Report bugs to %s\n", PACKAGE_BUGREPORT); + ub_event_base_free(base); } #ifndef unbound_testbound @@ -539,7 +548,9 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode, log_warn("unable to initgroups %s: %s", cfg->username, strerror(errno)); # endif /* HAVE_INITGROUPS */ +# ifdef HAVE_ENDPWENT endpwent(); +# endif #ifdef HAVE_SETRESGID if(setresgid(cfg_gid,cfg_gid,cfg_gid) != 0) diff --git a/daemon/worker.c b/daemon/worker.c index 33a6883f9219..70d07ba8e8f7 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -773,6 +773,8 @@ deny_refuse(struct comm_point* c, enum acl_access acl, LDNS_QR_SET(sldns_buffer_begin(c->buffer)); LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), LDNS_RCODE_REFUSED); + sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE); + sldns_buffer_flip(c->buffer); return 1; } @@ -804,6 +806,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error, struct query_info qinfo; struct edns_data edns; enum acl_access acl; + struct acl_addr* acladdr; int rc = 0; if(error != NETEVENT_NOERROR) { @@ -816,8 +819,9 @@ worker_handle_request(struct comm_point* c, void* arg, int error, dt_msg_send_client_query(&worker->dtenv, &repinfo->addr, c->type, c->buffer); #endif - acl = acl_list_lookup(worker->daemon->acl, &repinfo->addr, + acladdr = acl_addr_lookup(worker->daemon->acl, &repinfo->addr, repinfo->addrlen); + acl = acl_get_control(acladdr); if((ret=deny_refuse_all(c, acl, worker, repinfo)) != -1) { if(ret == 1) @@ -941,7 +945,11 @@ worker_handle_request(struct comm_point* c, void* arg, int error, goto send_reply; } if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns, - c->buffer, worker->scratchpad, repinfo)) { + c->buffer, worker->scratchpad, repinfo, + acladdr->taglist, acladdr->taglen, acladdr->tag_actions, + acladdr->tag_actions_size, acladdr->tag_datas, + acladdr->tag_datas_size, worker->daemon->cfg->tagname, + worker->daemon->cfg->num_tags)) { regional_free_all(worker->scratchpad); if(sldns_buffer_limit(c->buffer) == 0) { comm_point_drop_reply(repinfo); |
