x509v3.c

Go to the documentation of this file.
00001 
00016 #include "includes.h"
00017 
00018 #include "common.h"
00019 
00020 #ifdef CONFIG_INTERNAL_X509
00021 
00022 #include "asn1.h"
00023 #include "crypto.h"
00024 #include "x509v3.h"
00025 
00026 
00027 static void x509_free_name(struct x509_name *name)
00028 {
00029         os_free(name->cn);
00030         os_free(name->c);
00031         os_free(name->l);
00032         os_free(name->st);
00033         os_free(name->o);
00034         os_free(name->ou);
00035         os_free(name->email);
00036         name->cn = name->c = name->l = name->st = name->o = name->ou = NULL;
00037         name->email = NULL;
00038 
00039         os_free(name->alt_email);
00040         os_free(name->dns);
00041         os_free(name->uri);
00042         os_free(name->ip);
00043         name->alt_email = name->dns = name->uri = NULL;
00044         name->ip = NULL;
00045         name->ip_len = 0;
00046         os_memset(&name->rid, 0, sizeof(name->rid));
00047 }
00048 
00049 
00055 void x509_certificate_free(struct x509_certificate *cert)
00056 {
00057         if (cert == NULL)
00058                 return;
00059         if (cert->next) {
00060                 wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
00061                            "was still on a list (next=%p)\n",
00062                            cert, cert->next);
00063         }
00064         x509_free_name(&cert->issuer);
00065         x509_free_name(&cert->subject);
00066         os_free(cert->public_key);
00067         os_free(cert->sign_value);
00068         os_free(cert);
00069 }
00070 
00071 
00077 void x509_certificate_chain_free(struct x509_certificate *cert)
00078 {
00079         struct x509_certificate *next;
00080 
00081         while (cert) {
00082                 next = cert->next;
00083                 cert->next = NULL;
00084                 x509_certificate_free(cert);
00085                 cert = next;
00086         }
00087 }
00088 
00089 
00090 static int x509_whitespace(char c)
00091 {
00092         return c == ' ' || c == '\t';
00093 }
00094 
00095 
00096 static void x509_str_strip_whitespace(char *a)
00097 {
00098         char *ipos, *opos;
00099         int remove_whitespace = 1;
00100 
00101         ipos = opos = a;
00102 
00103         while (*ipos) {
00104                 if (remove_whitespace && x509_whitespace(*ipos))
00105                         ipos++;
00106                 else {
00107                         remove_whitespace = x509_whitespace(*ipos);
00108                         *opos++ = *ipos++;
00109                 }
00110         }
00111 
00112         *opos-- = '\0';
00113         if (opos > a && x509_whitespace(*opos))
00114                 *opos = '\0';
00115 }
00116 
00117 
00118 static int x509_str_compare(const char *a, const char *b)
00119 {
00120         char *aa, *bb;
00121         int ret;
00122 
00123         if (!a && b)
00124                 return -1;
00125         if (a && !b)
00126                 return 1;
00127         if (!a && !b)
00128                 return 0;
00129 
00130         aa = os_strdup(a);
00131         bb = os_strdup(b);
00132 
00133         if (aa == NULL || bb == NULL) {
00134                 os_free(aa);
00135                 os_free(bb);
00136                 return os_strcasecmp(a, b);
00137         }
00138 
00139         x509_str_strip_whitespace(aa);
00140         x509_str_strip_whitespace(bb);
00141 
00142         ret = os_strcasecmp(aa, bb);
00143 
00144         os_free(aa);
00145         os_free(bb);
00146 
00147         return ret;
00148 }
00149 
00150 
00159 int x509_name_compare(struct x509_name *a, struct x509_name *b)
00160 {
00161         int res;
00162 
00163         if (!a && b)
00164                 return -1;
00165         if (a && !b)
00166                 return 1;
00167         if (!a && !b)
00168                 return 0;
00169 
00170         res = x509_str_compare(a->cn, b->cn);
00171         if (res)
00172                 return res;
00173         res = x509_str_compare(a->c, b->c);
00174         if (res)
00175                 return res;
00176         res = x509_str_compare(a->l, b->l);
00177         if (res)
00178                 return res;
00179         res = x509_str_compare(a->st, b->st);
00180         if (res)
00181                 return res;
00182         res = x509_str_compare(a->o, b->o);
00183         if (res)
00184                 return res;
00185         res = x509_str_compare(a->ou, b->ou);
00186         if (res)
00187                 return res;
00188         res = x509_str_compare(a->email, b->email);
00189         if (res)
00190                 return res;
00191 
00192         return 0;
00193 }
00194 
00195 
00196 static int x509_parse_algorithm_identifier(
00197         const u8 *buf, size_t len,
00198         struct x509_algorithm_identifier *id, const u8 **next)
00199 {
00200         struct asn1_hdr hdr;
00201         const u8 *pos, *end;
00202 
00203         /*
00204          * AlgorithmIdentifier ::= SEQUENCE {
00205          *     algorithm            OBJECT IDENTIFIER,
00206          *     parameters           ANY DEFINED BY algorithm OPTIONAL
00207          * }
00208          */
00209 
00210         if (asn1_get_next(buf, len, &hdr) < 0 ||
00211             hdr.class != ASN1_CLASS_UNIVERSAL ||
00212             hdr.tag != ASN1_TAG_SEQUENCE) {
00213                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
00214                            "(AlgorithmIdentifier) - found class %d tag 0x%x",
00215                            hdr.class, hdr.tag);
00216                 return -1;
00217         }
00218         pos = hdr.payload;
00219         end = pos + hdr.length;
00220 
00221         if (end > buf + len)
00222                 return -1;
00223 
00224         *next = end;
00225 
00226         if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
00227                 return -1;
00228 
00229         /* TODO: optional parameters */
00230 
00231         return 0;
00232 }
00233 
00234 
00235 static int x509_parse_public_key(const u8 *buf, size_t len,
00236                                  struct x509_certificate *cert,
00237                                  const u8 **next)
00238 {
00239         struct asn1_hdr hdr;
00240         const u8 *pos, *end;
00241 
00242         /*
00243          * SubjectPublicKeyInfo ::= SEQUENCE {
00244          *     algorithm            AlgorithmIdentifier,
00245          *     subjectPublicKey     BIT STRING
00246          * }
00247          */
00248 
00249         pos = buf;
00250         end = buf + len;
00251 
00252         if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
00253             hdr.class != ASN1_CLASS_UNIVERSAL ||
00254             hdr.tag != ASN1_TAG_SEQUENCE) {
00255                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
00256                            "(SubjectPublicKeyInfo) - found class %d tag 0x%x",
00257                            hdr.class, hdr.tag);
00258                 return -1;
00259         }
00260         pos = hdr.payload;
00261 
00262         if (pos + hdr.length > end)
00263                 return -1;
00264         end = pos + hdr.length;
00265         *next = end;
00266 
00267         if (x509_parse_algorithm_identifier(pos, end - pos,
00268                                             &cert->public_key_alg, &pos))
00269                 return -1;
00270 
00271         if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
00272             hdr.class != ASN1_CLASS_UNIVERSAL ||
00273             hdr.tag != ASN1_TAG_BITSTRING) {
00274                 wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
00275                            "(subjectPublicKey) - found class %d tag 0x%x",
00276                            hdr.class, hdr.tag);
00277                 return -1;
00278         }
00279         if (hdr.length < 1)
00280                 return -1;
00281         pos = hdr.payload;
00282         if (*pos) {
00283                 wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
00284                            *pos);
00285                 /*
00286                  * TODO: should this be rejected? X.509 certificates are
00287                  * unlikely to use such a construction. Now we would end up
00288                  * including the extra bits in the buffer which may also be
00289                  * ok.
00290                  */
00291         }
00292         os_free(cert->public_key);
00293         cert->public_key = os_malloc(hdr.length - 1);
00294         if (cert->public_key == NULL) {
00295                 wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
00296                            "public key");
00297                 return -1;
00298         }
00299         os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
00300         cert->public_key_len = hdr.length - 1;
00301         wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
00302                     cert->public_key, cert->public_key_len);
00303 
00304         return 0;
00305 }
00306 
00307 
00308 static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
00309                            const u8 **next)
00310 {
00311         struct asn1_hdr hdr;
00312         const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
00313         struct asn1_oid oid;
00314         char **fieldp;
00315 
00316         /*
00317          * Name ::= CHOICE { RDNSequence }
00318          * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
00319          * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
00320          * AttributeTypeAndValue ::= SEQUENCE {
00321          *     type     AttributeType,
00322          *     value    AttributeValue
00323          * }
00324          * AttributeType ::= OBJECT IDENTIFIER
00325          * AttributeValue ::= ANY DEFINED BY AttributeType
00326          */
00327 
00328         if (asn1_get_next(buf, len, &hdr) < 0 ||
00329             hdr.class != ASN1_CLASS_UNIVERSAL ||
00330             hdr.tag != ASN1_TAG_SEQUENCE) {
00331                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
00332                            "(Name / RDNSequencer) - found class %d tag 0x%x",
00333                            hdr.class, hdr.tag);
00334                 return -1;
00335         }
00336         pos = hdr.payload;
00337 
00338         if (pos + hdr.length > buf + len)
00339                 return -1;
00340 
00341         end = *next = pos + hdr.length;
00342 
00343         while (pos < end) {
00344                 if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
00345                     hdr.class != ASN1_CLASS_UNIVERSAL ||
00346                     hdr.tag != ASN1_TAG_SET) {
00347                         wpa_printf(MSG_DEBUG, "X509: Expected SET "
00348                                    "(RelativeDistinguishedName) - found class "
00349                                    "%d tag 0x%x", hdr.class, hdr.tag);
00350                         x509_free_name(name);
00351                         return -1;
00352                 }
00353 
00354                 set_pos = hdr.payload;
00355                 pos = set_end = hdr.payload + hdr.length;
00356 
00357                 if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
00358                     hdr.class != ASN1_CLASS_UNIVERSAL ||
00359                     hdr.tag != ASN1_TAG_SEQUENCE) {
00360                         wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
00361                                    "(AttributeTypeAndValue) - found class %d "
00362                                    "tag 0x%x", hdr.class, hdr.tag);
00363                         x509_free_name(name);
00364                         return -1;
00365                 }
00366 
00367                 seq_pos = hdr.payload;
00368                 seq_end = hdr.payload + hdr.length;
00369 
00370                 if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
00371                         x509_free_name(name);
00372                         return -1;
00373                 }
00374 
00375                 if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
00376                     hdr.class != ASN1_CLASS_UNIVERSAL) {
00377                         wpa_printf(MSG_DEBUG, "X509: Failed to parse "
00378                                    "AttributeValue");
00379                         x509_free_name(name);
00380                         return -1;
00381                 }
00382 
00383                 /* RFC 3280:
00384                  * MUST: country, organization, organizational-unit,
00385                  * distinguished name qualifier, state or province name,
00386                  * common name, serial number.
00387                  * SHOULD: locality, title, surname, given name, initials,
00388                  * pseudonym, generation qualifier.
00389                  * MUST: domainComponent (RFC 2247).
00390                  */
00391                 fieldp = NULL;
00392                 if (oid.len == 4 &&
00393                     oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
00394                         /* id-at ::= 2.5.4 */
00395                         switch (oid.oid[3]) {
00396                         case 3:
00397                                 /* commonName */
00398                                 fieldp = &name->cn;
00399                                 break;
00400                         case 6:
00401                                 /*  countryName */
00402                                 fieldp = &name->c;
00403                                 break;
00404                         case 7:
00405                                 /* localityName */
00406                                 fieldp = &name->l;
00407                                 break;
00408                         case 8:
00409                                 /* stateOrProvinceName */
00410                                 fieldp = &name->st;
00411                                 break;
00412                         case 10:
00413                                 /* organizationName */
00414                                 fieldp = &name->o;
00415                                 break;
00416                         case 11:
00417                                 /* organizationalUnitName */
00418                                 fieldp = &name->ou;
00419                                 break;
00420                         }
00421                 } else if (oid.len == 7 &&
00422                            oid.oid[0] == 1 && oid.oid[1] == 2 &&
00423                            oid.oid[2] == 840 && oid.oid[3] == 113549 &&
00424                            oid.oid[4] == 1 && oid.oid[5] == 9 &&
00425                            oid.oid[6] == 1) {
00426                         /* 1.2.840.113549.1.9.1 - e-mailAddress */
00427                         fieldp = &name->email;
00428                 }
00429 
00430                 if (fieldp == NULL) {
00431                         wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
00432                                     (u8 *) oid.oid,
00433                                     oid.len * sizeof(oid.oid[0]));
00434                         wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
00435                                           hdr.payload, hdr.length);
00436                         continue;
00437                 }
00438 
00439                 os_free(*fieldp);
00440                 *fieldp = os_malloc(hdr.length + 1);
00441                 if (*fieldp == NULL) {
00442                         x509_free_name(name);
00443                         return -1;
00444                 }
00445                 os_memcpy(*fieldp, hdr.payload, hdr.length);
00446                 (*fieldp)[hdr.length] = '\0';
00447                 if (os_strlen(*fieldp) != hdr.length) {
00448                         wpa_printf(MSG_INFO, "X509: Reject certificate with "
00449                                    "embedded NUL byte in a string (%s[NUL])",
00450                                    *fieldp);
00451                         x509_free_name(name);
00452                         return -1;
00453                 }
00454         }
00455 
00456         return 0;
00457 }
00458 
00459 
00467 void x509_name_string(struct x509_name *name, char *buf, size_t len)
00468 {
00469         char *pos, *end;
00470         int ret;
00471 
00472         if (len == 0)
00473                 return;
00474 
00475         pos = buf;
00476         end = buf + len;
00477 
00478         if (name->c) {
00479                 ret = os_snprintf(pos, end - pos, "C=%s, ", name->c);
00480                 if (ret < 0 || ret >= end - pos)
00481                         goto done;
00482                 pos += ret;
00483         }
00484         if (name->st) {
00485                 ret = os_snprintf(pos, end - pos, "ST=%s, ", name->st);
00486                 if (ret < 0 || ret >= end - pos)
00487                         goto done;
00488                 pos += ret;
00489         }
00490         if (name->l) {
00491                 ret = os_snprintf(pos, end - pos, "L=%s, ", name->l);
00492                 if (ret < 0 || ret >= end - pos)
00493                         goto done;
00494                 pos += ret;
00495         }
00496         if (name->o) {
00497                 ret = os_snprintf(pos, end - pos, "O=%s, ", name->o);
00498                 if (ret < 0 || ret >= end - pos)
00499                         goto done;
00500                 pos += ret;
00501         }
00502         if (name->ou) {
00503                 ret = os_snprintf(pos, end - pos, "OU=%s, ", name->ou);
00504                 if (ret < 0 || ret >= end - pos)
00505                         goto done;
00506                 pos += ret;
00507         }
00508         if (name->cn) {
00509                 ret = os_snprintf(pos, end - pos, "CN=%s, ", name->cn);
00510                 if (ret < 0 || ret >= end - pos)
00511                         goto done;
00512                 pos += ret;
00513         }
00514 
00515         if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
00516                 *pos-- = '\0';
00517                 *pos-- = '\0';
00518         }
00519 
00520         if (name->email) {
00521                 ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
00522                                   name->email);
00523                 if (ret < 0 || ret >= end - pos)
00524                         goto done;
00525                 pos += ret;
00526         }
00527 
00528 done:
00529         end[-1] = '\0';
00530 }
00531 
00532 
00533 static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
00534                            os_time_t *val)
00535 {
00536         const char *pos;
00537         int year, month, day, hour, min, sec;
00538 
00539         /*
00540          * Time ::= CHOICE {
00541          *     utcTime        UTCTime,
00542          *     generalTime    GeneralizedTime
00543          * }
00544          *
00545          * UTCTime: YYMMDDHHMMSSZ
00546          * GeneralizedTime: YYYYMMDDHHMMSSZ
00547          */
00548 
00549         pos = (const char *) buf;
00550 
00551         switch (asn1_tag) {
00552         case ASN1_TAG_UTCTIME:
00553                 if (len != 13 || buf[12] != 'Z') {
00554                         wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
00555                                           "UTCTime format", buf, len);
00556                         return -1;
00557                 }
00558                 if (sscanf(pos, "%02d", &year) != 1) {
00559                         wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
00560                                           "UTCTime year", buf, len);
00561                         return -1;
00562                 }
00563                 if (year < 50)
00564                         year += 2000;
00565                 else
00566                         year += 1900;
00567                 pos += 2;
00568                 break;
00569         case ASN1_TAG_GENERALIZEDTIME:
00570                 if (len != 15 || buf[14] != 'Z') {
00571                         wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
00572                                           "GeneralizedTime format", buf, len);
00573                         return -1;
00574                 }
00575                 if (sscanf(pos, "%04d", &year) != 1) {
00576                         wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
00577                                           "GeneralizedTime year", buf, len);
00578                         return -1;
00579                 }
00580                 pos += 4;
00581                 break;
00582         default:
00583                 wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
00584                            "GeneralizedTime - found tag 0x%x", asn1_tag);
00585                 return -1;
00586         }
00587 
00588         if (sscanf(pos, "%02d", &month) != 1) {
00589                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
00590                                   "(month)", buf, len);
00591                 return -1;
00592         }
00593         pos += 2;
00594 
00595         if (sscanf(pos, "%02d", &day) != 1) {
00596                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
00597                                   "(day)", buf, len);
00598                 return -1;
00599         }
00600         pos += 2;
00601 
00602         if (sscanf(pos, "%02d", &hour) != 1) {
00603                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
00604                                   "(hour)", buf, len);
00605                 return -1;
00606         }
00607         pos += 2;
00608 
00609         if (sscanf(pos, "%02d", &min) != 1) {
00610                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
00611                                   "(min)", buf, len);
00612                 return -1;
00613         }
00614         pos += 2;
00615 
00616         if (sscanf(pos, "%02d", &sec) != 1) {
00617                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
00618                                   "(sec)", buf, len);
00619                 return -1;
00620         }
00621 
00622         if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
00623                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
00624                                   buf, len);
00625                 if (year < 1970) {
00626                         /*
00627                          * At least some test certificates have been configured
00628                          * to use dates prior to 1970. Set the date to
00629                          * beginning of 1970 to handle these case.
00630                          */
00631                         wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
00632                                    "assume epoch as the time", year);
00633                         *val = 0;
00634                         return 0;
00635                 }
00636                 return -1;
00637         }
00638 
00639         return 0;
00640 }
00641 
00642 
00643 static int x509_parse_validity(const u8 *buf, size_t len,
00644                                struct x509_certificate *cert, const u8 **next)
00645 {
00646         struct asn1_hdr hdr;
00647         const u8 *pos;
00648         size_t plen;
00649 
00650         /*
00651          * Validity ::= SEQUENCE {
00652          *     notBefore      Time,
00653          *     notAfter       Time
00654          * }
00655          *
00656          * RFC 3280, 4.1.2.5:
00657          * CAs conforming to this profile MUST always encode certificate
00658          * validity dates through the year 2049 as UTCTime; certificate
00659          * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
00660          */
00661 
00662         if (asn1_get_next(buf, len, &hdr) < 0 ||
00663             hdr.class != ASN1_CLASS_UNIVERSAL ||
00664             hdr.tag != ASN1_TAG_SEQUENCE) {
00665                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
00666                            "(Validity) - found class %d tag 0x%x",
00667                            hdr.class, hdr.tag);
00668                 return -1;
00669         }
00670         pos = hdr.payload;
00671         plen = hdr.length;
00672 
00673         if (pos + plen > buf + len)
00674                 return -1;
00675 
00676         *next = pos + plen;
00677 
00678         if (asn1_get_next(pos, plen, &hdr) < 0 ||
00679             hdr.class != ASN1_CLASS_UNIVERSAL ||
00680             x509_parse_time(hdr.payload, hdr.length, hdr.tag,
00681                             &cert->not_before) < 0) {
00682                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
00683                                   "Time", hdr.payload, hdr.length);
00684                 return -1;
00685         }
00686 
00687         pos = hdr.payload + hdr.length;
00688         plen = *next - pos;
00689 
00690         if (asn1_get_next(pos, plen, &hdr) < 0 ||
00691             hdr.class != ASN1_CLASS_UNIVERSAL ||
00692             x509_parse_time(hdr.payload, hdr.length, hdr.tag,
00693                             &cert->not_after) < 0) {
00694                 wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
00695                                   "Time", hdr.payload, hdr.length);
00696                 return -1;
00697         }
00698 
00699         wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu",
00700                    (unsigned long) cert->not_before,
00701                    (unsigned long) cert->not_after);
00702 
00703         return 0;
00704 }
00705 
00706 
00707 static int x509_id_ce_oid(struct asn1_oid *oid)
00708 {
00709         /* id-ce arc from X.509 for standard X.509v3 extensions */
00710         return oid->len >= 4 &&
00711                 oid->oid[0] == 2 /* joint-iso-ccitt */ &&
00712                 oid->oid[1] == 5 /* ds */ &&
00713                 oid->oid[2] == 29 /* id-ce */;
00714 }
00715 
00716 
00717 static int x509_parse_ext_key_usage(struct x509_certificate *cert,
00718                                     const u8 *pos, size_t len)
00719 {
00720         struct asn1_hdr hdr;
00721 
00722         /*
00723          * KeyUsage ::= BIT STRING {
00724          *     digitalSignature        (0),
00725          *     nonRepudiation          (1),
00726          *     keyEncipherment         (2),
00727          *     dataEncipherment        (3),
00728          *     keyAgreement            (4),
00729          *     keyCertSign             (5),
00730          *     cRLSign                 (6),
00731          *     encipherOnly            (7),
00732          *     decipherOnly            (8) }
00733          */
00734 
00735         if (asn1_get_next(pos, len, &hdr) < 0 ||
00736             hdr.class != ASN1_CLASS_UNIVERSAL ||
00737             hdr.tag != ASN1_TAG_BITSTRING ||
00738             hdr.length < 1) {
00739                 wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in "
00740                            "KeyUsage; found %d tag 0x%x len %d",
00741                            hdr.class, hdr.tag, hdr.length);
00742                 return -1;
00743         }
00744 
00745         cert->extensions_present |= X509_EXT_KEY_USAGE;
00746         cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
00747 
00748         wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
00749 
00750         return 0;
00751 }
00752 
00753 
00754 static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
00755                                             const u8 *pos, size_t len)
00756 {
00757         struct asn1_hdr hdr;
00758         unsigned long value;
00759         size_t left;
00760 
00761         /*
00762          * BasicConstraints ::= SEQUENCE {
00763          * cA                      BOOLEAN DEFAULT FALSE,
00764          * pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
00765          */
00766 
00767         if (asn1_get_next(pos, len, &hdr) < 0 ||
00768             hdr.class != ASN1_CLASS_UNIVERSAL ||
00769             hdr.tag != ASN1_TAG_SEQUENCE) {
00770                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
00771                            "BasicConstraints; found %d tag 0x%x",
00772                            hdr.class, hdr.tag);
00773                 return -1;
00774         }
00775 
00776         cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
00777 
00778         if (hdr.length == 0)
00779                 return 0;
00780 
00781         if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
00782             hdr.class != ASN1_CLASS_UNIVERSAL) {
00783                 wpa_printf(MSG_DEBUG, "X509: Failed to parse "
00784                            "BasicConstraints");
00785                 return -1;
00786         }
00787 
00788         if (hdr.tag == ASN1_TAG_BOOLEAN) {
00789                 if (hdr.length != 1) {
00790                         wpa_printf(MSG_DEBUG, "X509: Unexpected "
00791                                    "Boolean length (%u) in BasicConstraints",
00792                                    hdr.length);
00793                         return -1;
00794                 }
00795                 cert->ca = hdr.payload[0];
00796 
00797                 if (hdr.payload + hdr.length == pos + len) {
00798                         wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
00799                                    cert->ca);
00800                         return 0;
00801                 }
00802 
00803                 if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
00804                                   &hdr) < 0 ||
00805                     hdr.class != ASN1_CLASS_UNIVERSAL) {
00806                         wpa_printf(MSG_DEBUG, "X509: Failed to parse "
00807                                    "BasicConstraints");
00808                         return -1;
00809                 }
00810         }
00811 
00812         if (hdr.tag != ASN1_TAG_INTEGER) {
00813                 wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in "
00814                            "BasicConstraints; found class %d tag 0x%x",
00815                            hdr.class, hdr.tag);
00816                 return -1;
00817         }
00818 
00819         pos = hdr.payload;
00820         left = hdr.length;
00821         value = 0;
00822         while (left) {
00823                 value <<= 8;
00824                 value |= *pos++;
00825                 left--;
00826         }
00827 
00828         cert->path_len_constraint = value;
00829         cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
00830 
00831         wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
00832                    "pathLenConstraint=%lu",
00833                    cert->ca, cert->path_len_constraint);
00834 
00835         return 0;
00836 }
00837 
00838 
00839 static int x509_parse_alt_name_rfc8222(struct x509_name *name,
00840                                        const u8 *pos, size_t len)
00841 {
00842         /* rfc822Name IA5String */
00843         wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
00844         os_free(name->alt_email);
00845         name->alt_email = os_zalloc(len + 1);
00846         if (name->alt_email == NULL)
00847                 return -1;
00848         os_memcpy(name->alt_email, pos, len);
00849         if (os_strlen(name->alt_email) != len) {
00850                 wpa_printf(MSG_INFO, "X509: Reject certificate with "
00851                            "embedded NUL byte in rfc822Name (%s[NUL])",
00852                            name->alt_email);
00853                 os_free(name->alt_email);
00854                 name->alt_email = NULL;
00855                 return -1;
00856         }
00857         return 0;
00858 }
00859 
00860 
00861 static int x509_parse_alt_name_dns(struct x509_name *name,
00862                                    const u8 *pos, size_t len)
00863 {
00864         /* dNSName IA5String */
00865         wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
00866         os_free(name->dns);
00867         name->dns = os_zalloc(len + 1);
00868         if (name->dns == NULL)
00869                 return -1;
00870         os_memcpy(name->dns, pos, len);
00871         if (os_strlen(name->dns) != len) {
00872                 wpa_printf(MSG_INFO, "X509: Reject certificate with "
00873                            "embedded NUL byte in dNSName (%s[NUL])",
00874                            name->dns);
00875                 os_free(name->dns);
00876                 name->dns = NULL;
00877                 return -1;
00878         }
00879         return 0;
00880 }
00881 
00882 
00883 static int x509_parse_alt_name_uri(struct x509_name *name,
00884                                    const u8 *pos, size_t len)
00885 {
00886         /* uniformResourceIdentifier IA5String */
00887         wpa_hexdump_ascii(MSG_MSGDUMP,
00888                           "X509: altName - uniformResourceIdentifier",
00889                           pos, len);
00890         os_free(name->uri);
00891         name->uri = os_zalloc(len + 1);
00892         if (name->uri == NULL)
00893                 return -1;
00894         os_memcpy(name->uri, pos, len);
00895         if (os_strlen(name->uri) != len) {
00896                 wpa_printf(MSG_INFO, "X509: Reject certificate with "
00897                            "embedded NUL byte in uniformResourceIdentifier "
00898                            "(%s[NUL])", name->uri);
00899                 os_free(name->uri);
00900                 name->uri = NULL;
00901                 return -1;
00902         }
00903         return 0;
00904 }
00905 
00906 
00907 static int x509_parse_alt_name_ip(struct x509_name *name,
00908                                        const u8 *pos, size_t len)
00909 {
00910         /* iPAddress OCTET STRING */
00911         wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
00912         os_free(name->ip);
00913         name->ip = os_malloc(len);
00914         if (name->ip == NULL)
00915                 return -1;
00916         os_memcpy(name->ip, pos, len);
00917         name->ip_len = len;
00918         return 0;
00919 }
00920 
00921 
00922 static int x509_parse_alt_name_rid(struct x509_name *name,
00923                                    const u8 *pos, size_t len)
00924 {
00925         char buf[80];
00926 
00927         /* registeredID OBJECT IDENTIFIER */
00928         if (asn1_parse_oid(pos, len, &name->rid) < 0)
00929                 return -1;
00930 
00931         asn1_oid_to_str(&name->rid, buf, sizeof(buf));
00932         wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf);
00933 
00934         return 0;
00935 }
00936 
00937 
00938 static int x509_parse_ext_alt_name(struct x509_name *name,
00939                                    const u8 *pos, size_t len)
00940 {
00941         struct asn1_hdr hdr;
00942         const u8 *p, *end;
00943 
00944         /*
00945          * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
00946          *
00947          * GeneralName ::= CHOICE {
00948          *     otherName                       [0]     OtherName,
00949          *     rfc822Name                      [1]     IA5String,
00950          *     dNSName                         [2]     IA5String,
00951          *     x400Address                     [3]     ORAddress,
00952          *     directoryName                   [4]     Name,
00953          *     ediPartyName                    [5]     EDIPartyName,
00954          *     uniformResourceIdentifier       [6]     IA5String,
00955          *     iPAddress                       [7]     OCTET STRING,
00956          *     registeredID                    [8]     OBJECT IDENTIFIER }
00957          *
00958          * OtherName ::= SEQUENCE {
00959          *     type-id    OBJECT IDENTIFIER,
00960          *     value      [0] EXPLICIT ANY DEFINED BY type-id }
00961          *
00962          * EDIPartyName ::= SEQUENCE {
00963          *     nameAssigner            [0]     DirectoryString OPTIONAL,
00964          *     partyName               [1]     DirectoryString }
00965          */
00966 
00967         for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
00968                 int res;
00969 
00970                 if (asn1_get_next(p, end - p, &hdr) < 0) {
00971                         wpa_printf(MSG_DEBUG, "X509: Failed to parse "
00972                                    "SubjectAltName item");
00973                         return -1;
00974                 }
00975 
00976                 if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
00977                         continue;
00978 
00979                 switch (hdr.tag) {
00980                 case 1:
00981                         res = x509_parse_alt_name_rfc8222(name, hdr.payload,
00982                                                           hdr.length);
00983                         break;
00984                 case 2:
00985                         res = x509_parse_alt_name_dns(name, hdr.payload,
00986                                                       hdr.length);
00987                         break;
00988                 case 6:
00989                         res = x509_parse_alt_name_uri(name, hdr.payload,
00990                                                       hdr.length);
00991                         break;
00992                 case 7:
00993                         res = x509_parse_alt_name_ip(name, hdr.payload,
00994                                                      hdr.length);
00995                         break;
00996                 case 8:
00997                         res = x509_parse_alt_name_rid(name, hdr.payload,
00998                                                       hdr.length);
00999                         break;
01000                 case 0: /* TODO: otherName */
01001                 case 3: /* TODO: x500Address */
01002                 case 4: /* TODO: directoryName */
01003                 case 5: /* TODO: ediPartyName */
01004                 default:
01005                         res = 0;
01006                         break;
01007                 }
01008                 if (res < 0)
01009                         return res;
01010         }
01011 
01012         return 0;
01013 }
01014 
01015 
01016 static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
01017                                            const u8 *pos, size_t len)
01018 {
01019         struct asn1_hdr hdr;
01020 
01021         /* SubjectAltName ::= GeneralNames */
01022 
01023         if (asn1_get_next(pos, len, &hdr) < 0 ||
01024             hdr.class != ASN1_CLASS_UNIVERSAL ||
01025             hdr.tag != ASN1_TAG_SEQUENCE) {
01026                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
01027                            "SubjectAltName; found %d tag 0x%x",
01028                            hdr.class, hdr.tag);
01029                 return -1;
01030         }
01031 
01032         wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
01033         cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
01034 
01035         if (hdr.length == 0)
01036                 return 0;
01037 
01038         return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
01039                                        hdr.length);
01040 }
01041 
01042 
01043 static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
01044                                           const u8 *pos, size_t len)
01045 {
01046         struct asn1_hdr hdr;
01047 
01048         /* IssuerAltName ::= GeneralNames */
01049 
01050         if (asn1_get_next(pos, len, &hdr) < 0 ||
01051             hdr.class != ASN1_CLASS_UNIVERSAL ||
01052             hdr.tag != ASN1_TAG_SEQUENCE) {
01053                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
01054                            "IssuerAltName; found %d tag 0x%x",
01055                            hdr.class, hdr.tag);
01056                 return -1;
01057         }
01058 
01059         wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
01060         cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
01061 
01062         if (hdr.length == 0)
01063                 return 0;
01064 
01065         return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
01066                                        hdr.length);
01067 }
01068 
01069 
01070 static int x509_parse_extension_data(struct x509_certificate *cert,
01071                                      struct asn1_oid *oid,
01072                                      const u8 *pos, size_t len)
01073 {
01074         if (!x509_id_ce_oid(oid))
01075                 return 1;
01076 
01077         /* TODO: add other extensions required by RFC 3280, Ch 4.2:
01078          * certificate policies (section 4.2.1.5)
01079          * name constraints (section 4.2.1.11)
01080          * policy constraints (section 4.2.1.12)
01081          * extended key usage (section 4.2.1.13)
01082          * inhibit any-policy (section 4.2.1.15)
01083          */
01084         switch (oid->oid[3]) {
01085         case 15: /* id-ce-keyUsage */
01086                 return x509_parse_ext_key_usage(cert, pos, len);
01087         case 17: /* id-ce-subjectAltName */
01088                 return x509_parse_ext_subject_alt_name(cert, pos, len);
01089         case 18: /* id-ce-issuerAltName */
01090                 return x509_parse_ext_issuer_alt_name(cert, pos, len);
01091         case 19: /* id-ce-basicConstraints */
01092                 return x509_parse_ext_basic_constraints(cert, pos, len);
01093         default:
01094                 return 1;
01095         }
01096 }
01097 
01098 
01099 static int x509_parse_extension(struct x509_certificate *cert,
01100                                 const u8 *pos, size_t len, const u8 **next)
01101 {
01102         const u8 *end;
01103         struct asn1_hdr hdr;
01104         struct asn1_oid oid;
01105         int critical_ext = 0, res;
01106         char buf[80];
01107 
01108         /*
01109          * Extension  ::=  SEQUENCE  {
01110          *     extnID      OBJECT IDENTIFIER,
01111          *     critical    BOOLEAN DEFAULT FALSE,
01112          *     extnValue   OCTET STRING
01113          * }
01114          */
01115 
01116         if (asn1_get_next(pos, len, &hdr) < 0 ||
01117             hdr.class != ASN1_CLASS_UNIVERSAL ||
01118             hdr.tag != ASN1_TAG_SEQUENCE) {
01119                 wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
01120                            "Extensions: class %d tag 0x%x; expected SEQUENCE",
01121                            hdr.class, hdr.tag);
01122                 return -1;
01123         }
01124         pos = hdr.payload;
01125         *next = end = pos + hdr.length;
01126 
01127         if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
01128                 wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
01129                            "Extension (expected OID)");
01130                 return -1;
01131         }
01132 
01133         if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01134             hdr.class != ASN1_CLASS_UNIVERSAL ||
01135             (hdr.tag != ASN1_TAG_BOOLEAN &&
01136              hdr.tag != ASN1_TAG_OCTETSTRING)) {
01137                 wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
01138                            "Extensions: class %d tag 0x%x; expected BOOLEAN "
01139                            "or OCTET STRING", hdr.class, hdr.tag);
01140                 return -1;
01141         }
01142 
01143         if (hdr.tag == ASN1_TAG_BOOLEAN) {
01144                 if (hdr.length != 1) {
01145                         wpa_printf(MSG_DEBUG, "X509: Unexpected "
01146                                    "Boolean length (%u)", hdr.length);
01147                         return -1;
01148                 }
01149                 critical_ext = hdr.payload[0];
01150                 pos = hdr.payload;
01151                 if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01152                     (hdr.class != ASN1_CLASS_UNIVERSAL &&
01153                      hdr.class != ASN1_CLASS_PRIVATE) ||
01154                     hdr.tag != ASN1_TAG_OCTETSTRING) {
01155                         wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header "
01156                                    "in Extensions: class %d tag 0x%x; "
01157                                    "expected OCTET STRING",
01158                                    hdr.class, hdr.tag);
01159                         return -1;
01160                 }
01161         }
01162 
01163         asn1_oid_to_str(&oid, buf, sizeof(buf));
01164         wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
01165                    buf, critical_ext);
01166         wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
01167 
01168         res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
01169         if (res < 0)
01170                 return res;
01171         if (res == 1 && critical_ext) {
01172                 wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
01173                            buf);
01174                 return -1;
01175         }
01176 
01177         return 0;
01178 }
01179 
01180 
01181 static int x509_parse_extensions(struct x509_certificate *cert,
01182                                  const u8 *pos, size_t len)
01183 {
01184         const u8 *end;
01185         struct asn1_hdr hdr;
01186 
01187         /* Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension */
01188 
01189         if (asn1_get_next(pos, len, &hdr) < 0 ||
01190             hdr.class != ASN1_CLASS_UNIVERSAL ||
01191             hdr.tag != ASN1_TAG_SEQUENCE) {
01192                 wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data "
01193                            "for Extensions: class %d tag 0x%x; "
01194                            "expected SEQUENCE", hdr.class, hdr.tag);
01195                 return -1;
01196         }
01197 
01198         pos = hdr.payload;
01199         end = pos + hdr.length;
01200 
01201         while (pos < end) {
01202                 if (x509_parse_extension(cert, pos, end - pos, &pos)
01203                     < 0)
01204                         return -1;
01205         }
01206 
01207         return 0;
01208 }
01209 
01210 
01211 static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
01212                                       struct x509_certificate *cert,
01213                                       const u8 **next)
01214 {
01215         struct asn1_hdr hdr;
01216         const u8 *pos, *end;
01217         size_t left;
01218         char sbuf[128];
01219         unsigned long value;
01220 
01221         /* tbsCertificate TBSCertificate ::= SEQUENCE */
01222         if (asn1_get_next(buf, len, &hdr) < 0 ||
01223             hdr.class != ASN1_CLASS_UNIVERSAL ||
01224             hdr.tag != ASN1_TAG_SEQUENCE) {
01225                 wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start "
01226                            "with a valid SEQUENCE - found class %d tag 0x%x",
01227                            hdr.class, hdr.tag);
01228                 return -1;
01229         }
01230         pos = hdr.payload;
01231         end = *next = pos + hdr.length;
01232 
01233         /*
01234          * version [0]  EXPLICIT Version DEFAULT v1
01235          * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
01236          */
01237         if (asn1_get_next(pos, end - pos, &hdr) < 0)
01238                 return -1;
01239         pos = hdr.payload;
01240 
01241         if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) {
01242                 if (asn1_get_next(pos, end - pos, &hdr) < 0)
01243                         return -1;
01244 
01245                 if (hdr.class != ASN1_CLASS_UNIVERSAL ||
01246                     hdr.tag != ASN1_TAG_INTEGER) {
01247                         wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
01248                                    "version field - found class %d tag 0x%x",
01249                                    hdr.class, hdr.tag);
01250                         return -1;
01251                 }
01252                 if (hdr.length != 1) {
01253                         wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
01254                                    "length %u (expected 1)", hdr.length);
01255                         return -1;
01256                 }
01257                 pos = hdr.payload;
01258                 left = hdr.length;
01259                 value = 0;
01260                 while (left) {
01261                         value <<= 8;
01262                         value |= *pos++;
01263                         left--;
01264                 }
01265 
01266                 cert->version = value;
01267                 if (cert->version != X509_CERT_V1 &&
01268                     cert->version != X509_CERT_V2 &&
01269                     cert->version != X509_CERT_V3) {
01270                         wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
01271                                    cert->version + 1);
01272                         return -1;
01273                 }
01274 
01275                 if (asn1_get_next(pos, end - pos, &hdr) < 0)
01276                         return -1;
01277         } else
01278                 cert->version = X509_CERT_V1;
01279         wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1);
01280 
01281         /* serialNumber CertificateSerialNumber ::= INTEGER */
01282         if (hdr.class != ASN1_CLASS_UNIVERSAL ||
01283             hdr.tag != ASN1_TAG_INTEGER) {
01284                 wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
01285                            "serialNumber; class=%d tag=0x%x",
01286                            hdr.class, hdr.tag);
01287                 return -1;
01288         }
01289 
01290         pos = hdr.payload;
01291         left = hdr.length;
01292         while (left) {
01293                 cert->serial_number <<= 8;
01294                 cert->serial_number |= *pos++;
01295                 left--;
01296         }
01297         wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number);
01298 
01299         /* signature AlgorithmIdentifier */
01300         if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
01301                                             &pos))
01302                 return -1;
01303 
01304         /* issuer Name */
01305         if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
01306                 return -1;
01307         x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
01308         wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf);
01309 
01310         /* validity Validity */
01311         if (x509_parse_validity(pos, end - pos, cert, &pos))
01312                 return -1;
01313 
01314         /* subject Name */
01315         if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
01316                 return -1;
01317         x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
01318         wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);
01319 
01320         /* subjectPublicKeyInfo SubjectPublicKeyInfo */
01321         if (x509_parse_public_key(pos, end - pos, cert, &pos))
01322                 return -1;
01323 
01324         if (pos == end)
01325                 return 0;
01326 
01327         if (cert->version == X509_CERT_V1)
01328                 return 0;
01329 
01330         if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01331             hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
01332                 wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
01333                            " tag to parse optional tbsCertificate "
01334                            "field(s); parsed class %d tag 0x%x",
01335                            hdr.class, hdr.tag);
01336                 return -1;
01337         }
01338 
01339         if (hdr.tag == 1) {
01340                 /* issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL */
01341                 wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
01342                 /* TODO: parse UniqueIdentifier ::= BIT STRING */
01343 
01344                 if (hdr.payload + hdr.length == end)
01345                         return 0;
01346 
01347                 if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01348                     hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
01349                         wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
01350                                    " tag to parse optional tbsCertificate "
01351                                    "field(s); parsed class %d tag 0x%x",
01352                                    hdr.class, hdr.tag);
01353                         return -1;
01354                 }
01355         }
01356 
01357         if (hdr.tag == 2) {
01358                 /* subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL */
01359                 wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
01360                 /* TODO: parse UniqueIdentifier ::= BIT STRING */
01361 
01362                 if (hdr.payload + hdr.length == end)
01363                         return 0;
01364 
01365                 if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01366                     hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
01367                         wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
01368                                    " tag to parse optional tbsCertificate "
01369                                    "field(s); parsed class %d tag 0x%x",
01370                                    hdr.class, hdr.tag);
01371                         return -1;
01372                 }
01373         }
01374 
01375         if (hdr.tag != 3) {
01376                 wpa_printf(MSG_DEBUG, "X509: Ignored unexpected "
01377                            "Context-Specific tag %d in optional "
01378                            "tbsCertificate fields", hdr.tag);
01379                 return 0;
01380         }
01381 
01382         /* extensions      [3]  EXPLICIT Extensions OPTIONAL */
01383 
01384         if (cert->version != X509_CERT_V3) {
01385                 wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
01386                            "Extensions data which are only allowed for "
01387                            "version 3", cert->version + 1);
01388                 return -1;
01389         }
01390 
01391         if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
01392                 return -1;
01393 
01394         pos = hdr.payload + hdr.length;
01395         if (pos < end) {
01396                 wpa_hexdump(MSG_DEBUG,
01397                             "X509: Ignored extra tbsCertificate data",
01398                             pos, end - pos);
01399         }
01400 
01401         return 0;
01402 }
01403 
01404 
01405 static int x509_rsadsi_oid(struct asn1_oid *oid)
01406 {
01407         return oid->len >= 4 &&
01408                 oid->oid[0] == 1 /* iso */ &&
01409                 oid->oid[1] == 2 /* member-body */ &&
01410                 oid->oid[2] == 840 /* us */ &&
01411                 oid->oid[3] == 113549 /* rsadsi */;
01412 }
01413 
01414 
01415 static int x509_pkcs_oid(struct asn1_oid *oid)
01416 {
01417         return oid->len >= 5 &&
01418                 x509_rsadsi_oid(oid) &&
01419                 oid->oid[4] == 1 /* pkcs */;
01420 }
01421 
01422 
01423 static int x509_digest_oid(struct asn1_oid *oid)
01424 {
01425         return oid->len >= 5 &&
01426                 x509_rsadsi_oid(oid) &&
01427                 oid->oid[4] == 2 /* digestAlgorithm */;
01428 }
01429 
01430 
01431 static int x509_sha1_oid(struct asn1_oid *oid)
01432 {
01433         return oid->len == 6 &&
01434                 oid->oid[0] == 1 /* iso */ &&
01435                 oid->oid[1] == 3 /* identified-organization */ &&
01436                 oid->oid[2] == 14 /* oiw */ &&
01437                 oid->oid[3] == 3 /* secsig */ &&
01438                 oid->oid[4] == 2 /* algorithms */ &&
01439                 oid->oid[5] == 26 /* id-sha1 */;
01440 }
01441 
01442 
01443 static int x509_sha256_oid(struct asn1_oid *oid)
01444 {
01445         return oid->len == 9 &&
01446                 oid->oid[0] == 2 /* joint-iso-itu-t */ &&
01447                 oid->oid[1] == 16 /* country */ &&
01448                 oid->oid[2] == 840 /* us */ &&
01449                 oid->oid[3] == 1 /* organization */ &&
01450                 oid->oid[4] == 101 /* gov */ &&
01451                 oid->oid[5] == 3 /* csor */ &&
01452                 oid->oid[6] == 4 /* nistAlgorithm */ &&
01453                 oid->oid[7] == 2 /* hashAlgs */ &&
01454                 oid->oid[8] == 1 /* sha256 */;
01455 }
01456 
01457 
01468 struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
01469 {
01470         struct asn1_hdr hdr;
01471         const u8 *pos, *end, *hash_start;
01472         struct x509_certificate *cert;
01473 
01474         cert = os_zalloc(sizeof(*cert) + len);
01475         if (cert == NULL)
01476                 return NULL;
01477         os_memcpy(cert + 1, buf, len);
01478         cert->cert_start = (u8 *) (cert + 1);
01479         cert->cert_len = len;
01480 
01481         pos = buf;
01482         end = buf + len;
01483 
01484         /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
01485 
01486         /* Certificate ::= SEQUENCE */
01487         if (asn1_get_next(pos, len, &hdr) < 0 ||
01488             hdr.class != ASN1_CLASS_UNIVERSAL ||
01489             hdr.tag != ASN1_TAG_SEQUENCE) {
01490                 wpa_printf(MSG_DEBUG, "X509: Certificate did not start with "
01491                            "a valid SEQUENCE - found class %d tag 0x%x",
01492                            hdr.class, hdr.tag);
01493                 x509_certificate_free(cert);
01494                 return NULL;
01495         }
01496         pos = hdr.payload;
01497 
01498         if (pos + hdr.length > end) {
01499                 x509_certificate_free(cert);
01500                 return NULL;
01501         }
01502 
01503         if (pos + hdr.length < end) {
01504                 wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
01505                             "encoded certificate",
01506                             pos + hdr.length, end - pos + hdr.length);
01507                 end = pos + hdr.length;
01508         }
01509 
01510         hash_start = pos;
01511         cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
01512         if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
01513                 x509_certificate_free(cert);
01514                 return NULL;
01515         }
01516         cert->tbs_cert_len = pos - hash_start;
01517 
01518         /* signatureAlgorithm AlgorithmIdentifier */
01519         if (x509_parse_algorithm_identifier(pos, end - pos,
01520                                             &cert->signature_alg, &pos)) {
01521                 x509_certificate_free(cert);
01522                 return NULL;
01523         }
01524 
01525         /* signatureValue BIT STRING */
01526         if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01527             hdr.class != ASN1_CLASS_UNIVERSAL ||
01528             hdr.tag != ASN1_TAG_BITSTRING) {
01529                 wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
01530                            "(signatureValue) - found class %d tag 0x%x",
01531                            hdr.class, hdr.tag);
01532                 x509_certificate_free(cert);
01533                 return NULL;
01534         }
01535         if (hdr.length < 1) {
01536                 x509_certificate_free(cert);
01537                 return NULL;
01538         }
01539         pos = hdr.payload;
01540         if (*pos) {
01541                 wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
01542                            *pos);
01543                 /* PKCS #1 v1.5 10.2.1:
01544                  * It is an error if the length in bits of the signature S is
01545                  * not a multiple of eight.
01546                  */
01547                 x509_certificate_free(cert);
01548                 return NULL;
01549         }
01550         os_free(cert->sign_value);
01551         cert->sign_value = os_malloc(hdr.length - 1);
01552         if (cert->sign_value == NULL) {
01553                 wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
01554                            "signatureValue");
01555                 x509_certificate_free(cert);
01556                 return NULL;
01557         }
01558         os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
01559         cert->sign_value_len = hdr.length - 1;
01560         wpa_hexdump(MSG_MSGDUMP, "X509: signature",
01561                     cert->sign_value, cert->sign_value_len);
01562 
01563         return cert;
01564 }
01565 
01566 
01575 int x509_certificate_check_signature(struct x509_certificate *issuer,
01576                                      struct x509_certificate *cert)
01577 {
01578         struct crypto_public_key *pk;
01579         u8 *data;
01580         const u8 *pos, *end, *next, *da_end;
01581         size_t data_len;
01582         struct asn1_hdr hdr;
01583         struct asn1_oid oid;
01584         u8 hash[32];
01585         size_t hash_len;
01586 
01587         if (!x509_pkcs_oid(&cert->signature.oid) ||
01588             cert->signature.oid.len != 7 ||
01589             cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
01590                 wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
01591                            "algorithm");
01592                 return -1;
01593         }
01594 
01595         pk = crypto_public_key_import(issuer->public_key,
01596                                       issuer->public_key_len);
01597         if (pk == NULL)
01598                 return -1;
01599 
01600         data_len = cert->sign_value_len;
01601         data = os_malloc(data_len);
01602         if (data == NULL) {
01603                 crypto_public_key_free(pk);
01604                 return -1;
01605         }
01606 
01607         if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value,
01608                                             cert->sign_value_len, data,
01609                                             &data_len) < 0) {
01610                 wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
01611                 crypto_public_key_free(pk);
01612                 os_free(data);
01613                 return -1;
01614         }
01615         crypto_public_key_free(pk);
01616 
01617         wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
01618 
01619         /*
01620          * PKCS #1 v1.5, 10.1.2:
01621          *
01622          * DigestInfo ::= SEQUENCE {
01623          *     digestAlgorithm DigestAlgorithmIdentifier,
01624          *     digest Digest
01625          * }
01626          *
01627          * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
01628          *
01629          * Digest ::= OCTET STRING
01630          *
01631          */
01632         if (asn1_get_next(data, data_len, &hdr) < 0 ||
01633             hdr.class != ASN1_CLASS_UNIVERSAL ||
01634             hdr.tag != ASN1_TAG_SEQUENCE) {
01635                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
01636                            "(DigestInfo) - found class %d tag 0x%x",
01637                            hdr.class, hdr.tag);
01638                 os_free(data);
01639                 return -1;
01640         }
01641 
01642         pos = hdr.payload;
01643         end = pos + hdr.length;
01644 
01645         /*
01646          * X.509:
01647          * AlgorithmIdentifier ::= SEQUENCE {
01648          *     algorithm            OBJECT IDENTIFIER,
01649          *     parameters           ANY DEFINED BY algorithm OPTIONAL
01650          * }
01651          */
01652 
01653         if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01654             hdr.class != ASN1_CLASS_UNIVERSAL ||
01655             hdr.tag != ASN1_TAG_SEQUENCE) {
01656                 wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
01657                            "(AlgorithmIdentifier) - found class %d tag 0x%x",
01658                            hdr.class, hdr.tag);
01659                 os_free(data);
01660                 return -1;
01661         }
01662         da_end = hdr.payload + hdr.length;
01663 
01664         if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
01665                 wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
01666                 os_free(data);
01667                 return -1;
01668         }
01669 
01670         if (x509_sha1_oid(&oid)) {
01671                 if (cert->signature.oid.oid[6] !=
01672                     5 /* sha-1WithRSAEncryption */) {
01673                         wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
01674                                    "does not match with certificate "
01675                                    "signatureAlgorithm (%lu)",
01676                                    cert->signature.oid.oid[6]);
01677                         os_free(data);
01678                         return -1;
01679                 }
01680                 goto skip_digest_oid;
01681         }
01682 
01683         if (x509_sha256_oid(&oid)) {
01684                 if (cert->signature.oid.oid[6] !=
01685                     11 /* sha2561WithRSAEncryption */) {
01686                         wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
01687                                    "does not match with certificate "
01688                                    "signatureAlgorithm (%lu)",
01689                                    cert->signature.oid.oid[6]);
01690                         os_free(data);
01691                         return -1;
01692                 }
01693                 goto skip_digest_oid;
01694         }
01695 
01696         if (!x509_digest_oid(&oid)) {
01697                 wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
01698                 os_free(data);
01699                 return -1;
01700         }
01701         switch (oid.oid[5]) {
01702         case 5: /* md5 */
01703                 if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
01704                 {
01705                         wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
01706                                    "not match with certificate "
01707                                    "signatureAlgorithm (%lu)",
01708                                    cert->signature.oid.oid[6]);
01709                         os_free(data);
01710                         return -1;
01711                 }
01712                 break;
01713         case 2: /* md2 */
01714         case 4: /* md4 */
01715         default:
01716                 wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
01717                            "(%lu)", oid.oid[5]);
01718                 os_free(data);
01719                 return -1;
01720         }
01721 
01722 skip_digest_oid:
01723         /* Digest ::= OCTET STRING */
01724         pos = da_end;
01725         end = data + data_len;
01726 
01727         if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
01728             hdr.class != ASN1_CLASS_UNIVERSAL ||
01729             hdr.tag != ASN1_TAG_OCTETSTRING) {
01730                 wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING "
01731                            "(Digest) - found class %d tag 0x%x",
01732                            hdr.class, hdr.tag);
01733                 os_free(data);
01734                 return -1;
01735         }
01736         wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
01737                     hdr.payload, hdr.length);
01738 
01739         switch (cert->signature.oid.oid[6]) {
01740         case 4: /* md5WithRSAEncryption */
01741                 md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
01742                            hash);
01743                 hash_len = 16;
01744                 wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
01745                             hash, hash_len);
01746                 break;
01747         case 5: /* sha-1WithRSAEncryption */
01748                 sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
01749                             hash);
01750                 hash_len = 20;
01751                 wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
01752                             hash, hash_len);
01753                 break;
01754         case 11: /* sha256WithRSAEncryption */
01755 #ifdef NEED_SHA256
01756                 sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
01757                               hash);
01758                 hash_len = 32;
01759                 wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
01760                             hash, hash_len);
01761                 break;
01762 #else /* NEED_SHA256 */
01763                 wpa_printf(MSG_INFO, "X509: SHA256 support disabled");
01764                 os_free(data);
01765                 return -1;
01766 #endif /* NEED_SHA256 */
01767         case 2: /* md2WithRSAEncryption */
01768         case 12: /* sha384WithRSAEncryption */
01769         case 13: /* sha512WithRSAEncryption */
01770         default:
01771                 wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
01772                            "algorithm (%lu)", cert->signature.oid.oid[6]);
01773                 os_free(data);
01774                 return -1;
01775         }
01776 
01777         if (hdr.length != hash_len ||
01778             os_memcmp(hdr.payload, hash, hdr.length) != 0) {
01779                 wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
01780                            "with calculated tbsCertificate hash");
01781                 os_free(data);
01782                 return -1;
01783         }
01784 
01785         os_free(data);
01786 
01787         wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
01788                    "calculated tbsCertificate hash");
01789 
01790         return 0;
01791 }
01792 
01793 
01794 static int x509_valid_issuer(const struct x509_certificate *cert)
01795 {
01796         if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
01797             !cert->ca) {
01798                 wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
01799                            "issuer");
01800                 return -1;
01801         }
01802 
01803         if (cert->version == X509_CERT_V3 &&
01804             !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
01805                 wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
01806                            "include BasicConstraints extension");
01807                 return -1;
01808         }
01809 
01810         if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
01811             !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
01812                 wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
01813                            "keyCertSign bit in Key Usage");
01814                 return -1;
01815         }
01816 
01817         return 0;
01818 }
01819 
01820 
01830 int x509_certificate_chain_validate(struct x509_certificate *trusted,
01831                                     struct x509_certificate *chain,
01832                                     int *reason)
01833 {
01834         long unsigned idx;
01835         int chain_trusted = 0;
01836         struct x509_certificate *cert, *trust;
01837         char buf[128];
01838         struct os_time now;
01839 
01840         *reason = X509_VALIDATE_OK;
01841 
01842         wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
01843         os_get_time(&now);
01844 
01845         for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
01846                 x509_name_string(&cert->subject, buf, sizeof(buf)); 
01847                 wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
01848 
01849                 if (chain_trusted)
01850                         continue;
01851 
01852                 if ((unsigned long) now.sec <
01853                     (unsigned long) cert->not_before ||
01854                     (unsigned long) now.sec >
01855                     (unsigned long) cert->not_after) {
01856                         wpa_printf(MSG_INFO, "X509: Certificate not valid "
01857                                    "(now=%lu not_before=%lu not_after=%lu)",
01858                                    now.sec, cert->not_before, cert->not_after);
01859                         *reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
01860                         return -1;
01861                 }
01862 
01863                 if (cert->next) {
01864                         if (x509_name_compare(&cert->issuer,
01865                                               &cert->next->subject) != 0) {
01866                                 wpa_printf(MSG_DEBUG, "X509: Certificate "
01867                                            "chain issuer name mismatch");
01868                                 x509_name_string(&cert->issuer, buf,
01869                                                  sizeof(buf)); 
01870                                 wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
01871                                            buf);
01872                                 x509_name_string(&cert->next->subject, buf,
01873                                                  sizeof(buf)); 
01874                                 wpa_printf(MSG_DEBUG, "X509: next cert "
01875                                            "subject: %s", buf);
01876                                 *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
01877                                 return -1;
01878                         }
01879 
01880                         if (x509_valid_issuer(cert->next) < 0) {
01881                                 *reason = X509_VALIDATE_BAD_CERTIFICATE;
01882                                 return -1;
01883                         }
01884 
01885                         if ((cert->next->extensions_present &
01886                              X509_EXT_PATH_LEN_CONSTRAINT) &&
01887                             idx > cert->next->path_len_constraint) {
01888                                 wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
01889                                            " not met (idx=%lu issuer "
01890                                            "pathLenConstraint=%lu)", idx,
01891                                            cert->next->path_len_constraint);
01892                                 *reason = X509_VALIDATE_BAD_CERTIFICATE;
01893                                 return -1;
01894                         }
01895 
01896                         if (x509_certificate_check_signature(cert->next, cert)
01897                             < 0) {
01898                                 wpa_printf(MSG_DEBUG, "X509: Invalid "
01899                                            "certificate signature within "
01900                                            "chain");
01901                                 *reason = X509_VALIDATE_BAD_CERTIFICATE;
01902                                 return -1;
01903                         }
01904                 }
01905 
01906                 for (trust = trusted; trust; trust = trust->next) {
01907                         if (x509_name_compare(&cert->issuer, &trust->subject)
01908                             == 0)
01909                                 break;
01910                 }
01911 
01912                 if (trust) {
01913                         wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
01914                                    "list of trusted certificates");
01915                         if (x509_valid_issuer(trust) < 0) {
01916                                 *reason = X509_VALIDATE_BAD_CERTIFICATE;
01917                                 return -1;
01918                         }
01919 
01920                         if (x509_certificate_check_signature(trust, cert) < 0)
01921                         {
01922                                 wpa_printf(MSG_DEBUG, "X509: Invalid "
01923                                            "certificate signature");
01924                                 *reason = X509_VALIDATE_BAD_CERTIFICATE;
01925                                 return -1;
01926                         }
01927 
01928                         wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
01929                                    "found to complete the chain");
01930                         chain_trusted = 1;
01931                 }
01932         }
01933 
01934         if (!chain_trusted) {
01935                 wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
01936                            "from the list of trusted certificates");
01937                 if (trusted) {
01938                         *reason = X509_VALIDATE_UNKNOWN_CA;
01939                         return -1;
01940                 }
01941                 wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
01942                            "disabled - ignore unknown CA issue");
01943         }
01944 
01945         wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
01946 
01947         return 0;
01948 }
01949 
01950 
01959 struct x509_certificate *
01960 x509_certificate_get_subject(struct x509_certificate *chain,
01961                              struct x509_name *name)
01962 {
01963         struct x509_certificate *cert;
01964 
01965         for (cert = chain; cert; cert = cert->next) {
01966                 if (x509_name_compare(&cert->subject, name) == 0)
01967                         return cert;
01968         }
01969         return NULL;
01970 }
01971 
01972 
01979 int x509_certificate_self_signed(struct x509_certificate *cert)
01980 {
01981         return x509_name_compare(&cert->issuer, &cert->subject) == 0;
01982 }
01983 
01984 #endif /* CONFIG_INTERNAL_X509 */
01985 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on Sat Nov 21 23:16:53 2009 for hostapd by  doxygen 1.6.1