1 /* $OpenBSD: a_time_tm.c,v 1.8 2015/10/22 15:38:05 jsing 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 #include <ctype.h> 18 #include <limits.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <time.h> 22 23 #include <openssl/asn1t.h> 24 #include <openssl/err.h> 25 26 #include "o_time.h" 27 28 #define RFC5280 0 29 #define GENTIME_LENGTH 15 30 #define UTCTIME_LENGTH 13 31 32 int 33 asn1_tm_cmp(struct tm *tm1, struct tm *tm2) { 34 if (tm1->tm_year < tm2->tm_year) 35 return (-1); 36 if (tm1->tm_year > tm2->tm_year) 37 return (1); 38 if (tm1->tm_mon < tm2->tm_mon) 39 return (-1); 40 if (tm1->tm_mon > tm2->tm_mon) 41 return (1); 42 if (tm1->tm_mday < tm2->tm_mday) 43 return (-1); 44 if (tm1->tm_mday > tm2->tm_mday) 45 return (1); 46 if (tm1->tm_hour < tm2->tm_hour) 47 return (-1); 48 if (tm1->tm_hour > tm2->tm_hour) 49 return (1); 50 if (tm1->tm_min < tm2->tm_min) 51 return (-1); 52 if (tm1->tm_min > tm2->tm_min) 53 return (1); 54 if (tm1->tm_sec < tm2->tm_sec) 55 return (-1); 56 if (tm1->tm_sec > tm2->tm_sec) 57 return (1); 58 return 0; 59 } 60 61 /* Format a time as an RFC 5280 format Generalized time */ 62 char * 63 gentime_string_from_tm(struct tm *tm) 64 { 65 char *ret = NULL; 66 int year; 67 68 year = tm->tm_year + 1900; 69 if (year < 0 || year > 9999) 70 return (NULL); 71 72 if (asprintf(&ret, "%04u%02u%02u%02u%02u%02uZ", year, 73 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, 74 tm->tm_sec) == -1) 75 ret = NULL; 76 77 return (ret); 78 } 79 80 /* Format a time as an RFC 5280 format UTC time */ 81 char * 82 utctime_string_from_tm(struct tm *tm) 83 { 84 char *ret = NULL; 85 86 if (tm->tm_year >= 150 || tm->tm_year < 50) 87 return (NULL); 88 89 if (asprintf(&ret, "%02u%02u%02u%02u%02u%02uZ", 90 tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, 91 tm->tm_hour, tm->tm_min, tm->tm_sec) == -1) 92 ret = NULL; 93 94 return (ret); 95 } 96 97 /* Format a time correctly for an X509 object as per RFC 5280 */ 98 char * 99 rfc5280_string_from_tm(struct tm *tm) 100 { 101 char *ret = NULL; 102 int year; 103 104 year = tm->tm_year + 1900; 105 if (year < 1950 || year > 9999) 106 return (NULL); 107 108 if (year < 2050) 109 ret = utctime_string_from_tm(tm); 110 else 111 ret = gentime_string_from_tm(tm); 112 113 return (ret); 114 } 115 116 /* 117 * Parse an RFC 5280 format ASN.1 time string. 118 * 119 * mode must be: 120 * 0 if we expect to parse a time as specified in RFC 5280 from an X509 object. 121 * V_ASN1_UTCTIME if we wish to parse on RFC5280 format UTC time. 122 * V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized time. 123 * 124 * Returns: 125 * -1 if the string was invalid. 126 * V_ASN1_UTCTIME if the string validated as a UTC time string. 127 * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string. 128 * 129 * Fills in *tm with the corresponding time if tm is non NULL. 130 */ 131 #define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0')) 132 int 133 asn1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode) 134 { 135 size_t i; 136 int type = 0; 137 struct tm ltm; 138 struct tm *lt; 139 const char *p; 140 141 if (bytes == NULL) 142 return (-1); 143 144 /* Constrain to valid lengths. */ 145 if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH) 146 return (-1); 147 148 lt = tm; 149 if (lt == NULL) { 150 memset(<m, 0, sizeof(ltm)); 151 lt = <m; 152 } 153 154 /* Timezone is required and must be GMT (Zulu). */ 155 if (bytes[len - 1] != 'Z') 156 return (-1); 157 158 /* Make sure everything else is digits. */ 159 for (i = 0; i < len - 1; i++) { 160 if (isdigit((unsigned char)bytes[i])) 161 continue; 162 return (-1); 163 } 164 165 /* 166 * Validate and convert the time 167 */ 168 p = bytes; 169 switch (len) { 170 case GENTIME_LENGTH: 171 if (mode == V_ASN1_UTCTIME) 172 return (-1); 173 lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */ 174 type = V_ASN1_GENERALIZEDTIME; 175 /* FALLTHROUGH */ 176 case UTCTIME_LENGTH: 177 if (type == 0) { 178 if (mode == V_ASN1_GENERALIZEDTIME) 179 return (-1); 180 type = V_ASN1_UTCTIME; 181 } 182 lt->tm_year += ATOI2(p); /* yy */ 183 if (type == V_ASN1_UTCTIME) { 184 if (lt->tm_year < 50) 185 lt->tm_year += 100; 186 } 187 lt->tm_mon = ATOI2(p) - 1; /* mm */ 188 if (lt->tm_mon < 0 || lt->tm_mon > 11) 189 return (-1); 190 lt->tm_mday = ATOI2(p); /* dd */ 191 if (lt->tm_mday < 1 || lt->tm_mday > 31) 192 return (-1); 193 lt->tm_hour = ATOI2(p); /* HH */ 194 if (lt->tm_hour < 0 || lt->tm_hour > 23) 195 return (-1); 196 lt->tm_min = ATOI2(p); /* MM */ 197 if (lt->tm_min < 0 || lt->tm_min > 59) 198 return (-1); 199 lt->tm_sec = ATOI2(p); /* SS */ 200 /* Leap second 60 is not accepted. Reconsider later? */ 201 if (lt->tm_sec < 0 || lt->tm_sec > 59) 202 return (-1); 203 break; 204 default: 205 return (-1); 206 } 207 208 return (type); 209 } 210 211 /* 212 * ASN1_TIME generic functions. 213 */ 214 215 static int 216 ASN1_TIME_set_string_internal(ASN1_TIME *s, const char *str, int mode) 217 { 218 int type; 219 char *tmp; 220 221 if ((type = asn1_time_parse(str, strlen(str), NULL, mode)) == -1) 222 return (0); 223 if (mode != 0 && mode != type) 224 return (0); 225 226 if (s == NULL) 227 return (1); 228 229 if ((tmp = strdup(str)) == NULL) 230 return (0); 231 free(s->data); 232 s->data = tmp; 233 s->length = strlen(tmp); 234 s->type = type; 235 236 return (1); 237 } 238 239 static ASN1_TIME * 240 ASN1_TIME_adj_internal(ASN1_TIME *s, time_t t, int offset_day, long offset_sec, 241 int mode) 242 { 243 int allocated = 0; 244 struct tm tm; 245 size_t len; 246 char * p; 247 248 if (gmtime_r(&t, &tm) == NULL) 249 return (NULL); 250 251 if (offset_day || offset_sec) { 252 if (!OPENSSL_gmtime_adj(&tm, offset_day, offset_sec)) 253 return (NULL); 254 } 255 256 switch (mode) { 257 case V_ASN1_UTCTIME: 258 p = utctime_string_from_tm(&tm); 259 break; 260 case V_ASN1_GENERALIZEDTIME: 261 p = gentime_string_from_tm(&tm); 262 break; 263 case RFC5280: 264 p = rfc5280_string_from_tm(&tm); 265 break; 266 default: 267 return (NULL); 268 } 269 if (p == NULL) { 270 ASN1err(ASN1_F_ASN1_GENERALIZEDTIME_ADJ, 271 ASN1_R_ILLEGAL_TIME_VALUE); 272 return (NULL); 273 } 274 275 if (s == NULL) { 276 if ((s = ASN1_TIME_new()) == NULL) 277 return (NULL); 278 allocated = 1; 279 } 280 281 len = strlen(p); 282 switch (len) { 283 case GENTIME_LENGTH: 284 s->type = V_ASN1_GENERALIZEDTIME; 285 break; 286 case UTCTIME_LENGTH: 287 s->type = V_ASN1_UTCTIME; 288 break; 289 default: 290 if (allocated) 291 ASN1_TIME_free(s); 292 free(p); 293 return (NULL); 294 } 295 free(s->data); 296 s->data = p; 297 s->length = len; 298 return (s); 299 } 300 301 ASN1_TIME * 302 ASN1_TIME_set(ASN1_TIME *s, time_t t) 303 { 304 return (ASN1_TIME_adj(s, t, 0, 0)); 305 } 306 307 ASN1_TIME * 308 ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day, long offset_sec) 309 { 310 return (ASN1_TIME_adj_internal(s, t, offset_day, offset_sec, RFC5280)); 311 } 312 313 int 314 ASN1_TIME_check(ASN1_TIME *t) 315 { 316 if (t->type != V_ASN1_GENERALIZEDTIME && t->type != V_ASN1_UTCTIME) 317 return (0); 318 return (t->type == asn1_time_parse(t->data, t->length, NULL, t->type)); 319 } 320 321 ASN1_GENERALIZEDTIME * 322 ASN1_TIME_to_generalizedtime(ASN1_TIME *t, ASN1_GENERALIZEDTIME **out) 323 { 324 ASN1_GENERALIZEDTIME *tmp = NULL; 325 struct tm tm; 326 char *str; 327 328 if (t->type != V_ASN1_GENERALIZEDTIME && t->type != V_ASN1_UTCTIME) 329 return (NULL); 330 331 memset(&tm, 0, sizeof(tm)); 332 if (t->type != asn1_time_parse(t->data, t->length, &tm, t->type)) 333 return (NULL); 334 if ((str = gentime_string_from_tm(&tm)) == NULL) 335 return (NULL); 336 337 if (out != NULL) 338 tmp = *out; 339 if (tmp == NULL && (tmp = ASN1_GENERALIZEDTIME_new()) == NULL) { 340 free(str); 341 return (NULL); 342 } 343 if (out != NULL) 344 *out = tmp; 345 346 free(tmp->data); 347 tmp->data = str; 348 tmp->length = strlen(str); 349 return (tmp); 350 } 351 352 int 353 ASN1_TIME_set_string(ASN1_TIME *s, const char *str) 354 { 355 return (ASN1_TIME_set_string_internal(s, str, 0)); 356 } 357 358 /* 359 * ASN1_UTCTIME wrappers 360 */ 361 362 int 363 ASN1_UTCTIME_check(ASN1_UTCTIME *d) 364 { 365 if (d->type != V_ASN1_UTCTIME) 366 return (0); 367 return (d->type == asn1_time_parse(d->data, d->length, NULL, d->type)); 368 } 369 370 int 371 ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str) 372 { 373 if (s != NULL && s->type != V_ASN1_UTCTIME) 374 return (0); 375 return (ASN1_TIME_set_string_internal(s, str, V_ASN1_UTCTIME)); 376 } 377 378 ASN1_UTCTIME * 379 ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t) 380 { 381 return (ASN1_UTCTIME_adj(s, t, 0, 0)); 382 } 383 384 ASN1_UTCTIME * 385 ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t, int offset_day, long offset_sec) 386 { 387 return (ASN1_TIME_adj_internal(s, t, offset_day, offset_sec, 388 V_ASN1_UTCTIME)); 389 } 390 391 int 392 ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t2) 393 { 394 struct tm tm1, tm2; 395 396 /* 397 * This function has never handled failure conditions properly 398 * and should be deprecated. The OpenSSL version used to 399 * simply follow NULL pointers on failure. BoringSSL and 400 * OpenSSL now make it return -2 on failure. 401 * 402 * The danger is that users of this function will not 403 * differentiate the -2 failure case from t1 < t2. 404 */ 405 if (asn1_time_parse(s->data, s->length, &tm1, V_ASN1_UTCTIME) == -1) 406 return (-2); /* XXX */ 407 408 if (gmtime_r(&t2, &tm2) == NULL) 409 return (-2); /* XXX */ 410 411 return asn1_tm_cmp(&tm1, &tm2); 412 } 413 414 /* 415 * ASN1_GENERALIZEDTIME wrappers 416 */ 417 418 int 419 ASN1_GENERALIZEDTIME_check(ASN1_GENERALIZEDTIME *d) 420 { 421 if (d->type != V_ASN1_GENERALIZEDTIME) 422 return (0); 423 return (d->type == asn1_time_parse(d->data, d->length, NULL, d->type)); 424 } 425 426 int 427 ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str) 428 { 429 if (s != NULL && s->type != V_ASN1_GENERALIZEDTIME) 430 return (0); 431 return (ASN1_TIME_set_string_internal(s, str, V_ASN1_GENERALIZEDTIME)); 432 } 433 434 ASN1_GENERALIZEDTIME * 435 ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s, time_t t) 436 { 437 return (ASN1_GENERALIZEDTIME_adj(s, t, 0, 0)); 438 } 439 440 ASN1_GENERALIZEDTIME * 441 ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s, time_t t, int offset_day, 442 long offset_sec) 443 { 444 return (ASN1_TIME_adj_internal(s, t, offset_day, offset_sec, 445 V_ASN1_GENERALIZEDTIME)); 446 } 447