1 /*
2  * Copyright (C) 2007-2016 Free Software Foundation, Inc.
3  * Copyright (C) 2015-2016 Red Hat, Inc.
4  *
5  * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Martin Ukrop
6  *
7  * This file is part of GnuTLS.
8  *
9  * The GnuTLS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>
21  *
22  */
23 
24 #include "gnutls_int.h"
25 #include "ip.h"
26 #include <gnutls/x509.h>
27 #include <arpa/inet.h>
28 
29 /*-
30  * _gnutls_mask_to_prefix:
31  * @mask: CIDR mask
32  * @mask_size: number of bytes in @mask
33  *
34  * Check for mask validity (form of 1*0*) and return its prefix numerically.
35  *
36  * Returns: Length of 1-prefix (0 -- mask_size*8), -1 in case of invalid mask
37  -*/
_gnutls_mask_to_prefix(const unsigned char * mask,unsigned mask_size)38 int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size)
39 {
40 	unsigned i, prefix_length = 0;
41 	for (i=0; i<mask_size; i++) {
42 		if (mask[i] == 0xFF) {
43 			prefix_length += 8;
44 		} else {
45 			switch(mask[i]) {
46 				case 0xFE: prefix_length += 7; break;
47 				case 0xFC: prefix_length += 6; break;
48 				case 0xF8: prefix_length += 5; break;
49 				case 0xF0: prefix_length += 4; break;
50 				case 0xE0: prefix_length += 3; break;
51 				case 0xC0: prefix_length += 2; break;
52 				case 0x80: prefix_length += 1; break;
53 				case 0x00: break;
54 				default:
55 					return -1;
56 			}
57 			break;
58 		}
59 	}
60 	i++;
61 	// mask is invalid, if there follows something else than 0x00
62 	for ( ; i<mask_size; i++) {
63 		if (mask[i] != 0)
64 			return -1;
65 	}
66 	return prefix_length;
67 }
68 
69 /*-
70  * _gnutls_ip_to_string:
71  * @_ip: IP address (RFC5280 format)
72  * @ip_size: Size of @_ip (4 or 16)
73  * @out: Resulting string
74  * @out_size: Size of @out
75  *
76  * Transform IP address into human-readable string.
77  * @string must be already allocated and
78  * at least 16/48 bytes for IPv4/v6 address respectively.
79  *
80  * Returns: Address of result string.
81  -*/
_gnutls_ip_to_string(const void * _ip,unsigned int ip_size,char * out,unsigned int out_size)82 const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size)
83 {
84 
85 	if (ip_size != 4 && ip_size != 16) {
86 		gnutls_assert();
87 		return NULL;
88 	}
89 
90 	if (ip_size == 4 && out_size < 16) {
91 		gnutls_assert();
92 		return NULL;
93 	}
94 
95 	if (ip_size == 16 && out_size < 48) {
96 		gnutls_assert();
97 		return NULL;
98 	}
99 
100 	if (ip_size == 4)
101 		return inet_ntop(AF_INET, _ip, out, out_size);
102 	else
103 		return inet_ntop(AF_INET6, _ip, out, out_size);
104 }
105 
106 /*-
107  * _gnutls_cidr_to_string:
108  * @_ip: CIDR range (RFC5280 format)
109  * @ip_size: Size of @_ip (8 or 32)
110  * @out: Resulting string
111  * @out_size: Size of @out
112  *
113  * Transform CIDR IP address range into human-readable string.
114  * The input @_ip must be in RFC5280 format (IP address in network byte
115  * order, followed by its network mask which is 4 bytes in IPv4 and
116  * 16 bytes in IPv6). @string must be already allocated and
117  * at least 33/97 bytes for IPv4/v6 address respectively.
118  *
119  * Returns: Address of result string.
120  -*/
_gnutls_cidr_to_string(const void * _ip,unsigned int ip_size,char * out,unsigned int out_size)121 const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size)
122 {
123 	const unsigned char *ip = _ip;
124 	char tmp[64];
125 	const char *p;
126 
127 	if (ip_size != 8 && ip_size != 32) {
128 		gnutls_assert();
129 		return NULL;
130 	}
131 
132 	if (ip_size == 8) {
133 		p = inet_ntop(AF_INET, ip, tmp, sizeof(tmp));
134 
135 		if (p)
136 			snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+4, 4));
137 	} else {
138 		p = inet_ntop(AF_INET6, ip, tmp, sizeof(tmp));
139 
140 		if (p)
141 			snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+16, 16));
142 	}
143 
144 	if (p == NULL)
145 		return NULL;
146 
147 	return out;
148 }
149 
prefix_to_mask(unsigned prefix,unsigned char * mask,size_t mask_size)150 static void prefix_to_mask(unsigned prefix, unsigned char *mask, size_t mask_size)
151 {
152 	int i;
153 	unsigned j;
154 	memset(mask, 0, mask_size);
155 
156 	for (i = prefix, j = 0; i > 0 && j < mask_size; i -= 8, j++) {
157 		if (i >= 8) {
158 			mask[j] = 0xff;
159 		} else {
160 			mask[j] = (unsigned long)(0xffU << (8 - i));
161 		}
162 	}
163 }
164 
165 /*-
166  * _gnutls_mask_ip:
167  * @ip: IP of @ipsize bytes
168  * @mask: netmask of @ipsize bytes
169  * @ipsize: IP length (4 or 16)
170  *
171  * Mask given IP in place according to the given mask.
172  *
173  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
174  -*/
_gnutls_mask_ip(unsigned char * ip,const unsigned char * mask,unsigned ipsize)175 int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize)
176 {
177 	unsigned i;
178 
179 	if (ipsize != 4 && ipsize != 16)
180 		return GNUTLS_E_MALFORMED_CIDR;
181 	for (i = 0; i < ipsize; i++)
182 		ip[i] &= mask[i];
183 	return GNUTLS_E_SUCCESS;
184 }
185 
186 /**
187  * gnutls_x509_cidr_to_rfc5280:
188  * @cidr: CIDR in RFC4632 format (IP/prefix), null-terminated
189  * @cidr_rfc5280: CIDR range converted to RFC5280 format
190  *
191  * This function will convert text CIDR range with prefix (such as '10.0.0.0/8')
192  * to RFC5280 (IP address in network byte order followed by its network mask).
193  * Works for both IPv4 and IPv6.
194  *
195  * The resulting object is directly usable for IP name constraints usage,
196  * for example in functions %gnutls_x509_name_constraints_add_permitted
197  * or %gnutls_x509_name_constraints_add_excluded.
198  *
199  * The data in datum needs to be deallocated using gnutls_free().
200  *
201  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
202  *
203  * Since: 3.5.4
204  */
gnutls_x509_cidr_to_rfc5280(const char * cidr,gnutls_datum_t * cidr_rfc5280)205 int gnutls_x509_cidr_to_rfc5280(const char *cidr, gnutls_datum_t *cidr_rfc5280)
206 {
207 	unsigned iplength, prefix;
208 	int ret;
209 	char *p;
210 	char *p_end = NULL;
211 	char *cidr_tmp;
212 
213 	p = strchr(cidr, '/');
214 	if (p != NULL) {
215 		prefix = strtol(p+1, &p_end, 10);
216 		if (prefix == 0 && p_end == p+1) {
217 			_gnutls_debug_log("Cannot parse prefix given in CIDR %s\n", cidr);
218 			gnutls_assert();
219 			return GNUTLS_E_MALFORMED_CIDR;
220 		}
221 		unsigned length = p-cidr+1;
222 		cidr_tmp = gnutls_malloc(length);
223 		if (cidr_tmp == NULL) {
224 			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
225 		}
226 		memcpy(cidr_tmp, cidr, length);
227 		cidr_tmp[length-1] = 0;
228 	} else {
229 		_gnutls_debug_log("No prefix given in CIDR %s\n", cidr);
230 		gnutls_assert();
231 		return GNUTLS_E_MALFORMED_CIDR;
232 	}
233 
234 	if (strchr(cidr, ':') != 0) { /* IPv6 */
235 		iplength = 16;
236 	} else { /* IPv4 */
237 		iplength = 4;
238 	}
239 	cidr_rfc5280->size = 2*iplength;
240 
241 	if (prefix > iplength*8) {
242 		_gnutls_debug_log("Invalid prefix given in CIDR %s (%d)\n", cidr, prefix);
243 		ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
244 		goto cleanup;
245 	}
246 
247 	cidr_rfc5280->data = gnutls_malloc(cidr_rfc5280->size);
248 	if (cidr_rfc5280->data == NULL) {
249 		ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
250 		goto cleanup;
251 	}
252 
253 	ret = inet_pton(iplength == 4 ? AF_INET : AF_INET6, cidr_tmp, cidr_rfc5280->data);
254 	if (ret == 0) {
255 		_gnutls_debug_log("Cannot parse IP from CIDR %s\n", cidr_tmp);
256 		ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
257 		goto cleanup;
258 	}
259 
260 	prefix_to_mask(prefix, &cidr_rfc5280->data[iplength], iplength);
261 	_gnutls_mask_ip(cidr_rfc5280->data, &cidr_rfc5280->data[iplength], iplength);
262 
263 	ret = GNUTLS_E_SUCCESS;
264 
265 cleanup:
266 	gnutls_free(cidr_tmp);
267 	return ret;
268 }
269