]>
Commit | Line | Data |
---|---|---|
2039c421 | 1 | /* |
60eba30f | 2 | * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved. |
f6aed2cd | 3 | * |
2039c421 RS |
4 | * Licensed under the OpenSSL license (the "License"). You may not use |
5 | * this file except in compliance with the License. You can obtain a copy | |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
f6aed2cd DSH |
8 | */ |
9 | ||
1d97c843 TH |
10 | /*- |
11 | * This is an implementation of the ASN1 Time structure which is: | |
f6aed2cd DSH |
12 | * Time ::= CHOICE { |
13 | * utcTime UTCTime, | |
14 | * generalTime GeneralizedTime } | |
f6aed2cd DSH |
15 | */ |
16 | ||
17 | #include <stdio.h> | |
18 | #include <time.h> | |
b39fc560 | 19 | #include "internal/cryptlib.h" |
9d6b1ce6 | 20 | #include <openssl/asn1t.h> |
1c455bc0 | 21 | #include "asn1_locl.h" |
f6aed2cd | 22 | |
9d6b1ce6 | 23 | IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME) |
08e9c1af | 24 | |
9d6b1ce6 | 25 | IMPLEMENT_ASN1_FUNCTIONS(ASN1_TIME) |
08e9c1af | 26 | |
3d0f1cb9 PY |
27 | static int leap_year(const int year) |
28 | { | |
29 | if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) | |
30 | return 1; | |
31 | return 0; | |
32 | } | |
33 | ||
34 | int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d) | |
35 | { | |
36 | static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 }; | |
37 | static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 }; | |
38 | static const int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
39 | char *a; | |
40 | int n, i, i2, l, o, min_l = 11, strict = 0, end = 6, btz = 5, md; | |
41 | struct tm tmp; | |
42 | ||
43 | /* | |
44 | * ASN1_STRING_FLAG_X509_TIME is used to enforce RFC 5280 | |
45 | * time string format, in which: | |
46 | * | |
47 | * 1. "seconds" is a 'MUST' | |
48 | * 2. "Zulu" timezone is a 'MUST' | |
49 | * 3. "+|-" is not allowed to indicate a time zone | |
50 | */ | |
51 | if (d->type == V_ASN1_UTCTIME) { | |
52 | if (d->flags & ASN1_STRING_FLAG_X509_TIME) { | |
53 | min_l = 13; | |
54 | strict = 1; | |
55 | } | |
56 | } else if (d->type == V_ASN1_GENERALIZEDTIME) { | |
57 | end = 7; | |
58 | btz = 6; | |
59 | if (d->flags & ASN1_STRING_FLAG_X509_TIME) { | |
60 | min_l = 15; | |
61 | strict = 1; | |
62 | } else { | |
63 | min_l = 13; | |
64 | } | |
65 | } else { | |
66 | return 0; | |
67 | } | |
68 | ||
69 | l = d->length; | |
70 | a = (char *)d->data; | |
71 | o = 0; | |
72 | memset(&tmp, 0, sizeof(tmp)); | |
73 | ||
74 | /* | |
75 | * GENERALIZEDTIME is similar to UTCTIME except the year is represented | |
76 | * as YYYY. This stuff treats everything as a two digit field so make | |
77 | * first two fields 00 to 99 | |
78 | */ | |
79 | ||
80 | if (l < min_l) | |
81 | goto err; | |
82 | for (i = 0; i < end; i++) { | |
83 | if (!strict && (i == btz) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) { | |
84 | i++; | |
85 | break; | |
86 | } | |
87 | if ((a[o] < '0') || (a[o] > '9')) | |
88 | goto err; | |
89 | n = a[o] - '0'; | |
90 | /* incomplete 2-digital number */ | |
91 | if (++o == l) | |
92 | goto err; | |
93 | ||
94 | if ((a[o] < '0') || (a[o] > '9')) | |
95 | goto err; | |
96 | n = (n * 10) + a[o] - '0'; | |
97 | /* no more bytes to read, but we haven't seen time-zone yet */ | |
98 | if (++o == l) | |
99 | goto err; | |
100 | ||
101 | i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i; | |
102 | ||
103 | if ((n < min[i2]) || (n > max[i2])) | |
104 | goto err; | |
105 | switch (i2) { | |
106 | case 0: | |
107 | /* UTC will never be here */ | |
108 | tmp.tm_year = n * 100 - 1900; | |
109 | break; | |
110 | case 1: | |
111 | if (d->type == V_ASN1_UTCTIME) | |
112 | tmp.tm_year = n < 50 ? n + 100 : n; | |
113 | else | |
114 | tmp.tm_year += n; | |
115 | break; | |
116 | case 2: | |
117 | tmp.tm_mon = n - 1; | |
118 | break; | |
119 | case 3: | |
120 | /* check if tm_mday is valid in tm_mon */ | |
121 | if (tmp.tm_mon == 1) { | |
122 | /* it's February */ | |
123 | md = mdays[1] + leap_year(tmp.tm_year + 1900); | |
124 | } else { | |
125 | md = mdays[tmp.tm_mon]; | |
126 | } | |
127 | if (n > md) | |
128 | goto err; | |
129 | tmp.tm_mday = n; | |
130 | break; | |
131 | case 4: | |
132 | tmp.tm_hour = n; | |
133 | break; | |
134 | case 5: | |
135 | tmp.tm_min = n; | |
136 | break; | |
137 | case 6: | |
138 | tmp.tm_sec = n; | |
139 | break; | |
140 | } | |
141 | } | |
142 | ||
143 | /* | |
144 | * Optional fractional seconds: decimal point followed by one or more | |
145 | * digits. | |
146 | */ | |
147 | if (d->type == V_ASN1_GENERALIZEDTIME && a[o] == '.') { | |
148 | if (strict) | |
149 | /* RFC 5280 forbids fractional seconds */ | |
150 | goto err; | |
151 | if (++o == l) | |
152 | goto err; | |
153 | i = o; | |
154 | while ((o < l) && (a[o] >= '0') && (a[o] <= '9')) | |
155 | o++; | |
156 | /* Must have at least one digit after decimal point */ | |
157 | if (i == o) | |
158 | goto err; | |
159 | /* no more bytes to read, but we haven't seen time-zone yet */ | |
160 | if (o == l) | |
161 | goto err; | |
162 | } | |
163 | ||
164 | /* | |
165 | * 'o' will never point to '\0' at this point, the only chance | |
166 | * 'o' can point to '\0' is either the subsequent if or the first | |
167 | * else if is true. | |
168 | */ | |
169 | if (a[o] == 'Z') { | |
170 | o++; | |
171 | } else if (!strict && ((a[o] == '+') || (a[o] == '-'))) { | |
172 | int offsign = a[o] == '-' ? 1 : -1; | |
173 | int offset = 0; | |
174 | ||
175 | o++; | |
176 | /* | |
177 | * if not equal, no need to do subsequent checks | |
178 | * since the following for-loop will add 'o' by 4 | |
179 | * and the final return statement will check if 'l' | |
180 | * and 'o' are equal. | |
181 | */ | |
182 | if (o + 4 != l) | |
183 | goto err; | |
184 | for (i = end; i < end + 2; i++) { | |
185 | if ((a[o] < '0') || (a[o] > '9')) | |
186 | goto err; | |
187 | n = a[o] - '0'; | |
188 | o++; | |
189 | if ((a[o] < '0') || (a[o] > '9')) | |
190 | goto err; | |
191 | n = (n * 10) + a[o] - '0'; | |
192 | i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i; | |
193 | if ((n < min[i2]) || (n > max[i2])) | |
194 | goto err; | |
195 | /* if tm is NULL, no need to adjust */ | |
196 | if (tm != NULL) { | |
197 | if (i == end) | |
198 | offset = n * 3600; | |
199 | else if (i == end + 1) | |
200 | offset += n * 60; | |
201 | } | |
202 | o++; | |
203 | } | |
204 | if (offset && !OPENSSL_gmtime_adj(&tmp, 0, offset * offsign)) | |
205 | goto err; | |
206 | } else { | |
207 | /* not Z, or not +/- in non-strict mode */ | |
208 | goto err; | |
209 | } | |
210 | if (o == l) { | |
211 | /* success, check if tm should be filled */ | |
212 | if (tm != NULL) | |
213 | *tm = tmp; | |
214 | return 1; | |
215 | } | |
216 | err: | |
217 | return 0; | |
218 | } | |
219 | ||
6b691a5c | 220 | ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t) |
0f113f3e MC |
221 | { |
222 | return ASN1_TIME_adj(s, t, 0, 0); | |
223 | } | |
87d3a0cd DSH |
224 | |
225 | ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t, | |
0f113f3e MC |
226 | int offset_day, long offset_sec) |
227 | { | |
228 | struct tm *ts; | |
229 | struct tm data; | |
230 | ||
231 | ts = OPENSSL_gmtime(&t, &data); | |
232 | if (ts == NULL) { | |
233 | ASN1err(ASN1_F_ASN1_TIME_ADJ, ASN1_R_ERROR_GETTING_TIME); | |
234 | return NULL; | |
235 | } | |
236 | if (offset_day || offset_sec) { | |
237 | if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec)) | |
238 | return NULL; | |
239 | } | |
240 | if ((ts->tm_year >= 50) && (ts->tm_year < 150)) | |
241 | return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec); | |
242 | return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec); | |
243 | } | |
02e4fbed | 244 | |
359b0c9f | 245 | int ASN1_TIME_check(const ASN1_TIME *t) |
0f113f3e MC |
246 | { |
247 | if (t->type == V_ASN1_GENERALIZEDTIME) | |
248 | return ASN1_GENERALIZEDTIME_check(t); | |
249 | else if (t->type == V_ASN1_UTCTIME) | |
250 | return ASN1_UTCTIME_check(t); | |
251 | return 0; | |
252 | } | |
02e4fbed DSH |
253 | |
254 | /* Convert an ASN1_TIME structure to GeneralizedTime */ | |
9bfeeef8 | 255 | ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *t, |
0f113f3e MC |
256 | ASN1_GENERALIZEDTIME **out) |
257 | { | |
4483e234 | 258 | ASN1_GENERALIZEDTIME *ret = NULL; |
0f113f3e | 259 | char *str; |
0f113f3e MC |
260 | |
261 | if (!ASN1_TIME_check(t)) | |
262 | return NULL; | |
263 | ||
75ebbd9a RS |
264 | if (out == NULL || *out == NULL) { |
265 | if ((ret = ASN1_GENERALIZEDTIME_new()) == NULL) | |
4483e234 | 266 | goto err; |
0f113f3e MC |
267 | } else |
268 | ret = *out; | |
269 | ||
270 | /* If already GeneralizedTime just copy across */ | |
271 | if (t->type == V_ASN1_GENERALIZEDTIME) { | |
272 | if (!ASN1_STRING_set(ret, t->data, t->length)) | |
4483e234 TS |
273 | goto err; |
274 | goto done; | |
0f113f3e MC |
275 | } |
276 | ||
60eba30f P |
277 | /* |
278 | * Grow the string by two bytes. | |
279 | * The actual allocation is t->length + 3 to include a terminator byte. | |
280 | */ | |
0f113f3e | 281 | if (!ASN1_STRING_set(ret, NULL, t->length + 2)) |
4483e234 | 282 | goto err; |
0f113f3e MC |
283 | str = (char *)ret->data; |
284 | /* Work out the century and prepend */ | |
60eba30f P |
285 | memcpy(str, t->data[0] >= '5' ? "19" : "20", 2); |
286 | /* | |
287 | * t->length + 1 is the size of the data and the allocated buffer has | |
288 | * this much space after the first two characters. | |
289 | */ | |
290 | OPENSSL_strlcpy(str + 2, (const char *)t->data, t->length + 1); | |
0f113f3e | 291 | |
4483e234 TS |
292 | done: |
293 | if (out != NULL && *out == NULL) | |
294 | *out = ret; | |
295 | return ret; | |
296 | ||
297 | err: | |
298 | if (out == NULL || *out != ret) | |
299 | ASN1_GENERALIZEDTIME_free(ret); | |
300 | return NULL; | |
0f113f3e | 301 | } |
33ab2e31 DSH |
302 | |
303 | int ASN1_TIME_set_string(ASN1_TIME *s, const char *str) | |
0f113f3e MC |
304 | { |
305 | ASN1_TIME t; | |
306 | ||
307 | t.length = strlen(str); | |
308 | t.data = (unsigned char *)str; | |
309 | t.flags = 0; | |
310 | ||
311 | t.type = V_ASN1_UTCTIME; | |
312 | ||
313 | if (!ASN1_TIME_check(&t)) { | |
314 | t.type = V_ASN1_GENERALIZEDTIME; | |
315 | if (!ASN1_TIME_check(&t)) | |
316 | return 0; | |
317 | } | |
318 | ||
319 | if (s && !ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t)) | |
320 | return 0; | |
321 | ||
322 | return 1; | |
323 | } | |
359b0c9f | 324 | |
04e62715 RS |
325 | int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str) |
326 | { | |
327 | ASN1_TIME t; | |
328 | struct tm tm; | |
329 | int rv = 0; | |
330 | ||
331 | t.length = strlen(str); | |
332 | t.data = (unsigned char *)str; | |
333 | t.flags = ASN1_STRING_FLAG_X509_TIME; | |
334 | ||
335 | t.type = V_ASN1_UTCTIME; | |
336 | ||
337 | if (!ASN1_TIME_check(&t)) { | |
338 | t.type = V_ASN1_GENERALIZEDTIME; | |
339 | if (!ASN1_TIME_check(&t)) | |
340 | goto out; | |
341 | } | |
342 | ||
343 | /* | |
344 | * Per RFC 5280 (section 4.1.2.5.), the valid input time | |
345 | * strings should be encoded with the following rules: | |
346 | * | |
347 | * 1. UTC: YYMMDDHHMMSSZ, if YY < 50 (20YY) --> UTC: YYMMDDHHMMSSZ | |
348 | * 2. UTC: YYMMDDHHMMSSZ, if YY >= 50 (19YY) --> UTC: YYMMDDHHMMSSZ | |
349 | * 3. G'd: YYYYMMDDHHMMSSZ, if YYYY >= 2050 --> G'd: YYYYMMDDHHMMSSZ | |
350 | * 4. G'd: YYYYMMDDHHMMSSZ, if YYYY < 2050 --> UTC: YYMMDDHHMMSSZ | |
351 | * | |
352 | * Only strings of the 4th rule should be reformatted, but since a | |
353 | * UTC can only present [1950, 2050), so if the given time string | |
354 | * is less than 1950 (e.g. 19230419000000Z), we do nothing... | |
355 | */ | |
356 | ||
357 | if (s != NULL && t.type == V_ASN1_GENERALIZEDTIME) { | |
3d0f1cb9 | 358 | if (!asn1_time_to_tm(&tm, &t)) |
04e62715 RS |
359 | goto out; |
360 | if (tm.tm_year >= 50 && tm.tm_year < 150) { | |
361 | t.length -= 2; | |
362 | /* | |
363 | * it's OK to let original t.data go since that's assigned | |
364 | * to a piece of memory allocated outside of this function. | |
365 | * new t.data would be freed after ASN1_STRING_copy is done. | |
366 | */ | |
367 | t.data = OPENSSL_zalloc(t.length + 1); | |
368 | if (t.data == NULL) | |
369 | goto out; | |
370 | memcpy(t.data, str + 2, t.length); | |
371 | t.type = V_ASN1_UTCTIME; | |
372 | } | |
373 | } | |
374 | ||
375 | if (s == NULL || ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t)) | |
376 | rv = 1; | |
377 | ||
378 | if (t.data != (unsigned char *)str) | |
379 | OPENSSL_free(t.data); | |
380 | out: | |
381 | return rv; | |
382 | } | |
383 | ||
1c036c64 | 384 | int ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm) |
0f113f3e | 385 | { |
1c036c64 | 386 | if (s == NULL) { |
0f113f3e | 387 | time_t now_t; |
1c036c64 | 388 | |
0f113f3e | 389 | time(&now_t); |
1c036c64 | 390 | memset(tm, 0, sizeof(*tm)); |
0f113f3e MC |
391 | if (OPENSSL_gmtime(&now_t, tm)) |
392 | return 1; | |
393 | return 0; | |
394 | } | |
395 | ||
3d0f1cb9 | 396 | return asn1_time_to_tm(tm, s); |
0f113f3e | 397 | } |
1c455bc0 | 398 | |
360ef676 | 399 | int ASN1_TIME_diff(int *pday, int *psec, |
0f113f3e MC |
400 | const ASN1_TIME *from, const ASN1_TIME *to) |
401 | { | |
402 | struct tm tm_from, tm_to; | |
1c036c64 TS |
403 | |
404 | if (!ASN1_TIME_to_tm(from, &tm_from)) | |
0f113f3e | 405 | return 0; |
1c036c64 | 406 | if (!ASN1_TIME_to_tm(to, &tm_to)) |
0f113f3e MC |
407 | return 0; |
408 | return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to); | |
409 | } | |
0d0099ea DSH |
410 | |
411 | int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm) | |
412 | { | |
413 | if (tm->type == V_ASN1_UTCTIME) | |
414 | return ASN1_UTCTIME_print(bp, tm); | |
415 | if (tm->type == V_ASN1_GENERALIZEDTIME) | |
416 | return ASN1_GENERALIZEDTIME_print(bp, tm); | |
417 | BIO_write(bp, "Bad time value", 14); | |
418 | return (0); | |
419 | } |