summaryrefslogtreecommitdiff
path: root/src/lib/libcrypto/asn1/a_time_tm.c
diff options
context:
space:
mode:
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}