windows-rs: Wrong HRESULT error code if the call was made from a different facility
Summary
The GetLastError calls internally the HRESULT::from_win32 which set the facility bits of the HRESULT to 7 aka win32.
However the facility is not always win32.
In this example I call the GetLastError however the same holds true for functions that already return the windows::core::Result like Cryptography::CryptAcquireCertificatePrivateKey
I provided a simple example that should look for a fingerprint in the CurrentUser/Personal Cert store if the fingerprint exists.
The is maybe not the simplest version of the bug but hopefully verbose enough.
Easy solutions would be a way to specify the facility on the GetLastError call or provide different windows::core::Error::from_ implementations for each facility. E.g
windows::core::Error::from_sspi
Crate manifest
[package]
name = "hresult_example"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hex = "0.4"
windows = { version = "0.51", features = [
"Win32_Security_Cryptography",
"Win32_Foundation",
] }
Crate code
use hex::FromHex;
use windows::Win32::Security::Cryptography;
// looks for a fingerprint in CurrentUser\My (Personal)
fn main() {
let path_as_wstr = "My"
.encode_utf16()
.chain(Some(0))
.collect::<Vec<_>>();
let dwflags = Cryptography::CERT_OPEN_STORE_FLAGS(Cryptography::CERT_SYSTEM_STORE_CURRENT_USER_ID << Cryptography::CERT_SYSTEM_STORE_LOCATION_SHIFT);
let handle = unsafe {
Cryptography::CertOpenStore(
Cryptography::CERT_STORE_PROV_SYSTEM_W,
Cryptography::CERT_QUERY_ENCODING_TYPE::default(),
Cryptography::HCRYPTPROV_LEGACY::default(),
dwflags,
Some(path_as_wstr.as_ptr() as _),
)
}.unwrap();
let hash = <[u8; 20]>::from_hex(&"ABF777B90E01AFFDC21BCCA4BB98D7D884DF702E").unwrap();
let fingerprint_blob = Cryptography::CRYPT_INTEGER_BLOB {
cbData: hash.len() as _,
pbData: hash.as_ptr() as _,
};
let cert_context = unsafe {
Cryptography::CertFindCertificateInStore(
handle,
Cryptography::X509_ASN_ENCODING,
0,
Cryptography::CERT_FIND_HASH,
Some(&fingerprint_blob as *const _ as _),
None,
)
};
assert_eq!(cert_context.is_null(), true, "the fingerprint should not be in the store");
// wrong facility as the error is from SSPI not win32
// let error = windows::core::Error::from_win32();
// this is the same as it internally calls the from_win32() function
let error = unsafe { windows::Win32::Foundation::GetLastError() }.unwrap_err();
match error {
// HACK: HRESULT conversion is wrong for crypto error codes
// HACK: Add 2 to the facility
e if e.code().0 + 0x0002_0000
== windows::Win32::Foundation::CRYPT_E_NOT_FOUND.0 =>
{
println!("error codes match");
}
_ => {
eprintln!("error codes did not match");
},
};
}
About this issue
- Original URL
- State: closed
- Created 10 months ago
- Comments: 21
We can potentially improve the situation for option 2 by updating
windows::core::Errorto store eitherHRESULToru32so that it still uses the same error type but in a lossless manner and thereby supporting error propagation seamlessly.More seriously, the
(HRESULT)(x) <= 0in the macro exists so that ifGetLastErroractually returns anHRESULT- as in the case ofCertFindCertificateInStore- rather than a traditional Win32 error code, then the severity bit will be detected here as a negativelongvalue and not reencoded. We should probably just do the same careful dance and call it a day.I’m starting to think “returns whatever” is a fair description of
GetLastError😆Oh that’s just a tag for internal housekeeping. This is not strictly a crate bug but rather an external one.
I think we have all the info we need on this, it’s just going to require a bit of work upstream in the metadata project. We’ll ping this thread when there’s more to report!
Right, the problem is again that the Win32 metadata assumes
GetLastErrorreturns an inventedWIN32_ERRORtype rather than justDWORD. So then when you ask the resultingWIN32_ERRORto convert to anHRESULTit naturally assumes the Win32 facility.