summaryrefslogtreecommitdiff
path: root/lib/x509
diff options
context:
space:
mode:
authorMartin Ukrop <mukrop@redhat.com>2016-06-29 11:23:40 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2016-08-09 10:48:27 +0200
commit90637db1425813c81169b1f12a613a7b2df803f1 (patch)
tree1826a82d8e2a29c4e1d8322e0bddb9f1bfd901f6 /lib/x509
parent25154fcff0f8ce5c0094e365920a2d7ce3bccdc9 (diff)
downloadgnutls-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.am3
-rw-r--r--lib/x509/ip-in-cidr.h51
-rw-r--r--lib/x509/ip.c269
-rw-r--r--lib/x509/ip.h39
-rw-r--r--lib/x509/name_constraints.c4
-rw-r--r--lib/x509/output.c92
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);