Coverage Report

Created: 2023-03-26 07:33

/src/gnutls/lib/x509/ip.c
Line
Count
Source (jump to first uncovered line)
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
int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size)
39
0
{
40
0
  unsigned i, prefix_length = 0;
41
0
  for (i = 0; i < mask_size; i++) {
42
0
    if (mask[i] == 0xFF) {
43
0
      prefix_length += 8;
44
0
    } else {
45
0
      switch (mask[i]) {
46
0
      case 0xFE:
47
0
        prefix_length += 7;
48
0
        break;
49
0
      case 0xFC:
50
0
        prefix_length += 6;
51
0
        break;
52
0
      case 0xF8:
53
0
        prefix_length += 5;
54
0
        break;
55
0
      case 0xF0:
56
0
        prefix_length += 4;
57
0
        break;
58
0
      case 0xE0:
59
0
        prefix_length += 3;
60
0
        break;
61
0
      case 0xC0:
62
0
        prefix_length += 2;
63
0
        break;
64
0
      case 0x80:
65
0
        prefix_length += 1;
66
0
        break;
67
0
      case 0x00:
68
0
        break;
69
0
      default:
70
0
        return -1;
71
0
      }
72
0
      break;
73
0
    }
74
0
  }
75
0
  i++;
76
  // mask is invalid, if there follows something else than 0x00
77
0
  for (; i < mask_size; i++) {
78
0
    if (mask[i] != 0)
79
0
      return -1;
80
0
  }
81
0
  return prefix_length;
82
0
}
83
84
/*-
85
 * _gnutls_ip_to_string:
86
 * @_ip: IP address (RFC5280 format)
87
 * @ip_size: Size of @_ip (4 or 16)
88
 * @out: Resulting string
89
 * @out_size: Size of @out
90
 *
91
 * Transform IP address into human-readable string.
92
 * @string must be already allocated and
93
 * at least 16/48 bytes for IPv4/v6 address respectively.
94
 *
95
 * Returns: Address of result string.
96
 -*/
97
const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size,
98
         char *out, unsigned int out_size)
99
0
{
100
101
0
  if (ip_size != 4 && ip_size != 16) {
102
0
    gnutls_assert();
103
0
    return NULL;
104
0
  }
105
106
0
  if (ip_size == 4 && out_size < 16) {
107
0
    gnutls_assert();
108
0
    return NULL;
109
0
  }
110
111
0
  if (ip_size == 16 && out_size < 48) {
112
0
    gnutls_assert();
113
0
    return NULL;
114
0
  }
115
116
0
  if (ip_size == 4)
117
0
    return inet_ntop(AF_INET, _ip, out, out_size);
118
0
  else
119
0
    return inet_ntop(AF_INET6, _ip, out, out_size);
120
0
}
121
122
/*-
123
 * _gnutls_cidr_to_string:
124
 * @_ip: CIDR range (RFC5280 format)
125
 * @ip_size: Size of @_ip (8 or 32)
126
 * @out: Resulting string
127
 * @out_size: Size of @out
128
 *
129
 * Transform CIDR IP address range into human-readable string.
130
 * The input @_ip must be in RFC5280 format (IP address in network byte
131
 * order, followed by its network mask which is 4 bytes in IPv4 and
132
 * 16 bytes in IPv6). @string must be already allocated and
133
 * at least 33/97 bytes for IPv4/v6 address respectively.
134
 *
135
 * Returns: Address of result string.
136
 -*/
137
const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size,
138
           char *out, unsigned int out_size)
139
0
{
140
0
  const unsigned char *ip = _ip;
141
0
  char tmp[64];
142
0
  const char *p;
143
144
0
  if (ip_size != 8 && ip_size != 32) {
145
0
    gnutls_assert();
146
0
    return NULL;
147
0
  }
148
149
0
  if (ip_size == 8) {
150
0
    p = inet_ntop(AF_INET, ip, tmp, sizeof(tmp));
151
152
0
    if (p)
153
0
      snprintf(out, out_size, "%s/%d", tmp,
154
0
         _gnutls_mask_to_prefix(ip + 4, 4));
155
0
  } else {
156
0
    p = inet_ntop(AF_INET6, ip, tmp, sizeof(tmp));
157
158
0
    if (p)
159
0
      snprintf(out, out_size, "%s/%d", tmp,
160
0
         _gnutls_mask_to_prefix(ip + 16, 16));
161
0
  }
162
163
0
  if (p == NULL)
164
0
    return NULL;
165
166
0
  return out;
167
0
}
168
169
static void prefix_to_mask(unsigned prefix, unsigned char *mask,
170
         size_t mask_size)
171
0
{
172
0
  int i;
173
0
  unsigned j;
174
0
  memset(mask, 0, mask_size);
175
176
0
  for (i = prefix, j = 0; i > 0 && j < mask_size; i -= 8, j++) {
177
0
    if (i >= 8) {
178
0
      mask[j] = 0xff;
179
0
    } else {
180
0
      mask[j] = (unsigned long)(0xffU << (8 - i));
181
0
    }
182
0
  }
183
0
}
184
185
/*-
186
 * _gnutls_mask_ip:
187
 * @ip: IP of @ipsize bytes
188
 * @mask: netmask of @ipsize bytes
189
 * @ipsize: IP length (4 or 16)
190
 *
191
 * Mask given IP in place according to the given mask.
192
 *
193
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
194
 -*/
195
int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask,
196
        unsigned ipsize)
197
0
{
198
0
  unsigned i;
199
200
0
  if (ipsize != 4 && ipsize != 16)
201
0
    return GNUTLS_E_MALFORMED_CIDR;
202
0
  for (i = 0; i < ipsize; i++)
203
0
    ip[i] &= mask[i];
204
0
  return GNUTLS_E_SUCCESS;
205
0
}
206
207
/**
208
 * gnutls_x509_cidr_to_rfc5280:
209
 * @cidr: CIDR in RFC4632 format (IP/prefix), null-terminated
210
 * @cidr_rfc5280: CIDR range converted to RFC5280 format
211
 *
212
 * This function will convert text CIDR range with prefix (such as '10.0.0.0/8')
213
 * to RFC5280 (IP address in network byte order followed by its network mask).
214
 * Works for both IPv4 and IPv6.
215
 *
216
 * The resulting object is directly usable for IP name constraints usage,
217
 * for example in functions %gnutls_x509_name_constraints_add_permitted
218
 * or %gnutls_x509_name_constraints_add_excluded.
219
 *
220
 * The data in datum needs to be deallocated using gnutls_free().
221
 *
222
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
223
 *
224
 * Since: 3.5.4
225
 */
226
int gnutls_x509_cidr_to_rfc5280(const char *cidr, gnutls_datum_t * cidr_rfc5280)
227
0
{
228
0
  unsigned iplength, prefix;
229
0
  int ret;
230
0
  char *p;
231
0
  char *p_end = NULL;
232
0
  char *cidr_tmp;
233
234
0
  p = strchr(cidr, '/');
235
0
  if (p != NULL) {
236
0
    prefix = strtol(p + 1, &p_end, 10);
237
0
    if (prefix == 0 && p_end == p + 1) {
238
0
      _gnutls_debug_log
239
0
          ("Cannot parse prefix given in CIDR %s\n", cidr);
240
0
      gnutls_assert();
241
0
      return GNUTLS_E_MALFORMED_CIDR;
242
0
    }
243
0
    unsigned length = p - cidr + 1;
244
0
    cidr_tmp = gnutls_malloc(length);
245
0
    if (cidr_tmp == NULL) {
246
0
      return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
247
0
    }
248
0
    memcpy(cidr_tmp, cidr, length);
249
0
    cidr_tmp[length - 1] = 0;
250
0
  } else {
251
0
    _gnutls_debug_log("No prefix given in CIDR %s\n", cidr);
252
0
    gnutls_assert();
253
0
    return GNUTLS_E_MALFORMED_CIDR;
254
0
  }
255
256
0
  if (strchr(cidr, ':') != 0) { /* IPv6 */
257
0
    iplength = 16;
258
0
  } else {   /* IPv4 */
259
0
    iplength = 4;
260
0
  }
261
0
  cidr_rfc5280->size = 2 * iplength;
262
263
0
  if (prefix > iplength * 8) {
264
0
    _gnutls_debug_log("Invalid prefix given in CIDR %s (%d)\n",
265
0
          cidr, prefix);
266
0
    ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
267
0
    goto cleanup;
268
0
  }
269
270
0
  cidr_rfc5280->data = gnutls_malloc(cidr_rfc5280->size);
271
0
  if (cidr_rfc5280->data == NULL) {
272
0
    ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
273
0
    goto cleanup;
274
0
  }
275
276
0
  ret =
277
0
      inet_pton(iplength == 4 ? AF_INET : AF_INET6, cidr_tmp,
278
0
          cidr_rfc5280->data);
279
0
  if (ret == 0) {
280
0
    _gnutls_debug_log("Cannot parse IP from CIDR %s\n", cidr_tmp);
281
0
    ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
282
0
    goto cleanup;
283
0
  }
284
285
0
  prefix_to_mask(prefix, &cidr_rfc5280->data[iplength], iplength);
286
0
  _gnutls_mask_ip(cidr_rfc5280->data, &cidr_rfc5280->data[iplength],
287
0
      iplength);
288
289
0
  ret = GNUTLS_E_SUCCESS;
290
291
0
 cleanup:
292
0
  gnutls_free(cidr_tmp);
293
0
  return ret;
294
0
}