Module: RubySMB::Client::Authentication

Included in:
RubySMB::Client
Defined in:
lib/ruby_smb/client/authentication.rb

Overview

This module holds all the backend client methods for authentication.

Instance Method Summary collapse

Instance Method Details

#authenticateWindowsError::NTStatus

Responsible for handling Authentication and Session Setup for the SMB Client. It returns the final Status code from the authentication exchange.

Returns:

  • (WindowsError::NTStatus)

    the NTStatus object from the SessionSetup exchange.



11
12
13
14
15
16
17
18
19
20
21
# File 'lib/ruby_smb/client/authentication.rb', line 11

def authenticate
  if self.smb1
    if self.username.empty? && self.password.empty?
      smb1_anonymous_auth
    else
      smb1_authenticate
    end
  else
    smb2_authenticate
  end
end

#smb1_anonymous_authWindowsError::ErrorCode

Attempts an Anonymous logon to the remote server.

Returns:

  • (WindowsError::ErrorCode)

    the status code the server returned



30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/ruby_smb/client/authentication.rb', line 30

def smb1_anonymous_auth
  request       = smb1_anonymous_auth_request
  raw_response  = send_recv(request)
  response      = smb1_anonymous_auth_response(raw_response)
  response_code = response.status_code

  if response_code.name == "STATUS_SUCCESS"
    self.user_id  = response.smb_header.uid
    self.peer_native_os = response.data_block.native_os
  end

  response_code
end

#smb1_anonymous_auth_requestObject

Creates a SessionSetupRequest for an anonymous access session.



46
47
48
49
50
51
52
53
# File 'lib/ruby_smb/client/authentication.rb', line 46

def smb1_anonymous_auth_request
  packet = RubySMB::SMB1::Packet::SessionSetupLegacyRequest.new
  packet.data_block.oem_password = "\x00"
  packet.parameter_block.max_buffer_size = 4356
  packet.parameter_block.max_mpx_count = 50
  packet.parameter_block.capabilities.extended_security = 0
  packet
end

#smb1_anonymous_auth_response(raw_response) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ruby_smb/client/authentication.rb', line 55

def smb1_anonymous_auth_response(raw_response)
  begin
    packet = RubySMB::SMB1::Packet::SessionSetupLegacyResponse.read(raw_response)
  rescue
    packet = RubySMB::SMB1::Packet::EmptyPacket.read(raw_response)
  end

  unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
  end
  packet
end

#smb1_authenticateObject

Handles the SMB1 NTLMSSP 4-way handshake for Authentication



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ruby_smb/client/authentication.rb', line 69

def smb1_authenticate
  response = smb1_ntlmssp_negotiate
  challenge_packet = smb1_ntlmssp_challenge_packet(response)
  user_id = challenge_packet.smb_header.uid
  challenge_message = smb1_type2_message(challenge_packet)
  raw = smb1_ntlmssp_authenticate(challenge_message, user_id)
  response = smb1_ntlmssp_final_packet(raw)
  response_code = response.status_code

  if response_code.name == "STATUS_SUCCESS"
    self.user_id  = user_id
    self.peer_native_os = response.data_block.native_os
  end

  response_code
end

#smb1_ntlmssp_auth_packet(type2_string, user_id) ⇒ RubySMB::SMB1::Packet::SessionSetupRequest

Generates the SMB1::Packet::SessionSetupRequest packet with the NTLM Type 3 (Auth) message in the security_blob field.

Parameters:

  • type2_string (String)

    the Base64 encoded Type2 challenge to respond to

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:



113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ruby_smb/client/authentication.rb', line 113

def smb1_ntlmssp_auth_packet(type2_string,user_id)
  type3_message = ntlm_client.init_context(type2_string)
  self.session_key = ntlm_client.session_key
  packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
  packet.smb_header.uid = user_id
  packet.set_type3_blob(type3_message.serialize)
  packet.parameter_block.max_buffer_size = 4356
  packet.parameter_block.max_mpx_count = 50
  packet.smb_header.flags2.extended_security = 1
  packet
end

#smb1_ntlmssp_authenticate(type2_string, user_id) ⇒ String

Takes the Base64 encoded NTLM Type 2 (Challenge) message and calls the routines to build the Auth packet, sends the packet and receives the raw response

Parameters:

  • type2_string (String)

    the Base64 Encoded NTLM Type 2 message

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:

  • (String)

    the raw binary response from the server



102
103
104
105
# File 'lib/ruby_smb/client/authentication.rb', line 102

def smb1_ntlmssp_authenticate(type2_string,user_id)
  packet = smb1_ntlmssp_auth_packet(type2_string,user_id)
  send_recv(packet)
end

#smb1_ntlmssp_challenge_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB1::Packet::SessionSetupResponse



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/ruby_smb/client/authentication.rb', line 155

def smb1_ntlmssp_challenge_packet(raw_response)
  packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
  status_code = packet.status_code

  unless status_code.name == "STATUS_MORE_PROCESSING_REQUIRED"
    raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
  end

  unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
  end
  packet
end

#smb1_ntlmssp_final_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB1::Packet::SessionSetupResponse



141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/ruby_smb/client/authentication.rb', line 141

def smb1_ntlmssp_final_packet(raw_response)
  begin
    packet = RubySMB::SMB1::Packet::SessionSetupResponse.read(raw_response)
  rescue
    packet = RubySMB::SMB1::Packet::EmptyPacket.read(raw_response)
  end

  unless packet.smb_header.command == RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb_header.command} and not #{RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP}"
  end
  packet
end

#smb1_ntlmssp_negotiateString

Sends the SMB1::Packet::SessionSetupRequest packet and receives the response.

Returns:

  • (String)

    the binary string response from the server



90
91
92
93
# File 'lib/ruby_smb/client/authentication.rb', line 90

def smb1_ntlmssp_negotiate
  packet = smb1_ntlmssp_negotiate_packet
  send_recv(packet)
end

#smb1_ntlmssp_negotiate_packetRubySMB::SMB1::Packet::SessionSetupRequest

Creates the SMB1::Packet::SessionSetupRequest packet for the first part of the NTLMSSP 4-way hnadshake. This packet initializes negotiations for the NTLMSSP authentication

Returns:



130
131
132
133
134
135
136
137
138
# File 'lib/ruby_smb/client/authentication.rb', line 130

def smb1_ntlmssp_negotiate_packet
  type1_message = ntlm_client.init_context
  packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
  packet.set_type1_blob(type1_message.serialize)
  packet.parameter_block.max_buffer_size = 4356
  packet.parameter_block.max_mpx_count = 50
  packet.smb_header.flags2.extended_security = 1
  packet
end

#smb1_type2_message(response_packet) ⇒ String

Parses out the NTLM Type 2 Message from a SMB1::Packet::SessionSetupResponse

Parameters:

Returns:

  • (String)

    the base64 encoded NTLM Challenge (Type2 Message) from the response



173
174
175
176
177
178
# File 'lib/ruby_smb/client/authentication.rb', line 173

def smb1_type2_message(response_packet)
  sec_blob = response_packet.data_block.security_blob
  ntlmssp_offset = sec_blob.index("NTLMSSP")
  type2_blob = sec_blob.slice(ntlmssp_offset..-1)
  [type2_blob].pack("m")
end

#smb2_authenticateObject

Handles the SMB1 NTLMSSP 4-way handshake for Authentication



185
186
187
188
189
190
191
192
193
194
# File 'lib/ruby_smb/client/authentication.rb', line 185

def smb2_authenticate
  response = smb2_ntlmssp_negotiate
  challenge_packet = smb2_ntlmssp_challenge_packet(response)
  session_id = challenge_packet.smb2_header.session_id
  self.session_id = session_id
  challenge_message = smb2_type2_message(challenge_packet)
  raw = smb2_ntlmssp_authenticate(challenge_message, session_id)
  response = smb2_ntlmssp_final_packet(raw)
  response.status_code
end

#smb2_ntlmssp_auth_packet(type2_string, session_id) ⇒ RubySMB::SMB2::Packet::SessionSetupRequest

Generates the SMB2::Packet::SessionSetupRequest packet with the NTLM Type 3 (Auth) message in the security_blob field.

Parameters:

  • type2_string (String)

    the Base64 encoded Type2 challenge to respond to

  • session_id (Integer)

    the temporary session id from the Type 2 response

Returns:



273
274
275
276
277
278
279
280
# File 'lib/ruby_smb/client/authentication.rb', line 273

def smb2_ntlmssp_auth_packet(type2_string, session_id)
  type3_message = ntlm_client.init_context(type2_string)
  self.session_key = ntlm_client.session_key
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
  packet.smb2_header.session_id = session_id
  packet.set_type3_blob(type3_message.serialize)
  packet
end

#smb2_ntlmssp_authenticate(type2_string, user_id) ⇒ String

Takes the Base64 encoded NTLM Type 2 (Challenge) message and calls the routines to build the Auth packet, sends the packet and receives the raw response

Parameters:

  • type2_string (String)

    the Base64 Encoded NTLM Type 2 message

  • user_id (Integer)

    the temporary user ID from the Type 2 response

Returns:

  • (String)

    the raw binary response from the server



262
263
264
265
# File 'lib/ruby_smb/client/authentication.rb', line 262

def smb2_ntlmssp_authenticate(type2_string,user_id)
  packet = smb2_ntlmssp_auth_packet(type2_string,user_id)
  send_recv(packet)
end

#smb2_ntlmssp_challenge_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB2::Packet::SessionSetupResponse



206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/ruby_smb/client/authentication.rb', line 206

def smb2_ntlmssp_challenge_packet(raw_response)
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
  status_code = packet.status_code
  unless status_code.name == "STATUS_MORE_PROCESSING_REQUIRED"
    raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
  end

  unless packet.smb2_header.command == RubySMB::SMB2::Commands::SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb2_header.command} and not #{RubySMB::SMB2::Commands::SESSION_SETUP}"
  end
  packet
end

#smb2_ntlmssp_final_packet(raw_response) ⇒ Object

Takes the raw binary string and returns a SMB2::Packet::SessionSetupResponse



197
198
199
200
201
202
203
# File 'lib/ruby_smb/client/authentication.rb', line 197

def smb2_ntlmssp_final_packet(raw_response)
  packet = RubySMB::SMB2::Packet::SessionSetupResponse.read(raw_response)
  unless packet.smb2_header.command == RubySMB::SMB2::Commands::SESSION_SETUP
    raise RubySMB::Error::InvalidPacket, "Command was #{packet.smb2_header.command} and not #{RubySMB::SMB2::Commands::SESSION_SETUP}"
  end
  packet
end

#smb2_ntlmssp_negotiateString

Sends the SMB2::Packet::SessionSetupRequest packet and receives the response.

Returns:

  • (String)

    the binary string response from the server



223
224
225
226
# File 'lib/ruby_smb/client/authentication.rb', line 223

def smb2_ntlmssp_negotiate
  packet = smb2_ntlmssp_negotiate_packet
  send_recv(packet)
end

#smb2_ntlmssp_negotiate_packetRubySMB::SMB2::Packet::SessionSetupRequest

Creates the SMB2::Packet::SessionSetupRequest packet for the first part of the NTLMSSP 4-way handshake. This packet initializes negotiations for the NTLMSSP authentication

Returns:



233
234
235
236
237
238
239
240
241
242
# File 'lib/ruby_smb/client/authentication.rb', line 233

def smb2_ntlmssp_negotiate_packet
  type1_message = ntlm_client.init_context
  packet = RubySMB::SMB2::Packet::SessionSetupRequest.new
  packet.set_type1_blob(type1_message.serialize)
  # This Message ID should always be 1, but thanks to Multi-Protocol Negotiation
  # the Message ID can be out of sync at this point so we re-synch it here.
  packet.smb2_header.message_id = 1
  self.smb2_message_id = 2
  packet
end

#smb2_type2_message(response_packet) ⇒ String

Parses out the NTLM Type 2 Message from a SMB2::Packet::SessionSetupResponse

Parameters:

Returns:

  • (String)

    the base64 encoded NTLM Challenge (Type2 Message) from the response



248
249
250
251
252
253
# File 'lib/ruby_smb/client/authentication.rb', line 248

def smb2_type2_message(response_packet)
  sec_blob = response_packet.buffer
  ntlmssp_offset = sec_blob.index("NTLMSSP")
  type2_blob = sec_blob.slice(ntlmssp_offset..-1)
  [type2_blob].pack("m")
end