Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 657528 Details for
Bug 876307
CVE-2012-5484 ipa: weakness when initiating join from IPA client can potentially compromise IPA domain
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
4/4
0113-Use-secure-method-to-acquire-IPA-CA-certificate.patch (text/plain), 31.68 KB, created by
Simo Sorce
on 2012-12-04 13:52:47 UTC
(
hide
)
Description:
4/4
Filename:
MIME Type:
Creator:
Simo Sorce
Created:
2012-12-04 13:52:47 UTC
Size:
31.68 KB
patch
obsolete
>From 63ff5f04c2dff3a6e98902984ff1df2bbaffe88b Mon Sep 17 00:00:00 2001 >From: John Dennis <jdennis@redhat.com> >Date: Thu, 15 Nov 2012 14:57:52 -0500 >Subject: [PATCH 113/113] Use secure method to acquire IPA CA certificate > >Major changes ipa-client-install: > >* Use GSSAPI connection to LDAP server to download CA cert (now > the default method) > >* Add --ca-cert-file option to load the CA cert from a disk file. > Validate the file. If this option is used the supplied CA cert > is considered definitive. > >* The insecure HTTP retrieval method is still supported but it must be > explicitly forced and a warning will be emitted. > >* Remain backward compatible with unattended case (except for aberrant > condition when preexisting /etc/ipa/ca.crt differs from securely > obtained CA cert, see below) > >* If /etc/ipa/ca.crt CA cert preexists the validate it matches the > securely acquired CA cert, if not: > > - If --unattended and not --force abort with error > > - If interactive query user to accept new CA cert, if not abort > > In either case warn user. > >* If interactive and LDAP retrieval fails prompt user if they want to > proceed with insecure HTTP method > >* If not interactive and LDAP retrieval fails abort unless --force > >* Backup preexisting /etc/ipa/ca.crt in FileStore prior to execution, > if ipa-client-install fails it will be restored. > >Other changes: > >* Add new exception class CertificateInvalidError > >* Add utility convert_ldap_error() to ipalib.ipautil > >* Replace all hardcoded instances of /etc/ipa/ca.crt in > ipa-client-install with CACERT constant (matches existing practice > elsewhere). > >* ipadiscovery no longer retrieves CA cert via HTTP. > >* Handle LDAP minssf failures during discovery, treat failure to check > ldap server as a warninbg in absebce of a provided CA certificate via > --ca-cert-file or though existing /etc/ipa/ca.crt file. > >Signed-off-by: Simo Sorce <simo@redhat.com> >Signed-off-by: Rob Crittenden <rcritten@redhat.com> >--- > ipa-client/ipa-install/ipa-client-install | 400 ++++++++++++++++++++++++++++-- > ipa-client/ipaclient/ipadiscovery.py | 47 ++-- > ipa-client/man/ipa-client-install.1 | 8 + > ipalib/errors.py | 16 ++ > ipapython/ipautil.py | 36 +++ > ipaserver/install/certs.py | 5 +- > 6 files changed, 460 insertions(+), 52 deletions(-) > >diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install >index 9f44da6..e4d7404 100755 >--- a/ipa-client/ipa-install/ipa-client-install >+++ b/ipa-client/ipa-install/ipa-client-install >@@ -25,14 +25,18 @@ try: > import os > import time > import socket >+ import ldap >+ import ldap.sasl >+ import urlparse > from ipapython.ipa_log_manager import * > import tempfile > import getpass > from base64 import b64decode > from ipaclient import ipadiscovery >+ from ipaclient.ipadiscovery import CACERT > import ipaclient.ipachangeconf > import ipaclient.ntpconf >- from ipapython.ipautil import run, user_input, CalledProcessError, file_exists, realm_to_suffix >+ from ipapython.ipautil import run, user_input, CalledProcessError, file_exists, realm_to_suffix, convert_ldap_error > import ipapython.services as ipaservices > from ipapython import ipautil > from ipapython import dnsclient >@@ -41,9 +45,10 @@ try: > from ipapython import certmonger > from ipapython.config import IPAOptionParser > from ipalib import api, errors >+ from ipalib import x509 > import SSSDConfig > from ConfigParser import RawConfigParser >- from optparse import SUPPRESS_HELP, OptionGroup >+ from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError > except ImportError: > print >> sys.stderr, """\ > There was a problem importing one of the required Python modules. The >@@ -53,6 +58,7 @@ error was: > """ % sys.exc_value > sys.exit(1) > >+SUCCESS = 0 > CLIENT_INSTALL_ERROR = 1 > CLIENT_NOT_CONFIGURED = 2 > CLIENT_ALREADY_CONFIGURED = 3 >@@ -61,6 +67,21 @@ CLIENT_UNINSTALL_ERROR = 4 # error after restoring files/state > client_nss_nickname_format = 'IPA Machine Certificate - %s' > > def parse_options(): >+ def validate_ca_cert_file_option(option, opt, value, parser): >+ if not os.path.exists(value): >+ raise OptionValueError("%s option '%s' does not exist" % (opt, value)) >+ if not os.path.isfile(value): >+ raise OptionValueError("%s option '%s' is not a file" % (opt, value)) >+ if not os.path.isabs(value): >+ raise OptionValueError("%s option '%s' is not an absolute file path" % (opt, value)) >+ >+ try: >+ cert = x509.load_certificate_from_file(value) >+ except Exception, e: >+ raise OptionValueError("%s option '%s' is not a valid certificate file" % (opt, value)) >+ >+ parser.values.ca_cert_file = value >+ > parser = IPAOptionParser(version=version.VERSION) > > basic_group = OptionGroup(parser, "basic options") >@@ -99,6 +120,9 @@ def parse_options(): > basic_group.add_option("-U", "--unattended", dest="unattended", > action="store_true", > help="unattended (un)installation never prompts the user") >+ basic_group.add_option("--ca-cert-file", dest="ca_cert_file", >+ type="string", action="callback", callback=validate_ca_cert_file_option, >+ help="load the CA certificate from this file") > # --on-master is used in ipa-server-install and ipa-replica-install > # only, it isn't meant to be used on clients. > basic_group.add_option("--on-master", dest="on_master", action="store_true", >@@ -155,6 +179,35 @@ def nickname_exists(nickname): > else: > return False > >+def cert_summary(msg, cert, indent=' '): >+ if msg: >+ s = '%s\n' % msg >+ else: >+ s = '' >+ s += '%sSubject: %s\n' % (indent, cert.subject) >+ s += '%sIssuer: %s\n' % (indent, cert.issuer) >+ s += '%sValid From: %s\n' % (indent, cert.valid_not_before_str) >+ s += '%sValid Until: %s\n' % (indent, cert.valid_not_after_str) >+ >+ return s >+ >+def get_cert_path(cert_path): >+ """ >+ If a CA certificate is passed in on the command line, use that. >+ >+ Else if a CA file exists in CACERT then use that. >+ >+ Otherwise return None. >+ """ >+ if cert_path is not None: >+ return cert_path >+ >+ if os.path.exists(CACERT): >+ return CACERT >+ >+ return None >+ >+ > # Checks whether nss_ldap or nss-pam-ldapd is installed. If anyone of mandatory files was found returns True and list of all files found. > def nssldap_exists(): > files_to_check = [{'function':'configure_ldap_conf', 'mandatory':['/etc/ldap.conf','/etc/nss_ldap.conf','/etc/libnss-ldap.conf'], 'optional':['/etc/pam_ldap.conf']}, >@@ -617,7 +670,7 @@ def configure_krb5_conf(fstore, cli_basedn, cli_realm, cli_domain, cli_server, c > {'name':'default_domain', 'type':'option', 'value':cli_domain}] > else: > kropts = [] >- kropts.append({'name':'pkinit_anchors', 'type':'option', 'value':'FILE:/etc/ipa/ca.crt'}) >+ kropts.append({'name':'pkinit_anchors', 'type':'option', 'value':'FILE:%s' % CACERT}) > ropts = [{'name':cli_realm, 'type':'subsection', 'value':kropts}] > > opts.append({'name':'realms', 'type':'section', 'value':ropts}) >@@ -779,7 +832,7 @@ def configure_sssd_conf(fstore, cli_realm, cli_domain, cli_server, options, clie > # Note that SSSD will force StartTLS because the channel is later used for > # authentication as well if password migration is enabled. Thus set the option > # unconditionally. >- domain.set_option('ldap_tls_cacert', '/etc/ipa/ca.crt') >+ domain.set_option('ldap_tls_cacert', CACERT) > > if options.dns_updates: > domain.set_option('ipa_dyndns_update', True) >@@ -1077,6 +1130,309 @@ def update_ssh_keys(server, hostname, ssh_dir, create_sshfp): > if not do_nsupdate(update_txt): > print "Warning: Could not update DNS SSHFP records." > >+def get_ca_cert_from_file(url): >+ ''' >+ Get the CA cert from a user supplied file and write it into the >+ CACERT file. >+ >+ Raises errors.NoCertificateError if unable to read cert. >+ Raises errors.FileError if unable to write cert. >+ ''' >+ >+ # pylint: disable=E1101 >+ try: >+ parsed = urlparse.urlparse(url, 'file') >+ except Exception, e: >+ raise errors.FileError("unable to parse file url '%s'" % (url)) >+ >+ if parsed.scheme != 'file': >+ raise errors.FileError("url is not a file scheme '%s'" % (url)) >+ >+ filename = parsed.path >+ >+ if not os.path.exists(filename): >+ raise errors.FileError("file '%s' does not exist" % (filename)) >+ >+ if not os.path.isfile(filename): >+ raise errors.FileError("file '%s' is not a file" % (filename)) >+ >+ root_logger.debug("trying to retrieve CA cert from file %s", filename) >+ try: >+ cert = x509.load_certificate_from_file(filename) >+ except Exception, e: >+ raise errors.NoCertificateError(entry=filename) >+ >+ try: >+ x509.write_certificate(cert.der_data, CACERT) >+ except Exception, e: >+ raise errors.FileError(reason = >+ u"cannot write certificate file '%s': %s" % (CACERT, e)) >+ >+def get_ca_cert_from_http(url, ca_file, warn=True): >+ ''' >+ Use HTTP to retrieve the CA cert and write it into the CACERT file. >+ This is insecure and should be avoided. >+ >+ Raises errors.NoCertificateError if unable to retrieve and write cert. >+ ''' >+ >+ if warn: >+ root_logger.warning("Downloading the CA certificate via HTTP, " + >+ "this is INSECURE") >+ >+ root_logger.debug("trying to retrieve CA cert via HTTP from %s", url) >+ try: >+ >+ run(["/usr/bin/wget", "-O", ca_file, url]) >+ except CalledProcessError, e: >+ raise errors.NoCertificateError(entry=url) >+ >+def get_ca_cert_from_ldap(url, basedn, ca_file): >+ ''' >+ Retrieve th CA cert from the LDAP server by binding to the >+ server with GSSAPI using the current Kerberos credentials. >+ Write the retrieved cert into the CACERT file. >+ >+ Raises errors.NoCertificateError if cert is not found. >+ Raises errors.NetworkError if LDAP connection can't be established. >+ Raises errors.LDAPError for any other generic LDAP error. >+ Raises errors.OnlyOneValueAllowed if more than one cert is found. >+ Raises errors.FileError if unable to write cert. >+ ''' >+ >+ ca_cert_attr = 'cAcertificate;binary' >+ dn = 'CN=CAcert, CN=ipa, CN=etc, %s' % basedn >+ >+ SASL_GSSAPI = ldap.sasl.sasl({},'GSSAPI') >+ >+ root_logger.debug("trying to retrieve CA cert via LDAP from %s", url) >+ >+ conn = ldap.initialize(url) >+ conn.set_option(ldap.OPT_X_SASL_NOCANON, ldap.OPT_ON) >+ try: >+ conn.sasl_interactive_bind_s('', SASL_GSSAPI) >+ result = conn.search_st(str(dn), ldap.SCOPE_BASE, 'objectclass=pkiCA', >+ [ca_cert_attr], timeout=10) >+ except ldap.NO_SUCH_OBJECT, e: >+ root_logger.debug("get_ca_cert_from_ldap() error: %s", >+ convert_ldap_error(e)) >+ raise errors.NoCertificateError(entry=url) >+ >+ except ldap.SERVER_DOWN, e: >+ root_logger.debug("get_ca_cert_from_ldap() error: %s", >+ convert_ldap_error(e)) >+ raise errors.NetworkError(uri=url, error=str(e)) >+ except Exception, e: >+ root_logger.debug("get_ca_cert_from_ldap() error: %s", >+ convert_ldap_error(e)) >+ raise errors.LDAPError(str(e)) >+ >+ if len(result) != 1: >+ raise errors.OnlyOneValueAllowed(attr=ca_cert_attr) >+ >+ attrs = result[0][1] >+ try: >+ der_cert = attrs[ca_cert_attr][0] >+ except KeyError: >+ raise errors.NoCertificateError(entry=ca_cert_attr) >+ >+ try: >+ x509.write_certificate(der_cert, ca_file) >+ except Exception, e: >+ raise errors.FileError(reason = >+ u"cannot write certificate file '%s': %s" % (ca_file, e)) >+ >+def validate_new_ca_cert(existing_ca_cert, ca_file, ask, override=False): >+ >+ try: >+ new_ca_cert = x509.load_certificate_from_file(ca_file) >+ except Exception, e: >+ raise errors.FileError( >+ "Unable to read new ca cert '%s': %s" % (ca_file, e)) >+ >+ if existing_ca_cert is None: >+ root_logger.info( >+ cert_summary("Successfully retrieved CA cert", new_ca_cert)) >+ return >+ >+ if existing_ca_cert.der_data != new_ca_cert.der_data: >+ root_logger.warning( >+ "The CA cert available from the IPA server does not match the\n" >+ "local certificate available at %s" % CACERT) >+ root_logger.warning( >+ cert_summary("Existing CA cert:", existing_ca_cert)) >+ root_logger.warning( >+ cert_summary("Retrieved CA cert:", new_ca_cert)) >+ if override: >+ root_logger.warning("Overriding existing CA cert\n") >+ elif not ask or not user_input( >+ "Do you want to replace the local certificate with the CA\n" >+ "certificate retrieved from the IPA server?", True): >+ raise errors.CertificateInvalidError(name='Retrieved CA') >+ else: >+ root_logger.debug( >+ "Existing CA cert and Retrieved CA cert are identical") >+ os.remove(ca_file) >+ >+ >+def get_ca_cert(fstore, options, server, basedn): >+ ''' >+ Examine the different options and determine a method for obtaining >+ the CA cert. >+ >+ If successful the CA cert will have been written into CACERT. >+ >+ Raises errors.NoCertificateError if not successful. >+ >+ The logic for determining how to load the CA cert is as follow: >+ >+ In the OTP case (not -p and -w): >+ >+ 1. load from user supplied cert file >+ 2. else load from HTTP >+ >+ In the 'user_auth' case ((-p and -w) or interactive): >+ >+ 1. load from user supplied cert file >+ 2. load from LDAP using SASL/GSS/Krb5 auth >+ (provides mutual authentication, integrity and security) >+ 3. if LDAP failed and interactive ask for permission to >+ use insecure HTTP (default: No) >+ >+ In the unattended case: >+ >+ 1. load from user supplied cert file >+ 2. load from HTTP if --force specified else fail >+ >+ In all cases if HTTP is used emit warning message >+ ''' >+ >+ ca_file = CACERT + ".new" >+ >+ def ldap_url(): >+ return urlparse.urlunparse(('ldap', ipautil.format_netloc(server), >+ '', '', '', '')) >+ >+ def file_url(): >+ return urlparse.urlunparse(('file', '', options.ca_cert_file, >+ '', '', '')) >+ >+ def http_url(): >+ return urlparse.urlunparse(('http', ipautil.format_netloc(server), >+ '/ipa/config/ca.crt', '', '', '')) >+ >+ >+ interactive = not options.unattended >+ otp_auth = options.principal is None and options.password is not None >+ existing_ca_cert = None >+ >+ if options.ca_cert_file: >+ url = file_url() >+ try: >+ get_ca_cert_from_file(url) >+ except Exception, e: >+ root_logger.debug(e) >+ raise errors.NoCertificateError(entry=url) >+ root_logger.debug("CA cert provided by user, use it!") >+ else: >+ if os.path.exists(CACERT): >+ if os.path.isfile(CACERT): >+ try: >+ existing_ca_cert = x509.load_certificate_from_file(CACERT) >+ except Exception, e: >+ raise errors.FileError(reason=u"Unable to load existing" + >+ " CA cert '%s': %s" % (CACERT, e)) >+ else: >+ raise errors.FileError(reason=u"Existing ca cert '%s' is " + >+ "not a plain file" % (CACERT)) >+ >+ if otp_auth: >+ if existing_ca_cert: >+ root_logger.info("OTP case, CA cert preexisted, use it") >+ else: >+ url = http_url() >+ override = not interactive >+ if interactive and not user_input( >+ "Do you want download the CA cert from " + url + " ?\n" >+ "(this is INSECURE)", False): >+ raise errors.NoCertificateError(message=u"HTTP certificate" >+ " download declined by user") >+ try: >+ get_ca_cert_from_http(url, ca_file, override) >+ except Exception, e: >+ root_logger.debug(e) >+ raise errors.NoCertificateError(entry=url) >+ >+ try: >+ validate_new_ca_cert(existing_ca_cert, ca_file, >+ False, override) >+ except Exception, e: >+ os.unlink(ca_file) >+ raise >+ else: >+ # Auth with user credentials >+ url = ldap_url() >+ try: >+ get_ca_cert_from_ldap(url, basedn, ca_file) >+ try: >+ validate_new_ca_cert(existing_ca_cert, >+ ca_file, interactive) >+ except Exception, e: >+ os.unlink(ca_file) >+ raise >+ except errors.NoCertificateError, e: >+ root_logger.debug(str(e)) >+ url = http_url() >+ if existing_ca_cert: >+ root_logger.warning( >+ "Unable to download CA cert from LDAP\n" >+ "but found preexisting cert, using it.\n") >+ elif interactive and not user_input( >+ "Unable to download CA cert from LDAP.\n" >+ "Do you want download the CA cert form " + url + "?\n" >+ "(this is INSECURE)", False): >+ raise errors.NoCertificateError(message=u"HTTP " >+ "certificate download declined by user") >+ elif not interactive and not options.force: >+ root_logger.error( >+ "In unattended mode without a One Time Password " >+ "(OTP) or without --ca-cert-file\nYou must specify" >+ " --force to retrieve the CA cert using HTTP") >+ raise errors.NoCertificateError(message=u"HTTP " >+ "certificate download requires --force") >+ else: >+ try: >+ get_ca_cert_from_http(url, ca_file) >+ except Exception, e: >+ root_logger.debug(e) >+ raise errors.NoCertificateError(entry=url) >+ try: >+ validate_new_ca_cert(existing_ca_cert, >+ ca_file, interactive) >+ except Exception, e: >+ os.unlink(ca_file) >+ raise >+ except Exception, e: >+ root_logger.debug(str(e)) >+ raise errors.NoCertificateError(entry=url) >+ >+ >+ # We should have a cert now, move it to the canonical place >+ if os.path.exists(ca_file): >+ os.rename(ca_file, CACERT) >+ elif existing_ca_cert is None: >+ raise errors.InternalError(u"expected CA cert file '%s' to " >+ u"exist, but it's absent" % (ca_file)) >+ >+ >+ # Make sure the file permissions are correct >+ try: >+ os.chmod(CACERT, 0644) >+ except Exception, e: >+ raise errors.FileError(reason=u"Unable set permissions on ca " >+ u"cert '%s': %s" % (CACERT, e)) >+ >+ > def install(options, env, fstore, statestore): > dnsok = False > >@@ -1111,7 +1467,7 @@ def install(options, env, fstore, statestore): > # Create the discovery instance > ds = ipadiscovery.IPADiscovery() > >- ret = ds.search(domain=options.domain, server=options.server, hostname=hostname) >+ ret = ds.search(domain=options.domain, server=options.server, hostname=hostname, ca_cert_path=get_cert_path(options.ca_cert_file)) > > if ret == ipadiscovery.BAD_HOST_CONFIG: > print >>sys.stderr, "Can't get the fully qualified name of this host" >@@ -1132,7 +1488,7 @@ def install(options, env, fstore, statestore): > print "DNS discovery failed to determine your DNS domain" > cli_domain = user_input("Provide the domain name of your IPA server (ex: example.com)", allow_empty = False) > root_logger.debug("will use domain: %s\n", cli_domain) >- ret = ds.search(domain=cli_domain, server=options.server, hostname=hostname) >+ ret = ds.search(domain=cli_domain, server=options.server, hostname=hostname, ca_cert_path=get_cert_path(options.ca_cert_file)) > > if not cli_domain: > if ds.getDomainName(): >@@ -1153,7 +1509,7 @@ def install(options, env, fstore, statestore): > print "DNS discovery failed to find the IPA Server" > cli_server = user_input("Provide your IPA server name (ex: ipa.example.com)", allow_empty = False) > root_logger.debug("will use server: %s\n", cli_server) >- ret = ds.search(domain=cli_domain, server=cli_server, hostname=hostname) >+ ret = ds.search(domain=cli_domain, server=cli_server, hostname=hostname, ca_cert_path=get_cert_path(options.ca_cert_file)) > else: > dnsok = True > if not cli_server: >@@ -1171,6 +1527,11 @@ def install(options, env, fstore, statestore): > print "Note: This is not an error if anonymous access has been explicitly restricted." > ret = 0 > >+ if ret == ipadiscovery.NO_TLS_LDAP: >+ print "Warning: The LDAP server requires TLS is but we do not have the CA." >+ print "Proceeding without strict verification." >+ ret = 0 >+ > if ret != 0: > print >>sys.stderr, "Failed to verify that "+cli_server+" is an IPA Server." > print >>sys.stderr, "This may mean that the remote server is not up or is not reachable" >@@ -1226,20 +1587,6 @@ def install(options, env, fstore, statestore): > options.principal = user_input("User authorized to enroll computers", allow_empty=False) > root_logger.debug("will use principal: %s\n", options.principal) > >- # Get the CA certificate >- try: >- # Remove anything already there so that wget doesn't use its >- # too-clever renaming feature >- os.remove("/etc/ipa/ca.crt") >- except: >- pass >- >- try: >- run(["/usr/bin/wget", "-O", "/etc/ipa/ca.crt", "http://%s/ipa/config/ca.crt" % ipautil.format_netloc(cli_server)]) >- except CalledProcessError, e: >- print 'Retrieving CA from %s failed.\n%s' % (cli_server, str(e)) >- return CLIENT_INSTALL_ERROR >- > if not options.on_master: > nolog = tuple() > # First test out the kerberos configuration >@@ -1320,6 +1667,15 @@ def install(options, env, fstore, statestore): > join_args.append(password) > nolog = (password,) > >+ # Get the CA certificate >+ try: >+ os.environ['KRB5_CONFIG'] = env['KRB5_CONFIG'] >+ get_ca_cert(fstore, options, cli_server, cli_basedn) >+ del os.environ['KRB5_CONFIG'] >+ except Exception, e: >+ root_logger.error("Cannot obtain CA certificate\n%s", e) >+ return CLIENT_INSTALL_ERROR >+ > # Now join the domain > (stdout, stderr, returncode) = run(join_args, raiseonerr=False, env=env, nolog=nolog) > >@@ -1363,7 +1719,7 @@ def install(options, env, fstore, statestore): > > # Add the CA to the default NSS database and trust it > try: >- run(["/usr/bin/certutil", "-A", "-d", "/etc/pki/nssdb", "-n", "IPA CA", "-t", "CT,C,C", "-a", "-i", "/etc/ipa/ca.crt"]) >+ run(["/usr/bin/certutil", "-A", "-d", "/etc/pki/nssdb", "-n", "IPA CA", "-t", "CT,C,C", "-a", "-i", CACERT]) > except CalledProcessError, e: > print >>sys.stderr, "Failed to add CA to the default NSS database." > return CLIENT_INSTALL_ERROR >diff --git a/ipa-client/ipaclient/ipadiscovery.py b/ipa-client/ipaclient/ipadiscovery.py >index 86bef28..4fd397e 100644 >--- a/ipa-client/ipaclient/ipadiscovery.py >+++ b/ipa-client/ipaclient/ipadiscovery.py >@@ -27,12 +27,14 @@ from ldap import LDAPError > from ipapython.ipautil import run, CalledProcessError, valid_ip, get_ipa_basedn, \ > realm_to_suffix, format_netloc, parse_items > >+CACERT = '/etc/ipa/ca.crt' > > NOT_FQDN = -1 > NO_LDAP_SERVER = -2 > REALM_NOT_FOUND = -3 > NOT_IPA_SERVER = -4 > NO_ACCESS_TO_LDAP = -5 >+NO_TLS_LDAP = -6 > BAD_HOST_CONFIG = -10 > UNKNOWN_ERROR = -15 > >@@ -105,7 +107,7 @@ class IPADiscovery: > domain = domain[p+1:] > return (None, None) > >- def search(self, domain = "", server = "", hostname=None): >+ def search(self, domain = "", server = "", hostname=None, ca_cert_path=None): > qname = "" > results = [] > result = [] >@@ -177,14 +179,14 @@ class IPADiscovery: > ldapaccess = True > for server in servers: > # check ldap now >- ldapret = self.ipacheckldap(server, self.realm) >+ ldapret = self.ipacheckldap(server, self.realm, ca_cert_path=ca_cert_path) > > if ldapret[0] == 0: > self.server = ldapret[1] > self.realm = ldapret[2] > break > >- if ldapret[0] == NO_ACCESS_TO_LDAP: >+ if ldapret[0] == NO_ACCESS_TO_LDAP or ldapret[0] == NO_TLS_LDAP: > ldapaccess = False > > # If one of LDAP servers checked rejects access (may be anonymous >@@ -205,12 +207,10 @@ class IPADiscovery: > > return ldapret[0] > >- def ipacheckldap(self, thost, trealm): >+ def ipacheckldap(self, thost, trealm, ca_cert_path=None): > """ > Given a host and kerberos realm verify that it is an IPA LDAP >- server hosting the realm. The connection is an SSL connection >- so the remote IPA CA cert must be available at >- http://HOST/ipa/config/ca.crt >+ server hosting the realm. > > Returns a list [errno, host, realm] or an empty list on error. > Errno is an error number: >@@ -228,29 +228,16 @@ class IPADiscovery: > > i = 0 > >- # Get the CA certificate >- try: >- # Create TempDir >- temp_ca_dir = tempfile.mkdtemp() >- except OSError, e: >- raise RuntimeError("Creating temporary directory failed: %s" % str(e)) >- >- try: >- run(["/usr/bin/wget", "-O", "%s/ca.crt" % temp_ca_dir, "-T", "15", "-t", "2", >- "http://%s/ipa/config/ca.crt" % format_netloc(thost)]) >- except CalledProcessError, e: >- root_logger.debug('Retrieving CA from %s failed.\n%s' % (thost, str(e))) >- return [NOT_IPA_SERVER] >- > #now verify the server is really an IPA server > try: > root_logger.debug("Init ldap with: ldap://"+format_netloc(thost, 389)) > lh = ldap.initialize("ldap://"+format_netloc(thost, 389)) >- ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, True) >- ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, "%s/ca.crt" % temp_ca_dir) >+ if ca_cert_path: >+ ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, True) >+ ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, ca_cert_path) >+ lh.set_option(ldap.OPT_X_TLS_DEMAND, True) >+ lh.start_tls_s() > lh.set_option(ldap.OPT_PROTOCOL_VERSION, 3) >- lh.set_option(ldap.OPT_X_TLS_DEMAND, True) >- lh.start_tls_s() > lh.simple_bind_s("","") > > # get IPA base DN >@@ -301,14 +288,16 @@ class IPADiscovery: > root_logger.debug("LDAP Error: Anonymous acces not allowed") > return [NO_ACCESS_TO_LDAP] > >+ # We should only get UNWILLING_TO_PERFORM if the remote LDAP server >+ # has minssf > 0 and we have attempted a non-TLS connection. >+ if ca_cert_path is None and isinstance(err, ldap.UNWILLING_TO_PERFORM): >+ root_logger.debug("LDAP server returned UNWILLING_TO_PERFORM. This likely means that minssf is enabled") >+ return [NO_TLS_LDAP] >+ > root_logger.error("LDAP Error: %s: %s" % > (err.args[0]['desc'], err.args[0].get('info', ''))) > return [UNKNOWN_ERROR] > >- finally: >- os.remove("%s/ca.crt" % temp_ca_dir) >- os.rmdir(temp_ca_dir) >- > > def ipadnssearchldap(self, tdomain): > servers = "" >diff --git a/ipa-client/man/ipa-client-install.1 b/ipa-client/man/ipa-client-install.1 >index cf3db2e..1e5b295 100644 >--- a/ipa-client/man/ipa-client-install.1 >+++ b/ipa-client/man/ipa-client-install.1 >@@ -88,6 +88,14 @@ Print debugging information to stdout > .TP > \fB\-U\fR, \fB\-\-unattended\fR > Unattended installation. The user will not be prompted. >+.TP >+\fB\-\-ca-cert-file\fR=\fICA_FILE\fR >+Do not attempt to acquire the IPA CA certificate via automated means, >+instead use the CA certificate found locally in in \fICA_FILE\fR. The >+\fICA_FILE\fR must be an absolute path to a PEM formatted certificate >+file. The CA certificate found in \fICA_FILE\fR is considered >+authoritative and will be installed without checking to see if it's >+valid for the IPA domain. > > .SS "SSSD OPTIONS" > .TP >diff --git a/ipalib/errors.py b/ipalib/errors.py >index df4ab41..c410be9 100644 >--- a/ipalib/errors.py >+++ b/ipalib/errors.py >@@ -1574,6 +1574,22 @@ class DependentEntry(ExecutionError): > errno = 4307 > format = _('%(key)s cannot be deleted because %(label)s %(dependent)s requires it') > >+class CertificateInvalidError(CertificateError): >+ """ >+ **4310** Raised when a certificate is not valid >+ >+ For example: >+ >+ >>> raise CertificateInvalidError(name=_(u'CA')) >+ Traceback (most recent call last): >+ ... >+ CertificateInvalidError: CA certificate is not valid >+ >+ """ >+ >+ errno = 4310 >+ format = _('%(name)s certificate is not valid') >+ > > ############################################################################## > # 5000 - 5999: Generic errors >diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py >index a14e03f..715d855 100644 >--- a/ipapython/ipautil.py >+++ b/ipapython/ipautil.py >@@ -1455,3 +1455,39 @@ def make_sshfp(key): > else: > return > return '%d 1 %s' % (algo, fp) >+ >+def convert_ldap_error(exc): >+ """ >+ Make LDAP exceptions prettier. >+ >+ Some LDAP exceptions have a dict with descriptive information, if >+ this exception has a dict extract useful information from it and >+ format it into something usable and return that. If the LDAP >+ exception does not have an information dict then return the name >+ of the LDAP exception. >+ >+ If the exception is not an LDAP exception then convert the >+ exception to a string and return that instead. >+ """ >+ if isinstance(exc, ldap.LDAPError): >+ name = exc.__class__.__name__ >+ >+ if len(exc.args): >+ d = exc.args[0] >+ if isinstance(d, dict): >+ desc = d.get('desc', '').strip() >+ info = d.get('info', '').strip() >+ if desc and info: >+ return '%s %s' % (desc, info) >+ elif desc: >+ return desc >+ elif info: >+ return info >+ else: >+ return name >+ else: >+ return name >+ else: >+ return name >+ else: >+ return str(exc) >diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py >index d25a471..ed0c178 100644 >--- a/ipaserver/install/certs.py >+++ b/ipaserver/install/certs.py >@@ -227,7 +227,10 @@ class CertDB(object): > self.subject_base = "O=IPA" > self.subject_format = "CN=%%s,%s" % self.subject_base > >- self.cacert_name = get_ca_nickname(self.realm) >+ if self.self_signed_ca: >+ self.cacert_name = get_ca_nickname(self.realm, 'CN=%s Certificate Authority') >+ else: >+ self.cacert_name = get_ca_nickname(self.realm) > self.valid_months = "120" > self.keysize = "1024" > >-- >1.7.11.7 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 876307
:
657525
|
657526
|
657527
| 657528