x509parse.c revision 299742
1232809Sjmallett/* 2232809Sjmallett * X.509 certificate and private key decoding 3232809Sjmallett * 4232809Sjmallett * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine 5232809Sjmallett * 6232809Sjmallett * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org> 7232809Sjmallett * 8232809Sjmallett * All rights reserved. 9232809Sjmallett * 10232809Sjmallett * Redistribution and use in source and binary forms, with or without 11232809Sjmallett * modification, are permitted provided that the following conditions 12232809Sjmallett * are met: 13232809Sjmallett * 14232809Sjmallett * * Redistributions of source code must retain the above copyright 15232809Sjmallett * notice, this list of conditions and the following disclaimer. 16232809Sjmallett * * Redistributions in binary form must reproduce the above copyright 17232809Sjmallett * notice, this list of conditions and the following disclaimer in the 18232809Sjmallett * documentation and/or other materials provided with the distribution. 19232809Sjmallett * * Neither the names of PolarSSL or XySSL nor the names of its contributors 20232809Sjmallett * may be used to endorse or promote products derived from this software 21232809Sjmallett * without specific prior written permission. 22232809Sjmallett * 23232809Sjmallett * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24232809Sjmallett * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25232809Sjmallett * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26232809Sjmallett * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27232809Sjmallett * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28232809Sjmallett * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29232809Sjmallett * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30232809Sjmallett * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31232809Sjmallett * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32232809Sjmallett * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33232809Sjmallett * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34232809Sjmallett */ 35232809Sjmallett/* 36232809Sjmallett * The ITU-T X.509 standard defines a certificate format for PKI. 37232809Sjmallett * 38232809Sjmallett * http://www.ietf.org/rfc/rfc5280.txt 39232809Sjmallett * http://www.ietf.org/rfc/rfc3279.txt 40232809Sjmallett * http://www.ietf.org/rfc/rfc6818.txt 41232809Sjmallett * 42232809Sjmallett * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc 43232809Sjmallett * 44232809Sjmallett * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf 45232809Sjmallett * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf 46232809Sjmallett */ 47232809Sjmallett 48232809Sjmallett#include <apr_pools.h> 49232809Sjmallett#include <apr_tables.h> 50232809Sjmallett#include "svn_hash.h" 51232809Sjmallett#include "svn_string.h" 52232809Sjmallett#include "svn_time.h" 53232809Sjmallett#include "svn_checksum.h" 54232809Sjmallett#include "svn_utf.h" 55232809Sjmallett#include "svn_ctype.h" 56232809Sjmallett#include "private/svn_utf_private.h" 57232809Sjmallett#include "private/svn_string_private.h" 58232809Sjmallett 59232809Sjmallett#include "x509.h" 60232809Sjmallett 61232809Sjmallett#include <string.h> 62232809Sjmallett#include <stdio.h> 63232809Sjmallett 64232809Sjmallett/* 65232809Sjmallett * ASN.1 DER decoding routines 66232809Sjmallett */ 67232809Sjmallettstatic svn_error_t * 68232809Sjmallettasn1_get_len(const unsigned char **p, const unsigned char *end, 69232809Sjmallett ptrdiff_t *len) 70232809Sjmallett{ 71232809Sjmallett if ((end - *p) < 1) 72232809Sjmallett return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); 73232809Sjmallett 74232809Sjmallett if ((**p & 0x80) == 0) 75232809Sjmallett *len = *(*p)++; 76232809Sjmallett else 77232809Sjmallett switch (**p & 0x7F) 78232809Sjmallett { 79232809Sjmallett case 1: 80232809Sjmallett if ((end - *p) < 2) 81232809Sjmallett return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); 82232809Sjmallett 83232809Sjmallett *len = (*p)[1]; 84232809Sjmallett (*p) += 2; 85232809Sjmallett break; 86232809Sjmallett 87232809Sjmallett case 2: 88232809Sjmallett if ((end - *p) < 3) 89232809Sjmallett return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); 90232809Sjmallett 91232809Sjmallett *len = ((*p)[1] << 8) | (*p)[2]; 92232809Sjmallett (*p) += 3; 93232809Sjmallett break; 94232809Sjmallett 95232809Sjmallett default: 96232809Sjmallett return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL); 97232809Sjmallett break; 98232809Sjmallett } 99232809Sjmallett 100232809Sjmallett if (*len > (end - *p)) 101232809Sjmallett return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); 102232809Sjmallett 103232809Sjmallett return SVN_NO_ERROR; 104232809Sjmallett} 105232809Sjmallett 106232809Sjmallettstatic svn_error_t * 107232809Sjmallettasn1_get_tag(const unsigned char **p, 108232809Sjmallett const unsigned char *end, ptrdiff_t *len, int tag) 109232809Sjmallett{ 110232809Sjmallett if ((end - *p) < 1) 111232809Sjmallett return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); 112232809Sjmallett 113232809Sjmallett if (**p != tag) 114232809Sjmallett return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); 115232809Sjmallett 116232809Sjmallett (*p)++; 117232809Sjmallett 118232809Sjmallett return svn_error_trace(asn1_get_len(p, end, len)); 119232809Sjmallett} 120232809Sjmallett 121232809Sjmallettstatic svn_error_t * 122232809Sjmallettasn1_get_int(const unsigned char **p, const unsigned char *end, int *val) 123232809Sjmallett{ 124232809Sjmallett ptrdiff_t len; 125232809Sjmallett 126232809Sjmallett SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER)); 127232809Sjmallett 128232809Sjmallett /* Reject bit patterns that would overflow the output and those that 129232809Sjmallett represent negative values. */ 130232809Sjmallett if (len > (int)sizeof(int) || (**p & 0x80) != 0) 131232809Sjmallett return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL); 132232809Sjmallett 133232809Sjmallett *val = 0; 134232809Sjmallett 135232809Sjmallett while (len-- > 0) { 136232809Sjmallett /* This would be undefined for bit-patterns of negative values. */ 137232809Sjmallett *val = (*val << 8) | **p; 138232809Sjmallett (*p)++; 139232809Sjmallett } 140232809Sjmallett 141232809Sjmallett return SVN_NO_ERROR; 142232809Sjmallett} 143232809Sjmallett 144232809Sjmallettstatic svn_boolean_t 145232809Sjmallettequal(const void *left, apr_size_t left_len, 146232809Sjmallett const void *right, apr_size_t right_len) 147232809Sjmallett{ 148232809Sjmallett if (left_len != right_len) 149232809Sjmallett return FALSE; 150232809Sjmallett 151232809Sjmallett return memcmp(left, right, right_len) == 0; 152232809Sjmallett} 153232809Sjmallett 154232809Sjmallettstatic svn_boolean_t 155232809Sjmallettoids_equal(x509_buf *left, x509_buf *right) 156232809Sjmallett{ 157232809Sjmallett return equal(left->p, left->len, 158232809Sjmallett right->p, right->len); 159232809Sjmallett} 160232809Sjmallett 161232809Sjmallett/* 162232809Sjmallett * Version ::= INTEGER { v1(0), v2(1), v3(2) } 163232809Sjmallett */ 164232809Sjmallettstatic svn_error_t * 165232809Sjmallettx509_get_version(const unsigned char **p, const unsigned char *end, int *ver) 166232809Sjmallett{ 167232809Sjmallett svn_error_t *err; 168232809Sjmallett ptrdiff_t len; 169232809Sjmallett 170232809Sjmallett /* 171232809Sjmallett * As defined in the Basic Certificate fields: 172232809Sjmallett * version [0] EXPLICIT Version DEFAULT v1, 173232809Sjmallett * the version is the context specific tag 0. 174232809Sjmallett */ 175232809Sjmallett err = asn1_get_tag(p, end, &len, 176232809Sjmallett ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0); 177232809Sjmallett if (err) 178232809Sjmallett { 179232809Sjmallett if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) 180232809Sjmallett { 181232809Sjmallett svn_error_clear(err); 182232809Sjmallett *ver = 0; 183232809Sjmallett return SVN_NO_ERROR; 184232809Sjmallett } 185232809Sjmallett 186232809Sjmallett return svn_error_trace(err); 187232809Sjmallett } 188232809Sjmallett 189232809Sjmallett end = *p + len; 190232809Sjmallett 191232809Sjmallett err = asn1_get_int(p, end, ver); 192232809Sjmallett if (err) 193232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL); 194232809Sjmallett 195232809Sjmallett if (*p != end) 196232809Sjmallett { 197232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 198232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL); 199232809Sjmallett } 200232809Sjmallett 201232809Sjmallett return SVN_NO_ERROR; 202232809Sjmallett} 203232809Sjmallett 204232809Sjmallett/* 205232809Sjmallett * CertificateSerialNumber ::= INTEGER 206232809Sjmallett */ 207232809Sjmallettstatic svn_error_t * 208232809Sjmallettx509_get_serial(const unsigned char **p, 209232809Sjmallett const unsigned char *end, x509_buf * serial) 210232809Sjmallett{ 211232809Sjmallett svn_error_t *err; 212232809Sjmallett 213232809Sjmallett if ((end - *p) < 1) 214232809Sjmallett { 215232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); 216232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); 217232809Sjmallett } 218232809Sjmallett 219232809Sjmallett if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) && 220232809Sjmallett **p != ASN1_INTEGER) 221232809Sjmallett { 222232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); 223232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); 224232809Sjmallett } 225232809Sjmallett 226232809Sjmallett serial->tag = *(*p)++; 227232809Sjmallett 228232809Sjmallett err = asn1_get_len(p, end, &serial->len); 229232809Sjmallett if (err) 230232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL); 231232809Sjmallett 232232809Sjmallett serial->p = *p; 233232809Sjmallett *p += serial->len; 234232809Sjmallett 235232809Sjmallett return SVN_NO_ERROR; 236232809Sjmallett} 237232809Sjmallett 238232809Sjmallett/* 239232809Sjmallett * AlgorithmIdentifier ::= SEQUENCE { 240232809Sjmallett * algorithm OBJECT IDENTIFIER, 241232809Sjmallett * parameters ANY DEFINED BY algorithm OPTIONAL } 242232809Sjmallett */ 243232809Sjmallettstatic svn_error_t * 244232809Sjmallettx509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg) 245232809Sjmallett{ 246232809Sjmallett svn_error_t *err; 247232809Sjmallett ptrdiff_t len; 248232809Sjmallett 249232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 250232809Sjmallett if (err) 251232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); 252232809Sjmallett 253232809Sjmallett end = *p + len; 254232809Sjmallett alg->tag = **p; 255232809Sjmallett 256232809Sjmallett err = asn1_get_tag(p, end, &alg->len, ASN1_OID); 257232809Sjmallett if (err) 258232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); 259232809Sjmallett 260232809Sjmallett alg->p = *p; 261232809Sjmallett *p += alg->len; 262232809Sjmallett 263232809Sjmallett if (*p == end) 264232809Sjmallett return SVN_NO_ERROR; 265232809Sjmallett 266232809Sjmallett /* 267232809Sjmallett * assume the algorithm parameters must be NULL 268232809Sjmallett */ 269232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_NULL); 270232809Sjmallett if (err) 271232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); 272232809Sjmallett 273232809Sjmallett if (*p != end) 274232809Sjmallett { 275232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 276232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL); 277232809Sjmallett } 278232809Sjmallett 279232809Sjmallett return SVN_NO_ERROR; 280232809Sjmallett} 281232809Sjmallett 282232809Sjmallett/* 283232809Sjmallett * AttributeTypeAndValue ::= SEQUENCE { 284232809Sjmallett * type AttributeType, 285232809Sjmallett * value AttributeValue } 286232809Sjmallett * 287232809Sjmallett * AttributeType ::= OBJECT IDENTIFIER 288232809Sjmallett * 289232809Sjmallett * AttributeValue ::= ANY DEFINED BY AttributeType 290232809Sjmallett */ 291232809Sjmallettstatic svn_error_t * 292232809Sjmallettx509_get_attribute(const unsigned char **p, const unsigned char *end, 293232809Sjmallett x509_name *cur, apr_pool_t *result_pool) 294232809Sjmallett{ 295232809Sjmallett svn_error_t *err; 296232809Sjmallett ptrdiff_t len; 297232809Sjmallett x509_buf *oid; 298232809Sjmallett x509_buf *val; 299232809Sjmallett 300232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 301232809Sjmallett if (err) 302232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); 303232809Sjmallett 304232809Sjmallett end = *p + len; 305232809Sjmallett 306232809Sjmallett oid = &cur->oid; 307232809Sjmallett 308232809Sjmallett err = asn1_get_tag(p, end, &oid->len, ASN1_OID); 309232809Sjmallett if (err) 310232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); 311232809Sjmallett 312232809Sjmallett oid->tag = ASN1_OID; 313232809Sjmallett oid->p = *p; 314232809Sjmallett *p += oid->len; 315232809Sjmallett 316232809Sjmallett if ((end - *p) < 1) 317232809Sjmallett { 318232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL); 319232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); 320232809Sjmallett } 321232809Sjmallett 322232809Sjmallett if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && 323232809Sjmallett **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && 324232809Sjmallett **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING) 325232809Sjmallett { 326232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL); 327232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); 328232809Sjmallett } 329232809Sjmallett 330232809Sjmallett val = &cur->val; 331232809Sjmallett val->tag = *(*p)++; 332232809Sjmallett 333232809Sjmallett err = asn1_get_len(p, end, &val->len); 334232809Sjmallett if (err) 335232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); 336232809Sjmallett 337232809Sjmallett val->p = *p; 338232809Sjmallett *p += val->len; 339232809Sjmallett 340232809Sjmallett cur->next = NULL; 341232809Sjmallett 342232809Sjmallett if (*p != end) 343232809Sjmallett { 344232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 345232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); 346232809Sjmallett } 347232809Sjmallett 348232809Sjmallett return SVN_NO_ERROR; 349232809Sjmallett} 350232809Sjmallett 351232809Sjmallett/* 352232809Sjmallett * RelativeDistinguishedName ::= 353232809Sjmallett * SET SIZE (1..MAX) OF AttributeTypeAndValue 354232809Sjmallett */ 355232809Sjmallettstatic svn_error_t * 356232809Sjmallettx509_get_name(const unsigned char **p, const unsigned char *name_end, 357232809Sjmallett x509_name *name, apr_pool_t *result_pool) 358232809Sjmallett{ 359232809Sjmallett svn_error_t *err; 360232809Sjmallett ptrdiff_t len; 361232809Sjmallett const unsigned char *set_end; 362232809Sjmallett x509_name *cur = NULL; 363232809Sjmallett 364232809Sjmallett err = asn1_get_tag(p, name_end, &len, ASN1_CONSTRUCTED | ASN1_SET); 365232809Sjmallett if (err) 366232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL); 367232809Sjmallett 368232809Sjmallett set_end = *p + len; 369232809Sjmallett 370232809Sjmallett /* 371232809Sjmallett * iterate until the end of the SET is reached 372232809Sjmallett */ 373232809Sjmallett while (*p < set_end) 374232809Sjmallett { 375232809Sjmallett if (!cur) 376232809Sjmallett { 377232809Sjmallett cur = name; 378232809Sjmallett } 379232809Sjmallett else 380232809Sjmallett { 381232809Sjmallett cur->next = apr_palloc(result_pool, sizeof(x509_name)); 382232809Sjmallett cur = cur->next; 383232809Sjmallett } 384232809Sjmallett SVN_ERR(x509_get_attribute(p, set_end, cur, result_pool)); 385232809Sjmallett } 386232809Sjmallett 387232809Sjmallett /* 388232809Sjmallett * recurse until end of SEQUENCE (name) is reached 389232809Sjmallett */ 390232809Sjmallett if (*p == name_end) 391232809Sjmallett return SVN_NO_ERROR; 392232809Sjmallett 393232809Sjmallett cur->next = apr_palloc(result_pool, sizeof(x509_name)); 394232809Sjmallett 395232809Sjmallett return svn_error_trace(x509_get_name(p, name_end, cur->next, result_pool)); 396232809Sjmallett} 397232809Sjmallett 398232809Sjmallett/* Retrieve the date from the X.509 cert data between *P and END in either 399232809Sjmallett * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and 400232809Sjmallett * 4.1.2.5.2 respectively) and place the result in WHEN using SCRATCH_POOL 401232809Sjmallett * for temporary allocations. */ 402232809Sjmallettstatic svn_error_t * 403232809Sjmallettx509_get_date(apr_time_t *when, 404232809Sjmallett const unsigned char **p, 405232809Sjmallett const unsigned char *end, 406232809Sjmallett apr_pool_t *scratch_pool) 407232809Sjmallett{ 408232809Sjmallett svn_error_t *err; 409232809Sjmallett apr_status_t ret; 410232809Sjmallett int tag; 411232809Sjmallett ptrdiff_t len; 412232809Sjmallett char *date; 413232809Sjmallett apr_time_exp_t xt = { 0 }; 414232809Sjmallett char tz; 415232809Sjmallett 416232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME); 417232809Sjmallett if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) 418232809Sjmallett { 419232809Sjmallett svn_error_clear(err); 420232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME); 421232809Sjmallett tag = ASN1_GENERALIZED_TIME; 422232809Sjmallett } 423232809Sjmallett else 424232809Sjmallett { 425232809Sjmallett tag = ASN1_UTC_TIME; 426232809Sjmallett } 427232809Sjmallett if (err) 428232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL); 429232809Sjmallett 430232809Sjmallett date = apr_pstrndup(scratch_pool, (const char *) *p, len); 431232809Sjmallett switch (tag) 432232809Sjmallett { 433232809Sjmallett case ASN1_UTC_TIME: 434232809Sjmallett if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c", 435232809Sjmallett &xt.tm_year, &xt.tm_mon, &xt.tm_mday, 436232809Sjmallett &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6) 437232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); 438232809Sjmallett 439232809Sjmallett /* UTCTime only provides a 2 digit year. X.509 specifies that years 440232809Sjmallett * greater than or equal to 50 must be interpreted as 19YY and years 441232809Sjmallett * less than 50 be interpreted as 20YY. This format is not used for 442232809Sjmallett * years greater than 2049. apr_time_exp_t wants years as the number 443232809Sjmallett * of years since 1900, so don't convert to 4 digits here. */ 444232809Sjmallett xt.tm_year += 100 * (xt.tm_year < 50); 445232809Sjmallett break; 446232809Sjmallett 447232809Sjmallett case ASN1_GENERALIZED_TIME: 448232809Sjmallett if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c", 449232809Sjmallett &xt.tm_year, &xt.tm_mon, &xt.tm_mday, 450232809Sjmallett &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6) 451232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); 452232809Sjmallett 453232809Sjmallett /* GeneralizedTime has the full 4 digit year. But apr_time_exp_t 454232809Sjmallett * wants years as the number of years since 1900. */ 455232809Sjmallett xt.tm_year -= 1900; 456232809Sjmallett break; 457232809Sjmallett 458232809Sjmallett default: 459232809Sjmallett /* shouldn't ever get here because we should error out above in the 460232809Sjmallett * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */ 461232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); 462232809Sjmallett break; 463232809Sjmallett } 464232809Sjmallett 465232809Sjmallett /* check that the timezone is GMT 466232809Sjmallett * ASN.1 allows for the timezone to be specified but X.509 says it must 467232809Sjmallett * always be GMT. A little bit of extra paranoia here seems like a good 468232809Sjmallett * idea. */ 469232809Sjmallett if (tz != 'Z') 470232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL); 471232809Sjmallett 472232809Sjmallett /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */ 473232809Sjmallett xt.tm_mon -= 1; 474232809Sjmallett 475232809Sjmallett ret = apr_time_exp_gmt_get(when, &xt); 476232809Sjmallett if (ret) 477232809Sjmallett return svn_error_wrap_apr(ret, NULL); 478232809Sjmallett 479232809Sjmallett *p += len; 480232809Sjmallett 481232809Sjmallett return SVN_NO_ERROR; 482232809Sjmallett} 483232809Sjmallett 484232809Sjmallett/* 485232809Sjmallett * Validity ::= SEQUENCE { 486232809Sjmallett * notBefore Time, 487232809Sjmallett * notAfter Time } 488232809Sjmallett * 489232809Sjmallett * Time ::= CHOICE { 490232809Sjmallett * utcTime UTCTime, 491232809Sjmallett * generalTime GeneralizedTime } 492232809Sjmallett */ 493232809Sjmallettstatic svn_error_t * 494232809Sjmallettx509_get_dates(apr_time_t *from, 495232809Sjmallett apr_time_t *to, 496232809Sjmallett const unsigned char **p, 497232809Sjmallett const unsigned char *end, 498232809Sjmallett apr_pool_t *scratch_pool) 499232809Sjmallett{ 500232809Sjmallett svn_error_t *err; 501232809Sjmallett ptrdiff_t len; 502232809Sjmallett 503232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 504232809Sjmallett if (err) 505232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL); 506232809Sjmallett 507232809Sjmallett end = *p + len; 508232809Sjmallett 509232809Sjmallett SVN_ERR(x509_get_date(from, p, end, scratch_pool)); 510232809Sjmallett 511232809Sjmallett SVN_ERR(x509_get_date(to, p, end, scratch_pool)); 512232809Sjmallett 513232809Sjmallett if (*p != end) 514232809Sjmallett { 515232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 516232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL); 517232809Sjmallett } 518232809Sjmallett 519232809Sjmallett return SVN_NO_ERROR; 520232809Sjmallett} 521232809Sjmallett 522232809Sjmallettstatic svn_error_t * 523232809Sjmallettx509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig) 524232809Sjmallett{ 525232809Sjmallett svn_error_t *err; 526232809Sjmallett ptrdiff_t len; 527232809Sjmallett 528232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING); 529232809Sjmallett if (err) 530232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL); 531232809Sjmallett 532232809Sjmallett sig->tag = ASN1_BIT_STRING; 533232809Sjmallett 534232809Sjmallett if (--len < 1 || *(*p)++ != 0) 535232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL); 536232809Sjmallett 537232809Sjmallett sig->len = len; 538232809Sjmallett sig->p = *p; 539232809Sjmallett 540232809Sjmallett *p += len; 541232809Sjmallett 542232809Sjmallett return SVN_NO_ERROR; 543232809Sjmallett} 544232809Sjmallett 545232809Sjmallett/* 546232809Sjmallett * X.509 v2/v3 unique identifier (not parsed) 547232809Sjmallett */ 548232809Sjmallettstatic svn_error_t * 549232809Sjmallettx509_get_uid(const unsigned char **p, 550232809Sjmallett const unsigned char *end, x509_buf * uid, int n) 551232809Sjmallett{ 552232809Sjmallett svn_error_t *err; 553232809Sjmallett 554232809Sjmallett if (*p == end) 555232809Sjmallett return SVN_NO_ERROR; 556232809Sjmallett 557232809Sjmallett err = asn1_get_tag(p, end, &uid->len, 558232809Sjmallett ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n); 559232809Sjmallett if (err) 560232809Sjmallett { 561232809Sjmallett if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) 562232809Sjmallett { 563232809Sjmallett svn_error_clear(err); 564232809Sjmallett return SVN_NO_ERROR; 565232809Sjmallett } 566232809Sjmallett 567232809Sjmallett return svn_error_trace(err); 568232809Sjmallett } 569232809Sjmallett 570232809Sjmallett uid->tag = ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n; 571232809Sjmallett uid->p = *p; 572232809Sjmallett *p += uid->len; 573232809Sjmallett 574232809Sjmallett return SVN_NO_ERROR; 575232809Sjmallett} 576232809Sjmallett 577232809Sjmallett/* 578232809Sjmallett * X.509 v3 extensions (not parsed) 579232809Sjmallett */ 580232809Sjmallettstatic svn_error_t * 581232809Sjmallettx509_get_ext(apr_array_header_t *dnsnames, 582232809Sjmallett const unsigned char **p, 583232809Sjmallett const unsigned char *end) 584232809Sjmallett{ 585232809Sjmallett svn_error_t *err; 586232809Sjmallett ptrdiff_t len; 587232809Sjmallett 588232809Sjmallett if (*p == end) 589232809Sjmallett return SVN_NO_ERROR; 590232809Sjmallett 591232809Sjmallett err = asn1_get_tag(p, end, &len, 592232809Sjmallett ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3); 593232809Sjmallett if (err) 594232809Sjmallett { 595232809Sjmallett /* If there aren't extensions that's ok they aren't required */ 596232809Sjmallett if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) 597232809Sjmallett { 598232809Sjmallett svn_error_clear(err); 599232809Sjmallett return SVN_NO_ERROR; 600232809Sjmallett } 601232809Sjmallett 602232809Sjmallett return svn_error_trace(err); 603232809Sjmallett } 604232809Sjmallett 605232809Sjmallett end = *p + len; 606232809Sjmallett 607232809Sjmallett SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE)); 608232809Sjmallett 609232809Sjmallett if (end != *p + len) 610232809Sjmallett { 611232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 612232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); 613232809Sjmallett } 614232809Sjmallett 615232809Sjmallett while (*p < end) 616232809Sjmallett { 617232809Sjmallett ptrdiff_t ext_len; 618232809Sjmallett const unsigned char *ext_start, *sna_end; 619232809Sjmallett err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 620232809Sjmallett if (err) 621232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, 622232809Sjmallett NULL); 623232809Sjmallett ext_start = *p; 624232809Sjmallett 625232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_OID); 626232809Sjmallett if (err) 627232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, 628232809Sjmallett NULL); 629232809Sjmallett 630232809Sjmallett /* skip all extensions except SubjectAltName */ 631232809Sjmallett if (!equal(*p, len, 632232809Sjmallett OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1)) 633232809Sjmallett { 634232809Sjmallett *p += ext_len - (*p - ext_start); 635232809Sjmallett continue; 636232809Sjmallett } 637232809Sjmallett *p += len; 638232809Sjmallett 639232809Sjmallett err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING); 640232809Sjmallett if (err) 641232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, 642232809Sjmallett NULL); 643232809Sjmallett 644232809Sjmallett /* SubjectAltName ::= GeneralNames 645232809Sjmallett 646232809Sjmallett GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName 647232809Sjmallett 648232809Sjmallett GeneralName ::= CHOICE { 649232809Sjmallett other Name [0] OtherName, 650232809Sjmallett rfc822Name [1] IA5String, 651232809Sjmallett dNSName [2] IA5String, 652232809Sjmallett x400Address [3] ORAddress, 653232809Sjmallett directoryName [4] Name, 654232809Sjmallett ediPartyName [5] EDIPartyName, 655232809Sjmallett uniformResourceIdentifier [6] IA5String, 656232809Sjmallett iPAddress [7] OCTET STRING, 657232809Sjmallett registeredID [8] OBJECT IDENTIFIER } */ 658232809Sjmallett sna_end = *p + len; 659232809Sjmallett 660232809Sjmallett err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 661232809Sjmallett if (err) 662232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, 663232809Sjmallett NULL); 664232809Sjmallett 665232809Sjmallett if (sna_end != *p + len) 666232809Sjmallett { 667232809Sjmallett err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 668232809Sjmallett return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL); 669232809Sjmallett } 670232809Sjmallett 671232809Sjmallett while (*p < sna_end) 672232809Sjmallett { 673232809Sjmallett err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC | 674232809Sjmallett ASN1_PRIMITIVE | 2); 675232809Sjmallett if (err) 676232809Sjmallett { 677232809Sjmallett /* not not a dNSName */ 678232809Sjmallett if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG) 679232809Sjmallett { 680232809Sjmallett svn_error_clear(err); 681232809Sjmallett /* need to skip the tag and then find the length to 682232809Sjmallett * skip to ignore this SNA entry. */ 683232809Sjmallett (*p)++; 684232809Sjmallett SVN_ERR(asn1_get_len(p, sna_end, &len)); 685232809Sjmallett *p += len; 686232809Sjmallett continue; 687232809Sjmallett } 688232809Sjmallett 689232809Sjmallett return svn_error_trace(err); 690 } 691 else 692 { 693 /* We found a dNSName entry */ 694 x509_buf *dnsname = apr_palloc(dnsnames->pool, 695 sizeof(x509_buf)); 696 dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */ 697 dnsname->len = len; 698 dnsname->p = *p; 699 APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname; 700 } 701 702 *p += len; 703 } 704 705 } 706 707 return SVN_NO_ERROR; 708} 709 710/* Escape all non-ascii or control characters similar to 711 * svn_xml_fuzzy_escape() and svn_utf_cstring_from_utf8_fuzzy(). 712 * All of the encoding formats somewhat overlap with ascii (BMPString 713 * and UniversalString are actually always wider so you'll end up 714 * with a bunch of escaped nul bytes, but ideally we don't get here 715 * for those). The result is always a nul-terminated C string. */ 716static const char * 717fuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool) 718{ 719 const char *end = src->data + src->len; 720 const char *p = src->data, *q; 721 svn_stringbuf_t *outstr; 722 char escaped_char[6]; /* ? \ u u u \0 */ 723 724 for (q = p; q < end; q++) 725 { 726 if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q)) 727 break; 728 } 729 730 if (q == end) 731 return src->data; 732 733 outstr = svn_stringbuf_create_empty(result_pool); 734 while (1) 735 { 736 q = p; 737 738 /* Traverse till either unsafe character or eos. */ 739 while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q)) 740 q++; 741 742 /* copy chunk before marker */ 743 svn_stringbuf_appendbytes(outstr, p, q - p); 744 745 if (q == end) 746 break; 747 748 apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u", 749 (unsigned char) *q); 750 svn_stringbuf_appendcstr(outstr, escaped_char); 751 752 p = q + 1; 753 } 754 755 return outstr->data; 756} 757 758/* Escape only NUL characters from a string that is presumed to 759 * be UTF-8 encoded and return a nul-terminated C string. */ 760static const char * 761nul_escape(const svn_string_t *src, apr_pool_t *result_pool) 762{ 763 const char *end = src->data + src->len; 764 const char *p = src->data, *q; 765 svn_stringbuf_t *outstr; 766 767 for (q = p; q < end; q++) 768 { 769 if (*q == '\0') 770 break; 771 } 772 773 if (q == end) 774 return src->data; 775 776 outstr = svn_stringbuf_create_empty(result_pool); 777 while (1) 778 { 779 q = p; 780 781 /* Traverse till either unsafe character or eos. */ 782 while (q < end && *q != '\0') 783 q++; 784 785 /* copy chunk before marker */ 786 svn_stringbuf_appendbytes(outstr, p, q - p); 787 788 if (q == end) 789 break; 790 791 svn_stringbuf_appendcstr(outstr, "?\\000"); 792 793 p = q + 1; 794 } 795 796 return outstr->data; 797} 798 799 800/* Convert an ISO-8859-1 (Latin-1) string to UTF-8. 801 ISO-8859-1 is a strict subset of Unicode. */ 802static svn_error_t * 803latin1_to_utf8(const svn_string_t **result, const svn_string_t *src, 804 apr_pool_t *result_pool) 805{ 806 apr_int32_t *ucs4buf; 807 svn_membuf_t resultbuf; 808 apr_size_t length; 809 apr_size_t i; 810 svn_string_t *res; 811 812 ucs4buf = apr_palloc(result_pool, src->len * sizeof(*ucs4buf)); 813 for (i = 0; i < src->len; ++i) 814 ucs4buf[i] = (unsigned char)(src->data[i]); 815 816 svn_membuf__create(&resultbuf, 2 * src->len, result_pool); 817 SVN_ERR(svn_utf__encode_ucs4_string( 818 &resultbuf, ucs4buf, src->len, &length)); 819 820 res = apr_palloc(result_pool, sizeof(*res)); 821 res->data = resultbuf.data; 822 res->len = length; 823 *result = res; 824 return SVN_NO_ERROR; 825} 826 827/* Make a best effort to convert a X.509 name to a UTF-8 encoded 828 * string and return it. If we can't properly convert just do a 829 * fuzzy conversion so we have something to display. */ 830static const char * 831x509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool) 832{ 833 const svn_string_t *src_string; 834 const svn_string_t *utf8_string; 835 svn_error_t *err; 836 837 src_string = svn_string_ncreate((const char *)name->val.p, 838 name->val.len, 839 result_pool); 840 switch (name->val.tag) 841 { 842 case ASN1_UTF8_STRING: 843 if (svn_utf__is_valid(src_string->data, src_string->len)) 844 return nul_escape(src_string, result_pool); 845 else 846 /* not a valid UTF-8 string, who knows what it is, 847 * so run it through the fuzzy_escape code. */ 848 return fuzzy_escape(src_string, result_pool); 849 break; 850 851 /* Both BMP and UNIVERSAL should always be in Big Endian (aka 852 * network byte order). But rumor has it that there are certs 853 * out there with other endianess and even Byte Order Marks. 854 * If we actually run into these, we might need to do something 855 * about it. */ 856 857 case ASN1_BMP_STRING: 858 if (0 != src_string->len % sizeof(apr_uint16_t)) 859 return fuzzy_escape(src_string, result_pool); 860 err = svn_utf__utf16_to_utf8(&utf8_string, 861 (const void*)(src_string->data), 862 src_string->len / sizeof(apr_uint16_t), 863 TRUE, result_pool, result_pool); 864 break; 865 866 case ASN1_UNIVERSAL_STRING: 867 if (0 != src_string->len % sizeof(apr_int32_t)) 868 return fuzzy_escape(src_string, result_pool); 869 err = svn_utf__utf32_to_utf8(&utf8_string, 870 (const void*)(src_string->data), 871 src_string->len / sizeof(apr_int32_t), 872 TRUE, result_pool, result_pool); 873 break; 874 875 /* Despite what all the IETF, ISO, ITU bits say everything out 876 * on the Internet that I can find treats this as ISO-8859-1. 877 * Even the name is misleading, it's not actually T.61. All the 878 * gory details can be found in the Character Sets section of: 879 * https://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt 880 */ 881 case ASN1_T61_STRING: 882 err = latin1_to_utf8(&utf8_string, src_string, result_pool); 883 break; 884 885 /* This leaves two types out there in the wild. PrintableString, 886 * which is just a subset of ASCII and IA5 which is ASCII (though 887 * 0x24 '$' and 0x23 '#' may be defined with differnet symbols 888 * depending on the location, in practice it seems everyone just 889 * treats it as ASCII). Since these are just ASCII run through 890 * the fuzzy_escape code to deal with anything that isn't actually 891 * ASCII. There shouldn't be any other types here but if we find 892 * a cert with some other encoding, the best we can do is the 893 * fuzzy_escape(). Note: Technically IA5 isn't valid in this 894 * context, however in the real world it may pop up. */ 895 default: 896 return fuzzy_escape(src_string, result_pool); 897 } 898 899 if (err) 900 { 901 svn_error_clear(err); 902 return fuzzy_escape(src_string, result_pool); 903 } 904 905 return nul_escape(utf8_string, result_pool); 906} 907 908static svn_error_t * 909x509_name_to_certinfo(apr_array_header_t **result, 910 const x509_name *dn, 911 apr_pool_t *scratch_pool, 912 apr_pool_t *result_pool) 913{ 914 const x509_name *name = dn; 915 916 *result = apr_array_make(result_pool, 6, sizeof(svn_x509_name_attr_t *)); 917 918 while (name != NULL) { 919 svn_x509_name_attr_t *attr = apr_palloc(result_pool, sizeof(svn_x509_name_attr_t)); 920 921 attr->oid_len = name->oid.len; 922 attr->oid = apr_palloc(result_pool, attr->oid_len); 923 memcpy(attr->oid, name->oid.p, attr->oid_len); 924 attr->utf8_value = x509name_to_utf8_string(name, result_pool); 925 if (!attr->utf8_value) 926 /* this should never happen */ 927 attr->utf8_value = apr_pstrdup(result_pool, "??"); 928 APR_ARRAY_PUSH(*result, const svn_x509_name_attr_t *) = attr; 929 930 name = name->next; 931 } 932 933 return SVN_NO_ERROR; 934} 935 936static svn_boolean_t 937is_hostname(const char *str) 938{ 939 apr_size_t i, len = strlen(str); 940 941 for (i = 0; i < len; i++) 942 { 943 char c = str[i]; 944 945 /* '-' is only legal when not at the start or end of a label */ 946 if (c == '-') 947 { 948 if (i + 1 != len) 949 { 950 if (str[i + 1] == '.') 951 return FALSE; /* '-' preceeds a '.' */ 952 } 953 else 954 return FALSE; /* '-' is at end of string */ 955 956 /* determine the previous character. */ 957 if (i == 0) 958 return FALSE; /* '-' is at start of string */ 959 else 960 if (str[i - 1] == '.') 961 return FALSE; /* '-' follows a '.' */ 962 } 963 else if (c != '*' && c != '.' && !svn_ctype_isalnum(c)) 964 return FALSE; /* some character not allowed */ 965 } 966 967 return TRUE; 968} 969 970static const char * 971x509parse_get_cn(apr_array_header_t *subject) 972{ 973 int i; 974 975 for (i = 0; i < subject->nelts; ++i) 976 { 977 const svn_x509_name_attr_t *attr = APR_ARRAY_IDX(subject, i, const svn_x509_name_attr_t *); 978 if (equal(attr->oid, attr->oid_len, 979 SVN_X509_OID_COMMON_NAME, sizeof(SVN_X509_OID_COMMON_NAME) - 1)) 980 return attr->utf8_value; 981 } 982 983 return NULL; 984} 985 986 987static void 988x509parse_get_hostnames(svn_x509_certinfo_t *ci, x509_cert *crt, 989 apr_pool_t *result_pool, apr_pool_t *scratch_pool) 990{ 991 ci->hostnames = NULL; 992 993 if (crt->dnsnames->nelts > 0) 994 { 995 int i; 996 997 ci->hostnames = apr_array_make(result_pool, crt->dnsnames->nelts, 998 sizeof(const char*)); 999 1000 /* Subject Alt Names take priority */ 1001 for (i = 0; i < crt->dnsnames->nelts; i++) 1002 { 1003 x509_buf *dnsname = APR_ARRAY_IDX(crt->dnsnames, i, x509_buf *); 1004 const svn_string_t *temp = svn_string_ncreate((const char *)dnsname->p, 1005 dnsname->len, 1006 scratch_pool); 1007 1008 APR_ARRAY_PUSH(ci->hostnames, const char*) 1009 = fuzzy_escape(temp, result_pool); 1010 } 1011 } 1012 else 1013 { 1014 /* no SAN then get the hostname from the CommonName on the cert */ 1015 const char *utf8_value; 1016 1017 utf8_value = x509parse_get_cn(ci->subject); 1018 1019 if (utf8_value && is_hostname(utf8_value)) 1020 { 1021 ci->hostnames = apr_array_make(result_pool, 1, sizeof(const char*)); 1022 APR_ARRAY_PUSH(ci->hostnames, const char*) = utf8_value; 1023 } 1024 } 1025} 1026 1027/* 1028 * Parse one certificate. 1029 */ 1030svn_error_t * 1031svn_x509_parse_cert(svn_x509_certinfo_t **certinfo, 1032 const char *buf, 1033 apr_size_t buflen, 1034 apr_pool_t *result_pool, 1035 apr_pool_t *scratch_pool) 1036{ 1037 svn_error_t *err; 1038 ptrdiff_t len; 1039 const unsigned char *p; 1040 const unsigned char *end; 1041 x509_cert *crt; 1042 svn_x509_certinfo_t *ci; 1043 1044 crt = apr_pcalloc(scratch_pool, sizeof(*crt)); 1045 p = (const unsigned char *)buf; 1046 len = buflen; 1047 end = p + len; 1048 1049 /* 1050 * Certificate ::= SEQUENCE { 1051 * tbsCertificate TBSCertificate, 1052 * signatureAlgorithm AlgorithmIdentifier, 1053 * signatureValue BIT STRING } 1054 */ 1055 err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 1056 if (err) 1057 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1058 1059 if (len != (end - p)) 1060 { 1061 err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 1062 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1063 } 1064 1065 /* 1066 * TBSCertificate ::= SEQUENCE { 1067 */ 1068 err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 1069 if (err) 1070 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1071 1072 end = p + len; 1073 1074 /* 1075 * Version ::= INTEGER { v1(0), v2(1), v3(2) } 1076 * 1077 * CertificateSerialNumber ::= INTEGER 1078 * 1079 * signature AlgorithmIdentifier 1080 */ 1081 SVN_ERR(x509_get_version(&p, end, &crt->version)); 1082 SVN_ERR(x509_get_serial(&p, end, &crt->serial)); 1083 SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid1)); 1084 1085 crt->version++; 1086 1087 if (crt->version > 3) 1088 return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL); 1089 1090 /* 1091 * issuer Name 1092 */ 1093 err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 1094 if (err) 1095 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1096 1097 SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool)); 1098 1099 /* 1100 * Validity ::= SEQUENCE { 1101 * notBefore Time, 1102 * notAfter Time } 1103 * 1104 */ 1105 SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end, 1106 scratch_pool)); 1107 1108 /* 1109 * subject Name 1110 */ 1111 err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 1112 if (err) 1113 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1114 1115 SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool)); 1116 1117 /* 1118 * SubjectPublicKeyInfo ::= SEQUENCE 1119 * algorithm AlgorithmIdentifier, 1120 * subjectPublicKey BIT STRING } 1121 */ 1122 err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE); 1123 if (err) 1124 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1125 1126 /* Skip pubkey. */ 1127 p += len; 1128 1129 /* 1130 * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 1131 * -- If present, version shall be v2 or v3 1132 * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 1133 * -- If present, version shall be v2 or v3 1134 * extensions [3] EXPLICIT Extensions OPTIONAL 1135 * -- If present, version shall be v3 1136 */ 1137 crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *)); 1138 1139 /* Try to parse issuerUniqueID, subjectUniqueID and extensions for *every* 1140 * version (X.509 v1, v2 and v3), not just v2 or v3. If they aren't present, 1141 * we are fine, but we don't want to throw an error if they are. v1 and v2 1142 * certificates with the corresponding extra fields are ill-formed per RFC 1143 * 5280 s. 4.1, but we suspect they could exist in the real world. Other 1144 * X.509 parsers (e.g., within OpenSSL or Microsoft CryptoAPI) aren't picky 1145 * about these certificates, and we also allow them. */ 1146 SVN_ERR(x509_get_uid(&p, end, &crt->issuer_id, 1)); 1147 SVN_ERR(x509_get_uid(&p, end, &crt->subject_id, 2)); 1148 SVN_ERR(x509_get_ext(crt->dnsnames, &p, end)); 1149 1150 if (p != end) 1151 { 1152 err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 1153 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1154 } 1155 1156 end = (const unsigned char*) buf + buflen; 1157 1158 /* 1159 * signatureAlgorithm AlgorithmIdentifier, 1160 * signatureValue BIT STRING 1161 */ 1162 SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2)); 1163 1164 if (!oids_equal(&crt->sig_oid1, &crt->sig_oid2)) 1165 return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL); 1166 1167 SVN_ERR(x509_get_sig(&p, end, &crt->sig)); 1168 1169 if (p != end) 1170 { 1171 err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL); 1172 return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL); 1173 } 1174 1175 ci = apr_pcalloc(result_pool, sizeof(*ci)); 1176 1177 /* Get the subject name */ 1178 SVN_ERR(x509_name_to_certinfo(&ci->subject, &crt->subject, 1179 scratch_pool, result_pool)); 1180 1181 /* Get the issuer name */ 1182 SVN_ERR(x509_name_to_certinfo(&ci->issuer, &crt->issuer, 1183 scratch_pool, result_pool)); 1184 1185 /* Copy the validity range */ 1186 ci->valid_from = crt->valid_from; 1187 ci->valid_to = crt->valid_to; 1188 1189 /* Calculate the SHA1 digest of the certificate, otherwise known as 1190 the fingerprint */ 1191 SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen, 1192 result_pool)); 1193 1194 /* Construct the array of host names */ 1195 x509parse_get_hostnames(ci, crt, result_pool, scratch_pool); 1196 1197 *certinfo = ci; 1198 return SVN_NO_ERROR; 1199} 1200 1201