I’ve been trying to use Ruby libraries to access Sharepoint and am having trouble getting to the starting line. NTLM authentication from Ruby doesn’t seem to be in a good state. I’m using httpi-0.9.6 as my HTTP library, which by default uses httpclient-2.2.4, and for NTLM support I’m using httpi-ntlm-0.9.6. All this is running under Ruby 1.8.7 which incorporates Net::NTLM 0.1.1 which http-ntlm relies on.
In my enterprise environment, I need to use NTLM authentication to access the Sharepoint server. So, I format the username string supplied to httpi as “USERDOMAIN\username”. It happens that the Sharepoint machine I want to access is in a different NTLM domain than the user account that I want to access it with. When httpclient does the NTLM authentication phase, it uses facilities from Net::NTLM to format a Type 1 Message. This is where the problems start. Firstly, httpclient doesn’t do the right (undocumented) things necessary to get Net::NTLM to properly include the user’s domain into the request.
case state when :init t1 = Net::NTLM::Message::Type1.new t1.domain = domain if domain return t1.encode64 end
The trouble here is that this code doesn’t properly tell Net::NTLM that this domain information should be incorporated into the request. As written, HTTPI generates a corrupt request, as can be seen from a Wireshark trace. It’s additionally necessary to enable the resulting SecurityBuffer and to set a flag in the request indicating that the domain is being signalled:
case state when :init t1 = Net::NTLM::Message::Type1.new if domain t1.domain = domain t1.enable(:domain) t1.set_flag(:DOMAIN_SUPPLIED) end return t1.encode64
But when you do this, you find that the packet is still corrupt, because the offset to the domain name data in the resulting request is incorrect. This is because of what appears to be a bug in Net::NTLM. That’s not so easy to fix and play about with, because it’s incorporated into Ruby itself. The problem is that the offsets are calculated by adding the sizes of the SecurityBuffers defined for the message type, regardless of whether a particular SecurityBuffer is active or not. For my testing purposes, I forced the additional “workstation” SecurityBuffer to be active (with an arbitrary non-null value) in the same way as the Domain, and by doing so, was able to generate a valid request.
But imagine my horror when my requests were still denied by the server. Further investigation revealed that Net::NTLM ignores the user-specified domain when generating the subsequent Type 3 Message, substituting instead the Target value returned in the Challenge of the Type 2 Message.
Further hackery will be required to fix that, but another approach might be to use Curb instead of httpclient.