[ANN] Ruby Win32 SSPI 0.0.1 - Enabling NTLM/Negotiate proxy authentication

My work place implemented install a proxy server recently, and because it
demands NTLM/Negotiate authentication, a bunch of my "utility" scripts that
scraped the web for me stopped working. Well, that sounded like the
opportunity to develop a cool Ruby library!

This library provides bindings to the Win32 SSPI library, which enables
authentication as the current user with proxy servers using the
"Negotiate"/SPNEGO protocol (which typically just means NTLM).

There are some pure Ruby implementations of the NTLM protocol, but this is
the only one I know of that will authenticate with a proxy server as the
current user - meaning you don't have to put your username or password in an
environment variable or anywhere else.

The library also includes a patch for open-uri and Net:HTTP that will make
the authentication process seamless.

I have only tested this in my own environment so I'm sure its fragile.
Please try it out if you have run into this issue yourself.

The project can be found at:

* fingers slipped and I sent previous announcement early - oops! *

My work place implemented install a proxy server recently, and because it
demands NTLM/Negotiate authentication, a bunch of my "utility" scripts that
scraped the web for me stopped working. Well, that sounded like the
opportunity to develop a cool Ruby library!

This library provides bindings to the Win32 SSPI library, which enables
authentication as the current user with proxy servers using the
"Negotiate"/SPNEGO protocol (which typically just means NTLM).

There are some pure Ruby implementations of the NTLM protocol, but this is
the only one I know of that will authenticate with a proxy server as the
current user - meaning you don't have to put your username or password in an
environment variable or anywhere else.

The library also includes a patch for open-uri and Net:HTTP that will make
the authentication process seamless.

I have only tested this in my own environment so I'm sure its fragile.
Please try it out if you have run into this issue yourself.

The project can be found at: http://rubyforge.org/projects/rubysspi/

Gem install will be available when the index updates:

  gem install rubysspi

Documentation is available in the gem, but here is the README from the
distribution. Comments and feedback welcome!

Justin

=== README ===

= Introduction

This library provides bindings to the Win32 SSPI libraries, which implement
various security protocols for Windows. The library was primarily developed
to give Negotiate/NTLM proxy authentication abilities to Net::HTTP, similar
to support found in Internet Explorer or Firefox.

The libary is NOT an implementation of the NTLM protocol, and does not give
the ability to authenticate as any given user. It is able to authenticate
with a proxy server as the current user.

This project can be found on rubyforge at:

http://rubyforge.org/projects/rubysspi

= Using with open-uri

To use the library with open-uri, make sure to set the environment variable
+http_proxy+ to your proxy server. This must be a hostname and port in URL
form. E.g.:

  http://proxy.corp.com:8080

The library will grab your current username and domain from the environment
variables +USERNAME+ and +USERDOMAIN+. This should be set for you by Windows
already.

The library implements a patch on top of Net::HTTP, which means open-uri
gets it too. At the top of your script, make sure to require the patch after
+open-uri+:

  require +open-uri+
  require +rubysspi/proxy_auth+

  open("http://www.google.com") { |f| puts(f.gets(nil)) }

Note that this patch does NOT work with the +http_proxy_user+ and
+http_proxy_password+ environment variables. The library will ONLY
authenticate as the current user.

= Using with Net::HTTP

Net::HTTP will not use the proxy server supplied in the environment variable
automatically, so you have to supply the proxy address yourself. Otherwise,
it's exactly the same:

  require 'net/http'
  require 'rubysspi/proxy_auth+

  Net::HTTP::Proxy("proxy.corp.com", 8080).start("www.google.com") do |http|
    resp = http.request_get "/"
    puts resp.body
  end

= Using rubysspi directly

As stated, the library is geared primarily towards supporting Negotiate/NTLM
authentication with proxy servers. In this vein, you can manually
authenticate a given HTTP connection with a single call:

  require 'rubysspi'

  Net::HTTP.Proxy(proxy.host, proxy.port).start("www.google.com") do |http|
    resp = SSPI::NegotiateAuth.proxy_auth_get http, "/"
  end

The +resp+ variable will contain the response from Google, with any proxy
authorization necessary taken care of automatically. Note that if the +http+
connection is not closed, any subsequent requests will NOT require
authentication.

If the above method is used, it is recommended that you do NOT require the
'rubysspi/proxy_auth' library, as the interaction between the two will fail.

The library can be used directly to generate tokens appropriate for the
current user, too.

To get started, first create an instance of the SSPI::NegotiateAuth class:

  require 'rubysspi'

  n = SSPI::NegotiateAuth.new

Next, get the first token by calling get_initial_token:

  token = n.get_initial_token

This token returned will be Base64 encoded and can be directly placed in an
HTTP header. This token can be easily decoded, however, and is usually an
NTLM Type 1 message.

After getting a response from the server (usually an NTLM Type 2 message),
pass it into the complete_authentication:

  token = n.complete_authentication(server_token)

Note that server_token can be Base64 encoded or not, and if it starts with
"Negotiate", that phrase will be stripped off. This allows the response from
a Proxy-Authentication header to be passed into the method directly. The
token can be decoded externally and passed in, too.

The token returned (usually an NTLM Type 3) message can then be sent to the
server and the connection should be authenticated.

= Thanks & References

Many references were used in decoding both NTLM messages and integrating
with the SSPI library. Among them are:

* Managed SSPI Sample - A .NET implementation of a simple client/server
using SSPI. A complex undertaking but provides a great resource for playing
with the API.

···

*
http://msdn.microsoft.com/library/?url=/library/en-us/dndotnet/html/remsspi.asp?frame=true
* John Lam's RubyCLR - http://www.rubyclr.com
  * Originally, I used RubyCLR to call into the Managed SSPI sample which
really helped me decode what the SSPI interface did and how it worked. I did
not end up using that implementation but it was great for research.
* The NTLM Authentication Protocol - The definitive explanation for the NTLM
protocol (outside MS internal documents, I presume).
  * http://davenport.sourceforge.net/ntlm.html
* Ruby/NTLM - A pure Ruby implementation of the NTLM protocol. Again, not
used in this project but invaluable for decoding NTLM messages and figuring
out what SSPI was returning.
  * http://rubyforge.org/projects/rubyntlm/
* Seamonkey/Mozilla NTLM implementation - The only source for an
implementation in an actual browser. How they figured out how to use SSPI
themselves is beyond me.
  *
http://lxr.mozilla.org/seamonkey/source/mailnews/base/util/nsMsgProtocol.cpp#899

And of course, thanks to my Lord and Savior, Jesus Christ. In the name of
the Father, the Son, and the Holy Spirit.