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


wpa_supplicant_node
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Jan 2 2014 11:25:15