/*
* 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
*
*/
#include "gnutls_int.h"
#include "ip.h"
#include
#include
/*-
* _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 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)
{
unsigned i;
if (ipsize != 4 && ipsize != 16)
return GNUTLS_E_MALFORMED_CIDR;
for (i = 0; i < ipsize; i++)
ip[i] &= mask[i];
return GNUTLS_E_SUCCESS;
}
/**
* gnutls_x509_cidr_to_rfc5280:
* @cidr: CIDR in RFC4632 format (IP/prefix), null-terminated
* @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;
}
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;
}