Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options to write lightweight CA cert or chain to file #177

Conversation

frasertweedale
Copy link
Contributor

@frasertweedale frasertweedale commented Oct 21, 2016

Administrators need a way to retrieve the certificate or certificate
chain of an IPA-managed lightweight CA. Add params to the ca' object for carrying the CA certificate and chain (as multiple DER values), and add the--certificate-out' option and `--chain' flag
as client-side options for writing one or the other to a file.

Fixes: https://fedorahosted.org/freeipa/ticket/6178

@HonzaCholasta
Copy link
Contributor

The original review thread is available at:

https://www.redhat.com/archives/freeipa-devel/2016-October/msg00578.html

@frasertweedale
Copy link
Contributor Author

Bump for review

@tiran
Copy link
Member

tiran commented Nov 17, 2016

pylint fails:

Pylint is running, please wait ...
************* Module ipalib.x509
ipalib/x509.py:161: [E0602(undefined-variable), pkcs7_to_pems] Undefined variable 'paths')
make: *** [pylint] Error 2
Makefile:1040: recipe for target 'pylint' failed


"""
cmd = [
paths.OPENSSL, "pkcs7", "-print_certs",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you forgot to add from ipaplatform.paths import paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, the import was removed in a refactoring commit and didn't notice because patch rebased cleanly :) fixed in latest update.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import was removed for a reason, putting it back will break the ipalib package on PyPI.

Now that we require pyasn1_modules, we can use PyASN1 to do the parsing instead:

    if datatype == PEM:
        match = re.search(r'-----BEGIN PKCS7-----(.*?)-----END PKCS7-----', data, re.DOTALL)
        if not match:
            raise ValueError()
        data = base64.b64decode(match.group(1))

    ci, _rest = decoder.decode(data, rfc2315.ContentInfo())
    if ci['contentType'] != rfc2315.ContentType('1.2.840.113549.1.7.2'):
        raise ValueError()

    data = str(ci['content'])
    sd, _rest = decoder.decode(data, rfc2315.SignedData())

    certs = [encoder.encode(c['certificate']) for c in sd['certificates']]
    return certs

@@ -290,7 +285,7 @@ def import_files(self, files, db_password_filename, import_keys=False,
filename, line, e)
continue
else:
extracted_certs += result.output + '\n'
extracted_certs += '\n'.join(certs) + '\n'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line is ugly. Please convert extracted_certs to a list, append/extend the list and perform '\n\.join(extracted_certs) at the end of the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a separate commit for this refactor.

subid += 1
for cert in certlist:
try:
(chain_fd, chain_name) = tempfile.mkstemp()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style nit-pick: The parenthesis are unnecessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest update.

@tkrizek tkrizek self-assigned this Nov 24, 2016
@tkrizek
Copy link
Contributor

tkrizek commented Nov 24, 2016

Please update the xmlrpc tests to reflect the extra certificate attributes (~12 failed tests in test_xmlrpc/test_ca_plugin.py, test_caacl_plugin.py and test_caacl_profile_enforcement.py).

There are also a couple tests failing with ACIError:

ACIError: Insufficient access: Principal 'srv/santest-host-1...' is not permitted to use CA 'default-profile-subca' with profile 'caIPAserviceCert' for certificate issuance.

I also found the --certificate-out option a bit confusing. At first I thought I should provide the certificate name to be exported. Perhaps the help text could be improved to make it clear the used should provide a file name?

@frasertweedale
Copy link
Contributor Author

@tomaskrizek thanks for reviewing. Updated tests and change the --certificate-out metavar to FILE.

@HonzaCholasta
Copy link
Contributor

To continue the discussion from the mailing list:

My point exactly - ca-show output should be equivalent to cert-show on the
CA certificate, as far as the certificate and chain are concerned.

I reused BaseCertObject.takes_params' and BaseCertObject._parse'
to define the params and do most of the work. There is some overlap
with what BaseCertObject' defines and fields of the ca' LDAP
attribute so these are ignored/removed.

What I actually meant is that cert-show should also have a chain option and certificate_chain param in the future, which should work the same as in ca-show. Adding everything from BaseCertObject is an overkill IMHO, and out of the scope of ticket 6178.

I think I would prefer if the certificate was always returned by the server,
but the chain only if --chain (or --all) is specified.

Additionally, ca-add should also get the new options and do all of this.

I've implemented this. --chain' implies --all' but otherwise
remains a client-side only param.

This does not scale well - if a new unrelated attribute is added to the CA LDAP entry, or if a new param is added to the CA object, --chain will imply retrieving them, which is not something we want. It should really be the other way around and --all should imply --chain, which also means --chain has to be defined on the server side.

Generator expressions are generally preferred over map():

data = '\n'.join(to_pem(der) for der in ders)

Preferred by whom? ;)

Pythonistas, I believe :)

@frasertweedale
Copy link
Contributor Author

@jcholast thanks for review. PR updated. No longer inheriting BaseCertObject. --chain now defined
server-side and no longer implies --all.

@frasertweedale
Copy link
Contributor Author

Never mind... my --chain option disappeared... not quite there yet >_<

@frasertweedale
Copy link
Contributor Author

@jcholast OK there we go. I'd forgotten to remove the include='cli' when converting to server-side option.

'chain',
default=False,
doc=_('Include certificate chain in output'),
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get rid of this copy-pasta? Maybe like this:

_chain_flag = Flag(
    'chain',
    doc=_('Include certificate chain in output'),
)

...

    takes_options = LDAPCreate.takes_options + (
        _chain_flag,
    )

(Also note that all flags implicitly have default value of False.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meet you halfway: yes to de-dup; no to deferring to default value. Explicit is better than implicit :)

@HonzaCholasta
Copy link
Contributor

HonzaCholasta commented Dec 7, 2016

Could you make ca-find return the cert/chain as well if (and only if) --all is specified? Do not add the --chain and --certificate-out options to it though. This is for consistency with cert-find, host-find, service-find, etc. Not a blocker.

Also see inline comments.

@frasertweedale
Copy link
Contributor Author

frasertweedale commented Dec 7, 2016

@jcholast returning cert and chain in ca_find when --all is given will incur n * 2 additional round-trips to Dogtag where n = number of IPA-managed CAs. I am hesitant to do it unless/until Dogtag provides a better way. Let's open tickets.

@HonzaCholasta
Copy link
Contributor

@frasertweedale, yep, I'm aware of that - cert-find does the same. Not a big deal IMO since it has to be explicitly requested by the user. But tickets are certainly a good idea.

@frasertweedale
Copy link
Contributor Author

@jcholast updated PR to include certificate and certificate_chain in ca_find output when --all is specified.

@HonzaCholasta
Copy link
Contributor

@frasertweedale, thanks. What about this?

@frasertweedale
Copy link
Contributor Author

frasertweedale commented Dec 12, 2016 via email

@HonzaCholasta
Copy link
Contributor

@frasertweedale, I'm afraid we can't do that. As I said in the comment, you cannot unconditionally import from ipaplatform to ipalib anymore, so you either have to make the change to PyASN1, or make the import conditional:

try:
    from ipaplatform.paths import paths
except ImportError:
    OPENSSL = '/usr/bin/openssl'
else:
    OPENSSL = paths.OPENSSL

Add a single function for extracting X.509 certs in PEM format from
a PKCS freeipa#7 object.  Refactor sites that execute ``openssl pkcs7`` to
use the new function.

Part of: https://fedorahosted.org/freeipa/ticket/6178
certdb.NSSDatabase.import_files currently accumulates certificates
extracted from input files as a string, which is ugly.  Accumulate a
list of PEMs instead, and join() them just in time for PKCS freeipa#12
creation.

Part of: https://fedorahosted.org/freeipa/ticket/6178
Administrators need a way to retrieve the certificate or certificate
chain of an IPA-managed lightweight CA.  Add params to the `ca'
object for carrying the CA certificate and chain (as multiple DER
values).  Add the `--chain' flag for including the chain in the
result (chain is also included with `--all').  Add the
`--certificate-out' option for writing the certificate to a file (or
the chain, if `--chain' was given).

Fixes: https://fedorahosted.org/freeipa/ticket/6178
@frasertweedale
Copy link
Contributor Author

@jcholast right you are. PR updated with conditional import.

Thanks.

@HonzaCholasta HonzaCholasta added ack Pull Request approved, can be merged pushed Pull Request has already been pushed labels Dec 12, 2016
@martbab
Copy link
Contributor

martbab commented Dec 12, 2016

@jcholast @frasertweedale I hope you did notice those failures in Travis CI before acking/pushing...

@frasertweedale frasertweedale deleted the feature/6178-ca-cert-chain-out branch December 12, 2016 12:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ack Pull Request approved, can be merged pushed Pull Request has already been pushed
Projects
None yet
5 participants