# ipv6_cidr_warn.dpatch by Francesco Paolo Lovergine diff -urNad proftpd-1.3.0~/modules/mod_core.c proftpd-1.3.0/modules/mod_core.c --- proftpd-1.3.0~/modules/mod_core.c 2006-05-22 12:27:46.000000000 +0200 +++ proftpd-1.3.0/modules/mod_core.c 2006-05-22 12:38:53.000000000 +0200 @@ -1025,10 +1025,15 @@ if (strcasecmp("all", *(cargv + 1)) == 0 || strcasecmp("none", *(cargv + 1)) == 0) { pr_netacl_t *acl = pr_netacl_create(cmd->tmp_pool, *(cargv + 1)); + if (!acl) { + CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad ACL definition '", + *(cargv + 1), "': ", strerror(errno), NULL)); + } - if (pr_class_add_acl(acl) < 0) + if (pr_class_add_acl(acl) < 0) { CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error adding rule '", *(cargv + 1), "': ", strerror(errno), NULL)); + } cargc = 0; } @@ -1043,18 +1048,24 @@ while ((ent = get_token(&str, ",")) != NULL) { if (*ent) { - pr_netacl_t *acl; + pr_netacl_t *acl; - if (strcasecmp(ent, "all") == 0 || - strcasecmp(ent, "none") == 0) { - cargc = 0; - break; + if (strcasecmp(ent, "all") == 0 || + strcasecmp(ent, "none") == 0) { + cargc = 0; + break; + } + + acl = pr_netacl_create(cmd->tmp_pool, ent); + if (!acl) { + CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad ACL definition '", + *(cargv + 1), "': ", strerror(errno), NULL)); } - acl = pr_netacl_create(cmd->tmp_pool, ent); - if (pr_class_add_acl(acl) < 0) - CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error adding rule '", ent, - "': ", strerror(errno), NULL)); + if (pr_class_add_acl(acl) < 0) { + CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error adding rule '", ent, + "': ", strerror(errno), NULL)); + } } } } @@ -2511,9 +2522,10 @@ } acl = pr_netacl_create(c->pool, ent); - if (!acl) - CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad ACL definition: '", + if (!acl) { + CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad ACL definition '", ent, "': ", strerror(errno), NULL)); + } *((pr_netacl_t **) push_array(list)) = acl; } diff -urNad proftpd-1.3.0~/src/netacl.c proftpd-1.3.0/src/netacl.c --- proftpd-1.3.0~/src/netacl.c 2006-05-22 12:27:46.000000000 +0200 +++ proftpd-1.3.0/src/netacl.c 2006-05-22 12:38:53.000000000 +0200 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server daemon - * Copyright (c) 2003 The ProFTPD Project team + * Copyright (c) 2003-2006 The ProFTPD Project team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -172,15 +172,20 @@ #ifdef PR_USE_IPV6 case AF_INET6: { - /* Make sure that the given number of bits is not more than supported - * for IPv6 addresses (128). - */ if (acl->masklen > 128) { - errno = EINVAL; - return NULL; - } + errno = EINVAL; + return NULL; - break; + } else if (pr_netaddr_is_v4mappedv6(acl->addr) == TRUE && + acl->masklen > 32) { + + /* The admin may be trying to use IPv6-style masks on IPv4-mapped + * IPv6 addresses, which of course will not work as expected. + * If the mask is 32 bits or more, warn the admin. + */ + pr_log_pri(PR_LOG_WARNING, "warning: possibly using IPv6-style netmask on IPv4-mapped IPv6 address, which will not work as expected"); + break; + } } #endif /* PR_USE_IPV6 */ diff -urNad proftpd-1.3.0~/src/netaddr.c proftpd-1.3.0/src/netaddr.c --- proftpd-1.3.0~/src/netaddr.c 2005-09-19 23:35:38.000000000 +0200 +++ proftpd-1.3.0/src/netaddr.c 2006-05-22 12:38:53.000000000 +0200 @@ -1,6 +1,6 @@ /* * ProFTPD - FTP server daemon - * Copyright (c) 2003-2005 The ProFTPD Project team + * Copyright (c) 2003-2006 The ProFTPD Project team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -219,8 +219,7 @@ } #endif /* !HAVE_INET_PTON */ -#ifdef HAVE_GETHOSTBYNAME2 -static void *get_v4inaddr(pr_netaddr_t *na) { +static void *get_v4inaddr(const pr_netaddr_t *na) { /* This function is specifically for IPv4 clients (when gethostbyname2(2) is * present) that have an IPv4-mapped IPv6 address, when performing reverse @@ -234,7 +233,6 @@ return (((char *) pr_netaddr_get_inaddr(na)) + 12); } -#endif /* HAVE_GETHOSTBYNAME2 */ int pr_netaddr_set_reverse_dns(int enable) { int old_enable = reverse_dns; @@ -652,6 +650,10 @@ } int pr_netaddr_cmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2) { + pool *tmp_pool = NULL; + pr_netaddr_t *a, *b; + int res; + if (na1 && !na2) return 1; @@ -662,29 +664,85 @@ return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { - /* Cannot compare addresses from different families. */ - errno = EINVAL; - return -1; + + /* Cannot compare addresses from different families, unless one + * of the netaddrs has an AF_INET family, and the other has an + * AF_INET6 family AND is an IPv4-mapped IPv6 address. + */ + + if (pr_netaddr_is_v4mappedv6(na1) != TRUE && + pr_netaddr_is_v4mappedv6(na2) != TRUE) { + errno = EINVAL; + return -1; + } + + if (pr_netaddr_is_v4mappedv6(na1) == TRUE) { + tmp_pool = make_sub_pool(permanent_pool); + + /* This case means that na1 is an IPv4-mapped IPv6 address, and + * na2 is an IPv4 address. + */ + a = pr_netaddr_alloc(tmp_pool); + pr_netaddr_set_family(a, AF_INET); + pr_netaddr_set_port(a, pr_netaddr_get_port(na1)); + memcpy(&a->na_addr.v4.sin_addr, get_v4inaddr(na1), + sizeof(struct in_addr)); + + b = (pr_netaddr_t *) na2; + + } else if (pr_netaddr_is_v4mappedv6(na2) == TRUE) { + tmp_pool = make_sub_pool(permanent_pool); + + /* This case means that na is an IPv4 address, and na2 is an + * IPv4-mapped IPv6 address. + */ + a = (pr_netaddr_t *) na1; + + b = pr_netaddr_alloc(tmp_pool); + pr_netaddr_set_family(b, AF_INET); + pr_netaddr_set_port(b, pr_netaddr_get_port(na2)); + memcpy(&b->na_addr.v4.sin_addr, get_v4inaddr(na2), + sizeof(struct in_addr)); + + } else { + a = (pr_netaddr_t *) na1; + b = (pr_netaddr_t *) na2; + } + + } else { + a = (pr_netaddr_t *) na1; + b = (pr_netaddr_t *) na2; } - switch (pr_netaddr_get_family(na1)) { + switch (pr_netaddr_get_family(a)) { case AF_INET: - return memcmp(&na1->na_addr.v4.sin_addr, &na2->na_addr.v4.sin_addr, + res = memcmp(&a->na_addr.v4.sin_addr, &b->na_addr.v4.sin_addr, sizeof(struct in_addr)); + if (tmp_pool) + destroy_pool(tmp_pool); + return res; #ifdef PR_USE_IPV6 case AF_INET6: - return memcmp(&na1->na_addr.v6.sin6_addr, &na2->na_addr.v6.sin6_addr, + res = memcmp(&a->na_addr.v6.sin6_addr, &b->na_addr.v6.sin6_addr, sizeof(struct in6_addr)); + if (tmp_pool); + destroy_pool(tmp_pool); + return res; #endif /* PR_USE_IPV6 */ } + if (tmp_pool) + destroy_pool(tmp_pool); + errno = EPERM; return -1; } int pr_netaddr_ncmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2, unsigned int bitlen) { + pool *tmp_pool = NULL; + pr_netaddr_t *a, *b; unsigned int nbytes, nbits; const unsigned char *in1, *in2; @@ -698,12 +756,57 @@ return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { - /* Cannot compare addresses from different families. */ - errno = EINVAL; - return -1; + + /* Cannot compare addresses from different families, unless one + * of the netaddrs has an AF_INET family, and the other has an + * AF_INET6 family AND is an IPv4-mapped IPv6 address. + */ + + if (pr_netaddr_is_v4mappedv6(na1) != TRUE && + pr_netaddr_is_v4mappedv6(na2) != TRUE) { + errno = EINVAL; + return -1; + } + + if (pr_netaddr_is_v4mappedv6(na1) == TRUE) { + tmp_pool = make_sub_pool(permanent_pool); + + /* This case means that na1 is an IPv4-mapped IPv6 address, and + * na2 is an IPv4 address. + */ + a = pr_netaddr_alloc(tmp_pool); + pr_netaddr_set_family(a, AF_INET); + pr_netaddr_set_port(a, pr_netaddr_get_port(na1)); + memcpy(&a->na_addr.v4.sin_addr, get_v4inaddr(na1), + sizeof(struct in_addr)); + + b = (pr_netaddr_t *) na2; + + } else if (pr_netaddr_is_v4mappedv6(na2) == TRUE) { + tmp_pool = make_sub_pool(permanent_pool); + + /* This case means that na is an IPv4 address, and na2 is an + * IPv4-mapped IPv6 address. + */ + a = (pr_netaddr_t *) na1; + + b = pr_netaddr_alloc(tmp_pool); + pr_netaddr_set_family(b, AF_INET); + pr_netaddr_set_port(b, pr_netaddr_get_port(na2)); + memcpy(&b->na_addr.v4.sin_addr, get_v4inaddr(na2), + sizeof(struct in_addr)); + + } else { + a = (pr_netaddr_t *) na1; + b = (pr_netaddr_t *) na2; + } + + } else { + a = (pr_netaddr_t *) na1; + b = (pr_netaddr_t *) na2; } - switch (pr_netaddr_get_family(na1)) { + switch (pr_netaddr_get_family(a)) { case AF_INET: { /* Make sure that the given number of bits is not more than supported * for IPv4 addresses (32). @@ -736,8 +839,8 @@ } /* Retrieve pointers to the contained in_addrs. */ - in1 = (const unsigned char *) pr_netaddr_get_inaddr(na1); - in2 = (const unsigned char *) pr_netaddr_get_inaddr(na2); + in1 = (const unsigned char *) pr_netaddr_get_inaddr(a); + in2 = (const unsigned char *) pr_netaddr_get_inaddr(b); /* Determine the number of bytes, and leftover bits, in the given * bit length. @@ -750,8 +853,12 @@ int res = memcmp(in1, in2, nbytes); /* No need to continue comparing the addresses if they differ already. */ - if (res != 0) + if (res != 0) { + if (tmp_pool) + destroy_pool(tmp_pool); + return res; + } } /* Next, compare the remaining bits in the addresses. */ @@ -765,13 +872,22 @@ /* Build up a mask covering the bits left to be checked. */ mask = (0xff << (8 - nbits)) & 0xff; - if ((in1byte & mask) > (in2byte & mask)) + if ((in1byte & mask) > (in2byte & mask)) { + if (tmp_pool) + destroy_pool(tmp_pool); return 1; + } - if ((in1byte & mask) < (in2byte & mask)) + if ((in1byte & mask) < (in2byte & mask)) { + if (tmp_pool) + destroy_pool(tmp_pool); return -1; + } } + if (tmp_pool) + destroy_pool(tmp_pool); + /* If we've made it this far, the addresses match, for the given bit * length. */