Modified: subversion/branches/svn-auth-x509/subversion/libsvn_subr/x509parse.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/libsvn_subr/x509parse.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/libsvn_subr/x509parse.c (original)
+++ subversion/branches/svn-auth-x509/subversion/libsvn_subr/x509parse.c Mon Aug 11 08:54:38 2014
@@ -1,1196 +1,1196 @@
-/*
- * X.509 certificate and private key decoding
- *
- * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
- *
- * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the names of PolarSSL or XySSL nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-/*
- * The ITU-T X.509 standard defines a certificate format for PKI.
- *
- * http://www.ietf.org/rfc/rfc5280.txt
- * http://www.ietf.org/rfc/rfc3279.txt
- * http://www.ietf.org/rfc/rfc6818.txt
- *
- * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
- *
- * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
- * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
- */
-
-#include <apr_pools.h>
-#include <apr_tables.h>
-#include "svn_hash.h"
-#include "svn_string.h"
-#include "svn_time.h"
-#include "svn_checksum.h"
-#include "svn_utf.h"
-#include "svn_ctype.h"
-#include "svn_x509.h"
-#include "private/svn_utf_private.h"
-#include "private/svn_string_private.h"
-
-#include "x509.h"
-
-#include <string.h>
-#include <stdio.h>
-
-/*
- * ASN.1 DER decoding routines
- */
-static svn_error_t *
-asn1_get_len(const unsigned char **p, const unsigned char *end,
- ptrdiff_t *len)
-{
- if ((end - *p) < 1)
- return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
-
- if ((**p & 0x80) == 0)
- *len = *(*p)++;
- else
- switch (**p & 0x7F)
- {
- case 1:
- if ((end - *p) < 2)
- return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
-
- *len = (*p)[1];
- (*p) += 2;
- break;
-
- case 2:
- if ((end - *p) < 3)
- return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
-
- *len = ((*p)[1] << 8) | (*p)[2];
- (*p) += 3;
- break;
-
- default:
- return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
- break;
- }
-
- if (*len > (end - *p))
- return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-asn1_get_tag(const unsigned char **p,
- const unsigned char *end, ptrdiff_t *len, int tag)
-{
- if ((end - *p) < 1)
- return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
-
- if (**p != tag)
- return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
-
- (*p)++;
-
- return svn_error_trace(asn1_get_len(p, end, len));
-}
-
-static svn_error_t *
-asn1_get_int(const unsigned char **p, const unsigned char *end, int *val)
-{
- ptrdiff_t len;
-
- SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER));
-
- if (len > (int)sizeof(int) || (**p & 0x80) != 0)
- return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
-
- *val = 0;
-
- while (len-- > 0) {
- *val = (*val << 8) | **p;
- (*p)++;
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Version ::= INTEGER { v1(0), v2(1), v3(2) }
- */
-static svn_error_t *
-x509_get_version(const unsigned char **p, const unsigned char *end, int *ver)
-{
- svn_error_t *err;
- ptrdiff_t len;
-
- err = asn1_get_tag(p, end, &len,
- ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0);
- if (err)
- {
- if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
- {
- svn_error_clear(err);
- *ver = 0;
- return SVN_NO_ERROR;
- }
-
- return svn_error_trace(err);
- }
-
- end = *p + len;
-
- err = asn1_get_int(p, end, ver);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
-
- if (*p != end)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * CertificateSerialNumber ::= INTEGER
- */
-static svn_error_t *
-x509_get_serial(const unsigned char **p,
- const unsigned char *end, x509_buf * serial)
-{
- svn_error_t *err;
-
- if ((end - *p) < 1)
- {
- err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
- }
-
- if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) &&
- **p != ASN1_INTEGER)
- {
- err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
- }
-
- serial->tag = *(*p)++;
-
- err = asn1_get_len(p, end, &serial->len);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
-
- serial->p = *p;
- *p += serial->len;
-
- return SVN_NO_ERROR;
-}
-
-/*
- * AlgorithmIdentifier ::= SEQUENCE {
- * algorithm OBJECT IDENTIFIER,
- * parameters ANY DEFINED BY algorithm OPTIONAL }
- */
-static svn_error_t *
-x509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg)
-{
- svn_error_t *err;
- ptrdiff_t len;
-
- err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
-
- end = *p + len;
- alg->tag = **p;
-
- err = asn1_get_tag(p, end, &alg->len, ASN1_OID);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
-
- alg->p = *p;
- *p += alg->len;
-
- if (*p == end)
- return SVN_NO_ERROR;
-
- /*
- * assume the algorithm parameters must be NULL
- */
- err = asn1_get_tag(p, end, &len, ASN1_NULL);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
-
- if (*p != end)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
- }
-
- return SVN_NO_ERROR;
-}
-
-/*
- * RelativeDistinguishedName ::=
- * SET OF AttributeTypeAndValue
- *
- * AttributeTypeAndValue ::= SEQUENCE {
- * type AttributeType,
- * value AttributeValue }
- *
- * AttributeType ::= OBJECT IDENTIFIER
- *
- * AttributeValue ::= ANY DEFINED BY AttributeType
- */
-static svn_error_t *
-x509_get_name(const unsigned char **p, const unsigned char *end,
- x509_name * cur, apr_pool_t *result_pool)
-{
- svn_error_t *err;
- ptrdiff_t len;
- const unsigned char *end2;
- x509_buf *oid;
- x509_buf *val;
-
- err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SET);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
-
- end2 = end;
- end = *p + len;
-
- err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
-
- if (*p + len != end)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
- }
-
- oid = &cur->oid;
- oid->tag = **p;
-
- err = asn1_get_tag(p, end, &oid->len, ASN1_OID);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
-
- oid->p = *p;
- *p += oid->len;
-
- if ((end - *p) < 1)
- {
- err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
- }
-
- if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING &&
- **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING &&
- **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING)
- {
- err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
- }
-
- val = &cur->val;
- val->tag = *(*p)++;
-
- err = asn1_get_len(p, end, &val->len);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
-
- val->p = *p;
- *p += val->len;
-
- cur->next = NULL;
-
- if (*p != end)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
- }
-
- /*
- * recurse until end of SEQUENCE is reached
- */
- if (*p == end2)
- return SVN_NO_ERROR;
-
- cur->next = (x509_name *) apr_palloc(result_pool, sizeof(x509_name));
-
- if (cur->next == NULL)
- return SVN_NO_ERROR;
-
- return svn_error_trace(x509_get_name(p, end2, cur->next, result_pool));
-}
-
-/* Retrieve the date from the X.509 cert data between *P and END in either
- * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and
- * 4.1.2.5.2 respectively) and place the result in WHEN using SCRATCH_POOL
- * for temporary allocations. */
-static svn_error_t *
-x509_get_date(apr_time_t *when,
- const unsigned char **p,
- const unsigned char *end,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
- apr_status_t ret;
- int tag;
- ptrdiff_t len;
- char *date;
- apr_time_exp_t xt = { 0 };
- char tz;
-
- tag = **p;
- err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME);
- if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
- {
- svn_error_clear(err);
- tag = **p;
- err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME);
- }
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
-
- date = apr_pstrndup(scratch_pool, (const char *) *p, len);
- switch (tag)
- {
- case ASN1_UTC_TIME:
- if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c",
- &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
- &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
-
- /* UTCTime only provides a 2 digit year. X.509 specifies that years
- * greater than or equal to 50 must be interpreted as 19YY and years
- * less than 50 be interpreted as 20YY. This format is not used for
- * years greater than 2049. apr_time_exp_t wants years as the number
- * of years since 1900, so don't convert to 4 digits here. */
- xt.tm_year += 100 * (xt.tm_year < 50);
- break;
-
- case ASN1_GENERALIZED_TIME:
- if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c",
- &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
- &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
-
- /* GeneralizedTime has the full 4 digit year. But apr_time_exp_t
- * wants years as the number of years since 1900. */
- xt.tm_year -= 1900;
- break;
-
- default:
- /* shouldn't ever get here because we should error out above in the
- * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
- break;
- }
-
- /* check that the timezone is GMT
- * ASN.1 allows for the timezone to be specified but X.509 says it must
- * always be GMT. A little bit of extra paranoia here seems like a good
- * idea. */
- if (tz != 'Z')
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
-
- /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */
- xt.tm_mon -= 1;
-
- ret = apr_time_exp_gmt_get(when, &xt);
- if (ret)
- return svn_error_wrap_apr(ret, NULL);
-
- *p += len;
-
- return SVN_NO_ERROR;
-}
-
-/*
- * Validity ::= SEQUENCE {
- * notBefore Time,
- * notAfter Time }
- *
- * Time ::= CHOICE {
- * utcTime UTCTime,
- * generalTime GeneralizedTime }
- */
-static svn_error_t *
-x509_get_dates(apr_time_t *from,
- apr_time_t *to,
- const unsigned char **p,
- const unsigned char *end,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
- ptrdiff_t len;
-
- err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
-
- end = *p + len;
-
- SVN_ERR(x509_get_date(from, p, end, scratch_pool));
-
- SVN_ERR(x509_get_date(to, p, end, scratch_pool));
-
- if (*p != end)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
- }
-
- return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-x509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig)
-{
- svn_error_t *err;
- ptrdiff_t len;
-
- sig->tag = **p;
-
- err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL);
-
- if (--len < 1 || *(*p)++ != 0)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL);
-
- sig->len = len;
- sig->p = *p;
-
- *p += len;
-
- return SVN_NO_ERROR;
-}
-
-/*
- * X.509 v2/v3 unique identifier (not parsed)
- */
-static svn_error_t *
-x509_get_uid(const unsigned char **p,
- const unsigned char *end, x509_buf * uid, int n)
-{
- svn_error_t *err;
-
- if (*p == end)
- return SVN_NO_ERROR;
-
- uid->tag = **p;
-
- err = asn1_get_tag(p, end, &uid->len,
- ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n);
- if (err)
- {
- if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
- {
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
-
- return svn_error_trace(err);
- }
-
- uid->p = *p;
- *p += uid->len;
-
- return SVN_NO_ERROR;
-}
-
-/*
- * X.509 v3 extensions (not parsed)
- */
-static svn_error_t *
-x509_get_ext(apr_array_header_t *dnsnames,
- const unsigned char **p,
- const unsigned char *end)
-{
- svn_error_t *err;
- ptrdiff_t len;
-
- if (*p == end)
- return SVN_NO_ERROR;
-
- err = asn1_get_tag(p, end, &len,
- ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3);
- if (err)
- {
- /* If there aren't extensions that's ok they aren't required */
- if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
- {
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
-
- return svn_error_trace(err);
- }
-
- end = *p + len;
-
- SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE));
-
- if (end != *p + len)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
- }
-
- while (*p < end)
- {
- ptrdiff_t ext_len;
- const unsigned char *ext_start, *sna_end;
- err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
- NULL);
- ext_start = *p;
-
- err = asn1_get_tag(p, end, &len, ASN1_OID);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
- NULL);
-
- /* skip all extensions except SubjectAltName */
- if (len != sizeof(OID_SUBJECT_ALT_NAME) - 1 ||
- memcmp(*p, OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1) != 0)
- {
- *p += ext_len - (*p - ext_start);
- continue;
- }
- *p += len;
-
- err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
- NULL);
-
- /* SubjectAltName ::= GeneralNames
-
- GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
-
- GeneralName ::= CHOICE {
- other Name [0] OtherName,
- rfc822Name [1] IA5String,
- dNSName [2] IA5String,
- x400Address [3] ORAddress,
- directoryName [4] Name,
- ediPartyName [5] EDIPartyName,
- uniformResourceIdentifier [6] IA5String,
- iPAddress [7] OCTET STRING,
- registeredID [8] OBJECT IDENTIFIER } */
- sna_end = *p + len;
-
- err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
- NULL);
-
- if (sna_end != *p + len)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
- }
-
- while (*p < sna_end)
- {
- err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC |
- ASN1_PRIMITIVE | 2);
- if (err)
- {
- /* not not a dNSName */
- if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
- {
- svn_error_clear(err);
- /* need to skip the tag and then find the length to
- * skip to ignore this SNA entry. */
- (*p)++;
- SVN_ERR(asn1_get_len(p, sna_end, &len));
- *p += len;
- continue;
- }
- }
- else
- {
- /* We found a dNSName entry */
- x509_buf *dnsname = apr_palloc(dnsnames->pool,
- sizeof(x509_buf));
- dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */
- dnsname->len = len;
- dnsname->p = *p;
- APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname;
- }
-
- *p += len;
- }
-
- }
-
- return SVN_NO_ERROR;
-}
-
-/* Escape all non-ascii or control characters similar to
- * svn_xml_fuzzy_escape() and svn_utf_cstring_from_utf8_fuzzy().
- * All of the encoding formats somewhat overlap with ascii (BMPString
- * and UniversalString are actually always wider so you'll end up
- * with a bunch of escaped nul bytes, but ideally we don't get here
- * for those). */
-static const svn_string_t *
-fuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool)
-{
- const char *end = src->data + src->len;
- const char *p = src->data, *q;
- svn_stringbuf_t *outstr;
- char escaped_char[6]; /* ? \ u u u \0 */
-
- for (q = p; q < end; q++)
- {
- if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q))
- break;
- }
-
- if (q == end)
- return src;
-
- outstr = svn_stringbuf_create_empty(result_pool);
- while (1)
- {
- q = p;
-
- /* Traverse till either unsafe character or eos. */
- while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q))
- q++;
-
- /* copy chunk before marker */
- svn_stringbuf_appendbytes(outstr, p, q - p);
-
- if (q == end)
- break;
-
- apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u",
- (unsigned char) *q);
- svn_stringbuf_appendcstr(outstr, escaped_char);
-
- p = q + 1;
- }
-
- return svn_stringbuf__morph_into_string(outstr);
-}
-
-/* Escape only NUL characters from a string that is presumed to
- * be UTF-8 encoded. */
-static const svn_string_t *
-nul_escape(const svn_string_t *src, apr_pool_t *result_pool)
-{
- const char *end = src->data + src->len;
- const char *p = src->data, *q;
- svn_stringbuf_t *outstr;
-
- for (q = p; q < end; q++)
- {
- if (*q == '\0')
- break;
- }
-
- if (q == end)
- return src;
-
- outstr = svn_stringbuf_create_empty(result_pool);
- while (1)
- {
- q = p;
-
- /* Traverse till either unsafe character or eos. */
- while (q < end && *q != '\0')
- q++;
-
- /* copy chunk before marker */
- svn_stringbuf_appendbytes(outstr, p, q - p);
-
- if (q == end)
- break;
-
- svn_stringbuf_appendcstr(outstr, "?\\000");
-
- p = q + 1;
- }
-
- return svn_stringbuf__morph_into_string(outstr);
-}
-
-
-/* Convert an ISO-8859-1 (Latin-1) string to UTF-8.
- ISO-8859-1 is a strict subset of Unicode. */
-static svn_error_t *
-latin1_to_utf8(const svn_string_t **result, const svn_string_t *src,
- apr_pool_t *result_pool)
-{
- apr_int32_t *ucs4buf;
- svn_membuf_t resultbuf;
- apr_size_t length;
- apr_size_t i;
- svn_string_t *res;
-
- ucs4buf = apr_palloc(result_pool, src->len * sizeof(*ucs4buf));
- for (i = 0; i < src->len; ++i)
- ucs4buf[i] = (unsigned char)(src->data[i]);
-
- svn_membuf__create(&resultbuf, 2 * src->len, result_pool);
- SVN_ERR(svn_utf__encode_ucs4_string(
- &resultbuf, ucs4buf, src->len, &length));
-
- res = apr_palloc(result_pool, sizeof(*res));
- res->data = resultbuf.data;
- res->len = length;
- *result = res;
- return SVN_NO_ERROR;
-}
-
-/* Make a best effort to convert a X.509 name to a UTF-8 encoded
- * string and return it. If we can't properly convert just do a
- * fuzzy conversion so we have something to display. */
-static const svn_string_t *
-x509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool)
-{
- const svn_string_t *src_string;
- const svn_string_t *utf8_string;
- svn_error_t *err;
-
- src_string = svn_string_ncreate((const char *)name->val.p,
- name->val.len,
- result_pool);
- switch (name->val.tag)
- {
- case ASN1_UTF8_STRING:
- if (svn_utf__is_valid(src_string->data, src_string->len))
- return nul_escape(src_string, result_pool);
- else
- /* not a valid UTF-8 string, who knows what it is,
- * so run it through the fuzzy_escape code. */
- return fuzzy_escape(src_string, result_pool);
- break;
-
- /* Both BMP and UNIVERSAL should always be in Big Endian (aka
- * network byte order). But rumor has it that there are certs
- * out there with other endianess and even Byte Order Marks.
- * If we actually run into these, we might need to do something
- * about it. */
-
- case ASN1_BMP_STRING:
- if (0 != src_string->len % sizeof(apr_uint16_t))
- return fuzzy_escape(src_string, result_pool);
- err = svn_utf__utf16_to_utf8(&utf8_string,
- (const void*)(src_string->data),
- src_string->len / sizeof(apr_uint16_t),
- TRUE, result_pool, result_pool);
- break;
-
- case ASN1_UNIVERSAL_STRING:
- if (0 != src_string->len % sizeof(apr_int32_t))
- return fuzzy_escape(src_string, result_pool);
- err = svn_utf__utf32_to_utf8(&utf8_string,
- (const void*)(src_string->data),
- src_string->len / sizeof(apr_int32_t),
- TRUE, result_pool, result_pool);
- break;
-
- /* Despite what all the IETF, ISO, ITU bits say everything out
- * on the Internet that I can find treats this as ISO-8859-1.
- * Even the name is misleading, it's not actually T.61. All the
- * gory details can be found in the Character Sets section of:
- * https://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt
- */
- case ASN1_T61_STRING:
- err = latin1_to_utf8(&utf8_string, src_string, result_pool);
- break;
-
- /* This leaves two types out there in the wild. PrintableString,
- * which is just a subset of ASCII and IA5 which is ASCII (though
- * 0x24 '$' and 0x23 '#' may be defined with differnet symbols
- * depending on the location, in practice it seems everyone just
- * treats it as ASCII). Since these are just ASCII run through
- * the fuzzy_escape code to deal with anything that isn't actually
- * ASCII. There shouldn't be any other types here but if we find
- * a cert with some other encoding, the best we can do is the
- * fuzzy_escape(). Note: Technically IA5 isn't valid in this
- * context, however in the real world it may pop up. */
- default:
- return fuzzy_escape(src_string, result_pool);
- }
-
- if (err)
- {
- svn_error_clear(err);
- return fuzzy_escape(src_string, result_pool);
- }
-
- return nul_escape(utf8_string, result_pool);
-}
-
-/*
- * Store the name from dn in printable form into buf,
- * using scratch_pool for any temporary allocations.
- */
-static void
-x509parse_dn_gets(svn_stringbuf_t *buf, const x509_name * dn,
- apr_pool_t *scratch_pool)
-{
- const x509_name *name;
- const char *temp;
-
- name = dn;
-
- while (name != NULL) {
- const svn_string_t *utf8_value;
-
- if (name != dn)
- svn_stringbuf_appendcstr(buf, ", ");
-
- if (memcmp(name->oid.p, OID_X520, 2) == 0) {
- switch (name->oid.p[2]) {
- case X520_COMMON_NAME:
- svn_stringbuf_appendcstr(buf, "CN=");
- break;
-
- case X520_COUNTRY:
- svn_stringbuf_appendcstr(buf, "C=");
- break;
-
- case X520_LOCALITY:
- svn_stringbuf_appendcstr(buf, "L=");
- break;
-
- case X520_STATE:
- svn_stringbuf_appendcstr(buf, "ST=");
- break;
-
- case X520_ORGANIZATION:
- svn_stringbuf_appendcstr(buf, "O=");
- break;
-
- case X520_ORG_UNIT:
- svn_stringbuf_appendcstr(buf, "OU=");
- break;
-
- default:
- temp = apr_psprintf(scratch_pool, "0x%02X=", name->oid.p[2]);
- svn_stringbuf_appendcstr(buf, temp);
- break;
- }
- } else if (memcmp(name->oid.p, OID_PKCS9, 8) == 0) {
- switch (name->oid.p[8]) {
- case PKCS9_EMAIL:
- svn_stringbuf_appendcstr(buf, "emailAddress=");
- break;
-
- default:
- temp = apr_psprintf(scratch_pool, "0x%02X=", name->oid.p[8]);
- svn_stringbuf_appendcstr(buf, temp);
- break;
- }
- } else
- svn_stringbuf_appendcstr(buf, "\?\?=");
-
- utf8_value = x509name_to_utf8_string(name, scratch_pool);
- if (utf8_value)
- svn_stringbuf_appendbytes(buf, utf8_value->data, utf8_value->len);
- else
- /* this should never happen */
- svn_stringbuf_appendfill(buf, '?', 2);
-
- name = name->next;
- }
-}
-
-static svn_boolean_t
-is_hostname(const svn_string_t *str)
-{
- apr_size_t i;
-
- for (i = 0; i < str->len; i++)
- {
- char c = str->data[i];
-
- /* '-' is only legal when not at the start or end of a label */
- if (c == '-')
- {
- if (i + 1 != str->len)
- {
- if (str->data[i + 1] == '.')
- return FALSE; /* '-' preceeds a '.' */
- }
- else
- return FALSE; /* '-' is at end of string */
-
- /* determine the previous character. */
- if (i == 0)
- return FALSE; /* '-' is at start of string */
- else
- if (str->data[i - 1] == '.')
- return FALSE; /* '-' follows a '.' */
- }
- else if (c != '*' && c != '.' && !svn_ctype_isalnum(c))
- return FALSE; /* some character not allowed */
- }
-
- return TRUE;
-}
-
-static void
-x509parse_get_hostnames(apr_array_header_t **names, x509_cert *crt,
- apr_pool_t *result_pool, apr_pool_t *scratch_pool)
-{
- if (crt->dnsnames->nelts > 0)
- {
- int i;
-
- *names = apr_array_make(result_pool, crt->dnsnames->nelts,
- sizeof(const char*));
-
- /* Subject Alt Names take priority */
- for (i = 0; i < crt->dnsnames->nelts; i++)
- {
- x509_buf *dnsname = APR_ARRAY_IDX(crt->dnsnames, i, x509_buf *);
- const svn_string_t *temp = svn_string_ncreate((const char *)dnsname->p,
- dnsname->len,
- scratch_pool);
-
- temp = fuzzy_escape(temp, result_pool);
- APR_ARRAY_PUSH(*names, const char*) = temp->data;
- }
- }
- else
- {
- const x509_name *name = &crt->subject;
- const svn_string_t *utf8_value = NULL;
-
- /* no SAN then get the hostname from the CommonName on the cert */
- while (name != NULL)
- {
- if (memcmp(name->oid.p, OID_X520, 2) == 0)
- {
- if (name->oid.p[2] == X520_COMMON_NAME)
- {
- utf8_value = x509name_to_utf8_string(name, scratch_pool);
- if (is_hostname(utf8_value))
- break;
- else
- utf8_value = NULL;
- }
- }
- name = name->next;
- }
-
- if (utf8_value)
- {
- *names = apr_array_make(result_pool, 1, sizeof(const char*));
- APR_ARRAY_PUSH(*names, const char*) = utf8_value->data;
- }
- }
-}
-
-/*
- * Parse one certificate.
- */
-svn_error_t *
-svn_x509_parse_cert(svn_x509_certinfo_t **certinfo,
- const char *buf,
- apr_size_t buflen,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
- ptrdiff_t len;
- const unsigned char *p;
- const unsigned char *end;
- x509_cert *crt;
- svn_x509_certinfo_t *ci;
- svn_stringbuf_t *namebuf;
-
- crt = apr_pcalloc(scratch_pool, sizeof(*crt));
- p = (const unsigned char *)buf;
- len = buflen;
- end = p + len;
-
- /*
- * Certificate ::= SEQUENCE {
- * tbsCertificate TBSCertificate,
- * signatureAlgorithm AlgorithmIdentifier,
- * signatureValue BIT STRING }
- */
- err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, NULL, NULL);
-
- if (len != (end - p))
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
- }
-
- /*
- * TBSCertificate ::= SEQUENCE {
- */
- err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
-
- end = p + len;
-
- /*
- * Version ::= INTEGER { v1(0), v2(1), v3(2) }
- *
- * CertificateSerialNumber ::= INTEGER
- *
- * signature AlgorithmIdentifier
- */
- SVN_ERR(x509_get_version(&p, end, &crt->version));
- SVN_ERR(x509_get_serial(&p, end, &crt->serial));
- SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid1));
-
- crt->version++;
-
- if (crt->version > 3)
- return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL);
-
- /*
- * issuer Name
- */
- err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
-
- SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool));
-
- /*
- * Validity ::= SEQUENCE {
- * notBefore Time,
- * notAfter Time }
- *
- */
- SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end,
- scratch_pool));
-
- /*
- * subject Name
- */
- err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
-
- SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool));
-
- /*
- * SubjectPublicKeyInfo ::= SEQUENCE
- * algorithm AlgorithmIdentifier,
- * subjectPublicKey BIT STRING }
- */
- err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
- if (err)
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
-
- /* Skip pubkey. */
- p += len;
-
- /*
- * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
- * -- If present, version shall be v2 or v3
- * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
- * -- If present, version shall be v2 or v3
- * extensions [3] EXPLICIT Extensions OPTIONAL
- * -- If present, version shall be v3
- */
- if (crt->version == 2 || crt->version == 3)
- SVN_ERR(x509_get_uid(&p, end, &crt->issuer_id, 1));
-
- if (crt->version == 2 || crt->version == 3)
- SVN_ERR(x509_get_uid(&p, end, &crt->subject_id, 2));
-
- if (crt->version == 3)
- {
- crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *));
- SVN_ERR(x509_get_ext(crt->dnsnames, &p, end));
- }
-
- if (p != end)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
- }
-
- end = (const unsigned char*) buf + buflen;
-
- /*
- * signatureAlgorithm AlgorithmIdentifier,
- * signatureValue BIT STRING
- */
- SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2));
-
- if (memcmp(crt->sig_oid1.p, crt->sig_oid2.p, 9) != 0)
- return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL);
-
- SVN_ERR(x509_get_sig(&p, end, &crt->sig));
-
- if (p != end)
- {
- err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
- return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
- }
-
- ci = apr_pcalloc(result_pool, sizeof(*ci));
-
- /* Get the subject name */
- namebuf = svn_stringbuf_create_empty(result_pool);
- x509parse_dn_gets(namebuf, &crt->subject, scratch_pool);
- ci->subject = namebuf->data;
-
- /* Get the issuer name */
- namebuf = svn_stringbuf_create_empty(result_pool);
- x509parse_dn_gets(namebuf, &crt->issuer, scratch_pool);
- ci->issuer = namebuf->data;
-
- /* Copy the validity range */
- ci->valid_from = crt->valid_from;
- ci->valid_to = crt->valid_to;
-
- /* Calculate the SHA1 digest of the certificate, otherwise known as
- the fingerprint */
- SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen,
- result_pool));
-
- /* Construct the array of host names */
- x509parse_get_hostnames(&ci->hostnames, crt, result_pool, scratch_pool);
-
- *certinfo = ci;
- return SVN_NO_ERROR;
-}
-
+/*
+ * X.509 certificate and private key decoding
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the names of PolarSSL or XySSL nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * The ITU-T X.509 standard defines a certificate format for PKI.
+ *
+ * http://www.ietf.org/rfc/rfc5280.txt
+ * http://www.ietf.org/rfc/rfc3279.txt
+ * http://www.ietf.org/rfc/rfc6818.txt
+ *
+ * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
+ *
+ * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
+ * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
+ */
+
+#include <apr_pools.h>
+#include <apr_tables.h>
+#include "svn_hash.h"
+#include "svn_string.h"
+#include "svn_time.h"
+#include "svn_checksum.h"
+#include "svn_utf.h"
+#include "svn_ctype.h"
+#include "svn_x509.h"
+#include "private/svn_utf_private.h"
+#include "private/svn_string_private.h"
+
+#include "x509.h"
+
+#include <string.h>
+#include <stdio.h>
+
+/*
+ * ASN.1 DER decoding routines
+ */
+static svn_error_t *
+asn1_get_len(const unsigned char **p, const unsigned char *end,
+ ptrdiff_t *len)
+{
+ if ((end - *p) < 1)
+ return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
+
+ if ((**p & 0x80) == 0)
+ *len = *(*p)++;
+ else
+ switch (**p & 0x7F)
+ {
+ case 1:
+ if ((end - *p) < 2)
+ return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
+
+ *len = (*p)[1];
+ (*p) += 2;
+ break;
+
+ case 2:
+ if ((end - *p) < 3)
+ return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
+
+ *len = ((*p)[1] << 8) | (*p)[2];
+ (*p) += 3;
+ break;
+
+ default:
+ return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
+ break;
+ }
+
+ if (*len > (end - *p))
+ return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+asn1_get_tag(const unsigned char **p,
+ const unsigned char *end, ptrdiff_t *len, int tag)
+{
+ if ((end - *p) < 1)
+ return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
+
+ if (**p != tag)
+ return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
+
+ (*p)++;
+
+ return svn_error_trace(asn1_get_len(p, end, len));
+}
+
+static svn_error_t *
+asn1_get_int(const unsigned char **p, const unsigned char *end, int *val)
+{
+ ptrdiff_t len;
+
+ SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER));
+
+ if (len > (int)sizeof(int) || (**p & 0x80) != 0)
+ return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
+
+ *val = 0;
+
+ while (len-- > 0) {
+ *val = (*val << 8) | **p;
+ (*p)++;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * Version ::= INTEGER { v1(0), v2(1), v3(2) }
+ */
+static svn_error_t *
+x509_get_version(const unsigned char **p, const unsigned char *end, int *ver)
+{
+ svn_error_t *err;
+ ptrdiff_t len;
+
+ err = asn1_get_tag(p, end, &len,
+ ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
+ {
+ svn_error_clear(err);
+ *ver = 0;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(err);
+ }
+
+ end = *p + len;
+
+ err = asn1_get_int(p, end, ver);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
+
+ if (*p != end)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * CertificateSerialNumber ::= INTEGER
+ */
+static svn_error_t *
+x509_get_serial(const unsigned char **p,
+ const unsigned char *end, x509_buf * serial)
+{
+ svn_error_t *err;
+
+ if ((end - *p) < 1)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
+ }
+
+ if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) &&
+ **p != ASN1_INTEGER)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
+ }
+
+ serial->tag = *(*p)++;
+
+ err = asn1_get_len(p, end, &serial->len);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
+
+ serial->p = *p;
+ *p += serial->len;
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ */
+static svn_error_t *
+x509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg)
+{
+ svn_error_t *err;
+ ptrdiff_t len;
+
+ err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
+
+ end = *p + len;
+ alg->tag = **p;
+
+ err = asn1_get_tag(p, end, &alg->len, ASN1_OID);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
+
+ alg->p = *p;
+ *p += alg->len;
+
+ if (*p == end)
+ return SVN_NO_ERROR;
+
+ /*
+ * assume the algorithm parameters must be NULL
+ */
+ err = asn1_get_tag(p, end, &len, ASN1_NULL);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
+
+ if (*p != end)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * RelativeDistinguishedName ::=
+ * SET OF AttributeTypeAndValue
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue }
+ *
+ * AttributeType ::= OBJECT IDENTIFIER
+ *
+ * AttributeValue ::= ANY DEFINED BY AttributeType
+ */
+static svn_error_t *
+x509_get_name(const unsigned char **p, const unsigned char *end,
+ x509_name * cur, apr_pool_t *result_pool)
+{
+ svn_error_t *err;
+ ptrdiff_t len;
+ const unsigned char *end2;
+ x509_buf *oid;
+ x509_buf *val;
+
+ err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SET);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+
+ end2 = end;
+ end = *p + len;
+
+ err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+
+ if (*p + len != end)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+ }
+
+ oid = &cur->oid;
+ oid->tag = **p;
+
+ err = asn1_get_tag(p, end, &oid->len, ASN1_OID);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+
+ oid->p = *p;
+ *p += oid->len;
+
+ if ((end - *p) < 1)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+ }
+
+ if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING &&
+ **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING &&
+ **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+ }
+
+ val = &cur->val;
+ val->tag = *(*p)++;
+
+ err = asn1_get_len(p, end, &val->len);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+
+ val->p = *p;
+ *p += val->len;
+
+ cur->next = NULL;
+
+ if (*p != end)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
+ }
+
+ /*
+ * recurse until end of SEQUENCE is reached
+ */
+ if (*p == end2)
+ return SVN_NO_ERROR;
+
+ cur->next = (x509_name *) apr_palloc(result_pool, sizeof(x509_name));
+
+ if (cur->next == NULL)
+ return SVN_NO_ERROR;
+
+ return svn_error_trace(x509_get_name(p, end2, cur->next, result_pool));
+}
+
+/* Retrieve the date from the X.509 cert data between *P and END in either
+ * UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and
+ * 4.1.2.5.2 respectively) and place the result in WHEN using SCRATCH_POOL
+ * for temporary allocations. */
+static svn_error_t *
+x509_get_date(apr_time_t *when,
+ const unsigned char **p,
+ const unsigned char *end,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ apr_status_t ret;
+ int tag;
+ ptrdiff_t len;
+ char *date;
+ apr_time_exp_t xt = { 0 };
+ char tz;
+
+ tag = **p;
+ err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME);
+ if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
+ {
+ svn_error_clear(err);
+ tag = **p;
+ err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME);
+ }
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
+
+ date = apr_pstrndup(scratch_pool, (const char *) *p, len);
+ switch (tag)
+ {
+ case ASN1_UTC_TIME:
+ if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c",
+ &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
+ &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
+
+ /* UTCTime only provides a 2 digit year. X.509 specifies that years
+ * greater than or equal to 50 must be interpreted as 19YY and years
+ * less than 50 be interpreted as 20YY. This format is not used for
+ * years greater than 2049. apr_time_exp_t wants years as the number
+ * of years since 1900, so don't convert to 4 digits here. */
+ xt.tm_year += 100 * (xt.tm_year < 50);
+ break;
+
+ case ASN1_GENERALIZED_TIME:
+ if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c",
+ &xt.tm_year, &xt.tm_mon, &xt.tm_mday,
+ &xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
+
+ /* GeneralizedTime has the full 4 digit year. But apr_time_exp_t
+ * wants years as the number of years since 1900. */
+ xt.tm_year -= 1900;
+ break;
+
+ default:
+ /* shouldn't ever get here because we should error out above in the
+ * asn1_get_tag() bits but doesn't hurt to be extra paranoid. */
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
+ break;
+ }
+
+ /* check that the timezone is GMT
+ * ASN.1 allows for the timezone to be specified but X.509 says it must
+ * always be GMT. A little bit of extra paranoia here seems like a good
+ * idea. */
+ if (tz != 'Z')
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
+
+ /* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */
+ xt.tm_mon -= 1;
+
+ ret = apr_time_exp_gmt_get(when, &xt);
+ if (ret)
+ return svn_error_wrap_apr(ret, NULL);
+
+ *p += len;
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * Validity ::= SEQUENCE {
+ * notBefore Time,
+ * notAfter Time }
+ *
+ * Time ::= CHOICE {
+ * utcTime UTCTime,
+ * generalTime GeneralizedTime }
+ */
+static svn_error_t *
+x509_get_dates(apr_time_t *from,
+ apr_time_t *to,
+ const unsigned char **p,
+ const unsigned char *end,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ ptrdiff_t len;
+
+ err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
+
+ end = *p + len;
+
+ SVN_ERR(x509_get_date(from, p, end, scratch_pool));
+
+ SVN_ERR(x509_get_date(to, p, end, scratch_pool));
+
+ if (*p != end)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+x509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig)
+{
+ svn_error_t *err;
+ ptrdiff_t len;
+
+ sig->tag = **p;
+
+ err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL);
+
+ if (--len < 1 || *(*p)++ != 0)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL);
+
+ sig->len = len;
+ sig->p = *p;
+
+ *p += len;
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * X.509 v2/v3 unique identifier (not parsed)
+ */
+static svn_error_t *
+x509_get_uid(const unsigned char **p,
+ const unsigned char *end, x509_buf * uid, int n)
+{
+ svn_error_t *err;
+
+ if (*p == end)
+ return SVN_NO_ERROR;
+
+ uid->tag = **p;
+
+ err = asn1_get_tag(p, end, &uid->len,
+ ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n);
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
+ {
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(err);
+ }
+
+ uid->p = *p;
+ *p += uid->len;
+
+ return SVN_NO_ERROR;
+}
+
+/*
+ * X.509 v3 extensions (not parsed)
+ */
+static svn_error_t *
+x509_get_ext(apr_array_header_t *dnsnames,
+ const unsigned char **p,
+ const unsigned char *end)
+{
+ svn_error_t *err;
+ ptrdiff_t len;
+
+ if (*p == end)
+ return SVN_NO_ERROR;
+
+ err = asn1_get_tag(p, end, &len,
+ ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3);
+ if (err)
+ {
+ /* If there aren't extensions that's ok they aren't required */
+ if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
+ {
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(err);
+ }
+
+ end = *p + len;
+
+ SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE));
+
+ if (end != *p + len)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
+ }
+
+ while (*p < end)
+ {
+ ptrdiff_t ext_len;
+ const unsigned char *ext_start, *sna_end;
+ err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
+ NULL);
+ ext_start = *p;
+
+ err = asn1_get_tag(p, end, &len, ASN1_OID);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
+ NULL);
+
+ /* skip all extensions except SubjectAltName */
+ if (len != sizeof(OID_SUBJECT_ALT_NAME) - 1 ||
+ memcmp(*p, OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1) != 0)
+ {
+ *p += ext_len - (*p - ext_start);
+ continue;
+ }
+ *p += len;
+
+ err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
+ NULL);
+
+ /* SubjectAltName ::= GeneralNames
+
+ GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+
+ GeneralName ::= CHOICE {
+ other Name [0] OtherName,
+ rfc822Name [1] IA5String,
+ dNSName [2] IA5String,
+ x400Address [3] ORAddress,
+ directoryName [4] Name,
+ ediPartyName [5] EDIPartyName,
+ uniformResourceIdentifier [6] IA5String,
+ iPAddress [7] OCTET STRING,
+ registeredID [8] OBJECT IDENTIFIER } */
+ sna_end = *p + len;
+
+ err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
+ NULL);
+
+ if (sna_end != *p + len)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
+ }
+
+ while (*p < sna_end)
+ {
+ err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC |
+ ASN1_PRIMITIVE | 2);
+ if (err)
+ {
+ /* not not a dNSName */
+ if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
+ {
+ svn_error_clear(err);
+ /* need to skip the tag and then find the length to
+ * skip to ignore this SNA entry. */
+ (*p)++;
+ SVN_ERR(asn1_get_len(p, sna_end, &len));
+ *p += len;
+ continue;
+ }
+ }
+ else
+ {
+ /* We found a dNSName entry */
+ x509_buf *dnsname = apr_palloc(dnsnames->pool,
+ sizeof(x509_buf));
+ dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */
+ dnsname->len = len;
+ dnsname->p = *p;
+ APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname;
+ }
+
+ *p += len;
+ }
+
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Escape all non-ascii or control characters similar to
+ * svn_xml_fuzzy_escape() and svn_utf_cstring_from_utf8_fuzzy().
+ * All of the encoding formats somewhat overlap with ascii (BMPString
+ * and UniversalString are actually always wider so you'll end up
+ * with a bunch of escaped nul bytes, but ideally we don't get here
+ * for those). */
+static const svn_string_t *
+fuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool)
+{
+ const char *end = src->data + src->len;
+ const char *p = src->data, *q;
+ svn_stringbuf_t *outstr;
+ char escaped_char[6]; /* ? \ u u u \0 */
+
+ for (q = p; q < end; q++)
+ {
+ if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q))
+ break;
+ }
+
+ if (q == end)
+ return src;
+
+ outstr = svn_stringbuf_create_empty(result_pool);
+ while (1)
+ {
+ q = p;
+
+ /* Traverse till either unsafe character or eos. */
+ while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q))
+ q++;
+
+ /* copy chunk before marker */
+ svn_stringbuf_appendbytes(outstr, p, q - p);
+
+ if (q == end)
+ break;
+
+ apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u",
+ (unsigned char) *q);
+ svn_stringbuf_appendcstr(outstr, escaped_char);
+
+ p = q + 1;
+ }
+
+ return svn_stringbuf__morph_into_string(outstr);
+}
+
+/* Escape only NUL characters from a string that is presumed to
+ * be UTF-8 encoded. */
+static const svn_string_t *
+nul_escape(const svn_string_t *src, apr_pool_t *result_pool)
+{
+ const char *end = src->data + src->len;
+ const char *p = src->data, *q;
+ svn_stringbuf_t *outstr;
+
+ for (q = p; q < end; q++)
+ {
+ if (*q == '\0')
+ break;
+ }
+
+ if (q == end)
+ return src;
+
+ outstr = svn_stringbuf_create_empty(result_pool);
+ while (1)
+ {
+ q = p;
+
+ /* Traverse till either unsafe character or eos. */
+ while (q < end && *q != '\0')
+ q++;
+
+ /* copy chunk before marker */
+ svn_stringbuf_appendbytes(outstr, p, q - p);
+
+ if (q == end)
+ break;
+
+ svn_stringbuf_appendcstr(outstr, "?\\000");
+
+ p = q + 1;
+ }
+
+ return svn_stringbuf__morph_into_string(outstr);
+}
+
+
+/* Convert an ISO-8859-1 (Latin-1) string to UTF-8.
+ ISO-8859-1 is a strict subset of Unicode. */
+static svn_error_t *
+latin1_to_utf8(const svn_string_t **result, const svn_string_t *src,
+ apr_pool_t *result_pool)
+{
+ apr_int32_t *ucs4buf;
+ svn_membuf_t resultbuf;
+ apr_size_t length;
+ apr_size_t i;
+ svn_string_t *res;
+
+ ucs4buf = apr_palloc(result_pool, src->len * sizeof(*ucs4buf));
+ for (i = 0; i < src->len; ++i)
+ ucs4buf[i] = (unsigned char)(src->data[i]);
+
+ svn_membuf__create(&resultbuf, 2 * src->len, result_pool);
+ SVN_ERR(svn_utf__encode_ucs4_string(
+ &resultbuf, ucs4buf, src->len, &length));
+
+ res = apr_palloc(result_pool, sizeof(*res));
+ res->data = resultbuf.data;
+ res->len = length;
+ *result = res;
+ return SVN_NO_ERROR;
+}
+
+/* Make a best effort to convert a X.509 name to a UTF-8 encoded
+ * string and return it. If we can't properly convert just do a
+ * fuzzy conversion so we have something to display. */
+static const svn_string_t *
+x509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool)
+{
+ const svn_string_t *src_string;
+ const svn_string_t *utf8_string;
+ svn_error_t *err;
+
+ src_string = svn_string_ncreate((const char *)name->val.p,
+ name->val.len,
+ result_pool);
+ switch (name->val.tag)
+ {
+ case ASN1_UTF8_STRING:
+ if (svn_utf__is_valid(src_string->data, src_string->len))
+ return nul_escape(src_string, result_pool);
+ else
+ /* not a valid UTF-8 string, who knows what it is,
+ * so run it through the fuzzy_escape code. */
+ return fuzzy_escape(src_string, result_pool);
+ break;
+
+ /* Both BMP and UNIVERSAL should always be in Big Endian (aka
+ * network byte order). But rumor has it that there are certs
+ * out there with other endianess and even Byte Order Marks.
+ * If we actually run into these, we might need to do something
+ * about it. */
+
+ case ASN1_BMP_STRING:
+ if (0 != src_string->len % sizeof(apr_uint16_t))
+ return fuzzy_escape(src_string, result_pool);
+ err = svn_utf__utf16_to_utf8(&utf8_string,
+ (const void*)(src_string->data),
+ src_string->len / sizeof(apr_uint16_t),
+ TRUE, result_pool, result_pool);
+ break;
+
+ case ASN1_UNIVERSAL_STRING:
+ if (0 != src_string->len % sizeof(apr_int32_t))
+ return fuzzy_escape(src_string, result_pool);
+ err = svn_utf__utf32_to_utf8(&utf8_string,
+ (const void*)(src_string->data),
+ src_string->len / sizeof(apr_int32_t),
+ TRUE, result_pool, result_pool);
+ break;
+
+ /* Despite what all the IETF, ISO, ITU bits say everything out
+ * on the Internet that I can find treats this as ISO-8859-1.
+ * Even the name is misleading, it's not actually T.61. All the
+ * gory details can be found in the Character Sets section of:
+ * https://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt
+ */
+ case ASN1_T61_STRING:
+ err = latin1_to_utf8(&utf8_string, src_string, result_pool);
+ break;
+
+ /* This leaves two types out there in the wild. PrintableString,
+ * which is just a subset of ASCII and IA5 which is ASCII (though
+ * 0x24 '$' and 0x23 '#' may be defined with differnet symbols
+ * depending on the location, in practice it seems everyone just
+ * treats it as ASCII). Since these are just ASCII run through
+ * the fuzzy_escape code to deal with anything that isn't actually
+ * ASCII. There shouldn't be any other types here but if we find
+ * a cert with some other encoding, the best we can do is the
+ * fuzzy_escape(). Note: Technically IA5 isn't valid in this
+ * context, however in the real world it may pop up. */
+ default:
+ return fuzzy_escape(src_string, result_pool);
+ }
+
+ if (err)
+ {
+ svn_error_clear(err);
+ return fuzzy_escape(src_string, result_pool);
+ }
+
+ return nul_escape(utf8_string, result_pool);
+}
+
+/*
+ * Store the name from dn in printable form into buf,
+ * using scratch_pool for any temporary allocations.
+ */
+static void
+x509parse_dn_gets(svn_stringbuf_t *buf, const x509_name * dn,
+ apr_pool_t *scratch_pool)
+{
+ const x509_name *name;
+ const char *temp;
+
+ name = dn;
+
+ while (name != NULL) {
+ const svn_string_t *utf8_value;
+
+ if (name != dn)
+ svn_stringbuf_appendcstr(buf, ", ");
+
+ if (memcmp(name->oid.p, OID_X520, 2) == 0) {
+ switch (name->oid.p[2]) {
+ case X520_COMMON_NAME:
+ svn_stringbuf_appendcstr(buf, "CN=");
+ break;
+
+ case X520_COUNTRY:
+ svn_stringbuf_appendcstr(buf, "C=");
+ break;
+
+ case X520_LOCALITY:
+ svn_stringbuf_appendcstr(buf, "L=");
+ break;
+
+ case X520_STATE:
+ svn_stringbuf_appendcstr(buf, "ST=");
+ break;
+
+ case X520_ORGANIZATION:
+ svn_stringbuf_appendcstr(buf, "O=");
+ break;
+
+ case X520_ORG_UNIT:
+ svn_stringbuf_appendcstr(buf, "OU=");
+ break;
+
+ default:
+ temp = apr_psprintf(scratch_pool, "0x%02X=", name->oid.p[2]);
+ svn_stringbuf_appendcstr(buf, temp);
+ break;
+ }
+ } else if (memcmp(name->oid.p, OID_PKCS9, 8) == 0) {
+ switch (name->oid.p[8]) {
+ case PKCS9_EMAIL:
+ svn_stringbuf_appendcstr(buf, "emailAddress=");
+ break;
+
+ default:
+ temp = apr_psprintf(scratch_pool, "0x%02X=", name->oid.p[8]);
+ svn_stringbuf_appendcstr(buf, temp);
+ break;
+ }
+ } else
+ svn_stringbuf_appendcstr(buf, "\?\?=");
+
+ utf8_value = x509name_to_utf8_string(name, scratch_pool);
+ if (utf8_value)
+ svn_stringbuf_appendbytes(buf, utf8_value->data, utf8_value->len);
+ else
+ /* this should never happen */
+ svn_stringbuf_appendfill(buf, '?', 2);
+
+ name = name->next;
+ }
+}
+
+static svn_boolean_t
+is_hostname(const svn_string_t *str)
+{
+ apr_size_t i;
+
+ for (i = 0; i < str->len; i++)
+ {
+ char c = str->data[i];
+
+ /* '-' is only legal when not at the start or end of a label */
+ if (c == '-')
+ {
+ if (i + 1 != str->len)
+ {
+ if (str->data[i + 1] == '.')
+ return FALSE; /* '-' preceeds a '.' */
+ }
+ else
+ return FALSE; /* '-' is at end of string */
+
+ /* determine the previous character. */
+ if (i == 0)
+ return FALSE; /* '-' is at start of string */
+ else
+ if (str->data[i - 1] == '.')
+ return FALSE; /* '-' follows a '.' */
+ }
+ else if (c != '*' && c != '.' && !svn_ctype_isalnum(c))
+ return FALSE; /* some character not allowed */
+ }
+
+ return TRUE;
+}
+
+static void
+x509parse_get_hostnames(apr_array_header_t **names, x509_cert *crt,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ if (crt->dnsnames->nelts > 0)
+ {
+ int i;
+
+ *names = apr_array_make(result_pool, crt->dnsnames->nelts,
+ sizeof(const char*));
+
+ /* Subject Alt Names take priority */
+ for (i = 0; i < crt->dnsnames->nelts; i++)
+ {
+ x509_buf *dnsname = APR_ARRAY_IDX(crt->dnsnames, i, x509_buf *);
+ const svn_string_t *temp = svn_string_ncreate((const char *)dnsname->p,
+ dnsname->len,
+ scratch_pool);
+
+ temp = fuzzy_escape(temp, result_pool);
+ APR_ARRAY_PUSH(*names, const char*) = temp->data;
+ }
+ }
+ else
+ {
+ const x509_name *name = &crt->subject;
+ const svn_string_t *utf8_value = NULL;
+
+ /* no SAN then get the hostname from the CommonName on the cert */
+ while (name != NULL)
+ {
+ if (memcmp(name->oid.p, OID_X520, 2) == 0)
+ {
+ if (name->oid.p[2] == X520_COMMON_NAME)
+ {
+ utf8_value = x509name_to_utf8_string(name, scratch_pool);
+ if (is_hostname(utf8_value))
+ break;
+ else
+ utf8_value = NULL;
+ }
+ }
+ name = name->next;
+ }
+
+ if (utf8_value)
+ {
+ *names = apr_array_make(result_pool, 1, sizeof(const char*));
+ APR_ARRAY_PUSH(*names, const char*) = utf8_value->data;
+ }
+ }
+}
+
+/*
+ * Parse one certificate.
+ */
+svn_error_t *
+svn_x509_parse_cert(svn_x509_certinfo_t **certinfo,
+ const char *buf,
+ apr_size_t buflen,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ ptrdiff_t len;
+ const unsigned char *p;
+ const unsigned char *end;
+ x509_cert *crt;
+ svn_x509_certinfo_t *ci;
+ svn_stringbuf_t *namebuf;
+
+ crt = apr_pcalloc(scratch_pool, sizeof(*crt));
+ p = (const unsigned char *)buf;
+ len = buflen;
+ end = p + len;
+
+ /*
+ * Certificate ::= SEQUENCE {
+ * tbsCertificate TBSCertificate,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING }
+ */
+ err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, NULL, NULL);
+
+ if (len != (end - p))
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
+ }
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ */
+ err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
+
+ end = p + len;
+
+ /*
+ * Version ::= INTEGER { v1(0), v2(1), v3(2) }
+ *
+ * CertificateSerialNumber ::= INTEGER
+ *
+ * signature AlgorithmIdentifier
+ */
+ SVN_ERR(x509_get_version(&p, end, &crt->version));
+ SVN_ERR(x509_get_serial(&p, end, &crt->serial));
+ SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid1));
+
+ crt->version++;
+
+ if (crt->version > 3)
+ return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL);
+
+ /*
+ * issuer Name
+ */
+ err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
+
+ SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool));
+
+ /*
+ * Validity ::= SEQUENCE {
+ * notBefore Time,
+ * notAfter Time }
+ *
+ */
+ SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end,
+ scratch_pool));
+
+ /*
+ * subject Name
+ */
+ err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
+
+ SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool));
+
+ /*
+ * SubjectPublicKeyInfo ::= SEQUENCE
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ */
+ err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
+ if (err)
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
+
+ /* Skip pubkey. */
+ p += len;
+
+ /*
+ * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ * -- If present, version shall be v2 or v3
+ * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ * -- If present, version shall be v2 or v3
+ * extensions [3] EXPLICIT Extensions OPTIONAL
+ * -- If present, version shall be v3
+ */
+ if (crt->version == 2 || crt->version == 3)
+ SVN_ERR(x509_get_uid(&p, end, &crt->issuer_id, 1));
+
+ if (crt->version == 2 || crt->version == 3)
+ SVN_ERR(x509_get_uid(&p, end, &crt->subject_id, 2));
+
+ if (crt->version == 3)
+ {
+ crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *));
+ SVN_ERR(x509_get_ext(crt->dnsnames, &p, end));
+ }
+
+ if (p != end)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
+ }
+
+ end = (const unsigned char*) buf + buflen;
+
+ /*
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING
+ */
+ SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2));
+
+ if (memcmp(crt->sig_oid1.p, crt->sig_oid2.p, 9) != 0)
+ return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL);
+
+ SVN_ERR(x509_get_sig(&p, end, &crt->sig));
+
+ if (p != end)
+ {
+ err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
+ return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
+ }
+
+ ci = apr_pcalloc(result_pool, sizeof(*ci));
+
+ /* Get the subject name */
+ namebuf = svn_stringbuf_create_empty(result_pool);
+ x509parse_dn_gets(namebuf, &crt->subject, scratch_pool);
+ ci->subject = namebuf->data;
+
+ /* Get the issuer name */
+ namebuf = svn_stringbuf_create_empty(result_pool);
+ x509parse_dn_gets(namebuf, &crt->issuer, scratch_pool);
+ ci->issuer = namebuf->data;
+
+ /* Copy the validity range */
+ ci->valid_from = crt->valid_from;
+ ci->valid_to = crt->valid_to;
+
+ /* Calculate the SHA1 digest of the certificate, otherwise known as
+ the fingerprint */
+ SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen,
+ result_pool));
+
+ /* Construct the array of host names */
+ x509parse_get_hostnames(&ci->hostnames, crt, result_pool, scratch_pool);
+
+ *certinfo = ci;
+ return SVN_NO_ERROR;
+}
+
Propchange: subversion/branches/svn-auth-x509/subversion/libsvn_subr/x509parse.c
('svn:eol-style' removed)
Modified: subversion/branches/svn-auth-x509/subversion/libsvn_wc/adm_crawler.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/libsvn_wc/adm_crawler.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/libsvn_wc/adm_crawler.c (original)
+++ subversion/branches/svn-auth-x509/subversion/libsvn_wc/adm_crawler.c Mon Aug 11 08:54:38 2014
@@ -1083,8 +1083,10 @@ svn_wc__internal_transmit_text_deltas(co
/* ### Why '..._display()'? expected_md5_checksum should never be all-
* zero, but if it is, we would want to pass NULL not an all-zero
* digest to apply_textdelta(), wouldn't we? */
- base_digest_hex = svn_checksum_to_cstring_display(expected_md5_checksum,
- scratch_pool);
+ base_digest_hex =
+ svn_checksum_to_cstring_display2(expected_md5_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool);
SVN_ERR(editor->apply_textdelta(file_baton, base_digest_hex, scratch_pool,
&handler, &wh_baton));
Modified: subversion/branches/svn-auth-x509/subversion/libsvn_wc/entries.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/libsvn_wc/entries.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/libsvn_wc/entries.c (original)
+++ subversion/branches/svn-auth-x509/subversion/libsvn_wc/entries.c Mon Aug 11 08:54:38 2014
@@ -2055,10 +2055,14 @@ write_entry(struct write_baton **entry_n
local_relpath,
scratch_pool),
scratch_pool),
- svn_checksum_to_cstring_display(
- entry_md5_checksum, scratch_pool),
- svn_checksum_to_cstring_display(
- found_md5_checksum, scratch_pool));
+ svn_checksum_to_cstring_display2(
+ entry_md5_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool),
+ svn_checksum_to_cstring_display2(
+ found_md5_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool));
else
{
/* ### Not sure what conditions this should cover. */
Modified: subversion/branches/svn-auth-x509/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/libsvn_wc/update_editor.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/svn-auth-x509/subversion/libsvn_wc/update_editor.c Mon Aug 11 08:54:38 2014
@@ -3703,10 +3703,12 @@ apply_textdelta(void *file_baton,
" expected: %s\n"
" recorded: %s\n"),
svn_dirent_local_style(fb->local_abspath, pool),
- svn_checksum_to_cstring_display(expected_base_checksum,
- pool),
- svn_checksum_to_cstring_display(recorded_base_checksum,
- pool));
+ svn_checksum_to_cstring_display2(expected_base_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ pool),
+ svn_checksum_to_cstring_display2(recorded_base_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ pool));
/* Open the text base for reading, unless this is an added file. */
Modified: subversion/branches/svn-auth-x509/subversion/libsvn_wc/wc_db_pristine.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/libsvn_wc/wc_db_pristine.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/libsvn_wc/wc_db_pristine.c (original)
+++ subversion/branches/svn-auth-x509/subversion/libsvn_wc/wc_db_pristine.c Mon Aug 11 08:54:38 2014
@@ -127,8 +127,9 @@ svn_wc__db_pristine_get_path(const char
return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
_("The pristine text with checksum '%s' was "
"not found"),
- svn_checksum_to_cstring_display(sha1_checksum,
- scratch_pool));
+ svn_checksum_to_cstring_display2(sha1_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool));
SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath,
sha1_checksum,
@@ -192,8 +193,9 @@ pristine_read_txn(svn_stream_t **content
{
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Pristine text '%s' not present"),
- svn_checksum_to_cstring_display(
- sha1_checksum, scratch_pool));
+ svn_checksum_to_cstring_display2(
+ sha1_checksum, SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool));
}
/* Open the file as a readable stream. It will remain readable even when
@@ -318,7 +320,9 @@ pristine_install_txn(svn_sqlite__db_t *s
return svn_error_createf(
SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
_("New pristine text '%s' has different size: %ld versus %ld"),
- svn_checksum_to_cstring_display(sha1_checksum, scratch_pool),
+ svn_checksum_to_cstring_display2(sha1_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool),
(long int)finfo1.size, (long int)finfo2.size);
}
}
@@ -466,8 +470,9 @@ svn_wc__db_pristine_get_md5(const svn_ch
return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
_("The pristine text with checksum '%s' was "
"not found"),
- svn_checksum_to_cstring_display(sha1_checksum,
- scratch_pool));
+ svn_checksum_to_cstring_display2(sha1_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool));
SVN_ERR(svn_sqlite__column_checksum(md5_checksum, stmt, 0, result_pool));
SVN_ERR_ASSERT((*md5_checksum)->kind == svn_checksum_md5);
@@ -505,8 +510,9 @@ svn_wc__db_pristine_get_sha1(const svn_c
return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
_("The pristine text with MD5 checksum '%s' was "
"not found"),
- svn_checksum_to_cstring_display(md5_checksum,
- scratch_pool));
+ svn_checksum_to_cstring_display2(md5_checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ scratch_pool));
SVN_ERR(svn_sqlite__column_checksum(sha1_checksum, stmt, 0, result_pool));
SVN_ERR_ASSERT((*sha1_checksum)->kind == svn_checksum_sha1);
Modified: subversion/branches/svn-auth-x509/subversion/mod_dav_svn/activity.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/mod_dav_svn/activity.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/mod_dav_svn/activity.c (original)
+++ subversion/branches/svn-auth-x509/subversion/mod_dav_svn/activity.c Mon Aug 11 08:54:38 2014
@@ -53,7 +53,9 @@ escape_activity(const char *activity_id,
svn_checksum_t *checksum;
svn_error_clear(svn_checksum(&checksum, svn_checksum_md5, activity_id,
strlen(activity_id), pool));
- return svn_checksum_to_cstring_display(checksum, pool);
+ return svn_checksum_to_cstring_display2(checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ pool);
}
/* Return filename for ACTIVITY_ID under the repository in REPOS. */
Modified: subversion/branches/svn-auth-x509/subversion/svn/auth-cmd.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/svn/auth-cmd.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/svn/auth-cmd.c (original)
+++ subversion/branches/svn-auth-x509/subversion/svn/auth-cmd.c Mon Aug 11 08:54:38 2014
@@ -193,8 +193,10 @@ show_cert(const svn_string_t *pem_cert,
SVN_ERR(svn_cmdline_printf(scratch_pool, _("Issuer: %s\n"),
svn_x509_certinfo_get_issuer(certinfo)));
SVN_ERR(svn_cmdline_printf(scratch_pool, _("Fingerprint: %s\n"),
- svn_x509_fingerprint_to_display(
+ svn_checksum_to_cstring_display2(
svn_x509_certinfo_get_digest(certinfo),
+ SVN_CHECKSUM_CSTRING_UPPER |
+ SVN_CHECKSUM_CSTRING_COLONS,
scratch_pool)));
hostnames = svn_x509_certinfo_get_hostnames(certinfo);
Modified: subversion/branches/svn-auth-x509/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/svnserve/serve.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/svnserve/serve.c (original)
+++ subversion/branches/svn-auth-x509/subversion/svnserve/serve.c Mon Aug 11 08:54:38 2014
@@ -1562,7 +1562,9 @@ static svn_error_t *get_file(svn_ra_svn_
SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root,
full_path, TRUE, pool));
- hex_digest = svn_checksum_to_cstring_display(checksum, pool);
+ hex_digest = svn_checksum_to_cstring_display2(checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ pool);
/* Fetch the file's explicit and/or inherited properties if
requested. Although the wants-iprops boolean was added to the
Modified: subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/checksum-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/checksum-test.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/checksum-test.c (original)
+++ subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/checksum-test.c Mon Aug 11 08:54:38 2014
@@ -45,7 +45,9 @@ checksum_parse_kind(const char *digest,
svn_checksum_t *checksum;
SVN_ERR(svn_checksum_parse_hex(&checksum, kind, digest, pool));
- checksum_display = svn_checksum_to_cstring_display(checksum, pool);
+ checksum_display = svn_checksum_to_cstring_display2(checksum,
+ SVN_CHECKSUM_CSTRING_LOWER,
+ pool);
if (strcmp(checksum_display, digest) != 0)
return svn_error_createf
Modified: subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/x509-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/x509-test.c?rev=1617225&r1=1617224&r2=1617225&view=diff
==============================================================================
--- subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/x509-test.c (original)
+++ subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/x509-test.c Mon Aug 11 08:54:38 2014
@@ -474,8 +474,10 @@ compare_results(struct x509_test *xt,
xt->subject,
pool));
- v = svn_checksum_to_cstring_display(
- svn_x509_certinfo_get_digest(certinfo), pool);
+ v = svn_checksum_to_cstring_display2(
+ svn_x509_certinfo_get_digest(certinfo),
+ SVN_CHECKSUM_CSTRING_LOWER,
+ pool);
if (!v)
return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
"No SHA1 digest for cert '%s'", xt->subject);
@@ -515,27 +517,7 @@ test_x509_parse_cert(apr_pool_t *pool)
return SVN_NO_ERROR;
}
-static svn_error_t *
-test_svn_x509_fingerprint_to_display(apr_pool_t *pool)
-{
- svn_checksum_t *md5_fingerprint;
- svn_checksum_t *sha1_fingerprint;
-
- SVN_ERR(svn_checksum_parse_hex(&md5_fingerprint, svn_checksum_md5,
- "8518b76f7a45fe4de2d0955085b41f98", pool));
- SVN_ERR(svn_checksum_parse_hex(&sha1_fingerprint, svn_checksum_sha1,
- "74d82379bcc6771454377db03b912c2b62704139", pool));
-
- SVN_TEST_STRING_ASSERT(
- svn_x509_fingerprint_to_display(md5_fingerprint, pool),
- "85:18:B7:6F:7A:45:FE:4D:E2:D0:95:50:85:B4:1F:98");
-
- SVN_TEST_STRING_ASSERT(
- svn_x509_fingerprint_to_display(sha1_fingerprint, pool),
- "74:D8:23:79:BC:C6:77:14:54:37:7D:B0:3B:91:2C:2B:62:70:41:39");
-
- return SVN_NO_ERROR;
-}
+
/* The test table. */
static int max_threads = 1;
@@ -545,8 +527,6 @@ static struct svn_test_descriptor_t test
SVN_TEST_NULL,
SVN_TEST_PASS2(test_x509_parse_cert,
"test svn_x509_parse_cert"),
- SVN_TEST_PASS2(test_svn_x509_fingerprint_to_display,
- "test svn_x509_fingerprint_to_display"),
SVN_TEST_NULL
};
Propchange: subversion/branches/svn-auth-x509/subversion/tests/libsvn_subr/x509-test.c
('svn:eol-style' removed)
|