Class: Win32::SSPI::NegotiateAuth

Inherits:
Object
  • Object
show all
Defined in:
lib/win32/sspi.rb

Overview

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

Constant Summary collapse

REQUEST_FLAGS =

Default request flags for SSPI functions

ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION
B64_TOKEN_PREFIX =

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

Base64.encode64("NTLMSSP").delete("=\n")

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user = nil, domain = nil) ⇒ NegotiateAuth

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.



243
244
245
246
247
248
249
250
251
# File 'lib/win32/sspi.rb', line 243

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"]
	@cleaned_up = nil
end

Instance Attribute Details

#contextObject

Returns the value of attribute context.



210
211
212
# File 'lib/win32/sspi.rb', line 210

def context
  @context
end

#contextAttributesObject

Returns the value of attribute contextAttributes.



210
211
212
# File 'lib/win32/sspi.rb', line 210

def contextAttributes
  @contextAttributes
end

#credentialsObject

Returns the value of attribute credentials.



210
211
212
# File 'lib/win32/sspi.rb', line 210

def credentials
  @credentials
end

#domainObject

Returns the value of attribute domain.



210
211
212
# File 'lib/win32/sspi.rb', line 210

def domain
  @domain
end

#userObject

Returns the value of attribute user.



210
211
212
# File 'lib/win32/sspi.rb', line 210

def user
  @user
end

Class Method Details

.proxy_auth_get(http, path, user = nil, domain = nil) ⇒ Object

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)



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File '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("Negotiate") }
	# Negotiate may not be supported, so we need to look for NTLM too.
	
	if resp["Proxy-Authenticate"].include? "Negotiate"
		resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(resp["Proxy-Authenticate"].split(" ").last.strip) }
	elsif resp["Proxy-Authenticate"].include? "NTLM"
		resp = http.get path, { "Proxy-Authorization" => "NTLM " + nego_auth.get_initial_token("NTLM") }
		resp = http.get path, { "Proxy-Authorization" => "NTLM " + nego_auth.complete_authentication(resp["Proxy-Authenticate"].split(" ").last.strip) }
	end

	resp
end

Instance Method Details

#complete_authentication(token) ⇒ Object

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.



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/win32/sspi.rb', line 279

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") || token.include?("NTLM")
		# 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 = Base64.decode64(token.strip)
	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(auth_type) ⇒ Object

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

auth_type is the authentication method being used. It is usually “Negotiate” or “NTLM”.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/win32/sspi.rb', line 257

def get_initial_token(auth_type)
	raise "This object is no longer usable because its resources have been freed." if @cleaned_up
	get_credentials auth_type

	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