Copyright year updates
[openssl.git] / crypto / asn1 / a_time.c
1 /*
2  * Copyright 1999-2024 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (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
8  */
9
10 /*-
11  * This is an implementation of the ASN1 Time structure which is:
12  *    Time ::= CHOICE {
13  *      utcTime        UTCTime,
14  *      generalTime    GeneralizedTime }
15  */
16
17 #define _XOPEN_SOURCE            /* To get a definition of timezone */
18
19 #include <stdio.h>
20 #include <time.h>
21 #include "crypto/asn1.h"
22 #include "crypto/ctype.h"
23 #include "internal/cryptlib.h"
24 #include <openssl/asn1t.h>
25 #include "asn1_local.h"
26
27 IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME)
28
29 IMPLEMENT_ASN1_FUNCTIONS(ASN1_TIME)
30 IMPLEMENT_ASN1_DUP_FUNCTION(ASN1_TIME)
31
32 static int is_utc(const int year)
33 {
34     if (50 <= year && year <= 149)
35         return 1;
36     return 0;
37 }
38
39 static int leap_year(const int year)
40 {
41     if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
42         return 1;
43     return 0;
44 }
45
46 /*
47  * Compute the day of the week and the day of the year from the year, month
48  * and day.  The day of the year is straightforward, the day of the week uses
49  * a form of Zeller's congruence.  For this months start with March and are
50  * numbered 4 through 15.
51  */
52 static void determine_days(struct tm *tm)
53 {
54     static const int ydays[12] = {
55         0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
56     };
57     int y = tm->tm_year + 1900;
58     int m = tm->tm_mon;
59     int d = tm->tm_mday;
60     int c;
61
62     tm->tm_yday = ydays[m] + d - 1;
63     if (m >= 2) {
64         /* March and onwards can be one day further into the year */
65         tm->tm_yday += leap_year(y);
66         m += 2;
67     } else {
68         /* Treat January and February as part of the previous year */
69         m += 14;
70         y--;
71     }
72     c = y / 100;
73     y %= 100;
74     /* Zeller's congruence */
75     tm->tm_wday = (d + (13 * m) / 5 + y + y / 4 + c / 4 + 5 * c + 6) % 7;
76 }
77
78 int ossl_asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
79 {
80     static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
81     static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 };
82     static const int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
83     char *a;
84     int n, i, i2, l, o, min_l, strict = 0, end = 6, btz = 5, md;
85     struct tm tmp;
86 #if defined(CHARSET_EBCDIC)
87     const char upper_z = 0x5A, num_zero = 0x30, period = 0x2E, minus = 0x2D, plus = 0x2B;
88 #else
89     const char upper_z = 'Z', num_zero = '0', period = '.', minus = '-', plus = '+';
90 #endif
91     /*
92      * ASN1_STRING_FLAG_X509_TIME is used to enforce RFC 5280
93      * time string format, in which:
94      *
95      * 1. "seconds" is a 'MUST'
96      * 2. "Zulu" timezone is a 'MUST'
97      * 3. "+|-" is not allowed to indicate a timezone
98      */
99     if (d->type == V_ASN1_UTCTIME) {
100         min_l = 13;
101         if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
102             strict = 1;
103         }
104     } else if (d->type == V_ASN1_GENERALIZEDTIME) {
105         end = 7;
106         btz = 6;
107         min_l = 15;
108         if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
109             strict = 1;
110         }
111     } else {
112         return 0;
113     }
114
115     l = d->length;
116     a = (char *)d->data;
117     o = 0;
118     memset(&tmp, 0, sizeof(tmp));
119
120     /*
121      * GENERALIZEDTIME is similar to UTCTIME except the year is represented
122      * as YYYY. This stuff treats everything as a two digit field so make
123      * first two fields 00 to 99
124      */
125
126     if (l < min_l)
127         goto err;
128     for (i = 0; i < end; i++) {
129         if (!strict && (i == btz) && ((a[o] == upper_z) || (a[o] == plus) || (a[o] == minus))) {
130             i++;
131             break;
132         }
133         if (!ossl_ascii_isdigit(a[o]))
134             goto err;
135         n = a[o] - num_zero;
136         /* incomplete 2-digital number */
137         if (++o == l)
138             goto err;
139
140         if (!ossl_ascii_isdigit(a[o]))
141             goto err;
142         n = (n * 10) + a[o] - num_zero;
143         /* no more bytes to read, but we haven't seen time-zone yet */
144         if (++o == l)
145             goto err;
146
147         i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i;
148
149         if ((n < min[i2]) || (n > max[i2]))
150             goto err;
151         switch (i2) {
152         case 0:
153             /* UTC will never be here */
154             tmp.tm_year = n * 100 - 1900;
155             break;
156         case 1:
157             if (d->type == V_ASN1_UTCTIME)
158                 tmp.tm_year = n < 50 ? n + 100 : n;
159             else
160                 tmp.tm_year += n;
161             break;
162         case 2:
163             tmp.tm_mon = n - 1;
164             break;
165         case 3:
166             /* check if tm_mday is valid in tm_mon */
167             if (tmp.tm_mon == 1) {
168                 /* it's February */
169                 md = mdays[1] + leap_year(tmp.tm_year + 1900);
170             } else {
171                 md = mdays[tmp.tm_mon];
172             }
173             if (n > md)
174                 goto err;
175             tmp.tm_mday = n;
176             determine_days(&tmp);
177             break;
178         case 4:
179             tmp.tm_hour = n;
180             break;
181         case 5:
182             tmp.tm_min = n;
183             break;
184         case 6:
185             tmp.tm_sec = n;
186             break;
187         }
188     }
189
190     /*
191      * Optional fractional seconds: decimal point followed by one or more
192      * digits.
193      */
194     if (d->type == V_ASN1_GENERALIZEDTIME && a[o] == period) {
195         if (strict)
196             /* RFC 5280 forbids fractional seconds */
197             goto err;
198         if (++o == l)
199             goto err;
200         i = o;
201         while ((o < l) && ossl_ascii_isdigit(a[o]))
202             o++;
203         /* Must have at least one digit after decimal point */
204         if (i == o)
205             goto err;
206         /* no more bytes to read, but we haven't seen time-zone yet */
207         if (o == l)
208             goto err;
209     }
210
211     /*
212      * 'o' will never point to '\0' at this point, the only chance
213      * 'o' can point to '\0' is either the subsequent if or the first
214      * else if is true.
215      */
216     if (a[o] == upper_z) {
217         o++;
218     } else if (!strict && ((a[o] == plus) || (a[o] == minus))) {
219         int offsign = a[o] == minus ? 1 : -1;
220         int offset = 0;
221
222         o++;
223         /*
224          * if not equal, no need to do subsequent checks
225          * since the following for-loop will add 'o' by 4
226          * and the final return statement will check if 'l'
227          * and 'o' are equal.
228          */
229         if (o + 4 != l)
230             goto err;
231         for (i = end; i < end + 2; i++) {
232             if (!ossl_ascii_isdigit(a[o]))
233                 goto err;
234             n = a[o] - num_zero;
235             o++;
236             if (!ossl_ascii_isdigit(a[o]))
237                 goto err;
238             n = (n * 10) + a[o] - num_zero;
239             i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i;
240             if ((n < min[i2]) || (n > max[i2]))
241                 goto err;
242             /* if tm is NULL, no need to adjust */
243             if (tm != NULL) {
244                 if (i == end)
245                     offset = n * 3600;
246                 else if (i == end + 1)
247                     offset += n * 60;
248             }
249             o++;
250         }
251         if (offset && !OPENSSL_gmtime_adj(&tmp, 0, offset * offsign))
252             goto err;
253     } else {
254         /* not Z, or not +/- in non-strict mode */
255         goto err;
256     }
257     if (o == l) {
258         /* success, check if tm should be filled */
259         if (tm != NULL)
260             *tm = tmp;
261         return 1;
262     }
263  err:
264     return 0;
265 }
266
267 ASN1_TIME *ossl_asn1_time_from_tm(ASN1_TIME *s, struct tm *ts, int type)
268 {
269     char* p;
270     ASN1_TIME *tmps = NULL;
271     const size_t len = 20;
272
273     if (type == V_ASN1_UNDEF) {
274         if (is_utc(ts->tm_year))
275             type = V_ASN1_UTCTIME;
276         else
277             type = V_ASN1_GENERALIZEDTIME;
278     } else if (type == V_ASN1_UTCTIME) {
279         if (!is_utc(ts->tm_year))
280             goto err;
281     } else if (type != V_ASN1_GENERALIZEDTIME) {
282         goto err;
283     }
284
285     if (s == NULL)
286         tmps = ASN1_STRING_new();
287     else
288         tmps = s;
289     if (tmps == NULL)
290         return NULL;
291
292     if (!ASN1_STRING_set(tmps, NULL, len))
293         goto err;
294
295     tmps->type = type;
296     p = (char*)tmps->data;
297
298     if (ts->tm_mon > INT_MAX - 1)
299         goto err;
300
301     if (type == V_ASN1_GENERALIZEDTIME) {
302         if (ts->tm_year > INT_MAX - 1900)
303             goto err;
304         tmps->length = BIO_snprintf(p, len, "%04d%02d%02d%02d%02d%02dZ",
305                                     ts->tm_year + 1900, ts->tm_mon + 1,
306                                     ts->tm_mday, ts->tm_hour, ts->tm_min,
307                                     ts->tm_sec);
308     } else {
309         tmps->length = BIO_snprintf(p, len, "%02d%02d%02d%02d%02d%02dZ",
310                                     ts->tm_year % 100, ts->tm_mon + 1,
311                                     ts->tm_mday, ts->tm_hour, ts->tm_min,
312                                     ts->tm_sec);
313     }
314
315 #ifdef CHARSET_EBCDIC
316     ebcdic2ascii(tmps->data, tmps->data, tmps->length);
317 #endif
318     return tmps;
319  err:
320     if (tmps != s)
321         ASN1_STRING_free(tmps);
322     return NULL;
323 }
324
325 ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t)
326 {
327     return ASN1_TIME_adj(s, t, 0, 0);
328 }
329
330 ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t,
331                          int offset_day, long offset_sec)
332 {
333     struct tm *ts;
334     struct tm data;
335
336     ts = OPENSSL_gmtime(&t, &data);
337     if (ts == NULL) {
338         ERR_raise(ERR_LIB_ASN1, ASN1_R_ERROR_GETTING_TIME);
339         return NULL;
340     }
341     if (offset_day || offset_sec) {
342         if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
343             return NULL;
344     }
345     return ossl_asn1_time_from_tm(s, ts, V_ASN1_UNDEF);
346 }
347
348 int ASN1_TIME_check(const ASN1_TIME *t)
349 {
350     if (t->type == V_ASN1_GENERALIZEDTIME)
351         return ASN1_GENERALIZEDTIME_check(t);
352     else if (t->type == V_ASN1_UTCTIME)
353         return ASN1_UTCTIME_check(t);
354     return 0;
355 }
356
357 /* Convert an ASN1_TIME structure to GeneralizedTime */
358 ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *t,
359                                                    ASN1_GENERALIZEDTIME **out)
360 {
361     ASN1_GENERALIZEDTIME *ret = NULL;
362     struct tm tm;
363
364     if (!ASN1_TIME_to_tm(t, &tm))
365         return NULL;
366
367     if (out != NULL)
368         ret = *out;
369
370     ret = ossl_asn1_time_from_tm(ret, &tm, V_ASN1_GENERALIZEDTIME);
371
372     if (out != NULL && ret != NULL)
373         *out = ret;
374
375     return ret;
376 }
377
378 int ASN1_TIME_set_string(ASN1_TIME *s, const char *str)
379 {
380     /* Try UTC, if that fails, try GENERALIZED */
381     if (ASN1_UTCTIME_set_string(s, str))
382         return 1;
383     return ASN1_GENERALIZEDTIME_set_string(s, str);
384 }
385
386 int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str)
387 {
388     ASN1_TIME t;
389     struct tm tm;
390     int rv = 0;
391
392     t.length = strlen(str);
393     t.data = (unsigned char *)str;
394     t.flags = ASN1_STRING_FLAG_X509_TIME;
395
396     t.type = V_ASN1_UTCTIME;
397
398     if (!ASN1_TIME_check(&t)) {
399         t.type = V_ASN1_GENERALIZEDTIME;
400         if (!ASN1_TIME_check(&t))
401             goto out;
402     }
403
404     /*
405      * Per RFC 5280 (section 4.1.2.5.), the valid input time
406      * strings should be encoded with the following rules:
407      *
408      * 1. UTC: YYMMDDHHMMSSZ, if YY < 50 (20YY) --> UTC: YYMMDDHHMMSSZ
409      * 2. UTC: YYMMDDHHMMSSZ, if YY >= 50 (19YY) --> UTC: YYMMDDHHMMSSZ
410      * 3. G'd: YYYYMMDDHHMMSSZ, if YYYY >= 2050 --> G'd: YYYYMMDDHHMMSSZ
411      * 4. G'd: YYYYMMDDHHMMSSZ, if YYYY < 2050 --> UTC: YYMMDDHHMMSSZ
412      *
413      * Only strings of the 4th rule should be reformatted, but since a
414      * UTC can only present [1950, 2050), so if the given time string
415      * is less than 1950 (e.g. 19230419000000Z), we do nothing...
416      */
417
418     if (s != NULL && t.type == V_ASN1_GENERALIZEDTIME) {
419         if (!ossl_asn1_time_to_tm(&tm, &t))
420             goto out;
421         if (is_utc(tm.tm_year)) {
422             t.length -= 2;
423             /*
424              * it's OK to let original t.data go since that's assigned
425              * to a piece of memory allocated outside of this function.
426              * new t.data would be freed after ASN1_STRING_copy is done.
427              */
428             t.data = OPENSSL_zalloc(t.length + 1);
429             if (t.data == NULL)
430                 goto out;
431             memcpy(t.data, str + 2, t.length);
432             t.type = V_ASN1_UTCTIME;
433         }
434     }
435
436     if (s == NULL || ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t))
437         rv = 1;
438
439     if (t.data != (unsigned char *)str)
440         OPENSSL_free(t.data);
441 out:
442     return rv;
443 }
444
445 int ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm)
446 {
447     if (s == NULL) {
448         time_t now_t;
449
450         time(&now_t);
451         memset(tm, 0, sizeof(*tm));
452         if (OPENSSL_gmtime(&now_t, tm) != NULL)
453             return 1;
454         return 0;
455     }
456
457     return ossl_asn1_time_to_tm(tm, s);
458 }
459
460 int ASN1_TIME_diff(int *pday, int *psec,
461                    const ASN1_TIME *from, const ASN1_TIME *to)
462 {
463     struct tm tm_from, tm_to;
464
465     if (!ASN1_TIME_to_tm(from, &tm_from))
466         return 0;
467     if (!ASN1_TIME_to_tm(to, &tm_to))
468         return 0;
469     return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to);
470 }
471
472 static const char _asn1_mon[12][4] = {
473     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
474     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
475 };
476
477 /* prints the time with the default date format (RFC 822) */
478 int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
479 {
480     return ASN1_TIME_print_ex(bp, tm, ASN1_DTFLGS_RFC822);
481 }
482
483 /* returns 1 on success, 0 on BIO write error or parse failure */
484 int ASN1_TIME_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags)
485 {
486     return ossl_asn1_time_print_ex(bp, tm, flags) > 0;
487 }
488
489
490 /* prints the time with the date format of ISO 8601 */
491 /* returns 0 on BIO write error, else -1 in case of parse failure, else 1 */
492 int ossl_asn1_time_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags)
493 {
494     char *v;
495     int gmt = 0, l;
496     struct tm stm;
497     const char upper_z = 0x5A, period = 0x2E;
498
499     /* ossl_asn1_time_to_tm will check the time type */
500     if (!ossl_asn1_time_to_tm(&stm, tm))
501         return BIO_write(bp, "Bad time value", 14) ? -1 : 0;
502
503     l = tm->length;
504     v = (char *)tm->data;
505     if (v[l - 1] == upper_z)
506         gmt = 1;
507
508     if (tm->type == V_ASN1_GENERALIZEDTIME) {
509         char *f = NULL;
510         int f_len = 0;
511
512         /*
513          * Try to parse fractional seconds. '14' is the place of
514          * 'fraction point' in a GeneralizedTime string.
515          */
516         if (tm->length > 15 && v[14] == period) {
517             f = &v[14];
518             f_len = 1;
519             while (14 + f_len < l && ossl_ascii_isdigit(f[f_len]))
520                 ++f_len;
521         }
522
523         if ((flags & ASN1_DTFLGS_TYPE_MASK) == ASN1_DTFLGS_ISO8601) {
524             return BIO_printf(bp, "%4d-%02d-%02d %02d:%02d:%02d%.*s%s",
525                           stm.tm_year + 1900, stm.tm_mon + 1,
526                           stm.tm_mday, stm.tm_hour,
527                           stm.tm_min, stm.tm_sec, f_len, f,
528                           (gmt ? "Z" : "")) > 0;
529         }
530         else {
531             return BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
532                           _asn1_mon[stm.tm_mon], stm.tm_mday, stm.tm_hour,
533                           stm.tm_min, stm.tm_sec, f_len, f, stm.tm_year + 1900,
534                           (gmt ? " GMT" : "")) > 0;
535         }
536     } else {
537         if ((flags & ASN1_DTFLGS_TYPE_MASK) == ASN1_DTFLGS_ISO8601) {
538             return BIO_printf(bp, "%4d-%02d-%02d %02d:%02d:%02d%s",
539                           stm.tm_year + 1900, stm.tm_mon + 1,
540                           stm.tm_mday, stm.tm_hour,
541                           stm.tm_min, stm.tm_sec,
542                           (gmt ? "Z" : "")) > 0;
543         }
544         else {
545             return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s",
546                           _asn1_mon[stm.tm_mon], stm.tm_mday, stm.tm_hour,
547                           stm.tm_min, stm.tm_sec, stm.tm_year + 1900,
548                           (gmt ? " GMT" : "")) > 0;
549         }
550     }
551 }
552
553 int ASN1_TIME_cmp_time_t(const ASN1_TIME *s, time_t t)
554 {
555     struct tm stm, ttm;
556     int day, sec;
557
558     if (!ASN1_TIME_to_tm(s, &stm))
559         return -2;
560
561     if (!OPENSSL_gmtime(&t, &ttm))
562         return -2;
563
564     if (!OPENSSL_gmtime_diff(&day, &sec, &ttm, &stm))
565         return -2;
566
567     if (day > 0 || sec > 0)
568         return 1;
569     if (day < 0 || sec < 0)
570         return -1;
571     return 0;
572 }
573
574 int ASN1_TIME_normalize(ASN1_TIME *t)
575 {
576     struct tm tm;
577
578     if (t == NULL || !ASN1_TIME_to_tm(t, &tm))
579         return 0;
580
581     return ossl_asn1_time_from_tm(t, &tm, V_ASN1_UNDEF) != NULL;
582 }
583
584 int ASN1_TIME_compare(const ASN1_TIME *a, const ASN1_TIME *b)
585 {
586     int day, sec;
587
588     if (!ASN1_TIME_diff(&day, &sec, b, a))
589         return -2;
590     if (day > 0 || sec > 0)
591         return 1;
592     if (day < 0 || sec < 0)
593         return -1;
594     return 0;
595 }
596
597 /*
598  * tweak for Windows
599  */
600 #ifdef WIN32
601 # define timezone _timezone
602 #endif
603
604 #if defined(__FreeBSD__) || defined(__wasi__)
605 # define USE_TIMEGM
606 #endif
607
608 time_t ossl_asn1_string_to_time_t(const char *asn1_string)
609 {
610     ASN1_TIME *timestamp_asn1 = NULL;
611     struct tm *timestamp_tm = NULL;
612 #if defined(__DJGPP__)
613     char *tz = NULL;
614 #elif !defined(USE_TIMEGM)
615     time_t timestamp_local;
616 #endif
617     time_t timestamp_utc;
618
619     timestamp_asn1 = ASN1_TIME_new();
620     if (!ASN1_TIME_set_string(timestamp_asn1, asn1_string))
621     {
622         ASN1_TIME_free(timestamp_asn1);
623         return -1;
624     }
625
626     timestamp_tm = OPENSSL_malloc(sizeof(*timestamp_tm));
627     if (timestamp_tm == NULL) {
628         ASN1_TIME_free(timestamp_asn1);
629         return -1;
630     }
631     if (!(ASN1_TIME_to_tm(timestamp_asn1, timestamp_tm))) {
632         OPENSSL_free(timestamp_tm);
633         ASN1_TIME_free(timestamp_asn1);
634         return -1;
635     }
636     ASN1_TIME_free(timestamp_asn1);
637
638 #if defined(__DJGPP__)
639     /*
640      * This is NOT thread-safe.  Do not use this method for platforms other
641      * than djgpp.
642      */
643     tz = getenv("TZ");
644     if (tz != NULL) {
645         tz = OPENSSL_strdup(tz);
646         if (tz == NULL) {
647             OPENSSL_free(timestamp_tm);
648             return -1;
649         }
650     }
651     setenv("TZ", "UTC", 1);
652
653     timestamp_utc = mktime(timestamp_tm);
654
655     if (tz != NULL) {
656         setenv("TZ", tz, 1);
657         OPENSSL_free(tz);
658     } else {
659         unsetenv("TZ");
660     }
661 #elif defined(USE_TIMEGM)
662     timestamp_utc = timegm(timestamp_tm);
663 #else
664     timestamp_local = mktime(timestamp_tm);
665     timestamp_utc = timestamp_local - timezone;
666 #endif
667     OPENSSL_free(timestamp_tm);
668
669     return timestamp_utc;
670 }