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::Error
to store eitherHRESULT
oru32
so that it still uses the same error type but in a lossless manner and thereby supporting error propagation seamlessly.More seriously, the
(HRESULT)(x) <= 0
in the macro exists so that ifGetLastError
actually returns anHRESULT
- as in the case ofCertFindCertificateInStore
- rather than a traditional Win32 error code, then the severity bit will be detected here as a negativelong
value 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
GetLastError
returns an inventedWIN32_ERROR
type rather than justDWORD
. So then when you ask the resultingWIN32_ERROR
to convert to anHRESULT
it naturally assumes the Win32 facility.