async def hostnames_to_uris(self, data): ret = [] for h in data['hostname']: proto = 'ldaps' if SSL(data['ssl']) == SSL.USESSL else 'ldap' parsed = urlparse(f"{proto}://{h}") try: port = parsed.port host = parsed.netloc if not parsed.port else parsed.netloc.rsplit(':', 1)[0] except ValueError: """ ParseResult.port will raise a ValueError if the port is not an int Ignore for now. ValidationError will be raised in common_validate() """ host, port = h.rsplit(':', 1) if port is None: port = 636 if SSL(data['ssl']) == SSL.USESSL else 389 uri = f"{proto}://{host}:{port}" ret.append(uri) return ret
def _setup_ssl(self, data): if SSL(data['security']['ssl']) == SSL.NOSSL: return cert = data['security']['client_certificate'] if cert: pyldap.set_option( pyldap.OPT_X_TLS_CERTFILE, f"/etc/certificates/{cert}.crt" ) pyldap.set_option( pyldap.OPT_X_TLS_KEYFILE, f"/etc/certificates/{cert}.key" ) pyldap.set_option( pyldap.OPT_X_TLS_CACERTFILE, '/etc/ssl/truenas_cacerts.pem' ) if data['security']['validate_certificates']: pyldap.set_option( pyldap.OPT_X_TLS_REQUIRE_CERT, pyldap.OPT_X_TLS_DEMAND ) else: pyldap.set_option( pyldap.OPT_X_TLS_REQUIRE_CERT, pyldap.OPT_X_TLS_ALLOW ) try: pyldap.set_option(pyldap.OPT_X_TLS_NEWCTX, 0) except Exception: self.logger.warning('Failed to initialize new TLS context.', exc_info=True) return
def _open(self): """ We can only intialize a single host. In this case, we iterate through a list of hosts until we get one that works and then use that to set our LDAP handle. SASL GSSAPI bind only succeeds when DNS reverse lookup zone is correctly populated. Fall through to simple bind if this fails. """ res = None if self._isopen: return True if self.hosts: saved_simple_error = None saved_gssapi_error = None for server in self.hosts: try: self._handle = pyldap.initialize(server) except Exception as e: self.logger.debug( f'Failed to initialize ldap connection to [{server}]: ({e}). Moving to next server.' ) continue res = None pyldap.protocol_version = pyldap.VERSION3 pyldap.set_option(pyldap.OPT_REFERRALS, 0) pyldap.set_option(pyldap.OPT_NETWORK_TIMEOUT, self.ldap['dns_timeout']) if SSL(self.ldap['ssl']) != SSL.NOSSL: if self.ldap['certificate']: pyldap.set_option( pyldap.OPT_X_TLS_CERTFILE, f"/etc/certificates/{self.ldap['cert_name']}.crt") pyldap.set_option( pyldap.OPT_X_TLS_KEYFILE, f"/etc/certificates/{self.ldap['cert_name']}.key") pyldap.set_option(pyldap.OPT_X_TLS_CACERTFILE, '/etc/ssl/truenas_cacerts.pem') if self.ldap['validate_certificates']: pyldap.set_option(pyldap.OPT_X_TLS_REQUIRE_CERT, pyldap.OPT_X_TLS_DEMAND) else: pyldap.set_option(pyldap.OPT_X_TLS_REQUIRE_CERT, pyldap.OPT_X_TLS_ALLOW) try: pyldap.set_option(pyldap.OPT_X_TLS_NEWCTX, 0) except Exception: self.logger.warning( 'Failed to initialize new TLS context.', exc_info=True) if SSL(self.ldap['ssl']) == SSL.USESTARTTLS: try: self._handle.start_tls_s() except pyldap.LDAPError as e: self.logger.debug( 'Encountered error initializing start_tls: %s', e) saved_simple_error = e continue if self.ldap['anonbind']: try: res = self._handle.simple_bind_s() break except Exception as e: saved_simple_error = e self.logger.debug('Anonymous bind failed: %s' % e) continue if self.ldap['certificate']: try: res = self._handle.sasl_non_interactive_bind_s( 'EXTERNAL') if self.ldap['verbose_logging']: self.logger.debug( 'Successfully bound to [%s] using client certificate.', server) break except Exception as e: saved_simple_error = e self.logger.debug('SASL EXTERNAL bind failed.', exc_info=True) continue if self.ldap['kerberos_principal']: try: self._handle.set_option(pyldap.OPT_X_SASL_NOCANON, 1) self._handle.sasl_gssapi_bind_s() res = True break except Exception as e: saved_gssapi_error = e self.logger.debug( f'SASL GSSAPI bind failed: {e}. Attempting simple bind' ) try: res = self._handle.simple_bind_s(self.ldap['binddn'], self.ldap['bindpw']) break except Exception as e: self.logger.debug( f'Failed to bind to [{server}] using [{self.ldap["binddn"]}]: {e}' ) saved_simple_error = e continue if res: self._isopen = True elif saved_gssapi_error: self._convert_exception(saved_gssapi_error) elif saved_simple_error: self._convert_exception(saved_simple_error) return (self._isopen is True)
async def ldap_validate(self, ldap): port = 636 if SSL(ldap['ssl']) == SSL.USESSL else 389 for h in ldap['hostname']: await self.middleware.call('ldap.port_is_listening', h, port, ldap['dns_timeout']) await self.middleware.call('ldap.validate_credentials')