summaryrefslogtreecommitdiff
path: root/src/lib/libcrypto/asn1/a_time_tm.c
diff options
context:
space:
mode:
authorbeck <>2015-10-02 15:04:45 +0000
committerbeck <>2015-10-02 15:04:45 +0000
commit61992d68f1934e7e4171e633f39fb76a4654b5a2 (patch)
tree8649498e5e9fdda4e44ebac5989504efbcc57b61 /src/lib/libcrypto/asn1/a_time_tm.c
parent5951a0298417b41fc2a1fb4ad8a057fb9530e872 (diff)
downloadopenbsd-61992d68f1934e7e4171e633f39fb76a4654b5a2.tar.gz
openbsd-61992d68f1934e7e4171e633f39fb76a4654b5a2.tar.bz2
openbsd-61992d68f1934e7e4171e633f39fb76a4654b5a2.zip
Flense the greasy black guts of unreadble string parsing code out of three areas
in asn1 and x509 code, all dealing with an ASN1_TIME. This brings the parsing together in one function that converts into a struct tm. While we are at it this also brings us into conformance with RFC 5280 for times allowed in an X509 cert, as OpenSSL is very liberal with what it allows. input and fixes from deraadt@ jsing@ guethther@ and others. ok krw@, guenther@, jsing@
Diffstat (limited to 'src/lib/libcrypto/asn1/a_time_tm.c')
-rw-r--r--src/lib/libcrypto/asn1/a_time_tm.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/src/lib/libcrypto/asn1/a_time_tm.c b/src/lib/libcrypto/asn1/a_time_tm.c
new file mode 100644
index 0000000000..65f75c68cc
--- /dev/null
+++ b/src/lib/libcrypto/asn1/a_time_tm.c
@@ -0,0 +1,257 @@
1/* $OpenBSD: a_time_tm.c,v 1.1 2015/10/02 15:04:45 beck Exp $ */
2/*
3 * Copyright (c) 2015 Bob Beck <beck@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <stdio.h>
19#include <string.h>
20#include <time.h>
21#include <ctype.h>
22#include <sys/limits.h>
23
24#include <openssl/asn1t.h>
25#include <openssl/err.h>
26
27#include "o_time.h"
28#include "asn1_locl.h"
29
30char *
31gentime_string_from_tm(struct tm *tm)
32{
33 char *ret = NULL;
34 int year;
35 year = tm->tm_year + 1900;
36 if (year < 0 || year > 9999)
37 return (NULL);
38 if (asprintf(&ret, "%04u%02u%02u%02u%02u%02uZ", year,
39 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
40 tm->tm_sec) == -1)
41 ret = NULL;
42 return (ret);
43}
44
45char *
46utctime_string_from_tm(struct tm *tm)
47{
48 char *ret = NULL;
49 if (tm->tm_year >= 150 || tm->tm_year < 50)
50 return (NULL);
51 if (asprintf(&ret, "%02u%02u%02u%02u%02u%02uZ",
52 tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
53 tm->tm_hour, tm->tm_min, tm->tm_sec) == -1)
54 ret = NULL;
55 return (ret);
56}
57
58/*
59 * Parse an ASN.1 time string.
60 *
61 * mode must be:
62 * 0 if we expect to parse a time as specified in RFC 5280 from an
63 * X509 certificate.
64 * V_ASN1_UTCTIME if we wish to parse a legacy ASN1 UTC time.
65 * V_ASN1_GENERALIZEDTIME if we wish to parse a legacy ASN1
66 * Generalizd time.
67 *
68 * Returns:
69 * -1 if the string was invalid.
70 * V_ASN1_UTCTIME if the string validated as a UTC time string.
71 * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string.
72 *
73 * Fills in *tm with the corresponding time if tm is non NULL.
74 */
75#define RFC5280 0
76#define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))
77int asn1_time_parse(const char * bytes, size_t len, struct tm *tm, int mode)
78{
79 char *p, *buf = NULL, *dot = NULL, *tz = NULL;
80 int i, offset, noseconds = 0, type = 0;
81 struct tm ltm;
82 struct tm *lt;
83 size_t tlen;
84 char tzc;
85
86 if (bytes == NULL)
87 goto err;
88
89 if (len > INT_MAX)
90 goto err;
91
92 /* Constrain the RFC5280 case within max/min valid lengths. */
93 if (mode == RFC5280 && (len > 15 || len < 13))
94 goto err;
95
96 if ((buf = strndup(bytes, len)) == NULL)
97 goto err;
98 lt = tm;
99 if (lt == NULL) {
100 time_t t = time(NULL);
101 lt = gmtime_r(&t, &ltm);
102 if (lt == NULL)
103 goto err;
104 }
105
106 /*
107 * Find position of the optional fractional seconds, and the
108 * start of the timezone, while ensuring everything else is
109 * digits.
110 */
111 for (i = 0; i < len; i++) {
112 char *t = buf + i;
113 if (isdigit((unsigned char)*t))
114 continue;
115 if (*t == '.' && dot == NULL) {
116 dot = t;
117 continue;
118 }
119 if ((*t == 'Z' || *t == '+' || *t == '-') && tz == NULL) {
120 tz = t;
121 continue;
122 }
123 goto err;
124 }
125
126 /*
127 * Timezone is required. For the non-RFC case it may be
128 * either Z or +- HHMM, but for RFC5280 it may be only Z.
129 */
130 if (tz == NULL)
131 goto err;
132 tzc = *tz;
133 *tz++ = '\0';
134 if (tzc == 'Z') {
135 if (*tz != '\0')
136 goto err;
137 offset = 0;
138 } else if (mode != RFC5280 && (tzc == '+' || tzc == '-') &&
139 (strlen(tz) == 4)) {
140 int hours, mins;
141 hours = ATOI2(tz);
142 mins = ATOI2(tz);
143 if (hours > 12 || mins > 59)
144 goto err;
145 offset = hours * 3600 + mins * 60;
146 if (tzc == '-')
147 offset = -offset;
148 } else
149 goto err;
150
151 if (mode != RFC5280) {
152 /* XXX - yuck - OPENSSL_gmtime_adj should go away */
153 if (!OPENSSL_gmtime_adj(lt, 0, offset))
154 goto err;
155 }
156
157 /*
158 * We only allow fractional seconds to be present if we are in
159 * the non-RFC case of a Generalized time. RFC 5280 forbids
160 * fractional seconds.
161 */
162 if (dot != NULL) {
163 if (mode != V_ASN1_GENERALIZEDTIME)
164 goto err;
165 *dot++ = '\0';
166 if (!isdigit((unsigned char)*dot))
167 goto err;
168 }
169
170 /*
171 * Validate and convert the time
172 */
173 p = buf;
174 tlen = strlen(buf);
175 switch (tlen) {
176 case 14:
177 lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
178 if (mode == RFC5280 || mode == V_ASN1_GENERALIZEDTIME)
179 type = V_ASN1_GENERALIZEDTIME;
180 else
181 goto err;
182 /* FALLTHROUGH */
183 case 12:
184 if (type == 0 && mode == V_ASN1_GENERALIZEDTIME) {
185 /*
186 * In the non-RFC case of a Generalized time
187 * seconds may not have been provided. RFC
188 * 5280 mandates that seconds must be present.
189 */
190 noseconds = 1;
191 lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
192 type = V_ASN1_GENERALIZEDTIME;
193 }
194 /* FALLTHROUGH */
195 case 10:
196 if (type == 0) {
197 /*
198 * At this point we must have a UTC time.
199 * In the RFC 5280 case it must have the
200 * seconds present. In the non-RFC case
201 * may have no seconds.
202 */
203 if (mode == V_ASN1_GENERALIZEDTIME)
204 goto err;
205 if (tlen == 10) {
206 if (mode == V_ASN1_UTCTIME)
207 noseconds = 1;
208 else
209 goto err;
210 }
211 type = V_ASN1_UTCTIME;
212 }
213 lt->tm_year += ATOI2(p); /* yy */
214 if (type == V_ASN1_UTCTIME) {
215 if (lt->tm_year < 50)
216 lt->tm_year += 100;
217 }
218 lt->tm_mon = ATOI2(p); /* mm */
219 if ((lt->tm_mon > 12) || !lt->tm_mon)
220 goto err;
221 --lt->tm_mon; /* struct tm is 0 - 11 */
222 lt->tm_mday = ATOI2(p); /* dd */
223 if ((lt->tm_mday > 31) || !lt->tm_mday)
224 goto err;
225 lt->tm_hour = ATOI2(p); /* HH */
226 if (lt->tm_hour > 23)
227 goto err;
228 lt->tm_min = ATOI2(p); /* MM */
229 if (lt->tm_min > 59)
230 goto err;
231 lt->tm_sec = 0; /* SS */
232 if (noseconds)
233 break;
234 lt->tm_sec = ATOI2(p);
235 /* Leap second 60 is not accepted. Reconsider later? */
236 if (lt->tm_sec > 59)
237 goto err;
238 break;
239 default:
240 goto err;
241 }
242
243 /* RFC 5280 section 4.1.2.5 */
244 if (mode == RFC5280 && lt->tm_year < 150 &&
245 type != V_ASN1_UTCTIME)
246 goto err;
247 if (mode == RFC5280 && lt->tm_year >= 150 &&
248 type != V_ASN1_GENERALIZEDTIME)
249 goto err;
250
251 free(buf);
252 return type;
253
254err:
255 free(buf);
256 return -1;
257}