check.c revision ec5347e2c775f027573ce5648b910361aa926c01
/*
* Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 2001-2003 Internet Software Consortium.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: check.c,v 1.80 2007/06/18 23:47:39 tbox Exp $ */
/*! \file */
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <isc/parseint.h>
#include <isc/sockaddr.h>
#include <dns/fixedname.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#ifndef DNS_RDATASET_FIXED
#define DNS_RDATASET_FIXED 1
#endif
static void
}
static isc_result_t
isc_buffer_t b;
const char *str;
if (cfg_obj_isstring(obj)) {
if (tresult != ISC_R_SUCCESS) {
"rrset-order: invalid class '%s'",
r.base);
}
}
if (cfg_obj_isstring(obj)) {
if (tresult != ISC_R_SUCCESS) {
"rrset-order: invalid type '%s'",
r.base);
}
}
if (cfg_obj_isstring(obj)) {
if (tresult != ISC_R_SUCCESS) {
"rrset-order: invalid name '%s'", str);
}
}
if (!cfg_obj_isstring(obj) ||
"rrset-order: keyword 'order' missing");
}
if (!cfg_obj_isstring(obj)) {
"rrset-order: missing ordering");
#if !DNS_RDATASET_FIXED
"rrset-order: order 'fixed' not fully implemented");
#endif
"rrset-order: invalid order '%s'",
}
return (result);
}
static isc_result_t
const cfg_listelt_t *element;
return (result);
{
if (tresult != ISC_R_SUCCESS)
}
return (result);
}
static isc_result_t
const cfg_listelt_t *element;
const char *str;
if (alternates == NULL)
return (ISC_R_SUCCESS);
if (cfg_obj_isuint32(obj)) {
if (val > ISC_UINT16_MAX) {
"port '%u' out of range", val);
}
}
if (cfg_obj_issockaddr(value))
continue;
if (tresult != ISC_R_SUCCESS) {
"bad name '%s'", str);
}
if (cfg_obj_isuint32(obj)) {
if (val > ISC_UINT16_MAX) {
"port '%u' out of range", val);
}
}
}
return (result);
}
static isc_result_t
"no matching 'forwarders' statement");
return (ISC_R_FAILURE);
}
return (ISC_R_SUCCESS);
}
static isc_result_t
const cfg_listelt_t *element;
const char *str;
isc_buffer_t b;
if (tresult != ISC_R_SUCCESS) {
"bad domain name '%s'", str);
}
{
if (tresult != ISC_R_SUCCESS) {
}
if (tresult != ISC_R_SUCCESS) {
ISC_LOG_ERROR, "invalid algorithm '%s'",
r.base);
}
}
return (result);
}
static isc_result_t
{
char *key;
const char *file;
unsigned int line;
return (ISC_R_NOMEMORY);
if (result == ISC_R_EXISTS) {
&symvalue) == ISC_R_SUCCESS);
file = "<unknown file>";
} else if (result != ISC_R_SUCCESS) {
}
return (result);
}
static isc_result_t
{
char namebuf[DNS_NAME_FORMATSIZE];
const char *str;
isc_buffer_t b;
if (result != ISC_R_SUCCESS) {
"bad domain name '%s'", str);
} else {
"dnssec-must-be-secure '%s': already "
"exists previous definition: %s:%u",
}
return (result);
}
static isc_result_t
{
}
}
return (ISC_R_SUCCESS);
return (result);
}
static isc_result_t
{
int i = 0;
"allow-query-cache", "allow-query-cache-on",
"blackhole", "match-clients", "match-destinations",
"sortlist", NULL };
if (tresult != ISC_R_SUCCESS)
}
return (result);
}
/*
* Check allow-recursion and allow-recursion-on acls, and also log a
* warning if they're inconsistent with the "recursion" option.
*/
static isc_result_t
{
const char *forview = " for view ";
int i = 0;
NULL };
}
else
viewname = "";
forview = "";
}
}
continue;
if (tresult != ISC_R_SUCCESS)
continue;
"both \"recursion no;\" and "
"\"%s\" active%s%s",
}
}
return (result);
}
typedef struct {
const char *name;
unsigned int scale;
unsigned int max;
static isc_result_t
unsigned int i;
const cfg_listelt_t *element;
const char *str;
isc_buffer_t b;
static intervaltable intervals[] = {
{ "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */
{ "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */
{ "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */
{ "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */
{ "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */
{ "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */
{ "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */
{ "sig-validity-interval", 86400, 10 * 366 }, /* 10 years */
{ "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */
};
/*
* Check that fields specified in units of time other than seconds
* have reasonable values.
*/
continue;
"%s '%u' is out of range (0..%u)",
"%s '%d' is out of range",
}
}
const char *str;
"preferred-glue unexpected value '%s'",
str);
}
if (!cfg_obj_isvoid(obj)) {
const cfg_listelt_t *element;
const char *str;
isc_buffer_t b;
if (tresult != ISC_R_SUCCESS) {
"bad domain name '%s'",
str);
}
}
}
}
/*
* Set supported DNSSEC algorithms.
*/
{
if (tresult != ISC_R_SUCCESS)
}
}
/*
* Check the DLV zone name.
*/
if (tresult != ISC_R_SUCCESS)
{
const char *dlv;
if (tresult != ISC_R_SUCCESS) {
"bad domain name '%s'", dlv);
}
"dnssec-lookaside '%s': "
"already exists previous "
"definition: %s:%u",
if (tresult != ISC_R_SUCCESS &&
result == ISC_R_SUCCESS)
}
/*
* XXXMPA to be removed when multiple lookaside
* namespaces are supported.
*/
"dnssec-lookaside '%s': "
"non-root not yet supported", dlv);
if (result == ISC_R_SUCCESS)
}
"trust-anchor"));
if (tresult != ISC_R_SUCCESS) {
"bad domain name '%s'", dlv);
if (result == ISC_R_SUCCESS)
}
}
}
/*
* Check dnssec-must-be-secure.
*/
if (tresult != ISC_R_SUCCESS)
{
if (tresult != ISC_R_SUCCESS)
}
}
/*
* Check empty zone configuration.
*/
if (tresult != ISC_R_SUCCESS) {
"empty-server: invalid name '%s'", str);
}
}
if (tresult != ISC_R_SUCCESS) {
"empty-contact: invalid name '%s'", str);
}
}
{
if (tresult != ISC_R_SUCCESS) {
"disable-empty-zone: invalid name '%s'",
str);
}
}
return (result);
}
static isc_result_t
const cfg_listelt_t *elt;
if (result != ISC_R_SUCCESS)
return (result);
const char *listname;
return (ISC_R_SUCCESS);
}
}
return (ISC_R_NOTFOUND);
}
static isc_result_t
{
isc_uint32_t count = 0;
const cfg_listelt_t *element;
if (result != ISC_R_SUCCESS) {
return (result);
}
for ( ;
{
const char *listname;
"masterselement");
if (cfg_obj_issockaddr(addr)) {
count++;
continue;
}
if (!cfg_obj_isvoid(key)) {
"unexpected token '%s'",
if (result == ISC_R_SUCCESS)
}
if (tresult == ISC_R_EXISTS)
continue;
if (tresult != ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS)
"unable to find masters list '%s'",
listname);
continue;
}
/* Grow stack? */
if (stackcount == pushed) {
void * new;
goto cleanup;
if (stackcount != 0) {
}
stackcount = newlen;
}
goto newlist;
}
if (pushed != 0) {
goto resume;
}
return (result);
}
static isc_result_t
const cfg_listelt_t *element;
const cfg_listelt_t *element2;
const char *str;
isc_buffer_t b;
{
if (tresult != ISC_R_SUCCESS) {
"'%s' is not a valid name", str);
}
if (tresult != ISC_R_SUCCESS) {
"'%s' is not a valid name", str);
}
if (tresult == ISC_R_SUCCESS &&
"'%s' is not a wildcard", str);
}
{
if (tresult != ISC_R_SUCCESS) {
"'%s' is not a valid type", r.base);
}
}
}
return (result);
}
#define MASTERZONE 1
#define SLAVEZONE 2
#define STUBZONE 4
#define HINTZONE 8
#define FORWARDZONE 16
#define DELEGATIONZONE 32
#define CHECKACL 64
typedef struct {
const char *name;
int allowed;
} optionstable;
static isc_result_t
{
const char *zname;
const char *typestr;
unsigned int ztype;
unsigned int i;
isc_buffer_t b;
static optionstable options[] = {
{ "sig-validity-interval", MASTERZONE },
{ "update-policy", MASTERZONE },
{ "key-directory", MASTERZONE },
{ "check-wildcard", MASTERZONE },
{ "check-mx", MASTERZONE },
{ "integrity-check", MASTERZONE },
{ "check-mx-cname", MASTERZONE },
{ "check-srv-cname", MASTERZONE },
{ "update-check-ksk", MASTERZONE },
{ "try-tcp-refresh", SLAVEZONE },
};
static optionstable dialups[] = {
{ "notify-passive", SLAVEZONE },
};
"zone '%s': type not present", zname);
return (ISC_R_FAILURE);
}
ztype = MASTERZONE;
ztype = FORWARDZONE;
else {
"zone '%s': invalid type %s",
return (ISC_R_FAILURE);
}
if (cfg_obj_isstring(obj)) {
if (result != ISC_R_SUCCESS) {
"zone '%s': invalid class %s",
return (ISC_R_FAILURE);
}
"zone '%s': class '%s' does not "
return (ISC_R_FAILURE);
}
}
/*
* Look for an already existing zone.
* We need to make this cannonical as isc_symtab_define()
* deals with strings.
*/
if (result != ISC_R_SUCCESS) {
"zone '%s': is not a valid name", zname);
} else {
char namebuf[DNS_NAME_FORMATSIZE];
symtab, "zone '%s': already exists "
if (tresult != ISC_R_SUCCESS)
}
/*
* Look for inappropriate options for the given zone type.
* Check that ACLs expand correctly.
*/
{
"option '%s' is not allowed "
"in '%s' zone '%s'",
} else
"option '%s' is not allowed "
"in '%s' zone '%s'",
}
if (tresult != ISC_R_SUCCESS)
}
}
/*
* Slave & stub zones must have a "masters" field.
*/
"zone '%s': missing 'masters' entry",
zname);
} else {
"zone '%s': empty 'masters' entry",
zname);
}
}
}
/*
* Master zones can't have both "allow-update" and "update-policy".
*/
if (ztype == MASTERZONE) {
"zone '%s': 'allow-update' is ignored "
"when 'update-policy' is present",
zname);
} else if (res2 == ISC_R_SUCCESS &&
}
/*
* Check the excessively complicated "dialup" option.
*/
for (i = 0;
i++)
{
continue;
"dialup type '%s' is not "
"allowed in '%s' "
"zone '%s'",
}
break;
}
"invalid dialup type '%s' in zone "
}
}
}
/*
* Check that forwarding is reasonable.
*/
/*
* Check various options.
*/
if (tresult != ISC_R_SUCCESS)
/*
* require file clauses.
*/
if (tresult == ISC_R_NOTFOUND ||
(tresult == ISC_R_SUCCESS &&
if (tresult != ISC_R_SUCCESS &&
"zone '%s': missing 'file' entry",
zname);
}
}
return (result);
}
typedef struct keyalgorithms {
const char *name;
const char *algorithm;
int i;
static const algorithmtable algorithms[] = {
{ "hmac-md5", 128 },
{ "hmac-md5.sig-alg.reg.int", 0 },
{ "hmac-md5.sig-alg.reg.int.", 0 },
{ "hmac-sha1", 160 },
{ "hmac-sha224", 224 },
{ "hmac-sha256", 256 },
{ "hmac-sha384", 384 },
{ "hmac-sha512", 512 },
{ NULL, 0 }
};
"key '%s' must have both 'secret' and "
"'algorithm' defined",
keyname);
return (ISC_R_FAILURE);
}
break;
}
"unknown algorithm '%s'", algorithm);
return (ISC_R_NOTFOUND);
}
if (result == ISC_R_RANGE ||
"key '%s' digest-bits too large "
"[%u..%u]", keyname,
algorithms[i].size);
return (ISC_R_RANGE);
}
if ((digestbits % 8) != 0) {
"key '%s' digest-bits not multiple"
" of 8", keyname);
return (ISC_R_RANGE);
}
/*
* Recommended minima for hmac algorithms.
*/
(digestbits < 80U)))
"key '%s' digest-bits too small "
"[<%u]", keyname,
} else {
"key '%s': unable to parse digest-bits",
keyname);
return (result);
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
const cfg_listelt_t *element;
{
if (tresult != ISC_R_SUCCESS)
return (tresult);
if (tresult == ISC_R_EXISTS) {
const char *file;
unsigned int line;
file = "<unknown file>";
"key '%s': already exists "
"previous definition: %s:%u",
} else if (tresult != ISC_R_SUCCESS)
return (tresult);
}
return (result);
}
static struct {
const char *v4;
const char *v6;
} sources[] = {
{ "transfer-source", "transfer-source-v6" },
{ "notify-source", "notify-source-v6" },
{ "query-source", "query-source-v6" },
};
static isc_result_t
char buf[ISC_NETADDR_FORMATSIZE];
const char *xfr;
int source;
/*
* Check that unused bits are zero.
*/
if (tresult != ISC_R_SUCCESS) {
"server '%s/%u': invalid prefix "
}
source = 0;
do {
else
"server '%s': %s not legal",
}
file = "<unknown file>";
"server '%s/%u': already exists "
"previous definition: %s:%u",
}
}
}
return (result);
}
static isc_result_t
{
const cfg_listelt_t *element;
/*
* Check that all zone statements are syntactically correct and
* there are no duplicate zones.
*/
if (tresult != ISC_R_SUCCESS)
return (ISC_R_NOMEMORY);
else
{
if (tresult != ISC_R_SUCCESS)
}
/*
* Check that all key statements are syntactically correct and
* there are no duplicate keys.
*/
if (tresult != ISC_R_SUCCESS)
return (ISC_R_NOMEMORY);
if (tresult == ISC_R_EXISTS)
else if (tresult != ISC_R_SUCCESS) {
return (tresult);
}
if (tresult == ISC_R_EXISTS)
else if (tresult != ISC_R_SUCCESS) {
return (tresult);
}
}
/*
* Check that forwarding is reasonable.
*/
} else {
}
/*
* Check that dual-stack-servers is reasonable.
*/
} else {
}
/*
* Check that rrset-order is reasonable.
*/
}
}
/*
* Check that dnssec-enable/dnssec-validation are sensible.
*/
else
else
if (enablevalidation && !enablednssec)
"'dnssec-validation yes;' and 'dnssec-enable no;'");
else
if (tresult != ISC_R_SUCCESS)
if (tresult != ISC_R_SUCCESS)
if (tresult != ISC_R_SUCCESS)
return (result);
}
static const char *
default_channels[] = {
"default_syslog",
"default_stderr",
"default_debug",
"null",
};
static isc_result_t
{
const cfg_listelt_t *element;
const cfg_listelt_t *delement;
const char *channelname;
const char *catname;
int i;
return (ISC_R_SUCCESS);
if (result != ISC_R_SUCCESS)
return (result);
for (i = 0; default_channels[i] != NULL; i++) {
if (tresult != ISC_R_SUCCESS)
}
{
i = 0;
i++;
i++;
i++;
i++;
if (i != 1) {
"channel '%s': exactly one of file, syslog, "
"null, and stderr must be present",
}
if (tresult != ISC_R_SUCCESS)
}
{
"undefined category: '%s'", catname);
}
{
&symvalue);
if (tresult != ISC_R_SUCCESS) {
"undefined channel: '%s'",
}
}
}
return (result);
}
static isc_result_t
const cfg_listelt_t *element;
const char *str;
return (ISC_R_NOTFOUND);
{
return (ISC_R_SUCCESS);
}
return (ISC_R_NOTFOUND);
}
static isc_result_t
{
const cfg_obj_t *control_keylist;
const cfg_listelt_t *element;
return (ISC_R_SUCCESS);
{
if (tresult != ISC_R_SUCCESS) {
}
}
return (result);
}
static isc_result_t
{
const cfg_obj_t *inetcontrols;
const cfg_obj_t *unixcontrols;
const char *path;
int i;
if (controlslist == NULL)
return (ISC_R_SUCCESS);
/*
* INET: Check allow clause.
* UNIX: Check "perm" for sanity, check path length.
*/
unixcontrols = NULL;
inetcontrols = NULL;
if (tresult != ISC_R_SUCCESS)
logctx);
if (tresult != ISC_R_SUCCESS)
}
if (tresult == ISC_R_NOSPACE) {
"unix control '%s': path too long",
path);
}
for (i = 0; i < 3; i++) {
#ifdef NEED_SECURE_DIRECTORY
#else
#endif
break;
}
if (i == 0) {
"unix control '%s' allows access "
"to everyone", path);
} else if (i == 3) {
"unix control '%s' allows access "
"to nobody", path);
}
logctx);
if (tresult != ISC_R_SUCCESS)
}
}
return (result);
}
{
const cfg_listelt_t *velement;
"any", "none"};
} else {
"when using 'view' statements, "
"all zones must be in views");
}
}
if (tresult != ISC_R_SUCCESS)
{
if (cfg_obj_isstring(vclassobj)) {
if (tresult != ISC_R_SUCCESS)
"view '%s': invalid class %s",
}
if (tresult == ISC_R_EXISTS) {
const char *file;
unsigned int line;
"view '%s': already exists "
"previous definition: %s:%u",
} else if (result != ISC_R_SUCCESS) {
vclass == dns_rdataclass_ch) ||
vclass == dns_rdataclass_in)) {
"attempt to redefine builtin view "
"'%s'", key);
}
}
if (tresult == ISC_R_SUCCESS)
if (tresult != ISC_R_SUCCESS)
}
if (tresult == ISC_R_SUCCESS) {
"'cache-file' cannot be a global "
"option if views are present");
}
}
if (tresult == ISC_R_SUCCESS) {
const cfg_listelt_t *elt;
const cfg_listelt_t *elt2;
const char *aclname;
unsigned int i;
for (i = 0;
i++)
"attempt to redefine "
"builtin acl '%s'",
aclname);
break;
}
const char *name;
"name"));
file = "<unknown file>";
"attempt to redefine "
"acl '%s' previous "
"definition: %s:%u",
}
}
}
}
if (tresult == ISC_R_SUCCESS) {
const cfg_listelt_t *elt;
const cfg_listelt_t *elt2;
const char *aclname;
const char *name;
"name"));
file = "<unknown file>";
"attempt to redefine "
"kal '%s' previous "
"definition: %s:%u",
}
}
}
}
return (result);
}