access-control

git-svn-id: file:///svn/unbound/trunk@769 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-11-19 15:32:55 +00:00
parent fc8657c421
commit eda6528c14
18 changed files with 1007 additions and 834 deletions

View File

@ -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");

View File

@ -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");

View File

@ -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);

View File

@ -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;
};
/**

View File

@ -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) {

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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;

View File

@ -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
View 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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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)

View File

@ -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.