jstedfast / gmime

A C/C++ MIME creation and parser library with support for S/MIME, PGP, and Unix mbox spools.
GNU Lesser General Public License v2.1
113 stars 36 forks source link

GMimeSignature created and expires are time_t, fail in Y2038 on platforms with 32-bit signed time_t #68

Open dkg opened 4 years ago

dkg commented 4 years ago

g_mime_signature_{s,g}et_{created,expires} uses time_t.

They are produced from the underlying GPGME gpgme_signature_t object, which indicate that they are unsigned long values.

But time(2) from linux manpages says that time_t is 32-bit signed on some platforms:

On Linux, a call to time() with tloc specified as NULL cannot fail with the error EOVERFLOW, even on ABIs where time_t is a signed 32-bit inte‐ ger and the clock ticks past the time 2**31 (2038-01-19 03:14:08 UTC, ignoring leap seconds). (POSIX.1 permits, but does not require, the EOVERFLOW error in the case where the seconds since the Epoch will not fit in time_t.) Instead, the behavior on Linux is undefined when the system time is out of the time_t range. Applications intended to run after 2038 should use ABIs with time_t wider than 32 bits.

This means that on a platform where long is 32 bits (e.g. x86_64), even though GPGME can correctly report a signature with validity ranges between 2**31 and 2**32 seconds, or (2038-01-19 03:14:08 → 2106-02-07 06:28:16 UTC), GMime wraps those values around into the negative range.

I'm running into this when I use GMime to handle sample S/MIME certificates whose expiration dates are later than Y2038.

dkg commented 4 years ago

I'm not sure the right fix for this. From a glib perspective, i'd like to see the creation and expiration dates either returned as a GDateTime or a guint64 (microseconds since the epoch).

As glib documentation says:

GTimeVal is not year-2038-safe. Use guint64 for representing microseconds since the epoch, or use GDateTime.

The advantage of guint64 is that the caller doesn't need to worry about invoking g_date_time_free.

I suppose we could offer both mechanisms, though, and let the caller decide.

dkg commented 4 years ago

69 is a simple, conservative approach that should at least expose the correct information.

I haven't written a test for this yet, but simply running g_mime_application_pkcs7_mime_verify() against the following message would probably do it (the signing certificate expires later than Y2038):

Received: from localhost (localhost [127.0.0.1]); Tue, 26 Nov 2019
 20:11:46 -0400 (UTC-04:00)
Content-Transfer-Encoding: base64
Content-Type: application/pkcs7-mime; name="smime.p7m";
 smime-type="signed-data"
MIME-Version: 1.0
From: Alice Lovelace <alice@smime.example>
To: Bob Babbage <bob@smime.example>
Date: Tue, 26 Nov 2019 20:11:29 -0400
Subject: The FooCorp contract
Message-ID: <smime-onepart-signed@protected-headers.example>

MIIHRQYJKoZIhvcNAQcCoIIHNjCCBzICAQExDTALBglghkgBZQMEAgEwggHJBgkq
hkiG9w0BBwGgggG6BIIBtkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgY2hhcnNl
dD0idXMtYXNjaWkiDQpGcm9tOiBBbGljZSBMb3ZlbGFjZSA8YWxpY2VAc21pbWUu
ZXhhbXBsZT4NClRvOiBCb2IgQmFiYmFnZSA8Ym9iQHNtaW1lLmV4YW1wbGU+DQpE
YXRlOiBUdWUsIDI2IE5vdiAyMDE5IDIwOjExOjI5IC0wNDAwDQpTdWJqZWN0OiBU
aGUgRm9vQ29ycCBjb250cmFjdA0KTWVzc2FnZS1JRDogPHNtaW1lLW9uZXBhcnQt
c2lnbmVkQHByb3RlY3RlZC1oZWFkZXJzLmV4YW1wbGU+DQoNCkJvYiwgd2UgbmVl
ZCB0byBjYW5jZWwgdGhpcyBjb250cmFjdC4NCg0KUGxlYXNlIHN0YXJ0IHRoZSBu
ZWNlc3NhcnkgcHJvY2Vzc2VzIHRvIG1ha2UgdGhhdCBoYXBwZW4gdG9kYXkuDQoN
ClRoYW5rcywgQWxpY2UNCi0tIA0KQWxpY2UgTG92ZWxhY2UNClByZXNpZGVudA0K
T3BlblBHUCBFeGFtcGxlIENvcnANCqCCA3IwggNuMIICVqADAgECAhRngrRZc1JL
wfRxRxlq8P0RiqpMCzANBgkqhkiG9w0BAQ0FADAtMSswKQYDVQQDEyJTYW1wbGUg
TEFNUFMgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MCAXDTE5MTEyMDA2NTQxOFoYDzIw
NTIwOTI3MDY1NDE4WjAZMRcwFQYDVQQDEw5BbGljZSBMb3ZlbGFjZTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPurfll0bYkDPMkY1kNn2xXsAqHSGVF
+gWNNk3mbhF6BABhLJqDjei5aLXFE3Rq9/RRNivCMrTipF1XsbMIAKgQqr/GI1Q6
yN8lfNsK5uU3d9kw5cOyEooGpOGUrvlKMD0LPGDt6MaiJj+KJ2TR73Wd4rfRIIJo
FMmV9HZkOs+Tvcg8x6SzGhNq18X2HD10MD78eLXKm039obRD+z2JwWvGvrLbNBey
O5A+aMxmCPXRoP1xrNZWBFgKB+WGYDRXW5CXXChthTwMBXFWf4aBpurKMZAyjK2E
grQafn6h/DFddQz/NtT6Dr7UhJ2hfFFEW2rYbNsiqQAdllCb4FucWuECAwEAAaOB
lzCBlDAMBgNVHRMBAf8EAjAAMB4GA1UdEQQXMBWBE2FsaWNlQHNtaW1lLmV4YW1w
bGUwEwYDVR0lBAwwCgYIKwYBBQUHAwQwDwYDVR0PAQH/BAUDAwegADAdBgNVHQ4E
FgQUrC5UWqT9VRivLuhmRDjRJdHXAHkwHwYDVR0jBBgwFoAUt1JNc8CIPbLDeloM
85T394Cid9swDQYJKoZIhvcNAQENBQADggEBAHvqjhjPvKtVIVyleoutwa10jir3
dooJcQIILM1AunjJ6yHpuuppkc0m3BhwnlOptTKb2EnvSIkTiMY037IBlHWW217Q
cUpggEozgQm6Yb77aGptRovPi2XToEdpA8K//02I1jur1H1z8HqzVjMeHCqRaG3Z
r4C2AngGSkb6D4yZkxBX8CjtHAsUon06UxYsGYRcVykgk3Qek9qxPScSX8yai1K7
7xGcKUCLfIV/JMpv7ysPtXG7Jd62oNnp1T/3+KoP9JlLs5AiPLC13fjeYALPcHVG
UXEwdIDp1AB/Zu0a6apHQqICncqRhEB4+hompiQHtlp3TqeAWXQbQUc437sxggHZ
MIIB1QIBATBFMC0xKzApBgNVBAMTIlNhbXBsZSBMQU1QUyBDZXJ0aWZpY2F0ZSBB
dXRob3JpdHkCFGeCtFlzUkvB9HFHGWrw/RGKqkwLMAsGCWCGSAFlAwQCAaBpMBgG
CSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE5MTEyNzAw
MTEyOVowLwYJKoZIhvcNAQkEMSIEILsI9kL3zfZiVOEDjAUWrbjHjGMLoGUwEqYH
pOA9XZ+QMA0GCSqGSIb3DQEBAQUABIIBAGDat8UYN9MShlKEw3hYVVUk6HKO6Xjp
rdgCBKpoyoWJy0VJis0xHxaT2gn/+TPu8a5l6RslgeALjMyflzyzAmrqnknQQG8K
bvbt/MwpU/TxnmxT+2oP9TVmAx/IQOq4pQ35uK7peSPck2CcTvZjHTeVBWcsLVEk
hELoSD8XFRBo34qdinBzW0/sMlyK1XnlN7khKry1g7uaXcurVqptRA1rWOvCOt72
aElKG/Q7OoVgHxbUpdzV3Hqe9/UeTRDUqCs++on2pLlA0TA0Pq8RQ0hDHD/p0t41
1RAT1/RbnGQiVfRilMan+VGT4shokb1RoANy/1rOO9ZKlyWToYdRl9E=

(this example message is from an upcoming revision to draft-autocrypt-lamps-protected-headers)

jstedfast commented 4 years ago

GDateTime uses gint64 seconds from epoch. I'm not sure we need higher resolution than seconds for created/expires timestamps.

https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-new-from-unix-local

That said, I absolutely agree that I'd rather use a gint64 than a GDateTime - especially if someone can easily convert to a GDateTime if that's what they really want.

jstedfast commented 4 years ago

I added get_created64() and get_expires64() instead which does similar casting to get seconds since epoch.

I can't test this because on MacOS, time_t is 64bit which makes this a non-issue on this platform.