def reload_systemwide_ca_store(self): try: ipautil.run([paths.UPDATE_CA_TRUST]) except CalledProcessError, e: root_logger.error( "Could not update systemwide CA trust database: %s", e) return False
def store_session_cookie(self, cookie_header): ''' Given the contents of a Set-Cookie header scan the header and extract each cookie contained within until the session cookie is located. Examine the session cookie if the domain and path are specified, if not update the cookie with those values from the request URL. Then write the session cookie into the key store for the principal. If the cookie header is None or the session cookie is not present in the header no action is taken. Context Dependencies: The per thread context is expected to contain: principal The current pricipal the HTTP request was issued for. request_url The URL of the HTTP request. ''' if cookie_header is None: return principal = getattr(context, 'principal', None) request_url = getattr(context, 'request_url', None) root_logger.debug("received Set-Cookie '%s'", cookie_header) # Search for the session cookie try: session_cookie = Cookie.get_named_cookie_from_string(cookie_header, COOKIE_NAME, request_url) except Exception, e: root_logger.error("unable to parse cookie header '%s': %s", cookie_header, e) return
def setup_pkinit(self): ca_db = certs.CertDB(self.realm, host_name=self.fqdn, subject_base=self.subject_base) if self.pkcs12_info: ca_db.install_pem_from_p12(self.pkcs12_info[0], self.pkcs12_info[1], paths.KDC_CERT) ca_db.install_key_from_p12(self.pkcs12_info[0], self.pkcs12_info[1], paths.KDC_KEY) else: subject = str(DN(('cn', self.fqdn), self.subject_base)) krbtgt = "krbtgt/" + self.realm + "@" + self.realm certpath = (paths.KDC_CERT, paths.KDC_KEY) try: reqid = certmonger.request_cert(certpath, u'KDC-Cert', subject, krbtgt, dns=self.fqdn, storage='FILE', profile='KDCs_PKINIT_Certs') except dbus.DBusException as e: # if the certificate is already tracked, ignore the error name = e.get_dbus_name() if name != 'org.fedorahosted.certmonger.duplicate': root_logger.error("Failed to initiate the request: %s", e) return try: certmonger.wait_for_request(reqid) except RuntimeError as e: root_logger.error("Failed to wait for request: %s", e) # Finally copy the cacert in the krb directory so we don't # have any selinux issues with the file context shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM)
def _create_socket(self): ssl_enable_renegotiation = getattr( ssl, 'SSL_ENABLE_RENEGOTIATION', 20) ssl_require_safe_negotiation = getattr( ssl,'SSL_REQUIRE_SAFE_NEGOTIATION', 21) ssl_renegotiate_requires_xtn = getattr( ssl, 'SSL_RENEGOTIATE_REQUIRES_XTN', 2) # Create the socket here so we can do things like let the caller # override the NSS callbacks self.sock = ssl.SSLSocket(family=self.family) self.sock.set_ssl_option(ssl.SSL_SECURITY, True) self.sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True) try: self.sock.set_ssl_version_range(self.tls_version_min, self.tls_version_max) except NSPRError: root_logger.error('Failed to set TLS range to %s, %s' % (self.tls_version_min, self.tls_version_max)) raise self.sock.set_ssl_option(ssl_require_safe_negotiation, False) self.sock.set_ssl_option(ssl_enable_renegotiation, ssl_renegotiate_requires_xtn) # Provide a callback which notifies us when the SSL handshake is complete self.sock.set_handshake_callback(self.handshake_callback) # Provide a callback to verify the servers certificate self.sock.set_auth_certificate_callback(auth_certificate_callback, nss.get_default_certdb()) self.sock.set_hostname(self.host)
def stop_tracking(secdir=None, request_id=None, nickname=None, certfile=None): """ Stop tracking the current request using either the request_id or nickname. Returns True or False """ if request_id is None and nickname is None and certfile is None: raise RuntimeError('One of request_id, nickname and certfile is' ' required.') if secdir is not None and certfile is not None: raise RuntimeError("Can't specify both secdir and certfile.") criteria = dict() if secdir: criteria['cert-database'] = secdir if request_id: criteria['nickname'] = request_id if nickname: criteria['cert-nickname'] = nickname if certfile: criteria['cert-file'] = certfile try: request = _get_request(criteria) except RuntimeError as e: root_logger.error('Failed to get request: %s' % e) raise if request: request.parent.obj_if.remove_request(request.path)
def __enable(self): if self.get_state("enabled") is None: self.backup_state("enabled", self.is_running()) self.backup_state("named-regular-enabled", self.named_regular.is_running()) # We do not let the system start IPA components on its own, # Instead we reply on the IPA init script to start only enabled # components as found in our LDAP configuration tree try: self.ldap_enable('DNS', self.fqdn, self.dm_password, self.suffix) except errors.DuplicateEntry: # service already exists (forced DNS reinstall) # don't crash, just report error root_logger.error("DNS service already exists") # disable named, we need to run named-pkcs11 only if self.get_state("named-regular-running") is None: # first time store status self.backup_state("named-regular-running", self.named_regular.is_running()) try: self.named_regular.stop() except Exception as e: root_logger.debug("Unable to stop named (%s)", e) try: self.named_regular.mask() except Exception as e: root_logger.debug("Unable to mask named (%s)", e)
def release_ipa_ccache(ccache_name): ''' Stop using the current request's ccache. * Remove KRB5CCNAME from the enviroment * Remove the ccache file from the file system Note, we do not demand any of these elements exist, but if they do we'll remove them. ''' if 'KRB5CCNAME' in os.environ: if ccache_name != os.environ['KRB5CCNAME']: root_logger.error('release_ipa_ccache: ccache_name (%s) != KRB5CCNAME environment variable (%s)', ccache_name, os.environ['KRB5CCNAME']) del os.environ['KRB5CCNAME'] else: root_logger.debug('release_ipa_ccache: KRB5CCNAME environment variable not set') scheme, name = krb5_parse_ccache(ccache_name) if scheme == 'FILE': if os.path.exists(name): try: os.unlink(name) except Exception as e: root_logger.error('unable to delete session ccache file "%s", %s', name, e) else: raise ValueError('ccache scheme "%s" unsupported (%s)', scheme, ccache_name)
def retrieve_entries_without_sid(api): """ Retrieve a list of entries without assigned SIDs. :returns: a list of entries or an empty list if an error occurs """ # The filter corresponds to ipa_sidgen_task.c LDAP search filter filter = '(&(objectclass=ipaobject)(!(objectclass=mepmanagedentry))' \ '(|(objectclass=posixaccount)(objectclass=posixgroup)' \ '(objectclass=ipaidobject))(!(ipantsecurityidentifier=*)))' base_dn = api.env.basedn try: root_logger.debug( "Searching for objects with missing SID with " "filter=%s, base_dn=%s", filter, base_dn) entries, _truncated = api.Backend.ldap2.find_entries( filter=filter, base_dn=base_dn, attrs_list=['']) return entries except errors.NotFound: # All objects have SIDs assigned pass except (errors.DatabaseError, errors.NetworkError) as e: root_logger.error( "Could not retrieve a list of objects that need a SID " "identifier assigned: %s", e) return []
def retrieve_potential_adtrust_agents(api): """ Retrieve a sorted list of potential AD trust agents :param api: initialized API instance :returns: sorted list of FQDNs of masters which are not AD trust agents """ try: # Search only masters which have support for domain levels # because only these masters will have SSSD recent enough # to support AD trust agents dl_enabled_masters = api.Command.server_find( ipamindomainlevel=DOMAIN_LEVEL_0, all=True)['result'] except (errors.DatabaseError, errors.NetworkError) as e: root_logger.error( "Could not retrieve a list of existing IPA masters: %s", e) return try: # search for existing AD trust agents adtrust_agents = api.Command.server_find( servrole=u'AD trust agent', all=True)['result'] except (errors.DatabaseError, errors.NetworkError) as e: root_logger.error("Could not retrieve a list of adtrust agents: %s", e) return dl_enabled_master_cns = {m['cn'][0] for m in dl_enabled_masters} adtrust_agents_cns = {m['cn'][0] for m in adtrust_agents} potential_agents_cns = dl_enabled_master_cns - adtrust_agents_cns # remove the local host from the potential agents since it will be set up # by adtrustinstance configuration code potential_agents_cns -= {api.env.host} return sorted(potential_agents_cns)
def request_cert( nssdb, nickname, subject, principal, passwd_fname=None, dns=None): """ Execute certmonger to request a server certificate. ``dns`` A sequence of DNS names to appear in SAN request extension. """ cm = _certmonger() ca_path = cm.obj_if.find_ca_by_nickname('IPA') if not ca_path: raise RuntimeError('IPA CA not found') request_parameters = dict(KEY_STORAGE='NSSDB', CERT_STORAGE='NSSDB', CERT_LOCATION=nssdb, CERT_NICKNAME=nickname, KEY_LOCATION=nssdb, KEY_NICKNAME=nickname, SUBJECT=subject, PRINCIPAL=[principal], CA=ca_path) if dns is not None and len(dns) > 0: request_parameters['DNS'] = dns if passwd_fname: request_parameters['KEY_PIN_FILE'] = passwd_fname result = cm.obj_if.add_request(request_parameters) try: if result[0]: request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF, DBUS_CM_IF, True) else: raise RuntimeError('add_request() returned False') except Exception as e: root_logger.error('Failed to create a new request: {error}' .format(error=e)) raise return request.obj_if.get_nickname()
def __enable(self): try: self.ldap_enable('DNSKeyExporter', self.fqdn, self.dm_password, self.suffix) except errors.DuplicateEntry: root_logger.error("DNSKeyExporter service already exists")
def decorated(installer): success = False try: func(installer) success = True except KeyboardInterrupt: ds = installer._ds print("\nCleaning up...") if ds: print("Removing configuration for %s instance" % ds.serverid) ds.stop() if ds.serverid: try: dsinstance.remove_ds_instance(ds.serverid) except ipautil.CalledProcessError: root_logger.error("Failed to remove DS instance. You " "may need to remove instance data " "manually") raise ScriptError() finally: if not success and installer._installation_cleanup: # Do a cautious clean up as we don't know what failed and # what is the state of the environment try: installer._fstore.restore_file(paths.HOSTS) except Exception: pass
def __setup_resolv_conf(self): if not self.fstore.has_file(RESOLV_CONF): self.fstore.backup_file(RESOLV_CONF) resolv_txt = "search "+self.domain+"\n" for ip_address in self.ip_addresses: if ip_address.version == 4: resolv_txt += "nameserver 127.0.0.1\n" break for ip_address in self.ip_addresses: if ip_address.version == 6: resolv_txt += "nameserver ::1\n" break try: resolv_fd = open(RESOLV_CONF, 'w') resolv_fd.seek(0) resolv_fd.truncate(0) resolv_fd.write(resolv_txt) resolv_fd.close() except IOError as e: root_logger.error('Could not write to resolv.conf: %s', e) else: # python DNS might have global resolver cached in this variable # we have to re-initialize it because resolv.conf has changed dns.resolver.default_resolver = None
def decorated(*args, **kwargs): success = False try: try: func(*args, **kwargs) except BaseException: destroy_private_ccache() raise success = True except KeyboardInterrupt: global ds print "\nCleaning up..." if ds: print "Removing configuration for %s instance" % ds.serverid ds.stop() if ds.serverid: try: dsinstance.remove_ds_instance(ds.serverid) except ipautil.CalledProcessError: root_logger.error("Failed to remove DS instance. You " "may need to remove instance data " "manually") sys.exit(1) finally: global installation_cleanup if not success and installation_cleanup: # Do a cautious clean up as we don't know what failed and # what is the state of the environment try: fstore.restore_file(paths.HOSTS) except: pass
def execute(self, **options): root_logger.debug("Upgrading referential integrity plugin configuration") ldap = self.api.Backend.ldap2 try: entry = ldap.get_entry(self.referint_dn) except errors.NotFound: root_logger.error("Referential integrity configuration not found") return False, [] referint_membership_attrs = [] root_logger.debug("Initial value: %s", repr(entry)) # nsslapd-pluginArg0 -> referint-update-delay update_delay = entry.get('nsslapd-pluginArg0') if update_delay: root_logger.debug("add: referint-update-delay: %s", update_delay) entry['referint-update-delay'] = update_delay entry['nsslapd-pluginArg0'] = None else: root_logger.info("Plugin already uses new style, skipping") return False, [] # nsslapd-pluginArg1 -> referint-logfile logfile = entry.get('nsslapd-pluginArg1') if logfile: root_logger.debug("add: referint-logfile: %s", logfile) entry['referint-logfile'] = logfile entry['nsslapd-pluginArg1'] = None # nsslapd-pluginArg2 -> referint-logchanges logchanges = entry.get('nsslapd-pluginArg2') if logchanges: root_logger.debug("add: referint-logchanges: %s", logchanges) entry['referint-logchanges'] = logchanges entry['nsslapd-pluginArg2'] = None # nsslapd-pluginArg3..N -> referint-membership-attr [3..N] for key in entry.keys(): if key.lower().startswith('nsslapd-pluginarg'): arg_val = entry.single_value[key] if arg_val: referint_membership_attrs.append(arg_val) entry[key] = None if referint_membership_attrs: # entry['referint-membership-attr'] is None, plugin doesn't allow # mixing old and new style entry['referint-membership-attr'] = referint_membership_attrs root_logger.debug("Final value: %s", repr(entry)) try: ldap.update_entry(entry) except errors.EmptyModlist: root_logger.debug("No modifications required") return False, [] return False, []
def auth_certificate_callback(sock, check_sig, is_server, certdb): cert_is_valid = False cert = sock.get_peer_certificate() pin_args = sock.get_pkcs11_pin_arg() if pin_args is None: pin_args = () # Define how the cert is being used based upon the is_server flag. This may # seem backwards, but isn't. If we're a server we're trying to validate a # client cert. If we're a client we're trying to validate a server cert. if is_server: intended_usage = nss.certificateUsageSSLClient else: intended_usage = nss.certificateUsageSSLServer try: # If the cert fails validation it will raise an exception, the errno attribute # will be set to the error code matching the reason why the validation failed # and the strerror attribute will contain a string describing the reason. approved_usage = cert.verify_now(certdb, check_sig, intended_usage, *pin_args) except Exception as e: root_logger.error('cert validation failed for "%s" (%s)', cert.subject, e.strerror) # pylint: disable=no-member cert_is_valid = False return cert_is_valid root_logger.debug( "approved_usage = %s intended_usage = %s", ", ".join(nss.cert_usage_flags(approved_usage)), ", ".join(nss.cert_usage_flags(intended_usage)), ) # Is the intended usage a proper subset of the approved usage cert_is_valid = bool(approved_usage & intended_usage) # If this is a server, we're finished if is_server or not cert_is_valid: root_logger.debug('cert valid %s for "%s"', cert_is_valid, cert.subject) return cert_is_valid # Certificate is OK. Since this is the client side of an SSL # connection, we need to verify that the name field in the cert # matches the desired hostname. This is our defense against # man-in-the-middle attacks. hostname = sock.get_hostname() try: # If the cert fails validation it will raise an exception cert_is_valid = cert.verify_hostname(hostname) except Exception as e: root_logger.error( 'failed verifying socket hostname "%s" matches cert subject "%s" (%s)', hostname, cert.subject, e.strerror ) # pylint: disable=no-member cert_is_valid = False return cert_is_valid root_logger.debug('cert valid %s for "%s"', cert_is_valid, cert.subject) return cert_is_valid
def application(environ, start_response): try: index = get_plugin_index() status = "200 OK" except Exception, e: root_logger.error("plugin index generation failed: %s" % e) status = "200 OK" index = get_failed()
def parse(self): ldif.LDIFParser.parse(self) # check if there are any remaining modifications remaining_changes = set(self.modifications.keys()) - self.dn_updated for dn in remaining_changes: root_logger.error( "DN: %s does not exists or haven't been updated", dn)
def untrack_server_cert(self, nickname): """ Tell certmonger to stop tracking the given certificate nickname. """ try: certmonger.stop_tracking(self.secdir, nickname=nickname) except RuntimeError as e: root_logger.error("certmonger failed to stop tracking certificate: %s" % str(e))
def remove_file(filename): """ Remove a file and log any exceptions raised. """ try: if os.path.lexists(filename): os.unlink(filename) except Exception as e: root_logger.error('Error removing %s: %s' % (filename, str(e)))
def execute(self, **options): ldap = self.api.Backend.ldap2 old_style_plugin_search_filter = ( "(&" "(objectclass=nsSlapdPlugin)" "(nsslapd-pluginId=NSUniqueAttr)" "(nsslapd-pluginPath=libattr-unique-plugin)" "(nsslapd-pluginarg0=*)" # only entries with old configuration ")" ) try: entries, truncated = ldap.find_entries( filter=old_style_plugin_search_filter, base_dn=self.plugins_dn, ) except errors.NotFound: root_logger.debug("No uniqueness plugin entries with old style " "configuration found") return False, [] update_list = [] new_attributes = [ 'uniqueness-subtree-entries-oc', 'uniqueness-top-entry-oc', 'uniqueness-attribute-name', 'uniqueness-subtrees', 'uniqueness-across-all-subtrees', ] for entry in entries: # test for mixed configuration if any(attr in entry for attr in new_attributes): root_logger.critical("Mixed old and new style configuration " "for plugin %s. Plugin will not work. " "Skipping plugin migration, please fix it " "manually", entry.dn) continue root_logger.debug("Configuration of plugin %s will be migrated " "to new style", entry.dn) try: # detect which configuration was used arg0 = entry.get('nsslapd-pluginarg0') if '=' in arg0: update = self.__objectclass_style(entry) else: update = self.__subtree_style(entry) except ValueError as e: root_logger.error("Unable to migrate configuration of " "plugin %s (%s)", entry.dn, e) else: update_list.append(update) return False, update_list
def rmtree(path): """ Remove a directory structure and log any exceptions raised. """ try: if os.path.exists(path): shutil.rmtree(path) except Exception as e: root_logger.error('Error removing %s: %s' % (path, str(e)))
def remove_httpd_service_ipa_conf(self): """Remove systemd config for httpd service of IPA""" try: os.unlink(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF) except OSError as e: root_logger.error( 'Error removing %s: %s', paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, e )
def create_replica_config(dirman_password, filename, options): top_dir = None try: top_dir, dir = expand_replica_info(filename, dirman_password) except Exception, e: root_logger.error("Failed to decrypt or open the replica file.") print "ERROR: Failed to decrypt or open the replica file." print "Verify you entered the correct Directory Manager password." sys.exit(1)
def store_session_cookie(self, cookie_header): ''' Given the contents of a Set-Cookie header scan the header and extract each cookie contained within until the session cookie is located. Examine the session cookie if the domain and path are specified, if not update the cookie with those values from the request URL. Then write the session cookie into the key store for the principal. If the cookie header is None or the session cookie is not present in the header no action is taken. Context Dependencies: The per thread context is expected to contain: principal The current pricipal the HTTP request was issued for. request_url The URL of the HTTP request. ''' if cookie_header is None: return principal = getattr(context, 'principal', None) request_url = getattr(context, 'request_url', None) root_logger.debug("received Set-Cookie (%s)'%s'", type(cookie_header), cookie_header) if not isinstance(cookie_header, list): cookie_header = [cookie_header] # Search for the session cookie session_cookie = None try: for cookie in cookie_header: session_cookie = ( Cookie.get_named_cookie_from_string( cookie, COOKIE_NAME, request_url, timestamp=datetime.datetime.utcnow()) ) if session_cookie is not None: break except Exception as e: root_logger.error("unable to parse cookie header '%s': %s", cookie_header, e) return if session_cookie is None: return cookie_string = self._slice_session_cookie(session_cookie) root_logger.debug("storing cookie '%s' for principal %s", cookie_string, principal) try: update_persistent_client_session_data(principal, cookie_string) except Exception as e: # Not fatal, we just can't use the session cookie we were sent. pass
def __start(self): try: if self.get_state("running") is None: # first time store status self.backup_state("running", self.is_running()) self.restart() except Exception as e: root_logger.error("Named service failed to start (%s)", e) print("named service failed to start")
def execute(self, **options): ldap = self.api.Backend.ldap2 base_dn = DN(self.api.env.container_ranges, self.api.env.basedn) search_filter = ("(&(objectClass=ipaTrustedADDomainRange)" "(ipaRangeType=ipa-ad-trust-posix)" "(!(ipaBaseRID=0)))") root_logger.debug( "update_idrange_baserid: search for ipa-ad-trust-posix ID ranges " "with ipaBaseRID != 0" ) try: (entries, truncated) = ldap.find_entries( search_filter, ['ipabaserid'], base_dn, paged_search=True, time_limit=0, size_limit=0) except errors.NotFound: root_logger.debug("update_idrange_baserid: no AD domain " "range with posix attributes found") return False, [] except errors.ExecutionError as e: root_logger.error("update_idrange_baserid: cannot retrieve " "list of affected ranges: %s", e) return False, [] root_logger.debug("update_idrange_baserid: found %d " "idranges possible to update", len(entries)) error = False # Set the range type for entry in entries: entry['ipabaserid'] = 0 try: root_logger.debug("Updating existing idrange: %s" % (entry.dn)) ldap.update_entry(entry) root_logger.info("Done") except (errors.EmptyModlist, errors.NotFound): pass except errors.ExecutionError as e: root_logger.debug("update_idrange_type: cannot " "update idrange: %s", e) error = True if error: root_logger.error("update_idrange_baserid: error(s) " "detected during idrange baserid update") else: # All affected entries updated, exit the loop root_logger.debug("update_idrange_baserid: all affected " "idranges updated") return False, []
def reload_systemwide_ca_store(self): try: ipautil.run([paths.UPDATE_CA_TRUST]) except CalledProcessError as e: root_logger.error( "Could not update systemwide CA trust database: %s", e) return False else: root_logger.info("Systemwide CA database updated.") return True
def issue_ipa_ca_signed_pkinit_certs(self): try: self._call_certmonger() self._install_pkinit_ca_bundle() self.pkinit_enable() except RuntimeError as e: root_logger.error("PKINIT certificate request failed: %s", e) root_logger.error("Failed to configure PKINIT") self.stop_tracking_certs() self.issue_selfsigned_pkinit_certs()
def insert_ca_certs_into_systemwide_ca_store(self, ca_certs): new_cacert_path = paths.SYSTEMWIDE_IPA_CA_CRT if os.path.exists(new_cacert_path): try: os.remove(new_cacert_path) except OSError, e: root_logger.error( "Could not remove %s: %s", new_cacert_path, e) return False
def request_cert( certpath, nickname, subject, principal, passwd_fname=None, dns=None, ca='IPA', profile=None, pre_command=None, post_command=None, storage='NSSDB'): """ Execute certmonger to request a server certificate. ``dns`` A sequence of DNS names to appear in SAN request extension. """ if storage == 'FILE': certfile, keyfile = certpath else: certfile = certpath keyfile = certpath cm = _certmonger() ca_path = cm.obj_if.find_ca_by_nickname(ca) if not ca_path: raise RuntimeError('{} CA not found'.format(ca)) request_parameters = dict(KEY_STORAGE=storage, CERT_STORAGE=storage, CERT_LOCATION=certfile, CERT_NICKNAME=nickname, KEY_LOCATION=keyfile, KEY_NICKNAME=nickname, SUBJECT=subject, CA=ca_path) if principal: request_parameters['PRINCIPAL'] = [principal] if dns is not None and len(dns) > 0: request_parameters['DNS'] = dns if passwd_fname: request_parameters['KEY_PIN_FILE'] = passwd_fname if profile: request_parameters['ca-profile'] = profile certmonger_cmd_template = paths.CERTMONGER_COMMAND_TEMPLATE if pre_command: if not os.path.isabs(pre_command): pre_command = certmonger_cmd_template % (pre_command) request_parameters['cert-presave-command'] = pre_command if post_command: if not os.path.isabs(post_command): post_command = certmonger_cmd_template % (post_command) request_parameters['cert-postsave-command'] = post_command result = cm.obj_if.add_request(request_parameters) try: if result[0]: request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF, DBUS_CM_IF, True) else: raise RuntimeError('add_request() returned False') except Exception as e: root_logger.error('Failed to create a new request: {error}' .format(error=e)) raise return request.obj_if.get_nickname()
def setup_pkinit(self): if self.pkcs12_info: certs.install_pem_from_p12(self.pkcs12_info[0], self.pkcs12_info[1], paths.KDC_CERT) certs.install_key_from_p12(self.pkcs12_info[0], self.pkcs12_info[1], paths.KDC_KEY) else: subject = str(DN(('cn', self.fqdn), self.subject_base)) krbtgt = "krbtgt/" + self.realm + "@" + self.realm certpath = (paths.KDC_CERT, paths.KDC_KEY) try: prev_helper = None if self.master_fqdn is None: ca_args = [ paths.CERTMONGER_DOGTAG_SUBMIT, '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, '--certfile', paths.RA_AGENT_PEM, '--keyfile', paths.RA_AGENT_KEY, '--cafile', paths.IPA_CA_CRT, '--agent-submit' ] helper = " ".join(ca_args) prev_helper = certmonger.modify_ca_helper('IPA', helper) else: self._wait_for_replica_kdc_entry() certmonger.request_and_wait_for_cert( certpath, subject, krbtgt, dns=self.fqdn, storage='FILE', profile='KDCs_PKINIT_Certs') except dbus.DBusException as e: # if the certificate is already tracked, ignore the error name = e.get_dbus_name() if name != 'org.fedorahosted.certmonger.duplicate': root_logger.error("Failed to initiate the request: %s", e) return finally: if prev_helper is not None: certmonger.modify_ca_helper('IPA', prev_helper) # Finally copy the cacert in the krb directory so we don't # have any selinux issues with the file context shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) try: self.restart() except Exception: root_logger.critical("krb5kdc service failed to restart") raise
def get_base_dn(ldap_uri): """ Retrieve LDAP server base DN. """ try: conn = IPAdmin(ldap_uri=ldap_uri) conn.do_simple_bind(DN(), '') base_dn = get_ipa_basedn(conn) except Exception, e: root_logger.error('migration context search failed: %s' % e) return ''
def find_subject_base(self): """ Try to find the current value of certificate subject base. 1) Look in sysupgrade first 2) If no value is found there, look in DS (start DS if necessary) 3) If all fails, log loudly and return None Note that this method can only be executed AFTER the ipa server is configured, the api is initialized elsewhere and that a ticket already have been acquired. """ root_logger.debug( 'Trying to find certificate subject base in sysupgrade') subject_base = sysupgrade.get_upgrade_state('certmap.conf', 'subject_base') if subject_base: root_logger.debug( 'Found certificate subject base in sysupgrade: %s', subject_base) return subject_base root_logger.debug( 'Unable to find certificate subject base in sysupgrade') root_logger.debug('Trying to find certificate subject base in DS') ds_is_running = is_ds_running() if not ds_is_running: try: self.start() ds_is_running = True except ipautil.CalledProcessError as e: root_logger.error( 'Cannot start DS to find certificate ' 'subject base: %s', e) if ds_is_running: try: ret = api.Command['config_show']() subject_base = str( ret['result']['ipacertificatesubjectbase'][0]) root_logger.debug('Found certificate subject base in DS: %s', subject_base) except errors.PublicError as e: root_logger.error( 'Cannot connect to DS to find certificate ' 'subject base: %s', e) if subject_base: return subject_base root_logger.debug('Unable to find certificate subject base in ' 'certmap.conf') return None
def add_request_value(request_id, directive, value): """ Add a new directive to a certmonger request file. """ try: request = _get_request({'nickname': request_id}) except RuntimeError as e: root_logger.error('Failed to get request: %s' % e) raise if request: request.obj_if.modify({directive: value})
def remove_httpd_service_ipa_conf(self): """Remove systemd config for httpd service of IPA""" try: os.unlink(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF) except OSError as e: if e.errno == errno.ENOENT: root_logger.debug( 'Trying to remove %s but file does not exist', paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF) else: root_logger.error('Error removing %s: %s', paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, e)
def application(environ, start_response): try: index = get_plugin_index() status = '200 OK' except Exception as e: root_logger.error('plugin index generation failed: %s' % e) status = '200 OK' index = get_failed() headers = [('Content-type', 'application/javascript'), ('Content-Length', str(len(index)))] start_response(status, headers) return [index]
def _stop_private_conn(self): if self._proc: retcode = self._proc.poll() if retcode is not None: return self._proc.terminate() for _t in range(0, self.timeout, 5): retcode = self._proc.poll() if retcode is not None: return time.sleep(5) root_logger.error("Failed to stop certmonger.")
def bind(ldap_uri, base_dn, username, password): if not base_dn: root_logger.error('migration unable to get base dn') raise IOError(errno.EIO, 'Cannot get Base DN') bind_dn = DN(('uid', username), ('cn', 'users'), ('cn', 'accounts'), base_dn) try: conn = IPAdmin(ldap_uri=ldap_uri) conn.do_simple_bind(bind_dn, password) except (errors.ACIError, errors.DatabaseError, errors.NotFound), e: root_logger.error('migration invalid credentials for %s: %s' % (bind_dn, e)) raise IOError(errno.EPERM, 'Invalid LDAP credentials for user %s' % username)
def __upgrade(self): try: ld = ldapupdate.LDAPUpdate(dm_password='', ldapi=True) if len(self.files) == 0: self.files = ld.get_all_files(ldapupdate.UPDATES_DIR) self.modified = (ld.update(self.files) or self.modified) except ldapupdate.BadSyntax as e: root_logger.error('Bad syntax in upgrade %s', e) raise except Exception as e: # Bad things happened, return gracefully root_logger.error('Upgrade failed with %s', e) root_logger.debug('%s', traceback.format_exc()) raise RuntimeError(e)
def restore_hostname(self, fstore, statestore): old_hostname = statestore.get_state('network', 'hostname') if old_hostname is not None: try: self.set_hostname(old_hostname) except ipautil.CalledProcessError as e: root_logger.debug(traceback.format_exc()) root_logger.error( "Failed to restore this machine hostname to %s (%s).", old_hostname, e) filepath = paths.ETC_HOSTNAME if fstore.has_file(filepath): fstore.restore_file(filepath)
def uninstall(self): if self.is_configured(): self.print_msg("Unconfiguring directory server") enabled = self.restore_state("enabled") # Just eat this state if it exists self.restore_state("running") try: self.fstore.restore_file(paths.LIMITS_CONF) self.fstore.restore_file(paths.SYSCONFIG_DIRSRV) except ValueError as error: root_logger.debug(error) # disabled during IPA installation if enabled: self.enable() serverid = self.restore_state("serverid") if serverid is not None: self.stop_tracking_certificates(serverid) root_logger.debug("Removing DS instance %s" % serverid) try: remove_ds_instance(serverid) installutils.remove_keytab(paths.DS_KEYTAB) installutils.remove_ccache(run_as=DS_USER) except ipautil.CalledProcessError: root_logger.error("Failed to remove DS instance. You may " "need to remove instance data manually") # Just eat this state self.restore_state("user_exists") # Make sure some upgrade-related state is removed. This could cause # re-installation problems. self.restore_state('nsslapd-port') self.restore_state('nsslapd-security') self.restore_state('nsslapd-ldapiautobind') # If any dirsrv instances remain after we've removed ours then # (re)start them. for ds_instance in get_ds_instances(): try: services.knownservices.dirsrv.restart(ds_instance, wait=False) except Exception as e: root_logger.error('Unable to restart DS instance %s: %s', ds_instance, e)
def store_session_cookie(self, cookie_header): ''' Given the contents of a Set-Cookie header scan the header and extract each cookie contained within until the session cookie is located. Examine the session cookie if the domain and path are specified, if not update the cookie with those values from the request URL. Then write the session cookie into the key store for the principal. If the cookie header is None or the session cookie is not present in the header no action is taken. Context Dependencies: The per thread context is expected to contain: principal The current pricipal the HTTP request was issued for. request_url The URL of the HTTP request. ''' if cookie_header is None: return principal = getattr(context, 'principal', None) request_url = getattr(context, 'request_url', None) root_logger.debug("received Set-Cookie '%s'", cookie_header) # Search for the session cookie try: session_cookie = Cookie.get_named_cookie_from_string( cookie_header, COOKIE_NAME, request_url) except Exception as e: root_logger.error("unable to parse cookie header '%s': %s", cookie_header, e) return if session_cookie is None: return cookie_string = str(session_cookie) root_logger.debug("storing cookie '%s' for principal %s", cookie_string, principal) try: update_persistent_client_session_data(principal, cookie_string) except Exception as e: # Not fatal, we just can't use the session cookie we were sent. pass
def host_port_open(host, port, socket_type=socket.SOCK_STREAM, socket_timeout=None, log_errors=False): """ host: either hostname or IP address; if hostname is provided, port MUST be open on ALL resolved IPs returns True is port is open, False otherwise """ port_open = True # port has to be open on ALL resolved IPs for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket_type): af, socktype, proto, _canonname, sa = res s = None try: s = socket.socket(af, socktype, proto) if socket_timeout is not None: s.settimeout(socket_timeout) s.connect(sa) if socket_type == socket.SOCK_DGRAM: s.send('') s.recv(512) except socket.error: port_open = False if log_errors: msg = ('Failed to connect to port %(port)d %(proto)s on ' '%(addr)s' % dict(port=port, proto=PROTOCOL_NAMES[socket_type], addr=sa[0])) # Do not log udp failures as errors (to be consistent with # the rest of the code that checks for open ports) if socket_type == socket.SOCK_DGRAM: root_logger.warning(msg) else: root_logger.error(msg) finally: if s is not None: s.close() return port_open
def remove_httpd_service_ipa_conf(self): """Remove systemd config for httpd service of IPA""" try: os.unlink(paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF) except OSError as e: if e.errno == errno.ENOENT: root_logger.debug( 'Trying to remove %s but file does not exist', paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF) else: root_logger.error('Error removing %s: %s', paths.SYSTEMD_SYSTEM_HTTPD_IPA_CONF, e) return ipautil.run([paths.SYSTEMCTL, "--system", "daemon-reload"], raiseonerr=False)
def remove_ca_certs_from_systemwide_ca_store(self): result = True update = False # Remove CA cert from systemwide store for new_cacert_path in (paths.IPA_P11_KIT, paths.SYSTEMWIDE_IPA_CA_CRT): if not os.path.exists(new_cacert_path): continue try: os.remove(new_cacert_path) except OSError, e: root_logger.error("Could not remove %s: %s", new_cacert_path, e) result = False else: update = True
def _call_certmonger(self, certmonger_ca='IPA'): subject = str(DN(('cn', self.fqdn), self.subject_base)) krbtgt = "krbtgt/" + self.realm + "@" + self.realm certpath = (paths.KDC_CERT, paths.KDC_KEY) try: prev_helper = None # on the first CA-ful master without '--no-pkinit', we issue the # certificate by contacting Dogtag directly use_dogtag_submit = all([ self.master_fqdn is None, self.pkcs12_info is None, self.config_pkinit ]) if use_dogtag_submit: ca_args = [ paths.CERTMONGER_DOGTAG_SUBMIT, '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, '--certfile', paths.RA_AGENT_PEM, '--keyfile', paths.RA_AGENT_KEY, '--cafile', paths.IPA_CA_CRT, '--agent-submit' ] helper = " ".join(ca_args) prev_helper = certmonger.modify_ca_helper( certmonger_ca, helper) certmonger.request_and_wait_for_cert(certpath, subject, krbtgt, ca=certmonger_ca, dns=self.fqdn, storage='FILE', profile=KDC_PROFILE, post_command='renew_kdc_cert', perms=(0o644, 0o600)) except dbus.DBusException as e: # if the certificate is already tracked, ignore the error name = e.get_dbus_name() if name != 'org.fedorahosted.certmonger.duplicate': root_logger.error("Failed to initiate the request: %s", e) return finally: if prev_helper is not None: certmonger.modify_ca_helper(certmonger_ca, prev_helper)
def get_request_value(request_id, directive): """ Get property of request. """ try: request = _get_request(dict(nickname=request_id)) except RuntimeError as e: root_logger.error('Failed to get request: %s' % e) raise if request: if directive == 'ca-name': ca_path = request.obj_if.get_ca() ca = _cm_dbus_object(request.bus, request, ca_path, DBUS_CM_CA_IF, DBUS_CM_IF) return ca.obj_if.get_nickname() else: return request.prop_if.Get(DBUS_CM_REQUEST_IF, directive) else: return None
def get_request_id(criteria): """ If you don't know the certmonger request_id then try to find it by looking through all the requests. criteria is a tuple of key/value to search for. The more specific the better. An error is raised if multiple request_ids are returned for the same criteria. None is returned if none of the criteria match. """ try: request = _get_request(criteria) except RuntimeError as e: root_logger.error('Failed to get request: %s' % e) raise if request: return request.prop_if.Get(DBUS_CM_REQUEST_IF, 'nickname') else: return None
def track_server_cert(self, nickname, principal, password_file=None, command=None): """ Tell certmonger to track the given certificate nickname. If command is not a full path then it is prefixed with /usr/lib[64]/ipa/certmonger. """ if command is not None and not os.path.isabs(command): command = paths.CERTMONGER_COMMAND_TEMPLATE % (command) try: request_id = certmonger.start_tracking(nickname, self.secdir, password_file, command) except RuntimeError as e: root_logger.error("certmonger failed starting to track certificate: %s" % str(e)) return cert = self.get_cert_from_db(nickname) cert_obj = x509.load_certificate(cert) subject = str(DN(cert_obj.subject)) certmonger.add_principal(request_id, principal) certmonger.add_subject(request_id, subject)
def read_reverse_zone(default, ip_address, allow_zone_overlap=False): while True: zone = ipautil.user_input("Please specify the reverse zone name", default=default) if not zone: return None if not verify_reverse_zone(zone, ip_address): root_logger.error("Invalid reverse zone %s for IP address %s" % (zone, ip_address)) continue if not allow_zone_overlap: try: dnsutil.check_zone_overlap(zone, raise_on_error=False) except ValueError as e: root_logger.error("Reverse zone %s will not be used: %s" % (zone, e)) continue break return normalize_zone(zone)
def request_cert(nssdb, nickname, subject, principal, passwd_fname=None, dns=None): """ Execute certmonger to request a server certificate. ``dns`` A sequence of DNS names to appear in SAN request extension. """ cm = _certmonger() ca_path = cm.obj_if.find_ca_by_nickname('IPA') if not ca_path: raise RuntimeError('IPA CA not found') request_parameters = dict(KEY_STORAGE='NSSDB', CERT_STORAGE='NSSDB', CERT_LOCATION=nssdb, CERT_NICKNAME=nickname, KEY_LOCATION=nssdb, KEY_NICKNAME=nickname, SUBJECT=subject, PRINCIPAL=[principal], CA=ca_path) if dns is not None and len(dns) > 0: request_parameters['DNS'] = dns if passwd_fname: request_parameters['KEY_PIN_FILE'] = passwd_fname result = cm.obj_if.add_request(request_parameters) try: if result[0]: request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF, DBUS_CM_IF, True) else: raise RuntimeError('add_request() returned False') except Exception as e: root_logger.error( 'Failed to create a new request: {error}'.format(error=e)) raise return request.obj_if.get_nickname()
def remove_ds_instance(serverid, force=False): """A wrapper around the 'remove-ds.pl' script used by 389ds to remove a single directory server instance. In case of error additional call with the '-f' flag is performed (forced removal). If this also fails, then an exception is raised. """ instance_name = ''.join([DS_INSTANCE_PREFIX, serverid]) args = [paths.REMOVE_DS_PL, '-i', instance_name] if force: args.append('-f') root_logger.debug("Forcing instance removal") try: ipautil.run(args) except ipautil.CalledProcessError: if force: root_logger.error("Instance removal failed.") raise root_logger.debug("'%s' failed. " "Attempting to force removal" % paths.REMOVE_DS_PL) remove_ds_instance(serverid, force=True)
def __enable(self): try: self.ldap_enable('DNSSEC', self.fqdn, None, self.suffix, self.extra_config) except errors.DuplicateEntry: root_logger.error("DNSSEC service already exists") # add the KEYMASTER identifier into ipaConfigString # this is needed for the re-enabled DNSSEC master dn = DN(('cn', 'DNSSEC'), ('cn', self.fqdn), api.env.container_masters, api.env.basedn) try: entry = api.Backend.ldap2.get_entry(dn, ['ipaConfigString']) except errors.NotFound as e: root_logger.error( "DNSSEC service entry not found in the LDAP (%s)", e) else: config = entry.setdefault('ipaConfigString', []) if KEYMASTER not in config: config.append(KEYMASTER) api.Backend.ldap2.update_entry(entry)
def stop_tracking(secdir, request_id=None, nickname=None): """ Stop tracking the current request using either the request_id or nickname. Returns True or False """ if request_id is None and nickname is None: raise RuntimeError('Both request_id and nickname are missing.') criteria = {'cert-database': secdir} if request_id: criteria['nickname'] = request_id if nickname: criteria['cert-nickname'] = nickname try: request = _get_request(criteria) except RuntimeError as e: root_logger.error('Failed to get request: %s' % e) raise if request: request.parent.obj_if.remove_request(request.path)
def modifications_from_ldif(self, ldif_file): """ Parse ldif file. Default operation is add, only changetypes "add" and "modify" are supported. :param ldif_file: an opened file for read :raises: ValueError """ parser = ldif.LDIFRecordList(ldif_file) parser.parse() last_dn = None for dn, entry in parser.all_records: if dn is None: # ldif parser return None, if records belong to previous DN dn = last_dn else: last_dn = dn if "replace" in entry: for attr in entry["replace"]: try: self.replace_value(dn, attr, entry[attr]) except KeyError: raise ValueError("replace: {dn}, {attr}: values are " "missing".format(dn=dn, attr=attr)) elif "delete" in entry: for attr in entry["delete"]: self.remove_value(dn, attr, entry.get(attr, None)) elif "add" in entry: for attr in entry["add"]: try: self.replace_value(dn, attr, entry[attr]) except KeyError: raise ValueError("add: {dn}, {attr}: values are " "missing".format(dn=dn, attr=attr)) else: root_logger.error( "Ignoring entry: %s : only modifications " "are allowed (missing \"changetype: " "modify\")", dn)
def untrack_file(self, path): """Remove file at path @path from list of backed up files. Does not remove any files from the filesystem. Returns #True if the file was untracked, #False if there was no backup file to restore """ root_logger.debug("Untracking system configuration file '%s'", path) if not os.path.isabs(path): raise ValueError("Absolute path required") filename = None for (key, value) in self.files.items(): _mode, _uid, _gid, filepath = value.split(',', 3) if (filepath == path): filename = key break if not filename: raise ValueError("No such file name in the index") backup_path = os.path.join(self._path, filename) if not os.path.exists(backup_path): root_logger.debug(" -> Not restoring - '%s' doesn't exist", backup_path) return False try: os.unlink(backup_path) except Exception as e: root_logger.error('Error removing %s: %s' % (backup_path, str(e))) del self.files[filename] self.save() return True
def start_tracking(nickname, secdir, password_file=None, command=None): """ Tell certmonger to track the given certificate nickname in NSS database in secdir protected by optional password file password_file. command is an optional parameter which specifies a command for certmonger to run when it renews a certificate. This command must reside in /usr/lib/ipa/certmonger to work with SELinux. Returns certificate nickname. """ cm = _certmonger() params = {'TRACK': True} params['cert-nickname'] = nickname params['cert-database'] = os.path.abspath(secdir) params['cert-storage'] = 'NSSDB' params['key-nickname'] = nickname params['key-database'] = os.path.abspath(secdir) params['key-storage'] = 'NSSDB' ca_path = cm.obj_if.find_ca_by_nickname('IPA') if not ca_path: raise RuntimeError('IPA CA not found') params['ca'] = ca_path if command: params['cert-postsave-command'] = command if password_file: params['KEY_PIN_FILE'] = os.path.abspath(password_file) result = cm.obj_if.add_request(params) try: if result[0]: request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF, DBUS_CM_IF, True) else: raise RuntimeError('add_request() returned False') except Exception as e: root_logger.error('Failed to add new request: {error}' .format(error=e)) raise return request.prop_if.Get(DBUS_CM_REQUEST_IF, 'nickname')