Skip to content

OpenSSL::ASN1.decode doesn't correctly parse UTCTime or GeneralizedTime with fractional seconds or a timezone #725

Open
@rhenium

Description

@rhenium

#724 reminded me of this issue.

Due to an insufficient error check with sscanf() in asn1time_to_time(), OpenSSL::ASN1.decode ignores any fractional seconds or time zone information in DER/BER.

$  ruby -ropenssl -e'p OpenSSL::ASN1.decode("\x18\x13" + "20161208193439.123Z").value'
2016-12-08 19:34:39 UTC # <- The fractional seconds is lost
$  ruby -ropenssl -e'p OpenSSL::ASN1.decode("\x18\x13" + "20161208193439+0900").value'
2016-12-08 19:34:39 UTC # <- Timezone is lost

VALUE
asn1time_to_time(const ASN1_TIME *time)
{
struct tm tm;
VALUE argv[6];
int count;
memset(&tm, 0, sizeof(struct tm));
switch (time->type) {
case V_ASN1_UTCTIME:
count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min,
&tm.tm_sec);
if (count == 5) {
tm.tm_sec = 0;
} else if (count != 6) {
ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"",
time->data);
}
if (tm.tm_year < 69) {
tm.tm_year += 2000;
} else {
tm.tm_year += 1900;
}
break;
case V_ASN1_GENERALIZEDTIME:
count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min,
&tm.tm_sec);
if (count == 5) {
tm.tm_sec = 0;
}
else if (count != 6) {
ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"",
time->data);
}
break;
default:
rb_warning("unknown time format");
return Qnil;
}
argv[0] = INT2NUM(tm.tm_year);
argv[1] = INT2NUM(tm.tm_mon);
argv[2] = INT2NUM(tm.tm_mday);
argv[3] = INT2NUM(tm.tm_hour);
argv[4] = INT2NUM(tm.tm_min);
argv[5] = INT2NUM(tm.tm_sec);
return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv);
}

# not implemented
# decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))
# decode_test B(%w{ 18 11 }) + "201612081934-0930".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:30"))
# decode_test B(%w{ 18 11 }) + "201612081934-09".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 0, "-09:00"))
# decode_test B(%w{ 18 0D }) + "2016120819.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
# decode_test B(%w{ 18 0D }) + "2016120819,5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 30, 0))
# decode_test B(%w{ 18 0F }) + "201612081934.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 30))
# decode_test B(%w{ 18 11 }) + "20161208193439.5Z".b,
# OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 39.5))
# assert_raise(OpenSSL::ASN1::ASN1Error) {
# OpenSSL::ASN1.decode(B(%w{ 18 0D }) + "201612081934Y".b)
# }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions