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