]> git.ipfire.org Git - thirdparty/openssl.git/blob - crypto/asn1/a_time.c
Add asn1_time_to_tm function and check days in month
[thirdparty/openssl.git] / crypto / asn1 / a_time.c
1 /*
2 * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the OpenSSL license (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 #include <stdio.h>
18 #include <time.h>
19 #include "internal/cryptlib.h"
20 #include <openssl/asn1t.h>
21 #include "asn1_locl.h"
22
23 IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME)
24
25 IMPLEMENT_ASN1_FUNCTIONS(ASN1_TIME)
26
27 static int leap_year(const int year)
28 {
29 if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
30 return 1;
31 return 0;
32 }
33
34 int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
35 {
36 static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
37 static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 };
38 static const int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
39 char *a;
40 int n, i, i2, l, o, min_l = 11, strict = 0, end = 6, btz = 5, md;
41 struct tm tmp;
42
43 /*
44 * ASN1_STRING_FLAG_X509_TIME is used to enforce RFC 5280
45 * time string format, in which:
46 *
47 * 1. "seconds" is a 'MUST'
48 * 2. "Zulu" timezone is a 'MUST'
49 * 3. "+|-" is not allowed to indicate a time zone
50 */
51 if (d->type == V_ASN1_UTCTIME) {
52 if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
53 min_l = 13;
54 strict = 1;
55 }
56 } else if (d->type == V_ASN1_GENERALIZEDTIME) {
57 end = 7;
58 btz = 6;
59 if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
60 min_l = 15;
61 strict = 1;
62 } else {
63 min_l = 13;
64 }
65 } else {
66 return 0;
67 }
68
69 l = d->length;
70 a = (char *)d->data;
71 o = 0;
72 memset(&tmp, 0, sizeof(tmp));
73
74 /*
75 * GENERALIZEDTIME is similar to UTCTIME except the year is represented
76 * as YYYY. This stuff treats everything as a two digit field so make
77 * first two fields 00 to 99
78 */
79
80 if (l < min_l)
81 goto err;
82 for (i = 0; i < end; i++) {
83 if (!strict && (i == btz) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) {
84 i++;
85 break;
86 }
87 if ((a[o] < '0') || (a[o] > '9'))
88 goto err;
89 n = a[o] - '0';
90 /* incomplete 2-digital number */
91 if (++o == l)
92 goto err;
93
94 if ((a[o] < '0') || (a[o] > '9'))
95 goto err;
96 n = (n * 10) + a[o] - '0';
97 /* no more bytes to read, but we haven't seen time-zone yet */
98 if (++o == l)
99 goto err;
100
101 i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i;
102
103 if ((n < min[i2]) || (n > max[i2]))
104 goto err;
105 switch (i2) {
106 case 0:
107 /* UTC will never be here */
108 tmp.tm_year = n * 100 - 1900;
109 break;
110 case 1:
111 if (d->type == V_ASN1_UTCTIME)
112 tmp.tm_year = n < 50 ? n + 100 : n;
113 else
114 tmp.tm_year += n;
115 break;
116 case 2:
117 tmp.tm_mon = n - 1;
118 break;
119 case 3:
120 /* check if tm_mday is valid in tm_mon */
121 if (tmp.tm_mon == 1) {
122 /* it's February */
123 md = mdays[1] + leap_year(tmp.tm_year + 1900);
124 } else {
125 md = mdays[tmp.tm_mon];
126 }
127 if (n > md)
128 goto err;
129 tmp.tm_mday = n;
130 break;
131 case 4:
132 tmp.tm_hour = n;
133 break;
134 case 5:
135 tmp.tm_min = n;
136 break;
137 case 6:
138 tmp.tm_sec = n;
139 break;
140 }
141 }
142
143 /*
144 * Optional fractional seconds: decimal point followed by one or more
145 * digits.
146 */
147 if (d->type == V_ASN1_GENERALIZEDTIME && a[o] == '.') {
148 if (strict)
149 /* RFC 5280 forbids fractional seconds */
150 goto err;
151 if (++o == l)
152 goto err;
153 i = o;
154 while ((o < l) && (a[o] >= '0') && (a[o] <= '9'))
155 o++;
156 /* Must have at least one digit after decimal point */
157 if (i == o)
158 goto err;
159 /* no more bytes to read, but we haven't seen time-zone yet */
160 if (o == l)
161 goto err;
162 }
163
164 /*
165 * 'o' will never point to '\0' at this point, the only chance
166 * 'o' can point to '\0' is either the subsequent if or the first
167 * else if is true.
168 */
169 if (a[o] == 'Z') {
170 o++;
171 } else if (!strict && ((a[o] == '+') || (a[o] == '-'))) {
172 int offsign = a[o] == '-' ? 1 : -1;
173 int offset = 0;
174
175 o++;
176 /*
177 * if not equal, no need to do subsequent checks
178 * since the following for-loop will add 'o' by 4
179 * and the final return statement will check if 'l'
180 * and 'o' are equal.
181 */
182 if (o + 4 != l)
183 goto err;
184 for (i = end; i < end + 2; i++) {
185 if ((a[o] < '0') || (a[o] > '9'))
186 goto err;
187 n = a[o] - '0';
188 o++;
189 if ((a[o] < '0') || (a[o] > '9'))
190 goto err;
191 n = (n * 10) + a[o] - '0';
192 i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i;
193 if ((n < min[i2]) || (n > max[i2]))
194 goto err;
195 /* if tm is NULL, no need to adjust */
196 if (tm != NULL) {
197 if (i == end)
198 offset = n * 3600;
199 else if (i == end + 1)
200 offset += n * 60;
201 }
202 o++;
203 }
204 if (offset && !OPENSSL_gmtime_adj(&tmp, 0, offset * offsign))
205 goto err;
206 } else {
207 /* not Z, or not +/- in non-strict mode */
208 goto err;
209 }
210 if (o == l) {
211 /* success, check if tm should be filled */
212 if (tm != NULL)
213 *tm = tmp;
214 return 1;
215 }
216 err:
217 return 0;
218 }
219
220 ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t)
221 {
222 return ASN1_TIME_adj(s, t, 0, 0);
223 }
224
225 ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t,
226 int offset_day, long offset_sec)
227 {
228 struct tm *ts;
229 struct tm data;
230
231 ts = OPENSSL_gmtime(&t, &data);
232 if (ts == NULL) {
233 ASN1err(ASN1_F_ASN1_TIME_ADJ, ASN1_R_ERROR_GETTING_TIME);
234 return NULL;
235 }
236 if (offset_day || offset_sec) {
237 if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
238 return NULL;
239 }
240 if ((ts->tm_year >= 50) && (ts->tm_year < 150))
241 return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec);
242 return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec);
243 }
244
245 int ASN1_TIME_check(const ASN1_TIME *t)
246 {
247 if (t->type == V_ASN1_GENERALIZEDTIME)
248 return ASN1_GENERALIZEDTIME_check(t);
249 else if (t->type == V_ASN1_UTCTIME)
250 return ASN1_UTCTIME_check(t);
251 return 0;
252 }
253
254 /* Convert an ASN1_TIME structure to GeneralizedTime */
255 ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *t,
256 ASN1_GENERALIZEDTIME **out)
257 {
258 ASN1_GENERALIZEDTIME *ret = NULL;
259 char *str;
260
261 if (!ASN1_TIME_check(t))
262 return NULL;
263
264 if (out == NULL || *out == NULL) {
265 if ((ret = ASN1_GENERALIZEDTIME_new()) == NULL)
266 goto err;
267 } else
268 ret = *out;
269
270 /* If already GeneralizedTime just copy across */
271 if (t->type == V_ASN1_GENERALIZEDTIME) {
272 if (!ASN1_STRING_set(ret, t->data, t->length))
273 goto err;
274 goto done;
275 }
276
277 /*
278 * Grow the string by two bytes.
279 * The actual allocation is t->length + 3 to include a terminator byte.
280 */
281 if (!ASN1_STRING_set(ret, NULL, t->length + 2))
282 goto err;
283 str = (char *)ret->data;
284 /* Work out the century and prepend */
285 memcpy(str, t->data[0] >= '5' ? "19" : "20", 2);
286 /*
287 * t->length + 1 is the size of the data and the allocated buffer has
288 * this much space after the first two characters.
289 */
290 OPENSSL_strlcpy(str + 2, (const char *)t->data, t->length + 1);
291
292 done:
293 if (out != NULL && *out == NULL)
294 *out = ret;
295 return ret;
296
297 err:
298 if (out == NULL || *out != ret)
299 ASN1_GENERALIZEDTIME_free(ret);
300 return NULL;
301 }
302
303 int ASN1_TIME_set_string(ASN1_TIME *s, const char *str)
304 {
305 ASN1_TIME t;
306
307 t.length = strlen(str);
308 t.data = (unsigned char *)str;
309 t.flags = 0;
310
311 t.type = V_ASN1_UTCTIME;
312
313 if (!ASN1_TIME_check(&t)) {
314 t.type = V_ASN1_GENERALIZEDTIME;
315 if (!ASN1_TIME_check(&t))
316 return 0;
317 }
318
319 if (s && !ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t))
320 return 0;
321
322 return 1;
323 }
324
325 int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str)
326 {
327 ASN1_TIME t;
328 struct tm tm;
329 int rv = 0;
330
331 t.length = strlen(str);
332 t.data = (unsigned char *)str;
333 t.flags = ASN1_STRING_FLAG_X509_TIME;
334
335 t.type = V_ASN1_UTCTIME;
336
337 if (!ASN1_TIME_check(&t)) {
338 t.type = V_ASN1_GENERALIZEDTIME;
339 if (!ASN1_TIME_check(&t))
340 goto out;
341 }
342
343 /*
344 * Per RFC 5280 (section 4.1.2.5.), the valid input time
345 * strings should be encoded with the following rules:
346 *
347 * 1. UTC: YYMMDDHHMMSSZ, if YY < 50 (20YY) --> UTC: YYMMDDHHMMSSZ
348 * 2. UTC: YYMMDDHHMMSSZ, if YY >= 50 (19YY) --> UTC: YYMMDDHHMMSSZ
349 * 3. G'd: YYYYMMDDHHMMSSZ, if YYYY >= 2050 --> G'd: YYYYMMDDHHMMSSZ
350 * 4. G'd: YYYYMMDDHHMMSSZ, if YYYY < 2050 --> UTC: YYMMDDHHMMSSZ
351 *
352 * Only strings of the 4th rule should be reformatted, but since a
353 * UTC can only present [1950, 2050), so if the given time string
354 * is less than 1950 (e.g. 19230419000000Z), we do nothing...
355 */
356
357 if (s != NULL && t.type == V_ASN1_GENERALIZEDTIME) {
358 if (!asn1_time_to_tm(&tm, &t))
359 goto out;
360 if (tm.tm_year >= 50 && tm.tm_year < 150) {
361 t.length -= 2;
362 /*
363 * it's OK to let original t.data go since that's assigned
364 * to a piece of memory allocated outside of this function.
365 * new t.data would be freed after ASN1_STRING_copy is done.
366 */
367 t.data = OPENSSL_zalloc(t.length + 1);
368 if (t.data == NULL)
369 goto out;
370 memcpy(t.data, str + 2, t.length);
371 t.type = V_ASN1_UTCTIME;
372 }
373 }
374
375 if (s == NULL || ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t))
376 rv = 1;
377
378 if (t.data != (unsigned char *)str)
379 OPENSSL_free(t.data);
380 out:
381 return rv;
382 }
383
384 int ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm)
385 {
386 if (s == NULL) {
387 time_t now_t;
388
389 time(&now_t);
390 memset(tm, 0, sizeof(*tm));
391 if (OPENSSL_gmtime(&now_t, tm))
392 return 1;
393 return 0;
394 }
395
396 return asn1_time_to_tm(tm, s);
397 }
398
399 int ASN1_TIME_diff(int *pday, int *psec,
400 const ASN1_TIME *from, const ASN1_TIME *to)
401 {
402 struct tm tm_from, tm_to;
403
404 if (!ASN1_TIME_to_tm(from, &tm_from))
405 return 0;
406 if (!ASN1_TIME_to_tm(to, &tm_to))
407 return 0;
408 return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to);
409 }
410
411 int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
412 {
413 if (tm->type == V_ASN1_UTCTIME)
414 return ASN1_UTCTIME_print(bp, tm);
415 if (tm->type == V_ASN1_GENERALIZEDTIME)
416 return ASN1_GENERALIZEDTIME_print(bp, tm);
417 BIO_write(bp, "Bad time value", 14);
418 return (0);
419 }