diff --git a/ChangeLog.d/x509write-set-validity-check.txt b/ChangeLog.d/x509write-set-validity-check.txt new file mode 100644 index 0000000000..2088ff2db0 --- /dev/null +++ b/ChangeLog.d/x509write-set-validity-check.txt @@ -0,0 +1,7 @@ +Bugfix + * mbedtls_x509write_crt_set_validity() only checked the length of the + not_before and not_after strings, so timestamps with non-digit + characters or out-of-range fields were written into the certificate + as is, and a year before 1950 was written as a UTCTime that reads + back as a year in 2000-2049. Such timestamps are now rejected with + MBEDTLS_ERR_X509_BAD_INPUT_DATA. diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index 0a7b532404..1c9f721368 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -975,7 +975,9 @@ int mbedtls_x509write_crt_set_serial_raw(mbedtls_x509write_cert *ctx, * \param not_after not_after timestamp * * \return 0 if timestamp was parsed successfully, or - * a specific error code + * #MBEDTLS_ERR_X509_BAD_INPUT_DATA if a timestamp is not + * a valid date and time in the above format, or if its + * year is before 1950. */ int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx, const char *not_before, const char *not_after); diff --git a/library/x509write_crt.c b/library/x509write_crt.c index 8c77f10c34..9d5dcb6cdf 100644 --- a/library/x509write_crt.c +++ b/library/x509write_crt.c @@ -105,12 +105,60 @@ int mbedtls_x509write_crt_set_serial_raw(mbedtls_x509write_cert *ctx, return 0; } +/* + * Check that a timestamp is a valid date and time in the format + * "YYYYMMDDhhmmss" that x509_write_time() can encode. + */ +static int x509_write_check_time(const char *t) +{ + static const unsigned char days_in_month[12] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + /* century, year in century, month, day, hour, minute, second */ + unsigned int field[7]; + unsigned int year, mon, month_days; + + if (strlen(t) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + for (size_t i = 0; i < 7; i++) { + unsigned int d1 = (unsigned int) ((unsigned char) t[2 * i] - '0'); + unsigned int d2 = (unsigned int) ((unsigned char) t[2 * i + 1] - '0'); + if (d1 > 9 || d2 > 9) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + field[i] = d1 * 10 + d2; + } + + year = field[0] * 100 + field[1]; + mon = field[2]; + + /* Years before 2050 are written as UTCTime, which cannot represent + * a year before 1950: it would be read back as 20YY (RFC 5280 + * section 4.1.2.5.1). */ + if (year < 1950 || mon < 1 || mon > 12) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + month_days = days_in_month[mon - 1]; + if (mon == 2 && year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) { + month_days = 29; + } + + if (field[3] < 1 || field[3] > month_days || + field[4] > 23 || field[5] > 59 || field[6] > 59) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + return 0; +} + int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx, const char *not_before, const char *not_after) { - if (strlen(not_before) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 || - strlen(not_after) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1) { + if (x509_write_check_time(not_before) != 0 || + x509_write_check_time(not_after) != 0) { return MBEDTLS_ERR_X509_BAD_INPUT_DATA; } strncpy(ctx->not_before, not_before, MBEDTLS_X509_RFC5280_UTC_TIME_LEN);