5cfc3ff15ec8c2535e154ab917f17d457771035c
[openssl.git] / crypto / asn1 / a_gentm.c
1 /*
2  * Copyright 1995-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  * GENERALIZEDTIME implementation. Based on UTCTIME
12  */
13
14 #include <stdio.h>
15 #include <time.h>
16 #include "internal/cryptlib.h"
17 #include <openssl/asn1.h>
18 #include "asn1_locl.h"
19
20 int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
21 {
22     static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
23     static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 };
24     char *a;
25     int n, i, l, o, min_l = 13, strict = 0;
26
27     if (d->type != V_ASN1_GENERALIZEDTIME)
28         return (0);
29     l = d->length;
30     a = (char *)d->data;
31     o = 0;
32     /*
33      * GENERALIZEDTIME is similar to UTCTIME except the year is represented
34      * as YYYY. This stuff treats everything as a two digit field so make
35      * first two fields 00 to 99
36      */
37
38     /*
39      * ASN1_STRING_FLAG_X509_TIME is used to enforce RFC 5280
40      * time string format, in which:
41      *
42      * 1. "seconds" is a 'MUST'
43      * 2. "Zulu" timezone is a 'MUST'
44      * 3. "+|-" is not allowed to indicate a time zone
45      * 4. fractional seconds are not allowed in GeneralizedTime
46      */
47
48     if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
49         min_l = 15;
50         strict = 1;
51     }
52
53     if (l < min_l)
54         goto err;
55     for (i = 0; i < 7; i++) {
56         if (!strict && (i == 6) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) {
57             i++;
58             if (tm)
59                 tm->tm_sec = 0;
60             break;
61         }
62         if ((a[o] < '0') || (a[o] > '9'))
63             goto err;
64         n = a[o] - '0';
65         /* incomplete 2-digital number */
66         if (++o == l)
67             goto err;
68
69         if ((a[o] < '0') || (a[o] > '9'))
70             goto err;
71         n = (n * 10) + a[o] - '0';
72         /* no more bytes to read, but we haven't seen time-zone yet */
73         if (++o == l)
74             goto err;
75
76         if ((n < min[i]) || (n > max[i]))
77             goto err;
78         if (tm) {
79             switch (i) {
80             case 0:
81                 tm->tm_year = n * 100 - 1900;
82                 break;
83             case 1:
84                 tm->tm_year += n;
85                 break;
86             case 2:
87                 tm->tm_mon = n - 1;
88                 break;
89             case 3:
90                 tm->tm_mday = n;
91                 break;
92             case 4:
93                 tm->tm_hour = n;
94                 break;
95             case 5:
96                 tm->tm_min = n;
97                 break;
98             case 6:
99                 tm->tm_sec = n;
100                 break;
101             }
102         }
103     }
104     /*
105      * Optional fractional seconds: decimal point followed by one or more
106      * digits.
107      */
108     if (a[o] == '.') {
109         if (strict)
110             /* RFC 5280 forbids fractional seconds */
111             goto err;
112         if (++o == l)
113             goto err;
114         i = o;
115         while ((o < l) && (a[o] >= '0') && (a[o] <= '9'))
116             o++;
117         /* Must have at least one digit after decimal point */
118         if (i == o)
119             goto err;
120         /* no more bytes to read, but we haven't seen time-zone yet */
121         if (o == l)
122             goto err;
123     }
124
125     /*
126      * 'o' will never point to '\0' at this point, the only chance
127      * 'o' can point th '\0' is either the subsequent if or the first
128      * else if is true.
129      */
130     if (a[o] == 'Z') {
131         o++;
132     } else if (!strict && ((a[o] == '+') || (a[o] == '-'))) {
133         int offsign = a[o] == '-' ? 1 : -1, offset = 0;
134         o++;
135         /*
136          * if not equal, no need to do subsequent checks
137          * since the following for-loop will add 'o' by 4
138          * and the final return statement will check if 'l'
139          * and 'o' are equal.
140          */
141         if (o + 4 != l)
142             goto err;
143         for (i = 7; i < 9; i++) {
144             if ((a[o] < '0') || (a[o] > '9'))
145                 goto err;
146             n = a[o] - '0';
147             o++;
148             if ((a[o] < '0') || (a[o] > '9'))
149                 goto err;
150             n = (n * 10) + a[o] - '0';
151             if ((n < min[i]) || (n > max[i]))
152                 goto err;
153             if (tm) {
154                 if (i == 7)
155                     offset = n * 3600;
156                 else if (i == 8)
157                     offset += n * 60;
158             }
159             o++;
160         }
161         if (offset && !OPENSSL_gmtime_adj(tm, 0, offset * offsign))
162             return 0;
163     } else if (a[o]) {
164         /* Missing time zone information. */
165         goto err;
166     }
167     return (o == l);
168  err:
169     return (0);
170 }
171
172 int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *d)
173 {
174     return asn1_generalizedtime_to_tm(NULL, d);
175 }
176
177 int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str)
178 {
179     ASN1_GENERALIZEDTIME t;
180
181     t.type = V_ASN1_GENERALIZEDTIME;
182     t.length = strlen(str);
183     t.data = (unsigned char *)str;
184     t.flags = 0;
185
186     if (ASN1_GENERALIZEDTIME_check(&t)) {
187         if (s != NULL) {
188             if (!ASN1_STRING_set((ASN1_STRING *)s, str, t.length))
189                 return 0;
190             s->type = V_ASN1_GENERALIZEDTIME;
191         }
192         return (1);
193     } else
194         return (0);
195 }
196
197 ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s,
198                                                time_t t)
199 {
200     return ASN1_GENERALIZEDTIME_adj(s, t, 0, 0);
201 }
202
203 ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s,
204                                                time_t t, int offset_day,
205                                                long offset_sec)
206 {
207     char *p;
208     struct tm *ts;
209     struct tm data;
210     const size_t len = 20;
211     ASN1_GENERALIZEDTIME *tmps = NULL;
212
213     if (s == NULL)
214         tmps = ASN1_GENERALIZEDTIME_new();
215     else
216         tmps = s;
217     if (tmps == NULL)
218         return NULL;
219
220     ts = OPENSSL_gmtime(&t, &data);
221     if (ts == NULL)
222         goto err;
223
224     if (offset_day || offset_sec) {
225         if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
226             goto err;
227     }
228
229     p = (char *)tmps->data;
230     if ((p == NULL) || ((size_t)tmps->length < len)) {
231         p = OPENSSL_malloc(len);
232         if (p == NULL) {
233             ASN1err(ASN1_F_ASN1_GENERALIZEDTIME_ADJ, ERR_R_MALLOC_FAILURE);
234             goto err;
235         }
236         OPENSSL_free(tmps->data);
237         tmps->data = (unsigned char *)p;
238     }
239
240     tmps->length = BIO_snprintf(p, len, "%04d%02d%02d%02d%02d%02dZ",
241                                 ts->tm_year + 1900, ts->tm_mon + 1,
242                                 ts->tm_mday, ts->tm_hour, ts->tm_min,
243                                 ts->tm_sec);
244     tmps->type = V_ASN1_GENERALIZEDTIME;
245 #ifdef CHARSET_EBCDIC_not
246     ebcdic2ascii(tmps->data, tmps->data, tmps->length);
247 #endif
248     return tmps;
249  err:
250     if (s == NULL)
251         ASN1_GENERALIZEDTIME_free(tmps);
252     return NULL;
253 }
254
255 const char *_asn1_mon[12] = {
256     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
257     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
258 };
259
260 int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
261 {
262     char *v;
263     int gmt = 0;
264     int i;
265     int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
266     char *f = NULL;
267     int f_len = 0;
268
269     i = tm->length;
270     v = (char *)tm->data;
271
272     if (i < 12)
273         goto err;
274     if (v[i - 1] == 'Z')
275         gmt = 1;
276     for (i = 0; i < 12; i++)
277         if ((v[i] > '9') || (v[i] < '0'))
278             goto err;
279     y = (v[0] - '0') * 1000 + (v[1] - '0') * 100
280         + (v[2] - '0') * 10 + (v[3] - '0');
281     M = (v[4] - '0') * 10 + (v[5] - '0');
282     if ((M > 12) || (M < 1))
283         goto err;
284     d = (v[6] - '0') * 10 + (v[7] - '0');
285     h = (v[8] - '0') * 10 + (v[9] - '0');
286     m = (v[10] - '0') * 10 + (v[11] - '0');
287     if (tm->length >= 14 &&
288         (v[12] >= '0') && (v[12] <= '9') &&
289         (v[13] >= '0') && (v[13] <= '9')) {
290         s = (v[12] - '0') * 10 + (v[13] - '0');
291         /* Check for fractions of seconds. */
292         if (tm->length >= 15 && v[14] == '.') {
293             int l = tm->length;
294             f = &v[14];         /* The decimal point. */
295             f_len = 1;
296             while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
297                 ++f_len;
298         }
299     }
300
301     if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
302                    _asn1_mon[M - 1], d, h, m, s, f_len, f, y,
303                    (gmt) ? " GMT" : "") <= 0)
304         return (0);
305     else
306         return (1);
307  err:
308     BIO_write(bp, "Bad time value", 14);
309     return (0);
310 }