Bug 16231 - AD DC cannot accept PKINIT with EC (ECDSA) client certificates — Heimdal built without ECDSA support
Summary: AD DC cannot accept PKINIT with EC (ECDSA) client certificates — Heimdal buil...
Status: NEW
Alias: None
Product: Samba 4.1 and newer
Classification: Unclassified
Component: Build (show other bugs)
Version: 4.22.10
Hardware: x64 Linux
: P5 normal (vote)
Target Milestone: ---
Assignee: Samba QA Contact
QA Contact: Samba QA Contact
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-09-01 23:18 UTC by keeler
Modified: 2026-09-02 15:11 UTC (History)
0 users

See Also:


Attachments
Samba: scope HAVE_HCRYPTO_W_OPENSSL to hx509/kdc/krb5 (cause 1) (2.82 KB, patch)
2026-09-02 15:05 UTC, keeler
no flags Details
Heimdal (bundled): accept id-ecPublicKey in sig_algs[] (cause 2) (1.91 KB, patch)
2026-09-02 15:05 UTC, keeler
no flags Details
Heimdal (bundled): allow P-384/P-521 ECDH in KDC (cause 3) (1.86 KB, patch)
2026-09-02 15:06 UTC, keeler
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description keeler 2026-09-01 23:18:12 UTC
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.
Comment 1 keeler 2026-09-02 14:30:16 UTC
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.
Comment 2 keeler 2026-09-02 15:05:12 UTC
Created attachment 19191 [details]
Samba: scope HAVE_HCRYPTO_W_OPENSSL to hx509/kdc/krb5 (cause 1)
Comment 3 keeler 2026-09-02 15:05:35 UTC
Created attachment 19192 [details]
Heimdal (bundled): accept id-ecPublicKey in sig_algs[] (cause 2)
Comment 4 keeler 2026-09-02 15:06:12 UTC
Created attachment 19193 [details]
Heimdal (bundled): allow P-384/P-521 ECDH in KDC (cause 3)
Comment 5 keeler 2026-09-02 15:11:54 UTC
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.