|
32 | 32 | #include <openssl/ssl.h> |
33 | 33 | #include <openssl/x509v3.h> |
34 | 34 |
|
| 35 | +#ifndef _WIN32 |
| 36 | +#include <dlfcn.h> |
| 37 | +#endif |
| 38 | + |
35 | 39 | using namespace digidoc; |
36 | 40 | using namespace std; |
37 | 41 |
|
@@ -166,6 +170,38 @@ unique_free_t<X509_STORE> X509CertStore::createStore(const Type &type, tm &tm) |
166 | 170 | return store; |
167 | 171 | } |
168 | 172 |
|
| 173 | +unique_free_t<X509> X509CertStore::legacyCertificate(X509 *cert) |
| 174 | +{ |
| 175 | +#ifndef _WIN32 |
| 176 | + // Fedora/RHEL can disable SHA-1 even for historical certificate and OCSP signatures. |
| 177 | + // Keep that exception in a private context, leaving the caller's policy intact. |
| 178 | + static const auto context = [] { |
| 179 | + auto ctx = make_unique_ptr<OSSL_LIB_CTX_free>(nullptr); |
| 180 | + auto *allow = reinterpret_cast<int(*)(void*, int, int)>( |
| 181 | + dlsym(RTLD_DEFAULT, "ossl_ctx_legacy_digest_signatures_allowed_set")); |
| 182 | + if(!allow) |
| 183 | + return ctx; |
| 184 | + ctx.reset(OSSL_LIB_CTX_new()); |
| 185 | + if(!ctx || OSSL_LIB_CTX_load_config(ctx.get(), nullptr) != 1 || allow(ctx.get(), 1, 0) != 1) |
| 186 | + { |
| 187 | + WARN("Failed to enable legacy certificate verification in private OpenSSL context"); |
| 188 | + ctx.reset(); |
| 189 | + } |
| 190 | + return ctx; |
| 191 | + }(); |
| 192 | + if(context) |
| 193 | + { |
| 194 | + ERR_clear_error(); |
| 195 | + auto der = i2d<i2d_X509>(cert); |
| 196 | + X509 *local = X509_new_ex(context.get(), nullptr); |
| 197 | + // d2i_X509 frees local and sets it to nullptr on failure. |
| 198 | + if(const unsigned char *data = der.data(); local && d2i_X509(&local, &data, long(der.size()))) |
| 199 | + return {local, X509_free}; |
| 200 | + } |
| 201 | +#endif |
| 202 | + return {nullptr, X509_free}; |
| 203 | +} |
| 204 | + |
169 | 205 | int X509CertStore::validate(int ok, X509_STORE_CTX *ctx) |
170 | 206 | { |
171 | 207 | switch(X509_STORE_CTX_get_error(ctx)) |
@@ -201,9 +237,21 @@ int X509CertStore::validate(int ok, X509_STORE_CTX *ctx) |
201 | 237 | if(none_of(s.certs, [&](const X509Cert &issuer) { |
202 | 238 | if(issuer == x509) // certificate is listed by service |
203 | 239 | return true; |
204 | | - if(X509_check_issued(issuer.handle(), x509) == X509_V_OK && // certificate is issued by service (function checks only issuer name) |
205 | | - X509_verify(x509, X509_get0_pubkey(issuer.handle())) == 1) // certificate is signed by service |
206 | | - return true; |
| 240 | + if(X509_check_issued(issuer.handle(), x509) == X509_V_OK) // certificate is issued by service (function checks only issuer name) |
| 241 | + { |
| 242 | + auto *key = X509_get0_pubkey(issuer.handle()); |
| 243 | + if(X509_verify(x509, key) == 1) // certificate is signed by service |
| 244 | + return true; |
| 245 | +#ifndef _WIN32 |
| 246 | + if(int digest = NID_undef; |
| 247 | + OBJ_find_sigid_algs(X509_get_signature_nid(x509), &digest, nullptr) == 1 && digest == NID_sha1) |
| 248 | + { |
| 249 | + // X509_verify uses the context of the certificate, not X509_STORE_CTX. |
| 250 | + if(auto local = legacyCertificate(x509); local && X509_verify(local.get(), key) == 1) |
| 251 | + return true; |
| 252 | + } |
| 253 | +#endif |
| 254 | + } |
207 | 255 | ERR_clear_error(); |
208 | 256 | return false; |
209 | 257 | })) // certificate is trusted by service |
|
0 commit comments