1/* $OpenBSD: a_time_tm.c,v 1.42 2024/05/03 18:33:27 tb 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 <ctype.h>
19#include <limits.h>
20#include <stdio.h>
21#include <string.h>
22#include <time.h>
23
24#include <openssl/asn1t.h>
25#include <openssl/err.h>
26
27#include "bytestring.h"
28#include "asn1_local.h"
29
30#define RFC5280 0
31#define GENTIME_LENGTH 15
32#define UTCTIME_LENGTH 13
33
34int
35ASN1_time_tm_cmp(struct tm *tm1, struct tm *tm2)
36{
37	if (tm1->tm_year < tm2->tm_year)
38		return -1;
39	if (tm1->tm_year > tm2->tm_year)
40		return 1;
41	if (tm1->tm_mon < tm2->tm_mon)
42		return -1;
43	if (tm1->tm_mon > tm2->tm_mon)
44		return 1;
45	if (tm1->tm_mday < tm2->tm_mday)
46		return -1;
47	if (tm1->tm_mday > tm2->tm_mday)
48		return 1;
49	if (tm1->tm_hour < tm2->tm_hour)
50		return -1;
51	if (tm1->tm_hour > tm2->tm_hour)
52		return 1;
53	if (tm1->tm_min < tm2->tm_min)
54		return -1;
55	if (tm1->tm_min > tm2->tm_min)
56		return 1;
57	if (tm1->tm_sec < tm2->tm_sec)
58		return -1;
59	if (tm1->tm_sec > tm2->tm_sec)
60		return 1;
61	return 0;
62}
63
64int
65ASN1_time_tm_clamp_notafter(struct tm *tm)
66{
67#ifdef SMALL_TIME_T
68	struct tm broken_os_epoch_tm;
69	time_t broken_os_epoch_time = INT_MAX;
70
71	if (!asn1_time_time_t_to_tm(&broken_os_epoch_time, &broken_os_epoch_tm))
72		return 0;
73
74	if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
75		memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
76#endif
77	return 1;
78}
79
80/* Convert time to GeneralizedTime, X.690, 11.7. */
81static int
82tm_to_gentime(struct tm *tm, ASN1_TIME *atime)
83{
84	char *time_str = NULL;
85
86	if (tm->tm_year < -1900 || tm->tm_year > 9999 - 1900) {
87		ASN1error(ASN1_R_ILLEGAL_TIME_VALUE);
88		return 0;
89	}
90
91	if (asprintf(&time_str, "%04u%02u%02u%02u%02u%02uZ", tm->tm_year + 1900,
92	    tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
93	    tm->tm_sec) == -1) {
94		ASN1error(ERR_R_MALLOC_FAILURE);
95		return 0;
96	}
97
98	free(atime->data);
99	atime->data = time_str;
100	atime->length = GENTIME_LENGTH;
101	atime->type = V_ASN1_GENERALIZEDTIME;
102
103	return 1;
104}
105
106/* Convert time to UTCTime, X.690, 11.8. */
107static int
108tm_to_utctime(struct tm *tm, ASN1_TIME *atime)
109{
110	char *time_str = NULL;
111
112	if (tm->tm_year >= 150 || tm->tm_year < 50) {
113		ASN1error(ASN1_R_ILLEGAL_TIME_VALUE);
114		return 0;
115	}
116
117	if (asprintf(&time_str, "%02u%02u%02u%02u%02u%02uZ",
118	    tm->tm_year % 100,  tm->tm_mon + 1, tm->tm_mday,
119	    tm->tm_hour, tm->tm_min, tm->tm_sec) == -1) {
120		ASN1error(ERR_R_MALLOC_FAILURE);
121		return 0;
122	}
123
124	free(atime->data);
125	atime->data = time_str;
126	atime->length = UTCTIME_LENGTH;
127	atime->type = V_ASN1_UTCTIME;
128
129	return 1;
130}
131
132static int
133tm_to_rfc5280_time(struct tm *tm, ASN1_TIME *atime)
134{
135	if (tm->tm_year >= 50 && tm->tm_year < 150)
136		return tm_to_utctime(tm, atime);
137
138	return tm_to_gentime(tm, atime);
139}
140
141
142static int
143cbs_get_two_digit_value(CBS *cbs, int *out)
144{
145	uint8_t first_digit, second_digit;
146
147	if (!CBS_get_u8(cbs, &first_digit))
148		return 0;
149	if (!isdigit(first_digit))
150		return 0;
151	if (!CBS_get_u8(cbs, &second_digit))
152		return 0;
153	if (!isdigit(second_digit))
154		return 0;
155
156	*out = (first_digit - '0') * 10 + (second_digit - '0');
157
158	return 1;
159}
160
161static int
162is_valid_day(int year, int month, int day)
163{
164	if (day < 1)
165		return 0;
166	switch (month) {
167	case 1:
168	case 3:
169	case 5:
170	case 7:
171	case 8:
172	case 10:
173	case 12:
174		return day <= 31;
175	case 4:
176	case 6:
177	case 9:
178	case 11:
179		return day <= 30;
180	case 2:
181		if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
182			return day <= 29;
183		 else
184			return day <= 28;
185	default:
186		return 0;
187	}
188}
189
190/*
191 * asn1_time_parse_cbs returns one if |cbs| is a valid DER-encoded, ASN.1 Time
192 * body within the limitations imposed by RFC 5280, or zero otherwise. The time
193 * is expected to parse as a Generalized Time if is_gentime is true, and as a
194 * UTC Time otherwise. If |out_tm| is non-NULL, |*out_tm| will be zeroed, and
195 * then set to the corresponding time in UTC. This function does not compute
196 * |out_tm->tm_wday| or |out_tm->tm_yday|. |cbs| is not consumed.
197 */
198int
199asn1_time_parse_cbs(const CBS *cbs, int is_gentime, struct tm *out_tm)
200{
201	int year, month, day, hour, min, sec, val;
202	CBS copy;
203	uint8_t tz;
204
205	CBS_dup(cbs, &copy);
206
207	if (is_gentime) {
208		if (!cbs_get_two_digit_value(&copy, &val))
209			return 0;
210		year = val * 100;
211		if (!cbs_get_two_digit_value(&copy, &val))
212			return 0;
213		year += val;
214	} else {
215		year = 1900;
216		if (!cbs_get_two_digit_value(&copy, &val))
217			return 0;
218		year += val;
219		if (year < 1950)
220			year += 100;
221		if (year >= 2050)
222			return 0;  /* A Generalized time must be used. */
223	}
224
225	if (!cbs_get_two_digit_value(&copy, &month))
226		return 0;
227	if (month < 1 || month > 12)
228		return 0; /* Reject invalid months. */
229
230	if (!cbs_get_two_digit_value(&copy, &day))
231		return 0;
232	if (!is_valid_day(year, month, day))
233		return 0; /* Reject invalid days. */
234
235	if (!cbs_get_two_digit_value(&copy, &hour))
236		return 0;
237	if (hour > 23)
238		return 0; /* Reject invalid hours. */
239
240	if (!cbs_get_two_digit_value(&copy, &min))
241		return 0;
242	if (min > 59)
243		return 0; /* Reject invalid minutes. */
244
245	if (!cbs_get_two_digit_value(&copy, &sec))
246		return 0;
247	if (sec > 59)
248		return 0; /* Reject invalid seconds. Leap seconds are invalid. */
249
250	if (!CBS_get_u8(&copy, &tz))
251		return 0;
252	if (tz != 'Z')
253		return 0; /* Reject anything but Z on the end. */
254
255	if (CBS_len(&copy) != 0)
256		return 0;  /* Reject invalid lengths. */
257
258	if (out_tm != NULL) {
259		memset(out_tm, 0, sizeof(*out_tm));
260		/* Fill in the tm fields corresponding to what we validated. */
261		out_tm->tm_year = year - 1900;
262		out_tm->tm_mon = month - 1;
263		out_tm->tm_mday = day;
264		out_tm->tm_hour = hour;
265		out_tm->tm_min = min;
266		out_tm->tm_sec = sec;
267	}
268
269	return 1;
270}
271
272/*
273 * Parse an RFC 5280 format ASN.1 time string.
274 *
275 * mode must be:
276 * 0 if we expect to parse a time as specified in RFC 5280 for an X509 object.
277 * V_ASN1_UTCTIME if we wish to parse an RFC5280 format UTC time.
278 * V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized time.
279 *
280 * Returns:
281 * -1 if the string was invalid.
282 * V_ASN1_UTCTIME if the string validated as a UTC time string.
283 * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string.
284 *
285 * Fills in *tm with the corresponding time if tm is non NULL.
286 */
287int
288ASN1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode)
289{
290	int type = 0;
291	CBS cbs;
292
293	if (bytes == NULL)
294		return -1;
295
296	CBS_init(&cbs, bytes, len);
297
298	if (CBS_len(&cbs) == UTCTIME_LENGTH)
299		type = V_ASN1_UTCTIME;
300	if (CBS_len(&cbs) == GENTIME_LENGTH)
301		type = V_ASN1_GENERALIZEDTIME;
302	if (asn1_time_parse_cbs(&cbs, type == V_ASN1_GENERALIZEDTIME, tm)) {
303		if (mode != 0 && mode != type)
304			return -1;
305		return type;
306	}
307
308	return -1;
309}
310
311/*
312 * ASN1_TIME generic functions.
313 */
314
315static int
316ASN1_TIME_set_string_internal(ASN1_TIME *s, const char *str, int mode)
317{
318	struct tm tm;
319
320	if (ASN1_time_parse(str, strlen(str), &tm, mode) == -1)
321		return 0;
322
323	/* Only check str's format, as documented. */
324	if (s == NULL)
325		return 1;
326
327	switch (mode) {
328	case V_ASN1_UTCTIME:
329		return tm_to_utctime(&tm, s);
330	case V_ASN1_GENERALIZEDTIME:
331		return tm_to_gentime(&tm, s);
332	case RFC5280:
333		return tm_to_rfc5280_time(&tm, s);
334	default:
335		return 0;
336	}
337}
338
339static ASN1_TIME *
340ASN1_TIME_adj_internal(ASN1_TIME *s, time_t t, int offset_day, long offset_sec,
341    int mode)
342{
343	ASN1_TIME *atime = s;
344	struct tm tm;
345
346	if (!asn1_time_time_t_to_tm(&t, &tm))
347		goto err;
348
349	if (offset_day != 0 || offset_sec != 0) {
350		if (!OPENSSL_gmtime_adj(&tm, offset_day, offset_sec))
351			goto err;
352	}
353
354	if (atime == NULL)
355		atime = ASN1_TIME_new();
356	if (atime == NULL)
357		goto err;
358
359	switch (mode) {
360	case V_ASN1_UTCTIME:
361		if (!tm_to_utctime(&tm, atime))
362			goto err;
363		break;
364	case V_ASN1_GENERALIZEDTIME:
365		if (!tm_to_gentime(&tm, atime))
366			goto err;
367		break;
368	case RFC5280:
369		if (!tm_to_rfc5280_time(&tm, atime))
370			goto err;
371		break;
372	default:
373		goto err;
374	}
375
376	return atime;
377
378 err:
379	if (atime != s)
380		ASN1_TIME_free(atime);
381
382	return NULL;
383}
384
385ASN1_TIME *
386ASN1_TIME_set(ASN1_TIME *s, time_t t)
387{
388	return ASN1_TIME_adj(s, t, 0, 0);
389}
390LCRYPTO_ALIAS(ASN1_TIME_set);
391
392ASN1_TIME *
393ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day, long offset_sec)
394{
395	return ASN1_TIME_adj_internal(s, t, offset_day, offset_sec, RFC5280);
396}
397LCRYPTO_ALIAS(ASN1_TIME_adj);
398
399int
400ASN1_TIME_check(const ASN1_TIME *t)
401{
402	if (t->type != V_ASN1_GENERALIZEDTIME && t->type != V_ASN1_UTCTIME)
403		return 0;
404	return t->type == ASN1_time_parse(t->data, t->length, NULL, t->type);
405}
406LCRYPTO_ALIAS(ASN1_TIME_check);
407
408ASN1_GENERALIZEDTIME *
409ASN1_TIME_to_generalizedtime(const ASN1_TIME *t, ASN1_GENERALIZEDTIME **out)
410{
411	ASN1_GENERALIZEDTIME *agt = NULL;
412	struct tm tm;
413
414	if (t->type != V_ASN1_GENERALIZEDTIME && t->type != V_ASN1_UTCTIME)
415		goto err;
416
417	if (t->type != ASN1_time_parse(t->data, t->length, &tm, t->type))
418		goto err;
419
420	if (out == NULL || (agt = *out) == NULL)
421		agt = ASN1_TIME_new();
422	if (agt == NULL)
423		goto err;
424
425	if (!tm_to_gentime(&tm, agt))
426		goto err;
427
428	if (out != NULL)
429		*out = agt;
430
431	return agt;
432
433 err:
434	if (out == NULL || *out != agt)
435		ASN1_TIME_free(agt);
436
437	return NULL;
438}
439LCRYPTO_ALIAS(ASN1_TIME_to_generalizedtime);
440
441int
442ASN1_TIME_set_string(ASN1_TIME *s, const char *str)
443{
444	return ASN1_TIME_set_string_internal(s, str, RFC5280);
445}
446LCRYPTO_ALIAS(ASN1_TIME_set_string);
447
448static int
449ASN1_TIME_cmp_time_t_internal(const ASN1_TIME *s, time_t t2, int mode)
450{
451	struct tm tm1, tm2;
452
453	/*
454	 * This function has never handled failure conditions properly
455	 * The OpenSSL version used to simply follow NULL pointers on failure.
456	 * BoringSSL and OpenSSL now make it return -2 on failure.
457	 *
458	 * The danger is that users of this function will not differentiate the
459	 * -2 failure case from s < t2. Callers must be careful. Sadly this is
460	 * one of those pervasive things from OpenSSL we must continue with.
461	 */
462
463	if (ASN1_time_parse(s->data, s->length, &tm1, mode) == -1)
464		return -2;
465
466	if (!asn1_time_time_t_to_tm(&t2, &tm2))
467		return -2;
468
469	return ASN1_time_tm_cmp(&tm1, &tm2);
470}
471
472int
473ASN1_TIME_compare(const ASN1_TIME *t1, const ASN1_TIME *t2)
474{
475	struct tm tm1, tm2;
476
477	if (t1->type != V_ASN1_UTCTIME && t1->type != V_ASN1_GENERALIZEDTIME)
478		return -2;
479
480	if (t2->type != V_ASN1_UTCTIME && t2->type != V_ASN1_GENERALIZEDTIME)
481		return -2;
482
483	if (ASN1_time_parse(t1->data, t1->length, &tm1, t1->type) == -1)
484		return -2;
485
486	if (ASN1_time_parse(t2->data, t2->length, &tm2, t2->type) == -1)
487		return -2;
488
489	return ASN1_time_tm_cmp(&tm1, &tm2);
490}
491LCRYPTO_ALIAS(ASN1_TIME_compare);
492
493int
494ASN1_TIME_cmp_time_t(const ASN1_TIME *s, time_t t)
495{
496	if (s->type == V_ASN1_UTCTIME)
497		return ASN1_TIME_cmp_time_t_internal(s, t, V_ASN1_UTCTIME);
498	if (s->type == V_ASN1_GENERALIZEDTIME)
499		return ASN1_TIME_cmp_time_t_internal(s, t,
500		    V_ASN1_GENERALIZEDTIME);
501	return -2;
502}
503LCRYPTO_ALIAS(ASN1_TIME_cmp_time_t);
504
505/*
506 * ASN1_UTCTIME wrappers
507 */
508
509int
510ASN1_UTCTIME_check(const ASN1_UTCTIME *d)
511{
512	if (d->type != V_ASN1_UTCTIME)
513		return 0;
514	return d->type == ASN1_time_parse(d->data, d->length, NULL, d->type);
515}
516LCRYPTO_ALIAS(ASN1_UTCTIME_check);
517
518int
519ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str)
520{
521	if (s != NULL && s->type != V_ASN1_UTCTIME)
522		return 0;
523	return ASN1_TIME_set_string_internal(s, str, V_ASN1_UTCTIME);
524}
525LCRYPTO_ALIAS(ASN1_UTCTIME_set_string);
526
527ASN1_UTCTIME *
528ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t)
529{
530	return ASN1_UTCTIME_adj(s, t, 0, 0);
531}
532LCRYPTO_ALIAS(ASN1_UTCTIME_set);
533
534ASN1_UTCTIME *
535ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t, int offset_day, long offset_sec)
536{
537	return ASN1_TIME_adj_internal(s, t, offset_day, offset_sec,
538	    V_ASN1_UTCTIME);
539}
540LCRYPTO_ALIAS(ASN1_UTCTIME_adj);
541
542int
543ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t)
544{
545	if (s->type == V_ASN1_UTCTIME)
546		return ASN1_TIME_cmp_time_t_internal(s, t, V_ASN1_UTCTIME);
547	return -2;
548}
549LCRYPTO_ALIAS(ASN1_UTCTIME_cmp_time_t);
550
551/*
552 * ASN1_GENERALIZEDTIME wrappers
553 */
554
555int
556ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *d)
557{
558	if (d->type != V_ASN1_GENERALIZEDTIME)
559		return 0;
560	return d->type == ASN1_time_parse(d->data, d->length, NULL, d->type);
561}
562LCRYPTO_ALIAS(ASN1_GENERALIZEDTIME_check);
563
564int
565ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str)
566{
567	if (s != NULL && s->type != V_ASN1_GENERALIZEDTIME)
568		return 0;
569	return ASN1_TIME_set_string_internal(s, str, V_ASN1_GENERALIZEDTIME);
570}
571LCRYPTO_ALIAS(ASN1_GENERALIZEDTIME_set_string);
572
573ASN1_GENERALIZEDTIME *
574ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s, time_t t)
575{
576	return ASN1_GENERALIZEDTIME_adj(s, t, 0, 0);
577}
578LCRYPTO_ALIAS(ASN1_GENERALIZEDTIME_set);
579
580ASN1_GENERALIZEDTIME *
581ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s, time_t t, int offset_day,
582    long offset_sec)
583{
584	return ASN1_TIME_adj_internal(s, t, offset_day, offset_sec,
585	    V_ASN1_GENERALIZEDTIME);
586}
587LCRYPTO_ALIAS(ASN1_GENERALIZEDTIME_adj);
588
589int
590ASN1_TIME_normalize(ASN1_TIME *t)
591{
592	struct tm tm;
593
594	if (t == NULL)
595		return 0;
596	if (!ASN1_TIME_to_tm(t, &tm))
597		return 0;
598	return tm_to_rfc5280_time(&tm, t);
599}
600LCRYPTO_ALIAS(ASN1_TIME_normalize);
601
602int
603ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str)
604{
605	return ASN1_TIME_set_string_internal(s, str, RFC5280);
606}
607LCRYPTO_ALIAS(ASN1_TIME_set_string_X509);
608