Support for multiple pkinit identities

Ken Hornstein kenh at
Thu Jan 7 21:55:28 EST 2021

So, in the continual quest to move from our broke-assed ancient PKINIT
plugin to the MIT Kerberos pkinit plugin ...

One of our developers noticed that the MIT pkinit plugin does not
support multiple pkinit_identities lines, at least not in the way our
plugin does.  Specifically, if you have multiple PKCS11 modules listed,
it will only try the first one (we list a bunch of possible PKCS11
modules in our distributed krb5.conf, as there are a bunch of PKCS11
implementations out there; this hasn't been a problem).  According to
the official MIT documentation, you should be able to have multiple
pkinit_identities lines and the first one that finds identity information
should be tried.

It seems like the core issue is in pkinit_identity_initialize, specifically
in the loop here:

            for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) {
                retval = process_option_identity(context, plg_cryptoctx,
                                                 req_cryptoctx, idopts,

Now in theory this SHOULD work fine.  But in practice, the only way for
process_option_identity() to fail is if you give it a completely mangled
identity string.  So I think it's going to pick the first pkinit_identities
line most of the time assuming it is formatted correctly.

It turns out our plugin was modified many, many years ago to move the call
to crypto_load_certs() inside of that loop, which seems like that makes
sense; I don't see how that could work otherwise.

That works for many identity types, but for a PKCS11 module that's NOT
quite sufficient.  When you're using a PKCS11 module, what happens is
crypto_load_certs() calls pkinit_get_certs_pkcs11(), who calls
pkinit_open_session().  pkinit_open_session() WILL return an error
if it can't load a module ... but if it is successful AND defer_id_prompt
it set (it always is at this point), it will ALSO return an error (well,
it will do that if the PKCS11 module sets CKF_LOGIN_REQUIRED; in my very
limited experience, most of them do).

I suspect everyone wants this to work, since the documentation SAYS
it will work, so probably as a baseline everyone would be happy with
moving the call to crypto_load_certs() inside of that loop (along with
crypto_free_cert_info()).  But I'm a little unclear of the semantics
when attempting to load a PKCS11 module, and I know that the PKINIT
code is complicated enough that a simple change can have unintended
consequences.  So, spitballing here.

It seems to me that the cleanest thing to do is make it so if
defer_id_prompt is prompt is set, do NOT return an error if the
pkcs11 module is loaded correctly and an acceptable token is found.
That would let you remove the test in pkinit_get_certs_pkcs11() that
makes it so it always returns success when defer_id_prompt.  That, combined
with the change to move crypto_load_certs(), would let you bubble up
an error during a PKCS11 module load and let you try other identity
lines.  But .. I have been around the PKINIT code enough to know that
it is very possible I am missing something.  I'm glad to develop a pull
request if people think this is the correct way forward, but I wanted
some feedback first.


More information about the krbdev mailing list