Hi Samba team — first, thanks for the AD DC; standing up a small Kerberos realm for native Windows smart-card logon was otherwise not practical for us. Windows 11 interactive smart-card logon (PKINIT) against a Samba AD DC fails for any ECDSA client certificate. RSA client certificates work fine on the same DC, same config, same card. I've traced it to three distinct causes. The first is Samba's own build configuration; the other two are in the bundled Heimdal, but a Samba user hits all three, so I'm recording them together here and filing the Heimdal-specific ones upstream separately. Each one masks the next, so fixing only the first still fails. Environment - Samba 4.22.10, AD DC role (Debian 13 package `2:4.22.10+dfsg-0+deb13u2`) - Client: Windows 11 Pro (10.0.26200), OpenSC minidriver, PIV card (OpenFIPS201 applet) - Client certificate: ECDSA P-384, EKU Smart Card Logon + Client Auth, UPN SAN, signed SHA-384 by a private EC P-384 CA - KDC certificate: RSA-2048, EKU `1.3.6.1.5.2.3.5`, DNS SAN - `enable-pkinit = true`, `pkinit_principal_in_certificate = yes`, `pkinit_ms_san = true` Also reproduces on Samba 4.17.12 (Debian 12)** — this is longstanding, not a regression. --- Cause 1 — Samba builds Heimdal without ECDSA support `lib/hx509/crypto.c` and `lib/hx509/crypto-ec.c` guard every ECDSA `signature_alg` behind `#ifdef HAVE_HCRYPTO_W_OPENSSL`, and `kdc/pkinit-ec.c` guards all EC/ECDH handling the same way. Samba's build never defines it and never compiles `lib/hcrypto/evp-openssl.c`, so the shipped AD DC has **no ECDSA verifier at all**. Evidence: `libhx509-private-samba.so.0` links `libhcrypto-private-samba` rather than OpenSSL, and the DER encoding of `ecdsa-with-SHA384` (`1.2.840.10045.4.3.3`) does not appear in the binary. Symptom in the KDC log: PKINIT: Signature algorithm not supported One caution for whoever fixes this. Defining `HAVE_HCRYPTO_W_OPENSSL` *globally* also switches hcrypto's **symmetric** EVP provider to OpenSSL, which is broken in Samba's build and breaks Kerberos `ENC-TS` pre-authentication entirely — we observed 51 AS-REQs and 0 successes, with `kinit` failing for every principal, including plain password logons. We had to scope the define to the `hx509`, `kdc` and `krb5` targets and leave `hcrypto` stock. A proper fix should either make the OpenSSL EVP provider work or keep the EC code independent of the symmetric provider selection. Cause 2 — Heimdal `sig_algs[]` has no entry for `id-ecPublicKey` (upstream) This is the core interop problem, and it bites even with Cause 1 fixed. `tshark` dissection of a real Windows AS-REQ: SignerInfo.signatureAlgorithm = id-ecPublicKey (1.2.840.10045.2.1) SignerInfo.digestAlgorithm = sha384 (2.16.840.1.101.3.4.2.2) Windows/CNG puts the key algorithm OID in `SignerInfo.signatureAlgorithm`, where RFC 5652 expects the **signature** algorithm OID (`ecdsa-with-SHA384`). Heimdal's `sig_algs[]` (`lib/hx509/crypto.c`) contains only `ecdsa_with_sha{1,256,384,512}_alg`, so `_hx509_find_sig_alg()` returns NULL → `HX509_SIG_ALG_NO_SUPPORTED`: PKINIT: failed to verify signature: Failed to verify signature in CMS SignedData Filed upstream at Heimdal; noting here because Samba ships the affected code. Cause 3 — Heimdal KDC ECDH parameter parser hardcodes secp256r1 (upstream) `kdc/pkinit-ec.c`, both variants: - `get_ecdh_param_ossl30()` resolves the curve correctly via `_hx509_ossl_oid2nid()` (which supports P-256/384/521) and then discards that with a hardcoded `der_heim_oid_cmp(..., &asn1_oid_id_ec_group_secp256r1)` check. - `get_ecdh_param_ossl11()` supports **only** P-256, with no table at all. Debian's Samba compiles the **ossl11** variant (the binary references `o2i_ECPublicKey` / `EC_GROUP_get_degree`, not `EVP_PKEY_fromdata`), so patching only the ossl30 variant has no effect — worth knowing if this is tested downstream. Windows chooses its ephemeral ECDH curve **to match the logon certificate**, so a P-384 certificate produces a P-384 ECDH key and is rejected. Corollary worth confirming: **P-256 client certificates may well work on stock Samba** — the blocker is P-384 specifically, not EC in general. --- Two smaller things, happy to split into separate bugs if you prefer 1. The KDC logs the error code's catalog string, not the contextual message. Cause 3 surfaces as: Invalid message type specified for encoding …which is the generic text for `KRB5_BADMSGTYPE`, while the actual `krb5_set_error_message()` text ("PKINIT client used an unsupported curve") is never shown. That made this very hard to localise — surfacing the contextual message would be a real diagnostic improvement independent of the EC work. 2. Wiki suggestion. The "AD Smart Card Login" wiki page demonstrates RSA only. Two additions would have saved us a lot of time: - `pkinit_principal_in_certificate = no` silently disables MS UPN SAN matching and falls through to the `pki-mappings-file` DN matcher, producing `NT_STATUS_PKINIT_NAME_MISMATCH`. The diagnostic tell is that **neither** `found MS UPN SAN` **nor** `No PKINIT MS UPN SAN` is logged — the matcher never ran. For AD-style UPN mapping it must be `yes`. - A note that EC client certificates are not supported as shipped. --- Result after patching all three Kerberos: PKINIT pre-authentication succeeded -- alice@example.com using CN=alice Kerberos: PKINIT using ecdh AS-REQ SUCCESS pkinit_client_cert=CN=alice pa=PK-INIT(ietf) etype=18/18 Windows 11 smart-card logon on an ECDSA P-384 credential then works both online and offline (cached). As far as I can tell this combination hasn't worked before, which would explain why the gaps went unnoticed. Working `[kdc]` configuration, for the record: ini [kdc] enable-pkinit = true pkinit_identity = FILE:/var/lib/samba/private/kdc.crt,/var/lib/samba/private/kdc.key pkinit_anchors = FILE:/var/lib/samba/private/pkinit-anchors.pem pkinit_principal_in_certificate = yes pkinit_ms_san = true Glad to test patches, provide packet captures or full KDC logs, or rework ours into something mergeable — just let me know what's most useful.
Two cross-references, and one finding that changes where part of this should be fixed. Related: bug 15944 Bug 15944 ("PKINIT logon fails under Windows 11 with Credential Guard", ASSIGNED) is adjacent — same territory, different root cause. That one is about Windows requiring dhKeyExpiration and serverDHNonce in the PKINIT AS-REP when Credential Guard is enabled; this one is about EC client certificates being rejected outright regardless of Credential Guard. They are independent, but anyone working on Windows 11 PKINIT interop against Heimdal probably wants both in view. Our testing had Credential Guard **disabled**, so we did not hit 15944. Heimdal upstream: two of the three causes look already fixed on master I checked Heimdal master before filing the upstream issues, and it has moved on substantially via two merged PRs from January 2026: - heimdal#1289 — OpenSSL 3.x migration, RFC 8636 PKINIT algorithm agility; removes lib/hcrypto/ entirely - heimdal#1038 — hx509 ECDSA support, explicitly covering secp256r1 / secp384r1 / secp521r1 Consequences for this bug: - Cause 1 (HAVE_HCRYPTO_W_OPENSSL never defined): the mechanism disappears upstream, since lib/hcrypto is gone. Still real for Samba as it ships today. - Cause 3 (kdc/pkinit-ec.c hardcodes secp256r1): kdc/pkinit-ec.c no longer exists on master. The defect is confirmed present in heimdal-7.8.0 (~line 219), which is what gets bundled. I have asked upstream whether a 7.x fix is planned or whether downstreams should be moving to 8.0. - Cause 2 (`sig_algs[]` has no `id-ecPublicKey` entry): **appears to survive the rework** — master's `sig_algs[]` gained `ed25519`/`ed448` but still nothing keyed on the key OID, and `_hx509_find_sig_alg()` is still a plain OID lookup. Filed upstream with an explicit caveat that I tested the bundled Heimdal, not master. Upstream issues now filed: - heimdal#1421 — `sig_algs[]` has no `id-ecPublicKey` entry (cause 2). Includes a read of master showing the verify path still resolves `SignerInfo.signatureAlgorithm` through `_hx509_find_sig_alg()`, so this one looks to survive the #1289 rework. - heimdal#1422 — kdc/pkinit-ec.c hardcodes secp256r1 (cause 3), filed against 7.8.0 rather than master, asking whether a 7.x fix is planned or whether downstreams should move to 8.0. https://github.com/heimdal/heimdal/issues/1421 https://github.com/heimdal/heimdal/issues/1422 Which of these is Samba's to fix Given the above, the durable Samba-side question may be less "apply these three patches" and more **which Heimdal to carry**. Patch `0001` (scoping the build define) is genuinely Samba's. Patches `0002` and `0003` are stopgaps against the bundled tree — and since Samba tracks **lorikeet-heimdal** rather than upstream directly (as bug 15944 notes), the natural home for causes 2 and 3 is a lorikeet-heimdal update rather than local patches, if the upstream fixes land in a form that can be pulled. Happy to be redirected. If it is more useful for me to re-test against a current lorikeet-heimdal or a master build rather than the shipped 4.22.10, say so and I will — the lab reproduces this reliably.
Created attachment 19191 [details] Samba: scope HAVE_HCRYPTO_W_OPENSSL to hx509/kdc/krb5 (cause 1)
Created attachment 19192 [details] Heimdal (bundled): accept id-ecPublicKey in sig_algs[] (cause 2)
Created attachment 19193 [details] Heimdal (bundled): allow P-384/P-521 ECDH in KDC (cause 3)
Attaching the three lab patches referenced in the initial report. They apply cleanly to samba-4.22.10+dfsg (Debian 13's 2:4.22.10+dfsg-0+deb13u2) with patch -p1. Please treat these as a demonstration of the diagnosis rather than merge candidates. They're what we needed to get a working DC, not what I'd expect upstream to take as-is — specific caveats below. I'm happy to rework any of them along whatever lines you prefer. Only 0001 is Samba's own; 0002 and 0003 patch the bundled Heimdal and are filed upstream as #1421 and #1422 (see the previous comment — and note the point there about lorikeet-heimdal likely being the right home for those two). I've split them so the build fix can be considered independently. 0001 — scope HAVE_HCRYPTO_W_OPENSSL to hx509/kdc/krb5 ----------------------------------------------------- Adds -DHAVE_HCRYPTO_W_OPENSSL=1 and a crypto dependency to those three targets only, plus OpenSSL header/lib checks in wscript_configure. The scoping is the important part. Defining it globally also switches hcrypto's *symmetric* EVP provider to OpenSSL, which breaks Kerberos ENC-TS pre-authentication outright — we measured 51 AS-REQs and 0 successes, with kinit failing for every principal including plain password logons. Leaving hcrypto stock avoids that. A cleaner upstream fix would either make the OpenSSL EVP provider work or decouple the EC code from the symmetric provider selection; this patch just sidesteps it. Caveat: it makes those targets link OpenSSL unconditionally. A real fix presumably wants a configure option and a fallback for builds without OpenSSL. 0002 — accept id-ecPublicKey in sig_algs[] (bundled Heimdal) ------------------------------------------------------------ Adds ecdsa_ecpubkey_sha384_alg keyed on id-ecPublicKey and registers it, so the CMS SignerInfo that Windows actually sends can be matched. Known limitation, flagged in the code comment: the digest is hardcoded to SHA-384, which is correct only for P-384 clients. A correct fix should derive the digest from SignerInfo.digestAlgorithm. I didn't want to guess at where that logic belongs in hx509, so this is deliberately the narrow version. 0003 — remove the P-256-only ECDH restrictions (bundled Heimdal) ---------------------------------------------------------------- Drops the hardcoded secp256r1 comparison in get_ecdh_param_ossl30() and adds P-384/P-521 to get_ecdh_param_ossl11(). Worth knowing for testing: Debian builds the ossl11 variant — the binary references o2i_ECPublicKey / EC_GROUP_get_degree rather than EVP_PKEY_fromdata — so patching only the ossl30 path has no observable effect. That cost us a rebuild cycle to notice. The ossl30 side could be tidier: _hx509_ossl_oid2nid() already resolves the curve correctly just above the check, so removing the restriction is sufficient there. Result ------ With all three applied: Kerberos: PKINIT pre-authentication succeeded -- alice@example.com using CN=alice Kerberos: PKINIT using ecdh AS-REQ SUCCESS pkinit_client_cert=CN=alice pa=PK-INIT(ietf) etype=18/18 Windows 11 smart-card logon on an ECDSA P-384 credential then works both online and offline (cached, DC unreachable). Verified reproducible from a clean build. Happy to supply the pcap of the AS-REQ CMS structure, full KDC logs at level 10, or a reproduction script — just say which would help.