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