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(&ltm, 0, sizeof(ltm));
151 		lt = &ltm;
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