From 07c0e0fdcf6ccea2a60b2acc7059f56a834ad040 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 14 Jul 2017 10:40:49 +0200 Subject: QAsn1Element - fix toDateTime function ASN UTCTime uses two characters to encode a year (YY). When converting it into QDate, it's quite naive to just add 2000. According to RFC 2459, these YY represent dates in the range [1950, 2049]. This patch also introduces a helper function doing the checked conversion from a string to int (to be reused in the following-up patches). Task-number: QTBUG-61934 Change-Id: I3f6f471d24e8357b83b2f5973023b2b842751389 Reviewed-by: Edward Welbourne Reviewed-by: Timur Pocheptsov --- src/network/ssl/qasn1element.cpp | 34 ++++++++++++++++++++-- .../network/ssl/qasn1element/tst_qasn1element.cpp | 27 +++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp index dc59c41d59..6558643386 100644 --- a/src/network/ssl/qasn1element.cpp +++ b/src/network/ssl/qasn1element.cpp @@ -45,6 +45,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE typedef QMap OidNameMap; @@ -82,6 +84,27 @@ static OidNameMap createOidMap() } Q_GLOBAL_STATIC_WITH_ARGS(OidNameMap, oidNameMap, (createOidMap())) +static bool stringToNonNegativeInt(const QByteArray &asnString, int *val) +{ + // Helper function for toDateTime(), which handles chunking of the original + // string into smaller sub-components, so we expect the whole 'asnString' to + // be a valid non-negative number. + Q_ASSERT(val); + + // We want the C locale, as used by QByteArray; however, no leading sign is + // allowed (which QByteArray would accept), so we have to check the data: + const std::locale localeC; + for (char v : asnString) { + if (!std::isdigit(v, localeC)) + return false; + } + + bool ok = false; + *val = asnString.toInt(&ok); + Q_ASSERT(ok && *val >= 0); + return true; +} + QAsn1Element::QAsn1Element(quint8 type, const QByteArray &value) : mType(type) , mValue(value) @@ -231,15 +254,19 @@ bool QAsn1Element::toBool(bool *ok) const QDateTime QAsn1Element::toDateTime() const { if (mValue.endsWith('Z')) { - if (mType == UtcTimeType && mValue.size() == 13) - return QDateTime(QDate(2000 + mValue.mid(0, 2).toInt(), + if (mType == UtcTimeType && mValue.size() == 13) { + int year = 0; + if (!stringToNonNegativeInt(mValue.mid(0, 2), &year)) + return QDateTime(); + // RFC 2459: YY represents a year in the range [1950, 2049] + return QDateTime(QDate(year < 50 ? 2000 + year : 1900 + year, mValue.mid(2, 2).toInt(), mValue.mid(4, 2).toInt()), QTime(mValue.mid(6, 2).toInt(), mValue.mid(8, 2).toInt(), mValue.mid(10, 2).toInt()), Qt::UTC); - else if (mType == GeneralizedTimeType && mValue.size() == 15) + } else if (mType == GeneralizedTimeType && mValue.size() == 15) { return QDateTime(QDate(mValue.mid(0, 4).toInt(), mValue.mid(4, 2).toInt(), mValue.mid(6, 2).toInt()), @@ -247,6 +274,7 @@ QDateTime QAsn1Element::toDateTime() const mValue.mid(10, 2).toInt(), mValue.mid(12, 2).toInt()), Qt::UTC); + } } return QDateTime(); } diff --git a/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp b/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp index 401ed85587..0928ecc5a1 100644 --- a/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp +++ b/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp @@ -134,6 +134,33 @@ void tst_QAsn1Element::dateTime_data() QTest::newRow("UTCTime - no trailing Z") << QByteArray::fromHex("170d30373034313730373430323659") << QDateTime(); + QTest::newRow("UTCTime - year 1950") + << QByteArray::fromHex("170d3530313232343035353530305a") + << QDateTime(QDate(1950, 12, 24), QTime(5, 55), Qt::UTC); + QTest::newRow("UTCTime - year 1999") + << QByteArray::fromHex("170d3939313232343035353530305a") + << QDateTime(QDate(1999, 12, 24), QTime(5, 55), Qt::UTC); + QTest::newRow("UTCTime - year 2000") + << QByteArray::fromHex("170d3030313232343035353530305a") + << QDateTime(QDate(2000, 12, 24), QTime(5, 55), Qt::UTC); + QTest::newRow("UTCTime - year 2049") + << QByteArray::fromHex("170d3439313232343035353530305a") + << QDateTime(QDate(2049, 12, 24), QTime(5, 55), Qt::UTC); + QTest::newRow("UTCTime - invalid year ('-9')") + << QByteArray::fromHex("170d2d39313232343035353530305a") + << QDateTime(); + QTest::newRow("UTCTime - invalid year ('*9')") + << QByteArray::fromHex("170d2a39313232343035353530305a") + << QDateTime(); + QTest::newRow("UTCTime - invalid year ('5*')") + << QByteArray::fromHex("170d352a313232343035353530305a") + << QDateTime(); + QTest::newRow("UTCTime - invalid year ('AB')") + << QByteArray::fromHex("170d4142313232343035353530305a") + << QDateTime(); + QTest::newRow("UTCTime - invalid year ('+1')") + << QByteArray::fromHex("170d2b31313232343035353530305a") + << QDateTime(); QTest::newRow("GeneralizedTime - 20510829095341Z") << QByteArray::fromHex("180f32303531303832393039353334315a") << QDateTime(QDate(2051, 8, 29), QTime(9, 53, 41), Qt::UTC); -- cgit v1.2.3