Line data Source code
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 : -*/
38 124 : int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size)
39 : {
40 124 : unsigned i, prefix_length = 0;
41 502 : for (i=0; i<mask_size; i++) {
42 498 : if (mask[i] == 0xFF) {
43 378 : prefix_length += 8;
44 : } else {
45 120 : switch(mask[i]) {
46 6 : case 0xFE: prefix_length += 7; break;
47 1 : case 0xFC: prefix_length += 6; break;
48 10 : case 0xF8: prefix_length += 5; break;
49 4 : case 0xF0: prefix_length += 4; break;
50 11 : case 0xE0: prefix_length += 3; break;
51 18 : case 0xC0: prefix_length += 2; break;
52 1 : case 0x80: prefix_length += 1; break;
53 : case 0x00: break;
54 : default:
55 : return -1;
56 : }
57 : break;
58 : }
59 : }
60 121 : i++;
61 : // mask is invalid, if there follows something else than 0x00
62 600 : for ( ; i<mask_size; i++) {
63 487 : if (mask[i] != 0)
64 : return -1;
65 : }
66 113 : 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 : -*/
82 559 : const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size)
83 : {
84 :
85 559 : if (ip_size != 4 && ip_size != 16) {
86 15 : gnutls_assert();
87 15 : return NULL;
88 : }
89 :
90 544 : if (ip_size == 4 && out_size < 16) {
91 0 : gnutls_assert();
92 0 : return NULL;
93 : }
94 :
95 544 : if (ip_size == 16 && out_size < 48) {
96 0 : gnutls_assert();
97 0 : return NULL;
98 : }
99 :
100 544 : if (ip_size == 4)
101 526 : return inet_ntop(AF_INET, _ip, out, out_size);
102 : else
103 18 : 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 : -*/
121 31 : const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size)
122 : {
123 31 : const unsigned char *ip = _ip;
124 31 : char tmp[64];
125 31 : const char *p;
126 :
127 31 : if (ip_size != 8 && ip_size != 32) {
128 0 : gnutls_assert();
129 0 : return NULL;
130 : }
131 :
132 31 : if (ip_size == 8) {
133 20 : p = inet_ntop(AF_INET, ip, tmp, sizeof(tmp));
134 :
135 20 : if (p)
136 20 : snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+4, 4));
137 : } else {
138 11 : p = inet_ntop(AF_INET6, ip, tmp, sizeof(tmp));
139 :
140 11 : if (p)
141 11 : snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+16, 16));
142 : }
143 :
144 31 : if (p == NULL)
145 0 : return NULL;
146 :
147 : return out;
148 : }
149 :
150 104 : static void prefix_to_mask(unsigned prefix, unsigned char *mask, size_t mask_size)
151 : {
152 104 : int i;
153 104 : unsigned j;
154 104 : memset(mask, 0, mask_size);
155 :
156 714 : for (i = prefix, j = 0; i > 0 && j < mask_size; i -= 8, j++) {
157 610 : if (i >= 8) {
158 591 : mask[j] = 0xff;
159 : } else {
160 19 : mask[j] = (unsigned long)(0xffU << (8 - i));
161 : }
162 : }
163 104 : }
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 : -*/
175 116 : int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize)
176 : {
177 116 : unsigned i;
178 :
179 116 : if (ipsize != 4 && ipsize != 16)
180 : return GNUTLS_E_MALFORMED_CIDR;
181 1036 : for (i = 0; i < ipsize; i++)
182 920 : 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 : */
205 115 : int gnutls_x509_cidr_to_rfc5280(const char *cidr, gnutls_datum_t *cidr_rfc5280)
206 : {
207 115 : unsigned iplength, prefix;
208 115 : int ret;
209 115 : char *p;
210 115 : char *p_end = NULL;
211 115 : char *cidr_tmp;
212 :
213 115 : p = strchr(cidr, '/');
214 115 : if (p != NULL) {
215 114 : prefix = strtol(p+1, &p_end, 10);
216 114 : if (prefix == 0 && p_end == p+1) {
217 3 : _gnutls_debug_log("Cannot parse prefix given in CIDR %s\n", cidr);
218 3 : gnutls_assert();
219 3 : return GNUTLS_E_MALFORMED_CIDR;
220 : }
221 111 : unsigned length = p-cidr+1;
222 111 : cidr_tmp = gnutls_malloc(length);
223 111 : if (cidr_tmp == NULL) {
224 0 : return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
225 : }
226 111 : memcpy(cidr_tmp, cidr, length);
227 111 : cidr_tmp[length-1] = 0;
228 : } else {
229 1 : _gnutls_debug_log("No prefix given in CIDR %s\n", cidr);
230 1 : gnutls_assert();
231 1 : return GNUTLS_E_MALFORMED_CIDR;
232 : }
233 :
234 111 : if (strchr(cidr, ':') != 0) { /* IPv6 */
235 : iplength = 16;
236 : } else { /* IPv4 */
237 74 : iplength = 4;
238 : }
239 111 : cidr_rfc5280->size = 2*iplength;
240 :
241 111 : if (prefix > iplength*8) {
242 7 : _gnutls_debug_log("Invalid prefix given in CIDR %s (%d)\n", cidr, prefix);
243 7 : ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
244 7 : goto cleanup;
245 : }
246 :
247 104 : cidr_rfc5280->data = gnutls_malloc(cidr_rfc5280->size);
248 104 : if (cidr_rfc5280->data == NULL) {
249 0 : ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
250 0 : goto cleanup;
251 : }
252 :
253 140 : ret = inet_pton(iplength == 4 ? AF_INET : AF_INET6, cidr_tmp, cidr_rfc5280->data);
254 104 : if (ret == 0) {
255 0 : _gnutls_debug_log("Cannot parse IP from CIDR %s\n", cidr_tmp);
256 0 : ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
257 0 : goto cleanup;
258 : }
259 :
260 104 : prefix_to_mask(prefix, &cidr_rfc5280->data[iplength], iplength);
261 104 : _gnutls_mask_ip(cidr_rfc5280->data, &cidr_rfc5280->data[iplength], iplength);
262 :
263 104 : ret = GNUTLS_E_SUCCESS;
264 :
265 111 : cleanup:
266 111 : gnutls_free(cidr_tmp);
267 111 : return ret;
268 : }
|