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, ©); 206 207 if (is_gentime) { 208 if (!cbs_get_two_digit_value(©, &val)) 209 return 0; 210 year = val * 100; 211 if (!cbs_get_two_digit_value(©, &val)) 212 return 0; 213 year += val; 214 } else { 215 year = 1900; 216 if (!cbs_get_two_digit_value(©, &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(©, &month)) 226 return 0; 227 if (month < 1 || month > 12) 228 return 0; /* Reject invalid months. */ 229 230 if (!cbs_get_two_digit_value(©, &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(©, &hour)) 236 return 0; 237 if (hour > 23) 238 return 0; /* Reject invalid hours. */ 239 240 if (!cbs_get_two_digit_value(©, &min)) 241 return 0; 242 if (min > 59) 243 return 0; /* Reject invalid minutes. */ 244 245 if (!cbs_get_two_digit_value(©, &sec)) 246 return 0; 247 if (sec > 59) 248 return 0; /* Reject invalid seconds. Leap seconds are invalid. */ 249 250 if (!CBS_get_u8(©, &tz)) 251 return 0; 252 if (tz != 'Z') 253 return 0; /* Reject anything but Z on the end. */ 254 255 if (CBS_len(©) != 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