summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbeck <>2022-06-29 08:56:44 +0000
committerbeck <>2022-06-29 08:56:44 +0000
commit6d7ca2b2aebbc58b83cbb3b71398deeb7acbfed6 (patch)
treeb366ef947fe73670b1c21ffc3ffba4ed862616d2 /src
parent1de11e7e8f780f3c0123b481a3ecf4d8ae835e20 (diff)
downloadopenbsd-6d7ca2b2aebbc58b83cbb3b71398deeb7acbfed6.tar.gz
openbsd-6d7ca2b2aebbc58b83cbb3b71398deeb7acbfed6.tar.bz2
openbsd-6d7ca2b2aebbc58b83cbb3b71398deeb7acbfed6.zip
Refactor asn1 time parsing to use CBS - enforce valid times in ASN.1 parsing.
While we're here enforce valid days for months and leap years. Inspired by same in boringssl. ok jsing@
Diffstat (limited to 'src')
-rw-r--r--src/lib/libcrypto/asn1/a_time_tm.c210
-rw-r--r--src/lib/libcrypto/asn1/asn1_locl.h4
-rw-r--r--src/lib/libcrypto/asn1/tasn_dec.c9
3 files changed, 155 insertions, 68 deletions
diff --git a/src/lib/libcrypto/asn1/a_time_tm.c b/src/lib/libcrypto/asn1/a_time_tm.c
index 23e2ce4b4c..9ddae82768 100644
--- a/src/lib/libcrypto/asn1/a_time_tm.c
+++ b/src/lib/libcrypto/asn1/a_time_tm.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: a_time_tm.c,v 1.21 2022/06/27 13:54:57 beck Exp $ */ 1/* $OpenBSD: a_time_tm.c,v 1.22 2022/06/29 08:56:44 beck Exp $ */
2/* 2/*
3 * Copyright (c) 2015 Bob Beck <beck@openbsd.org> 3 * Copyright (c) 2015 Bob Beck <beck@openbsd.org>
4 * 4 *
@@ -24,6 +24,7 @@
24#include <openssl/asn1t.h> 24#include <openssl/asn1t.h>
25#include <openssl/err.h> 25#include <openssl/err.h>
26 26
27#include "bytestring.h"
27#include "o_time.h" 28#include "o_time.h"
28 29
29#define RFC5280 0 30#define RFC5280 0
@@ -173,6 +174,137 @@ tm_to_rfc5280_time(struct tm *tm, ASN1_TIME *atime)
173 return (tm_to_gentime(tm, atime)); 174 return (tm_to_gentime(tm, atime));
174} 175}
175 176
177
178static int
179cbs_get_two_digit_value(CBS *cbs, int *out)
180{
181 uint8_t first_digit, second_digit;
182
183 if (!CBS_get_u8(cbs, &first_digit))
184 return 0;
185 if (!isdigit(first_digit))
186 return 0;
187 if (!CBS_get_u8(cbs, &second_digit))
188 return 0;
189 if (!isdigit(second_digit))
190 return 0;
191
192 *out = (first_digit - '0') * 10 + (second_digit - '0');
193
194 return 1;
195}
196
197static int
198is_valid_day(int year, int month, int day)
199{
200 if (day < 1)
201 return 0;
202 switch (month) {
203 case 1:
204 case 3:
205 case 5:
206 case 7:
207 case 8:
208 case 10:
209 case 12:
210 return day <= 31;
211 case 4:
212 case 6:
213 case 9:
214 case 11:
215 return day <= 30;
216 case 2:
217 if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
218 return day <= 29;
219 else
220 return day <= 28;
221 default:
222 return 0;
223 }
224}
225
226/*
227 * asn1_time_parse_cbs returns one if |cbs| is a valid DER-encoded, ASN.1 Time
228 * body within the limitations imposed by RFC 5280, or zero otherwise. The time
229 * is expected to parse as a Generalized Time if is_gentime is true, and as a
230 * UTC Time otherwise. If |out_tm| is non-NULL, |*out_tm| will be zeroed, and
231 * then set to the corresponding time in UTC. This function does not compute
232 * |out_tm->tm_wday| or |out_tm->tm_yday|. |cbs| is not consumed.
233 */
234int
235asn1_time_parse_cbs(const CBS *cbs, int is_gentime, struct tm *out_tm)
236{
237 int year, month, day, hour, min, sec, val;
238 CBS copy;
239 uint8_t tz;
240
241 CBS_dup(cbs, &copy);
242
243 if (is_gentime) {
244 if (!cbs_get_two_digit_value(&copy, &val))
245 return 0;
246 year = val * 100;
247 if (!cbs_get_two_digit_value(&copy, &val))
248 return 0;
249 year += val;
250 } else {
251 year = 1900;
252 if (!cbs_get_two_digit_value(&copy, &val))
253 return 0;
254 year += val;
255 if (year < 1950)
256 year += 100;
257 if (year >= 2050)
258 return 0; /* A Generalized time must be used. */
259 }
260
261 if (!cbs_get_two_digit_value(&copy, &month))
262 return 0;
263 if (month < 1 || month > 12)
264 return 0; /* Reject invalid months. */
265
266 if (!cbs_get_two_digit_value(&copy, &day))
267 return 0;
268 if (!is_valid_day(year, month, day))
269 return 0; /* Reject invalid days. */
270
271 if (!cbs_get_two_digit_value(&copy, &hour))
272 return 0;
273 if (hour > 23)
274 return 0; /* Reject invalid hours. */
275
276 if (!cbs_get_two_digit_value(&copy, &min))
277 return 0;
278 if (min > 59)
279 return 0; /* Reject invalid minutes. */
280
281 if (!cbs_get_two_digit_value(&copy, &sec))
282 return 0;
283 if (sec > 59)
284 return 0; /* Reject invalid seconds. Leap seconds are invalid. */
285
286 if (!CBS_get_u8(&copy, &tz))
287 return 0;
288 if ( tz != 'Z')
289 return 0; /* Reject anything but Z on the end. */
290
291 if (CBS_len(&copy) != 0)
292 return 0; /* Reject invalid lengths. */
293
294 if (out_tm != NULL) {
295 memset(out_tm, 0, sizeof(*out_tm));
296 /* Fill in the tm fields corresponding to what we validated. */
297 out_tm->tm_year = year - 1900;
298 out_tm->tm_mon = month - 1;
299 out_tm->tm_mday = day;
300 out_tm->tm_hour = hour;
301 out_tm->tm_min = min;
302 out_tm->tm_sec = sec;
303 }
304
305 return 1;
306}
307
176/* 308/*
177 * Parse an RFC 5280 format ASN.1 time string. 309 * Parse an RFC 5280 format ASN.1 time string.
178 * 310 *
@@ -188,83 +320,29 @@ tm_to_rfc5280_time(struct tm *tm, ASN1_TIME *atime)
188 * 320 *
189 * Fills in *tm with the corresponding time if tm is non NULL. 321 * Fills in *tm with the corresponding time if tm is non NULL.
190 */ 322 */
191#define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))
192int 323int
193ASN1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode) 324ASN1_time_parse(const char *bytes, size_t len, struct tm *tm, int mode)
194{ 325{
195 size_t i; 326 struct tm tml, *tmp = tm ? tm : &tml;
196 int type = 0; 327 int type = 0;
197 struct tm ltm; 328 CBS cbs;
198 struct tm *lt;
199 const char *p;
200 329
201 if (bytes == NULL) 330 if (bytes == NULL)
202 return (-1); 331 return (-1);
203 332
204 /* Constrain to valid lengths. */ 333 CBS_init(&cbs, bytes, len);
205 if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH)
206 return (-1);
207
208 lt = tm;
209 if (lt == NULL)
210 lt = &ltm;
211 memset(lt, 0, sizeof(*lt));
212 334
213 /* Timezone is required and must be GMT (Zulu). */ 335 if (CBS_len(&cbs) == UTCTIME_LENGTH)
214 if (bytes[len - 1] != 'Z') 336 type = V_ASN1_UTCTIME;
215 return (-1); 337 if (CBS_len(&cbs) == GENTIME_LENGTH)
216
217 /* Make sure everything else is digits. */
218 for (i = 0; i < len - 1; i++) {
219 if (isdigit((unsigned char)bytes[i]))
220 continue;
221 return (-1);
222 }
223
224 /*
225 * Validate and convert the time
226 */
227 p = bytes;
228 switch (len) {
229 case GENTIME_LENGTH:
230 if (mode == V_ASN1_UTCTIME)
231 return (-1);
232 lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
233 type = V_ASN1_GENERALIZEDTIME; 338 type = V_ASN1_GENERALIZEDTIME;
234 /* FALLTHROUGH */ 339 if (asn1_time_parse_cbs(&cbs, type == V_ASN1_GENERALIZEDTIME, tmp)) {
235 case UTCTIME_LENGTH: 340 if (mode != 0 && mode != type)
236 if (type == 0) { 341 return -1;
237 if (mode == V_ASN1_GENERALIZEDTIME) 342 return type;
238 return (-1);
239 type = V_ASN1_UTCTIME;
240 }
241 lt->tm_year += ATOI2(p); /* yy */
242 if (type == V_ASN1_UTCTIME) {
243 if (lt->tm_year < 50)
244 lt->tm_year += 100;
245 }
246 lt->tm_mon = ATOI2(p) - 1; /* mm */
247 if (lt->tm_mon < 0 || lt->tm_mon > 11)
248 return (-1);
249 lt->tm_mday = ATOI2(p); /* dd */
250 if (lt->tm_mday < 1 || lt->tm_mday > 31)
251 return (-1);
252 lt->tm_hour = ATOI2(p); /* HH */
253 if (lt->tm_hour < 0 || lt->tm_hour > 23)
254 return (-1);
255 lt->tm_min = ATOI2(p); /* MM */
256 if (lt->tm_min < 0 || lt->tm_min > 59)
257 return (-1);
258 lt->tm_sec = ATOI2(p); /* SS */
259 /* Leap second 60 is not accepted. Reconsider later? */
260 if (lt->tm_sec < 0 || lt->tm_sec > 59)
261 return (-1);
262 break;
263 default:
264 return (-1);
265 } 343 }
266 344
267 return (type); 345 return -1;
268} 346}
269 347
270/* 348/*
diff --git a/src/lib/libcrypto/asn1/asn1_locl.h b/src/lib/libcrypto/asn1/asn1_locl.h
index a0a1842d99..ec853250f2 100644
--- a/src/lib/libcrypto/asn1/asn1_locl.h
+++ b/src/lib/libcrypto/asn1/asn1_locl.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: asn1_locl.h,v 1.34 2022/06/27 12:36:05 tb Exp $ */ 1/* $OpenBSD: asn1_locl.h,v 1.35 2022/06/29 08:56:44 beck Exp $ */
2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3 * project 2006. 3 * project 2006.
4 */ 4 */
@@ -219,4 +219,6 @@ int i2t_ASN1_OBJECT_internal(const ASN1_OBJECT *aobj, char *buf, int buf_len,
219 int no_name); 219 int no_name);
220ASN1_OBJECT *t2i_ASN1_OBJECT_internal(const char *oid); 220ASN1_OBJECT *t2i_ASN1_OBJECT_internal(const char *oid);
221 221
222int asn1_time_parse_cbs(const CBS *cbs, int is_gentime, struct tm *out_tm);
223
222__END_HIDDEN_DECLS 224__END_HIDDEN_DECLS
diff --git a/src/lib/libcrypto/asn1/tasn_dec.c b/src/lib/libcrypto/asn1/tasn_dec.c
index 375425a9f2..235685484c 100644
--- a/src/lib/libcrypto/asn1/tasn_dec.c
+++ b/src/lib/libcrypto/asn1/tasn_dec.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: tasn_dec.c,v 1.77 2022/06/25 17:43:56 jsing Exp $ */ 1/* $OpenBSD: tasn_dec.c,v 1.78 2022/06/29 08:56:44 beck Exp $ */
2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 2/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
3 * project 2000. 3 * project 2000.
4 */ 4 */
@@ -353,6 +353,13 @@ asn1_c2i_primitive(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *
353 ASN1error(ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH); 353 ASN1error(ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH);
354 goto err; 354 goto err;
355 } 355 }
356 if (utype == V_ASN1_UTCTIME || utype == V_ASN1_GENERALIZEDTIME) {
357 if (!asn1_time_parse_cbs(content,
358 utype == V_ASN1_GENERALIZEDTIME, NULL)) {
359 ASN1error(ASN1_R_INVALID_TIME_FORMAT);
360 goto err;
361 }
362 }
356 /* All based on ASN1_STRING and handled the same way. */ 363 /* All based on ASN1_STRING and handled the same way. */
357 if (*pval == NULL) { 364 if (*pval == NULL) {
358 if ((stmp = ASN1_STRING_type_new(utype)) == NULL) { 365 if ((stmp = ASN1_STRING_type_new(utype)) == NULL) {