@@ -4293,6 +4293,14 @@ static int wolfssl_utctime_year(const unsigned char* str, int len, int* year)
42934293 ret = 0 ;
42944294 }
42954295
4296+ if (ret == 1 ) {
4297+ if ((str [0 ] < '0' ) || (str [0 ] > '9' ) ||
4298+ (str [1 ] < '0' ) || (str [1 ] > '9' )) {
4299+ WOLFSSL_MSG ("Invalid characters in UTC year." );
4300+ ret = 0 ;
4301+ }
4302+ }
4303+
42964304 if (ret == 1 ) {
42974305 int tm_year ;
42984306 /* 2-digit year. */
@@ -4334,6 +4342,16 @@ static int wolfssl_gentime_year(const unsigned char* str, int len, int* year)
43344342 ret = 0 ;
43354343 }
43364344
4345+ if (ret == 1 ) {
4346+ if ((str [0 ] < '0' ) || (str [0 ] > '9' ) ||
4347+ (str [1 ] < '0' ) || (str [1 ] > '9' ) ||
4348+ (str [2 ] < '0' ) || (str [2 ] > '9' ) ||
4349+ (str [3 ] < '0' ) || (str [3 ] > '9' )) {
4350+ WOLFSSL_MSG ("Invalid characters in generalized year." );
4351+ ret = 0 ;
4352+ }
4353+ }
4354+
43374355 if (ret == 1 ) {
43384356 int tm_year ;
43394357 /* 4-digit year. */
@@ -4363,11 +4381,12 @@ static int wolfssl_asn1_time_to_tm(const WOLFSSL_ASN1_TIME* asnTime,
43634381 const unsigned char * asn1TimeBuf ;
43644382 int asn1TimeBufLen ;
43654383 int i = 0 ;
4366- #ifdef XMKTIME
4384+ /* Parse into a local struct so the caller's tm is only written on
4385+ * success. Avoids leaving a partially-populated struct behind when
4386+ * the input fails validation. */
43674387 struct tm localTm ;
43684388
4369- XMEMSET (& localTm , 0 , sizeof localTm );
4370- #endif
4389+ XMEMSET (& localTm , 0 , sizeof (localTm ));
43714390
43724391 /* Get the string buffer - fixed array, can't fail. */
43734392 asn1TimeBuf = wolfSSL_ASN1_TIME_get_data (asnTime );
@@ -4378,15 +4397,12 @@ static int wolfssl_asn1_time_to_tm(const WOLFSSL_ASN1_TIME* asnTime,
43784397 ret = 0 ;
43794398 }
43804399 if (ret == 1 ) {
4381- /* Zero out values in broken-down time. */
4382- XMEMSET (tm , 0 , sizeof (struct tm ));
4383-
43844400 if (asnTime -> type == WOLFSSL_V_ASN1_UTCTIME ) {
43854401 /* Get year from UTC TIME string. */
43864402 int tm_year ;
43874403 if ((ret = wolfssl_utctime_year (asn1TimeBuf , asn1TimeBufLen ,
43884404 & tm_year )) == 1 ) {
4389- tm -> tm_year = tm_year ;
4405+ localTm . tm_year = tm_year ;
43904406 /* Month starts after year - 2 characters. */
43914407 i = 2 ;
43924408 }
@@ -4396,7 +4412,7 @@ static int wolfssl_asn1_time_to_tm(const WOLFSSL_ASN1_TIME* asnTime,
43964412 int tm_year ;
43974413 if ((ret = wolfssl_gentime_year (asn1TimeBuf , asn1TimeBufLen ,
43984414 & tm_year )) == 1 ) {
4399- tm -> tm_year = tm_year ;
4415+ localTm . tm_year = tm_year ;
44004416 /* Month starts after year - 4 characters. */
44014417 i = 4 ;
44024418 }
@@ -4406,26 +4422,51 @@ static int wolfssl_asn1_time_to_tm(const WOLFSSL_ASN1_TIME* asnTime,
44064422 WOLFSSL_MSG ("asnTime->type is invalid." );
44074423 ret = 0 ;
44084424 }
4409- }
4410- if (ret == 1 ) {
4425+ }
4426+
4427+ if (ret == 1 ) {
4428+ int j ;
4429+ /* Validate 10 digits: MMDDHHMMSS. Length was already checked
4430+ * (>= UTCTIME_LEN or >= GENTIME_LEN), so i+10 is in range. */
4431+ for (j = i ; j < i + 10 ; j ++ ) {
4432+ if (asn1TimeBuf [j ] < '0' || asn1TimeBuf [j ] > '9' ) {
4433+ WOLFSSL_MSG ("Non-digit in ASN.1 TIME." );
4434+ ret = 0 ;
4435+ break ;
4436+ }
4437+ }
4438+ }
4439+
4440+ if (ret == 1 ) {
44114441 /* Fill in rest of broken-down time from string. */
44124442 /* January is 0 not 1 */
4413- tm -> tm_mon = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4414- tm -> tm_mon += (asn1TimeBuf [i ] - '0' ) - 1 ; i ++ ;
4415- tm -> tm_mday = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4416- tm -> tm_mday += (asn1TimeBuf [i ] - '0' ); i ++ ;
4417- tm -> tm_hour = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4418- tm -> tm_hour += (asn1TimeBuf [i ] - '0' ); i ++ ;
4419- tm -> tm_min = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4420- tm -> tm_min += (asn1TimeBuf [i ] - '0' ); i ++ ;
4421- tm -> tm_sec = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4422- tm -> tm_sec += (asn1TimeBuf [i ] - '0' );
4443+ localTm .tm_mon = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4444+ localTm .tm_mon += (asn1TimeBuf [i ] - '0' ) - 1 ; i ++ ;
4445+ localTm .tm_mday = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4446+ localTm .tm_mday += (asn1TimeBuf [i ] - '0' ); i ++ ;
4447+ localTm .tm_hour = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4448+ localTm .tm_hour += (asn1TimeBuf [i ] - '0' ); i ++ ;
4449+ localTm .tm_min = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4450+ localTm .tm_min += (asn1TimeBuf [i ] - '0' ); i ++ ;
4451+ localTm .tm_sec = (asn1TimeBuf [i ] - '0' ) * 10 ; i ++ ;
4452+ localTm .tm_sec += (asn1TimeBuf [i ] - '0' );
4453+ }
44234454
4455+ if (ret == 1 ) {
4456+ /* Range-check broken-down fields. ValidateGmtime returns 0 on
4457+ * success. */
4458+ if (ValidateGmtime (& localTm )) {
4459+ WOLFSSL_MSG ("Out-of-range field in ASN.1 TIME." );
4460+ ret = 0 ;
4461+ }
4462+ }
4463+
4464+ if (ret == 1 ) {
4465+ /* Publish to caller. */
4466+ XMEMCPY (tm , & localTm , sizeof (* tm ));
44244467 #ifdef XMKTIME
4425- XMEMCPY (& localTm , tm , sizeof (struct tm ));
4426- /* Call XMKTIME on tm to get tm_wday and tm_yday fields populated.
4427- Note that localTm is used here to avoid modifying other fields,
4428- such as tm_isdst/tm_gmtoff. */
4468+ /* XMKTIME may set tm_isdst/tm_gmtoff on localTm; call after the
4469+ * copy so those fields stay zero in the caller's tm. */
44294470 XMKTIME (& localTm );
44304471 tm -> tm_wday = localTm .tm_wday ;
44314472 tm -> tm_yday = localTm .tm_yday ;
0 commit comments