Авторизация NTLM в Perl

Я пытаюсь реализовать авторизацию NTLM для веб-сервера, написанного на Perl (или, возможно, модуля XS). Я понимаю, что он должен работать следующим образом:

c -> s: GET
s -> c: 401, WWW-Authenticate: NTLM
c -> s: GET, Authorization: NTLM [Type1 Message]
s -> c: 401, WWW-Authenticate: NTLM [Type2 Message]
c -> s: GET, Authorization: NTLM [Type3 Message]

IF s.Check([Type3 Message]):
  s -> c: 200
ELSE:
  s -> c: 401

для генерации сообщения Type3 я использовал оба Authen::Perl:: NTLM и Authen::NTLM:: HTTP, оба из них, похоже, генерируют сообщения совершенно нормально, однако они не предлагают никаких функций для проверки сообщения Type3.

мой следующий шаг-попробовать и использовать Win32:: IntAuth для аутентификации маркера NTLM. этой где я работал в беде, разработчик и другие фрагменты информации, найденной в поиске, говорят, что этот модуль должен иметь возможность аутентифицировать двоичный токен NTLM.

модуль обертывает некоторые вызовы Win32 API, а именно AcquireCredntialsHandle, AcceptSecurityContext, CompleteAuthToken и ImpersonateSecurityContext.

к сожалению, все мои попытки аутентификация маркера NTLM не удалась в AcceptSecurityContext с помощью SEC_E_INVALID_TOKEN или SEC_E_INSUFFICIENT_MEMORY заставляя меня предположить, что мой токен NTLM не верен. Ниже приведены некоторые фрагменты кода, которые помогут показать мои методы.

# other code
...
if (not defined $headers->header('Authorization')) {
    initHandshake($response); 
} else { 
    my $authHeader = $headers->header('Authorization');
    if ($authHeader =~ m/^NTLMs(.+)$/i) { 
        my $message = ;
        if (length($message) == 56) {
            handleType1($response, $message);
        } else {
            handleType3($response, $message);
        }
    } else {
        printf "ERROR - Unable to pull out an NTLM message.n";
        print $authHeader . "n";
    }
} 
... 
sub handleType3 {
    my $response = shift();
    my $message = shift();
    print "handleType3 - ", $message, "n";
    my $auth = Win32::IntAuth->new(debug => 1);
    my $token = $auth->get_token_upn(decode_base64($message)) or die 
                           "Couldn'timpersonate user, ", $auth->last_err_txt();
    print "Hurrargh. User ", $auth->get_username(), " authed!n";
    $response->status(200);
} 
..

полный код можно посмотреть здесь:http://codepad.org/cpMWnFru

1 ответов


мне удалось заставить это работать, bastardizing Win32:: IntAuth (который, я считаю, имеет ошибку в нем). По сути, я не держал частичный контекст, созданный во время создания токена типа 2, это и тот факт, что в Win32::IntAuth:

my $buf_size     = 4096;
my $sec_inbuf    = pack("L L P$buf_size", $buf_size, SECBUFFER_TOKEN, $token);

это вызывало ошибку токена, поскольку это не была правильная длина токена, поэтому:

my $sec_inbuf    = pack("L L P" . length($token), length($token), SECBUFFER_TOKEN, $token);

дал правильные результаты.

предыдущий код был изменен кому:

...
sub handleType1 {
    my $response = shift();
    my $message = shift();                               
    print "handleType1 - |", ${$message}, "|\n";
    my $challenge = acceptSecurityContext(${$message});
    ${$response}->status(401);
    ${$response}->header("WWW-Authenticate" => "NTLM " . $challenge);
}
...
sub handleType3 {
    my $response = shift();               
    my $message = shift();
    print "handleType3 - ", ${$message}, "\n";
    if (acceptSecurityContext(${$message})) {
        ${$response}->status(200);
    } else {
        ${$response}->status(401);
    }
}
...

acceptSecurityContext-это функция, которая следует за этим pseudoish код:

credentials = Win32->AcquireCredentialsHandle(...)
challenge = Win32->AcceptSecurityContext(credentials, token, globalCtx ? globalCtx : 0, ...)

надеюсь, это поможет людям, которые могут оказаться в подобной лодке. Не стесняйтесь обращаться ко мне для полной демонстрации.