]>
Commit | Line | Data |
---|---|---|
0f113f3e | 1 | /* |
33388b44 | 2 | * Copyright 1999-2020 The OpenSSL Project Authors. All Rights Reserved. |
9aeaf1b4 | 3 | * |
4286ca47 | 4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
2039c421 RS |
5 | * this file except in compliance with the License. You can obtain a copy |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
9aeaf1b4 | 8 | */ |
2039c421 | 9 | |
9aeaf1b4 DSH |
10 | /* X509 v3 extension utilities */ |
11 | ||
07016a8a P |
12 | #include "e_os.h" |
13 | #include "internal/cryptlib.h" | |
e527ba09 | 14 | #include <stdio.h> |
25f2138b | 15 | #include "crypto/ctype.h" |
ec577822 | 16 | #include <openssl/conf.h> |
10a3195f | 17 | #include <openssl/crypto.h> |
ec577822 | 18 | #include <openssl/x509v3.h> |
25f2138b | 19 | #include "crypto/x509.h" |
0f814687 | 20 | #include <openssl/bn.h> |
9021a5df | 21 | #include "ext_dat.h" |
c90c4693 | 22 | #include "x509_local.h" |
9aeaf1b4 | 23 | |
852c2ed2 RS |
24 | DEFINE_STACK_OF(CONF_VALUE) |
25 | DEFINE_STACK_OF(GENERAL_NAME) | |
26 | DEFINE_STACK_OF(ACCESS_DESCRIPTION) | |
27 | DEFINE_STACK_OF(X509_EXTENSION) | |
28 | DEFINE_STACK_OF_STRING() | |
29 | ||
9aeaf1b4 | 30 | static char *strip_spaces(char *name); |
0f113f3e | 31 | static int sk_strcmp(const char *const *a, const char *const *b); |
8cc86b81 | 32 | static STACK_OF(OPENSSL_STRING) *get_email(const X509_NAME *name, |
0f113f3e | 33 | GENERAL_NAMES *gens); |
c869da88 | 34 | static void str_free(OPENSSL_STRING str); |
278260bf DDO |
35 | static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, |
36 | const ASN1_IA5STRING *email); | |
9aeaf1b4 | 37 | |
4e5d3a7f DSH |
38 | static int ipv4_from_asc(unsigned char *v4, const char *in); |
39 | static int ipv6_from_asc(unsigned char *v6, const char *in); | |
40 | static int ipv6_cb(const char *elem, int len, void *usr); | |
41 | static int ipv6_hex(unsigned char *out, const char *in, int inlen); | |
42 | ||
9aeaf1b4 DSH |
43 | /* Add a CONF_VALUE name value pair to stack */ |
44 | ||
ba404b5e | 45 | int X509V3_add_value(const char *name, const char *value, |
0f113f3e | 46 | STACK_OF(CONF_VALUE) **extlist) |
9aeaf1b4 | 47 | { |
0f113f3e MC |
48 | CONF_VALUE *vtmp = NULL; |
49 | char *tname = NULL, *tvalue = NULL; | |
32f3b98d | 50 | int sk_allocated = (*extlist == NULL); |
75ebbd9a | 51 | |
7644a9ae | 52 | if (name && (tname = OPENSSL_strdup(name)) == NULL) |
0f113f3e | 53 | goto err; |
7644a9ae | 54 | if (value && (tvalue = OPENSSL_strdup(value)) == NULL) |
0f113f3e | 55 | goto err; |
75ebbd9a | 56 | if ((vtmp = OPENSSL_malloc(sizeof(*vtmp))) == NULL) |
0f113f3e | 57 | goto err; |
32f3b98d | 58 | if (sk_allocated && (*extlist = sk_CONF_VALUE_new_null()) == NULL) |
0f113f3e MC |
59 | goto err; |
60 | vtmp->section = NULL; | |
61 | vtmp->name = tname; | |
62 | vtmp->value = tvalue; | |
63 | if (!sk_CONF_VALUE_push(*extlist, vtmp)) | |
64 | goto err; | |
65 | return 1; | |
66 | err: | |
67 | X509V3err(X509V3_F_X509V3_ADD_VALUE, ERR_R_MALLOC_FAILURE); | |
432f8688 | 68 | if (sk_allocated) { |
32f3b98d | 69 | sk_CONF_VALUE_free(*extlist); |
432f8688 AP |
70 | *extlist = NULL; |
71 | } | |
b548a1f1 RS |
72 | OPENSSL_free(vtmp); |
73 | OPENSSL_free(tname); | |
74 | OPENSSL_free(tvalue); | |
0f113f3e | 75 | return 0; |
9aeaf1b4 DSH |
76 | } |
77 | ||
61f5b6f3 | 78 | int X509V3_add_value_uchar(const char *name, const unsigned char *value, |
0f113f3e MC |
79 | STACK_OF(CONF_VALUE) **extlist) |
80 | { | |
81 | return X509V3_add_value(name, (const char *)value, extlist); | |
82 | } | |
61f5b6f3 | 83 | |
66ab08b1 | 84 | /* Free function for STACK_OF(CONF_VALUE) */ |
9aeaf1b4 | 85 | |
6b691a5c | 86 | void X509V3_conf_free(CONF_VALUE *conf) |
9aeaf1b4 | 87 | { |
0f113f3e MC |
88 | if (!conf) |
89 | return; | |
b548a1f1 RS |
90 | OPENSSL_free(conf->name); |
91 | OPENSSL_free(conf->value); | |
92 | OPENSSL_free(conf->section); | |
0f113f3e | 93 | OPENSSL_free(conf); |
9aeaf1b4 DSH |
94 | } |
95 | ||
ba404b5e | 96 | int X509V3_add_value_bool(const char *name, int asn1_bool, |
0f113f3e | 97 | STACK_OF(CONF_VALUE) **extlist) |
9aeaf1b4 | 98 | { |
0f113f3e MC |
99 | if (asn1_bool) |
100 | return X509V3_add_value(name, "TRUE", extlist); | |
101 | return X509V3_add_value(name, "FALSE", extlist); | |
9aeaf1b4 DSH |
102 | } |
103 | ||
c8f717fe | 104 | int X509V3_add_value_bool_nf(const char *name, int asn1_bool, |
0f113f3e | 105 | STACK_OF(CONF_VALUE) **extlist) |
9aeaf1b4 | 106 | { |
0f113f3e MC |
107 | if (asn1_bool) |
108 | return X509V3_add_value(name, "TRUE", extlist); | |
109 | return 1; | |
9aeaf1b4 DSH |
110 | } |
111 | ||
10a3195f DB |
112 | static char *bignum_to_string(const BIGNUM *bn) |
113 | { | |
114 | char *tmp, *ret; | |
115 | size_t len; | |
116 | ||
117 | /* | |
118 | * Display large numbers in hex and small numbers in decimal. Converting to | |
119 | * decimal takes quadratic time and is no more useful than hex for large | |
120 | * numbers. | |
121 | */ | |
122 | if (BN_num_bits(bn) < 128) | |
123 | return BN_bn2dec(bn); | |
124 | ||
125 | tmp = BN_bn2hex(bn); | |
126 | if (tmp == NULL) | |
127 | return NULL; | |
128 | ||
129 | len = strlen(tmp) + 3; | |
130 | ret = OPENSSL_malloc(len); | |
131 | if (ret == NULL) { | |
132 | X509V3err(X509V3_F_BIGNUM_TO_STRING, ERR_R_MALLOC_FAILURE); | |
133 | OPENSSL_free(tmp); | |
134 | return NULL; | |
135 | } | |
136 | ||
137 | /* Prepend "0x", but place it after the "-" if negative. */ | |
138 | if (tmp[0] == '-') { | |
139 | OPENSSL_strlcpy(ret, "-0x", len); | |
140 | OPENSSL_strlcat(ret, tmp + 1, len); | |
141 | } else { | |
142 | OPENSSL_strlcpy(ret, "0x", len); | |
143 | OPENSSL_strlcat(ret, tmp, len); | |
144 | } | |
145 | OPENSSL_free(tmp); | |
146 | return ret; | |
147 | } | |
148 | ||
bf9d5e48 | 149 | char *i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *method, const ASN1_ENUMERATED *a) |
c74f1eb9 | 150 | { |
0f113f3e MC |
151 | BIGNUM *bntmp = NULL; |
152 | char *strtmp = NULL; | |
75ebbd9a | 153 | |
0f113f3e MC |
154 | if (!a) |
155 | return NULL; | |
75ebbd9a | 156 | if ((bntmp = ASN1_ENUMERATED_to_BN(a, NULL)) == NULL |
10a3195f | 157 | || (strtmp = bignum_to_string(bntmp)) == NULL) |
0f113f3e MC |
158 | X509V3err(X509V3_F_I2S_ASN1_ENUMERATED, ERR_R_MALLOC_FAILURE); |
159 | BN_free(bntmp); | |
160 | return strtmp; | |
c74f1eb9 DSH |
161 | } |
162 | ||
a6a283b3 | 163 | char *i2s_ASN1_INTEGER(X509V3_EXT_METHOD *method, const ASN1_INTEGER *a) |
0ca5f8b1 | 164 | { |
0f113f3e MC |
165 | BIGNUM *bntmp = NULL; |
166 | char *strtmp = NULL; | |
75ebbd9a | 167 | |
0f113f3e MC |
168 | if (!a) |
169 | return NULL; | |
75ebbd9a | 170 | if ((bntmp = ASN1_INTEGER_to_BN(a, NULL)) == NULL |
10a3195f | 171 | || (strtmp = bignum_to_string(bntmp)) == NULL) |
0f113f3e MC |
172 | X509V3err(X509V3_F_I2S_ASN1_INTEGER, ERR_R_MALLOC_FAILURE); |
173 | BN_free(bntmp); | |
174 | return strtmp; | |
0ca5f8b1 DSH |
175 | } |
176 | ||
2b91da96 | 177 | ASN1_INTEGER *s2i_ASN1_INTEGER(X509V3_EXT_METHOD *method, const char *value) |
28a98809 | 178 | { |
0f113f3e MC |
179 | BIGNUM *bn = NULL; |
180 | ASN1_INTEGER *aint; | |
181 | int isneg, ishex; | |
182 | int ret; | |
278260bf | 183 | |
90945fa3 | 184 | if (value == NULL) { |
0f113f3e | 185 | X509V3err(X509V3_F_S2I_ASN1_INTEGER, X509V3_R_INVALID_NULL_VALUE); |
90945fa3 | 186 | return NULL; |
0f113f3e MC |
187 | } |
188 | bn = BN_new(); | |
90945fa3 MC |
189 | if (bn == NULL) { |
190 | X509V3err(X509V3_F_S2I_ASN1_INTEGER, ERR_R_MALLOC_FAILURE); | |
191 | return NULL; | |
192 | } | |
0f113f3e MC |
193 | if (value[0] == '-') { |
194 | value++; | |
195 | isneg = 1; | |
278260bf | 196 | } else { |
0f113f3e | 197 | isneg = 0; |
278260bf | 198 | } |
0f113f3e MC |
199 | |
200 | if (value[0] == '0' && ((value[1] == 'x') || (value[1] == 'X'))) { | |
201 | value += 2; | |
202 | ishex = 1; | |
278260bf | 203 | } else { |
0f113f3e | 204 | ishex = 0; |
278260bf | 205 | } |
0f113f3e MC |
206 | |
207 | if (ishex) | |
208 | ret = BN_hex2bn(&bn, value); | |
209 | else | |
210 | ret = BN_dec2bn(&bn, value); | |
211 | ||
212 | if (!ret || value[ret]) { | |
213 | BN_free(bn); | |
214 | X509V3err(X509V3_F_S2I_ASN1_INTEGER, X509V3_R_BN_DEC2BN_ERROR); | |
90945fa3 | 215 | return NULL; |
0f113f3e MC |
216 | } |
217 | ||
218 | if (isneg && BN_is_zero(bn)) | |
219 | isneg = 0; | |
220 | ||
221 | aint = BN_to_ASN1_INTEGER(bn, NULL); | |
222 | BN_free(bn); | |
223 | if (!aint) { | |
224 | X509V3err(X509V3_F_S2I_ASN1_INTEGER, | |
225 | X509V3_R_BN_TO_ASN1_INTEGER_ERROR); | |
90945fa3 | 226 | return NULL; |
0f113f3e MC |
227 | } |
228 | if (isneg) | |
229 | aint->type |= V_ASN1_NEG; | |
230 | return aint; | |
28a98809 DSH |
231 | } |
232 | ||
bf9d5e48 | 233 | int X509V3_add_value_int(const char *name, const ASN1_INTEGER *aint, |
0f113f3e | 234 | STACK_OF(CONF_VALUE) **extlist) |
9aeaf1b4 | 235 | { |
0f113f3e MC |
236 | char *strtmp; |
237 | int ret; | |
75ebbd9a | 238 | |
0f113f3e MC |
239 | if (!aint) |
240 | return 1; | |
75ebbd9a | 241 | if ((strtmp = i2s_ASN1_INTEGER(NULL, aint)) == NULL) |
0f113f3e MC |
242 | return 0; |
243 | ret = X509V3_add_value(name, strtmp, extlist); | |
244 | OPENSSL_free(strtmp); | |
245 | return ret; | |
9aeaf1b4 DSH |
246 | } |
247 | ||
bf9d5e48 | 248 | int X509V3_get_value_bool(const CONF_VALUE *value, int *asn1_bool) |
9aeaf1b4 | 249 | { |
bf9d5e48 | 250 | const char *btmp; |
75ebbd9a RS |
251 | |
252 | if ((btmp = value->value) == NULL) | |
0f113f3e | 253 | goto err; |
86885c28 RS |
254 | if (strcmp(btmp, "TRUE") == 0 |
255 | || strcmp(btmp, "true") == 0 | |
256 | || strcmp(btmp, "Y") == 0 | |
257 | || strcmp(btmp, "y") == 0 | |
258 | || strcmp(btmp, "YES") == 0 | |
259 | || strcmp(btmp, "yes") == 0) { | |
0f113f3e MC |
260 | *asn1_bool = 0xff; |
261 | return 1; | |
86885c28 RS |
262 | } |
263 | if (strcmp(btmp, "FALSE") == 0 | |
264 | || strcmp(btmp, "false") == 0 | |
265 | || strcmp(btmp, "N") == 0 | |
266 | || strcmp(btmp, "n") == 0 | |
267 | || strcmp(btmp, "NO") == 0 | |
268 | || strcmp(btmp, "no") == 0) { | |
0f113f3e MC |
269 | *asn1_bool = 0; |
270 | return 1; | |
271 | } | |
272 | err: | |
273 | X509V3err(X509V3_F_X509V3_GET_VALUE_BOOL, | |
274 | X509V3_R_INVALID_BOOLEAN_STRING); | |
c90c4693 | 275 | X509V3_conf_add_error_name_value(value); |
0f113f3e | 276 | return 0; |
9aeaf1b4 DSH |
277 | } |
278 | ||
bf9d5e48 | 279 | int X509V3_get_value_int(const CONF_VALUE *value, ASN1_INTEGER **aint) |
9aeaf1b4 | 280 | { |
0f113f3e | 281 | ASN1_INTEGER *itmp; |
75ebbd9a RS |
282 | |
283 | if ((itmp = s2i_ASN1_INTEGER(NULL, value->value)) == NULL) { | |
c90c4693 | 284 | X509V3_conf_add_error_name_value(value); |
0f113f3e MC |
285 | return 0; |
286 | } | |
287 | *aint = itmp; | |
288 | return 1; | |
9aeaf1b4 DSH |
289 | } |
290 | ||
0f113f3e MC |
291 | #define HDR_NAME 1 |
292 | #define HDR_VALUE 2 | |
9aeaf1b4 | 293 | |
0f113f3e MC |
294 | /* |
295 | * #define DEBUG | |
296 | */ | |
9aeaf1b4 | 297 | |
535d79da | 298 | STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line) |
9aeaf1b4 | 299 | { |
0f113f3e MC |
300 | char *p, *q, c; |
301 | char *ntmp, *vtmp; | |
302 | STACK_OF(CONF_VALUE) *values = NULL; | |
303 | char *linebuf; | |
304 | int state; | |
278260bf | 305 | |
0f113f3e | 306 | /* We are going to modify the line so copy it first */ |
7644a9ae | 307 | linebuf = OPENSSL_strdup(line); |
344c271e KC |
308 | if (linebuf == NULL) { |
309 | X509V3err(X509V3_F_X509V3_PARSE_LIST, ERR_R_MALLOC_FAILURE); | |
310 | goto err; | |
311 | } | |
0f113f3e MC |
312 | state = HDR_NAME; |
313 | ntmp = NULL; | |
314 | /* Go through all characters */ | |
315 | for (p = linebuf, q = linebuf; (c = *p) && (c != '\r') && (c != '\n'); | |
316 | p++) { | |
317 | ||
318 | switch (state) { | |
319 | case HDR_NAME: | |
320 | if (c == ':') { | |
321 | state = HDR_VALUE; | |
322 | *p = 0; | |
323 | ntmp = strip_spaces(q); | |
324 | if (!ntmp) { | |
325 | X509V3err(X509V3_F_X509V3_PARSE_LIST, | |
1ac658ac | 326 | X509V3_R_INVALID_EMPTY_NAME); |
0f113f3e MC |
327 | goto err; |
328 | } | |
329 | q = p + 1; | |
330 | } else if (c == ',') { | |
331 | *p = 0; | |
332 | ntmp = strip_spaces(q); | |
333 | q = p + 1; | |
0f113f3e MC |
334 | if (!ntmp) { |
335 | X509V3err(X509V3_F_X509V3_PARSE_LIST, | |
1ac658ac | 336 | X509V3_R_INVALID_EMPTY_NAME); |
0f113f3e MC |
337 | goto err; |
338 | } | |
339 | X509V3_add_value(ntmp, NULL, &values); | |
340 | } | |
341 | break; | |
342 | ||
343 | case HDR_VALUE: | |
344 | if (c == ',') { | |
345 | state = HDR_NAME; | |
346 | *p = 0; | |
347 | vtmp = strip_spaces(q); | |
0f113f3e MC |
348 | if (!vtmp) { |
349 | X509V3err(X509V3_F_X509V3_PARSE_LIST, | |
350 | X509V3_R_INVALID_NULL_VALUE); | |
351 | goto err; | |
352 | } | |
353 | X509V3_add_value(ntmp, vtmp, &values); | |
354 | ntmp = NULL; | |
355 | q = p + 1; | |
356 | } | |
357 | ||
358 | } | |
359 | } | |
360 | ||
361 | if (state == HDR_VALUE) { | |
362 | vtmp = strip_spaces(q); | |
0f113f3e MC |
363 | if (!vtmp) { |
364 | X509V3err(X509V3_F_X509V3_PARSE_LIST, | |
365 | X509V3_R_INVALID_NULL_VALUE); | |
366 | goto err; | |
367 | } | |
368 | X509V3_add_value(ntmp, vtmp, &values); | |
369 | } else { | |
370 | ntmp = strip_spaces(q); | |
0f113f3e | 371 | if (!ntmp) { |
1ac658ac | 372 | X509V3err(X509V3_F_X509V3_PARSE_LIST, X509V3_R_INVALID_EMPTY_NAME); |
0f113f3e MC |
373 | goto err; |
374 | } | |
375 | X509V3_add_value(ntmp, NULL, &values); | |
376 | } | |
377 | OPENSSL_free(linebuf); | |
378 | return values; | |
379 | ||
380 | err: | |
381 | OPENSSL_free(linebuf); | |
382 | sk_CONF_VALUE_pop_free(values, X509V3_conf_free); | |
383 | return NULL; | |
9aeaf1b4 DSH |
384 | |
385 | } | |
386 | ||
387 | /* Delete leading and trailing spaces from a string */ | |
6b691a5c | 388 | static char *strip_spaces(char *name) |
9aeaf1b4 | 389 | { |
0f113f3e | 390 | char *p, *q; |
278260bf | 391 | |
0f113f3e MC |
392 | /* Skip over leading spaces */ |
393 | p = name; | |
a1df06b3 | 394 | while (*p && ossl_isspace(*p)) |
0f113f3e | 395 | p++; |
12a765a5 | 396 | if (*p == '\0') |
0f113f3e MC |
397 | return NULL; |
398 | q = p + strlen(p) - 1; | |
a1df06b3 | 399 | while ((q != p) && ossl_isspace(*q)) |
0f113f3e MC |
400 | q--; |
401 | if (p != q) | |
402 | q[1] = 0; | |
12a765a5 | 403 | if (*p == '\0') |
0f113f3e MC |
404 | return NULL; |
405 | return p; | |
9aeaf1b4 | 406 | } |
175b0942 | 407 | |
d08d8da4 | 408 | |
0f113f3e MC |
409 | /* |
410 | * V2I name comparison function: returns zero if 'name' matches cmp or cmp.* | |
d08d8da4 DSH |
411 | */ |
412 | ||
5f5edd7d | 413 | int v3_name_cmp(const char *name, const char *cmp) |
d08d8da4 | 414 | { |
0f113f3e MC |
415 | int len, ret; |
416 | char c; | |
278260bf | 417 | |
0f113f3e MC |
418 | len = strlen(cmp); |
419 | if ((ret = strncmp(name, cmp, len))) | |
420 | return ret; | |
421 | c = name[len]; | |
422 | if (!c || (c == '.')) | |
423 | return 0; | |
424 | return 1; | |
d08d8da4 | 425 | } |
a91dedca | 426 | |
0f113f3e | 427 | static int sk_strcmp(const char *const *a, const char *const *b) |
a91dedca | 428 | { |
0f113f3e | 429 | return strcmp(*a, *b); |
a91dedca DSH |
430 | } |
431 | ||
c869da88 | 432 | STACK_OF(OPENSSL_STRING) *X509_get1_email(X509 *x) |
a91dedca | 433 | { |
0f113f3e MC |
434 | GENERAL_NAMES *gens; |
435 | STACK_OF(OPENSSL_STRING) *ret; | |
5ce278a7 | 436 | |
0f113f3e MC |
437 | gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); |
438 | ret = get_email(X509_get_subject_name(x), gens); | |
439 | sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); | |
440 | return ret; | |
a91dedca DSH |
441 | } |
442 | ||
c869da88 | 443 | STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x) |
67c8e7f4 | 444 | { |
0f113f3e MC |
445 | AUTHORITY_INFO_ACCESS *info; |
446 | STACK_OF(OPENSSL_STRING) *ret = NULL; | |
447 | int i; | |
448 | ||
449 | info = X509_get_ext_d2i(x, NID_info_access, NULL, NULL); | |
450 | if (!info) | |
451 | return NULL; | |
452 | for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) { | |
453 | ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i); | |
454 | if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) { | |
455 | if (ad->location->type == GEN_URI) { | |
456 | if (!append_ia5 | |
457 | (&ret, ad->location->d.uniformResourceIdentifier)) | |
458 | break; | |
459 | } | |
460 | } | |
461 | } | |
462 | AUTHORITY_INFO_ACCESS_free(info); | |
463 | return ret; | |
67c8e7f4 DSH |
464 | } |
465 | ||
c869da88 | 466 | STACK_OF(OPENSSL_STRING) *X509_REQ_get1_email(X509_REQ *x) |
a91dedca | 467 | { |
0f113f3e MC |
468 | GENERAL_NAMES *gens; |
469 | STACK_OF(X509_EXTENSION) *exts; | |
470 | STACK_OF(OPENSSL_STRING) *ret; | |
471 | ||
472 | exts = X509_REQ_get_extensions(x); | |
473 | gens = X509V3_get_d2i(exts, NID_subject_alt_name, NULL, NULL); | |
474 | ret = get_email(X509_REQ_get_subject_name(x), gens); | |
475 | sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); | |
476 | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); | |
477 | return ret; | |
a91dedca DSH |
478 | } |
479 | ||
8cc86b81 | 480 | static STACK_OF(OPENSSL_STRING) *get_email(const X509_NAME *name, |
0f113f3e | 481 | GENERAL_NAMES *gens) |
a91dedca | 482 | { |
0f113f3e MC |
483 | STACK_OF(OPENSSL_STRING) *ret = NULL; |
484 | X509_NAME_ENTRY *ne; | |
d2a56999 | 485 | const ASN1_IA5STRING *email; |
0f113f3e | 486 | GENERAL_NAME *gen; |
d2a56999 F |
487 | int i = -1; |
488 | ||
0f113f3e | 489 | /* Now add any email address(es) to STACK */ |
0f113f3e MC |
490 | /* First supplied X509_NAME */ |
491 | while ((i = X509_NAME_get_index_by_NID(name, | |
492 | NID_pkcs9_emailAddress, i)) >= 0) { | |
493 | ne = X509_NAME_get_entry(name, i); | |
494 | email = X509_NAME_ENTRY_get_data(ne); | |
495 | if (!append_ia5(&ret, email)) | |
496 | return NULL; | |
497 | } | |
498 | for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { | |
499 | gen = sk_GENERAL_NAME_value(gens, i); | |
500 | if (gen->type != GEN_EMAIL) | |
501 | continue; | |
502 | if (!append_ia5(&ret, gen->d.ia5)) | |
503 | return NULL; | |
504 | } | |
505 | return ret; | |
a91dedca DSH |
506 | } |
507 | ||
c869da88 | 508 | static void str_free(OPENSSL_STRING str) |
a91dedca | 509 | { |
0f113f3e | 510 | OPENSSL_free(str); |
a91dedca DSH |
511 | } |
512 | ||
278260bf DDO |
513 | static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, |
514 | const ASN1_IA5STRING *email) | |
a91dedca | 515 | { |
0f113f3e | 516 | char *emtmp; |
278260bf | 517 | |
0f113f3e MC |
518 | /* First some sanity checks */ |
519 | if (email->type != V_ASN1_IA5STRING) | |
520 | return 1; | |
521 | if (!email->data || !email->length) | |
522 | return 1; | |
90945fa3 | 523 | if (*sk == NULL) |
0f113f3e | 524 | *sk = sk_OPENSSL_STRING_new(sk_strcmp); |
90945fa3 | 525 | if (*sk == NULL) |
0f113f3e MC |
526 | return 0; |
527 | /* Don't add duplicates */ | |
528 | if (sk_OPENSSL_STRING_find(*sk, (char *)email->data) != -1) | |
529 | return 1; | |
7644a9ae | 530 | emtmp = OPENSSL_strdup((char *)email->data); |
90945fa3 | 531 | if (emtmp == NULL || !sk_OPENSSL_STRING_push(*sk, emtmp)) { |
278260bf | 532 | OPENSSL_free(emtmp); /* free on push failure */ |
0f113f3e MC |
533 | X509_email_free(*sk); |
534 | *sk = NULL; | |
535 | return 0; | |
536 | } | |
537 | return 1; | |
a91dedca DSH |
538 | } |
539 | ||
c869da88 | 540 | void X509_email_free(STACK_OF(OPENSSL_STRING) *sk) |
a91dedca | 541 | { |
0f113f3e | 542 | sk_OPENSSL_STRING_pop_free(sk, str_free); |
a91dedca | 543 | } |
4e5d3a7f | 544 | |
0f113f3e MC |
545 | typedef int (*equal_fn) (const unsigned char *pattern, size_t pattern_len, |
546 | const unsigned char *subject, size_t subject_len, | |
547 | unsigned int flags); | |
d88926f1 | 548 | |
a09e4d24 VD |
549 | /* Skip pattern prefix to match "wildcard" subject */ |
550 | static void skip_prefix(const unsigned char **p, size_t *plen, | |
a773b52a | 551 | size_t subject_len, |
0f113f3e MC |
552 | unsigned int flags) |
553 | { | |
554 | const unsigned char *pattern = *p; | |
555 | size_t pattern_len = *plen; | |
556 | ||
557 | /* | |
558 | * If subject starts with a leading '.' followed by more octets, and | |
559 | * pattern is longer, compare just an equal-length suffix with the | |
560 | * full subject (starting at the '.'), provided the prefix contains | |
561 | * no NULs. | |
562 | */ | |
563 | if ((flags & _X509_CHECK_FLAG_DOT_SUBDOMAINS) == 0) | |
564 | return; | |
565 | ||
566 | while (pattern_len > subject_len && *pattern) { | |
567 | if ((flags & X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS) && | |
568 | *pattern == '.') | |
569 | break; | |
570 | ++pattern; | |
571 | --pattern_len; | |
572 | } | |
573 | ||
574 | /* Skip if entire prefix acceptable */ | |
575 | if (pattern_len == subject_len) { | |
576 | *p = pattern; | |
577 | *plen = pattern_len; | |
578 | } | |
579 | } | |
a09e4d24 | 580 | |
d88926f1 DSH |
581 | /* Compare while ASCII ignoring case. */ |
582 | static int equal_nocase(const unsigned char *pattern, size_t pattern_len, | |
0f113f3e MC |
583 | const unsigned char *subject, size_t subject_len, |
584 | unsigned int flags) | |
585 | { | |
a773b52a | 586 | skip_prefix(&pattern, &pattern_len, subject_len, flags); |
0f113f3e MC |
587 | if (pattern_len != subject_len) |
588 | return 0; | |
278260bf | 589 | while (pattern_len != 0) { |
0f113f3e MC |
590 | unsigned char l = *pattern; |
591 | unsigned char r = *subject; | |
278260bf | 592 | |
0f113f3e MC |
593 | /* The pattern must not contain NUL characters. */ |
594 | if (l == 0) | |
595 | return 0; | |
596 | if (l != r) { | |
597 | if ('A' <= l && l <= 'Z') | |
598 | l = (l - 'A') + 'a'; | |
599 | if ('A' <= r && r <= 'Z') | |
600 | r = (r - 'A') + 'a'; | |
601 | if (l != r) | |
602 | return 0; | |
603 | } | |
604 | ++pattern; | |
605 | ++subject; | |
606 | --pattern_len; | |
607 | } | |
608 | return 1; | |
609 | } | |
d88926f1 DSH |
610 | |
611 | /* Compare using memcmp. */ | |
612 | static int equal_case(const unsigned char *pattern, size_t pattern_len, | |
0f113f3e MC |
613 | const unsigned char *subject, size_t subject_len, |
614 | unsigned int flags) | |
d88926f1 | 615 | { |
a773b52a | 616 | skip_prefix(&pattern, &pattern_len, subject_len, flags); |
0f113f3e MC |
617 | if (pattern_len != subject_len) |
618 | return 0; | |
619 | return !memcmp(pattern, subject, pattern_len); | |
d88926f1 DSH |
620 | } |
621 | ||
0f113f3e MC |
622 | /* |
623 | * RFC 5280, section 7.5, requires that only the domain is compared in a | |
624 | * case-insensitive manner. | |
625 | */ | |
d88926f1 | 626 | static int equal_email(const unsigned char *a, size_t a_len, |
0f113f3e MC |
627 | const unsigned char *b, size_t b_len, |
628 | unsigned int unused_flags) | |
629 | { | |
630 | size_t i = a_len; | |
278260bf | 631 | |
0f113f3e MC |
632 | if (a_len != b_len) |
633 | return 0; | |
634 | /* | |
635 | * We search backwards for the '@' character, so that we do not have to | |
636 | * deal with quoted local-parts. The domain part is compared in a | |
637 | * case-insensitive manner. | |
638 | */ | |
639 | while (i > 0) { | |
640 | --i; | |
641 | if (a[i] == '@' || b[i] == '@') { | |
642 | if (!equal_nocase(a + i, a_len - i, b + i, a_len - i, 0)) | |
643 | return 0; | |
644 | break; | |
645 | } | |
646 | } | |
647 | if (i == 0) | |
648 | i = a_len; | |
649 | return equal_case(a, i, b, i, 0); | |
650 | } | |
651 | ||
652 | /* | |
653 | * Compare the prefix and suffix with the subject, and check that the | |
654 | * characters in-between are valid. | |
655 | */ | |
d88926f1 | 656 | static int wildcard_match(const unsigned char *prefix, size_t prefix_len, |
0f113f3e MC |
657 | const unsigned char *suffix, size_t suffix_len, |
658 | const unsigned char *subject, size_t subject_len, | |
659 | unsigned int flags) | |
660 | { | |
661 | const unsigned char *wildcard_start; | |
662 | const unsigned char *wildcard_end; | |
663 | const unsigned char *p; | |
664 | int allow_multi = 0; | |
665 | int allow_idna = 0; | |
666 | ||
667 | if (subject_len < prefix_len + suffix_len) | |
668 | return 0; | |
669 | if (!equal_nocase(prefix, prefix_len, subject, prefix_len, flags)) | |
670 | return 0; | |
671 | wildcard_start = subject + prefix_len; | |
672 | wildcard_end = subject + (subject_len - suffix_len); | |
673 | if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len, flags)) | |
674 | return 0; | |
675 | /* | |
676 | * If the wildcard makes up the entire first label, it must match at | |
677 | * least one character. | |
678 | */ | |
679 | if (prefix_len == 0 && *suffix == '.') { | |
680 | if (wildcard_start == wildcard_end) | |
681 | return 0; | |
682 | allow_idna = 1; | |
683 | if (flags & X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS) | |
684 | allow_multi = 1; | |
685 | } | |
686 | /* IDNA labels cannot match partial wildcards */ | |
687 | if (!allow_idna && | |
688 | subject_len >= 4 && strncasecmp((char *)subject, "xn--", 4) == 0) | |
689 | return 0; | |
690 | /* The wildcard may match a literal '*' */ | |
691 | if (wildcard_end == wildcard_start + 1 && *wildcard_start == '*') | |
692 | return 1; | |
693 | /* | |
694 | * Check that the part matched by the wildcard contains only | |
695 | * permitted characters and only matches a single label unless | |
696 | * allow_multi is set. | |
697 | */ | |
698 | for (p = wildcard_start; p != wildcard_end; ++p) | |
699 | if (!(('0' <= *p && *p <= '9') || | |
700 | ('A' <= *p && *p <= 'Z') || | |
701 | ('a' <= *p && *p <= 'z') || | |
702 | *p == '-' || (allow_multi && *p == '.'))) | |
703 | return 0; | |
704 | return 1; | |
705 | } | |
706 | ||
707 | #define LABEL_START (1 << 0) | |
708 | #define LABEL_END (1 << 1) | |
709 | #define LABEL_HYPHEN (1 << 2) | |
710 | #define LABEL_IDNA (1 << 3) | |
d88926f1 | 711 | |
397a8e74 | 712 | static const unsigned char *valid_star(const unsigned char *p, size_t len, |
0f113f3e MC |
713 | unsigned int flags) |
714 | { | |
715 | const unsigned char *star = 0; | |
716 | size_t i; | |
717 | int state = LABEL_START; | |
718 | int dots = 0; | |
278260bf | 719 | |
0f113f3e MC |
720 | for (i = 0; i < len; ++i) { |
721 | /* | |
722 | * Locate first and only legal wildcard, either at the start | |
723 | * or end of a non-IDNA first and not final label. | |
724 | */ | |
725 | if (p[i] == '*') { | |
726 | int atstart = (state & LABEL_START); | |
9a3bf973 | 727 | int atend = (i == len - 1 || p[i + 1] == '.'); |
35a1cc90 MC |
728 | /*- |
729 | * At most one wildcard per pattern. | |
730 | * No wildcards in IDNA labels. | |
731 | * No wildcards after the first label. | |
732 | */ | |
0f113f3e MC |
733 | if (star != NULL || (state & LABEL_IDNA) != 0 || dots) |
734 | return NULL; | |
735 | /* Only full-label '*.example.com' wildcards? */ | |
736 | if ((flags & X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS) | |
737 | && (!atstart || !atend)) | |
738 | return NULL; | |
739 | /* No 'foo*bar' wildcards */ | |
740 | if (!atstart && !atend) | |
741 | return NULL; | |
742 | star = &p[i]; | |
743 | state &= ~LABEL_START; | |
744 | } else if (('a' <= p[i] && p[i] <= 'z') | |
745 | || ('A' <= p[i] && p[i] <= 'Z') | |
746 | || ('0' <= p[i] && p[i] <= '9')) { | |
747 | if ((state & LABEL_START) != 0 | |
748 | && len - i >= 4 && strncasecmp((char *)&p[i], "xn--", 4) == 0) | |
749 | state |= LABEL_IDNA; | |
750 | state &= ~(LABEL_HYPHEN | LABEL_START); | |
751 | } else if (p[i] == '.') { | |
752 | if ((state & (LABEL_HYPHEN | LABEL_START)) != 0) | |
753 | return NULL; | |
754 | state = LABEL_START; | |
755 | ++dots; | |
756 | } else if (p[i] == '-') { | |
9f9a3926 ZL |
757 | /* no domain/subdomain starts with '-' */ |
758 | if ((state & LABEL_START) != 0) | |
0f113f3e MC |
759 | return NULL; |
760 | state |= LABEL_HYPHEN; | |
278260bf | 761 | } else { |
0f113f3e | 762 | return NULL; |
278260bf | 763 | } |
0f113f3e MC |
764 | } |
765 | ||
766 | /* | |
767 | * The final label must not end in a hyphen or ".", and | |
768 | * there must be at least two dots after the star. | |
769 | */ | |
770 | if ((state & (LABEL_START | LABEL_HYPHEN)) != 0 || dots < 2) | |
771 | return NULL; | |
772 | return star; | |
773 | } | |
d88926f1 DSH |
774 | |
775 | /* Compare using wildcards. */ | |
776 | static int equal_wildcard(const unsigned char *pattern, size_t pattern_len, | |
0f113f3e MC |
777 | const unsigned char *subject, size_t subject_len, |
778 | unsigned int flags) | |
779 | { | |
780 | const unsigned char *star = NULL; | |
781 | ||
782 | /* | |
783 | * Subject names starting with '.' can only match a wildcard pattern | |
784 | * via a subject sub-domain pattern suffix match. | |
785 | */ | |
786 | if (!(subject_len > 1 && subject[0] == '.')) | |
787 | star = valid_star(pattern, pattern_len, flags); | |
788 | if (star == NULL) | |
789 | return equal_nocase(pattern, pattern_len, | |
790 | subject, subject_len, flags); | |
791 | return wildcard_match(pattern, star - pattern, | |
792 | star + 1, (pattern + pattern_len) - star - 1, | |
793 | subject, subject_len, flags); | |
794 | } | |
795 | ||
796 | /* | |
797 | * Compare an ASN1_STRING to a supplied string. If they match return 1. If | |
798 | * cmp_type > 0 only compare if string matches the type, otherwise convert it | |
799 | * to UTF8. | |
a70da5b3 DSH |
800 | */ |
801 | ||
9f5466b9 | 802 | static int do_check_string(const ASN1_STRING *a, int cmp_type, equal_fn equal, |
0f113f3e MC |
803 | unsigned int flags, const char *b, size_t blen, |
804 | char **peername) | |
805 | { | |
806 | int rv = 0; | |
807 | ||
808 | if (!a->data || !a->length) | |
809 | return 0; | |
810 | if (cmp_type > 0) { | |
811 | if (cmp_type != a->type) | |
812 | return 0; | |
813 | if (cmp_type == V_ASN1_IA5STRING) | |
814 | rv = equal(a->data, a->length, (unsigned char *)b, blen, flags); | |
815 | else if (a->length == (int)blen && !memcmp(a->data, b, blen)) | |
816 | rv = 1; | |
817 | if (rv > 0 && peername) | |
7644a9ae | 818 | *peername = OPENSSL_strndup((char *)a->data, a->length); |
0f113f3e MC |
819 | } else { |
820 | int astrlen; | |
821 | unsigned char *astr; | |
822 | astrlen = ASN1_STRING_to_UTF8(&astr, a); | |
0923e7df EK |
823 | if (astrlen < 0) { |
824 | /* | |
825 | * -1 could be an internal malloc failure or a decoding error from | |
826 | * malformed input; we can't distinguish. | |
827 | */ | |
0f113f3e | 828 | return -1; |
0923e7df | 829 | } |
0f113f3e MC |
830 | rv = equal(astr, astrlen, (unsigned char *)b, blen, flags); |
831 | if (rv > 0 && peername) | |
7644a9ae | 832 | *peername = OPENSSL_strndup((char *)astr, astrlen); |
0f113f3e MC |
833 | OPENSSL_free(astr); |
834 | } | |
835 | return rv; | |
836 | } | |
a70da5b3 | 837 | |
297c67fc | 838 | static int do_x509_check(X509 *x, const char *chk, size_t chklen, |
0f113f3e MC |
839 | unsigned int flags, int check_type, char **peername) |
840 | { | |
841 | GENERAL_NAMES *gens = NULL; | |
8cc86b81 | 842 | const X509_NAME *name = NULL; |
0f113f3e | 843 | int i; |
fffc2fae | 844 | int cnid = NID_undef; |
0f113f3e MC |
845 | int alt_type; |
846 | int san_present = 0; | |
847 | int rv = 0; | |
848 | equal_fn equal; | |
849 | ||
850 | /* See below, this flag is internal-only */ | |
851 | flags &= ~_X509_CHECK_FLAG_DOT_SUBDOMAINS; | |
852 | if (check_type == GEN_EMAIL) { | |
853 | cnid = NID_pkcs9_emailAddress; | |
854 | alt_type = V_ASN1_IA5STRING; | |
855 | equal = equal_email; | |
856 | } else if (check_type == GEN_DNS) { | |
857 | cnid = NID_commonName; | |
858 | /* Implicit client-side DNS sub-domain pattern */ | |
859 | if (chklen > 1 && chk[0] == '.') | |
860 | flags |= _X509_CHECK_FLAG_DOT_SUBDOMAINS; | |
861 | alt_type = V_ASN1_IA5STRING; | |
862 | if (flags & X509_CHECK_FLAG_NO_WILDCARDS) | |
863 | equal = equal_nocase; | |
864 | else | |
865 | equal = equal_wildcard; | |
866 | } else { | |
0f113f3e MC |
867 | alt_type = V_ASN1_OCTET_STRING; |
868 | equal = equal_case; | |
869 | } | |
870 | ||
871 | if (chklen == 0) | |
872 | chklen = strlen(chk); | |
873 | ||
874 | gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); | |
875 | if (gens) { | |
876 | for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { | |
877 | GENERAL_NAME *gen; | |
878 | ASN1_STRING *cstr; | |
278260bf | 879 | |
0f113f3e MC |
880 | gen = sk_GENERAL_NAME_value(gens, i); |
881 | if (gen->type != check_type) | |
882 | continue; | |
883 | san_present = 1; | |
884 | if (check_type == GEN_EMAIL) | |
885 | cstr = gen->d.rfc822Name; | |
886 | else if (check_type == GEN_DNS) | |
887 | cstr = gen->d.dNSName; | |
888 | else | |
889 | cstr = gen->d.iPAddress; | |
890 | /* Positive on success, negative on error! */ | |
891 | if ((rv = do_check_string(cstr, alt_type, equal, flags, | |
892 | chk, chklen, peername)) != 0) | |
893 | break; | |
894 | } | |
895 | GENERAL_NAMES_free(gens); | |
896 | if (rv != 0) | |
897 | return rv; | |
dd60efea | 898 | if (san_present && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)) |
0f113f3e MC |
899 | return 0; |
900 | } | |
fffc2fae VD |
901 | |
902 | /* We're done if CN-ID is not pertinent */ | |
dd60efea | 903 | if (cnid == NID_undef || (flags & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT)) |
fffc2fae VD |
904 | return 0; |
905 | ||
0f113f3e MC |
906 | i = -1; |
907 | name = X509_get_subject_name(x); | |
908 | while ((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) { | |
9f5466b9 F |
909 | const X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i); |
910 | const ASN1_STRING *str = X509_NAME_ENTRY_get_data(ne); | |
911 | ||
0f113f3e MC |
912 | /* Positive on success, negative on error! */ |
913 | if ((rv = do_check_string(str, -1, equal, flags, | |
914 | chk, chklen, peername)) != 0) | |
915 | return rv; | |
916 | } | |
917 | return 0; | |
918 | } | |
a70da5b3 | 919 | |
297c67fc | 920 | int X509_check_host(X509 *x, const char *chk, size_t chklen, |
0f113f3e MC |
921 | unsigned int flags, char **peername) |
922 | { | |
923 | if (chk == NULL) | |
924 | return -2; | |
925 | /* | |
926 | * Embedded NULs are disallowed, except as the last character of a | |
927 | * string of length 2 or more (tolerate caller including terminating | |
928 | * NUL in string length). | |
929 | */ | |
930 | if (chklen == 0) | |
931 | chklen = strlen(chk); | |
932 | else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen)) | |
933 | return -2; | |
934 | if (chklen > 1 && chk[chklen - 1] == '\0') | |
935 | --chklen; | |
936 | return do_x509_check(x, chk, chklen, flags, GEN_DNS, peername); | |
937 | } | |
a70da5b3 | 938 | |
297c67fc | 939 | int X509_check_email(X509 *x, const char *chk, size_t chklen, |
0f113f3e MC |
940 | unsigned int flags) |
941 | { | |
942 | if (chk == NULL) | |
943 | return -2; | |
944 | /* | |
945 | * Embedded NULs are disallowed, except as the last character of a | |
946 | * string of length 2 or more (tolerate caller including terminating | |
947 | * NUL in string length). | |
948 | */ | |
949 | if (chklen == 0) | |
950 | chklen = strlen((char *)chk); | |
951 | else if (memchr(chk, '\0', chklen > 1 ? chklen - 1 : chklen)) | |
952 | return -2; | |
953 | if (chklen > 1 && chk[chklen - 1] == '\0') | |
954 | --chklen; | |
955 | return do_x509_check(x, chk, chklen, flags, GEN_EMAIL, NULL); | |
956 | } | |
a70da5b3 DSH |
957 | |
958 | int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, | |
0f113f3e MC |
959 | unsigned int flags) |
960 | { | |
961 | if (chk == NULL) | |
962 | return -2; | |
963 | return do_x509_check(x, (char *)chk, chklen, flags, GEN_IPADD, NULL); | |
964 | } | |
a70da5b3 DSH |
965 | |
966 | int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) | |
0f113f3e MC |
967 | { |
968 | unsigned char ipout[16]; | |
969 | size_t iplen; | |
970 | ||
971 | if (ipasc == NULL) | |
972 | return -2; | |
973 | iplen = (size_t)a2i_ipadd(ipout, ipasc); | |
974 | if (iplen == 0) | |
975 | return -2; | |
976 | return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL); | |
977 | } | |
978 | ||
278260bf DDO |
979 | char *ipaddr_to_asc(unsigned char *p, int len) |
980 | { | |
981 | char buf[40], *out; | |
982 | ||
983 | switch (len) { | |
984 | case 4: /* IPv4 */ | |
985 | BIO_snprintf(buf, sizeof(buf), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); | |
986 | break; | |
987 | /* TODO possibly combine with static i2r_address() in v3_addr.c */ | |
988 | case 16: /* IPv6 */ | |
989 | for (out = buf; out < buf + 8 * 3; out += 3) { | |
990 | BIO_snprintf(out, 3 + 1, "%X:", p[0] << 8 | p[1]); | |
991 | p += 2; | |
992 | } | |
993 | out[-1] = '\0'; | |
994 | break; | |
995 | default: | |
996 | BIO_snprintf(buf, sizeof(buf), "<invalid length=%d>", len); | |
997 | break; | |
998 | } | |
999 | return OPENSSL_strdup(buf); | |
1000 | } | |
1001 | ||
0f113f3e MC |
1002 | /* |
1003 | * Convert IP addresses both IPv4 and IPv6 into an OCTET STRING compatible | |
1004 | * with RFC3280. | |
4e5d3a7f DSH |
1005 | */ |
1006 | ||
1007 | ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc) | |
0f113f3e MC |
1008 | { |
1009 | unsigned char ipout[16]; | |
1010 | ASN1_OCTET_STRING *ret; | |
1011 | int iplen; | |
4e5d3a7f | 1012 | |
0f113f3e | 1013 | /* If string contains a ':' assume IPv6 */ |
4e5d3a7f | 1014 | |
0f113f3e | 1015 | iplen = a2i_ipadd(ipout, ipasc); |
520b76ff | 1016 | |
0f113f3e MC |
1017 | if (!iplen) |
1018 | return NULL; | |
4e5d3a7f | 1019 | |
0f113f3e | 1020 | ret = ASN1_OCTET_STRING_new(); |
90945fa3 | 1021 | if (ret == NULL) |
0f113f3e MC |
1022 | return NULL; |
1023 | if (!ASN1_OCTET_STRING_set(ret, ipout, iplen)) { | |
1024 | ASN1_OCTET_STRING_free(ret); | |
1025 | return NULL; | |
1026 | } | |
1027 | return ret; | |
1028 | } | |
4e5d3a7f | 1029 | |
520b76ff | 1030 | ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc) |
0f113f3e MC |
1031 | { |
1032 | ASN1_OCTET_STRING *ret = NULL; | |
1033 | unsigned char ipout[32]; | |
1034 | char *iptmp = NULL, *p; | |
1035 | int iplen1, iplen2; | |
12a765a5 | 1036 | |
0f113f3e | 1037 | p = strchr(ipasc, '/'); |
12a765a5 | 1038 | if (p == NULL) |
0f113f3e | 1039 | return NULL; |
7644a9ae | 1040 | iptmp = OPENSSL_strdup(ipasc); |
12a765a5 | 1041 | if (iptmp == NULL) |
0f113f3e MC |
1042 | return NULL; |
1043 | p = iptmp + (p - ipasc); | |
1044 | *p++ = 0; | |
1045 | ||
1046 | iplen1 = a2i_ipadd(ipout, iptmp); | |
1047 | ||
1048 | if (!iplen1) | |
1049 | goto err; | |
1050 | ||
1051 | iplen2 = a2i_ipadd(ipout + iplen1, p); | |
1052 | ||
1053 | OPENSSL_free(iptmp); | |
1054 | iptmp = NULL; | |
1055 | ||
1056 | if (!iplen2 || (iplen1 != iplen2)) | |
1057 | goto err; | |
1058 | ||
1059 | ret = ASN1_OCTET_STRING_new(); | |
90945fa3 | 1060 | if (ret == NULL) |
0f113f3e MC |
1061 | goto err; |
1062 | if (!ASN1_OCTET_STRING_set(ret, ipout, iplen1 + iplen2)) | |
1063 | goto err; | |
1064 | ||
1065 | return ret; | |
1066 | ||
1067 | err: | |
b548a1f1 | 1068 | OPENSSL_free(iptmp); |
2ace7450 | 1069 | ASN1_OCTET_STRING_free(ret); |
0f113f3e MC |
1070 | return NULL; |
1071 | } | |
520b76ff | 1072 | |
96ea4ae9 | 1073 | int a2i_ipadd(unsigned char *ipout, const char *ipasc) |
0f113f3e MC |
1074 | { |
1075 | /* If string contains a ':' assume IPv6 */ | |
1076 | ||
1077 | if (strchr(ipasc, ':')) { | |
1078 | if (!ipv6_from_asc(ipout, ipasc)) | |
1079 | return 0; | |
1080 | return 16; | |
1081 | } else { | |
1082 | if (!ipv4_from_asc(ipout, ipasc)) | |
1083 | return 0; | |
1084 | return 4; | |
1085 | } | |
1086 | } | |
520b76ff | 1087 | |
4e5d3a7f | 1088 | static int ipv4_from_asc(unsigned char *v4, const char *in) |
0f113f3e MC |
1089 | { |
1090 | int a0, a1, a2, a3; | |
278260bf | 1091 | |
0f113f3e MC |
1092 | if (sscanf(in, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) != 4) |
1093 | return 0; | |
1094 | if ((a0 < 0) || (a0 > 255) || (a1 < 0) || (a1 > 255) | |
1095 | || (a2 < 0) || (a2 > 255) || (a3 < 0) || (a3 > 255)) | |
1096 | return 0; | |
1097 | v4[0] = a0; | |
1098 | v4[1] = a1; | |
1099 | v4[2] = a2; | |
1100 | v4[3] = a3; | |
1101 | return 1; | |
1102 | } | |
4e5d3a7f DSH |
1103 | |
1104 | typedef struct { | |
0f113f3e MC |
1105 | /* Temporary store for IPV6 output */ |
1106 | unsigned char tmp[16]; | |
1107 | /* Total number of bytes in tmp */ | |
1108 | int total; | |
1109 | /* The position of a zero (corresponding to '::') */ | |
1110 | int zero_pos; | |
1111 | /* Number of zeroes */ | |
1112 | int zero_cnt; | |
1113 | } IPV6_STAT; | |
4e5d3a7f DSH |
1114 | |
1115 | static int ipv6_from_asc(unsigned char *v6, const char *in) | |
0f113f3e MC |
1116 | { |
1117 | IPV6_STAT v6stat; | |
278260bf | 1118 | |
0f113f3e MC |
1119 | v6stat.total = 0; |
1120 | v6stat.zero_pos = -1; | |
1121 | v6stat.zero_cnt = 0; | |
1122 | /* | |
1123 | * Treat the IPv6 representation as a list of values separated by ':'. | |
1124 | * The presence of a '::' will parse as one, two or three zero length | |
1125 | * elements. | |
1126 | */ | |
1127 | if (!CONF_parse_list(in, ':', 0, ipv6_cb, &v6stat)) | |
1128 | return 0; | |
1129 | ||
1130 | /* Now for some sanity checks */ | |
1131 | ||
1132 | if (v6stat.zero_pos == -1) { | |
1133 | /* If no '::' must have exactly 16 bytes */ | |
1134 | if (v6stat.total != 16) | |
1135 | return 0; | |
1136 | } else { | |
1137 | /* If '::' must have less than 16 bytes */ | |
1138 | if (v6stat.total == 16) | |
1139 | return 0; | |
1140 | /* More than three zeroes is an error */ | |
278260bf | 1141 | if (v6stat.zero_cnt > 3) { |
0f113f3e MC |
1142 | return 0; |
1143 | /* Can only have three zeroes if nothing else present */ | |
278260bf | 1144 | } else if (v6stat.zero_cnt == 3) { |
0f113f3e MC |
1145 | if (v6stat.total > 0) |
1146 | return 0; | |
278260bf DDO |
1147 | } else if (v6stat.zero_cnt == 2) { |
1148 | /* Can only have two zeroes if at start or end */ | |
0f113f3e MC |
1149 | if ((v6stat.zero_pos != 0) |
1150 | && (v6stat.zero_pos != v6stat.total)) | |
1151 | return 0; | |
278260bf | 1152 | } else { |
0f113f3e | 1153 | /* Can only have one zero if *not* start or end */ |
0f113f3e MC |
1154 | if ((v6stat.zero_pos == 0) |
1155 | || (v6stat.zero_pos == v6stat.total)) | |
1156 | return 0; | |
1157 | } | |
1158 | } | |
1159 | ||
1160 | /* Format result */ | |
1161 | ||
1162 | if (v6stat.zero_pos >= 0) { | |
1163 | /* Copy initial part */ | |
1164 | memcpy(v6, v6stat.tmp, v6stat.zero_pos); | |
1165 | /* Zero middle */ | |
1166 | memset(v6 + v6stat.zero_pos, 0, 16 - v6stat.total); | |
1167 | /* Copy final part */ | |
1168 | if (v6stat.total != v6stat.zero_pos) | |
1169 | memcpy(v6 + v6stat.zero_pos + 16 - v6stat.total, | |
1170 | v6stat.tmp + v6stat.zero_pos, | |
1171 | v6stat.total - v6stat.zero_pos); | |
278260bf | 1172 | } else { |
0f113f3e | 1173 | memcpy(v6, v6stat.tmp, 16); |
278260bf | 1174 | } |
0f113f3e MC |
1175 | |
1176 | return 1; | |
1177 | } | |
4e5d3a7f DSH |
1178 | |
1179 | static int ipv6_cb(const char *elem, int len, void *usr) | |
0f113f3e MC |
1180 | { |
1181 | IPV6_STAT *s = usr; | |
278260bf | 1182 | |
0f113f3e MC |
1183 | /* Error if 16 bytes written */ |
1184 | if (s->total == 16) | |
1185 | return 0; | |
1186 | if (len == 0) { | |
1187 | /* Zero length element, corresponds to '::' */ | |
1188 | if (s->zero_pos == -1) | |
1189 | s->zero_pos = s->total; | |
1190 | /* If we've already got a :: its an error */ | |
1191 | else if (s->zero_pos != s->total) | |
1192 | return 0; | |
1193 | s->zero_cnt++; | |
1194 | } else { | |
1195 | /* If more than 4 characters could be final a.b.c.d form */ | |
1196 | if (len > 4) { | |
1197 | /* Need at least 4 bytes left */ | |
1198 | if (s->total > 12) | |
1199 | return 0; | |
1200 | /* Must be end of string */ | |
1201 | if (elem[len]) | |
1202 | return 0; | |
1203 | if (!ipv4_from_asc(s->tmp + s->total, elem)) | |
1204 | return 0; | |
1205 | s->total += 4; | |
1206 | } else { | |
1207 | if (!ipv6_hex(s->tmp + s->total, elem, len)) | |
1208 | return 0; | |
1209 | s->total += 2; | |
1210 | } | |
1211 | } | |
1212 | return 1; | |
1213 | } | |
1214 | ||
1215 | /* | |
1216 | * Convert a string of up to 4 hex digits into the corresponding IPv6 form. | |
4e5d3a7f DSH |
1217 | */ |
1218 | ||
1219 | static int ipv6_hex(unsigned char *out, const char *in, int inlen) | |
0f113f3e MC |
1220 | { |
1221 | unsigned char c; | |
1222 | unsigned int num = 0; | |
49445f21 RS |
1223 | int x; |
1224 | ||
0f113f3e MC |
1225 | if (inlen > 4) |
1226 | return 0; | |
1227 | while (inlen--) { | |
1228 | c = *in++; | |
1229 | num <<= 4; | |
49445f21 RS |
1230 | x = OPENSSL_hexchar2int(c); |
1231 | if (x < 0) | |
0f113f3e | 1232 | return 0; |
49445f21 | 1233 | num |= (char)x; |
0f113f3e MC |
1234 | } |
1235 | out[0] = num >> 8; | |
1236 | out[1] = num & 0xff; | |
1237 | return 1; | |
1238 | } | |
f0dc08e6 | 1239 | |
a7b1eed5 | 1240 | int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE) *dn_sk, |
0f113f3e MC |
1241 | unsigned long chtype) |
1242 | { | |
1243 | CONF_VALUE *v; | |
b5292f7b | 1244 | int i, mval, spec_char, plus_char; |
0f113f3e | 1245 | char *p, *type; |
278260bf | 1246 | |
0f113f3e MC |
1247 | if (!nm) |
1248 | return 0; | |
1249 | ||
1250 | for (i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) { | |
1251 | v = sk_CONF_VALUE_value(dn_sk, i); | |
1252 | type = v->name; | |
1253 | /* | |
1254 | * Skip past any leading X. X: X, etc to allow for multiple instances | |
1255 | */ | |
b5292f7b | 1256 | for (p = type; *p; p++) { |
f0dc08e6 | 1257 | #ifndef CHARSET_EBCDIC |
b5292f7b | 1258 | spec_char = ((*p == ':') || (*p == ',') || (*p == '.')); |
f0dc08e6 | 1259 | #else |
b5292f7b | 1260 | spec_char = ((*p == os_toascii[':']) || (*p == os_toascii[',']) |
278260bf | 1261 | || (*p == os_toascii['.'])); |
f0dc08e6 | 1262 | #endif |
b5292f7b | 1263 | if (spec_char) { |
0f113f3e MC |
1264 | p++; |
1265 | if (*p) | |
1266 | type = p; | |
1267 | break; | |
1268 | } | |
b5292f7b | 1269 | } |
1a15c899 | 1270 | #ifndef CHARSET_EBCDIC |
b5292f7b | 1271 | plus_char = (*type == '+'); |
1a15c899 | 1272 | #else |
b5292f7b | 1273 | plus_char = (*type == os_toascii['+']); |
1a15c899 | 1274 | #endif |
b5292f7b | 1275 | if (plus_char) { |
0f113f3e MC |
1276 | mval = -1; |
1277 | type++; | |
278260bf | 1278 | } else { |
0f113f3e | 1279 | mval = 0; |
278260bf | 1280 | } |
0f113f3e MC |
1281 | if (!X509_NAME_add_entry_by_txt(nm, type, chtype, |
1282 | (unsigned char *)v->value, -1, -1, | |
1283 | mval)) | |
1284 | return 0; | |
1285 | ||
1286 | } | |
1287 | return 1; | |
1288 | } |