suricata
util-decode-der-get.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2011-2012 ANSSI
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /**
29  * \file
30  *
31  * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
32  *
33  */
34 
35 #include "suricata-common.h"
36 
37 #include "util-decode-der.h"
38 #include "util-decode-der-get.h"
39 
40 static const uint8_t SEQ_IDX_SERIAL[] = { 0, 0 };
41 static const uint8_t SEQ_IDX_ISSUER[] = { 0, 2 };
42 static const uint8_t SEQ_IDX_VALIDITY[] = { 0, 3 };
43 static const uint8_t SEQ_IDX_SUBJECT[] = { 0, 4 };
44 
45 static const char *Oid2ShortStr(const char *oid)
46 {
47  if (strcmp(oid, "1.2.840.113549.1.9.1") == 0)
48  return "emailAddress";
49 
50  if (strcmp(oid, "2.5.4.3") == 0)
51  return "CN";
52 
53  if (strcmp(oid, "2.5.4.5") == 0)
54  return "serialNumber";
55 
56  if (strcmp(oid, "2.5.4.6") == 0)
57  return "C";
58 
59  if (strcmp(oid, "2.5.4.7") == 0)
60  return "L";
61 
62  if (strcmp(oid, "2.5.4.8") == 0)
63  return "ST";
64 
65  if (strcmp(oid, "2.5.4.10") == 0)
66  return "O";
67 
68  if (strcmp(oid, "2.5.4.11") == 0)
69  return "OU";
70 
71  if (strcmp(oid, "0.9.2342.19200300.100.1.25") == 0)
72  return "DC";
73 
74  return "unknown";
75 }
76 
77 static time_t GentimeToTime(char *gentime)
78 {
79  time_t time;
80  struct tm tm;
81 
82  /* GeneralizedTime values MUST be expressed in Greenwich Mean Time
83  * (Zulu) and MUST include seconds (rfc5280 4.1.2.5.2). It MUST NOT
84  * include fractional seconds. It should therefore be on the format
85  * YYYYmmddHHMMSSZ. */
86  if (strlen(gentime) != 15)
87  goto error;
88 
89  memset(&tm, 0, sizeof(tm));
90  strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
91  time = SCMkTimeUtc(&tm);
92 
93  if (time < 0)
94  goto error;
95 
96  return time;
97 
98 error:
99  return -1;
100 }
101 
102 static time_t UtctimeToTime(char *utctime)
103 {
104  time_t time;
105  unsigned int year;
106  char yy[3];
107  char buf[20];
108 
109  /* UTCTime values MUST be expressed in Greenwich Mean Time (Zulu)
110  * and MUST include seconds (rfc5280 4.1.2.5.1). It should
111  * therefore be on the format YYmmddHHMMSSZ. */
112  if (strlen(utctime) != 13)
113  goto error;
114 
115  /* UTCTime use two digits to represent the year. The year field (YY)
116  * should be interpreted as 19YY when it is greater than or equal to
117  * 50. If it is less than 50 it should be interpreted as 20YY.
118  * Because of this, GeneralizedTime must be used for dates in the
119  * year 2050 or later. */
120  strlcpy(yy, utctime, sizeof(yy));
121  year = strtol(yy, NULL, 10);
122  if (year >= 50)
123  snprintf(buf, sizeof(buf), "%i%s", 19, utctime);
124  else
125  snprintf(buf, sizeof(buf), "%i%s", 20, utctime);
126 
127  time = GentimeToTime(buf);
128  if (time == -1)
129  goto error;
130 
131  return time;
132 
133 error:
134  return -1;
135 }
136 
137 /**
138  * \brief Iterate through an ASN.1 structure, following the index sequence.
139  * Context specific elements are skipped.
140  *
141  * \retval The matching node, or NULL
142  */
143 const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index,
144  const uint32_t seqsz, uint32_t *errcode)
145 {
146  const Asn1Generic * node;
147  uint8_t idx, i;
148  uint8_t offset = 0;
149 
150  if (errcode)
151  *errcode = ERR_DER_MISSING_ELEMENT;
152 
153  node = top;
154  if (node == NULL || seq_index == NULL)
155  return NULL;
156 
157  for (offset=0; offset<seqsz; offset++) {
158 
159  idx = seq_index[offset];
160  for (i=0; i<idx; i++) {
161  if (node == NULL || node->data == NULL)
162  return NULL;
163 
164  /* skip context-specific elements */
165  while (node->data->header.cls == ASN1_CLASS_CONTEXTSPEC) {
166  node = node->next;
167  if (node == NULL || node->data == NULL)
168  return NULL;
169  }
170 
171  node = node->next;
172  if (node == NULL || node->data == NULL)
173  return NULL;
174  }
175 
176  /* skip context-specific elements */
177  if (node == NULL || node->data == NULL)
178  return NULL;
179  while (node->data->header.cls == ASN1_CLASS_CONTEXTSPEC) {
180  node = node->next;
181  if (node == NULL || node->data == NULL)
182  return NULL;
183  }
184 
185  node = node->data;
186  }
187 
188  if (errcode)
189  *errcode = 0;
190 
191  return node;
192 }
193 
194 int Asn1DerGetValidity(const Asn1Generic *cert, time_t *not_before,
195  time_t *not_after, uint32_t *errcode)
196 {
197  const Asn1Generic *node, *it;
198  int rc = -1;
199 
200  if (errcode)
201  *errcode = ERR_DER_MISSING_ELEMENT;
202 
203  node = Asn1DerGet(cert, SEQ_IDX_VALIDITY, sizeof(SEQ_IDX_VALIDITY), errcode);
204  if ((node == NULL) || node->type != ASN1_SEQUENCE)
205  goto validity_error;
206 
207  it = node->data;
208  if (it == NULL || it->str == NULL)
209  goto validity_error;
210 
211  if (it->type == ASN1_UTCTIME)
212  *not_before = UtctimeToTime(it->str);
213  else if (it->type == ASN1_GENERALIZEDTIME)
214  *not_before = GentimeToTime(it->str);
215  else
216  goto validity_error;
217 
218  if (*not_before == -1)
219  goto validity_error;
220 
221  if (node->next == NULL)
222  goto validity_error;
223 
224  it = node->next->data;
225 
226  if (it == NULL || it->str == NULL)
227  goto validity_error;
228 
229  if (it->type == ASN1_UTCTIME)
230  *not_after = UtctimeToTime(it->str);
231  else if (it->type == ASN1_GENERALIZEDTIME)
232  *not_after = GentimeToTime(it->str);
233  else
234  goto validity_error;
235 
236  if (*not_after == -1)
237  goto validity_error;
238 
239  rc = 0;
240 
241 validity_error:
242  return rc;
243 }
244 
245 int Asn1DerGetSerial(const Asn1Generic *cert, char *buffer, uint32_t length,
246  uint32_t *errcode)
247 {
248  const Asn1Generic *node;
249  uint32_t node_len, i;
250  int rc = -1;
251 
252  if (errcode)
253  *errcode = ERR_DER_MISSING_ELEMENT;
254 
255  buffer[0] = '\0';
256 
257  node = Asn1DerGet(cert, SEQ_IDX_SERIAL, sizeof(SEQ_IDX_SERIAL), errcode);
258  if ((node == NULL) || node->type != ASN1_INTEGER || node->str == NULL)
259  goto serial_error;
260 
261  node_len = strlen(node->str);
262 
263  /* make sure the buffer is big enough */
264  if (node_len + (node_len / 2) > length)
265  goto serial_error;
266 
267  /* format serial number (e.g. XX:XX:XX:XX:XX) */
268  for (i = 0; i < node_len; i++) {
269  char c[3];
270  /* insert separator before each even number */
271  if (((i % 2) == 0) && (i != 0)) {
272  snprintf(c, sizeof(c), ":%c", node->str[i]);
273  } else {
274  snprintf(c, sizeof(c), "%c", node->str[i]);
275  }
276 
277  strlcat(buffer, c, length);
278  }
279 
280  if (errcode)
281  *errcode = 0;
282 
283  rc = 0;
284 
285 serial_error:
286  return rc;
287 }
288 
289 int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length,
290  uint32_t *errcode)
291 {
292  const Asn1Generic *node_oid;
293  const Asn1Generic *node;
294  const Asn1Generic *it;
295  const Asn1Generic *node_set;
296  const Asn1Generic *node_str;
297  const char *shortname;
298  int rc = -1;
299  const char *separator = ", ";
300 
301  if (errcode)
302  *errcode = ERR_DER_MISSING_ELEMENT;
303 
304  if (length < 10)
305  goto issuer_dn_error;
306 
307  buffer[0] = '\0';
308 
309  node = Asn1DerGet(cert, SEQ_IDX_ISSUER, sizeof(SEQ_IDX_ISSUER), errcode);
310  if ((node == NULL) || node->type != ASN1_SEQUENCE)
311  goto issuer_dn_error;
312 
313  it = node;
314  while (it != NULL) {
315  if (it->data == NULL)
316  goto issuer_dn_error;
317  node_set = it->data;
318  if (node_set->type != ASN1_SET || node_set->data == NULL)
319  goto issuer_dn_error;
320  node = node_set->data;
321  if (node->type != ASN1_SEQUENCE || node->data == NULL)
322  goto issuer_dn_error;
323  node_oid = node->data;
324  if (node_oid->str == NULL || node_oid->type != ASN1_OID)
325  goto issuer_dn_error;
326  shortname = Oid2ShortStr(node_oid->str);
327  if (node->next == NULL)
328  goto issuer_dn_error;
329  node = node->next;
330  node_str = node->data;
331  if (node_str == NULL || node_str->str == NULL)
332  goto issuer_dn_error;
333 
334  switch (node_str->type) {
335  case ASN1_PRINTSTRING:
336  case ASN1_IA5STRING:
337  case ASN1_T61STRING:
338  case ASN1_UTF8STRING:
339  case ASN1_OCTETSTRING:
340  strlcat(buffer, shortname, length);
341  strlcat(buffer, "=", length);
342  strlcat(buffer, node_str->str, length);
343  break;
344  default:
345  if (errcode)
346  *errcode = ERR_DER_UNSUPPORTED_STRING;
347  goto issuer_dn_error;
348  }
349 
350  if (strcmp(shortname,"CN") == 0)
351  separator = "/";
352  if (it->next != NULL)
353  strlcat(buffer, separator, length);
354  it = it->next;
355  }
356 
357  if (errcode)
358  *errcode = 0;
359 
360  rc = 0;
361 issuer_dn_error:
362  return rc;
363 }
364 
365 int Asn1DerGetSubjectDN(const Asn1Generic *cert, char *buffer, uint32_t length,
366  uint32_t *errcode)
367 {
368  const Asn1Generic *node_oid;
369  const Asn1Generic *node;
370  const Asn1Generic *it;
371  const Asn1Generic *node_set;
372  const Asn1Generic *node_str;
373  const char *shortname;
374  int rc = -1;
375  const char *separator = ", ";
376 
377  if (errcode)
378  *errcode = ERR_DER_MISSING_ELEMENT;
379 
380  if (length < 10)
381  goto subject_dn_error;
382 
383  buffer[0] = '\0';
384 
385  node = Asn1DerGet(cert, SEQ_IDX_SUBJECT, sizeof(SEQ_IDX_SUBJECT), errcode);
386 
387  if ((node == NULL) || node->type != ASN1_SEQUENCE)
388  goto subject_dn_error;
389 
390  it = node;
391  while (it != NULL) {
392  if (it == NULL || it->data == NULL)
393  goto subject_dn_error;
394  node_set = it->data;
395  if (node_set->type != ASN1_SET || node_set->data == NULL)
396  goto subject_dn_error;
397  node = node_set->data;
398  if (node->type != ASN1_SEQUENCE || node->data == NULL)
399  goto subject_dn_error;
400  node_oid = node->data;
401  if (node_oid->str == NULL || node_oid->type != ASN1_OID)
402  goto subject_dn_error;
403  shortname = Oid2ShortStr(node_oid->str);
404  if (node->next == NULL)
405  goto subject_dn_error;
406  node = node->next;
407  node_str = node->data;
408  if (node_str == NULL || node_str->str == NULL)
409  goto subject_dn_error;
410 
411  switch (node_str->type) {
412  case ASN1_PRINTSTRING:
413  case ASN1_IA5STRING:
414  case ASN1_T61STRING:
415  case ASN1_UTF8STRING:
416  case ASN1_OCTETSTRING:
417  strlcat(buffer, shortname, length);
418  strlcat(buffer, "=", length);
419  strlcat(buffer, node_str->str, length);
420  break;
421  default:
422  if (errcode)
423  *errcode = ERR_DER_UNSUPPORTED_STRING;
424  goto subject_dn_error;
425  }
426 
427  if (strcmp(shortname,"CN") == 0)
428  separator = "/";
429  if (it->next != NULL)
430  strlcat(buffer, separator, length);
431  it = it->next;
432  }
433 
434  if (errcode)
435  *errcode = 0;
436 
437  rc = 0;
438 subject_dn_error:
439  return rc;
440 }
441 
int Asn1DerGetValidity(const Asn1Generic *cert, time_t *not_before, time_t *not_after, uint32_t *errcode)
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
#define ASN1_OID
#define ASN1_CLASS_CONTEXTSPEC
#define ASN1_UTF8STRING
struct Asn1Generic_ * next
char * strptime(const char *__restrict, const char *__restrict, struct tm *__restrict)
Definition: util-strptime.c:97
size_t strlcat(char *, const char *src, size_t siz)
Definition: util-strlcatu.c:45
Asn1ElementType header
uint64_t offset
#define ASN1_PRINTSTRING
#define ASN1_SEQUENCE
time_t SCMkTimeUtc(struct tm *tp)
Convert broken-down time to seconds since Unix epoch.
Definition: util-time.c:418
int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode)
#define ASN1_OCTETSTRING
const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index, const uint32_t seqsz, uint32_t *errcode)
Iterate through an ASN.1 structure, following the index sequence. Context specific elements are skipp...
struct Asn1Generic_ * data
int Asn1DerGetSubjectDN(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode)
#define ASN1_IA5STRING
#define ERR_DER_MISSING_ELEMENT
#define ASN1_GENERALIZEDTIME
#define ASN1_SET
#define ERR_DER_UNSUPPORTED_STRING
#define ASN1_INTEGER
#define ASN1_UTCTIME
uint16_t length
int Asn1DerGetSerial(const Asn1Generic *cert, char *buffer, uint32_t length, uint32_t *errcode)
#define ASN1_T61STRING