]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * X.509v3 certificate parsing and processing (RFC 3280 profile) | |
235279e7 | 3 | * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> |
6fc6879b | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
03da66bd | 12 | #include "crypto/crypto.h" |
6fc6879b | 13 | #include "asn1.h" |
6fc6879b JM |
14 | #include "x509v3.h" |
15 | ||
16 | ||
17 | static void x509_free_name(struct x509_name *name) | |
18 | { | |
32b752ef JM |
19 | size_t i; |
20 | ||
21 | for (i = 0; i < name->num_attr; i++) { | |
22 | os_free(name->attr[i].value); | |
23 | name->attr[i].value = NULL; | |
24 | name->attr[i].type = X509_NAME_ATTR_NOT_USED; | |
25 | } | |
26 | name->num_attr = 0; | |
6fc6879b | 27 | os_free(name->email); |
6fc6879b | 28 | name->email = NULL; |
efe22727 JM |
29 | |
30 | os_free(name->alt_email); | |
31 | os_free(name->dns); | |
32 | os_free(name->uri); | |
33 | os_free(name->ip); | |
34 | name->alt_email = name->dns = name->uri = NULL; | |
35 | name->ip = NULL; | |
36 | name->ip_len = 0; | |
37 | os_memset(&name->rid, 0, sizeof(name->rid)); | |
6fc6879b JM |
38 | } |
39 | ||
40 | ||
41 | /** | |
42 | * x509_certificate_free - Free an X.509 certificate | |
43 | * @cert: Certificate to be freed | |
44 | */ | |
45 | void x509_certificate_free(struct x509_certificate *cert) | |
46 | { | |
47 | if (cert == NULL) | |
48 | return; | |
49 | if (cert->next) { | |
50 | wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p " | |
51 | "was still on a list (next=%p)\n", | |
52 | cert, cert->next); | |
53 | } | |
54 | x509_free_name(&cert->issuer); | |
55 | x509_free_name(&cert->subject); | |
56 | os_free(cert->public_key); | |
57 | os_free(cert->sign_value); | |
58 | os_free(cert); | |
59 | } | |
60 | ||
61 | ||
62 | /** | |
63 | * x509_certificate_free - Free an X.509 certificate chain | |
64 | * @cert: Pointer to the first certificate in the chain | |
65 | */ | |
66 | void x509_certificate_chain_free(struct x509_certificate *cert) | |
67 | { | |
68 | struct x509_certificate *next; | |
69 | ||
70 | while (cert) { | |
71 | next = cert->next; | |
72 | cert->next = NULL; | |
73 | x509_certificate_free(cert); | |
74 | cert = next; | |
75 | } | |
76 | } | |
77 | ||
78 | ||
79 | static int x509_whitespace(char c) | |
80 | { | |
81 | return c == ' ' || c == '\t'; | |
82 | } | |
83 | ||
84 | ||
85 | static void x509_str_strip_whitespace(char *a) | |
86 | { | |
87 | char *ipos, *opos; | |
88 | int remove_whitespace = 1; | |
89 | ||
90 | ipos = opos = a; | |
91 | ||
92 | while (*ipos) { | |
93 | if (remove_whitespace && x509_whitespace(*ipos)) | |
94 | ipos++; | |
95 | else { | |
96 | remove_whitespace = x509_whitespace(*ipos); | |
97 | *opos++ = *ipos++; | |
98 | } | |
99 | } | |
100 | ||
101 | *opos-- = '\0'; | |
102 | if (opos > a && x509_whitespace(*opos)) | |
103 | *opos = '\0'; | |
104 | } | |
105 | ||
106 | ||
107 | static int x509_str_compare(const char *a, const char *b) | |
108 | { | |
109 | char *aa, *bb; | |
110 | int ret; | |
111 | ||
112 | if (!a && b) | |
113 | return -1; | |
114 | if (a && !b) | |
115 | return 1; | |
116 | if (!a && !b) | |
117 | return 0; | |
118 | ||
119 | aa = os_strdup(a); | |
120 | bb = os_strdup(b); | |
121 | ||
122 | if (aa == NULL || bb == NULL) { | |
123 | os_free(aa); | |
124 | os_free(bb); | |
125 | return os_strcasecmp(a, b); | |
126 | } | |
127 | ||
128 | x509_str_strip_whitespace(aa); | |
129 | x509_str_strip_whitespace(bb); | |
130 | ||
131 | ret = os_strcasecmp(aa, bb); | |
132 | ||
133 | os_free(aa); | |
134 | os_free(bb); | |
135 | ||
136 | return ret; | |
137 | } | |
138 | ||
139 | ||
140 | /** | |
141 | * x509_name_compare - Compare X.509 certificate names | |
142 | * @a: Certificate name | |
143 | * @b: Certificate name | |
144 | * Returns: <0, 0, or >0 based on whether a is less than, equal to, or | |
145 | * greater than b | |
146 | */ | |
147 | int x509_name_compare(struct x509_name *a, struct x509_name *b) | |
148 | { | |
149 | int res; | |
32b752ef | 150 | size_t i; |
6fc6879b JM |
151 | |
152 | if (!a && b) | |
153 | return -1; | |
154 | if (a && !b) | |
155 | return 1; | |
156 | if (!a && !b) | |
157 | return 0; | |
32b752ef JM |
158 | if (a->num_attr < b->num_attr) |
159 | return -1; | |
160 | if (a->num_attr > b->num_attr) | |
161 | return 1; | |
6fc6879b | 162 | |
32b752ef JM |
163 | for (i = 0; i < a->num_attr; i++) { |
164 | if (a->attr[i].type < b->attr[i].type) | |
165 | return -1; | |
166 | if (a->attr[i].type > b->attr[i].type) | |
167 | return -1; | |
168 | res = x509_str_compare(a->attr[i].value, b->attr[i].value); | |
169 | if (res) | |
170 | return res; | |
171 | } | |
6fc6879b JM |
172 | res = x509_str_compare(a->email, b->email); |
173 | if (res) | |
174 | return res; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | ||
180 | static int x509_parse_algorithm_identifier( | |
181 | const u8 *buf, size_t len, | |
182 | struct x509_algorithm_identifier *id, const u8 **next) | |
183 | { | |
184 | struct asn1_hdr hdr; | |
185 | const u8 *pos, *end; | |
186 | ||
187 | /* | |
188 | * AlgorithmIdentifier ::= SEQUENCE { | |
189 | * algorithm OBJECT IDENTIFIER, | |
190 | * parameters ANY DEFINED BY algorithm OPTIONAL | |
191 | * } | |
192 | */ | |
193 | ||
194 | if (asn1_get_next(buf, len, &hdr) < 0 || | |
195 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
196 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
197 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " | |
198 | "(AlgorithmIdentifier) - found class %d tag 0x%x", | |
199 | hdr.class, hdr.tag); | |
200 | return -1; | |
201 | } | |
202 | pos = hdr.payload; | |
203 | end = pos + hdr.length; | |
204 | ||
205 | if (end > buf + len) | |
206 | return -1; | |
207 | ||
208 | *next = end; | |
209 | ||
210 | if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) | |
211 | return -1; | |
212 | ||
213 | /* TODO: optional parameters */ | |
214 | ||
215 | return 0; | |
216 | } | |
217 | ||
218 | ||
219 | static int x509_parse_public_key(const u8 *buf, size_t len, | |
220 | struct x509_certificate *cert, | |
221 | const u8 **next) | |
222 | { | |
223 | struct asn1_hdr hdr; | |
224 | const u8 *pos, *end; | |
225 | ||
226 | /* | |
227 | * SubjectPublicKeyInfo ::= SEQUENCE { | |
228 | * algorithm AlgorithmIdentifier, | |
229 | * subjectPublicKey BIT STRING | |
230 | * } | |
231 | */ | |
232 | ||
233 | pos = buf; | |
234 | end = buf + len; | |
235 | ||
236 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
237 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
238 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
239 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " | |
240 | "(SubjectPublicKeyInfo) - found class %d tag 0x%x", | |
241 | hdr.class, hdr.tag); | |
242 | return -1; | |
243 | } | |
244 | pos = hdr.payload; | |
245 | ||
246 | if (pos + hdr.length > end) | |
247 | return -1; | |
248 | end = pos + hdr.length; | |
249 | *next = end; | |
250 | ||
251 | if (x509_parse_algorithm_identifier(pos, end - pos, | |
252 | &cert->public_key_alg, &pos)) | |
253 | return -1; | |
254 | ||
255 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
256 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
257 | hdr.tag != ASN1_TAG_BITSTRING) { | |
258 | wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " | |
259 | "(subjectPublicKey) - found class %d tag 0x%x", | |
260 | hdr.class, hdr.tag); | |
261 | return -1; | |
262 | } | |
263 | if (hdr.length < 1) | |
264 | return -1; | |
265 | pos = hdr.payload; | |
266 | if (*pos) { | |
267 | wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", | |
268 | *pos); | |
269 | /* | |
270 | * TODO: should this be rejected? X.509 certificates are | |
271 | * unlikely to use such a construction. Now we would end up | |
272 | * including the extra bits in the buffer which may also be | |
273 | * ok. | |
274 | */ | |
275 | } | |
276 | os_free(cert->public_key); | |
277 | cert->public_key = os_malloc(hdr.length - 1); | |
278 | if (cert->public_key == NULL) { | |
279 | wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " | |
280 | "public key"); | |
281 | return -1; | |
282 | } | |
283 | os_memcpy(cert->public_key, pos + 1, hdr.length - 1); | |
284 | cert->public_key_len = hdr.length - 1; | |
285 | wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", | |
286 | cert->public_key, cert->public_key_len); | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
291 | ||
292 | static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, | |
293 | const u8 **next) | |
294 | { | |
295 | struct asn1_hdr hdr; | |
296 | const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; | |
297 | struct asn1_oid oid; | |
32b752ef | 298 | char *val; |
6fc6879b JM |
299 | |
300 | /* | |
301 | * Name ::= CHOICE { RDNSequence } | |
302 | * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName | |
303 | * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue | |
304 | * AttributeTypeAndValue ::= SEQUENCE { | |
305 | * type AttributeType, | |
306 | * value AttributeValue | |
307 | * } | |
308 | * AttributeType ::= OBJECT IDENTIFIER | |
309 | * AttributeValue ::= ANY DEFINED BY AttributeType | |
310 | */ | |
311 | ||
312 | if (asn1_get_next(buf, len, &hdr) < 0 || | |
313 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
314 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
315 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " | |
316 | "(Name / RDNSequencer) - found class %d tag 0x%x", | |
317 | hdr.class, hdr.tag); | |
318 | return -1; | |
319 | } | |
320 | pos = hdr.payload; | |
321 | ||
322 | if (pos + hdr.length > buf + len) | |
323 | return -1; | |
324 | ||
325 | end = *next = pos + hdr.length; | |
326 | ||
327 | while (pos < end) { | |
32b752ef JM |
328 | enum x509_name_attr_type type; |
329 | ||
6fc6879b JM |
330 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || |
331 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
332 | hdr.tag != ASN1_TAG_SET) { | |
333 | wpa_printf(MSG_DEBUG, "X509: Expected SET " | |
334 | "(RelativeDistinguishedName) - found class " | |
335 | "%d tag 0x%x", hdr.class, hdr.tag); | |
336 | x509_free_name(name); | |
337 | return -1; | |
338 | } | |
339 | ||
340 | set_pos = hdr.payload; | |
341 | pos = set_end = hdr.payload + hdr.length; | |
342 | ||
343 | if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || | |
344 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
345 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
346 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " | |
347 | "(AttributeTypeAndValue) - found class %d " | |
348 | "tag 0x%x", hdr.class, hdr.tag); | |
349 | x509_free_name(name); | |
350 | return -1; | |
351 | } | |
352 | ||
353 | seq_pos = hdr.payload; | |
354 | seq_end = hdr.payload + hdr.length; | |
355 | ||
356 | if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { | |
357 | x509_free_name(name); | |
358 | return -1; | |
359 | } | |
360 | ||
361 | if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || | |
362 | hdr.class != ASN1_CLASS_UNIVERSAL) { | |
363 | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | |
364 | "AttributeValue"); | |
365 | x509_free_name(name); | |
366 | return -1; | |
367 | } | |
368 | ||
369 | /* RFC 3280: | |
370 | * MUST: country, organization, organizational-unit, | |
371 | * distinguished name qualifier, state or province name, | |
372 | * common name, serial number. | |
373 | * SHOULD: locality, title, surname, given name, initials, | |
374 | * pseudonym, generation qualifier. | |
375 | * MUST: domainComponent (RFC 2247). | |
376 | */ | |
32b752ef | 377 | type = X509_NAME_ATTR_NOT_USED; |
6fc6879b JM |
378 | if (oid.len == 4 && |
379 | oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { | |
380 | /* id-at ::= 2.5.4 */ | |
381 | switch (oid.oid[3]) { | |
382 | case 3: | |
383 | /* commonName */ | |
32b752ef | 384 | type = X509_NAME_ATTR_CN; |
6fc6879b JM |
385 | break; |
386 | case 6: | |
387 | /* countryName */ | |
32b752ef | 388 | type = X509_NAME_ATTR_C; |
6fc6879b JM |
389 | break; |
390 | case 7: | |
391 | /* localityName */ | |
32b752ef | 392 | type = X509_NAME_ATTR_L; |
6fc6879b JM |
393 | break; |
394 | case 8: | |
395 | /* stateOrProvinceName */ | |
32b752ef | 396 | type = X509_NAME_ATTR_ST; |
6fc6879b JM |
397 | break; |
398 | case 10: | |
399 | /* organizationName */ | |
32b752ef | 400 | type = X509_NAME_ATTR_O; |
6fc6879b JM |
401 | break; |
402 | case 11: | |
403 | /* organizationalUnitName */ | |
32b752ef | 404 | type = X509_NAME_ATTR_OU; |
6fc6879b JM |
405 | break; |
406 | } | |
407 | } else if (oid.len == 7 && | |
408 | oid.oid[0] == 1 && oid.oid[1] == 2 && | |
409 | oid.oid[2] == 840 && oid.oid[3] == 113549 && | |
410 | oid.oid[4] == 1 && oid.oid[5] == 9 && | |
411 | oid.oid[6] == 1) { | |
412 | /* 1.2.840.113549.1.9.1 - e-mailAddress */ | |
32b752ef JM |
413 | os_free(name->email); |
414 | name->email = os_malloc(hdr.length + 1); | |
415 | if (name->email == NULL) { | |
416 | x509_free_name(name); | |
417 | return -1; | |
418 | } | |
419 | os_memcpy(name->email, hdr.payload, hdr.length); | |
420 | name->email[hdr.length] = '\0'; | |
421 | continue; | |
969b403f JM |
422 | } else if (oid.len == 7 && |
423 | oid.oid[0] == 0 && oid.oid[1] == 9 && | |
424 | oid.oid[2] == 2342 && oid.oid[3] == 19200300 && | |
425 | oid.oid[4] == 100 && oid.oid[5] == 1 && | |
426 | oid.oid[6] == 25) { | |
427 | /* 0.9.2342.19200300.100.1.25 - domainComponent */ | |
32b752ef | 428 | type = X509_NAME_ATTR_DC; |
6fc6879b JM |
429 | } |
430 | ||
32b752ef | 431 | if (type == X509_NAME_ATTR_NOT_USED) { |
6fc6879b JM |
432 | wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", |
433 | (u8 *) oid.oid, | |
434 | oid.len * sizeof(oid.oid[0])); | |
435 | wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data", | |
436 | hdr.payload, hdr.length); | |
437 | continue; | |
438 | } | |
439 | ||
32b752ef JM |
440 | if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { |
441 | wpa_printf(MSG_INFO, "X509: Too many Name attributes"); | |
6fc6879b JM |
442 | x509_free_name(name); |
443 | return -1; | |
444 | } | |
32b752ef JM |
445 | |
446 | val = os_malloc(hdr.length + 1); | |
447 | if (val == NULL) { | |
448 | x509_free_name(name); | |
449 | return -1; | |
450 | } | |
451 | os_memcpy(val, hdr.payload, hdr.length); | |
452 | val[hdr.length] = '\0'; | |
453 | if (os_strlen(val) != hdr.length) { | |
ad469aec JM |
454 | wpa_printf(MSG_INFO, "X509: Reject certificate with " |
455 | "embedded NUL byte in a string (%s[NUL])", | |
32b752ef | 456 | val); |
ad469aec JM |
457 | x509_free_name(name); |
458 | return -1; | |
459 | } | |
32b752ef JM |
460 | |
461 | name->attr[name->num_attr].type = type; | |
462 | name->attr[name->num_attr].value = val; | |
463 | name->num_attr++; | |
6fc6879b JM |
464 | } |
465 | ||
466 | return 0; | |
467 | } | |
468 | ||
469 | ||
32b752ef JM |
470 | static char * x509_name_attr_str(enum x509_name_attr_type type) |
471 | { | |
472 | switch (type) { | |
473 | case X509_NAME_ATTR_NOT_USED: | |
474 | return "[N/A]"; | |
475 | case X509_NAME_ATTR_DC: | |
476 | return "DC"; | |
477 | case X509_NAME_ATTR_CN: | |
478 | return "CN"; | |
479 | case X509_NAME_ATTR_C: | |
480 | return "C"; | |
481 | case X509_NAME_ATTR_L: | |
482 | return "L"; | |
483 | case X509_NAME_ATTR_ST: | |
484 | return "ST"; | |
485 | case X509_NAME_ATTR_O: | |
486 | return "O"; | |
487 | case X509_NAME_ATTR_OU: | |
488 | return "OU"; | |
489 | } | |
490 | return "?"; | |
491 | } | |
492 | ||
493 | ||
6fc6879b JM |
494 | /** |
495 | * x509_name_string - Convert an X.509 certificate name into a string | |
496 | * @name: Name to convert | |
497 | * @buf: Buffer for the string | |
498 | * @len: Maximum buffer length | |
499 | */ | |
500 | void x509_name_string(struct x509_name *name, char *buf, size_t len) | |
501 | { | |
502 | char *pos, *end; | |
503 | int ret; | |
32b752ef | 504 | size_t i; |
6fc6879b JM |
505 | |
506 | if (len == 0) | |
507 | return; | |
508 | ||
509 | pos = buf; | |
510 | end = buf + len; | |
511 | ||
32b752ef JM |
512 | for (i = 0; i < name->num_attr; i++) { |
513 | ret = os_snprintf(pos, end - pos, "%s=%s, ", | |
514 | x509_name_attr_str(name->attr[i].type), | |
515 | name->attr[i].value); | |
969b403f JM |
516 | if (ret < 0 || ret >= end - pos) |
517 | goto done; | |
518 | pos += ret; | |
519 | } | |
6fc6879b JM |
520 | |
521 | if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { | |
32b752ef JM |
522 | pos--; |
523 | *pos = '\0'; | |
524 | pos--; | |
525 | *pos = '\0'; | |
6fc6879b JM |
526 | } |
527 | ||
528 | if (name->email) { | |
529 | ret = os_snprintf(pos, end - pos, "/emailAddress=%s", | |
530 | name->email); | |
531 | if (ret < 0 || ret >= end - pos) | |
532 | goto done; | |
533 | pos += ret; | |
534 | } | |
535 | ||
536 | done: | |
537 | end[-1] = '\0'; | |
538 | } | |
539 | ||
540 | ||
541 | static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, | |
542 | os_time_t *val) | |
543 | { | |
544 | const char *pos; | |
545 | int year, month, day, hour, min, sec; | |
546 | ||
547 | /* | |
548 | * Time ::= CHOICE { | |
549 | * utcTime UTCTime, | |
550 | * generalTime GeneralizedTime | |
551 | * } | |
552 | * | |
553 | * UTCTime: YYMMDDHHMMSSZ | |
554 | * GeneralizedTime: YYYYMMDDHHMMSSZ | |
555 | */ | |
556 | ||
557 | pos = (const char *) buf; | |
558 | ||
559 | switch (asn1_tag) { | |
560 | case ASN1_TAG_UTCTIME: | |
561 | if (len != 13 || buf[12] != 'Z') { | |
562 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " | |
563 | "UTCTime format", buf, len); | |
564 | return -1; | |
565 | } | |
566 | if (sscanf(pos, "%02d", &year) != 1) { | |
567 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " | |
568 | "UTCTime year", buf, len); | |
569 | return -1; | |
570 | } | |
571 | if (year < 50) | |
572 | year += 2000; | |
573 | else | |
574 | year += 1900; | |
575 | pos += 2; | |
576 | break; | |
577 | case ASN1_TAG_GENERALIZEDTIME: | |
578 | if (len != 15 || buf[14] != 'Z') { | |
579 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " | |
580 | "GeneralizedTime format", buf, len); | |
581 | return -1; | |
582 | } | |
583 | if (sscanf(pos, "%04d", &year) != 1) { | |
584 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " | |
585 | "GeneralizedTime year", buf, len); | |
586 | return -1; | |
587 | } | |
588 | pos += 4; | |
589 | break; | |
590 | default: | |
591 | wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " | |
592 | "GeneralizedTime - found tag 0x%x", asn1_tag); | |
593 | return -1; | |
594 | } | |
595 | ||
596 | if (sscanf(pos, "%02d", &month) != 1) { | |
597 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | |
598 | "(month)", buf, len); | |
599 | return -1; | |
600 | } | |
601 | pos += 2; | |
602 | ||
603 | if (sscanf(pos, "%02d", &day) != 1) { | |
604 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | |
605 | "(day)", buf, len); | |
606 | return -1; | |
607 | } | |
608 | pos += 2; | |
609 | ||
610 | if (sscanf(pos, "%02d", &hour) != 1) { | |
611 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | |
612 | "(hour)", buf, len); | |
613 | return -1; | |
614 | } | |
615 | pos += 2; | |
616 | ||
617 | if (sscanf(pos, "%02d", &min) != 1) { | |
618 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | |
619 | "(min)", buf, len); | |
620 | return -1; | |
621 | } | |
622 | pos += 2; | |
623 | ||
624 | if (sscanf(pos, "%02d", &sec) != 1) { | |
625 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | |
626 | "(sec)", buf, len); | |
627 | return -1; | |
628 | } | |
629 | ||
630 | if (os_mktime(year, month, day, hour, min, sec, val) < 0) { | |
631 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", | |
632 | buf, len); | |
633 | if (year < 1970) { | |
634 | /* | |
635 | * At least some test certificates have been configured | |
636 | * to use dates prior to 1970. Set the date to | |
637 | * beginning of 1970 to handle these case. | |
638 | */ | |
639 | wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " | |
640 | "assume epoch as the time", year); | |
641 | *val = 0; | |
642 | return 0; | |
643 | } | |
644 | return -1; | |
645 | } | |
646 | ||
647 | return 0; | |
648 | } | |
649 | ||
650 | ||
651 | static int x509_parse_validity(const u8 *buf, size_t len, | |
652 | struct x509_certificate *cert, const u8 **next) | |
653 | { | |
654 | struct asn1_hdr hdr; | |
655 | const u8 *pos; | |
656 | size_t plen; | |
657 | ||
658 | /* | |
659 | * Validity ::= SEQUENCE { | |
660 | * notBefore Time, | |
661 | * notAfter Time | |
662 | * } | |
663 | * | |
664 | * RFC 3280, 4.1.2.5: | |
665 | * CAs conforming to this profile MUST always encode certificate | |
666 | * validity dates through the year 2049 as UTCTime; certificate | |
667 | * validity dates in 2050 or later MUST be encoded as GeneralizedTime. | |
668 | */ | |
669 | ||
670 | if (asn1_get_next(buf, len, &hdr) < 0 || | |
671 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
672 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
673 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " | |
674 | "(Validity) - found class %d tag 0x%x", | |
675 | hdr.class, hdr.tag); | |
676 | return -1; | |
677 | } | |
678 | pos = hdr.payload; | |
679 | plen = hdr.length; | |
680 | ||
681 | if (pos + plen > buf + len) | |
682 | return -1; | |
683 | ||
684 | *next = pos + plen; | |
685 | ||
686 | if (asn1_get_next(pos, plen, &hdr) < 0 || | |
687 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
688 | x509_parse_time(hdr.payload, hdr.length, hdr.tag, | |
689 | &cert->not_before) < 0) { | |
690 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " | |
691 | "Time", hdr.payload, hdr.length); | |
692 | return -1; | |
693 | } | |
694 | ||
695 | pos = hdr.payload + hdr.length; | |
696 | plen = *next - pos; | |
697 | ||
698 | if (asn1_get_next(pos, plen, &hdr) < 0 || | |
699 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
700 | x509_parse_time(hdr.payload, hdr.length, hdr.tag, | |
701 | &cert->not_after) < 0) { | |
702 | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " | |
703 | "Time", hdr.payload, hdr.length); | |
704 | return -1; | |
705 | } | |
706 | ||
707 | wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu", | |
708 | (unsigned long) cert->not_before, | |
709 | (unsigned long) cert->not_after); | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
714 | ||
715 | static int x509_id_ce_oid(struct asn1_oid *oid) | |
716 | { | |
717 | /* id-ce arc from X.509 for standard X.509v3 extensions */ | |
718 | return oid->len >= 4 && | |
719 | oid->oid[0] == 2 /* joint-iso-ccitt */ && | |
720 | oid->oid[1] == 5 /* ds */ && | |
721 | oid->oid[2] == 29 /* id-ce */; | |
722 | } | |
723 | ||
724 | ||
725 | static int x509_parse_ext_key_usage(struct x509_certificate *cert, | |
726 | const u8 *pos, size_t len) | |
727 | { | |
728 | struct asn1_hdr hdr; | |
729 | ||
730 | /* | |
731 | * KeyUsage ::= BIT STRING { | |
732 | * digitalSignature (0), | |
733 | * nonRepudiation (1), | |
734 | * keyEncipherment (2), | |
735 | * dataEncipherment (3), | |
736 | * keyAgreement (4), | |
737 | * keyCertSign (5), | |
738 | * cRLSign (6), | |
739 | * encipherOnly (7), | |
740 | * decipherOnly (8) } | |
741 | */ | |
742 | ||
743 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
744 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
745 | hdr.tag != ASN1_TAG_BITSTRING || | |
746 | hdr.length < 1) { | |
747 | wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in " | |
748 | "KeyUsage; found %d tag 0x%x len %d", | |
749 | hdr.class, hdr.tag, hdr.length); | |
750 | return -1; | |
751 | } | |
752 | ||
753 | cert->extensions_present |= X509_EXT_KEY_USAGE; | |
754 | cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); | |
755 | ||
756 | wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage); | |
757 | ||
758 | return 0; | |
759 | } | |
760 | ||
761 | ||
762 | static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, | |
763 | const u8 *pos, size_t len) | |
764 | { | |
765 | struct asn1_hdr hdr; | |
766 | unsigned long value; | |
767 | size_t left; | |
768 | ||
769 | /* | |
770 | * BasicConstraints ::= SEQUENCE { | |
771 | * cA BOOLEAN DEFAULT FALSE, | |
772 | * pathLenConstraint INTEGER (0..MAX) OPTIONAL } | |
773 | */ | |
774 | ||
775 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
776 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
777 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
778 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " | |
779 | "BasicConstraints; found %d tag 0x%x", | |
780 | hdr.class, hdr.tag); | |
781 | return -1; | |
782 | } | |
783 | ||
784 | cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS; | |
785 | ||
786 | if (hdr.length == 0) | |
787 | return 0; | |
788 | ||
789 | if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || | |
790 | hdr.class != ASN1_CLASS_UNIVERSAL) { | |
791 | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | |
792 | "BasicConstraints"); | |
793 | return -1; | |
794 | } | |
795 | ||
796 | if (hdr.tag == ASN1_TAG_BOOLEAN) { | |
797 | if (hdr.length != 1) { | |
798 | wpa_printf(MSG_DEBUG, "X509: Unexpected " | |
799 | "Boolean length (%u) in BasicConstraints", | |
800 | hdr.length); | |
801 | return -1; | |
802 | } | |
803 | cert->ca = hdr.payload[0]; | |
804 | ||
805 | if (hdr.payload + hdr.length == pos + len) { | |
806 | wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", | |
807 | cert->ca); | |
808 | return 0; | |
809 | } | |
810 | ||
811 | if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, | |
812 | &hdr) < 0 || | |
813 | hdr.class != ASN1_CLASS_UNIVERSAL) { | |
814 | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | |
815 | "BasicConstraints"); | |
816 | return -1; | |
817 | } | |
818 | } | |
819 | ||
820 | if (hdr.tag != ASN1_TAG_INTEGER) { | |
821 | wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in " | |
822 | "BasicConstraints; found class %d tag 0x%x", | |
823 | hdr.class, hdr.tag); | |
824 | return -1; | |
825 | } | |
826 | ||
827 | pos = hdr.payload; | |
828 | left = hdr.length; | |
829 | value = 0; | |
830 | while (left) { | |
831 | value <<= 8; | |
832 | value |= *pos++; | |
833 | left--; | |
834 | } | |
835 | ||
836 | cert->path_len_constraint = value; | |
837 | cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; | |
838 | ||
839 | wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " | |
840 | "pathLenConstraint=%lu", | |
841 | cert->ca, cert->path_len_constraint); | |
842 | ||
843 | return 0; | |
844 | } | |
845 | ||
846 | ||
efe22727 JM |
847 | static int x509_parse_alt_name_rfc8222(struct x509_name *name, |
848 | const u8 *pos, size_t len) | |
849 | { | |
850 | /* rfc822Name IA5String */ | |
851 | wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); | |
852 | os_free(name->alt_email); | |
853 | name->alt_email = os_zalloc(len + 1); | |
854 | if (name->alt_email == NULL) | |
855 | return -1; | |
856 | os_memcpy(name->alt_email, pos, len); | |
ad469aec JM |
857 | if (os_strlen(name->alt_email) != len) { |
858 | wpa_printf(MSG_INFO, "X509: Reject certificate with " | |
859 | "embedded NUL byte in rfc822Name (%s[NUL])", | |
860 | name->alt_email); | |
861 | os_free(name->alt_email); | |
862 | name->alt_email = NULL; | |
863 | return -1; | |
864 | } | |
efe22727 JM |
865 | return 0; |
866 | } | |
867 | ||
868 | ||
869 | static int x509_parse_alt_name_dns(struct x509_name *name, | |
870 | const u8 *pos, size_t len) | |
871 | { | |
872 | /* dNSName IA5String */ | |
873 | wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); | |
874 | os_free(name->dns); | |
875 | name->dns = os_zalloc(len + 1); | |
876 | if (name->dns == NULL) | |
877 | return -1; | |
878 | os_memcpy(name->dns, pos, len); | |
ad469aec JM |
879 | if (os_strlen(name->dns) != len) { |
880 | wpa_printf(MSG_INFO, "X509: Reject certificate with " | |
881 | "embedded NUL byte in dNSName (%s[NUL])", | |
882 | name->dns); | |
883 | os_free(name->dns); | |
884 | name->dns = NULL; | |
885 | return -1; | |
886 | } | |
efe22727 JM |
887 | return 0; |
888 | } | |
889 | ||
890 | ||
891 | static int x509_parse_alt_name_uri(struct x509_name *name, | |
892 | const u8 *pos, size_t len) | |
893 | { | |
894 | /* uniformResourceIdentifier IA5String */ | |
895 | wpa_hexdump_ascii(MSG_MSGDUMP, | |
896 | "X509: altName - uniformResourceIdentifier", | |
897 | pos, len); | |
898 | os_free(name->uri); | |
899 | name->uri = os_zalloc(len + 1); | |
900 | if (name->uri == NULL) | |
901 | return -1; | |
902 | os_memcpy(name->uri, pos, len); | |
ad469aec JM |
903 | if (os_strlen(name->uri) != len) { |
904 | wpa_printf(MSG_INFO, "X509: Reject certificate with " | |
905 | "embedded NUL byte in uniformResourceIdentifier " | |
906 | "(%s[NUL])", name->uri); | |
907 | os_free(name->uri); | |
908 | name->uri = NULL; | |
909 | return -1; | |
910 | } | |
efe22727 JM |
911 | return 0; |
912 | } | |
913 | ||
914 | ||
915 | static int x509_parse_alt_name_ip(struct x509_name *name, | |
916 | const u8 *pos, size_t len) | |
917 | { | |
918 | /* iPAddress OCTET STRING */ | |
919 | wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); | |
920 | os_free(name->ip); | |
921 | name->ip = os_malloc(len); | |
922 | if (name->ip == NULL) | |
923 | return -1; | |
924 | os_memcpy(name->ip, pos, len); | |
925 | name->ip_len = len; | |
926 | return 0; | |
927 | } | |
928 | ||
929 | ||
930 | static int x509_parse_alt_name_rid(struct x509_name *name, | |
931 | const u8 *pos, size_t len) | |
932 | { | |
933 | char buf[80]; | |
934 | ||
935 | /* registeredID OBJECT IDENTIFIER */ | |
936 | if (asn1_parse_oid(pos, len, &name->rid) < 0) | |
937 | return -1; | |
938 | ||
939 | asn1_oid_to_str(&name->rid, buf, sizeof(buf)); | |
940 | wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); | |
941 | ||
942 | return 0; | |
943 | } | |
944 | ||
945 | ||
946 | static int x509_parse_ext_alt_name(struct x509_name *name, | |
947 | const u8 *pos, size_t len) | |
948 | { | |
949 | struct asn1_hdr hdr; | |
950 | const u8 *p, *end; | |
951 | ||
952 | /* | |
953 | * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName | |
954 | * | |
955 | * GeneralName ::= CHOICE { | |
956 | * otherName [0] OtherName, | |
957 | * rfc822Name [1] IA5String, | |
958 | * dNSName [2] IA5String, | |
959 | * x400Address [3] ORAddress, | |
960 | * directoryName [4] Name, | |
961 | * ediPartyName [5] EDIPartyName, | |
962 | * uniformResourceIdentifier [6] IA5String, | |
963 | * iPAddress [7] OCTET STRING, | |
964 | * registeredID [8] OBJECT IDENTIFIER } | |
965 | * | |
966 | * OtherName ::= SEQUENCE { | |
967 | * type-id OBJECT IDENTIFIER, | |
968 | * value [0] EXPLICIT ANY DEFINED BY type-id } | |
969 | * | |
970 | * EDIPartyName ::= SEQUENCE { | |
971 | * nameAssigner [0] DirectoryString OPTIONAL, | |
972 | * partyName [1] DirectoryString } | |
973 | */ | |
974 | ||
975 | for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { | |
976 | int res; | |
977 | ||
978 | if (asn1_get_next(p, end - p, &hdr) < 0) { | |
979 | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | |
980 | "SubjectAltName item"); | |
981 | return -1; | |
982 | } | |
983 | ||
984 | if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) | |
985 | continue; | |
986 | ||
987 | switch (hdr.tag) { | |
988 | case 1: | |
989 | res = x509_parse_alt_name_rfc8222(name, hdr.payload, | |
990 | hdr.length); | |
991 | break; | |
992 | case 2: | |
993 | res = x509_parse_alt_name_dns(name, hdr.payload, | |
994 | hdr.length); | |
995 | break; | |
996 | case 6: | |
997 | res = x509_parse_alt_name_uri(name, hdr.payload, | |
998 | hdr.length); | |
999 | break; | |
1000 | case 7: | |
1001 | res = x509_parse_alt_name_ip(name, hdr.payload, | |
1002 | hdr.length); | |
1003 | break; | |
1004 | case 8: | |
1005 | res = x509_parse_alt_name_rid(name, hdr.payload, | |
1006 | hdr.length); | |
1007 | break; | |
1008 | case 0: /* TODO: otherName */ | |
1009 | case 3: /* TODO: x500Address */ | |
1010 | case 4: /* TODO: directoryName */ | |
1011 | case 5: /* TODO: ediPartyName */ | |
1012 | default: | |
1013 | res = 0; | |
1014 | break; | |
1015 | } | |
1016 | if (res < 0) | |
1017 | return res; | |
1018 | } | |
1019 | ||
1020 | return 0; | |
1021 | } | |
1022 | ||
1023 | ||
1024 | static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, | |
1025 | const u8 *pos, size_t len) | |
1026 | { | |
1027 | struct asn1_hdr hdr; | |
1028 | ||
1029 | /* SubjectAltName ::= GeneralNames */ | |
1030 | ||
1031 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
1032 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1033 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1034 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " | |
1035 | "SubjectAltName; found %d tag 0x%x", | |
1036 | hdr.class, hdr.tag); | |
1037 | return -1; | |
1038 | } | |
1039 | ||
1040 | wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); | |
1041 | cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME; | |
1042 | ||
1043 | if (hdr.length == 0) | |
1044 | return 0; | |
1045 | ||
1046 | return x509_parse_ext_alt_name(&cert->subject, hdr.payload, | |
1047 | hdr.length); | |
1048 | } | |
1049 | ||
1050 | ||
1051 | static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, | |
1052 | const u8 *pos, size_t len) | |
1053 | { | |
1054 | struct asn1_hdr hdr; | |
1055 | ||
1056 | /* IssuerAltName ::= GeneralNames */ | |
1057 | ||
1058 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
1059 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1060 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1061 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " | |
1062 | "IssuerAltName; found %d tag 0x%x", | |
1063 | hdr.class, hdr.tag); | |
1064 | return -1; | |
1065 | } | |
1066 | ||
1067 | wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); | |
1068 | cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME; | |
1069 | ||
1070 | if (hdr.length == 0) | |
1071 | return 0; | |
1072 | ||
1073 | return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, | |
1074 | hdr.length); | |
1075 | } | |
1076 | ||
1077 | ||
6fc6879b JM |
1078 | static int x509_parse_extension_data(struct x509_certificate *cert, |
1079 | struct asn1_oid *oid, | |
1080 | const u8 *pos, size_t len) | |
1081 | { | |
1082 | if (!x509_id_ce_oid(oid)) | |
1083 | return 1; | |
1084 | ||
1085 | /* TODO: add other extensions required by RFC 3280, Ch 4.2: | |
1086 | * certificate policies (section 4.2.1.5) | |
6fc6879b JM |
1087 | * name constraints (section 4.2.1.11) |
1088 | * policy constraints (section 4.2.1.12) | |
1089 | * extended key usage (section 4.2.1.13) | |
1090 | * inhibit any-policy (section 4.2.1.15) | |
1091 | */ | |
1092 | switch (oid->oid[3]) { | |
1093 | case 15: /* id-ce-keyUsage */ | |
1094 | return x509_parse_ext_key_usage(cert, pos, len); | |
efe22727 JM |
1095 | case 17: /* id-ce-subjectAltName */ |
1096 | return x509_parse_ext_subject_alt_name(cert, pos, len); | |
1097 | case 18: /* id-ce-issuerAltName */ | |
1098 | return x509_parse_ext_issuer_alt_name(cert, pos, len); | |
6fc6879b JM |
1099 | case 19: /* id-ce-basicConstraints */ |
1100 | return x509_parse_ext_basic_constraints(cert, pos, len); | |
1101 | default: | |
1102 | return 1; | |
1103 | } | |
1104 | } | |
1105 | ||
1106 | ||
1107 | static int x509_parse_extension(struct x509_certificate *cert, | |
1108 | const u8 *pos, size_t len, const u8 **next) | |
1109 | { | |
1110 | const u8 *end; | |
1111 | struct asn1_hdr hdr; | |
1112 | struct asn1_oid oid; | |
1113 | int critical_ext = 0, res; | |
1114 | char buf[80]; | |
1115 | ||
1116 | /* | |
1117 | * Extension ::= SEQUENCE { | |
1118 | * extnID OBJECT IDENTIFIER, | |
1119 | * critical BOOLEAN DEFAULT FALSE, | |
1120 | * extnValue OCTET STRING | |
1121 | * } | |
1122 | */ | |
1123 | ||
1124 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
1125 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1126 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1127 | wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " | |
1128 | "Extensions: class %d tag 0x%x; expected SEQUENCE", | |
1129 | hdr.class, hdr.tag); | |
1130 | return -1; | |
1131 | } | |
1132 | pos = hdr.payload; | |
1133 | *next = end = pos + hdr.length; | |
1134 | ||
1135 | if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { | |
1136 | wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " | |
1137 | "Extension (expected OID)"); | |
1138 | return -1; | |
1139 | } | |
1140 | ||
1141 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1142 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1143 | (hdr.tag != ASN1_TAG_BOOLEAN && | |
1144 | hdr.tag != ASN1_TAG_OCTETSTRING)) { | |
1145 | wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " | |
1146 | "Extensions: class %d tag 0x%x; expected BOOLEAN " | |
1147 | "or OCTET STRING", hdr.class, hdr.tag); | |
1148 | return -1; | |
1149 | } | |
1150 | ||
1151 | if (hdr.tag == ASN1_TAG_BOOLEAN) { | |
1152 | if (hdr.length != 1) { | |
1153 | wpa_printf(MSG_DEBUG, "X509: Unexpected " | |
1154 | "Boolean length (%u)", hdr.length); | |
1155 | return -1; | |
1156 | } | |
1157 | critical_ext = hdr.payload[0]; | |
1158 | pos = hdr.payload; | |
1159 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1160 | (hdr.class != ASN1_CLASS_UNIVERSAL && | |
1161 | hdr.class != ASN1_CLASS_PRIVATE) || | |
1162 | hdr.tag != ASN1_TAG_OCTETSTRING) { | |
1163 | wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " | |
1164 | "in Extensions: class %d tag 0x%x; " | |
1165 | "expected OCTET STRING", | |
1166 | hdr.class, hdr.tag); | |
1167 | return -1; | |
1168 | } | |
1169 | } | |
1170 | ||
1171 | asn1_oid_to_str(&oid, buf, sizeof(buf)); | |
1172 | wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d", | |
1173 | buf, critical_ext); | |
1174 | wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); | |
1175 | ||
1176 | res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length); | |
1177 | if (res < 0) | |
1178 | return res; | |
1179 | if (res == 1 && critical_ext) { | |
1180 | wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", | |
1181 | buf); | |
1182 | return -1; | |
1183 | } | |
1184 | ||
1185 | return 0; | |
1186 | } | |
1187 | ||
1188 | ||
1189 | static int x509_parse_extensions(struct x509_certificate *cert, | |
1190 | const u8 *pos, size_t len) | |
1191 | { | |
1192 | const u8 *end; | |
1193 | struct asn1_hdr hdr; | |
1194 | ||
1195 | /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ | |
1196 | ||
1197 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
1198 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1199 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1200 | wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " | |
1201 | "for Extensions: class %d tag 0x%x; " | |
1202 | "expected SEQUENCE", hdr.class, hdr.tag); | |
1203 | return -1; | |
1204 | } | |
1205 | ||
1206 | pos = hdr.payload; | |
1207 | end = pos + hdr.length; | |
1208 | ||
1209 | while (pos < end) { | |
1210 | if (x509_parse_extension(cert, pos, end - pos, &pos) | |
1211 | < 0) | |
1212 | return -1; | |
1213 | } | |
1214 | ||
1215 | return 0; | |
1216 | } | |
1217 | ||
1218 | ||
1219 | static int x509_parse_tbs_certificate(const u8 *buf, size_t len, | |
1220 | struct x509_certificate *cert, | |
1221 | const u8 **next) | |
1222 | { | |
1223 | struct asn1_hdr hdr; | |
1224 | const u8 *pos, *end; | |
1225 | size_t left; | |
1226 | char sbuf[128]; | |
1227 | unsigned long value; | |
1228 | ||
1229 | /* tbsCertificate TBSCertificate ::= SEQUENCE */ | |
1230 | if (asn1_get_next(buf, len, &hdr) < 0 || | |
1231 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1232 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1233 | wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " | |
1234 | "with a valid SEQUENCE - found class %d tag 0x%x", | |
1235 | hdr.class, hdr.tag); | |
1236 | return -1; | |
1237 | } | |
1238 | pos = hdr.payload; | |
1239 | end = *next = pos + hdr.length; | |
1240 | ||
1241 | /* | |
1242 | * version [0] EXPLICIT Version DEFAULT v1 | |
1243 | * Version ::= INTEGER { v1(0), v2(1), v3(2) } | |
1244 | */ | |
1245 | if (asn1_get_next(pos, end - pos, &hdr) < 0) | |
1246 | return -1; | |
1247 | pos = hdr.payload; | |
1248 | ||
1249 | if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { | |
1250 | if (asn1_get_next(pos, end - pos, &hdr) < 0) | |
1251 | return -1; | |
1252 | ||
1253 | if (hdr.class != ASN1_CLASS_UNIVERSAL || | |
1254 | hdr.tag != ASN1_TAG_INTEGER) { | |
1255 | wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " | |
1256 | "version field - found class %d tag 0x%x", | |
1257 | hdr.class, hdr.tag); | |
1258 | return -1; | |
1259 | } | |
1260 | if (hdr.length != 1) { | |
1261 | wpa_printf(MSG_DEBUG, "X509: Unexpected version field " | |
1262 | "length %u (expected 1)", hdr.length); | |
1263 | return -1; | |
1264 | } | |
1265 | pos = hdr.payload; | |
1266 | left = hdr.length; | |
1267 | value = 0; | |
1268 | while (left) { | |
1269 | value <<= 8; | |
1270 | value |= *pos++; | |
1271 | left--; | |
1272 | } | |
1273 | ||
1274 | cert->version = value; | |
1275 | if (cert->version != X509_CERT_V1 && | |
1276 | cert->version != X509_CERT_V2 && | |
1277 | cert->version != X509_CERT_V3) { | |
1278 | wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", | |
1279 | cert->version + 1); | |
1280 | return -1; | |
1281 | } | |
1282 | ||
1283 | if (asn1_get_next(pos, end - pos, &hdr) < 0) | |
1284 | return -1; | |
1285 | } else | |
1286 | cert->version = X509_CERT_V1; | |
1287 | wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); | |
1288 | ||
1289 | /* serialNumber CertificateSerialNumber ::= INTEGER */ | |
1290 | if (hdr.class != ASN1_CLASS_UNIVERSAL || | |
1291 | hdr.tag != ASN1_TAG_INTEGER) { | |
1292 | wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " | |
1293 | "serialNumber; class=%d tag=0x%x", | |
1294 | hdr.class, hdr.tag); | |
1295 | return -1; | |
1296 | } | |
1297 | ||
1298 | pos = hdr.payload; | |
1299 | left = hdr.length; | |
1300 | while (left) { | |
1301 | cert->serial_number <<= 8; | |
1302 | cert->serial_number |= *pos++; | |
1303 | left--; | |
1304 | } | |
1305 | wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); | |
1306 | ||
1307 | /* signature AlgorithmIdentifier */ | |
1308 | if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, | |
1309 | &pos)) | |
1310 | return -1; | |
1311 | ||
1312 | /* issuer Name */ | |
1313 | if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) | |
1314 | return -1; | |
1315 | x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); | |
1316 | wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf); | |
1317 | ||
1318 | /* validity Validity */ | |
1319 | if (x509_parse_validity(pos, end - pos, cert, &pos)) | |
1320 | return -1; | |
1321 | ||
1322 | /* subject Name */ | |
1323 | if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) | |
1324 | return -1; | |
1325 | x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); | |
1326 | wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); | |
1327 | ||
1328 | /* subjectPublicKeyInfo SubjectPublicKeyInfo */ | |
1329 | if (x509_parse_public_key(pos, end - pos, cert, &pos)) | |
1330 | return -1; | |
1331 | ||
1332 | if (pos == end) | |
1333 | return 0; | |
1334 | ||
1335 | if (cert->version == X509_CERT_V1) | |
1336 | return 0; | |
1337 | ||
1338 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1339 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { | |
1340 | wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" | |
1341 | " tag to parse optional tbsCertificate " | |
1342 | "field(s); parsed class %d tag 0x%x", | |
1343 | hdr.class, hdr.tag); | |
1344 | return -1; | |
1345 | } | |
1346 | ||
1347 | if (hdr.tag == 1) { | |
1348 | /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */ | |
1349 | wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); | |
1350 | /* TODO: parse UniqueIdentifier ::= BIT STRING */ | |
1351 | ||
1352 | if (hdr.payload + hdr.length == end) | |
1353 | return 0; | |
1354 | ||
1355 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1356 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { | |
1357 | wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" | |
1358 | " tag to parse optional tbsCertificate " | |
1359 | "field(s); parsed class %d tag 0x%x", | |
1360 | hdr.class, hdr.tag); | |
1361 | return -1; | |
1362 | } | |
1363 | } | |
1364 | ||
1365 | if (hdr.tag == 2) { | |
1366 | /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */ | |
1367 | wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); | |
1368 | /* TODO: parse UniqueIdentifier ::= BIT STRING */ | |
1369 | ||
1370 | if (hdr.payload + hdr.length == end) | |
1371 | return 0; | |
1372 | ||
1373 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1374 | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { | |
1375 | wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" | |
1376 | " tag to parse optional tbsCertificate " | |
1377 | "field(s); parsed class %d tag 0x%x", | |
1378 | hdr.class, hdr.tag); | |
1379 | return -1; | |
1380 | } | |
1381 | } | |
1382 | ||
1383 | if (hdr.tag != 3) { | |
1384 | wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " | |
1385 | "Context-Specific tag %d in optional " | |
1386 | "tbsCertificate fields", hdr.tag); | |
1387 | return 0; | |
1388 | } | |
1389 | ||
1390 | /* extensions [3] EXPLICIT Extensions OPTIONAL */ | |
1391 | ||
1392 | if (cert->version != X509_CERT_V3) { | |
1393 | wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " | |
1394 | "Extensions data which are only allowed for " | |
1395 | "version 3", cert->version + 1); | |
1396 | return -1; | |
1397 | } | |
1398 | ||
1399 | if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) | |
1400 | return -1; | |
1401 | ||
1402 | pos = hdr.payload + hdr.length; | |
1403 | if (pos < end) { | |
1404 | wpa_hexdump(MSG_DEBUG, | |
1405 | "X509: Ignored extra tbsCertificate data", | |
1406 | pos, end - pos); | |
1407 | } | |
1408 | ||
1409 | return 0; | |
1410 | } | |
1411 | ||
1412 | ||
1413 | static int x509_rsadsi_oid(struct asn1_oid *oid) | |
1414 | { | |
1415 | return oid->len >= 4 && | |
1416 | oid->oid[0] == 1 /* iso */ && | |
1417 | oid->oid[1] == 2 /* member-body */ && | |
1418 | oid->oid[2] == 840 /* us */ && | |
1419 | oid->oid[3] == 113549 /* rsadsi */; | |
1420 | } | |
1421 | ||
1422 | ||
1423 | static int x509_pkcs_oid(struct asn1_oid *oid) | |
1424 | { | |
1425 | return oid->len >= 5 && | |
1426 | x509_rsadsi_oid(oid) && | |
1427 | oid->oid[4] == 1 /* pkcs */; | |
1428 | } | |
1429 | ||
1430 | ||
1431 | static int x509_digest_oid(struct asn1_oid *oid) | |
1432 | { | |
1433 | return oid->len >= 5 && | |
1434 | x509_rsadsi_oid(oid) && | |
1435 | oid->oid[4] == 2 /* digestAlgorithm */; | |
1436 | } | |
1437 | ||
1438 | ||
1439 | static int x509_sha1_oid(struct asn1_oid *oid) | |
1440 | { | |
1441 | return oid->len == 6 && | |
1442 | oid->oid[0] == 1 /* iso */ && | |
1443 | oid->oid[1] == 3 /* identified-organization */ && | |
1444 | oid->oid[2] == 14 /* oiw */ && | |
1445 | oid->oid[3] == 3 /* secsig */ && | |
1446 | oid->oid[4] == 2 /* algorithms */ && | |
1447 | oid->oid[5] == 26 /* id-sha1 */; | |
1448 | } | |
1449 | ||
1450 | ||
1d8ce433 JM |
1451 | static int x509_sha256_oid(struct asn1_oid *oid) |
1452 | { | |
1453 | return oid->len == 9 && | |
1454 | oid->oid[0] == 2 /* joint-iso-itu-t */ && | |
1455 | oid->oid[1] == 16 /* country */ && | |
1456 | oid->oid[2] == 840 /* us */ && | |
1457 | oid->oid[3] == 1 /* organization */ && | |
1458 | oid->oid[4] == 101 /* gov */ && | |
1459 | oid->oid[5] == 3 /* csor */ && | |
1460 | oid->oid[6] == 4 /* nistAlgorithm */ && | |
1461 | oid->oid[7] == 2 /* hashAlgs */ && | |
1462 | oid->oid[8] == 1 /* sha256 */; | |
1463 | } | |
1464 | ||
1465 | ||
6fc6879b JM |
1466 | /** |
1467 | * x509_certificate_parse - Parse a X.509 certificate in DER format | |
1468 | * @buf: Pointer to the X.509 certificate in DER format | |
1469 | * @len: Buffer length | |
1470 | * Returns: Pointer to the parsed certificate or %NULL on failure | |
1471 | * | |
1472 | * Caller is responsible for freeing the returned certificate by calling | |
1473 | * x509_certificate_free(). | |
1474 | */ | |
1475 | struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) | |
1476 | { | |
1477 | struct asn1_hdr hdr; | |
1478 | const u8 *pos, *end, *hash_start; | |
1479 | struct x509_certificate *cert; | |
1480 | ||
1481 | cert = os_zalloc(sizeof(*cert) + len); | |
1482 | if (cert == NULL) | |
1483 | return NULL; | |
1484 | os_memcpy(cert + 1, buf, len); | |
1485 | cert->cert_start = (u8 *) (cert + 1); | |
1486 | cert->cert_len = len; | |
1487 | ||
1488 | pos = buf; | |
1489 | end = buf + len; | |
1490 | ||
1491 | /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ | |
1492 | ||
1493 | /* Certificate ::= SEQUENCE */ | |
1494 | if (asn1_get_next(pos, len, &hdr) < 0 || | |
1495 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1496 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1497 | wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " | |
1498 | "a valid SEQUENCE - found class %d tag 0x%x", | |
1499 | hdr.class, hdr.tag); | |
1500 | x509_certificate_free(cert); | |
1501 | return NULL; | |
1502 | } | |
1503 | pos = hdr.payload; | |
1504 | ||
1505 | if (pos + hdr.length > end) { | |
1506 | x509_certificate_free(cert); | |
1507 | return NULL; | |
1508 | } | |
1509 | ||
1510 | if (pos + hdr.length < end) { | |
1511 | wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " | |
1512 | "encoded certificate", | |
1513 | pos + hdr.length, end - pos + hdr.length); | |
1514 | end = pos + hdr.length; | |
1515 | } | |
1516 | ||
1517 | hash_start = pos; | |
1518 | cert->tbs_cert_start = cert->cert_start + (hash_start - buf); | |
1519 | if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) { | |
1520 | x509_certificate_free(cert); | |
1521 | return NULL; | |
1522 | } | |
1523 | cert->tbs_cert_len = pos - hash_start; | |
1524 | ||
1525 | /* signatureAlgorithm AlgorithmIdentifier */ | |
1526 | if (x509_parse_algorithm_identifier(pos, end - pos, | |
1527 | &cert->signature_alg, &pos)) { | |
1528 | x509_certificate_free(cert); | |
1529 | return NULL; | |
1530 | } | |
1531 | ||
1532 | /* signatureValue BIT STRING */ | |
1533 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1534 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1535 | hdr.tag != ASN1_TAG_BITSTRING) { | |
1536 | wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " | |
1537 | "(signatureValue) - found class %d tag 0x%x", | |
1538 | hdr.class, hdr.tag); | |
1539 | x509_certificate_free(cert); | |
1540 | return NULL; | |
1541 | } | |
1542 | if (hdr.length < 1) { | |
1543 | x509_certificate_free(cert); | |
1544 | return NULL; | |
1545 | } | |
1546 | pos = hdr.payload; | |
1547 | if (*pos) { | |
1548 | wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", | |
1549 | *pos); | |
1550 | /* PKCS #1 v1.5 10.2.1: | |
1551 | * It is an error if the length in bits of the signature S is | |
1552 | * not a multiple of eight. | |
1553 | */ | |
1554 | x509_certificate_free(cert); | |
1555 | return NULL; | |
1556 | } | |
1557 | os_free(cert->sign_value); | |
1558 | cert->sign_value = os_malloc(hdr.length - 1); | |
1559 | if (cert->sign_value == NULL) { | |
1560 | wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " | |
1561 | "signatureValue"); | |
1562 | x509_certificate_free(cert); | |
1563 | return NULL; | |
1564 | } | |
1565 | os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); | |
1566 | cert->sign_value_len = hdr.length - 1; | |
1567 | wpa_hexdump(MSG_MSGDUMP, "X509: signature", | |
1568 | cert->sign_value, cert->sign_value_len); | |
1569 | ||
1570 | return cert; | |
1571 | } | |
1572 | ||
1573 | ||
1574 | /** | |
1575 | * x509_certificate_check_signature - Verify certificate signature | |
1576 | * @issuer: Issuer certificate | |
1577 | * @cert: Certificate to be verified | |
1578 | * Returns: 0 if cert has a valid signature that was signed by the issuer, | |
1579 | * -1 if not | |
1580 | */ | |
1581 | int x509_certificate_check_signature(struct x509_certificate *issuer, | |
1582 | struct x509_certificate *cert) | |
1583 | { | |
1584 | struct crypto_public_key *pk; | |
1585 | u8 *data; | |
1586 | const u8 *pos, *end, *next, *da_end; | |
1587 | size_t data_len; | |
1588 | struct asn1_hdr hdr; | |
1589 | struct asn1_oid oid; | |
1d8ce433 | 1590 | u8 hash[32]; |
6fc6879b JM |
1591 | size_t hash_len; |
1592 | ||
1593 | if (!x509_pkcs_oid(&cert->signature.oid) || | |
1594 | cert->signature.oid.len != 7 || | |
1595 | cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { | |
1596 | wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " | |
1597 | "algorithm"); | |
1598 | return -1; | |
1599 | } | |
1600 | ||
1601 | pk = crypto_public_key_import(issuer->public_key, | |
1602 | issuer->public_key_len); | |
1603 | if (pk == NULL) | |
1604 | return -1; | |
1605 | ||
1606 | data_len = cert->sign_value_len; | |
1607 | data = os_malloc(data_len); | |
1608 | if (data == NULL) { | |
1609 | crypto_public_key_free(pk); | |
1610 | return -1; | |
1611 | } | |
1612 | ||
1613 | if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, | |
1614 | cert->sign_value_len, data, | |
1615 | &data_len) < 0) { | |
1616 | wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); | |
1617 | crypto_public_key_free(pk); | |
1618 | os_free(data); | |
1619 | return -1; | |
1620 | } | |
1621 | crypto_public_key_free(pk); | |
1622 | ||
1623 | wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); | |
1624 | ||
1625 | /* | |
1626 | * PKCS #1 v1.5, 10.1.2: | |
1627 | * | |
1628 | * DigestInfo ::= SEQUENCE { | |
1629 | * digestAlgorithm DigestAlgorithmIdentifier, | |
1630 | * digest Digest | |
1631 | * } | |
1632 | * | |
1633 | * DigestAlgorithmIdentifier ::= AlgorithmIdentifier | |
1634 | * | |
1635 | * Digest ::= OCTET STRING | |
1636 | * | |
1637 | */ | |
1638 | if (asn1_get_next(data, data_len, &hdr) < 0 || | |
1639 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1640 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1641 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " | |
1642 | "(DigestInfo) - found class %d tag 0x%x", | |
1643 | hdr.class, hdr.tag); | |
1644 | os_free(data); | |
1645 | return -1; | |
1646 | } | |
1647 | ||
1648 | pos = hdr.payload; | |
1649 | end = pos + hdr.length; | |
1650 | ||
1651 | /* | |
1652 | * X.509: | |
1653 | * AlgorithmIdentifier ::= SEQUENCE { | |
1654 | * algorithm OBJECT IDENTIFIER, | |
1655 | * parameters ANY DEFINED BY algorithm OPTIONAL | |
1656 | * } | |
1657 | */ | |
1658 | ||
1659 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1660 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1661 | hdr.tag != ASN1_TAG_SEQUENCE) { | |
1662 | wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " | |
1663 | "(AlgorithmIdentifier) - found class %d tag 0x%x", | |
1664 | hdr.class, hdr.tag); | |
1665 | os_free(data); | |
1666 | return -1; | |
1667 | } | |
1668 | da_end = hdr.payload + hdr.length; | |
1669 | ||
1670 | if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { | |
1671 | wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); | |
1672 | os_free(data); | |
1673 | return -1; | |
1674 | } | |
1675 | ||
1676 | if (x509_sha1_oid(&oid)) { | |
1677 | if (cert->signature.oid.oid[6] != | |
1678 | 5 /* sha-1WithRSAEncryption */) { | |
1679 | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " | |
1680 | "does not match with certificate " | |
1681 | "signatureAlgorithm (%lu)", | |
1682 | cert->signature.oid.oid[6]); | |
1683 | os_free(data); | |
1684 | return -1; | |
1685 | } | |
1686 | goto skip_digest_oid; | |
1687 | } | |
1688 | ||
1d8ce433 JM |
1689 | if (x509_sha256_oid(&oid)) { |
1690 | if (cert->signature.oid.oid[6] != | |
1691 | 11 /* sha2561WithRSAEncryption */) { | |
1692 | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " | |
1693 | "does not match with certificate " | |
1694 | "signatureAlgorithm (%lu)", | |
1695 | cert->signature.oid.oid[6]); | |
1696 | os_free(data); | |
1697 | return -1; | |
1698 | } | |
1699 | goto skip_digest_oid; | |
1700 | } | |
1701 | ||
6fc6879b JM |
1702 | if (!x509_digest_oid(&oid)) { |
1703 | wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); | |
1704 | os_free(data); | |
1705 | return -1; | |
1706 | } | |
1707 | switch (oid.oid[5]) { | |
1708 | case 5: /* md5 */ | |
1709 | if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) | |
1710 | { | |
1711 | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " | |
1712 | "not match with certificate " | |
1713 | "signatureAlgorithm (%lu)", | |
1714 | cert->signature.oid.oid[6]); | |
1715 | os_free(data); | |
1716 | return -1; | |
1717 | } | |
1718 | break; | |
1719 | case 2: /* md2 */ | |
1720 | case 4: /* md4 */ | |
1721 | default: | |
1722 | wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " | |
1723 | "(%lu)", oid.oid[5]); | |
1724 | os_free(data); | |
1725 | return -1; | |
1726 | } | |
1727 | ||
1728 | skip_digest_oid: | |
1729 | /* Digest ::= OCTET STRING */ | |
1730 | pos = da_end; | |
1731 | end = data + data_len; | |
1732 | ||
1733 | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | |
1734 | hdr.class != ASN1_CLASS_UNIVERSAL || | |
1735 | hdr.tag != ASN1_TAG_OCTETSTRING) { | |
1736 | wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " | |
1737 | "(Digest) - found class %d tag 0x%x", | |
1738 | hdr.class, hdr.tag); | |
1739 | os_free(data); | |
1740 | return -1; | |
1741 | } | |
1742 | wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", | |
1743 | hdr.payload, hdr.length); | |
1744 | ||
1745 | switch (cert->signature.oid.oid[6]) { | |
1746 | case 4: /* md5WithRSAEncryption */ | |
1747 | md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, | |
1748 | hash); | |
1749 | hash_len = 16; | |
1750 | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", | |
1751 | hash, hash_len); | |
1752 | break; | |
1753 | case 5: /* sha-1WithRSAEncryption */ | |
1754 | sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, | |
1755 | hash); | |
1756 | hash_len = 20; | |
1757 | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", | |
1758 | hash, hash_len); | |
1759 | break; | |
6fc6879b | 1760 | case 11: /* sha256WithRSAEncryption */ |
1d8ce433 JM |
1761 | sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, |
1762 | hash); | |
1763 | hash_len = 32; | |
1764 | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", | |
1765 | hash, hash_len); | |
1766 | break; | |
1d8ce433 | 1767 | case 2: /* md2WithRSAEncryption */ |
6fc6879b JM |
1768 | case 12: /* sha384WithRSAEncryption */ |
1769 | case 13: /* sha512WithRSAEncryption */ | |
1770 | default: | |
1771 | wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " | |
1772 | "algorithm (%lu)", cert->signature.oid.oid[6]); | |
1773 | os_free(data); | |
1774 | return -1; | |
1775 | } | |
1776 | ||
1777 | if (hdr.length != hash_len || | |
1778 | os_memcmp(hdr.payload, hash, hdr.length) != 0) { | |
1779 | wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " | |
1780 | "with calculated tbsCertificate hash"); | |
1781 | os_free(data); | |
1782 | return -1; | |
1783 | } | |
1784 | ||
1785 | os_free(data); | |
1786 | ||
1787 | wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " | |
1788 | "calculated tbsCertificate hash"); | |
1789 | ||
1790 | return 0; | |
1791 | } | |
1792 | ||
1793 | ||
1794 | static int x509_valid_issuer(const struct x509_certificate *cert) | |
1795 | { | |
1796 | if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) && | |
1797 | !cert->ca) { | |
1798 | wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " | |
1799 | "issuer"); | |
1800 | return -1; | |
1801 | } | |
1802 | ||
1803 | if (cert->version == X509_CERT_V3 && | |
1804 | !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { | |
1805 | wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " | |
1806 | "include BasicConstraints extension"); | |
1807 | return -1; | |
1808 | } | |
1809 | ||
1810 | if ((cert->extensions_present & X509_EXT_KEY_USAGE) && | |
1811 | !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { | |
1812 | wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " | |
1813 | "keyCertSign bit in Key Usage"); | |
1814 | return -1; | |
1815 | } | |
1816 | ||
1817 | return 0; | |
1818 | } | |
1819 | ||
1820 | ||
1821 | /** | |
1822 | * x509_certificate_chain_validate - Validate X.509 certificate chain | |
1823 | * @trusted: List of trusted certificates | |
1824 | * @chain: Certificate chain to be validated (first chain must be issued by | |
1825 | * signed by the second certificate in the chain and so on) | |
1826 | * @reason: Buffer for returning failure reason (X509_VALIDATE_*) | |
1827 | * Returns: 0 if chain is valid, -1 if not | |
1828 | */ | |
1829 | int x509_certificate_chain_validate(struct x509_certificate *trusted, | |
1830 | struct x509_certificate *chain, | |
235279e7 | 1831 | int *reason, int disable_time_checks) |
6fc6879b JM |
1832 | { |
1833 | long unsigned idx; | |
1834 | int chain_trusted = 0; | |
1835 | struct x509_certificate *cert, *trust; | |
1836 | char buf[128]; | |
1837 | struct os_time now; | |
1838 | ||
1839 | *reason = X509_VALIDATE_OK; | |
1840 | ||
1841 | wpa_printf(MSG_DEBUG, "X509: Validate certificate chain"); | |
1842 | os_get_time(&now); | |
1843 | ||
1844 | for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { | |
1845 | x509_name_string(&cert->subject, buf, sizeof(buf)); | |
1846 | wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); | |
1847 | ||
1848 | if (chain_trusted) | |
1849 | continue; | |
1850 | ||
235279e7 JM |
1851 | if (!disable_time_checks && |
1852 | ((unsigned long) now.sec < | |
1853 | (unsigned long) cert->not_before || | |
1854 | (unsigned long) now.sec > | |
1855 | (unsigned long) cert->not_after)) { | |
6fc6879b JM |
1856 | wpa_printf(MSG_INFO, "X509: Certificate not valid " |
1857 | "(now=%lu not_before=%lu not_after=%lu)", | |
1858 | now.sec, cert->not_before, cert->not_after); | |
1859 | *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; | |
1860 | return -1; | |
1861 | } | |
1862 | ||
1863 | if (cert->next) { | |
1864 | if (x509_name_compare(&cert->issuer, | |
1865 | &cert->next->subject) != 0) { | |
1866 | wpa_printf(MSG_DEBUG, "X509: Certificate " | |
1867 | "chain issuer name mismatch"); | |
1868 | x509_name_string(&cert->issuer, buf, | |
1869 | sizeof(buf)); | |
1870 | wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", | |
1871 | buf); | |
1872 | x509_name_string(&cert->next->subject, buf, | |
1873 | sizeof(buf)); | |
1874 | wpa_printf(MSG_DEBUG, "X509: next cert " | |
1875 | "subject: %s", buf); | |
1876 | *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; | |
1877 | return -1; | |
1878 | } | |
1879 | ||
1880 | if (x509_valid_issuer(cert->next) < 0) { | |
1881 | *reason = X509_VALIDATE_BAD_CERTIFICATE; | |
1882 | return -1; | |
1883 | } | |
1884 | ||
1885 | if ((cert->next->extensions_present & | |
1886 | X509_EXT_PATH_LEN_CONSTRAINT) && | |
1887 | idx > cert->next->path_len_constraint) { | |
1888 | wpa_printf(MSG_DEBUG, "X509: pathLenConstraint" | |
1889 | " not met (idx=%lu issuer " | |
1890 | "pathLenConstraint=%lu)", idx, | |
1891 | cert->next->path_len_constraint); | |
1892 | *reason = X509_VALIDATE_BAD_CERTIFICATE; | |
1893 | return -1; | |
1894 | } | |
1895 | ||
1896 | if (x509_certificate_check_signature(cert->next, cert) | |
1897 | < 0) { | |
1898 | wpa_printf(MSG_DEBUG, "X509: Invalid " | |
1899 | "certificate signature within " | |
1900 | "chain"); | |
1901 | *reason = X509_VALIDATE_BAD_CERTIFICATE; | |
1902 | return -1; | |
1903 | } | |
1904 | } | |
1905 | ||
1906 | for (trust = trusted; trust; trust = trust->next) { | |
1907 | if (x509_name_compare(&cert->issuer, &trust->subject) | |
1908 | == 0) | |
1909 | break; | |
1910 | } | |
1911 | ||
1912 | if (trust) { | |
1913 | wpa_printf(MSG_DEBUG, "X509: Found issuer from the " | |
1914 | "list of trusted certificates"); | |
1915 | if (x509_valid_issuer(trust) < 0) { | |
1916 | *reason = X509_VALIDATE_BAD_CERTIFICATE; | |
1917 | return -1; | |
1918 | } | |
1919 | ||
1920 | if (x509_certificate_check_signature(trust, cert) < 0) | |
1921 | { | |
1922 | wpa_printf(MSG_DEBUG, "X509: Invalid " | |
1923 | "certificate signature"); | |
1924 | *reason = X509_VALIDATE_BAD_CERTIFICATE; | |
1925 | return -1; | |
1926 | } | |
1927 | ||
1928 | wpa_printf(MSG_DEBUG, "X509: Trusted certificate " | |
1929 | "found to complete the chain"); | |
1930 | chain_trusted = 1; | |
1931 | } | |
1932 | } | |
1933 | ||
1934 | if (!chain_trusted) { | |
1935 | wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers " | |
1936 | "from the list of trusted certificates"); | |
1937 | if (trusted) { | |
1938 | *reason = X509_VALIDATE_UNKNOWN_CA; | |
1939 | return -1; | |
1940 | } | |
1941 | wpa_printf(MSG_DEBUG, "X509: Certificate chain validation " | |
1942 | "disabled - ignore unknown CA issue"); | |
1943 | } | |
1944 | ||
1945 | wpa_printf(MSG_DEBUG, "X509: Certificate chain valid"); | |
1946 | ||
1947 | return 0; | |
1948 | } | |
1949 | ||
1950 | ||
1951 | /** | |
1952 | * x509_certificate_get_subject - Get a certificate based on Subject name | |
1953 | * @chain: Certificate chain to search through | |
1954 | * @name: Subject name to search for | |
1955 | * Returns: Pointer to the certificate with the given Subject name or | |
1956 | * %NULL on failure | |
1957 | */ | |
1958 | struct x509_certificate * | |
1959 | x509_certificate_get_subject(struct x509_certificate *chain, | |
1960 | struct x509_name *name) | |
1961 | { | |
1962 | struct x509_certificate *cert; | |
1963 | ||
1964 | for (cert = chain; cert; cert = cert->next) { | |
1965 | if (x509_name_compare(&cert->subject, name) == 0) | |
1966 | return cert; | |
1967 | } | |
1968 | return NULL; | |
1969 | } | |
1970 | ||
1971 | ||
1972 | /** | |
1973 | * x509_certificate_self_signed - Is the certificate self-signed? | |
1974 | * @cert: Certificate | |
1975 | * Returns: 1 if certificate is self-signed, 0 if not | |
1976 | */ | |
1977 | int x509_certificate_self_signed(struct x509_certificate *cert) | |
1978 | { | |
1979 | return x509_name_compare(&cert->issuer, &cert->subject) == 0; | |
1980 | } |