class Win32::SSPI::NegotiateAuth

Handles “Negotiate” type authentication. Geared towards authenticating with a proxy server over HTTP

Constants

B64_TOKEN_PREFIX

NTLM tokens start with this header always. Encoding alone adds “==” and newline, so remove those

REQUEST_FLAGS

Default request flags for SSPI functions

Attributes

context[RW]
contextAttributes[RW]
credentials[RW]
domain[RW]
user[RW]

Public Class Methods

new(user = nil, domain = nil) click to toggle source

Creates a new instance ready for authentication as the given user in the given domain. Defaults to current user and domain as defined by ENV and ENV if no arguments are supplied.

# File win32/lib/win32/sspi.rb, line 238
def initialize(user = nil, domain = nil)
        if user.nil? && domain.nil? && ENV["USERNAME"].nil? && ENV["USERDOMAIN"].nil?
                raise "A username or domain must be supplied since they cannot be retrieved from the environment"
        end

        @user = user || ENV["USERNAME"]
        @domain = domain || ENV["USERDOMAIN"]
end
proxy_auth_get(http, path, user = nil, domain = nil) click to toggle source

Given a connection and a request path, performs authentication as the current user and returns the response from a GET request. The connnection should be a Net::HTTP object, and it should have been constructed using the Net::HTTP.Proxy method, but anything that responds to “get” will work. If a user and domain are given, will authenticate as the given user. Returns the response received from the get method (usually Net::HTTPResponse)

# File win32/lib/win32/sspi.rb, line 223
def NegotiateAuth.proxy_auth_get(http, path, user = nil, domain = nil)
        raise "http must respond to :get" unless http.respond_to?(:get)
        nego_auth = self.new user, domain

        resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.get_initial_token }
        if resp["Proxy-Authenticate"]
                resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(resp["Proxy-Authenticate"].split(" ").last.strip) }
        end

        resp
end

Public Instance Methods

complete_authentication(token) click to toggle source

Takes a token and gets the next token in the Negotiate authentication chain. Token can be Base64 encoded or not. The token can include the “Negotiate” header and it will be stripped. Does not indicate if SEC_I_CONTINUE or SEC_E_OK was returned. Token returned is Base64 encoded w/ all new lines removed.

# File win32/lib/win32/sspi.rb, line 271
              def complete_authentication(token)
                      raise "This object is no longer usable because its resources have been freed." if @cleaned_up

                      # Nil token OK, just set it to empty string
                      token = "" if token.nil?

                      if token.include? "Negotiate"
                              # If the Negotiate prefix is passed in, assume we are seeing "Negotiate <token>" and get the token.
                              token = token.split(" ").last
                      end

                      if token.include? B64_TOKEN_PREFIX
                              # indicates base64 encoded token
token = token.strip.unpack("m")[0]
                      end

                      outputBuffer = SecurityBuffer.new
                      result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, @context.to_p, nil,
                              REQUEST_FLAGS, 0, SECURITY_NETWORK_DREP, SecurityBuffer.new(token).to_p, 0,
                              @context.to_p,
                              outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p))

                      if result.ok? then
                              return encode_token(outputBuffer.token)
                      else
                              raise "Error: #{result.to_s}"
                      end
              ensure
                      # need to make sure we don't clean up if we've already cleaned up.
                      clean_up unless @cleaned_up
              end
get_initial_token() click to toggle source

Gets the initial Negotiate token. Returns it as a base64 encoded string suitable for use in HTTP. Can be easily decoded, however.

# File win32/lib/win32/sspi.rb, line 249
def get_initial_token
        raise "This object is no longer usable because its resources have been freed." if @cleaned_up
        get_credentials

        outputBuffer = SecurityBuffer.new
        @context = CtxtHandle.new
        @contextAttributes = "\0" * 4

        result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, nil, nil,
                REQUEST_FLAGS,0, SECURITY_NETWORK_DREP, nil, 0, @context.to_p, outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p))

        if result.ok? then
                return encode_token(outputBuffer.token)
        else
                raise "Error: #{result.to_s}"
        end
end

Private Instance Methods

clean_up() click to toggle source
# File win32/lib/win32/sspi.rb, line 305
def clean_up
        # free structures allocated
        @cleaned_up = true
        API::FreeCredentialsHandle.call(@credentials.to_p)
        API::DeleteSecurityContext.call(@context.to_p)
        @context = nil
        @credentials = nil
        @contextAttributes = nil
end
encode_token(t) click to toggle source
# File win32/lib/win32/sspi.rb, line 325
                def encode_token(t)
                        # encode64 will add newlines every 60 characters so we need to remove those.
[t].pack("m").delete("\n")
                end
get_credentials() click to toggle source

Gets credentials based on user, domain or both. If both are nil, an error occurs

# File win32/lib/win32/sspi.rb, line 316
def get_credentials
        @credentials = CredHandle.new
        ts = TimeStamp.new
        @identity = Identity.new @user, @domain
        result = SSPIResult.new(API::AcquireCredentialsHandle.call(nil, "Negotiate", SECPKG_CRED_OUTBOUND, nil, @identity.to_p,
                nil, nil, @credentials.to_p, ts.to_p))
        raise "Error acquire credentials: #{result}" unless result.ok?
end