Bug 1286694

Summary: X.509 certificate parsing handles leap years incorrectly in multiple ways.
Product: [Fedora] Fedora Reporter: Rudolf Polzer <rpolzer>
Component: kernelAssignee: Kernel Maintainer List <kernel-maint>
Status: NEW --- QA Contact: Fedora Extras Quality Assurance <extras-qa>
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: rawhideCC: dhowells, gansalmon, itamar, jonathan, kernel-maint, madhu.chinakonda, mchehab
Target Milestone: ---   
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Rudolf Polzer 2015-11-30 14:07:26 UTC
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.

Comment 1 Rudolf Polzer 2015-11-30 14:08:14 UTC
Sorry, the second error case would be Februrary xx, 2100.

Comment 2 Rudolf Polzer 2015-11-30 14:50:37 UTC
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.

Comment 3 David Howells 2015-12-01 14:51:52 UTC
(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.

Comment 4 Rudolf Polzer 2015-12-01 14:55:43 UTC
Exactly, the reuse of year in the mktime64() is what conflicts with this change of year.