# 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 ext/win32/lib/win32/sspi.rb, line 245
    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 connection 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 ext/win32/lib/win32/sspi.rb, line 230
    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 ext/win32/lib/win32/sspi.rb, line 278
    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 ext/win32/lib/win32/sspi.rb, line 256
    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::InitializeSecurityContextA.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 ext/win32/lib/win32/sspi.rb, line 312
    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 ext/win32/lib/win32/sspi.rb, line 332
    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 ext/win32/lib/win32/sspi.rb, line 323
    def get_credentials
      @credentials = CredHandle.new
      ts = TimeStamp.new
      @identity = Identity.new @user, @domain
      result = SSPIResult.new(API::AcquireCredentialsHandleA.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