[krbdev.mit.edu #7183] PKINIT should handle CMS SignedData without certificates
Greg Hudson via RT
rt-comment at krbdev.mit.edu
Thu Jun 21 16:28:52 EDT 2012
A KDC reply to a Diffie-Hellman PKINIT request contains an RFC 3852
SignedData object with a payload (the DH reply), an optional set of
certificates, and a SignerInfo object. The SignerInfo object contains
the identifier of the signing certificate and the signature itself.
RFC 4556 explicitly allows the SignedData certificates set to be omitted
if the request's kdcPkId matches the signer certificate; otherwise the
certificates "SHOULD be sufficient to construct at least one
certification path from the KDC certificate to one trust anchor
acceptable by the client". If the signer certificate is itself a trust
anchor as presented by the client, then it could also be considered
reasonable for the KDC to omit the certificates field of the SignedData,
as no certificates are needed to construct a path.
At present, we never (at least with OpenSSL) specify a kdcPkId value in
the request; there is code to construct it, but it relies on
pkinit_get_kdc_cert() doing something non-trivial, which it currently
doesn't. So a KDC will never omit the certificate field on account of a
kdcPkId value we send, as things stand.
But it is easy enough to tell the client that the KDC certificate is a
trust anchor, in which case a Heimdal KDC will reply with no
certificates because it removes trust anchors from the set of
certificates in the reply. So we ought to handle at least the case
where the SignerInfo sid is a trust anchor, rather than a certificate in
the SignedData message. We arguably also be able to handle the case
where the SignerInfo sid is an intermediate certificate (though we don't
tell the KDC about those, I don't think) or is the ID we put into
kdcPkId (although we currently never put an ID there).
At current there are three code paths for verifying the CMS SignedData
value: NSS, OpenSSL 1.0.x with CMS functions, and OpenSSL 0.9.x with the
older PKCS7 functions. The NSS code looks like it might already work
for these cases, but I'm not sure. The OpenSSL code wants to start by
extracting the signer cert, verify that cert, and then verify the
signature against the certificate with CMS_verify or PKCS7_verify.
With OpenSSL 0.9.x, we use PKCS7_cert_from_signer_info(), which only
works if the SignedData supplies the certificate. I'm not sure of an
easy way to fix the bug for this case, and time will eventually carry us
past having to worry about OpenSSL 0.9.x anyway.
With OpenSSL 1.0.x, we call CMS_set1_signers_certs(cms, NULL, 0) to
match the signer ID against the certificates in the cms message, and
then call CMS_SignerInfo_get0_algs to get the certificate which has (we
hope) been set in the signer info. We can use the second argument of
CMS_set1_signers_certs to match the signer ID against other certificates
sets such as the configured trusted anchors and intermediate certs. If
we ever build out pkinit_get_kdc_cert(), we'll need to match against
that as well.
Thanks to Henry Hotz and Nalin Dahyabhai for their help in investigating
this issue.
More information about the krb5-bugs
mailing list