@ -12,15 +12,18 @@
* OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE . */
# include <openssl/mem .h>
# include <openssl/asn1 .h>
# include <openssl/bytestring.h>
# include <openssl/mem.h>
# include <assert.h>
# include <ctype.h>
# include <inttypes.h>
# include <string.h>
# include "internal.h"
# include "../asn1/ internal.h"
# include "../internal.h"
# include "internal.h"
void CBS_init ( CBS * cbs , const uint8_t * data , size_t len ) {
@ -720,3 +723,161 @@ err:
CBB_cleanup ( & cbb ) ;
return NULL ;
}
static int cbs_get_two_digits ( CBS * cbs , int * out ) {
uint8_t first_digit , second_digit ;
if ( ! CBS_get_u8 ( cbs , & first_digit ) ) {
return 0 ;
}
if ( ! isdigit ( first_digit ) ) {
return 0 ;
}
if ( ! CBS_get_u8 ( cbs , & second_digit ) ) {
return 0 ;
}
if ( ! isdigit ( second_digit ) ) {
return 0 ;
}
* out = ( first_digit - ' 0 ' ) * 10 + ( second_digit - ' 0 ' ) ;
return 1 ;
}
static int is_valid_day ( int year , int month , int day ) {
if ( day < 1 ) {
return 0 ;
}
switch ( month ) {
case 1 :
case 3 :
case 5 :
case 7 :
case 8 :
case 10 :
case 12 :
return day < = 31 ;
case 4 :
case 6 :
case 9 :
case 11 :
return day < = 30 ;
case 2 :
if ( ( year % 4 = = 0 & & year % 100 ! = 0 ) | | year % 400 = = 0 ) {
return day < = 29 ;
} else {
return day < = 28 ;
}
default :
return 0 ;
}
}
static int CBS_parse_rfc5280_time_internal ( const CBS * cbs , int is_gentime ,
int allow_timezone_offset ,
struct tm * out_tm ) {
int year , month , day , hour , min , sec , tmp ;
CBS copy = * cbs ;
uint8_t tz ;
if ( is_gentime ) {
if ( ! cbs_get_two_digits ( & copy , & tmp ) ) {
return 0 ;
}
year = tmp * 100 ;
if ( ! cbs_get_two_digits ( & copy , & tmp ) ) {
return 0 ;
}
year + = tmp ;
} else {
year = 1900 ;
if ( ! cbs_get_two_digits ( & copy , & tmp ) ) {
return 0 ;
}
year + = tmp ;
if ( year < 1950 ) {
year + = 100 ;
}
if ( year > = 2050 ) {
return 0 ; // A Generalized time must be used.
}
}
if ( ! cbs_get_two_digits ( & copy , & month ) | | month < 1 | |
month > 12 | | // Reject invalid months.
! cbs_get_two_digits ( & copy , & day ) | |
! is_valid_day ( year , month , day ) | | // Reject invalid days.
! cbs_get_two_digits ( & copy , & hour ) | |
hour > 23 | | // Reject invalid hours.
! cbs_get_two_digits ( & copy , & min ) | |
min > 59 | | // Reject invalid minutes.
! cbs_get_two_digits ( & copy , & sec ) | | sec > 59 | | ! CBS_get_u8 ( & copy , & tz ) ) {
return 0 ;
}
int offset_sign = 0 ;
switch ( tz ) {
case ' Z ' :
break ; // We correctly have 'Z' on the end as per spec.
case ' + ' :
offset_sign = 1 ;
break ; // Should not be allowed per RFC 5280.
case ' - ' :
offset_sign = - 1 ;
break ; // Should not be allowed per RFC 5280.
default :
return 0 ; // Reject anything else after the time.
}
// If allow_timezone_offset is non-zero, allow for a four digit timezone
// offset to be specified even though this is not allowed by RFC 5280. We are
// permissive of this for UTCTimes due to the unfortunate existence of
// artisinally rolled long lived certificates that were baked into places that
// are now difficult to change. These certificates were generated with the
// 'openssl' command that permissively allowed the creation of certificates
// with notBefore and notAfter times specified as strings for direct
// certificate inclusion on the command line. For context see cl/237068815.
//
// TODO(bbe): This has been expunged from public web-pki as the ecosystem has
// managed to encourage CA compliance with standards. We should find a way to
// get rid of this or make it off by default.
int offset_seconds = 0 ;
if ( offset_sign ! = 0 ) {
if ( ! allow_timezone_offset ) {
return 0 ;
}
int offset_hours , offset_minutes ;
if ( ! cbs_get_two_digits ( & copy , & offset_hours ) | |
offset_hours > 23 | | // Reject invalid hours.
! cbs_get_two_digits ( & copy , & offset_minutes ) | |
offset_minutes > 59 ) { // Reject invalid minutes.
return 0 ;
}
offset_seconds = offset_sign * ( offset_hours * 3600 + offset_minutes * 60 ) ;
}
if ( CBS_len ( & copy ) ! = 0 ) {
return 0 ; // Reject invalid lengths.
}
if ( out_tm ! = NULL ) {
// Fill in the tm fields corresponding to what we validated.
out_tm - > tm_year = year - 1900 ;
out_tm - > tm_mon = month - 1 ;
out_tm - > tm_mday = day ;
out_tm - > tm_hour = hour ;
out_tm - > tm_min = min ;
out_tm - > tm_sec = sec ;
if ( offset_seconds & & ! OPENSSL_gmtime_adj ( out_tm , 0 , offset_seconds ) ) {
return 0 ;
}
}
return 1 ;
}
int CBS_parse_generalized_time ( const CBS * cbs , struct tm * out_tm ,
int allow_timezone_offset ) {
return CBS_parse_rfc5280_time_internal ( cbs , 1 , allow_timezone_offset , out_tm ) ;
}
int CBS_parse_utc_time ( const CBS * cbs , struct tm * out_tm ,
int allow_timezone_offset ) {
return CBS_parse_rfc5280_time_internal ( cbs , 0 , allow_timezone_offset , out_tm ) ;
}