Description of problem: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/crypto/asymmetric_keys/x509_cert_parser.c?id=cc25b994acfbc901429da682d0f73c190e960206#n499 Decoding a date fails in the following ways: - Feeding February xx, 2000 in an ASN1_UNITIM will actually return a date in the year 20 (i.e. anno domini twenty, not 1920/2020). - Feeding February xx, 2001 in an ASN1_GENTIM will actually return a date in the year 21 (i.e. anno domini twenty-one, not 1921/2021). This comes from this division by 100: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/crypto/asymmetric_keys/x509_cert_parser.c?id=cc25b994acfbc901429da682d0f73c190e960206#n549 which should rather be written as: if ((year / 100) % 4 != 0) (BTW: I suppose here the intention of using a division by 100 instead of modulo 400 is to use both quotient and remainder from one DIV instruction) If nothing else, this bug allows for two different ASN.1 representations of the same date (in the years 20/21), which violates the canonicality required by ASN.1 CER and thus MIGHT be exploitable in some protocols. How reproducible: Did not try. Found this when reading source. I suppose the way to reproduce this is creating a SSL cert dated sometime February 2000, changing your system clock back, and trying to verify it. using the kernel X.509 parser. Such a certificate should be accepted then, but will be rejected.
Sorry, the second error case would be Februrary xx, 2100.
There also seems to be another related issue: Feb 29, 2017 is not rejected but handled as Mar 1, 2017. The reason is incorrect leap year logic: mon_len starts at 29: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/crypto/asymmetric_keys/x509_cert_parser.c?id=cc25b994acfbc901429da682d0f73c190e960206#n503 in 0 mod 4 years, it's set to 29: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/crypto/asymmetric_keys/x509_cert_parser.c?id=cc25b994acfbc901429da682d0f73c190e960206#n547 in 0 mod 100 but not 0 mod 400 years, it's set to 28: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/crypto/asymmetric_keys/x509_cert_parser.c?id=cc25b994acfbc901429da682d0f73c190e960206#n551 Probably the initialization should be changed to 28. Keeping the rest of the code as is, this will do: not 0 mod 4: 28 0 mod 100 but not 0 mod 400: 28 otherwise: 29 which is the correct rule.
(In reply to Rudolf Polzer from comment #0) > if ((year / 100) % 4 != 0) This is the same as: year /= 100; if (year % 4 != 0) provided one doesn't use year again - which I am, which I think is the actual problem there.
Exactly, the reuse of year in the mktime64() is what conflicts with this change of year.