diff options
author | beck <> | 2015-10-02 15:04:45 +0000 |
---|---|---|
committer | beck <> | 2015-10-02 15:04:45 +0000 |
commit | 61992d68f1934e7e4171e633f39fb76a4654b5a2 (patch) | |
tree | 8649498e5e9fdda4e44ebac5989504efbcc57b61 /src/lib/libcrypto/asn1/a_time_tm.c | |
parent | 5951a0298417b41fc2a1fb4ad8a057fb9530e872 (diff) | |
download | openbsd-61992d68f1934e7e4171e633f39fb76a4654b5a2.tar.gz openbsd-61992d68f1934e7e4171e633f39fb76a4654b5a2.tar.bz2 openbsd-61992d68f1934e7e4171e633f39fb76a4654b5a2.zip |
Flense the greasy black guts of unreadble string parsing code out of three areas
in asn1 and x509 code, all dealing with an ASN1_TIME. This brings the parsing
together in one function that converts into a struct tm. While we are at it this
also brings us into conformance with RFC 5280 for times allowed in an X509 cert,
as OpenSSL is very liberal with what it allows.
input and fixes from deraadt@ jsing@ guethther@ and others.
ok krw@, guenther@, jsing@
Diffstat (limited to 'src/lib/libcrypto/asn1/a_time_tm.c')
-rw-r--r-- | src/lib/libcrypto/asn1/a_time_tm.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/src/lib/libcrypto/asn1/a_time_tm.c b/src/lib/libcrypto/asn1/a_time_tm.c new file mode 100644 index 0000000000..65f75c68cc --- /dev/null +++ b/src/lib/libcrypto/asn1/a_time_tm.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* $OpenBSD: a_time_tm.c,v 1.1 2015/10/02 15:04:45 beck 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 <stdio.h> | ||
19 | #include <string.h> | ||
20 | #include <time.h> | ||
21 | #include <ctype.h> | ||
22 | #include <sys/limits.h> | ||
23 | |||
24 | #include <openssl/asn1t.h> | ||
25 | #include <openssl/err.h> | ||
26 | |||
27 | #include "o_time.h" | ||
28 | #include "asn1_locl.h" | ||
29 | |||
30 | char * | ||
31 | gentime_string_from_tm(struct tm *tm) | ||
32 | { | ||
33 | char *ret = NULL; | ||
34 | int year; | ||
35 | year = tm->tm_year + 1900; | ||
36 | if (year < 0 || year > 9999) | ||
37 | return (NULL); | ||
38 | if (asprintf(&ret, "%04u%02u%02u%02u%02u%02uZ", year, | ||
39 | tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, | ||
40 | tm->tm_sec) == -1) | ||
41 | ret = NULL; | ||
42 | return (ret); | ||
43 | } | ||
44 | |||
45 | char * | ||
46 | utctime_string_from_tm(struct tm *tm) | ||
47 | { | ||
48 | char *ret = NULL; | ||
49 | if (tm->tm_year >= 150 || tm->tm_year < 50) | ||
50 | return (NULL); | ||
51 | if (asprintf(&ret, "%02u%02u%02u%02u%02u%02uZ", | ||
52 | tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, | ||
53 | tm->tm_hour, tm->tm_min, tm->tm_sec) == -1) | ||
54 | ret = NULL; | ||
55 | return (ret); | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * Parse an ASN.1 time string. | ||
60 | * | ||
61 | * mode must be: | ||
62 | * 0 if we expect to parse a time as specified in RFC 5280 from an | ||
63 | * X509 certificate. | ||
64 | * V_ASN1_UTCTIME if we wish to parse a legacy ASN1 UTC time. | ||
65 | * V_ASN1_GENERALIZEDTIME if we wish to parse a legacy ASN1 | ||
66 | * Generalizd time. | ||
67 | * | ||
68 | * Returns: | ||
69 | * -1 if the string was invalid. | ||
70 | * V_ASN1_UTCTIME if the string validated as a UTC time string. | ||
71 | * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string. | ||
72 | * | ||
73 | * Fills in *tm with the corresponding time if tm is non NULL. | ||
74 | */ | ||
75 | #define RFC5280 0 | ||
76 | #define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0')) | ||
77 | int asn1_time_parse(const char * bytes, size_t len, struct tm *tm, int mode) | ||
78 | { | ||
79 | char *p, *buf = NULL, *dot = NULL, *tz = NULL; | ||
80 | int i, offset, noseconds = 0, type = 0; | ||
81 | struct tm ltm; | ||
82 | struct tm *lt; | ||
83 | size_t tlen; | ||
84 | char tzc; | ||
85 | |||
86 | if (bytes == NULL) | ||
87 | goto err; | ||
88 | |||
89 | if (len > INT_MAX) | ||
90 | goto err; | ||
91 | |||
92 | /* Constrain the RFC5280 case within max/min valid lengths. */ | ||
93 | if (mode == RFC5280 && (len > 15 || len < 13)) | ||
94 | goto err; | ||
95 | |||
96 | if ((buf = strndup(bytes, len)) == NULL) | ||
97 | goto err; | ||
98 | lt = tm; | ||
99 | if (lt == NULL) { | ||
100 | time_t t = time(NULL); | ||
101 | lt = gmtime_r(&t, <m); | ||
102 | if (lt == NULL) | ||
103 | goto err; | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * Find position of the optional fractional seconds, and the | ||
108 | * start of the timezone, while ensuring everything else is | ||
109 | * digits. | ||
110 | */ | ||
111 | for (i = 0; i < len; i++) { | ||
112 | char *t = buf + i; | ||
113 | if (isdigit((unsigned char)*t)) | ||
114 | continue; | ||
115 | if (*t == '.' && dot == NULL) { | ||
116 | dot = t; | ||
117 | continue; | ||
118 | } | ||
119 | if ((*t == 'Z' || *t == '+' || *t == '-') && tz == NULL) { | ||
120 | tz = t; | ||
121 | continue; | ||
122 | } | ||
123 | goto err; | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Timezone is required. For the non-RFC case it may be | ||
128 | * either Z or +- HHMM, but for RFC5280 it may be only Z. | ||
129 | */ | ||
130 | if (tz == NULL) | ||
131 | goto err; | ||
132 | tzc = *tz; | ||
133 | *tz++ = '\0'; | ||
134 | if (tzc == 'Z') { | ||
135 | if (*tz != '\0') | ||
136 | goto err; | ||
137 | offset = 0; | ||
138 | } else if (mode != RFC5280 && (tzc == '+' || tzc == '-') && | ||
139 | (strlen(tz) == 4)) { | ||
140 | int hours, mins; | ||
141 | hours = ATOI2(tz); | ||
142 | mins = ATOI2(tz); | ||
143 | if (hours > 12 || mins > 59) | ||
144 | goto err; | ||
145 | offset = hours * 3600 + mins * 60; | ||
146 | if (tzc == '-') | ||
147 | offset = -offset; | ||
148 | } else | ||
149 | goto err; | ||
150 | |||
151 | if (mode != RFC5280) { | ||
152 | /* XXX - yuck - OPENSSL_gmtime_adj should go away */ | ||
153 | if (!OPENSSL_gmtime_adj(lt, 0, offset)) | ||
154 | goto err; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * We only allow fractional seconds to be present if we are in | ||
159 | * the non-RFC case of a Generalized time. RFC 5280 forbids | ||
160 | * fractional seconds. | ||
161 | */ | ||
162 | if (dot != NULL) { | ||
163 | if (mode != V_ASN1_GENERALIZEDTIME) | ||
164 | goto err; | ||
165 | *dot++ = '\0'; | ||
166 | if (!isdigit((unsigned char)*dot)) | ||
167 | goto err; | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Validate and convert the time | ||
172 | */ | ||
173 | p = buf; | ||
174 | tlen = strlen(buf); | ||
175 | switch (tlen) { | ||
176 | case 14: | ||
177 | lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */ | ||
178 | if (mode == RFC5280 || mode == V_ASN1_GENERALIZEDTIME) | ||
179 | type = V_ASN1_GENERALIZEDTIME; | ||
180 | else | ||
181 | goto err; | ||
182 | /* FALLTHROUGH */ | ||
183 | case 12: | ||
184 | if (type == 0 && mode == V_ASN1_GENERALIZEDTIME) { | ||
185 | /* | ||
186 | * In the non-RFC case of a Generalized time | ||
187 | * seconds may not have been provided. RFC | ||
188 | * 5280 mandates that seconds must be present. | ||
189 | */ | ||
190 | noseconds = 1; | ||
191 | lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */ | ||
192 | type = V_ASN1_GENERALIZEDTIME; | ||
193 | } | ||
194 | /* FALLTHROUGH */ | ||
195 | case 10: | ||
196 | if (type == 0) { | ||
197 | /* | ||
198 | * At this point we must have a UTC time. | ||
199 | * In the RFC 5280 case it must have the | ||
200 | * seconds present. In the non-RFC case | ||
201 | * may have no seconds. | ||
202 | */ | ||
203 | if (mode == V_ASN1_GENERALIZEDTIME) | ||
204 | goto err; | ||
205 | if (tlen == 10) { | ||
206 | if (mode == V_ASN1_UTCTIME) | ||
207 | noseconds = 1; | ||
208 | else | ||
209 | goto err; | ||
210 | } | ||
211 | type = V_ASN1_UTCTIME; | ||
212 | } | ||
213 | lt->tm_year += ATOI2(p); /* yy */ | ||
214 | if (type == V_ASN1_UTCTIME) { | ||
215 | if (lt->tm_year < 50) | ||
216 | lt->tm_year += 100; | ||
217 | } | ||
218 | lt->tm_mon = ATOI2(p); /* mm */ | ||
219 | if ((lt->tm_mon > 12) || !lt->tm_mon) | ||
220 | goto err; | ||
221 | --lt->tm_mon; /* struct tm is 0 - 11 */ | ||
222 | lt->tm_mday = ATOI2(p); /* dd */ | ||
223 | if ((lt->tm_mday > 31) || !lt->tm_mday) | ||
224 | goto err; | ||
225 | lt->tm_hour = ATOI2(p); /* HH */ | ||
226 | if (lt->tm_hour > 23) | ||
227 | goto err; | ||
228 | lt->tm_min = ATOI2(p); /* MM */ | ||
229 | if (lt->tm_min > 59) | ||
230 | goto err; | ||
231 | lt->tm_sec = 0; /* SS */ | ||
232 | if (noseconds) | ||
233 | break; | ||
234 | lt->tm_sec = ATOI2(p); | ||
235 | /* Leap second 60 is not accepted. Reconsider later? */ | ||
236 | if (lt->tm_sec > 59) | ||
237 | goto err; | ||
238 | break; | ||
239 | default: | ||
240 | goto err; | ||
241 | } | ||
242 | |||
243 | /* RFC 5280 section 4.1.2.5 */ | ||
244 | if (mode == RFC5280 && lt->tm_year < 150 && | ||
245 | type != V_ASN1_UTCTIME) | ||
246 | goto err; | ||
247 | if (mode == RFC5280 && lt->tm_year >= 150 && | ||
248 | type != V_ASN1_GENERALIZEDTIME) | ||
249 | goto err; | ||
250 | |||
251 | free(buf); | ||
252 | return type; | ||
253 | |||
254 | err: | ||
255 | free(buf); | ||
256 | return -1; | ||
257 | } | ||