2014-03-06 53 views
6

我正在嘗試爲使用Perl(或者XS模塊)編寫的Web服務器實現NTLM授權。我的理解是,它應該按以下方式工作:Perl中的NTLM授權

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::NTLMAuthen::NTLM::HTTP,這兩個似乎產生的消息完全正常的,但是,他們沒有提供的功能檢查Type3消息。

我的下一步是嘗試使用Win32::IntAuth來驗證NTLM令牌。 這個是我遇到麻煩的地方,開發者和其他搜索信息片斷說這個模塊應該能夠驗證NTLM二進制令牌。

該模塊包裝了一些Win32 API調用,即AcquireCredntialsHandle,AcceptSecurityContext,CompleteAuthToken和ImpersonateSecurityContext。

不幸的是我所有的努力來驗證NTLM令牌有兩種SEC_E_INVALID_TOKENSEC_E_INSUFFICIENT_MEMORY導致我建議我的NTLM令牌是不正確的失敗在AcceptSecurityContext。下面是一些幫助展示我的方法的代碼片段。

# other code 
... 
if (not defined $headers->header('Authorization')) { 
    initHandshake($response); 
} else { 
    my $authHeader = $headers->header('Authorization'); 
    if ($authHeader =~ m/^NTLM\s(.+)$/i) { 
     my $message = $1; 
     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

我也想知道如何做到這一點! –

回答

3

我設法bastardizing的Win32 :: IntAuth(我相信有一個錯誤在裏面)得到這個工作。基本上我沒有抱着創建2型令牌的過程中產生的部分上下文,這和有在的Win32 :: IntAuth錯誤的事實:

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

這是導致令牌錯誤,因爲它WASN 「T令牌的正確的長度,因此:

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, ...) 

希望這有助於人們誰可能是類似的船。請隨時與我聯繫以獲得完整演示。