Forum Discussion

LordTanamo_3750's avatar
LordTanamo_3750
Icon for Nimbostratus rankNimbostratus
May 14, 2009

Filter users authenticatiing via NTLM (MOSS) by domain name

Hello,

I was asked to filter certain users by their domain name (domain2\user or user@domain2) which are prohibited to connect to MOSS from the public internet, even though they may connect to the same backend server on a different VS within the corporate network.

There are two VS, both terminate SSL sessions and passing the traffic to the same pool that contains by now only one SharePoint MOSS server, which is member of domain1. There is a trust-relationship between domain 1 and 2.

The MOSS site offers Integrated Windows Authentication (merely NTLM) in order to have LAN-users automatically authenticated and prompt internet users for their credentials. The same LAN users (domain2) are not supposed to access the MOSS site from the public internet on VS. On the other hand certain partners that reside in domain1 may access this site.

I don’t want to setup ACA with LDAP, because users from outside would have to authenticate twice, which is not very fancy. Unfortunately it is not possible to offer a different authentication scheme or set up two MOSS site because then our security officer would go nuts or the URL for internal and external users are not exchangeable. My primary goal is to filter the users from doamin2 when they try to access the VS for public internet users. This works well when I set up MOSS to Basic-Authentication only.

Here is my iRule:

 
 when HTTP_REQUEST { 
 if { [HTTP::username] matches_regex {([Dd][Oo][Mm][Aa][Ii][Nn]2\\|@[Dd][Oo][Mm][Aa][Ii][Nn]2)} }  {  
 HTTP::respond 200 content { 
  
  
 Apology Page 
  
 You are not allowed to authenticate from the public internet 
  
 } 
 } else {  
 pool Pool_MOSS 
 } 
 } 
 Or  
 when HTTP_REQUEST { 
 if { [HTTP::username] matches_regex {([Dd][Oo][Mm][Aa][Ii][Nn]2\\|@[Dd][Oo][Mm][Aa] [Ii][Nn]2)} }  {  
 HTTP::respond 401 
 } else {  
 pool Pool_MOSS 
 } 
 } 
 

When I run wireshark with unencrypted traffic connecting straight to MOSS without LB I can see the domain name being sent by the client within the NTLMSSP haeader. Is there anyway to . I really tried my best to get this running with http::collect or tcp::collect, but this is tedious and there are also examples for SSL::collect. Also I would prefer sending a HTTP::respond 401 or HTTP::respond 200 content … instead of selecting a different pool, but anyhow my example does not work. Is there any way to get to see the domain name data sent by the client and to filter users that belong to a certain domain2.

 
 when CLIENTSSL_HANDSHAKE  { 
 SSL::collect 
 } 
 when CLIENTSSL_DATA {  
 if { [SSL::payload] matches_regex {([Dd][Oo][Mm][Aa][Ii][Nn]2\\|@[Dd][Oo][Mm][Aa] [Ii][Nn]2)} }  { 
 pool Pool_Error-Page 
 } else {  
 pool Pool_MOSS 
 } 
 } 
 

Anyone has an idea? I would really appreciate some help. I've also attached a screenshot of my wireshark.

Kind regards

Jackson

3 Replies

  • Hi Jackson,

     

     

    HTTP::username will return the parsed and base64 decoded username from a basic auth request. It will not parse the username or domain from an NTLM authentication session.

     

     

    Here is a handy site from a guy who helped reverse engineer the NTLM "protocol". He describes the different types of messages used in an NTLM authentication handshake (http://www.innovation.ch/personal/ronald/ntlm.html Click here). There are three different types of messages you would potentially need to handle. Though, I guess you could just handle the first type and prevent anyone from getting past that point to the type 2 (server response) or type 3 (second client request) steps.

     

     

    The format for the NTLM Authorization header is: "NTLM ". So you can use HTTP::header value Authorization to get the full value of the header. You can check if it starts with NTLM before trying to parse it:

     

     

     
     when HTTP_REQUEST { 
      
         Check for Authorization header value starting with NTLM before trying to parse the request. 
        if {[string tolower [HTTP::header Authorization]] starts_with "ntlm"}{ 
      
     ... 
     

     

     

    Once you've verified it looks like an NTLM auth header, you can try to base64 decode it and then check to see if it contains the domain you want to block. For type 1 messages, the domain name is listed normally. For type 3 messages, there is a null character between each character in the domain (why, I have no idea).

     

     

     
        if {[string match -nocase "*domain2*" [b64decode [getfield [HTTP::header Authorization] " " 2]]]}{ 
      
            Request was using a domain2 account 
     

     

     

    [HTTP::header Authorization] returns the raw header value "NTLM TlRMTVNTUAABAAAAA7IAAAoACgApAAAACQAJACAAAABMSUdIVENJVFlVUlNBLU1JTk9S"

     

    [getfield [HTTP::header Authorization] " " 2] returns the base64 encoded token "TlRMTVNTUAABAAAAA7IAAAoACgApAAAACQAJACAAAABMSUdIVENJVFlVUlNBLU1JTk9S"

     

    b64decode decodes the encoded token and returns the binary content.

     

     

    If you need to check the type of NTLM message you can use binary scan:

     

     

     
         Parse the type code (01, 02, 03) in order to determine the message type and corresponding domain name check 
         http://www.innovation.ch/personal/ronald/ntlm.html 
        binary scan [b64decode [getfield [HTTP::header Authorization] " " 2]] H16H2H* unused1 type_code unused2 
      
        log local0. "Type: $type_code" 
     

     

     

    Aaron
  • Thanks Aaron,

    now it works. Here is my listing to filter NTLM-users that belong to domain2 "testdomain".

     
     when HTTP_REQUEST { 
      Check for Authorization header value starting with NTLM before trying to parse the request.  
       if {[string tolower [HTTP::header Authorization]] starts_with "ntlm"}{  
       if { [b64decode [getfield [HTTP::header Authorization] " " 2]] matches_regex {([Tt].[Ee].[Ss].[Tt].[Dd].[Oo].[Mm].[Aa].[Ii].[Nn])} }  {  
      HTTP::respond 200 content { 
            
               
                 Apology Page 
               
               
     You are not allowed to authenticate as your user belongs to testdomain ... 
               
            
        } 
       } 
     }} 
      
     

    Thanks again and kind regards

    Jackson
  • Hi Jackson,

     

     

    A regex is going to be less efficient than a string comparison. That particular regex which matches the domain name letters with any single character between each letter won't match the first (type 1) NTLM message where the domain isn't separated by null characters. If you replace the regex check with a string comparison looking just for *testdomain*, you should be able to block the first message. The third message has the domain name letters separated by nulls.

     

     

    If you really want to use a regex, you could replace it with something like this which does a case insensitive match for testdomain with or without nulls between the letters. This would match the type 1 or type 3 messages.

     

     

    (?i)t\000?e\000?s\000?t\000?d\000?o\000?m\000?a\000?i\000?n

     

     

    But again, a string comparison will be more efficient than a regex and you would only need to check for the type 1 message string.

     

     

    Aaron