mirror of
https://github.com/samba-team/samba.git
synced 2025-08-02 00:22:11 +03:00
samldb: ensure subnets have proper net ranges
A subnet name needs to be a valid CIDR address range -- that's the ones that look like 10.9.8.0/22, where the number after the / determines how many bits are in the address suffix. It can be IPv4 or IPv6. There are a few odd constraints (see MS-ADTS v20150630 6.1.1.2.2.2.1 "Subnet Object") -- for example, with IPv4, the implied bit mask can't equal the address. That is, you can't have a subnet named "255.255.255.0/24" in a Windows subnet. This rule does not apply to IPv6. Windows and Samba both make some ensure that subnets have a unique valid name, though unfortunately Windows 2008R2 is rather slack when it comes to IPv6. We follow Windows 2012R2, which roughly follows RFC5952 -- with one caveat: Windows will allow an address like "::ffff:0:1:2", which translates to the IPv4 address "0.1.0.2" using the SIIT translation scheme, and which inet_ntop() would render as "::ffff:0:0.1.0.2". In the Samba implementation we use an inet_pton()/ inet_ntop() round-trip to establish canonicality, so these addresses fail. Windows wisely does not allow the SIIT style addresses (the acronym is widely agreed to be off-by-one in the second letter), and it will regard "::ffff:0:1:2" as simply "::ffff:0:1:2" and allow it. We would like to do that too. Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Garming Sam <garming@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
committed by
Andrew Bartlett
parent
cbb93977cd
commit
906a53f442
@ -110,3 +110,27 @@ class SitesSubnetCmdTestCase(BaseSitesCmdTestCase):
|
|||||||
self.assertIsNotNone(ret)
|
self.assertIsNotNone(ret)
|
||||||
self.assertEqual(len(ret), 1)
|
self.assertEqual(len(ret), 1)
|
||||||
self.samdb.delete(dnsubnet, ["tree_delete:0"])
|
self.samdb.delete(dnsubnet, ["tree_delete:0"])
|
||||||
|
|
||||||
|
def test_site_subnet_create_should_fail(self):
|
||||||
|
cidrs = (("10.9.8.0/33", self.sitename), # mask too big
|
||||||
|
("50.60.0.0/8", self.sitename2), # insufficient zeros
|
||||||
|
("50.261.0.0/16", self.sitename2), # bad octet
|
||||||
|
("7.0.0.0.0/0", self.sitename), # insufficient zeros
|
||||||
|
("aaaa:bbbb:cccc:dddd:eeee:ffff:2222:1100/119",
|
||||||
|
self.sitename), # insufficient zeros
|
||||||
|
)
|
||||||
|
|
||||||
|
for cidr, sitename in cidrs:
|
||||||
|
result, out, err = self.runsubcmd("sites", "subnet", "create",
|
||||||
|
cidr, sitename,
|
||||||
|
"-H", self.dburl,
|
||||||
|
self.creds_string)
|
||||||
|
self.assertCmdFail(result)
|
||||||
|
|
||||||
|
ret = self.samdb.search(base=self.config_dn,
|
||||||
|
scope=ldb.SCOPE_SUBTREE,
|
||||||
|
expression=('(&(objectclass=subnet)(cn=%s))'
|
||||||
|
% cidr))
|
||||||
|
|
||||||
|
self.assertIsNotNone(ret)
|
||||||
|
self.assertEqual(len(ret), 0)
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "ldb_wrap.h"
|
#include "ldb_wrap.h"
|
||||||
#include "param/param.h"
|
#include "param/param.h"
|
||||||
#include "libds/common/flag_mapping.h"
|
#include "libds/common/flag_mapping.h"
|
||||||
|
#include "system/network.h"
|
||||||
|
|
||||||
struct samldb_ctx;
|
struct samldb_ctx;
|
||||||
enum samldb_add_type {
|
enum samldb_add_type {
|
||||||
@ -2644,6 +2645,253 @@ static int samldb_fsmo_role_owner_check(struct samldb_ctx *ac)
|
|||||||
return LDB_SUCCESS;
|
return LDB_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return zero if the number of zero bits in the address (looking from low to
|
||||||
|
* high) is equal to or greater than the length minus the mask. Otherwise it
|
||||||
|
* returns -1.
|
||||||
|
*/
|
||||||
|
static int check_cidr_zero_bits(uint8_t *address, unsigned int len,
|
||||||
|
unsigned int mask)
|
||||||
|
{
|
||||||
|
/* <address> is an integer in big-endian form, <len> bits long. All
|
||||||
|
bits between <mask> and <len> must be zero. */
|
||||||
|
int i;
|
||||||
|
unsigned int byte_len;
|
||||||
|
unsigned int byte_mask;
|
||||||
|
unsigned int bit_mask;
|
||||||
|
if (len == 32) {
|
||||||
|
DBG_INFO("Looking at address %02x%02x%02x%02x, mask %u\n",
|
||||||
|
address[0], address[1], address[2], address[3],
|
||||||
|
mask);
|
||||||
|
} else if (len == 128){
|
||||||
|
DBG_INFO("Looking at address "
|
||||||
|
"%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
|
||||||
|
"%02x%02x-%02x%02x-%02x%02x-%02x%02x, mask %u\n",
|
||||||
|
address[0], address[1], address[2], address[3],
|
||||||
|
address[4], address[5], address[6], address[7],
|
||||||
|
address[8], address[9], address[10], address[11],
|
||||||
|
address[12], address[13], address[14], address[15],
|
||||||
|
mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask > len){
|
||||||
|
DBG_INFO("mask %u is too big (> %u)\n", mask, len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (mask == len){
|
||||||
|
/* single address subnet.
|
||||||
|
* In IPv4 all 255s is invalid by the bitmask != address rule
|
||||||
|
* in MS-ADTS. IPv6 does not suffer.
|
||||||
|
*/
|
||||||
|
if (len == 32){
|
||||||
|
if (address[0] == 255 &&
|
||||||
|
address[1] == 255 &&
|
||||||
|
address[2] == 255 &&
|
||||||
|
address[3] == 255){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_len = len / 8;
|
||||||
|
byte_mask = mask / 8;
|
||||||
|
|
||||||
|
for (i = byte_len - 1; i > byte_mask; i--){
|
||||||
|
DBG_DEBUG("checking byte %d %02x\n", i, address[i]);
|
||||||
|
if (address[i] != 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bit_mask = (1 << (8 - (mask & 7))) - 1;
|
||||||
|
DBG_DEBUG("checking bitmask %02x & %02x overlap %02x\n", bit_mask, address[byte_mask],
|
||||||
|
bit_mask & address[byte_mask]);
|
||||||
|
if (address[byte_mask] & bit_mask){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* According to MS-ADTS, the mask can't exactly equal the bitmask for
|
||||||
|
* IPv4 (but this is fine for v6). That is 255.255.80.0/17 is bad,
|
||||||
|
* because the bitmask implied by "/17" is 255.255.80.0.
|
||||||
|
*
|
||||||
|
* The bit_mask used in the previous check is the complement of what
|
||||||
|
* we want here.
|
||||||
|
*/
|
||||||
|
if (len == 32 && address[byte_mask] == (uint8_t)~bit_mask){
|
||||||
|
bool ok = false;
|
||||||
|
for (i = 0; i < byte_mask; i++){
|
||||||
|
if (address[i] != 255){
|
||||||
|
ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok == false){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int check_address_roundtrip(const char *address, int family,
|
||||||
|
const uint8_t *address_bytes,
|
||||||
|
char *buffer, int buffer_len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check that the address is in the canonical RFC5952 format for IPv6,
|
||||||
|
* and lacks extra leading zeros for each dotted decimal for IPv4.
|
||||||
|
* Handily this is what inet_ntop() gives you.
|
||||||
|
*/
|
||||||
|
const char *address_redux = inet_ntop(family, address_bytes,
|
||||||
|
buffer, buffer_len);
|
||||||
|
if (address_redux == NULL){
|
||||||
|
DBG_INFO("Address round trip %s failed unexpectedly"
|
||||||
|
" with errno %d\n", address, errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (strcasecmp(address, address_redux) != 0){
|
||||||
|
DBG_INFO("Address %s round trips to %s; fail!\n",
|
||||||
|
address, address_redux);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MS-ADTS v20150630 6.1.1.2.2.2.1 Subnet Object, refers to RFC1166 and
|
||||||
|
* RFC2373. It specifies something seemingly indistinguishable from an RFC4632
|
||||||
|
* CIDR address range without saying so explicitly. Here we follow the CIDR
|
||||||
|
* spec.
|
||||||
|
*
|
||||||
|
* Return 0 on success, -1 on error.
|
||||||
|
*/
|
||||||
|
static int verify_cidr(const char *cidr)
|
||||||
|
{
|
||||||
|
char *address = NULL, *slash = NULL, *endptr = NULL;
|
||||||
|
bool has_colon, has_dot;
|
||||||
|
int res, ret;
|
||||||
|
unsigned long mask;
|
||||||
|
uint8_t *address_bytes = NULL;
|
||||||
|
char *address_redux = NULL;
|
||||||
|
unsigned int address_len;
|
||||||
|
TALLOC_CTX *frame = NULL;
|
||||||
|
|
||||||
|
DBG_DEBUG("CIDR is %s\n", cidr);
|
||||||
|
frame = talloc_stackframe();
|
||||||
|
address = talloc_strdup(frame, cidr);
|
||||||
|
if (address == NULL){
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* there must be a '/' */
|
||||||
|
slash = strchr(address, '/');
|
||||||
|
if (slash == NULL){
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
/* terminate the address for strchr, inet_pton */
|
||||||
|
*slash = '\0';
|
||||||
|
|
||||||
|
mask = strtoul(slash + 1, &endptr, 10);
|
||||||
|
if (mask == 0){
|
||||||
|
DBG_INFO("Windows does not like the zero mask, "
|
||||||
|
"so nor do we: %s\n", cidr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*endptr != '\0' || endptr == slash + 1){
|
||||||
|
DBG_INFO("CIDR mask is not a proper integer: %s\n", cidr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_bytes = talloc_size(frame, sizeof(struct in6_addr));
|
||||||
|
if (address_bytes == NULL){
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
address_redux = talloc_size(frame, INET6_ADDRSTRLEN);
|
||||||
|
if (address_redux == NULL){
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_INFO("found address %s, mask %lu\n", address, mask);
|
||||||
|
has_colon = (strchr(address, ':') == NULL) ? false : true;
|
||||||
|
has_dot = (strchr(address, '.') == NULL) ? false : true;
|
||||||
|
if (has_dot && has_colon){
|
||||||
|
/* This seems to be an IPv4 address embedded in IPv6, which is
|
||||||
|
icky. We don't support it. */
|
||||||
|
DBG_INFO("Refusing to consider cidr '%s' with dots and colons\n",
|
||||||
|
cidr);
|
||||||
|
goto error;
|
||||||
|
} else if (has_colon){ /* looks like IPv6 */
|
||||||
|
res = inet_pton(AF_INET6, address, address_bytes);
|
||||||
|
if (res != 1) {
|
||||||
|
DBG_INFO("Address in %s fails to parse as IPv6\n", cidr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
address_len = 128;
|
||||||
|
if (check_address_roundtrip(address, AF_INET6, address_bytes,
|
||||||
|
address_redux, INET6_ADDRSTRLEN)){
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else if (has_dot) {
|
||||||
|
/* looks like IPv4 */
|
||||||
|
if (strcmp(address, "0.0.0.0") == 0){
|
||||||
|
DBG_INFO("Windows does not like the zero IPv4 address, "
|
||||||
|
"so nor do we.\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
res = inet_pton(AF_INET, address, address_bytes);
|
||||||
|
if (res != 1) {
|
||||||
|
DBG_INFO("Address in %s fails to parse as IPv4\n", cidr);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
address_len = 32;
|
||||||
|
|
||||||
|
if (check_address_roundtrip(address, AF_INET, address_bytes,
|
||||||
|
address_redux, INET_ADDRSTRLEN)){
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* This doesn't look like an IP address at all. */
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = check_cidr_zero_bits(address_bytes, address_len, mask);
|
||||||
|
talloc_free(frame);
|
||||||
|
return ret;
|
||||||
|
error:
|
||||||
|
talloc_free(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int samldb_verify_subnet(struct samldb_ctx *ac)
|
||||||
|
{
|
||||||
|
struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
|
||||||
|
const char *cidr = NULL;
|
||||||
|
const struct ldb_val *rdn_value = NULL;
|
||||||
|
|
||||||
|
rdn_value = ldb_dn_get_rdn_val(ac->msg->dn);
|
||||||
|
cidr = ldb_dn_escape_value(ac, *rdn_value);
|
||||||
|
DBG_INFO("looking at cidr '%s'\n", cidr);
|
||||||
|
if (cidr == NULL) {
|
||||||
|
ldb_set_errstring(ldb,
|
||||||
|
"samldb: adding an empty subnet cidr seems wrong");
|
||||||
|
return LDB_ERR_UNWILLING_TO_PERFORM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verify_cidr(cidr)){
|
||||||
|
ldb_set_errstring(ldb,
|
||||||
|
"samldb: subnet value is invalid");
|
||||||
|
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LDB_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* add */
|
/* add */
|
||||||
static int samldb_add(struct ldb_module *module, struct ldb_request *req)
|
static int samldb_add(struct ldb_module *module, struct ldb_request *req)
|
||||||
@ -2752,6 +3000,17 @@ static int samldb_add(struct ldb_module *module, struct ldb_request *req)
|
|||||||
return samldb_fill_object(ac);
|
return samldb_fill_object(ac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (samdb_find_attribute(ldb, ac->msg,
|
||||||
|
"objectclass", "subnet") != NULL) {
|
||||||
|
ret = samldb_verify_subnet(ac);
|
||||||
|
if (ret != LDB_SUCCESS) {
|
||||||
|
talloc_free(ac);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* We are just checking the value is valid, and there are no
|
||||||
|
values to fill in. */
|
||||||
|
}
|
||||||
|
|
||||||
talloc_free(ac);
|
talloc_free(ac);
|
||||||
|
|
||||||
/* nothing matched, go on */
|
/* nothing matched, go on */
|
||||||
@ -3099,6 +3358,15 @@ static int check_rename_constraints(struct ldb_message *msg,
|
|||||||
return LDB_ERR_UNWILLING_TO_PERFORM;
|
return LDB_ERR_UNWILLING_TO_PERFORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* subnet objects */
|
||||||
|
if (samdb_find_attribute(ldb, msg, "objectclass", "subnet") != NULL) {
|
||||||
|
ret = samldb_verify_subnet(ac);
|
||||||
|
if (ret != LDB_SUCCESS) {
|
||||||
|
talloc_free(ac);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* systemFlags */
|
/* systemFlags */
|
||||||
|
|
||||||
dn1 = ldb_dn_get_parent(ac, olddn);
|
dn1 = ldb_dn_get_parent(ac, olddn);
|
||||||
|
@ -183,5 +183,293 @@ class SimpleSubnetTests(SitesBaseTests):
|
|||||||
self.assertRaises(subnets.SubnetNotFound,
|
self.assertRaises(subnets.SubnetNotFound,
|
||||||
subnets.delete_subnet, self.ldb, basedn, cidr)
|
subnets.delete_subnet, self.ldb, basedn, cidr)
|
||||||
|
|
||||||
|
def test_create_bad_ranges(self):
|
||||||
|
"""These CIDR ranges all have something wrong with them, and they
|
||||||
|
should all fail."""
|
||||||
|
basedn = self.ldb.get_config_basedn()
|
||||||
|
|
||||||
|
cidrs = [
|
||||||
|
# IPv4
|
||||||
|
# insufficient zeros
|
||||||
|
"10.11.12.0/14",
|
||||||
|
"110.0.0.0/6",
|
||||||
|
"1.0.0.0/0",
|
||||||
|
"10.11.13.1/24",
|
||||||
|
"1.2.3.4/29",
|
||||||
|
"10.11.12.0/21",
|
||||||
|
# out of range mask
|
||||||
|
"110.0.0.0/33",
|
||||||
|
"110.0.0.0/-1",
|
||||||
|
"4.0.0.0/111",
|
||||||
|
# out of range address
|
||||||
|
"310.0.0.0/24",
|
||||||
|
"10.0.0.256/32",
|
||||||
|
"1.1.-20.0/24",
|
||||||
|
# badly formed
|
||||||
|
"1.0.0.0/1e",
|
||||||
|
"1.0.0.0/24.0",
|
||||||
|
"1.0.0.0/1/1",
|
||||||
|
"1.0.0.0",
|
||||||
|
"1.c.0.0/24",
|
||||||
|
"1.2.0.0.0/27",
|
||||||
|
"1.23.0/24",
|
||||||
|
"1.23.0.-7/24",
|
||||||
|
"1.-23.0.7/24",
|
||||||
|
"1.23.-0.7/24",
|
||||||
|
"1.23.0.0/0x10",
|
||||||
|
# IPv6 insufficient zeros -- this could be a subtle one
|
||||||
|
# due to the vagaries of endianness in the 16 bit groups.
|
||||||
|
"aaaa:bbbb:cccc:dddd:eeee:ffff:2222:1100/119",
|
||||||
|
"aaaa:bbbb::/31",
|
||||||
|
"a:b::/31",
|
||||||
|
"c000::/1",
|
||||||
|
"a::b00/119",
|
||||||
|
"1::1/127",
|
||||||
|
"1::2/126",
|
||||||
|
"1::100/119",
|
||||||
|
"1::8000/112",
|
||||||
|
# out of range mask
|
||||||
|
"a:b::/130",
|
||||||
|
"a:b::/-1",
|
||||||
|
"::/129",
|
||||||
|
# An IPv4 address can't be exactly the bitmask (MS ADTS)
|
||||||
|
"128.0.0.0/1",
|
||||||
|
"192.0.0.0/2",
|
||||||
|
"255.192.0.0/10",
|
||||||
|
"255.255.255.0/24",
|
||||||
|
"255.255.255.255/32",
|
||||||
|
"0.0.0.0/0",
|
||||||
|
# The address can't have leading zeros (not RFC 4632, but MS ADTS)
|
||||||
|
"00.1.2.0/24",
|
||||||
|
"003.1.2.0/24",
|
||||||
|
"022.1.0.0/16",
|
||||||
|
"00000000000000000000000003.1.2.0/24",
|
||||||
|
"09876::abfc/126",
|
||||||
|
"0aaaa:bbbb::/32",
|
||||||
|
"009876::abfc/126",
|
||||||
|
"000a:bbbb::/32",
|
||||||
|
|
||||||
|
# How about extraneous zeros later on
|
||||||
|
"3.01.2.0/24",
|
||||||
|
"3.1.2.00/24",
|
||||||
|
"22.001.0.0/16",
|
||||||
|
"3.01.02.0/24",
|
||||||
|
"100a:0bbb:0023::/48",
|
||||||
|
"100a::0023/128",
|
||||||
|
|
||||||
|
# Windows doesn't like the zero IPv4 address
|
||||||
|
"0.0.0.0/8",
|
||||||
|
# or the zero mask on IPv6
|
||||||
|
"::/0",
|
||||||
|
|
||||||
|
# various violations of RFC5952
|
||||||
|
"0:0:0:0:0:0:0:0/8",
|
||||||
|
"0::0/0",
|
||||||
|
"::0:0/48",
|
||||||
|
"::0:4/128",
|
||||||
|
"0::/8",
|
||||||
|
"0::4f/128",
|
||||||
|
"0::42:0:0:0:0/64",
|
||||||
|
"4f::0/48",
|
||||||
|
|
||||||
|
# badly formed -- mostly the wrong arrangement of colons
|
||||||
|
"a::b::0/120",
|
||||||
|
"a::abcdf:0/120",
|
||||||
|
"a::g:0/120",
|
||||||
|
"::0::3/48",
|
||||||
|
"2001:3::110::3/118",
|
||||||
|
"aaaa:bbbb:cccc:dddd:eeee:ffff:2222:1111:0000/128",
|
||||||
|
"a:::5:0/120",
|
||||||
|
|
||||||
|
# non-canonical representations (vs RFC 5952)
|
||||||
|
# "2001:0:c633:63::1:0/120" is correct
|
||||||
|
"2001:0:c633:63:0:0:1:0/120",
|
||||||
|
"2001::c633:63:0:0:1:0/120",
|
||||||
|
"2001:0:c633:63:0:0:1::/120",
|
||||||
|
|
||||||
|
# "10:0:0:42::/64" is correct
|
||||||
|
"10::42:0:0:0:0/64",
|
||||||
|
"10:0:0:42:0:0:0:0/64",
|
||||||
|
|
||||||
|
# "1::4:5:0:0:8/127" is correct
|
||||||
|
"1:0:0:4:5:0:0:8/127",
|
||||||
|
"1:0:0:4:5::8/127",
|
||||||
|
|
||||||
|
# "2001:db8:0:1:1:1:1:1/128" is correct
|
||||||
|
"2001:db8::1:1:1:1:1/128",
|
||||||
|
|
||||||
|
# IP4 embedded - rejected
|
||||||
|
"a::10.0.0.0/120",
|
||||||
|
"a::10.9.8.7/128",
|
||||||
|
"::ffff:0:0/96", #this one fails on WIN2012r2
|
||||||
|
# completely wrong
|
||||||
|
None,
|
||||||
|
"bob",
|
||||||
|
3.1415,
|
||||||
|
False,
|
||||||
|
"10.11.16.0/24\x00hidden bytes past a zero",
|
||||||
|
self,
|
||||||
|
]
|
||||||
|
|
||||||
|
failures = []
|
||||||
|
for cidr in cidrs:
|
||||||
|
try:
|
||||||
|
subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
|
||||||
|
except subnets.SubnetInvalid:
|
||||||
|
print >> sys.stderr, "%s fails properly" % (cidr,)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# we are here because it succeeded when it shouldn't have.
|
||||||
|
print >> sys.stderr, "CIDR %s fails to fail" % (cidr,)
|
||||||
|
failures.append(cidr)
|
||||||
|
subnets.delete_subnet(self.ldb, basedn, cidr)
|
||||||
|
|
||||||
|
if failures:
|
||||||
|
print "These bad subnet names were accepted:"
|
||||||
|
for cidr in failures:
|
||||||
|
print " %s" % cidr
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
def test_create_good_ranges(self):
|
||||||
|
"""All of these CIDRs are good, and the subnet creation should
|
||||||
|
succeed."""
|
||||||
|
basedn = self.ldb.get_config_basedn()
|
||||||
|
|
||||||
|
cidrs = [
|
||||||
|
# IPv4
|
||||||
|
"10.11.12.0/24",
|
||||||
|
"10.11.12.0/23",
|
||||||
|
"10.11.12.0/25",
|
||||||
|
"110.0.0.0/7",
|
||||||
|
"1.0.0.0/32",
|
||||||
|
"10.11.13.0/32",
|
||||||
|
"10.11.13.1/32",
|
||||||
|
"99.0.97.0/24",
|
||||||
|
"1.2.3.4/30",
|
||||||
|
"10.11.12.0/22",
|
||||||
|
"0.12.13.0/24",
|
||||||
|
# IPv6
|
||||||
|
"aaaa:bbbb:cccc:dddd:eeee:ffff:2222:1100/120",
|
||||||
|
"aaaa:bbbb:cccc:dddd:eeee:ffff:2222:11f0/124",
|
||||||
|
"aaaa:bbbb:cccc:dddd:eeee:ffff:2222:11fc/126",
|
||||||
|
# don't forget upper case
|
||||||
|
"FFFF:FFFF:FFFF:FFFF:ABCD:EfFF:FFFF:FFeF/128",
|
||||||
|
"9876::ab00/120",
|
||||||
|
"9876::abf0/124",
|
||||||
|
"9876::abfc/126",
|
||||||
|
"aaaa:bbbb::/32",
|
||||||
|
"aaaa:bbba::/31",
|
||||||
|
"aaaa:ba00::/23",
|
||||||
|
"aaaa:bb00::/24",
|
||||||
|
"aaaa:bb00::/77",
|
||||||
|
"::/48",
|
||||||
|
"a:b::/32",
|
||||||
|
"c000::/2",
|
||||||
|
"a::b00/120",
|
||||||
|
"1::2/127",
|
||||||
|
# this pattern of address suffix == mask is forbidden with
|
||||||
|
# IPv4 but OK for IPv6.
|
||||||
|
"8000::/1",
|
||||||
|
"c000::/2",
|
||||||
|
"ffff:ffff:ffc0::/42",
|
||||||
|
"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF/128",
|
||||||
|
# leading zeros are forbidden, but implicit IPv6 zeros
|
||||||
|
# (via "::") are OK.
|
||||||
|
"::1000/116",
|
||||||
|
"::8000/113",
|
||||||
|
# taken to the logical conclusion, "::/0" should be OK, but no.
|
||||||
|
"::/48",
|
||||||
|
|
||||||
|
# Try some reserved ranges, which it might be reasonable
|
||||||
|
# to exclude, but which are not excluded in practice.
|
||||||
|
"129.0.0.0/16",
|
||||||
|
"129.255.0.0/16",
|
||||||
|
"100.64.0.0/10",
|
||||||
|
"127.0.0.0/8",
|
||||||
|
"127.0.0.0/24",
|
||||||
|
"169.254.0.0/16",
|
||||||
|
"169.254.1.0/24",
|
||||||
|
"192.0.0.0/24",
|
||||||
|
"192.0.2.0/24",
|
||||||
|
"198.18.0.0/15",
|
||||||
|
"198.51.100.0/24",
|
||||||
|
"203.0.113.0/24",
|
||||||
|
"224.0.0.0/4",
|
||||||
|
"130.129.0.0/16",
|
||||||
|
"130.255.0.0/16",
|
||||||
|
"192.12.0.0/24",
|
||||||
|
"223.255.255.0/24",
|
||||||
|
"240.255.255.0/24",
|
||||||
|
"224.0.0.0/8",
|
||||||
|
"::/96",
|
||||||
|
"100::/64",
|
||||||
|
"2001:10::/28",
|
||||||
|
"fec0::/10",
|
||||||
|
"ff00::/8",
|
||||||
|
"::1/128",
|
||||||
|
"2001:db8::/32",
|
||||||
|
"2001:10::/28",
|
||||||
|
"2002::/24",
|
||||||
|
"2002:a00::/24",
|
||||||
|
"2002:7f00::/24",
|
||||||
|
"2002:a9fe::/32",
|
||||||
|
"2002:ac10::/28",
|
||||||
|
"2002:c000::/40",
|
||||||
|
"2002:c000:200::/40",
|
||||||
|
"2002:c0a8::/32",
|
||||||
|
"2002:c612::/31",
|
||||||
|
"2002:c633:6400::/40",
|
||||||
|
"2002:cb00:7100::/40",
|
||||||
|
"2002:e000::/20",
|
||||||
|
"2002:f000::/20",
|
||||||
|
"2002:ffff:ffff::/48",
|
||||||
|
"2001::/40",
|
||||||
|
"2001:0:a00::/40",
|
||||||
|
"2001:0:7f00::/40",
|
||||||
|
"2001:0:a9fe::/48",
|
||||||
|
"2001:0:ac10::/44",
|
||||||
|
"2001:0:c000::/56",
|
||||||
|
"2001:0:c000:200::/56",
|
||||||
|
"2001:0:c0a8::/48",
|
||||||
|
"2001:0:c612::/47",
|
||||||
|
"2001:0:c633:6400::/56",
|
||||||
|
"2001:0:cb00:7100::/56",
|
||||||
|
"2001:0:e000::/36",
|
||||||
|
"2001:0:f000::/36",
|
||||||
|
"2001:0:ffff:ffff::/64",
|
||||||
|
|
||||||
|
# non-RFC-5952 versions of these are tested in create_bad_ranges
|
||||||
|
"2001:0:c633:63::1:0/120",
|
||||||
|
"10:0:0:42::/64",
|
||||||
|
"1::4:5:0:0:8/127",
|
||||||
|
"2001:db8:0:1:1:1:1:1/128",
|
||||||
|
]
|
||||||
|
failures = []
|
||||||
|
|
||||||
|
for cidr in cidrs:
|
||||||
|
try:
|
||||||
|
subnets.create_subnet(self.ldb, basedn, cidr, self.sitename)
|
||||||
|
except subnets.SubnetInvalid, e:
|
||||||
|
print e
|
||||||
|
failures.append(cidr)
|
||||||
|
continue
|
||||||
|
|
||||||
|
ret = self.ldb.search(base=basedn, scope=SCOPE_SUBTREE,
|
||||||
|
expression=('(&(objectclass=subnet)(cn=%s))' %
|
||||||
|
cidr))
|
||||||
|
|
||||||
|
if len(ret) != 1:
|
||||||
|
print "%s was not created" % cidr
|
||||||
|
failures.append(cidr)
|
||||||
|
continue
|
||||||
|
subnets.delete_subnet(self.ldb, basedn, cidr)
|
||||||
|
|
||||||
|
if failures:
|
||||||
|
print "These good subnet names were not accepted:"
|
||||||
|
for cidr in failures:
|
||||||
|
print " %s" % cidr
|
||||||
|
self.fail()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TestProgram(module=__name__, opts=subunitopts)
|
TestProgram(module=__name__, opts=subunitopts)
|
||||||
|
Reference in New Issue
Block a user