diff --git a/lib/ruby_smb/client.rb b/lib/ruby_smb/client.rb index 47329309..1da4625c 100644 --- a/lib/ruby_smb/client.rb +++ b/lib/ruby_smb/client.rb @@ -517,6 +517,18 @@ def send_recv(packet, encrypt: false) break end unless version == 'SMB1' + # Handle STATUS_NETWORK_SESSION_EXPIRED. The 'net use' client upon receiving this error will automatically attempt + # to re-authenticate, which makes relaying ntlm authentication to multiple targets possible. This block ensures + # ruby_smb behaves in the same manner as 'net use'. + if smb2_header && smb2_header.nt_status == WindowsError::NTStatus::STATUS_NETWORK_SESSION_EXPIRED && !@mech_type.nil? + if @mech_type == :ntlm || @mech_type == :anonymous + session_setup(self.username, self.password, self.domain, local_workstation: self.local_workstation, ntlm_flags: NTLM::DEFAULT_CLIENT_FLAGS) + raw_response = send_recv(packet, encrypt: encrypt) # Retry the request after re-authentication + elsif @mech_type == :kerberos + raise RubySMB::Error::RubySMBError, 'WindowsError::NTStatus::STATUS_NETWORK_SESSION_EXPIRED received, but kerberos authentication is being used, so automatic re-authentication cannot be attempted.' + end + end + self.sequence_counter += 1 if signing_required && !session_key.empty? # update the SMB2 message ID according to the received Credit Charged self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && self.server_supports_multi_credit diff --git a/lib/ruby_smb/client/authentication.rb b/lib/ruby_smb/client/authentication.rb index 4be98535..710aff51 100644 --- a/lib/ruby_smb/client/authentication.rb +++ b/lib/ruby_smb/client/authentication.rb @@ -31,6 +31,7 @@ def authenticate # # @return [WindowsError::ErrorCode] the status code the server returned def smb1_anonymous_auth + @mech_type = :anonymous request = smb1_anonymous_auth_request raw_response = send_recv(request) response = smb1_anonymous_auth_response(raw_response) @@ -73,6 +74,7 @@ def smb1_anonymous_auth_response(raw_response) # Handles the SMB1 NTLMSSP 4-way handshake for Authentication and store # information about the peer/server. def smb1_authenticate + @mech_type = :ntlm response = smb1_ntlmssp_negotiate challenge_packet = smb1_ntlmssp_challenge_packet(response) @@ -205,6 +207,7 @@ def smb1_type2_message(response_packet) # Handles the SMB2 NTLMSSP 4-way handshake for Authentication and store # information about the peer/server. def smb2_authenticate + @mech_type = :ntlm response = smb2_ntlmssp_negotiate challenge_packet = smb2_ntlmssp_challenge_packet(response) if @dialect == '0x0311'