Аутентификация в Active Directory с помощью python-ldap всегда возвращает (97, [])

Подобно этому вопросу, я пытаюсь выполнить простую аутентификацию для Active Directory 2003 с использованием python ldap (CentOS 6.2 x86_64, Python 2.6.6, python-ldap 2.3.10).

Несмотря на выполнение всех обычных шагов инициализации, включая

conn.set_option(ldap.OPT_REFERRALS, 0)

если я передаю правильные учетные данные, я всегда получаю (97, []):

>>> import ldap
>>> conn = ldap.initialize('ldap://ad.server.domain.com')
>>> conn.protocol_version = 3
>>> conn.set_option(ldap.OPT_REFERRALS, 0)
>>> conn.simple_bind_s('[email protected]', 'WrongPassword')
ldap.INVALID_CREDENTIALS: {'info': '80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 52e, vece', 'desc': 'Invalid credentials'}
>>> conn.simple_bind_s('[email protected]', 'CorrectPassword')
(97, [])

Код ошибки 97 не является успешным; это ошибка LDAP_REFERRAL_LIMIT_EXCEEDED, возвращаемая из AD. Я также не могу использовать его как фактический индикатор успеха, потому что:

>>> conn.simple_bind_s('', 'CorrectPassword')
(97, [])
>>> conn.simple_bind_s('', '')
(97, [])

Еще большее разочарование вызывает то, что этот сценарий представляет собой переход от старого сценария Perl, использующего Net :: LDAP, который действительно возвращает 0 для успешного аутентифицированного связывания с тем же AD и тем же сервером.

Вся информация, которую я могу найти на python-ldap, указывает на то, что то, что я делаю, должно работать; Я был бы склонен думать, что с серверами AD что-то не так, но сценарий Perl действительно возвращает правильный код LDAP при успешном связывании.

Я тестировал python-ldap 2.2.0 и python 2.4.4 на старом ящике CentOS 5.5, который у меня лежал, и он "терпит неудачу" точно так же.

Кто-нибудь знает, что мне не хватает?

РЕДАКТИРОВАТЬ: По запросу, вот скрипт Perl, который работает. Net::LDAP возвращает код возврата от сервера LDAP, а сервер AD возвращает 0x00, «Успешный запрос», AFAICT.

#!/usr/bin/perl -w
use strict;
use Net::LDAP;

## Corporate subdomains
my @domains = ("americas", "asia", "europe");

# AD connect timeout
my $timeout = 10;
# Set AD server info.
my $port = "389";
my $host = "server.domain.com";

my $user = shift @ARGV;
chomp $user;

my $password = <STDIN>;
$password =~ s/\r\n//;
chomp $password;

my $ldap = Net::LDAP->new($host, port => $port, timeout => $timeout ) ||
        die "Unable to connect to LDAP server";

my $bind_return = 1;
foreach (@domains) {
        my $result = $ldap->bind( "$user\@$_.domain.com", password => $password );
        if( $result->code == 0) {
                $bind_return = 0;
                last;
        }
}

## Unbind and return
$ldap->unbind;

if ($bind_return) { print "Authentication Failed.  Access Denied\n" }
exit $bind_return;

person Chris Doherty    schedule 23.05.2012    source источник
comment
Можете ли вы опубликовать соответствующий код Perl?   -  person stark    schedule 24.05.2012
comment
Я предполагаю, что для server.domain.com вы имели в виду ad.server.domain.com для соответствия python? Кроме того, порт по умолчанию - 389, но было бы хорошо добавить: 389 в URL-адрес явно.   -  person stark    schedule 25.05.2012


Ответы (2)


Майкл Стредер, автор библиотеки python-ldap, просветил меня так:

97 не является кодом результата LDAP. Это результат типа ldap.RES_BIND. Обычно вам не нужно смотреть на результаты, возвращаемые LDAPObject.simple_bind_s () (если вы не хотите извлекать элементы управления откликом привязки).

Если код результата LDAP не равен 0, возникает сопутствующее исключение, например ldap.INVALID_CREDENTIALS в вашем примере.

Итак, ваш код должен выглядеть так:

try:
  conn.simple_bind_s('[email protected]', 'WrongPassword')
except ldap.INVALID_CREDENTIALS:
  user_error_msg('wrong password provided')

Причина таких результатов:

>>> conn.simple_bind_s('', 'CorrectPassword')
(97, [])
>>> conn.simple_bind_s('', '')
(97, [])

заключается в том, что из коробки 2003 Active Directory разрешает анонимные привязки. Таким образом, отсутствие идентификатора пользователя по-прежнему будет проходить простую проверку привязки, если единственное, что проверяется, - это то, вызывает ли simple_bind_s() ошибку.

Active Directory 2003 действительно требует аутентификации для любых поисков, которые не являются атрибутами rootDSE, поэтому для наших внутренних целей мы добавили простой поиск в блок try::

try:
  conn.simple_bind_s('[email protected]', 'SubmittedPassword')
  conn.search_st('DC=domain,DC=com', ldap.SCOPE_SUBTREE, '(objectClass=container)', 'name', 0, 30)
except ldap.INVALID_CREDENTIALS:
  user_error_msg('wrong password provided')
person Chris Doherty    schedule 24.05.2012

Эта ошибка означает, что ваш conn.set_option(ldap.OPT_REFERRALS, 0) не пострадает.

Поэтому попробуйте это:

import ldap

ldap.set_option(ldap.OPT_REFERRALS,0)
ldap.protocol_version = 3
conn = ldap.initialize('ldap://....')
conn.simple_bind_s('[email protected]', 'RightPassword')
person Burhan Khalid    schedule 24.05.2012
comment
Боюсь, никакой радости. По-прежнему возвращает (97, []). Я проверил с помощью conn.get_option (), что эти параметры были правильно установлены для экземпляра conn SimpleLDAPObject. - person Chris Doherty; 24.05.2012
comment
Я могу ошибаться, но я думаю, что в сценарии Perl используется [email protected]? - person Burhan Khalid; 24.05.2012