diff options
author | Martin Ukrop <mukrop@redhat.com> | 2016-06-29 11:23:40 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-08-09 10:48:27 +0200 |
commit | 90637db1425813c81169b1f12a613a7b2df803f1 (patch) | |
tree | 1826a82d8e2a29c4e1d8322e0bddb9f1bfd901f6 /lib/x509 | |
parent | 25154fcff0f8ce5c0094e365920a2d7ce3bccdc9 (diff) | |
download | gnutls-90637db1425813c81169b1f12a613a7b2df803f1.tar.gz |
x509: Separate out IP handling functions
- Moved IP/CIDR to string conversion functions into separate
header and export privately for the use in tests.
- Placed ip_in_cidr() into separate header for easy testing
- Add publicly available function to convert text CIDR to RFC5280
format for the use in name constraints extension.
- certtool: Use GnuTLS exported CIDR functions instead of local ones.
- Export mask_to_prefix, mask_ip for internal GnuTLS use.
- Introduce new error value (malformed cidr) and add to description
functions in errors.c.
Signed-off-by: Martin Ukrop <mukrop@redhat.com>
Diffstat (limited to 'lib/x509')
-rw-r--r-- | lib/x509/Makefile.am | 3 | ||||
-rw-r--r-- | lib/x509/ip-in-cidr.h | 51 | ||||
-rw-r--r-- | lib/x509/ip.c | 269 | ||||
-rw-r--r-- | lib/x509/ip.h | 39 | ||||
-rw-r--r-- | lib/x509/name_constraints.c | 4 | ||||
-rw-r--r-- | lib/x509/output.c | 92 |
6 files changed, 366 insertions, 92 deletions
diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index f340a12e74..8ee1191f05 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -69,7 +69,8 @@ libgnutls_x509_la_SOURCES = \ virt-san.h \ x509_ext_int.h \ tls_features.c \ - krb5.c krb5.h + krb5.c krb5.h \ + ip.c ip.h ip-in-cidr.h if ENABLE_OCSP libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c diff --git a/lib/x509/ip-in-cidr.h b/lib/x509/ip-in-cidr.h new file mode 100644 index 0000000000..778502a518 --- /dev/null +++ b/lib/x509/ip-in-cidr.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014-2016 Free Software Foundation, Inc. + * Copyright (C) 2016 Red Hat, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Daiki Ueno, Martin Ukrop + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +/*- + * ip_in_cidr: + * @ip: IP datum (IPv4 or IPv6) + * @cidr: CIDR datum (IPv4 or IPv6) + * + * Check if @ip lies in the given @cidr range. + * The @ip version must match the @cidr version (v4/v6), + * (this is not checked). + * + * Returns: 1 if @ip lies withing @cidr, 0 otherwise + -*/ +static unsigned ip_in_cidr(const gnutls_datum_t *ip, const gnutls_datum_t *cidr) +{ + char str_ip[48]; + char str_cidr[97]; + _gnutls_hard_log("matching %.*s with CIDR constraint %.*s\n", + (int) sizeof(str_ip), + _gnutls_ip_to_string(ip->data, ip->size, str_ip, sizeof(str_ip)), + (int) sizeof(str_cidr), + _gnutls_cidr_to_string(cidr->data, cidr->size, str_cidr, sizeof(str_cidr))); + + unsigned ipsize = ip->size; + for (unsigned byte = 0; byte < ipsize; byte++) + if (((ip->data[byte] ^ cidr->data[byte]) & cidr->data[ipsize+byte]) != 0) + return 0; + + return 1; /* match */ +} diff --git a/lib/x509/ip.c b/lib/x509/ip.c new file mode 100644 index 0000000000..7f2a6862c0 --- /dev/null +++ b/lib/x509/ip.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2007-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Martin Ukrop + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "ip.h" +#include <gnutls/x509.h> + +#ifdef HAVE_INET_NTOP +# include <arpa/inet.h> +#endif + +/*- + * _gnutls_mask_to_prefix: + * @mask: CIDR mask + * @mask_size: number of bytes in @mask + * + * Check for mask validity (form of 1*0*) and return its prefix numerically. + * + * Returns: Length of 1-prefix (0 -- mask_size*8), -1 in case of invalid mask + -*/ +int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size) +{ + unsigned i, prefix_length = 0; + for (i=0; i<mask_size; i++) { + if (mask[i] == 0xFF) { + prefix_length += 8; + } else { + switch(mask[i]) { + case 0xFE: prefix_length += 7; break; + case 0xFC: prefix_length += 6; break; + case 0xF8: prefix_length += 5; break; + case 0xF0: prefix_length += 4; break; + case 0xE0: prefix_length += 3; break; + case 0xC0: prefix_length += 2; break; + case 0x80: prefix_length += 1; break; + case 0x00: break; + default: + return -1; + } + break; + } + } + i++; + // mask is invalid, if there follows something else than 0x00 + for ( ; i<mask_size; i++) { + if (mask[i] != 0) + return -1; + } + return prefix_length; +} + +/*- + * _gnutls_ip_to_string: + * @_ip: IP address (RFC5280 format) + * @ip_size: Size of @_ip (4 or 16) + * @out: Resulting string + * @out_size: Size of @out + * + * Transform IP address into human-readable string. + * @string must be already allocated and + * at least 16/48 bytes for IPv4/v6 address respectively. + * + * Returns: Address of result string. + -*/ +const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size) +{ + + if (ip_size != 4 && ip_size != 16) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 4 && out_size < 16) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 16 && out_size < 48) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 4) + return inet_ntop(AF_INET, _ip, out, out_size); + else + return inet_ntop(AF_INET6, _ip, out, out_size); +} + +/*- + * _gnutls_cidr_to_string: + * @_ip: CIDR range (RFC5280 format) + * @ip_size: Size of @_ip (8 or 32) + * @out: Resulting string + * @out_size: Size of @out + * + * Transform CIDR IP address range into human-readable string. + * The input @_ip must be in RFC5280 format (IP address in network byte + * order, followed by its network mask which is 4 bytes in IPv4 and + * 16 bytes in IPv6). @string must be already allocated and + * at least 33/97 bytes for IPv4/v6 address respectively. + * + * Returns: Address of result string. + -*/ +const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size) +{ + const unsigned char *ip = _ip; + char tmp[64]; + const char *p; + + if (ip_size != 8 && ip_size != 32) { + gnutls_assert(); + return NULL; + } + + if (ip_size == 8) { + p = inet_ntop(AF_INET, ip, tmp, sizeof(tmp)); + + if (p) + snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+4, 4)); + } else { + p = inet_ntop(AF_INET6, ip, tmp, sizeof(tmp)); + + if (p) + snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+16, 16)); + } + + if (p == NULL) + return NULL; + + return out; +} + +static void prefix_to_mask(unsigned prefix, unsigned char *mask, size_t mask_size) +{ + int i; + unsigned j; + memset(mask, 0, mask_size); + + for (i = prefix, j = 0; i > 0 && j < mask_size; i -= 8, j++) { + if (i >= 8) { + mask[j] = 0xff; + } else { + mask[j] = (unsigned long)(0xffU << (8 - i)); + } + } +} + +/*- + * _gnutls_mask_ip: + * @ip: IP of @ipsize bytes + * @mask: netmask of @ipsize bytes + * @ipsize: IP length (4 or 16) + * + * Mask given IP in place according to the given mask. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + -*/ +int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize) { + if (ipsize != 4 && ipsize != 16) + return GNUTLS_E_MALFORMED_CIDR; + for (unsigned i = 0;i < ipsize; i++) + ip[i] &= mask[i]; + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_x509_cidr_to_rfc5280: + * @param cidr CIDR in RFC4632 format (IP/prefix), null-terminated + * @param cidr_rfc5280 CIDR range converted to RFC5280 format + * + * This function will convert text CIDR range with prefix (such as '10.0.0.0/8') + * to RFC5280 (IP address in network byte order followed by its network mask). + * Works for both IPv4 and IPv6. + * + * The resulting object is directly usable for IP name constraints usage, + * for example in functions %gnutls_x509_name_constraints_add_permitted + * or %gnutls_x509_name_constraints_add_excluded. + * + * The data in datum needs to be deallocated using gnutls_free(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. + * + * Since: 3.5.4 + */ +int gnutls_x509_cidr_to_rfc5280(const char *cidr, gnutls_datum_t *cidr_rfc5280) +{ + unsigned iplength, prefix; + int ret; + char *p; + char *p_end = NULL; + char *cidr_tmp; + + p = strchr(cidr, '/'); + if (p != NULL) { + prefix = strtol(p+1, &p_end, 10); + if (prefix == 0 && p_end == p+1) { + _gnutls_debug_log("Cannot parse prefix given in CIDR %s\n", cidr); + gnutls_assert(); + return GNUTLS_E_MALFORMED_CIDR; + } + unsigned length = p-cidr+1; + cidr_tmp = gnutls_malloc(length); + if (cidr_tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);; + } + memcpy(cidr_tmp, cidr, length); + cidr_tmp[length-1] = 0; + } else { + _gnutls_debug_log("No prefix given in CIDR %s\n", cidr); + gnutls_assert(); + return GNUTLS_E_MALFORMED_CIDR; + } + + if (strchr(cidr, ':') != 0) { /* IPv6 */ + iplength = 16; + } else { /* IPv4 */ + iplength = 4; + } + cidr_rfc5280->size = 2*iplength; + + if (prefix > iplength*8) { + _gnutls_debug_log("Invalid prefix given in CIDR %s (%d)\n", cidr, prefix); + ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR); + goto cleanup; + } + + cidr_rfc5280->data = gnutls_malloc(cidr_rfc5280->size); + if (cidr_rfc5280->data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto cleanup; + } + + ret = inet_pton(iplength == 4 ? AF_INET : AF_INET6, cidr_tmp, cidr_rfc5280->data); + if (ret == 0) { + _gnutls_debug_log("Cannot parse IP from CIDR %s\n", cidr_tmp); + ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR); + goto cleanup; + } + //memcpy(cidr_rfc5280, out, iplength); + + prefix_to_mask(prefix, &cidr_rfc5280->data[iplength], iplength); + _gnutls_mask_ip(cidr_rfc5280->data, &cidr_rfc5280->data[iplength], iplength); + + ret = GNUTLS_E_SUCCESS; + +cleanup: + gnutls_free(cidr_tmp); + return ret; +} diff --git a/lib/x509/ip.h b/lib/x509/ip.h new file mode 100644 index 0000000000..8cd686107d --- /dev/null +++ b/lib/x509/ip.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007-2016 Free Software Foundation, Inc. + * Copyright (C) 2015-2016 Red Hat, Inc. + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Martin Ukrop + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#ifndef IP_H +#define IP_H + +// for documentation, see the definition +int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size); + +// for documentation, see the definition +const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size); + +// for documentation, see the definition +const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size); + +// for documentation, see the definition +int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize); + +#endif // IP_H diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c index 1b448a4132..1303294ea9 100644 --- a/lib/x509/name_constraints.c +++ b/lib/x509/name_constraints.c @@ -35,8 +35,8 @@ #include <x509_int.h> #include <libtasn1.h> -/* Name constraints is limited to DNS names. - */ +#include "ip.h" +#include "ip-in-cidr.h" // for documentation see the implementation static int name_constraints_intersect_nodes(name_constraints_node_st * nc1, diff --git a/lib/x509/output.c b/lib/x509/output.c index 09b119b1ff..917cad0e5b 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -34,10 +34,7 @@ #include <c-ctype.h> #include <gnutls-idna.h> #include "extensions.h" - -#ifdef HAVE_INET_NTOP -# include <arpa/inet.h> -#endif +#include "ip.h" #define addf _gnutls_buffer_append_printf #define adds _gnutls_buffer_append_str @@ -45,89 +42,6 @@ #define NON_NULL(x) (((x)!=NULL)?((char*)(x)):"") #define ERROR_STR (char*) "(error)" -static const -char *ip_to_string(void *_ip, int ip_size, char *string, - int string_size) -{ - - if (ip_size != 4 && ip_size != 16) { - gnutls_assert(); - return NULL; - } - - if (ip_size == 4 && string_size < 16) { - gnutls_assert(); - return NULL; - } - - if (ip_size == 16 && string_size < 48) { - gnutls_assert(); - return NULL; - } - - if (ip_size == 4) - return inet_ntop(AF_INET, _ip, string, string_size); - else - return inet_ntop(AF_INET6, _ip, string, string_size); -} - -static unsigned mask_to_prefix(const uint8_t *mask, unsigned mask_size) -{ - unsigned i, c = 0; - for (i=0; i<mask_size; i++) { - if (mask[i] == 0xFF) { - c += 8; - } else { - switch(mask[i]) { - case 0xFE: c += 7; break; - case 0xFC: c += 6; break; - case 0xF8: c += 5; break; - case 0xF0: c += 4; break; - case 0xE0: c += 3; break; - case 0xC0: c += 2; break; - case 0x80: c += 1; break; - case 0x00: break; - default: - return 0; - } - break; - } - } - - return c; -} - -static const -char *cidr_to_string(void *_ip, int ip_size, char *string, - int string_size) -{ - uint8_t *ip = _ip; - char tmp[64]; - const char *p; - - if (ip_size != 8 && ip_size != 32) { - gnutls_assert(); - return NULL; - } - - if (ip_size == 8) { - p = inet_ntop(AF_INET, ip, tmp, sizeof(tmp)); - - if (p) - snprintf(string, string_size, "%s/%u", tmp, mask_to_prefix(ip+4, 4)); - } else { - p = inet_ntop(AF_INET6, ip, tmp, sizeof(tmp)); - - if (p) - snprintf(string, string_size, "%s/%u", tmp, mask_to_prefix(ip+16, 16)); - } - - if (p == NULL) - return NULL; - - return string; -} - static void print_name(gnutls_buffer_st *str, const char *prefix, unsigned type, gnutls_datum_t *name, unsigned ip_is_cidr) { @@ -188,9 +102,9 @@ unsigned i; case GNUTLS_SAN_IPADDRESS: if (!ip_is_cidr) - p = ip_to_string(name->data, name->size, str_ip, sizeof(str_ip)); + p = _gnutls_ip_to_string(name->data, name->size, str_ip, sizeof(str_ip)); else - p = cidr_to_string(name->data, name->size, str_ip, sizeof(str_ip)); + p = _gnutls_cidr_to_string(name->data, name->size, str_ip, sizeof(str_ip)); if (p == NULL) p = ERROR_STR; addf(str, "%sIPAddress: %s\n", prefix, p); |