2
* X.509 certificate and private key decoding
4
* Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
6
* Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
14
* * Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* * Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
* * Neither the names of PolarSSL or XySSL nor the names of its contributors
20
* may be used to endorse or promote products derived from this software
21
* without specific prior written permission.
23
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
* The ITU-T X.509 standard defines a certificate format for PKI.
38
* http://www.ietf.org/rfc/rfc5280.txt
39
* http://www.ietf.org/rfc/rfc3279.txt
40
* http://www.ietf.org/rfc/rfc6818.txt
42
* ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc
44
* http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf
45
* http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
48
#include <apr_pools.h>
49
#include <apr_tables.h>
51
#include "svn_string.h"
53
#include "svn_checksum.h"
55
#include "svn_ctype.h"
56
#include "private/svn_utf_private.h"
57
#include "private/svn_string_private.h"
65
* ASN.1 DER decoding routines
68
asn1_get_len(const unsigned char **p, const unsigned char *end,
72
return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
74
if ((**p & 0x80) == 0)
81
return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
89
return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
91
*len = ((*p)[1] << 8) | (*p)[2];
96
return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
100
if (*len > (end - *p))
101
return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
107
asn1_get_tag(const unsigned char **p,
108
const unsigned char *end, ptrdiff_t *len, int tag)
111
return svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
114
return svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
118
return svn_error_trace(asn1_get_len(p, end, len));
122
asn1_get_int(const unsigned char **p, const unsigned char *end, int *val)
126
SVN_ERR(asn1_get_tag(p, end, &len, ASN1_INTEGER));
128
/* Reject bit patterns that would overflow the output and those that
129
represent negative values. */
130
if (len > (int)sizeof(int) || (**p & 0x80) != 0)
131
return svn_error_create(SVN_ERR_ASN1_INVALID_LENGTH, NULL, NULL);
136
/* This would be undefined for bit-patterns of negative values. */
137
*val = (*val << 8) | **p;
145
equal(const void *left, apr_size_t left_len,
146
const void *right, apr_size_t right_len)
148
if (left_len != right_len)
151
return memcmp(left, right, right_len) == 0;
155
oids_equal(x509_buf *left, x509_buf *right)
157
return equal(left->p, left->len,
158
right->p, right->len);
162
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
165
x509_get_version(const unsigned char **p, const unsigned char *end, int *ver)
171
* As defined in the Basic Certificate fields:
172
* version [0] EXPLICIT Version DEFAULT v1,
173
* the version is the context specific tag 0.
175
err = asn1_get_tag(p, end, &len,
176
ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0);
179
if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
181
svn_error_clear(err);
186
return svn_error_trace(err);
191
err = asn1_get_int(p, end, ver);
193
return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
197
err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
198
return svn_error_create(SVN_ERR_X509_CERT_INVALID_VERSION, err, NULL);
205
* CertificateSerialNumber ::= INTEGER
208
x509_get_serial(const unsigned char **p,
209
const unsigned char *end, x509_buf * serial)
215
err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
216
return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
219
if (**p != (ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2) &&
222
err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
223
return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
226
serial->tag = *(*p)++;
228
err = asn1_get_len(p, end, &serial->len);
230
return svn_error_create(SVN_ERR_X509_CERT_INVALID_SERIAL, err, NULL);
239
* AlgorithmIdentifier ::= SEQUENCE {
240
* algorithm OBJECT IDENTIFIER,
241
* parameters ANY DEFINED BY algorithm OPTIONAL }
244
x509_get_alg(const unsigned char **p, const unsigned char *end, x509_buf * alg)
249
err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
251
return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
256
err = asn1_get_tag(p, end, &alg->len, ASN1_OID);
258
return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
267
* assume the algorithm parameters must be NULL
269
err = asn1_get_tag(p, end, &len, ASN1_NULL);
271
return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
275
err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
276
return svn_error_create(SVN_ERR_X509_CERT_INVALID_ALG, err, NULL);
283
* AttributeTypeAndValue ::= SEQUENCE {
284
* type AttributeType,
285
* value AttributeValue }
287
* AttributeType ::= OBJECT IDENTIFIER
289
* AttributeValue ::= ANY DEFINED BY AttributeType
292
x509_get_attribute(const unsigned char **p, const unsigned char *end,
293
x509_name *cur, apr_pool_t *result_pool)
300
err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
302
return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
308
err = asn1_get_tag(p, end, &oid->len, ASN1_OID);
310
return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
318
err = svn_error_create(SVN_ERR_ASN1_OUT_OF_DATA, NULL, NULL);
319
return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
322
if (**p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING &&
323
**p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING &&
324
**p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING)
326
err = svn_error_create(SVN_ERR_ASN1_UNEXPECTED_TAG, NULL, NULL);
327
return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
333
err = asn1_get_len(p, end, &val->len);
335
return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
344
err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
345
return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
352
* RelativeDistinguishedName ::=
353
* SET SIZE (1..MAX) OF AttributeTypeAndValue
356
x509_get_name(const unsigned char **p, const unsigned char *name_end,
357
x509_name *name, apr_pool_t *result_pool)
361
const unsigned char *set_end;
362
x509_name *cur = NULL;
364
err = asn1_get_tag(p, name_end, &len, ASN1_CONSTRUCTED | ASN1_SET);
366
return svn_error_create(SVN_ERR_X509_CERT_INVALID_NAME, err, NULL);
371
* iterate until the end of the SET is reached
381
cur->next = apr_palloc(result_pool, sizeof(x509_name));
384
SVN_ERR(x509_get_attribute(p, set_end, cur, result_pool));
388
* recurse until end of SEQUENCE (name) is reached
393
cur->next = apr_palloc(result_pool, sizeof(x509_name));
395
return svn_error_trace(x509_get_name(p, name_end, cur->next, result_pool));
398
/* Retrieve the date from the X.509 cert data between *P and END in either
399
* UTCTime or GeneralizedTime format (as defined in RFC 5280 s. 4.1.2.5.1 and
400
* 4.1.2.5.2 respectively) and place the result in WHEN using SCRATCH_POOL
401
* for temporary allocations. */
403
x509_get_date(apr_time_t *when,
404
const unsigned char **p,
405
const unsigned char *end,
406
apr_pool_t *scratch_pool)
413
apr_time_exp_t xt = { 0 };
416
err = asn1_get_tag(p, end, &len, ASN1_UTC_TIME);
417
if (err && err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
419
svn_error_clear(err);
420
err = asn1_get_tag(p, end, &len, ASN1_GENERALIZED_TIME);
421
tag = ASN1_GENERALIZED_TIME;
428
return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
430
date = apr_pstrndup(scratch_pool, (const char *) *p, len);
434
if (sscanf(date, "%2d%2d%2d%2d%2d%2d%c",
435
&xt.tm_year, &xt.tm_mon, &xt.tm_mday,
436
&xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
437
return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
439
/* UTCTime only provides a 2 digit year. X.509 specifies that years
440
* greater than or equal to 50 must be interpreted as 19YY and years
441
* less than 50 be interpreted as 20YY. This format is not used for
442
* years greater than 2049. apr_time_exp_t wants years as the number
443
* of years since 1900, so don't convert to 4 digits here. */
444
xt.tm_year += 100 * (xt.tm_year < 50);
447
case ASN1_GENERALIZED_TIME:
448
if (sscanf(date, "%4d%2d%2d%2d%2d%2d%c",
449
&xt.tm_year, &xt.tm_mon, &xt.tm_mday,
450
&xt.tm_hour, &xt.tm_min, &xt.tm_sec, &tz) < 6)
451
return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
453
/* GeneralizedTime has the full 4 digit year. But apr_time_exp_t
454
* wants years as the number of years since 1900. */
459
/* shouldn't ever get here because we should error out above in the
460
* asn1_get_tag() bits but doesn't hurt to be extra paranoid. */
461
return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
465
/* check that the timezone is GMT
466
* ASN.1 allows for the timezone to be specified but X.509 says it must
467
* always be GMT. A little bit of extra paranoia here seems like a good
470
return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, NULL, NULL);
472
/* apr_time_exp_t expects months to be zero indexed, 0=Jan, 11=Dec. */
475
ret = apr_time_exp_gmt_get(when, &xt);
477
return svn_error_wrap_apr(ret, NULL);
485
* Validity ::= SEQUENCE {
491
* generalTime GeneralizedTime }
494
x509_get_dates(apr_time_t *from,
496
const unsigned char **p,
497
const unsigned char *end,
498
apr_pool_t *scratch_pool)
503
err = asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
505
return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
509
SVN_ERR(x509_get_date(from, p, end, scratch_pool));
511
SVN_ERR(x509_get_date(to, p, end, scratch_pool));
515
err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
516
return svn_error_create(SVN_ERR_X509_CERT_INVALID_DATE, err, NULL);
523
x509_get_sig(const unsigned char **p, const unsigned char *end, x509_buf * sig)
528
err = asn1_get_tag(p, end, &len, ASN1_BIT_STRING);
530
return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, err, NULL);
532
sig->tag = ASN1_BIT_STRING;
534
if (--len < 1 || *(*p)++ != 0)
535
return svn_error_create(SVN_ERR_X509_CERT_INVALID_SIGNATURE, NULL, NULL);
546
* X.509 v2/v3 unique identifier (not parsed)
549
x509_get_uid(const unsigned char **p,
550
const unsigned char *end, x509_buf * uid, int n)
557
err = asn1_get_tag(p, end, &uid->len,
558
ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n);
561
if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
563
svn_error_clear(err);
567
return svn_error_trace(err);
570
uid->tag = ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n;
578
* X.509 v3 extensions (not parsed)
581
x509_get_ext(apr_array_header_t *dnsnames,
582
const unsigned char **p,
583
const unsigned char *end)
591
err = asn1_get_tag(p, end, &len,
592
ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3);
595
/* If there aren't extensions that's ok they aren't required */
596
if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
598
svn_error_clear(err);
602
return svn_error_trace(err);
607
SVN_ERR(asn1_get_tag(p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE));
611
err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
612
return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
618
const unsigned char *ext_start, *sna_end;
619
err = asn1_get_tag(p, end, &ext_len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
621
return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
625
err = asn1_get_tag(p, end, &len, ASN1_OID);
627
return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
630
/* skip all extensions except SubjectAltName */
632
OID_SUBJECT_ALT_NAME, sizeof(OID_SUBJECT_ALT_NAME) - 1))
634
*p += ext_len - (*p - ext_start);
639
err = asn1_get_tag(p, end, &len, ASN1_OCTET_STRING);
641
return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
644
/* SubjectAltName ::= GeneralNames
646
GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
648
GeneralName ::= CHOICE {
649
other Name [0] OtherName,
650
rfc822Name [1] IA5String,
651
dNSName [2] IA5String,
652
x400Address [3] ORAddress,
653
directoryName [4] Name,
654
ediPartyName [5] EDIPartyName,
655
uniformResourceIdentifier [6] IA5String,
656
iPAddress [7] OCTET STRING,
657
registeredID [8] OBJECT IDENTIFIER } */
660
err = asn1_get_tag(p, sna_end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
662
return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err,
665
if (sna_end != *p + len)
667
err = svn_error_create(SVN_ERR_ASN1_LENGTH_MISMATCH, NULL, NULL);
668
return svn_error_create(SVN_ERR_X509_CERT_INVALID_EXTENSIONS, err, NULL);
673
err = asn1_get_tag(p, sna_end, &len, ASN1_CONTEXT_SPECIFIC |
677
/* not not a dNSName */
678
if (err->apr_err == SVN_ERR_ASN1_UNEXPECTED_TAG)
680
svn_error_clear(err);
681
/* need to skip the tag and then find the length to
682
* skip to ignore this SNA entry. */
684
SVN_ERR(asn1_get_len(p, sna_end, &len));
689
return svn_error_trace(err);
693
/* We found a dNSName entry */
694
x509_buf *dnsname = apr_palloc(dnsnames->pool,
696
dnsname->tag = ASN1_IA5_STRING; /* implicit based on dNSName */
699
APR_ARRAY_PUSH(dnsnames, x509_buf *) = dnsname;
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. */
717
fuzzy_escape(const svn_string_t *src, apr_pool_t *result_pool)
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 */
724
for (q = p; q < end; q++)
726
if (!svn_ctype_isascii(*q) || svn_ctype_iscntrl(*q))
733
outstr = svn_stringbuf_create_empty(result_pool);
738
/* Traverse till either unsafe character or eos. */
739
while (q < end && svn_ctype_isascii(*q) && !svn_ctype_iscntrl(*q))
742
/* copy chunk before marker */
743
svn_stringbuf_appendbytes(outstr, p, q - p);
748
apr_snprintf(escaped_char, sizeof(escaped_char), "?\\%03u",
750
svn_stringbuf_appendcstr(outstr, escaped_char);
758
/* Escape only NUL characters from a string that is presumed to
759
* be UTF-8 encoded and return a nul-terminated C string. */
761
nul_escape(const svn_string_t *src, apr_pool_t *result_pool)
763
const char *end = src->data + src->len;
764
const char *p = src->data, *q;
765
svn_stringbuf_t *outstr;
767
for (q = p; q < end; q++)
776
outstr = svn_stringbuf_create_empty(result_pool);
781
/* Traverse till either unsafe character or eos. */
782
while (q < end && *q != '\0')
785
/* copy chunk before marker */
786
svn_stringbuf_appendbytes(outstr, p, q - p);
791
svn_stringbuf_appendcstr(outstr, "?\\000");
800
/* Convert an ISO-8859-1 (Latin-1) string to UTF-8.
801
ISO-8859-1 is a strict subset of Unicode. */
803
latin1_to_utf8(const svn_string_t **result, const svn_string_t *src,
804
apr_pool_t *result_pool)
806
apr_int32_t *ucs4buf;
807
svn_membuf_t resultbuf;
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]);
816
svn_membuf__create(&resultbuf, 2 * src->len, result_pool);
817
SVN_ERR(svn_utf__encode_ucs4_string(
818
&resultbuf, ucs4buf, src->len, &length));
820
res = apr_palloc(result_pool, sizeof(*res));
821
res->data = resultbuf.data;
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. */
831
x509name_to_utf8_string(const x509_name *name, apr_pool_t *result_pool)
833
const svn_string_t *src_string;
834
const svn_string_t *utf8_string;
837
src_string = svn_string_ncreate((const char *)name->val.p,
840
switch (name->val.tag)
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);
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);
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
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);
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);
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
881
case ASN1_T61_STRING:
882
err = latin1_to_utf8(&utf8_string, src_string, result_pool);
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. */
896
return fuzzy_escape(src_string, result_pool);
901
svn_error_clear(err);
902
return fuzzy_escape(src_string, result_pool);
905
return nul_escape(utf8_string, result_pool);
909
x509_name_to_certinfo(apr_array_header_t **result,
911
apr_pool_t *scratch_pool,
912
apr_pool_t *result_pool)
914
const x509_name *name = dn;
916
*result = apr_array_make(result_pool, 6, sizeof(svn_x509_name_attr_t *));
918
while (name != NULL) {
919
svn_x509_name_attr_t *attr = apr_palloc(result_pool, sizeof(svn_x509_name_attr_t));
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;
937
is_hostname(const char *str)
939
apr_size_t i, len = strlen(str);
941
for (i = 0; i < len; i++)
945
/* '-' is only legal when not at the start or end of a label */
950
if (str[i + 1] == '.')
951
return FALSE; /* '-' preceeds a '.' */
954
return FALSE; /* '-' is at end of string */
956
/* determine the previous character. */
958
return FALSE; /* '-' is at start of string */
960
if (str[i - 1] == '.')
961
return FALSE; /* '-' follows a '.' */
963
else if (c != '*' && c != '.' && !svn_ctype_isalnum(c))
964
return FALSE; /* some character not allowed */
971
x509parse_get_cn(apr_array_header_t *subject)
975
for (i = 0; i < subject->nelts; ++i)
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;
988
x509parse_get_hostnames(svn_x509_certinfo_t *ci, x509_cert *crt,
989
apr_pool_t *result_pool, apr_pool_t *scratch_pool)
991
ci->hostnames = NULL;
993
if (crt->dnsnames->nelts > 0)
997
ci->hostnames = apr_array_make(result_pool, crt->dnsnames->nelts,
998
sizeof(const char*));
1000
/* Subject Alt Names take priority */
1001
for (i = 0; i < crt->dnsnames->nelts; i++)
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,
1008
APR_ARRAY_PUSH(ci->hostnames, const char*)
1009
= fuzzy_escape(temp, result_pool);
1014
/* no SAN then get the hostname from the CommonName on the cert */
1015
const char *utf8_value;
1017
utf8_value = x509parse_get_cn(ci->subject);
1019
if (utf8_value && is_hostname(utf8_value))
1021
ci->hostnames = apr_array_make(result_pool, 1, sizeof(const char*));
1022
APR_ARRAY_PUSH(ci->hostnames, const char*) = utf8_value;
1028
* Parse one certificate.
1031
svn_x509_parse_cert(svn_x509_certinfo_t **certinfo,
1034
apr_pool_t *result_pool,
1035
apr_pool_t *scratch_pool)
1039
const unsigned char *p;
1040
const unsigned char *end;
1042
svn_x509_certinfo_t *ci;
1044
crt = apr_pcalloc(scratch_pool, sizeof(*crt));
1045
p = (const unsigned char *)buf;
1050
* Certificate ::= SEQUENCE {
1051
* tbsCertificate TBSCertificate,
1052
* signatureAlgorithm AlgorithmIdentifier,
1053
* signatureValue BIT STRING }
1055
err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1057
return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1059
if (len != (end - p))
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);
1066
* TBSCertificate ::= SEQUENCE {
1068
err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1070
return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1075
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
1077
* CertificateSerialNumber ::= INTEGER
1079
* signature AlgorithmIdentifier
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));
1087
if (crt->version > 3)
1088
return svn_error_create(SVN_ERR_X509_CERT_UNKNOWN_VERSION, NULL, NULL);
1093
err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1095
return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1097
SVN_ERR(x509_get_name(&p, p + len, &crt->issuer, scratch_pool));
1100
* Validity ::= SEQUENCE {
1105
SVN_ERR(x509_get_dates(&crt->valid_from, &crt->valid_to, &p, end,
1111
err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1113
return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
1115
SVN_ERR(x509_get_name(&p, p + len, &crt->subject, scratch_pool));
1118
* SubjectPublicKeyInfo ::= SEQUENCE
1119
* algorithm AlgorithmIdentifier,
1120
* subjectPublicKey BIT STRING }
1122
err = asn1_get_tag(&p, end, &len, ASN1_CONSTRUCTED | ASN1_SEQUENCE);
1124
return svn_error_create(SVN_ERR_X509_CERT_INVALID_FORMAT, err, NULL);
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
1137
crt->dnsnames = apr_array_make(scratch_pool, 3, sizeof(x509_buf *));
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));
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);
1156
end = (const unsigned char*) buf + buflen;
1159
* signatureAlgorithm AlgorithmIdentifier,
1160
* signatureValue BIT STRING
1162
SVN_ERR(x509_get_alg(&p, end, &crt->sig_oid2));
1164
if (!oids_equal(&crt->sig_oid1, &crt->sig_oid2))
1165
return svn_error_create(SVN_ERR_X509_CERT_SIG_MISMATCH, NULL, NULL);
1167
SVN_ERR(x509_get_sig(&p, end, &crt->sig));
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);
1175
ci = apr_pcalloc(result_pool, sizeof(*ci));
1177
/* Get the subject name */
1178
SVN_ERR(x509_name_to_certinfo(&ci->subject, &crt->subject,
1179
scratch_pool, result_pool));
1181
/* Get the issuer name */
1182
SVN_ERR(x509_name_to_certinfo(&ci->issuer, &crt->issuer,
1183
scratch_pool, result_pool));
1185
/* Copy the validity range */
1186
ci->valid_from = crt->valid_from;
1187
ci->valid_to = crt->valid_to;
1189
/* Calculate the SHA1 digest of the certificate, otherwise known as
1191
SVN_ERR(svn_checksum(&ci->digest, svn_checksum_sha1, buf, buflen,
1194
/* Construct the array of host names */
1195
x509parse_get_hostnames(ci, crt, result_pool, scratch_pool);
1198
return SVN_NO_ERROR;