x509info.c revision 362181
1189251Ssam/*
2189251Ssam * x509info.c:  Accessors for svn_x509_certinfo_t
3346981Scy *
4189251Ssam * ====================================================================
5252726Srpaulo *    Licensed to the Apache Software Foundation (ASF) under one
6252726Srpaulo *    or more contributor license agreements.  See the NOTICE file
7189251Ssam *    distributed with this work for additional information
8189251Ssam *    regarding copyright ownership.  The ASF licenses this file
9189251Ssam *    to you under the Apache License, Version 2.0 (the
10189251Ssam *    "License"); you may not use this file except in compliance
11189251Ssam *    with the License.  You may obtain a copy of the License at
12214734Srpaulo *
13214734Srpaulo *      http://www.apache.org/licenses/LICENSE-2.0
14214734Srpaulo *
15281806Srpaulo *    Unless required by applicable law or agreed to in writing,
16346981Scy *    software distributed under the License is distributed on an
17214734Srpaulo *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18214734Srpaulo *    KIND, either express or implied.  See the License for the
19189251Ssam *    specific language governing permissions and limitations
20189251Ssam *    under the License.
21189251Ssam * ====================================================================
22189251Ssam */
23189251Ssam
24346981Scy
25281806Srpaulo
26346981Scy#include <string.h>
27346981Scy
28346981Scy#include <apr_pools.h>
29281806Srpaulo#include <apr_tables.h>
30346981Scy
31346981Scy#include "svn_string.h"
32346981Scy#include "svn_hash.h"
33346981Scy#include "x509.h"
34346981Scy
35346981Scy
36346981Scy
37346981Scysvn_x509_name_attr_t *
38346981Scysvn_x509_name_attr_dup(const svn_x509_name_attr_t *attr,
39346981Scy                       apr_pool_t *result_pool,
40346981Scy                       apr_pool_t *scratch_pool)
41346981Scy{
42281806Srpaulo  svn_x509_name_attr_t *result = apr_palloc(result_pool, sizeof(*result));
43281806Srpaulo  result->oid_len = attr->oid_len;
44281806Srpaulo  result->oid = apr_pmemdup(result_pool, attr->oid, attr->oid_len);
45346981Scy  result->utf8_value = apr_pstrdup(result_pool, attr->utf8_value);
46346981Scy
47281806Srpaulo  return result;
48346981Scy}
49346981Scy
50346981Scyconst unsigned char *
51346981Scysvn_x509_name_attr_get_oid(const svn_x509_name_attr_t *attr, apr_size_t *len)
52346981Scy{
53346981Scy  *len = attr->oid_len;
54346981Scy  return attr->oid;
55346981Scy}
56346981Scy
57346981Scyconst char *
58346981Scysvn_x509_name_attr_get_value(const svn_x509_name_attr_t *attr)
59346981Scy{
60346981Scy  return attr->utf8_value;
61346981Scy}
62346981Scy
63346981Scy/* Array elements are assumed to be nul-terminated C strings. */
64346981Scystatic apr_array_header_t *
65346981Scydeep_copy_array(apr_array_header_t *s, apr_pool_t *result_pool)
66346981Scy{
67346981Scy  int i;
68346981Scy  apr_array_header_t *d;
69346981Scy
70281806Srpaulo  if (!s)
71346981Scy    return NULL;
72346981Scy
73346981Scy  d = apr_array_copy(result_pool, s);
74346981Scy
75346981Scy  /* Make a deep copy of the strings in the array. */
76346981Scy  for (i = 0; i < s->nelts; ++i)
77346981Scy    {
78281806Srpaulo      APR_ARRAY_IDX(d, i, const char *) =
79281806Srpaulo        apr_pstrdup(result_pool, APR_ARRAY_IDX(s, i, const char *));
80281806Srpaulo    }
81346981Scy
82346981Scy  return d;
83281806Srpaulo}
84346981Scy
85346981Scy/* Copy an array with elements that are svn_x509_name_attr_t's */
86346981Scystatic apr_array_header_t *
87346981Scydeep_copy_name_attrs(apr_array_header_t *s, apr_pool_t *result_pool)
88346981Scy{
89346981Scy  int i;
90346981Scy  apr_array_header_t *d;
91346981Scy
92346981Scy  if (!s)
93346981Scy    return NULL;
94346981Scy
95346981Scy  d = apr_array_copy(result_pool, s);
96346981Scy
97346981Scy  /* Make a deep copy of the svn_x509_name_attr_t's in the array. */
98346981Scy  for (i = 0; i < s->nelts; ++i)
99346981Scy    {
100346981Scy      APR_ARRAY_IDX(d, i, const svn_x509_name_attr_t *) =
101281806Srpaulo        svn_x509_name_attr_dup(APR_ARRAY_IDX(s, i, svn_x509_name_attr_t *),
102346981Scy                               result_pool, result_pool);
103346981Scy    }
104346981Scy
105346981Scy  return d;
106346981Scy}
107346981Scy
108346981Scysvn_x509_certinfo_t *
109346981Scysvn_x509_certinfo_dup(const svn_x509_certinfo_t *certinfo,
110346981Scy                      apr_pool_t *result_pool,
111346981Scy                      apr_pool_t *scratch_pool)
112346981Scy{
113346981Scy  svn_x509_certinfo_t *result = apr_palloc(result_pool, sizeof(*result));
114281806Srpaulo  result->subject = deep_copy_name_attrs(certinfo->subject, result_pool);
115281806Srpaulo  result->issuer = deep_copy_name_attrs(certinfo->issuer, result_pool);
116281806Srpaulo  result->valid_from = certinfo->valid_from;
117189251Ssam  result->valid_to = certinfo->valid_to;
118346981Scy  result->digest = svn_checksum_dup(certinfo->digest, result_pool);
119346981Scy  result->hostnames = deep_copy_array(certinfo->hostnames, result_pool);
120346981Scy
121346981Scy  return result;
122346981Scy}
123346981Scy
124346981Scytypedef struct asn1_oid {
125346981Scy  const unsigned char *oid;
126346981Scy  const apr_size_t oid_len;
127346981Scy  const char *short_label;
128346981Scy  const char *long_label;
129346981Scy} asn1_oid;
130346981Scy
131346981Scy#define CONSTANT_PAIR(c) (const unsigned char *)(c), sizeof((c)) - 1
132346981Scy
133346981Scystatic const asn1_oid asn1_oids[] = {
134346981Scy  { CONSTANT_PAIR(SVN_X509_OID_COMMON_NAME),  "CN", "commonName" },
135346981Scy  { CONSTANT_PAIR(SVN_X509_OID_COUNTRY),      "C",  "countryName" },
136346981Scy  { CONSTANT_PAIR(SVN_X509_OID_LOCALITY),     "L",  "localityName" },
137346981Scy  { CONSTANT_PAIR(SVN_X509_OID_STATE),        "ST", "stateOrProvinceName" },
138346981Scy  { CONSTANT_PAIR(SVN_X509_OID_ORGANIZATION), "O",  "organizationName" },
139346981Scy  { CONSTANT_PAIR(SVN_X509_OID_ORG_UNIT),     "OU", "organizationalUnitName"},
140346981Scy  { CONSTANT_PAIR(SVN_X509_OID_EMAIL),        NULL, "emailAddress" },
141346981Scy  { NULL },
142346981Scy};
143346981Scy
144346981Scy/* Given an OID return a null-terminated C string representation.
145346981Scy * For example an OID with the bytes "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01"
146346981Scy * would be converted to the string "1.2.840.113549.1.9.1". */
147346981Scyconst char *
148346981Scysvn_x509_oid_to_string(const unsigned char *oid, apr_size_t oid_len,
149346981Scy                       apr_pool_t *scratch_pool, apr_pool_t *result_pool)
150346981Scy{
151346981Scy  svn_stringbuf_t *out = svn_stringbuf_create_empty(result_pool);
152346981Scy  const unsigned char *p = oid;
153346981Scy  const unsigned char *end = p + oid_len;
154346981Scy  const char *temp;
155346981Scy
156346981Scy  while (p != end) {
157346981Scy    if (p == oid)
158346981Scy      {
159346981Scy        /* Handle decoding the first two values of the OID.  These values
160346981Scy         * are encoded by taking the first value and adding 40 to it and
161346981Scy         * adding the result to the second value, then placing this single
162346981Scy         * value in the first byte of the output.  This is unambiguous since
163346981Scy         * the first value is apparently limited to 0, 1 or 2 and the second
164346981Scy         * is limited to 0 to 39. */
165346981Scy        temp = apr_psprintf(scratch_pool, "%d.%d", *p / 40, *p % 40);
166346981Scy        p++;
167346981Scy      }
168346981Scy    else if (*p < 128)
169346981Scy      {
170346981Scy        /* The remaining values if they're less than 128 are just
171346981Scy         * the number one to one encoded */
172346981Scy        temp = apr_psprintf(scratch_pool, ".%d", *p);
173189251Ssam        p++;
174189251Ssam      }
175281806Srpaulo    else
176281806Srpaulo      {
177189251Ssam        /* Values greater than 128 are encoded as a series of 7 bit values
178189251Ssam         * with the left most bit set to indicate this encoding with the
179189251Ssam         * last octet missing the left most bit to finish out the series.. */
180189251Ssam        unsigned int collector = 0;
181189251Ssam        svn_boolean_t dot = FALSE;
182189251Ssam
183189251Ssam        do {
184189251Ssam          if (collector == 0 && *p == 0x80)
185189251Ssam            {
186189251Ssam              /* include leading zeros in the string representation
187189251Ssam                 technically not legal, but this seems nicer than just
188189251Ssam                 returning NULL */
189189251Ssam              if (!dot)
190189251Ssam                {
191189251Ssam                  svn_stringbuf_appendbyte(out, '.');
192281806Srpaulo                  dot = TRUE;
193281806Srpaulo                }
194189251Ssam              svn_stringbuf_appendbyte(out, '0');
195346981Scy            }
196189251Ssam          else if (collector > UINT_MAX >> 7)
197346981Scy            {
198346981Scy              /* overflow */
199346981Scy              return NULL;
200346981Scy            }
201346981Scy          collector = collector << 7 | (*(p++) & 0x7f);
202346981Scy        } while (p != end && *p > 127);
203189251Ssam        if (collector > UINT_MAX >> 7)
204252726Srpaulo          return NULL; /* overflow */
205189251Ssam        collector = collector << 7 | *(p++);
206346981Scy        temp = apr_psprintf(scratch_pool, "%s%d", dot ? "" : ".", collector);
207281806Srpaulo      }
208252726Srpaulo    svn_stringbuf_appendcstr(out, temp);
209189251Ssam  }
210346981Scy
211281806Srpaulo  if (svn_stringbuf_isempty(out))
212214734Srpaulo    return NULL;
213189251Ssam
214189251Ssam  return out->data;
215209158Srpaulo}
216189251Ssam
217346981Scystatic const asn1_oid *oid_to_asn1_oid(unsigned char *oid, apr_size_t oid_len)
218189251Ssam{
219209158Srpaulo  const asn1_oid *entry;
220281806Srpaulo
221281806Srpaulo  for (entry = asn1_oids; entry->oid; entry++)
222346981Scy    {
223346981Scy      if (oid_len == entry->oid_len &&
224346981Scy          memcmp(oid, entry->oid, oid_len) == 0)
225346981Scy        return entry;
226346981Scy    }
227346981Scy
228346981Scy  return NULL;
229281806Srpaulo}
230281806Srpaulo
231346981Scystatic const char *oid_to_best_label(unsigned char *oid, apr_size_t oid_len,
232346981Scy                                     apr_pool_t *result_pool)
233281806Srpaulo{
234281806Srpaulo  const asn1_oid *entry = oid_to_asn1_oid(oid, oid_len);
235281806Srpaulo
236281806Srpaulo  if (entry)
237346981Scy    {
238346981Scy      if (entry->short_label)
239281806Srpaulo        return entry->short_label;
240281806Srpaulo
241281806Srpaulo      if (entry->long_label)
242281806Srpaulo        return entry->long_label;
243281806Srpaulo    }
244281806Srpaulo  else
245281806Srpaulo    {
246346981Scy      const char *oid_string = svn_x509_oid_to_string(oid, oid_len,
247346981Scy                                                      result_pool, result_pool);
248281806Srpaulo      if (oid_string)
249281806Srpaulo        return oid_string;
250281806Srpaulo    }
251281806Srpaulo
252281806Srpaulo  return "??";
253346981Scy}
254346981Scy
255346981Scy/*
256346981Scy * Store the name from dn in printable form into buf,
257346981Scy * using scratch_pool for any temporary allocations.
258346981Scy * If CN is not NULL, return any common name in CN
259346981Scy */
260346981Scystatic const char *
261346981Scyget_dn(apr_array_header_t *name,
262346981Scy       apr_pool_t *result_pool)
263346981Scy{
264346981Scy  svn_stringbuf_t *buf = svn_stringbuf_create_empty(result_pool);
265346981Scy  int n;
266346981Scy
267346981Scy  for (n = 0; n < name->nelts; n++)
268346981Scy    {
269346981Scy      const svn_x509_name_attr_t *attr = APR_ARRAY_IDX(name, n, svn_x509_name_attr_t *);
270346981Scy
271346981Scy      if (n > 0)
272346981Scy        svn_stringbuf_appendcstr(buf, ", ");
273346981Scy
274346981Scy      svn_stringbuf_appendcstr(buf, oid_to_best_label(attr->oid, attr->oid_len, result_pool));
275346981Scy      svn_stringbuf_appendbyte(buf, '=');
276346981Scy      svn_stringbuf_appendcstr(buf, attr->utf8_value);
277346981Scy    }
278346981Scy
279346981Scy  return buf->data;
280346981Scy}
281346981Scy
282346981Scyconst char *
283346981Scysvn_x509_certinfo_get_subject(const svn_x509_certinfo_t *certinfo,
284346981Scy                              apr_pool_t *result_pool)
285346981Scy{
286346981Scy  return get_dn(certinfo->subject, result_pool);
287346981Scy}
288346981Scy
289346981Scyconst apr_array_header_t *
290346981Scysvn_x509_certinfo_get_subject_attrs(const svn_x509_certinfo_t *certinfo)
291346981Scy{
292346981Scy  return certinfo->subject;
293346981Scy}
294346981Scy
295346981Scyconst char *
296346981Scysvn_x509_certinfo_get_issuer(const svn_x509_certinfo_t *certinfo,
297346981Scy                             apr_pool_t *result_pool)
298346981Scy{
299346981Scy  return get_dn(certinfo->issuer, result_pool);
300346981Scy}
301346981Scy
302346981Scyconst apr_array_header_t *
303346981Scysvn_x509_certinfo_get_issuer_attrs(const svn_x509_certinfo_t *certinfo)
304346981Scy{
305346981Scy  return certinfo->issuer;
306346981Scy}
307346981Scy
308281806Srpauloapr_time_t
309346981Scysvn_x509_certinfo_get_valid_from(const svn_x509_certinfo_t *certinfo)
310346981Scy{
311346981Scy  return certinfo->valid_from;
312281806Srpaulo}
313281806Srpaulo
314281806Srpauloapr_time_t
315189251Ssamsvn_x509_certinfo_get_valid_to(const svn_x509_certinfo_t *certinfo)
316346981Scy{
317346981Scy  return certinfo->valid_to;
318346981Scy}
319189251Ssam
320189251Ssamconst svn_checksum_t *
321189251Ssamsvn_x509_certinfo_get_digest(const svn_x509_certinfo_t *certinfo)
322189251Ssam{
323189251Ssam  return certinfo->digest;
324189251Ssam}
325189251Ssam
326189251Ssamconst apr_array_header_t *
327189251Ssamsvn_x509_certinfo_get_hostnames(const svn_x509_certinfo_t *certinfo)
328189251Ssam{
329189251Ssam  return certinfo->hostnames;
330189251Ssam}
331189251Ssam
332189251Ssam