Add abillity to discover AD version and vendor name to discovery

refs #9605
This commit is contained in:
Matthias Jentsch 2015-07-14 18:32:44 +02:00
parent 40d432100b
commit 3ddb8ca1bd
4 changed files with 120 additions and 66 deletions

View File

@ -218,7 +218,7 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface
throw new ProgrammingError('It is required to set a attribute name where to find a user\'s name first');
}
if ($this->ds->getCapabilities()->hasAdOid()) {
if ($this->ds->getCapabilities()->isActiveDirectory()) {
$isActiveAttribute = 'userAccountControl';
$createdAtAttribute = 'whenCreated';
$lastModifiedAttribute = 'whenChanged';
@ -254,7 +254,7 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface
throw new ProgrammingError('It is required to set the objectClass where to look for users first');
}
if ($this->ds->getCapabilities()->hasAdOid()) {
if ($this->ds->getCapabilities()->isActiveDirectory()) {
$stateConverter = 'user_account_control';
} else {
$stateConverter = 'shadow_expire';

View File

@ -66,7 +66,7 @@ class Discovery {
*/
public function isAd()
{
return $this->connection->getCapabilities()->hasAdOid();
return $this->connection->getCapabilities()->isActiveDirectory();
}
/**

View File

@ -11,7 +11,6 @@ namespace Icinga\Protocol\Ldap;
*/
class LdapCapabilities
{
const LDAP_SERVER_START_TLS_OID = '1.3.6.1.4.1.1466.20037';
const LDAP_PAGED_RESULT_OID_STRING = '1.2.840.113556.1.4.319';
@ -141,11 +140,22 @@ class LdapCapabilities
*
* @return boolean
*/
public function hasAdOid()
public function isActiveDirectory()
{
return isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_OID]);
}
/**
* Whether the ldap server is an OpenLDAP server
*
* @return bool
*/
public function isOpenLdap()
{
return isset($this->attributes->structuralObjectClass) &&
$this->attributes->structuralObjectClass === 'OpenLDAProotDSE';
}
/**
* Return if the capability objects contains support for LdapV3, defaults to true if discovery failed
*
@ -211,29 +221,119 @@ class LdapCapabilities
public function getVendor()
{
// AD doesn't include the vendor entry
if ($this->hasAdOid()) {
/*
rfc #3045 specifies that the name of the server MAY be included in the attribute 'verndorName',
AD and OpenLDAP don't do this, but for all all other vendors we follow the standard and
just hope for the best.
*/
if ($this->isActiveDirectory()) {
return 'Microsoft Active Directory';
}
if (! isset($this->attributes->vendorName)) {
// OpenLDAP doesn't include the vendor entry
// TODO: bad, remove this and add proper OpenLDAP version checking
if ($this->isOpenLdap()) {
return 'OpenLDAP';
}
if (! isset($this->attributes->vendorName)) {
return null;
}
return $this->attributes->vendorName;
}
public function getVersion()
{
// AD doesn't include the version string
if ($this->hasAdOid()) {
// TODO: query AD version from cn=schema,cn=configuration,dc=yourdomain,dc=com attribute:ObjectVersion
/*
rfc #3045 specifies that the version of the server MAY be included in the attribute 'vendorVersion',
but AD and OpenLDAP don't do this. For OpenLDAP there is no way to query the server versions, but for all
all other vendors we follow the standard and just hope for the best.
*/
if ($this->isActiveDirectory()) {
return $this->getAdObjectVersionName();
}
if (! isset($this->attributes->vendorVersion)) {
return 'unknown';
return null;
}
return $this->attributes->vendorVersion;
}
/**
* Discover the capabilities of the given LDAP server
*
* @param LdapConnection $connection The ldap connection to use
* @param int $ds The link identifier of the current LDAP connection
*
* @return LdapCapabilities
*
* @throws LdapException In case the capability query has failed
*/
public static function discoverCapabilities(LdapConnection $connection, $ds)
{
$fields = array(
'defaultNamingContext',
'namingContexts',
'vendorName',
'vendorVersion',
'supportedSaslMechanisms',
'dnsHostName',
'schemaNamingContext',
'supportedLDAPVersion', // => array(3, 2)
'supportedCapabilities',
'supportedControl',
'supportedExtension',
'objectVersion',
'+'
);
$result = @ldap_read($ds, '', (string) $connection->select()->from('*', $fields), $fields);
if (! $result) {
throw new LdapException(
'Capability query failed (%s:%d): %s. Check if hostname and port of the'
. ' ldap resource are correct and if anonymous access is permitted.',
$connection->getHostname(),
$connection->getPort(),
ldap_error($ds)
);
}
$entry = ldap_first_entry($ds, $result);
if ($entry === false) {
throw new LdapException(
'Capabilities not available (%s:%d): %s. Discovery of root DSE probably not permitted.',
$connection->getHostname(),
$connection->getPort(),
ldap_error($ds)
);
}
$cap = new LdapCapabilities(
$connection->cleanupAttributes(
ldap_get_attributes($ds, $entry),
array_flip($fields)
)
);
return $cap;
}
/**
* Determine the active directory version using the available capabillities
*
* @return null|string The server version description or null when unknown
*/
protected function getAdObjectVersionName()
{
if (isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_W8_OID])) {
return 'Windows Server 2012 (or newer)';
}
if (isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_V61_R2_OID])) {
return 'Windows Server 2008 R2 (or newer)';
}
if (isset($this->oids[self::LDAP_CAP_ACTIVE_DIRECTORY_V60_OID])) {
return 'Windows Server 2008 (or newer)';
}
return null;
}
}

View File

@ -882,7 +882,7 @@ class LdapConnection implements Selectable, Inspectable
*
* @return object
*/
protected function cleanupAttributes($attributes, array $requestedFields)
public function cleanupAttributes($attributes, array $requestedFields)
{
// In case the result contains attributes with a differing case than the requested fields, it is
// necessary to create another array to map attributes case insensitively to their requested counterparts.
@ -1058,67 +1058,21 @@ class LdapConnection implements Selectable, Inspectable
putenv('LDAPTLS_REQCERT=never');
} else {
if ($this->validateCertificate) {
$ldap_conf = $this->getConfigDir('ldap_ca.conf');
// $ldap_conf = $this->getConfigDir('ldap_ca.conf');
} else {
$ldap_conf = $this->getConfigDir('ldap_nocert.conf');
// $ldap_conf = $this->getConfigDir('ldap_nocert.conf');
putenv('LDAPTLS_REQCERT=never');
}
/*
putenv('LDAPRC=' . $ldap_conf); // TODO: Does not have any effect
if (getenv('LDAPRC') !== $ldap_conf) {
throw new LdapException('putenv failed');
}
*/
}
}
/**
* Discover the capabilities of the given LDAP server
*
* @param resource $ds The link identifier of the current LDAP connection
*
* @return LdapCapabilities
*
* @throws LdapException In case the capability query has failed
*/
protected function discoverCapabilities($ds)
{
$fields = array(
'defaultNamingContext',
'namingContexts',
'vendorName',
'vendorVersion',
'supportedSaslMechanisms',
'dnsHostName',
'schemaNamingContext',
'supportedLDAPVersion', // => array(3, 2)
'supportedCapabilities',
'supportedControl',
'supportedExtension',
'+'
);
$result = @ldap_read($ds, '', (string) $this->select()->from('*', $fields), $fields);
if (! $result) {
throw new LdapException(
'Capability query failed (%s:%d): %s. Check if hostname and port of the'
. ' ldap resource are correct and if anonymous access is permitted.',
$this->hostname,
$this->port,
ldap_error($ds)
);
}
$entry = ldap_first_entry($ds, $result);
if ($entry === false) {
throw new LdapException(
'Capabilities not available (%s:%d): %s. Discovery of root DSE probably not permitted.',
$this->hostname,
$this->port,
ldap_error($ds)
);
}
return new LdapCapabilities($this->cleanupAttributes(ldap_get_attributes($ds, $entry), array_flip($fields)));
}
/**
* Create an LDAP entry
*