Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ jobs:
java-version: "21"

- name: Set up Android SDK
# Third-party action pinned to a full commit SHA (v3 tag head) per the
# repo's policy for non-official actions; a tag could be re-pointed.
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3
# Third-party action pinned to a full commit SHA (v4.0.1) per the repo's
# policy for non-official actions; a tag could be re-pointed. v4 runs on
# Node.js 24 (v3 targeted the now-deprecated Node.js 20).
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1

- name: Install Android NDK, platform, and build-tools
run: |
Expand Down
15 changes: 12 additions & 3 deletions apps/geolibre-desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ tauri-plugin-persisted-scope = "2"
duckdb = { version = "1.10504.0", features = ["bundled", "parquet"], optional = true }
chrono = { version = "0.4", default-features = false, features = ["std"] }
# rustls-tls stays the default backend for every request. rustls-tls-native-roots
# also trusts the OS/system certificate store (enterprise CAs), and native-tls is
# used only on the opt-in mTLS path for PKCS#12 client certificates (issue #1220).
# also trusts the OS/system certificate store (enterprise CAs). native-tls (which
# drags in openssl-sys) is used only on the opt-in mTLS path for PKCS#12 client
# certificates (issue #1220), so it is added per-target below — everywhere except
# Android, where there is no such path and openssl-sys cannot cross-compile for
# the NDK.
reqwest = { version = "0.12", default-features = false, features = [
"blocking",
"rustls-tls",
"rustls-tls-native-roots",
"native-tls",
] }
flate2 = "1"
tar = "0.4"
Expand All @@ -57,6 +59,13 @@ serde_json = "1"
# OS CSPRNG for the per-launch Jupyter token (already an indirect dep).
getrandom = "0.4"

# native-tls backs only the desktop mTLS PKCS#12 path (issue #1220). It pulls in
# openssl-sys, which has no vendored/cross build for the Android NDK, so the
# feature is added on every target except Android (the code that uses it is
# likewise cfg'd out in src/lib.rs).
[target.'cfg(not(target_os = "android"))'.dependencies]
reqwest = { version = "0.12", default-features = false, features = ["native-tls"] }

# Runtime WebKitGTK version check (already an indirect dep via tauri/wry).
[target."cfg(target_os = \"linux\")".dependencies]
webkit2gtk-sys = "2"
35 changes: 26 additions & 9 deletions apps/geolibre-desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,9 @@ impl reqwest::dns::Resolve for GuardedDnsResolver {
enum ClientIdentity {
/// PEM identity (certificate chain plus an unencrypted PKCS#8 key), rustls.
Pem(reqwest::Identity),
/// PKCS#12 identity, native-tls.
/// PKCS#12 identity, native-tls. Not available on Android, which does not
/// ship the native-tls backend (openssl-sys has no NDK cross build).
#[cfg(not(target_os = "android"))]
Pkcs12(reqwest::Identity),
}

Expand Down Expand Up @@ -811,14 +813,27 @@ fn client_identity() -> Result<Option<ClientIdentity>, String> {
)
})?;
if client_cert_is_pkcs12(&path, password.is_some()) {
let identity = reqwest::Identity::from_pkcs12_der(&bytes, password.as_deref().unwrap_or(""))
.map_err(|error| {
format!(
"Could not load PKCS#12 client certificate {}: {error}",
path.display()
)
})?;
Ok(Some(ClientIdentity::Pkcs12(identity)))
#[cfg(not(target_os = "android"))]
{
let identity =
reqwest::Identity::from_pkcs12_der(&bytes, password.as_deref().unwrap_or(""))
.map_err(|error| {
format!(
"Could not load PKCS#12 client certificate {}: {error}",
path.display()
)
})?;
Ok(Some(ClientIdentity::Pkcs12(identity)))
}
// Android lacks the native-tls backend that parses PKCS#12, so surface a
// clear error rather than silently misreading the bundle as PEM.
#[cfg(target_os = "android")]
{
Err(format!(
"PKCS#12 client certificates are not supported on this platform: {}",
path.display()
))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, clear failure mode instead of silently misreading the bundle. One follow-up: this is a genuinely new platform limitation (PKCS#12 client certs work on desktop, not on Android), but neither docs/android.md's "Known limitations / follow-ups" section nor the mTLS env-var table in docs/architecture.md (lines 120-129) mentions it. Worth a one-line addition to docs/android.md so this doesn't surprise someone configuring GEOLIBRE_HTTP_CLIENT_CERT on an Android build later.

Confidence: medium.

}
} else {
let identity = reqwest::Identity::from_pem(&bytes).map_err(|error| {
format!(
Expand Down Expand Up @@ -867,6 +882,8 @@ fn build_guarded_http_client() -> Result<reqwest::blocking::Client, String> {
builder = match client_identity()? {
// PKCS#12 identities are only understood by native-tls, which also reads
// the OS trust store on every platform; switch this one client over.
// (Not reachable on Android — client_identity errors out there.)
#[cfg(not(target_os = "android"))]
Some(ClientIdentity::Pkcs12(identity)) => builder.use_native_tls().identity(identity),
Some(ClientIdentity::Pem(identity)) => builder.use_rustls_tls().identity(identity),
None => builder.use_rustls_tls(),
Expand Down
Loading