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