class Net::NTLM::Client::Session

Constants

CLIENT_TO_SERVER_SEALING
CLIENT_TO_SERVER_SIGNING
MAX64
SERVER_TO_CLIENT_SEALING
SERVER_TO_CLIENT_SIGNING
TIME_OFFSET
VERSION_MAGIC

Attributes

challenge_message[R]
channel_binding[R]
client[R]

Public Class Methods

new(client, challenge_message, channel_binding = nil) click to toggle source

@param client [Net::NTLM::Client] the client instance @param challenge_message [Net::NTLM::Message::Type2] server message

# File lib/net/ntlm/client/session.rb, line 17
def initialize(client, challenge_message, channel_binding = nil)
  @client = client
  @challenge_message = challenge_message
  @channel_binding = channel_binding
end

Public Instance Methods

authenticate!() click to toggle source

Generate an NTLMv2 AUTHENTICATE_MESSAGE @see msdn.microsoft.com/en-us/library/cc236643.aspx @return [Net::NTLM::Message::Type3]

# File lib/net/ntlm/client/session.rb, line 26
def authenticate!
  calculate_user_session_key!
  type3_opts = {
    :lm_response   => is_anonymous? ? "\x00".b : lmv2_resp,
    :ntlm_response => is_anonymous? ? '' : ntlmv2_resp,
    :domain        => domain,
    :user          => username,
    :workstation   => workstation,
    :flag          => (challenge_message.flag & client.flags)
  }
  t3 = Message::Type3.create type3_opts
  if negotiate_key_exchange?
    t3.enable(:session_key)
    rc4 = Net::NTLM::Rc4.new(user_session_key)
    sk = rc4.encrypt exported_session_key
    t3.session_key = sk
  end
  t3
end
exported_session_key() click to toggle source
# File lib/net/ntlm/client/session.rb, line 46
def exported_session_key
  @exported_session_key ||=
    begin
      if negotiate_key_exchange?
        OpenSSL::Random.random_bytes(16)
      else
        user_session_key
      end
    end
end
is_anonymous?() click to toggle source
# File lib/net/ntlm/client/session.rb, line 83
def is_anonymous?
  username == '' && password == ''
end
seal_message(message) click to toggle source
# File lib/net/ntlm/client/session.rb, line 75
def seal_message(message)
  client_cipher.encrypt(message)
end
sign_message(message) click to toggle source
# File lib/net/ntlm/client/session.rb, line 57
def sign_message(message)
  seq = sequence
  sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, client_sign_key, "#{seq}#{message}")[0..7]
  if negotiate_key_exchange?
    sig = client_cipher.encrypt sig
  end
  "#{VERSION_MAGIC}#{sig}#{seq}"
end
unseal_message(emessage) click to toggle source
# File lib/net/ntlm/client/session.rb, line 79
def unseal_message(emessage)
  server_cipher.encrypt(emessage)
end
verify_signature(signature, message) click to toggle source
# File lib/net/ntlm/client/session.rb, line 66
def verify_signature(signature, message)
  seq = signature[-4..-1]
  sig = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, server_sign_key, "#{seq}#{message}")[0..7]
  if negotiate_key_exchange?
    sig = server_cipher.encrypt sig
  end
  "#{VERSION_MAGIC}#{sig}#{seq}" == signature
end

Private Instance Methods

blob() click to toggle source
# File lib/net/ntlm/client/session.rb, line 202
def blob
  @blob ||=
    begin
      b = Blob.new
      b.timestamp = timestamp
      b.challenge = client_challenge
      b.target_info = target_info
      b.serialize
    end
end
calculate_user_session_key!() click to toggle source
# File lib/net/ntlm/client/session.rb, line 181
def calculate_user_session_key!
  if is_anonymous?
    # see MS-NLMP section 3.4
    @user_session_key = "\x00".b * 16
  else
    @user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
  end
end
client_challenge() click to toggle source
# File lib/net/ntlm/client/session.rb, line 130
def client_challenge
  @client_challenge ||= NTLM.pack_int64le(rand(MAX64))
end
client_cipher() click to toggle source
# File lib/net/ntlm/client/session.rb, line 122
def client_cipher
  @client_cipher ||= Net::NTLM::Rc4.new(client_seal_key)
end
client_seal_key() click to toggle source
# File lib/net/ntlm/client/session.rb, line 114
def client_seal_key
  @client_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SEALING}"
end
client_sign_key() click to toggle source
# File lib/net/ntlm/client/session.rb, line 106
def client_sign_key
  @client_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SIGNING}"
end
domain() click to toggle source
# File lib/net/ntlm/client/session.rb, line 165
def domain
  (client.domain ? oem_or_unicode_str(client.domain) : "")
end
lmv2_resp() click to toggle source
# File lib/net/ntlm/client/session.rb, line 190
def lmv2_resp
  OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + client_challenge) + client_challenge
end
negotiate_key_exchange?() click to toggle source
# File lib/net/ntlm/client/session.rb, line 149
def negotiate_key_exchange?
  challenge_message.has_flag? :KEY_EXCHANGE
end
nt_proof_str() click to toggle source
# File lib/net/ntlm/client/session.rb, line 198
def nt_proof_str
  @nt_proof_str ||= OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + blob)
end
ntlmv2_hash() click to toggle source
# File lib/net/ntlm/client/session.rb, line 177
def ntlmv2_hash
  @ntlmv2_hash ||= NTLM.ntlmv2_hash(username, password, domain, {:client_challenge => client_challenge, :unicode => !use_oem_strings?})
end
ntlmv2_resp() click to toggle source
# File lib/net/ntlm/client/session.rb, line 194
def ntlmv2_resp
  nt_proof_str + blob
end
oem_or_unicode_str(str) click to toggle source
# File lib/net/ntlm/client/session.rb, line 169
def oem_or_unicode_str(str)
  if use_oem_strings?
    NTLM::EncodeUtil.decode_utf16le str
  else
    NTLM::EncodeUtil.encode_utf16le str
  end
end
password() click to toggle source
# File lib/net/ntlm/client/session.rb, line 157
def password
  oem_or_unicode_str client.password
end
raw_sequence() click to toggle source
# File lib/net/ntlm/client/session.rb, line 98
def raw_sequence
  if defined? @raw_sequence
    @raw_sequence += 1
  else
    @raw_sequence = 0
  end
end
sequence() click to toggle source
# File lib/net/ntlm/client/session.rb, line 94
def sequence
  [raw_sequence].pack("V*")
end
server_challenge() click to toggle source
# File lib/net/ntlm/client/session.rb, line 134
def server_challenge
  @server_challenge ||= challenge_message[:challenge].serialize
end
server_cipher() click to toggle source
# File lib/net/ntlm/client/session.rb, line 126
def server_cipher
  @server_cipher ||= Net::NTLM::Rc4.new(server_seal_key)
end
server_seal_key() click to toggle source
# File lib/net/ntlm/client/session.rb, line 118
def server_seal_key
  @server_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SEALING}"
end
server_sign_key() click to toggle source
# File lib/net/ntlm/client/session.rb, line 110
def server_sign_key
  @server_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SIGNING}"
end
target_info() click to toggle source
# File lib/net/ntlm/client/session.rb, line 213
def target_info
  @target_info ||= begin
    if channel_binding
      t = Net::NTLM::TargetInfo.new(challenge_message.target_info)
      av_id = Net::NTLM::TargetInfo::MSV_AV_CHANNEL_BINDINGS
      t.av_pairs[av_id] = channel_binding.channel_binding_token
      t.to_s
    else
      challenge_message.target_info
    end
  end
end
timestamp() click to toggle source

epoch -> milsec from Jan 1, 1601 @see support.microsoft.com/kb/188768

# File lib/net/ntlm/client/session.rb, line 140
def timestamp
  @timestamp ||= 10_000_000 * (Time.now.to_i + TIME_OFFSET)
end
use_oem_strings?() click to toggle source
# File lib/net/ntlm/client/session.rb, line 144
def use_oem_strings?
  # @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832
  !challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
end
user_session_key() click to toggle source
# File lib/net/ntlm/client/session.rb, line 90
def user_session_key
  @user_session_key ||=  nil
end
username() click to toggle source
# File lib/net/ntlm/client/session.rb, line 153
def username
  oem_or_unicode_str client.username
end
workstation() click to toggle source
# File lib/net/ntlm/client/session.rb, line 161
def workstation
  (client.workstation ? oem_or_unicode_str(client.workstation) : "")
end