mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
access-control
git-svn-id: file:///svn/unbound/trunk@769 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
fc8657c421
commit
eda6528c14
@ -92,6 +92,7 @@ morechecks(struct config_file* cfg)
|
||||
int i;
|
||||
struct sockaddr_storage a;
|
||||
socklen_t alen;
|
||||
struct config_acl* acl;
|
||||
for(i=0; i<cfg->num_ifs; i++) {
|
||||
if(!ipstrtoaddr(cfg->ifs[i], UNBOUND_DNS_PORT, &a, &alen)) {
|
||||
fatal_exit("cannot parse interface specified as '%s'",
|
||||
@ -105,6 +106,13 @@ morechecks(struct config_file* cfg)
|
||||
"specified as '%s'", cfg->out_ifs[i]);
|
||||
}
|
||||
}
|
||||
for(acl=cfg->acls; acl; acl = acl->next) {
|
||||
if(!netblockstrtoaddr(acl->address, UNBOUND_DNS_PORT,
|
||||
&a, &alen, &i)) {
|
||||
fatal_exit("cannot parse access control address %s %s",
|
||||
acl->address, acl->control);
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg->verbosity < 0)
|
||||
fatal_exit("verbosity value < 0");
|
||||
|
@ -116,7 +116,6 @@ acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2,
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
char* s = NULL;
|
||||
socklen_t addrlen;
|
||||
enum acl_access control;
|
||||
if(strcmp(s2, "allow") == 0)
|
||||
@ -129,33 +128,10 @@ acl_list_str_cfg(struct acl_list* acl, const char* str, const char* s2,
|
||||
log_err("access control type %s unknown", str);
|
||||
return 0;
|
||||
}
|
||||
net = (str_is_ip6(str)?128:32);
|
||||
if((s=strchr(str, '/'))) {
|
||||
if(atoi(s+1) > net) {
|
||||
log_err("acl netblock too large: %s", str);
|
||||
return 0;
|
||||
}
|
||||
net = atoi(s+1);
|
||||
if(net == 0 && strcmp(s+1, "0") != 0) {
|
||||
log_err("cannot parse acl netblock:"
|
||||
" '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
if(!(s = strdup(str))) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
*strchr(s, '/') = '\0';
|
||||
}
|
||||
if(!ipstrtoaddr(s?s:str, UNBOUND_DNS_PORT, &addr, &addrlen)) {
|
||||
free(s);
|
||||
log_err("cannot parse acl ip address: '%s'", str);
|
||||
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
|
||||
log_err("cannot parse access control: %s %s", str, s2);
|
||||
return 0;
|
||||
}
|
||||
if(s) {
|
||||
free(s);
|
||||
addr_mask(&addr, addrlen, net);
|
||||
}
|
||||
if(!acl_list_insert(acl, &addr, addrlen, net, control,
|
||||
complain_duplicates)) {
|
||||
log_err("out of memory");
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "config.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/acl_list.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/data/msgreply.h"
|
||||
@ -134,6 +135,12 @@ daemon_init()
|
||||
return NULL;
|
||||
}
|
||||
alloc_init(&daemon->superalloc, NULL, 0);
|
||||
daemon->acl = acl_list_create();
|
||||
if(!daemon->acl) {
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
return NULL;
|
||||
}
|
||||
return daemon;
|
||||
}
|
||||
|
||||
@ -397,6 +404,8 @@ void
|
||||
daemon_fork(struct daemon* daemon)
|
||||
{
|
||||
log_assert(daemon);
|
||||
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg))
|
||||
fatal_exit("Could not setup access control list");
|
||||
|
||||
/* setup modules */
|
||||
daemon_setup_modules(daemon);
|
||||
@ -465,6 +474,7 @@ daemon_delete(struct daemon* daemon)
|
||||
infra_delete(daemon->env->infra_cache);
|
||||
}
|
||||
alloc_clear(&daemon->superalloc);
|
||||
acl_list_delete(daemon->acl);
|
||||
free(daemon->pidfile);
|
||||
free(daemon->env);
|
||||
free(daemon);
|
||||
|
@ -50,6 +50,7 @@ struct listen_port;
|
||||
struct slabhash;
|
||||
struct module_env;
|
||||
struct rrset_cache;
|
||||
struct acl_list;
|
||||
|
||||
/**
|
||||
* Structure holding worker list.
|
||||
@ -78,6 +79,8 @@ struct daemon {
|
||||
int num_modules;
|
||||
/** the module callbacks, array of num_modules length */
|
||||
struct module_func_block** modfunc;
|
||||
/** access control, which client IPs are allowed to connect */
|
||||
struct acl_list* acl;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "util/random.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "daemon/acl_list.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/module.h"
|
||||
@ -663,12 +664,28 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
struct lruhash_entry* e;
|
||||
struct query_info qinfo;
|
||||
struct edns_data edns;
|
||||
enum acl_access acl;
|
||||
|
||||
if(error != NETEVENT_NOERROR) {
|
||||
/* some bad tcp query DNS formats give these error calls */
|
||||
verbose(VERB_ALGO, "handle request called with err=%d", error);
|
||||
return 0;
|
||||
}
|
||||
acl = acl_list_lookup(worker->daemon->acl, &repinfo->addr,
|
||||
repinfo->addrlen);
|
||||
if(acl == acl_deny) {
|
||||
comm_point_drop_reply(repinfo);
|
||||
return 0;
|
||||
} else if(acl == acl_refuse) {
|
||||
ldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE);
|
||||
ldns_buffer_write_at(c->buffer, 4,
|
||||
(uint8_t*)"\0\0\0\0\0\0\0\0", 8);
|
||||
LDNS_QR_SET(ldns_buffer_begin(c->buffer));
|
||||
LDNS_RCODE_SET(ldns_buffer_begin(c->buffer),
|
||||
LDNS_RCODE_REFUSED);
|
||||
log_buf(VERB_ALGO, "refuse", c->buffer);
|
||||
return 1;
|
||||
}
|
||||
if((ret=worker_check_request(c->buffer, worker)) != 0) {
|
||||
verbose(VERB_ALGO, "worker check request: bad query.");
|
||||
if(ret != -1) {
|
||||
|
@ -7,6 +7,8 @@
|
||||
- README talks about gnu make.
|
||||
- 0.8: unit test for addr_mask and fixups for it.
|
||||
and unit test for addr_in_common().
|
||||
- 0.8: access-control config file element.
|
||||
and unit test rpl replay file.
|
||||
|
||||
16 November 2007: Wouter
|
||||
- privilege separation is not needed in unbound at this time.
|
||||
|
@ -113,6 +113,15 @@ server:
|
||||
# Enable TCP, "yes" or "no".
|
||||
# do-tcp: yes
|
||||
|
||||
# control which clients are allowed to make (recursive) queries
|
||||
# to this server. Specify classless netblocks with /size and action.
|
||||
# By default everything is refused, except for localhost.
|
||||
# Choose deny (drop message), refuse (polite error reply), allow.
|
||||
# access-control: 0.0.0.0/0 refuse
|
||||
# access-control: 127.0.0.0/8 allow
|
||||
# access-control: ::0/0 refuse
|
||||
# access-control: ::1 allow
|
||||
|
||||
# if given, a chroot(2) is done to the given directory.
|
||||
# i.e. you can chroot to the working directory, for example,
|
||||
# for extra security, but make sure all files are in that directory.
|
||||
|
@ -48,6 +48,12 @@ server:
|
||||
# logfile: "/etc/unbound/unbound.log" #uncomment to use logfile.
|
||||
pidfile: "/etc/unbound/unbound.pid"
|
||||
# verbosity: 1 # uncomment and increase to get more logging.
|
||||
|
||||
# listen on all interfaces, answer queries from the local subnet.
|
||||
interface: 0.0.0.0
|
||||
interface: ::0
|
||||
access-control: 10.0.0.0/8 allow
|
||||
access-control: 2001:DB8::/64 allow
|
||||
.fi
|
||||
.Sh FILE FORMAT
|
||||
There must be whitespace between keywords. Attribute keywords end with a colon ':'. An attribute
|
||||
@ -155,6 +161,13 @@ Enable or disable whether ip6 queries are answered. Default is yes.
|
||||
Enable or disable whether UDP queries are answered. Default is yes.
|
||||
.It \fBdo-tcp:\fR <yes or no>
|
||||
Enable or disable whether TCP queries are answered. Default is yes.
|
||||
.It \fBaccess-control:\fR <IP netblock> <action>
|
||||
The netblock is given as an IP4 or IP6 address with /size appended for a
|
||||
classless network block. The action can be deny, refuse or allow.
|
||||
Deny stops queries from hosts from that netblock.
|
||||
Refuse stops queries too, but sends a DNS rcode REFUSED error message back.
|
||||
Allow gives access to clients from that netblock.
|
||||
By default only localhost is allowed, the rest is refused.
|
||||
.It \fBchroot:\fR <directory>
|
||||
If given a chroot is done to the given directory. The default is
|
||||
"/etc/unbound". If you give "" no chroot is performed.
|
||||
|
@ -114,35 +114,11 @@ donotq_str_cfg(struct iter_donotq* dq, const char* str)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int net;
|
||||
char* s = NULL;
|
||||
socklen_t addrlen;
|
||||
net = (str_is_ip6(str)?128:32);
|
||||
if((s=strchr(str, '/'))) {
|
||||
if(atoi(s+1) > net) {
|
||||
log_err("netblock too large: %s", str);
|
||||
return 0;
|
||||
}
|
||||
net = atoi(s+1);
|
||||
if(net == 0 && strcmp(s+1, "0") != 0) {
|
||||
log_err("cannot parse donotquery netblock:"
|
||||
" '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
if(!(s = strdup(str))) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
*strchr(s, '/') = '\0';
|
||||
}
|
||||
if(!ipstrtoaddr(s?s:str, UNBOUND_DNS_PORT, &addr, &addrlen)) {
|
||||
free(s);
|
||||
log_err("cannot parse donotquery ip address: '%s'", str);
|
||||
if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) {
|
||||
log_err("cannot parse donotquery netblock: %s", str);
|
||||
return 0;
|
||||
}
|
||||
if(s) {
|
||||
free(s);
|
||||
addr_mask(&addr, addrlen, net);
|
||||
}
|
||||
if(!donotq_insert(dq, &addr, addrlen, net)) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
|
@ -229,6 +229,11 @@ replay_moment_read(char* remain, FILE* in, const char* name, int* lineno,
|
||||
while(isspace((int)*remain))
|
||||
remain++;
|
||||
if(parse_keyword(&remain, "ADDRESS")) {
|
||||
while(isspace((int)*remain))
|
||||
remain++;
|
||||
if(strlen(remain) > 0) /* remove \n */
|
||||
remain[strlen(remain)-1] = 0;
|
||||
printf("remain '%s'\n", remain);
|
||||
if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen)) {
|
||||
log_err("line %d: could not parse ADDRESS: %s",
|
||||
*lineno, remain);
|
||||
|
52
testdata/acl.rpl
vendored
Normal file
52
testdata/acl.rpl
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
; config options
|
||||
server:
|
||||
hide-identity: no
|
||||
hide-version: no
|
||||
identity: "test-identity"
|
||||
version: "test-version"
|
||||
access-control: 20.0.0.0/8 allow
|
||||
access-control: 20.40.0.0/16 refuse
|
||||
access-control: 20.40.80.0/24 deny
|
||||
|
||||
CONFIG_END
|
||||
SCENARIO_BEGIN Test access control list
|
||||
|
||||
; version.bind.
|
||||
; allow
|
||||
STEP 1 QUERY ADDRESS 20.1.2.3
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
ENTRY_END
|
||||
STEP 2 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RA
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
SECTION ANSWER
|
||||
version.bind. 0 CH TXT "test-version"
|
||||
ENTRY_END
|
||||
|
||||
; refuse
|
||||
STEP 3 QUERY ADDRESS 20.40.2.3
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
ENTRY_END
|
||||
STEP 4 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR REFUSED
|
||||
ENTRY_END
|
||||
|
||||
; deny (drop)
|
||||
STEP 5 QUERY ADDRESS 20.40.80.3
|
||||
ENTRY_BEGIN
|
||||
SECTION QUESTION
|
||||
version.bind. CH TXT
|
||||
ENTRY_END
|
||||
|
||||
; no answer must be pending
|
||||
|
||||
SCENARIO_END
|
1142
util/configlexer.c
1142
util/configlexer.c
File diff suppressed because it is too large
Load Diff
@ -144,6 +144,7 @@ forward-addr{COLON} { YDOUT; return VAR_FORWARD_ADDR;}
|
||||
forward-host{COLON} { YDOUT; return VAR_FORWARD_HOST;}
|
||||
do-not-query-address{COLON} { YDOUT; return VAR_DO_NOT_QUERY_ADDRESS;}
|
||||
do-not-query-localhost{COLON} { YDOUT; return VAR_DO_NOT_QUERY_LOCALHOST;}
|
||||
access-control{COLON} { YDOUT; return VAR_ACCESS_CONTROL;}
|
||||
hide-identity{COLON} { YDOUT; return VAR_HIDE_IDENTITY;}
|
||||
hide-version{COLON} { YDOUT; return VAR_HIDE_VERSION;}
|
||||
identity{COLON} { YDOUT; return VAR_IDENTITY;}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -108,7 +108,8 @@
|
||||
VAR_ROOT_HINTS = 324,
|
||||
VAR_DO_NOT_QUERY_LOCALHOST = 325,
|
||||
VAR_CACHE_MAX_TTL = 326,
|
||||
VAR_HARDEN_DNNSEC_STRIPPED = 327
|
||||
VAR_HARDEN_DNNSEC_STRIPPED = 327,
|
||||
VAR_ACCESS_CONTROL = 328
|
||||
};
|
||||
#endif
|
||||
/* Tokens. */
|
||||
@ -182,6 +183,7 @@
|
||||
#define VAR_DO_NOT_QUERY_LOCALHOST 325
|
||||
#define VAR_CACHE_MAX_TTL 326
|
||||
#define VAR_HARDEN_DNNSEC_STRIPPED 327
|
||||
#define VAR_ACCESS_CONTROL 328
|
||||
|
||||
|
||||
|
||||
@ -193,7 +195,7 @@ typedef union YYSTYPE
|
||||
char* str;
|
||||
}
|
||||
/* Line 1489 of yacc.c. */
|
||||
#line 197 "util/configparser.h"
|
||||
#line 199 "util/configparser.h"
|
||||
YYSTYPE;
|
||||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
|
@ -86,7 +86,7 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_KEY_CACHE_SLABS VAR_TRUSTED_KEYS_FILE
|
||||
%token VAR_VAL_NSEC3_KEYSIZE_ITERATIONS VAR_USE_SYSLOG
|
||||
%token VAR_OUTGOING_INTERFACE VAR_ROOT_HINTS VAR_DO_NOT_QUERY_LOCALHOST
|
||||
%token VAR_CACHE_MAX_TTL VAR_HARDEN_DNNSEC_STRIPPED
|
||||
%token VAR_CACHE_MAX_TTL VAR_HARDEN_DNNSEC_STRIPPED VAR_ACCESS_CONTROL
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
@ -124,7 +124,7 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_trusted_keys_file | server_val_nsec3_keysize_iterations |
|
||||
server_use_syslog | server_outgoing_interface | server_root_hints |
|
||||
server_do_not_query_localhost | server_cache_max_ttl |
|
||||
server_harden_dnssec_stripped
|
||||
server_harden_dnssec_stripped | server_access_control
|
||||
;
|
||||
stubstart: VAR_STUB_ZONE
|
||||
{
|
||||
@ -574,6 +574,23 @@ server_do_not_query_localhost: VAR_DO_NOT_QUERY_LOCALHOST STRING
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_access_control: VAR_ACCESS_CONTROL STRING STRING
|
||||
{
|
||||
OUTYY(("P(server_access_control:%s %s)\n", $2, $3));
|
||||
if(strcmp($3, "deny")!=0 && strcmp($3, "refuse")!=0 &&
|
||||
strcmp($3, "allow")!=0) {
|
||||
yyerror("expected deny, refuse or allow in "
|
||||
"access control action");
|
||||
} else {
|
||||
struct config_acl* n = calloc(1, sizeof(*n));
|
||||
if(!n) fatal_exit("out of memory adding acl");
|
||||
n->address = $2;
|
||||
n->control = $3;
|
||||
n->next = cfg_parser->cfg->acls;
|
||||
cfg_parser->cfg->acls = n;
|
||||
}
|
||||
}
|
||||
;
|
||||
server_module_conf: VAR_MODULE_CONF STRING
|
||||
{
|
||||
OUTYY(("P(server_module_conf:%s)\n", $2));
|
||||
|
@ -229,6 +229,39 @@ ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int netblockstrtoaddr(const char* str, int port, struct sockaddr_storage* addr,
|
||||
socklen_t* addrlen, int* net)
|
||||
{
|
||||
char* s = NULL;
|
||||
*net = (str_is_ip6(str)?128:32);
|
||||
if((s=strchr(str, '/'))) {
|
||||
if(atoi(s+1) > *net) {
|
||||
log_err("netblock too large: %s", str);
|
||||
return 0;
|
||||
}
|
||||
*net = atoi(s+1);
|
||||
if(net == 0 && strcmp(s+1, "0") != 0) {
|
||||
log_err("cannot parse netblock: '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
if(!(s = strdup(str))) {
|
||||
log_err("out of memory");
|
||||
return 0;
|
||||
}
|
||||
*strchr(s, '/') = '\0';
|
||||
}
|
||||
if(!ipstrtoaddr(s?s:str, port, addr, addrlen)) {
|
||||
free(s);
|
||||
log_err("cannot parse ip address: '%s'", str);
|
||||
return 0;
|
||||
}
|
||||
if(s) {
|
||||
free(s);
|
||||
addr_mask(addr, *addrlen, *net);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
log_nametypeclass(enum verbosity_value v, const char* str, uint8_t* name,
|
||||
uint16_t type, uint16_t dclass)
|
||||
|
@ -186,6 +186,19 @@ int extstrtoaddr(const char* str, struct sockaddr_storage* addr,
|
||||
int ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
|
||||
socklen_t* addrlen);
|
||||
|
||||
/**
|
||||
* Convert ip netblock (ip/netsize) string and port to sockaddr.
|
||||
* *SLOW*, does a malloc internally to avoid writing over 'ip' string.
|
||||
* @param ip: ip4 or ip6 address string.
|
||||
* @param port: port number, host format.
|
||||
* @param addr: where to store sockaddr.
|
||||
* @param addrlen: length of stored sockaddr is returned.
|
||||
* @param net: netblock size is returned.
|
||||
* @return 0 on error.
|
||||
*/
|
||||
int netblockstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
|
||||
socklen_t* addrlen, int* net);
|
||||
|
||||
/**
|
||||
* Print string with neat domain name, type and class.
|
||||
* @param v: at what verbosity level to print this.
|
||||
|
Loading…
Reference in New Issue
Block a user