ruby / openssl

Provides SSL, TLS and general purpose cryptography.
Other
240 stars 167 forks source link

make cert/crl/name/attr/revoked/ext/extfactory shareable when frozen #816

Open HoneyryderChuck opened 1 week ago

HoneyryderChuck commented 1 week ago

Considering we're discussing the semantics of freezing Certs/CRLs here.

rhenium commented 1 week ago

These classes need more careful review of each OpenSSL function. I haven't reviewed enough, but I can spot a few issues:

HoneyryderChuck commented 1 week ago

Added frozen check for the mentioned functions in ossl_x509cert.c.

The whole X509_NAME appears to be not written with thread safety in mind.

Indeed. Zooming out a bit, perhaps we're better off just ensuring "shareable-when-frozen" only for certificates and crls? The whole point of the other objects is to use them to modify certs/crls via the setter methods that are now frozen-checked, so there doesn't seem to be a real usage for them outside of that use-case. LMK what you think.

rhenium commented 1 week ago

Added frozen check for the mentioned functions in ossl_x509cert.c.

I have not reviewed other methods. Please check the manpage of each function and the implementation if necessary, as there are so many edge cases in OpenSSL. Not having "set" or "add" in a function name doesn't necessarily mean it is completely read-only.

Zooming out a bit, perhaps we're better off just ensuring "shareable-when-frozen" only for certificates and crls? The whole point of the other objects is to use them to modify certs/crls via the setter methods that are now frozen-checked, so there doesn't seem to be a real usage for them outside of that use-case. LMK what you think.

I don't agree it's the whole point, except for OpenSSL::X509::ExtensionFactory that is indeed intended for building a certificate. That said, Name, Attribute, Revoked, and Extension are a non-reference-counted object and we can handle separately, after Certificate, etc. are done.

HoneyryderChuck commented 1 week ago

Checked all openssl methods used in ossl_x509cert.c as well as ossl_x509crl.c , and found one method requiring frozen check (since fixed). Everything else seems to be safe (internal pointers, should not be freed by caller). Couldn't find documentation nor implementation for PEM_write_bio_X509 and i2d_X509. Will check other classes some other time.

HoneyryderChuck commented 1 week ago

@rhenium I reviewed the documentation of the remainder of openssl functions used in the scope of these attributes, and while I tweaked/removed some of the previous additions, didn't spot anything particularly dangerous about the assumptions. Implementations may hide some details, but I'm a bit out of my depth navigating openssl code to judge that. But the Name#to_der example you referred to, assuming you mean that it's unsafe due to the modified flag manipulation on encode, seems a bit concerning, as I don't see a way to work around that, and perhaps that limitation extends to other fields? This may mean that it's impossible to consider Ruby Certificate/CRLs shareable when frozen :/ .

rhenium commented 3 days ago

What's really annoying is that OpenSSL's man pages often don't go so much into details. i2d_X509_NAME() is an unsafe function that directly modifies its fields, and it takes a const pointer! There is no mention about that in the man pages. I think it can't be the only instance.

I checked about the subject and issuer name in certificates. OpenSSL::X509::Certificate#subject= and #issuer= use X509_set_*_name() which encodes the name into the canonical (sorted) form and then copy, clearing the modified flag, so OpenSSL::X509::Certificate shouldn't be affected by this, at least in regards to these two fields.

Some part of OpenSSL::X509::Certificate should be shareable, at least. OpenSSL::X509::Certificate#to_der = i2d_X509() is supposed to be thread safe even if it's not explicitly documented so. SSL_CTX can be shared between threads, and that must be implying that i2d_X509() can be called concurrently.

Please note that OpenSSL::X509::Certificate and CRL depend on OpenSSL::PKey::PKey being shareable, which is not covered by this PR. I think pkeys can be assumed to be immutable in OpenSSL >= 3.0, but some work is needed for LibreSSL and older versions of OpenSSL.

HoneyryderChuck commented 1 day ago

Looking at your last comment, I believe we could go with the following compromise:

WDYT?