Bug 1292042 - freeipa-server-install fails to compare DNs in certificates
Summary: freeipa-server-install fails to compare DNs in certificates
Keywords:
Status: CLOSED DUPLICATE of bug 1292099
Alias: None
Product: Fedora
Classification: Fedora
Component: freeipa
Version: rawhide
Hardware: x86_64
OS: Linux
unspecified
high
Target Milestone: ---
Assignee: IPA Maintainers
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2015-12-16 10:26 UTC by hdunkel
Modified: 2016-06-21 09:45 UTC (History)
12 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed: 2016-01-07 14:51:39 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
FedoraHosted FreeIPA 5560 0 None None None Never

Description hdunkel 2015-12-16 10:26:14 UTC
Apparently ipa-server-install (4.2) gets confused about the attribute sequence in the DNs of the certificates. If I use

	ipa-server-install --external-ca --subject="C=DE,O=example AG"

then ipa's csr contains

	O=example AG, C=DE, CN=Certificate Authority

The signed certificate contains

	C=DE, O=example AG, CN=Certificate Authority

If I run ipa-server-install again to hand off the certificate chain, then the code in load_external_cert() (installutils.py) sees

	ca_subject = "CN=Certificate Authority,C=DE,O=example AG"
	subject    = "CN=Certificate Authority,O=example AG,C=DE"
	:
	if subject == ca_subject:
		ca_nickname = nickname
	:
	if ca_nickname is None:
		raise ScriptError("IPA CA certificate not found in %s" % (", ".join(files)))

The strings don't match and the certificate chain is rejected, even though it is valid.

Please check https://tools.ietf.org/html/rfc5280#section-7.1 for
reference.

Comment 1 Alexander Bokovoy 2015-12-16 10:46:02 UTC
Could you please attach ipaserver-install.log from /var/log? You may want to replace actual subject/dc/hostname/IP address values to avoid mentioning your own private names.

Comment 2 hdunkel 2015-12-16 11:57:01 UTC
Sorry to say, but the log file is gone. I have wiped out the container to continue. Fortunately the bad code is obvious:

	if subject == ca_subject:
		ca_nickname = nickname

Please check RFC5280 about how to compare distinguished names.

Comment 3 Petr Vobornik 2015-12-16 12:26:22 UTC
The code, correctly uses DN class to perform the comparison: https://git.fedorahosted.org/cgit/freeipa.git/tree/ipaserver/install/installutils.py?h=ipa-4-2#n1005

Issue here is different, RFC 5280 says: "and the matching RDNs appear in the same order in both DNs" which is not the case here.

Comment 4 hdunkel 2015-12-16 13:00:08 UTC
I worked around this using 

	ipa-server-install --external-ca --subject="O=example AG,C=DE"

but I would assume this worked just by chance. Every fix or stable workaround is highly appreciated.

Comment 5 Alexander Bokovoy 2015-12-16 13:08:51 UTC
The code in certutil (actually, in nss library) claims that it does reverse RDNs to comply with RFC1485 which is the only one supported by certutil for subjects. We are checking with NSS maintainers to see whether this is indeed a correct behavior.

Comment 6 Alexander Bokovoy 2015-12-16 13:27:58 UTC
(In reply to Petr Vobornik from comment #3)
> The code, correctly uses DN class to perform the comparison:
> https://git.fedorahosted.org/cgit/freeipa.git/tree/ipaserver/install/
> installutils.py?h=ipa-4-2#n1005
> 
> Issue here is different, RFC 5280 says: "and the matching RDNs appear in the
> same order in both DNs" which is not the case here.

The problem here is that comparison should actually be done on DER encoding of the distinguished name, not the string. Also, ipapython.DN class handles LDAP distinguished names, not x.509 ones. The difference is that for LDAP the order of RDNs is important while for x.509 it is not: see RFC4514 2. and 5.2.

Comment 7 Kai Engert (:kaie) (inactive account) 2015-12-16 15:45:47 UTC
If I understand correctly, what happens is:

- IPA code takes a partial-DN input
- IPA adds CN to partial-DN and creates DN
- IPA requests NSS to produce a certificate with DN
- the certificate produced contains DN-reversed
- later, IPA looks at the code and produces DN-of-certificate

We have learned that different tools use different strategies for creating the text representation of the DN.

OpenSSL tools seem to always create the string in the order as encoded, while NSS tools always reverses the order prior to displaying it.

I suspect the problem is caused by the step that reads the certificate for comparison purposes, and attempts to build the string representation of the DN. At this step, you might currently use non-NSS code, which makes different assumptions.

I guess, if IPA used NSS for both creating the CSR, and for later creating the string representation of the certificates's DN, then things would match.

Comment 8 Kai Engert (:kaie) (inactive account) 2015-12-16 16:05:37 UTC
(In reply to Kai Engert (:kaie) from comment #7)
> 
> I suspect the problem is caused by the step that reads the certificate for
> comparison purposes, and attempts to build the string representation of the
> DN. At this step, you might currently use non-NSS code, which makes
> different assumptions.
> 
> I guess, if IPA used NSS for both creating the CSR, and for later creating
> the string representation of the certificates's DN, then things would match.

I've learned that you already use NSS code, but apparently one that directly operates on the storage format.

I think you'd have to use NSS code that converts the name to the string presentation. That NSS code should know that it needs to reverse the order when creating the string.

I think you'd have to use the CERT_NameToAscii() function.

Comment 9 John Dennis 2015-12-16 16:44:00 UTC
python-nss Certificate.subject does call CERT_NameToAscii()

To the best of my understanding both the ipapython.DN implementation and the result of Certificate.subject produce a RFC 4514 representation with the RDN's in the same order.

NSS certutil calls CERT_AsciiToName on the string passed as the subject. So this would suggest everything is consistent.

Comment 10 John Dennis 2015-12-16 16:49:55 UTC
Re comment #6

I disagree with the assertion that RDN ordering is insignificant in x509.

Comment 11 Kai Engert (:kaie) (inactive account) 2015-12-17 12:03:49 UTC
So, let me do a complete description of what you're doing, plus experiments.

User input "partial DN":
  "C=DE,O=example AG"

IPA prepends to create the "complete DN":
  "CN=Certificate Authority,C=DE,O=example AG"

IPA runs certutil to create a CSR:
    goto temp dir
    certutil -d . -N --empty-password
    certutil -d . -R -s "CN=Certificate Authority,C=DE,O=example AG" -a -o example.csr -z /proc/uptime

External tools are used to transform the CSR into a certificate.
We assume the representation inside both CSR and certificate is identical
(whichever ordering it is, it must be the same).

In order to have a complete example, I'm using certutil again, to transform
the CSR into a certificate.

    certutil -d . -C -x -m 0 -Z SHA256 -a -i example.csr > example.crt

Then IPA loads the certificate and uses python-nss to transform the
cert's DN to a "cert DN string".
IPA expects the "cert DN string" to be identical to the original "complete DN".

John sent me a helper script that uses python-nss.

I tweaked it to obtain "cert.subject" and print it, which is what IPA uses:

$ python cert-print.py -f pem example.crt 
certificate filename=example.crt
Subject: CN=Certificate Authority,C=DE,O=example AG

This matches our expectation.

I don't understand why IPA gets a different ordering.

For further analysis, I'd like to ask that you please provide a full set of:
- exact certutil command that you use with all parameters
- copies of the csr and the certificate files you're using

Thanks

Comment 12 Kai Engert (:kaie) (inactive account) 2015-12-17 12:05:40 UTC
The relevant portions in John script are:

cert_der = nss.read_der_from_file(filename, options.cert_format == 'pem')

cert = nss.Certificate(cert_der)

print("Subject: %s" % cert.subject)

Comment 13 Alexander Bokovoy 2015-12-17 12:52:27 UTC
FreeIPA actually calls out for certutil in its wrapper around nssdb:
   def get_cert(self, nickname, pem=False):
        args = ['-L', '-n', nickname]
        if pem:
            args.append('-a')
        else:
            args.append('-r')
        try:
            cert, err, returncode = self.run_certutil(args)
        except ipautil.CalledProcessError:
            raise RuntimeError("Failed to get %s" % nickname)
        return cert

for the code in question we get 'certutil -d path -L -n nickname -a' and use output as a PEM cert input into nss.Certificate(). The reason it is done this way is because, I believe, we have already other NSS database open, so opening a second one would crash nss code. I may be wrong but we had those issues before.

Comment 14 Kai Engert (:kaie) (inactive account) 2015-12-17 12:57:47 UTC
(In reply to Alexander Bokovoy from comment #13)
> ... we get 'certutil -d path -L -n nickname -a' and use
> output as a PEM cert input into nss.Certificate().

This should be fine. As long as you feed the raw certificate into python-nss, and use the same python-nss code to extract the DN, you should get the same results as I.

So, we still need more details (and files) to try to understand what's going on on your side.

Comment 15 John Dennis 2015-12-17 13:53:56 UTC
FYI: my understanding is that OpenSSL and NSS handle the ordering of RDN's in opposite order, hopefully this is not causing some of the confusion.

It's a well documented fact that OpenSSL uses the reverse order from RFC 4514 (the display format for a directory name). There are several bugs reports open on this behavior, if need be I can probably find them.

Why does OpenSSL do this? Because apparently the DER encoding of a DN requires the RDN to be in opposite order from RFC 4514. One assumes the OpenSSL implementation is just a thin layer over the binary encoding (e.g. the protocol).

On the other hand NSS and every other library I've experimented with using the RFC 4514 ordering, which IMHO is correct.

FWIW OpenSSL also uses a completely non-standard string representation for subjects using backslashes. At one point I had to write a utility for Openstack to translated between RFC 4514 and OpenSSL formats.

The reason I raise this is because if at any point OpenSSL is involved you could be subject to the reverse ordering problem.

I'll double check the behavior of libpython.DN and python-nss subject handling.

Comment 16 Christian Heimes 2015-12-17 15:05:30 UTC
After some debugging I was able to find the problem. It's not related to order of RDNs in DNs at all.

How to reproduce:

1) ipa-server-install --external-ca --subject="C=DE,O=example AG" --realm=CA.TEST

This creates a CSR:
/usr/lib64/nss/unsupported-tools/pp -t cr -a -i /root/ipa.csr | grep Subject:
        Subject: "CN=Certificate Authority,C=DE,O=example AG"

2) I signed /root/ipa.csr with a custom CA.

/usr/lib64/nss/unsupported-tools/pp -t c -a -i /root/ipa.crt | grep Subject:
        Subject: "CN=Certificate Authority,C=DE,O=example AG"

3) /sbin/ipa-server-install --external-cert-file=ipa.crt --external-cert-file=ca.crt

For step 3 I added pdb.set_trace() to load_external_cert():

> /usr/lib/python2.7/site-packages/ipaserver/install/installutils.py(1000)load_external_cert()
-> ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
(Pdb) n
> /usr/lib/python2.7/site-packages/ipaserver/install/installutils.py(1001)load_external_cert()
-> ca_nickname = None
(Pdb) p ca_subject
ipapython.dn.DN('CN=Certificate Authority,O=CA.TEST')

Now that's interesting. The ca_subject looks completely different. Where does O=CA.TEST come from? It's the Kerberos realm!

https://git.fedorahosted.org/cgit/freeipa.git/tree/ipaserver/install/ca.py#n70
https://git.fedorahosted.org/cgit/freeipa.git/tree/ipaserver/install/server/install.py#n580

    if not options.subject:
        options.subject = DN(('O', realm_name))

The installer doesn't remember the custom subject and neglects to tell you that you must use the --subject argument in the second call, too. It's really a documentation and UI bug.

I was able to complete the second installation step with:

/sbin/ipa-server-install --external-cert-file=ipa.crt --external-cert-file=ca.crt --subject="C=DE,O=example AG"

Comment 17 Kai Engert (:kaie) (inactive account) 2015-12-17 16:12:39 UTC
Christian, when talking about the original problem report, can you please explain what must be done differently to avoid the problem? Thanks.

Comment 18 Christian Heimes 2015-12-17 16:24:50 UTC
Kai, sure!

In order to avoid the problem it is necessary to call ipa-server-install with the --subject="C=DE,O=example AG" *both* times. The problem occurs when --subject is missing in the second step.

I'm sorry for the inconvenience. The installer doesn't explain the fact. I'll make sure that future versions of FreeIPA are more helpful.

Comment 19 Petr Vobornik 2015-12-17 17:01:57 UTC
seems to me that the root cause is the same as in bug 1292099 a regression in installer - installer doesn't use the options from first step of installation.

There is a patch on the devel-list: https://www.redhat.com/archives/freeipa-devel/2015-December/msg00523.html

Comment 20 Petr Vobornik 2016-01-07 14:51:39 UTC
see comment 19, was fixed upstream. Will be part of 4.2.4 update.

*** This bug has been marked as a duplicate of bug 1292099 ***


Note You need to log in before you can comment on or make changes to this bug.