rubyntlm-0.6.5/0000755000004100000410000000000014634001700013421 5ustar www-datawww-datarubyntlm-0.6.5/rubyntlm.gemspec0000644000004100000410000000176014634001700016646 0ustar www-datawww-datarequire File.join(File.dirname(__FILE__), 'lib', 'net', 'ntlm', 'version') Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = 'rubyntlm' s.version = Net::NTLM::VERSION::STRING s.summary = 'Ruby/NTLM library.' s.description = 'Ruby/NTLM provides message creator and parser for the NTLM authentication.' s.authors = ['Kohei Kajimoto','Paul Morton'] s.email = ['koheik@gmail.com','paul.e.morton@gmail.com'] s.homepage = 'https://github.com/winrb/rubyntlm' s.files = `git ls-files`.split($/) s.test_files = s.files.grep(%r{^(test|spec|features)/}) s.require_paths = ["lib"] s.required_ruby_version = '>= 2.6.0' s.license = 'MIT' s.add_development_dependency 'github_changelog_generator', '1.14.3' s.add_development_dependency "pry" s.add_development_dependency "rake" s.add_development_dependency "rspec", ">= 2.11" s.add_development_dependency "simplecov" s.add_dependency "base64" s.metadata["rubygems_mfa_required"] = "true" end rubyntlm-0.6.5/.gitignore0000644000004100000410000000004614634001700015411 0ustar www-datawww-data.bundle .yardoc /doc Gemfile.lock pkg rubyntlm-0.6.5/.github/0000755000004100000410000000000014634001700014761 5ustar www-datawww-datarubyntlm-0.6.5/.github/dependabot.yml0000644000004100000410000000032514634001700017611 0ustar www-datawww-dataversion: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" - package-ecosystem: "devcontainers" directory: "/" schedule: interval: weekly rubyntlm-0.6.5/.github/workflows/0000755000004100000410000000000014634001700017016 5ustar www-datawww-datarubyntlm-0.6.5/.github/workflows/build.yml0000644000004100000410000000070614634001700020643 0ustar www-datawww-data--- name: build "on": pull_request: push: branches: - master jobs: unit: strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-2019] ruby: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - run: bundle exec rake rubyntlm-0.6.5/lib/0000755000004100000410000000000014634001700014167 5ustar www-datawww-datarubyntlm-0.6.5/lib/rubyntlm.rb0000644000004100000410000000002214634001700016362 0ustar www-datawww-datarequire 'net/ntlm'rubyntlm-0.6.5/lib/net/0000755000004100000410000000000014634001700014755 5ustar www-datawww-datarubyntlm-0.6.5/lib/net/ntlm.rb0000644000004100000410000002123714634001700016261 0ustar www-datawww-data# encoding: UTF-8 # # = net/ntlm.rb # # An NTLM Authentication Library for Ruby # # This code is a derivative of "dbf2.rb" written by yrock # and Minero Aoki. You can find original code here: # http://jp.rubyist.net/magazine/?0013-CodeReview # ------------------------------------------------------------- # Copyright (c) 2005,2006 yrock # # # 2006-02-11 refactored by Minero Aoki # ------------------------------------------------------------- # # All protocol information used to write this code stems from # "The NTLM Authentication Protocol" by Eric Glass. The author # would thank to him for this tremendous work and making it # available on the net. # http://davenport.sourceforge.net/ntlm.html # ------------------------------------------------------------- # Copyright (c) 2003 Eric Glass # # ------------------------------------------------------------- # # The author also looked Mozilla-Firefox-1.0.7 source code, # namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and # Jonathan Bastien-Filiatrault's libntlm-ruby. # "http://x2a.org/websvn/filedetails.php? # repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1" # The latter has a minor bug in its separate_keys function. # The third key has to begin from the 14th character of the # input string instead of 13th:) #-- # $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $ #++ require 'base64' require 'openssl' require 'openssl/digest' require 'socket' # Load Order is important here require 'net/ntlm/exceptions' require 'net/ntlm/field' require 'net/ntlm/int16_le' require 'net/ntlm/int32_le' require 'net/ntlm/int64_le' require 'net/ntlm/string' require 'net/ntlm/field_set' require 'net/ntlm/blob' require 'net/ntlm/security_buffer' require 'net/ntlm/message' require 'net/ntlm/message/type0' require 'net/ntlm/message/type1' require 'net/ntlm/message/type2' require 'net/ntlm/message/type3' require 'net/ntlm/encode_util' require 'net/ntlm/md4' require 'net/ntlm/rc4' require 'net/ntlm/client' require 'net/ntlm/channel_binding' require 'net/ntlm/target_info' module Net module NTLM LM_MAGIC = "KGS!@\#$%" TIME_OFFSET = 11644473600 MAX64 = 0xffffffffffffffff class << self # Valid format for LAN Manager hex digest portion: 32 hexadecimal characters. LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i # Valid format for NT LAN Manager hex digest portion: 32 hexadecimal characters. NT_LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i # Valid format for an NTLM hash composed of `':'`. DATA_REGEXP = /\A#{LAN_MANAGER_HEX_DIGEST_REGEXP}:#{NT_LAN_MANAGER_HEX_DIGEST_REGEXP}\z/ # Takes a string and determines whether it is a valid NTLM Hash # @param [String] the string to validate # @return [Boolean] whether or not the string is a valid NTLM hash def is_ntlm_hash?(data) decoded_data = data.dup decoded_data = EncodeUtil.decode_utf16le(decoded_data) if DATA_REGEXP.match(decoded_data) true else false end end # Convert the value to a 64-bit little-endian integer # @param [String] val The string to convert def pack_int64le(val) [val & 0x00000000ffffffff, val >> 32].pack("V2") end # Builds an array of strings that are 7 characters long # @param [String] str The string to split # @api private def split7(str) s = str.dup until s.empty? (ret ||= []).push s.slice!(0, 7) end ret end # Each byte of a DES key contains seven bits of key material and one odd-parity bit. # The parity bit should be set so that there are an odd number of 1 bits in each byte. # @param [String] str String to generate keys for # @api private def gen_keys(str) split7(str).map{ |str7| bits = split7(str7.unpack("B*")[0]).inject('')\ {|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s } [bits].pack("B*") } end def apply_des(plain, keys) keys.map {|k| # Spec requires des-cbc, but openssl 3 does not support single des # by default, so just do triple DES (EDE) with the same key dec = OpenSSL::Cipher.new("des-ede-cbc").encrypt dec.padding = 0 dec.key = k + k dec.update(plain) + dec.final } end # Generates a {https://en.wikipedia.org/wiki/LAN_Manager LAN Manager Hash} # @param [String] password The password to base the hash on def lm_hash(password) keys = gen_keys password.upcase.ljust(14, "\0") apply_des(LM_MAGIC, keys).join end # Generate an NTLM Hash # @param [String] password The password to base the hash on # @option opt :unicode (false) Unicode encode the password def ntlm_hash(password, opt = {}) pwd = password.dup unless opt[:unicode] pwd = EncodeUtil.encode_utf16le(pwd) end Net::NTLM::Md4.digest pwd end # Generate a NTLMv2 Hash # @param [String] user The username # @param [String] password The password # @param [String] target The domain or workstation to authenticate to # @option [Boolean] opt :unicode (false) Unicode encode the domain. def ntlmv2_hash(user, password, target, opt={}) if is_ntlm_hash? password decoded_password = EncodeUtil.decode_utf16le(password) ntlmhash = [decoded_password.upcase[33,65]].pack('H32') else ntlmhash = ntlm_hash(password, opt) end if opt[:unicode] # Uppercase operation on username containing non-ASCI characters # after behing unicode encoded with `EncodeUtil.encode_utf16le` # doesn't play well. Upcase should be done before encoding. user_upcase = EncodeUtil.decode_utf16le(user).upcase user_upcase = EncodeUtil.encode_utf16le(user_upcase) else user_upcase = user.upcase end userdomain = user_upcase + target unless opt[:unicode] userdomain = EncodeUtil.encode_utf16le(userdomain) end OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain) end def lm_response(arg) begin hash = arg[:lm_hash] chal = arg[:challenge] rescue raise ArgumentError end chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer) keys = gen_keys hash.ljust(21, "\0") apply_des(chal, keys).join end def ntlm_response(arg) hash = arg[:ntlm_hash] chal = arg[:challenge] chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer) keys = gen_keys hash.ljust(21, "\0") apply_des(chal, keys).join end def ntlmv2_response(arg, opt = {}) begin key = arg[:ntlmv2_hash] chal = arg[:challenge] ti = arg[:target_info] rescue raise ArgumentError end chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer) if opt[:client_challenge] cc = opt[:client_challenge] else cc = rand(MAX64) end cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer) if opt[:timestamp] ts = opt[:timestamp] else ts = Time.now.to_i end # epoch -> milsec from Jan 1, 1601 ts = 10_000_000 * (ts + TIME_OFFSET) blob = Blob.new blob.timestamp = ts blob.challenge = cc blob.target_info = ti bb = blob.serialize OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb end def lmv2_response(arg, opt = {}) key = arg[:ntlmv2_hash] chal = arg[:challenge] chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer) if opt[:client_challenge] cc = opt[:client_challenge] else cc = rand(MAX64) end cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer) OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc end def ntlm2_session(arg, opt = {}) begin passwd_hash = arg[:ntlm_hash] chal = arg[:challenge] rescue raise ArgumentError end chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer) if opt[:client_challenge] cc = opt[:client_challenge] else cc = rand(MAX64) end cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer) keys = gen_keys(passwd_hash.ljust(21, "\0")) session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8) response = apply_des(session_hash, keys).join [cc.ljust(24, "\0"), response] end end end end rubyntlm-0.6.5/lib/net/ntlm/0000755000004100000410000000000014634001700015727 5ustar www-datawww-datarubyntlm-0.6.5/lib/net/ntlm/int64_le.rb0000644000004100000410000000075114634001700017703 0ustar www-datawww-datamodule Net module NTLM class Int64LE < Field def initialize(opt) super(opt) @size = 8 end def parse(str, offset=0) if @active and str.size >= offset + @size d, u = str.slice(offset, @size).unpack("V2") @value = (u * 0x100000000 + d) @size else 0 end end def serialize [@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active end end end endrubyntlm-0.6.5/lib/net/ntlm/blob.rb0000644000004100000410000000146114634001700017174 0ustar www-datawww-datamodule Net module NTLM BLOB_SIGN = 0x00000101 class Blob < FieldSet int32LE :blob_signature, {:value => BLOB_SIGN} int32LE :reserved, {:value => 0} int64LE :timestamp, {:value => 0} string :challenge, {:value => "", :size => 8} int32LE :unknown1, {:value => 0} string :target_info, {:value => "", :size => 0} int32LE :unknown2, {:value => 0} def parse(str, offset=0) # 28 is the length of all fields before the variable-length # target_info field. if str.size > 28 enable(:target_info) # Grab everything except the last 4 bytes (which will be :unknown2) self[:target_info].value = str[28..-5] end super end end end end rubyntlm-0.6.5/lib/net/ntlm/rc4.rb0000644000004100000410000000260114634001700016743 0ustar www-datawww-datarequire 'openssl' module Net module NTLM begin OpenSSL::Cipher.new("rc4") rescue # libssl-3.0+ doesn't support legacy Rc4 -> use our own implementation class Rc4 def initialize(str) raise ArgumentError, "RC4: Key supplied is blank" if str.eql?('') initialize_state(str) @q1, @q2 = 0, 0 end def encrypt(text) text.each_byte.map do |b| @q1 = (@q1 + 1) % 256 @q2 = (@q2 + @state[@q1]) % 256 @state[@q1], @state[@q2] = @state[@q2], @state[@q1] b ^ @state[(@state[@q1] + @state[@q2]) % 256] end.pack("C*") end private # The initial state which is then modified by the key-scheduling algorithm INITIAL_STATE = (0..255).to_a # Performs the key-scheduling algorithm to initialize the state. def initialize_state(key) i = j = 0 @state = INITIAL_STATE.dup key_length = key.length while i < 256 j = (j + @state[i] + key.getbyte(i % key_length)) % 256 @state[i], @state[j] = @state[j], @state[i] i += 1 end end end else # Openssl/libssl provides RC4, so we can use it. class Rc4 def initialize(str) @ci = OpenSSL::Cipher.new("rc4") @ci.key = str end def encrypt(text) @ci.update(text) + @ci.final end end end end end rubyntlm-0.6.5/lib/net/ntlm/client/0000755000004100000410000000000014634001700017205 5ustar www-datawww-datarubyntlm-0.6.5/lib/net/ntlm/client/session.rb0000644000004100000410000001516314634001700021223 0ustar www-datawww-datamodule Net module NTLM class Client::Session VERSION_MAGIC = "\x01\x00\x00\x00" TIME_OFFSET = 11644473600 MAX64 = 0xffffffffffffffff CLIENT_TO_SERVER_SIGNING = "session key to client-to-server signing key magic constant\0" SERVER_TO_CLIENT_SIGNING = "session key to server-to-client signing key magic constant\0" CLIENT_TO_SERVER_SEALING = "session key to client-to-server sealing key magic constant\0" SERVER_TO_CLIENT_SEALING = "session key to server-to-client sealing key magic constant\0" attr_reader :client, :challenge_message, :channel_binding # @param client [Net::NTLM::Client] the client instance # @param challenge_message [Net::NTLM::Message::Type2] server message def initialize(client, challenge_message, channel_binding = nil) @client = client @challenge_message = challenge_message @channel_binding = channel_binding end # Generate an NTLMv2 AUTHENTICATE_MESSAGE # @see http://msdn.microsoft.com/en-us/library/cc236643.aspx # @return [Net::NTLM::Message::Type3] 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 def exported_session_key @exported_session_key ||= begin if negotiate_key_exchange? OpenSSL::Random.random_bytes(16) else user_session_key end end end 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 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 def seal_message(message) client_cipher.encrypt(message) end def unseal_message(emessage) server_cipher.encrypt(emessage) end def is_anonymous? username == '' && password == '' end private def user_session_key @user_session_key ||= nil end def sequence [raw_sequence].pack("V*") end def raw_sequence if defined? @raw_sequence @raw_sequence += 1 else @raw_sequence = 0 end end def client_sign_key @client_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SIGNING}" end def server_sign_key @server_sign_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SIGNING}" end def client_seal_key @client_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{CLIENT_TO_SERVER_SEALING}" end def server_seal_key @server_seal_key ||= OpenSSL::Digest::MD5.digest "#{exported_session_key}#{SERVER_TO_CLIENT_SEALING}" end def client_cipher @client_cipher ||= Net::NTLM::Rc4.new(client_seal_key) end def server_cipher @server_cipher ||= Net::NTLM::Rc4.new(server_seal_key) end def client_challenge @client_challenge ||= NTLM.pack_int64le(rand(MAX64)) end def server_challenge @server_challenge ||= challenge_message[:challenge].serialize end # epoch -> milsec from Jan 1, 1601 # @see http://support.microsoft.com/kb/188768 def timestamp @timestamp ||= 10_000_000 * (Time.now.to_i + TIME_OFFSET) end 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 def negotiate_key_exchange? challenge_message.has_flag? :KEY_EXCHANGE end def username oem_or_unicode_str client.username end def password oem_or_unicode_str client.password end def workstation (client.workstation ? oem_or_unicode_str(client.workstation) : "") end def domain (client.domain ? oem_or_unicode_str(client.domain) : "") end def oem_or_unicode_str(str) if use_oem_strings? NTLM::EncodeUtil.decode_utf16le str else NTLM::EncodeUtil.encode_utf16le str end end def ntlmv2_hash @ntlmv2_hash ||= NTLM.ntlmv2_hash(username, password, domain, {:client_challenge => client_challenge, :unicode => !use_oem_strings?}) end 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 def lmv2_resp OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + client_challenge) + client_challenge end def ntlmv2_resp nt_proof_str + blob end def nt_proof_str @nt_proof_str ||= OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, server_challenge + blob) end def blob @blob ||= begin b = Blob.new b.timestamp = timestamp b.challenge = client_challenge b.target_info = target_info b.serialize end end 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 end end end rubyntlm-0.6.5/lib/net/ntlm/channel_binding.rb0000644000004100000410000000432014634001700021355 0ustar www-datawww-datamodule Net module NTLM class ChannelBinding # Creates a ChannelBinding used for Extended Protection Authentication # @see http://blogs.msdn.com/b/openspecification/archive/2013/03/26/ntlm-and-channel-binding-hash-aka-exteneded-protection-for-authentication.aspx # # @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing # the outer TLS channel # @return [NTLM::ChannelBinding] A ChannelBinding holding a token that can be # embedded in a {Type3} message def self.create(outer_channel) new(outer_channel) end # @param outer_channel [OpenSSL::X509::Certificate] Server certificate securing # the outer TLS channel def initialize(outer_channel) @channel = outer_channel @unique_prefix = 'tls-server-end-point' @initiator_addtype = 0 @initiator_address_length = 0 @acceptor_addrtype = 0 @acceptor_address_length = 0 end attr_reader :channel, :unique_prefix, :initiator_addtype attr_reader :initiator_address_length, :acceptor_addrtype attr_reader :acceptor_address_length # Returns a channel binding hash acceptable for use as a AV_PAIR MsvAvChannelBindings # field value as specified in the NTLM protocol # # @return [String] MD5 hash of gss_channel_bindings_struct def channel_binding_token @channel_binding_token ||= OpenSSL::Digest::MD5.new(gss_channel_bindings_struct).digest end def gss_channel_bindings_struct @gss_channel_bindings_struct ||= begin token = [initiator_addtype].pack('I') token << [initiator_address_length].pack('I') token << [acceptor_addrtype].pack('I') token << [acceptor_address_length].pack('I') token << [application_data.length].pack('I') token << application_data token end end def channel_hash @channel_hash ||= OpenSSL::Digest::SHA256.new(channel.to_der) end def application_data @application_data ||= begin data = unique_prefix data << ':' data << channel_hash.digest data end end end end end rubyntlm-0.6.5/lib/net/ntlm/target_info.rb0000644000004100000410000000500414634001700020554 0ustar www-datawww-datamodule Net module NTLM # Represents a list of AV_PAIR structures # @see https://msdn.microsoft.com/en-us/library/cc236646.aspx class TargetInfo # Allowed AvId values for an AV_PAIR MSV_AV_EOL = "\x00\x00".freeze MSV_AV_NB_COMPUTER_NAME = "\x01\x00".freeze MSV_AV_NB_DOMAIN_NAME = "\x02\x00".freeze MSV_AV_DNS_COMPUTER_NAME = "\x03\x00".freeze MSV_AV_DNS_DOMAIN_NAME = "\x04\x00".freeze MSV_AV_DNS_TREE_NAME = "\x05\x00".freeze MSV_AV_FLAGS = "\x06\x00".freeze MSV_AV_TIMESTAMP = "\x07\x00".freeze MSV_AV_SINGLE_HOST = "\x08\x00".freeze MSV_AV_TARGET_NAME = "\x09\x00".freeze MSV_AV_CHANNEL_BINDINGS = "\x0A\x00".freeze # @param av_pair_sequence [String] AV_PAIR list from challenge message def initialize(av_pair_sequence) @av_pairs = read_pairs(av_pair_sequence) end attr_reader :av_pairs def to_s result = '' av_pairs.each do |k,v| result << k result << [v.length].pack('S') result << v end result << Net::NTLM::TargetInfo::MSV_AV_EOL result << [0].pack('S') result.force_encoding(Encoding::ASCII_8BIT) end private VALID_PAIR_ID = [ MSV_AV_EOL, MSV_AV_NB_COMPUTER_NAME, MSV_AV_NB_DOMAIN_NAME, MSV_AV_DNS_COMPUTER_NAME, MSV_AV_DNS_DOMAIN_NAME, MSV_AV_DNS_TREE_NAME, MSV_AV_FLAGS, MSV_AV_TIMESTAMP, MSV_AV_SINGLE_HOST, MSV_AV_TARGET_NAME, MSV_AV_CHANNEL_BINDINGS ].freeze def read_pairs(av_pair_sequence) offset = 0 result = {} return result if av_pair_sequence.nil? until offset >= av_pair_sequence.length id = av_pair_sequence[offset..offset+1] unless VALID_PAIR_ID.include?(id) raise Net::NTLM::InvalidTargetDataError.new( "Invalid AvId #{to_hex(id)} in AV_PAIR structure", av_pair_sequence ) end length = av_pair_sequence[offset+2..offset+3].unpack('S')[0].to_i if length > 0 value = av_pair_sequence[offset+4..offset+4+length-1] result[id] = value end offset += 4 + length end result end def to_hex(str) return nil if str.nil? str.bytes.map {|b| '0x' + b.to_s(16).rjust(2,'0').upcase}.join('-') end end end end rubyntlm-0.6.5/lib/net/ntlm/encode_util.rb0000644000004100000410000000342714634001700020554 0ustar www-datawww-datamodule Net module NTLM class EncodeUtil if RUBY_VERSION == "1.8.7" require "kconv" # Decode a UTF16 string to a ASCII string # @param [String] str The string to convert def self.decode_utf16le(str) Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16) end # Encodes a ASCII string to a UTF16 string # @param [String] str The string to convert def self.encode_utf16le(str) swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII)) end # Taggle the strings endianness between big/little and little/big # @param [String] str The string to swap the endianness on def self.swap16(str) str.unpack("v*").pack("n*") end else # Use native 1.9 string encoding functions # Decode a UTF16 string to a ASCII string # @param [String] str The string to convert def self.decode_utf16le(str) str = str.dup.force_encoding(Encoding::UTF_16LE) str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8') end # Encodes a ASCII string to a UTF16 string # @param [String] str The string to convert # @note This implementation may seem stupid but the problem is that UTF16-LE and UTF-8 are incompatiable # encodings. This library uses string contatination to build the packet bytes. The end result is that # you can either marshal the encodings elsewhere of simply know that each time you call encode_utf16le # the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte # concatination works seamlessly. def self.encode_utf16le(str) str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT') end end end end end rubyntlm-0.6.5/lib/net/ntlm/string.rb0000644000004100000410000000110114634001700017553 0ustar www-datawww-datamodule Net module NTLM class String < Field def initialize(opts) super(opts) @size = opts[:size] end def parse(str, offset=0) if @active and str.size >= offset + @size @value = str[offset, @size] @size else 0 end end def serialize if @active @value.to_s else "" end end def value=(val) @value = val @size = @value.nil? ? 0 : @value.size @active = (@size > 0) end end end endrubyntlm-0.6.5/lib/net/ntlm/md4.rb0000644000004100000410000000442114634001700016741 0ustar www-datawww-datarequire 'openssl' module Net module NTLM class Md4 begin OpenSSL::Digest::MD4.digest("") rescue # libssl-3.0+ doesn't support legacy MD4 -> use our own implementation require 'stringio' def self.digest(string) # functions mask = (1 << 32) - 1 f = proc {|x, y, z| x & y | x.^(mask) & z} g = proc {|x, y, z| x & y | x & z | y & z} h = proc {|x, y, z| x ^ y ^ z} r = proc {|v, s| (v << s).&(mask) | (v.&(mask) >> (32 - s))} # initial hash a, b, c, d = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 bit_len = string.size << 3 string += "\x80" while (string.size % 64) != 56 string += "\0" end string = string.force_encoding('ascii-8bit') + [bit_len & mask, bit_len >> 32].pack("V2") if string.size % 64 != 0 fail "failed to pad to correct length" end io = StringIO.new(string) block = "" while io.read(64, block) x = block.unpack("V16") # Process this block. aa, bb, cc, dd = a, b, c, d [0, 4, 8, 12].each {|i| a = r[a + f[b, c, d] + x[i], 3]; i += 1 d = r[d + f[a, b, c] + x[i], 7]; i += 1 c = r[c + f[d, a, b] + x[i], 11]; i += 1 b = r[b + f[c, d, a] + x[i], 19] } [0, 1, 2, 3].each {|i| a = r[a + g[b, c, d] + x[i] + 0x5a827999, 3]; i += 4 d = r[d + g[a, b, c] + x[i] + 0x5a827999, 5]; i += 4 c = r[c + g[d, a, b] + x[i] + 0x5a827999, 9]; i += 4 b = r[b + g[c, d, a] + x[i] + 0x5a827999, 13] } [0, 2, 1, 3].each {|i| a = r[a + h[b, c, d] + x[i] + 0x6ed9eba1, 3]; i += 8 d = r[d + h[a, b, c] + x[i] + 0x6ed9eba1, 9]; i -= 4 c = r[c + h[d, a, b] + x[i] + 0x6ed9eba1, 11]; i += 8 b = r[b + h[c, d, a] + x[i] + 0x6ed9eba1, 15] } a = (a + aa) & mask b = (b + bb) & mask c = (c + cc) & mask d = (d + dd) & mask end [a, b, c, d].pack("V4") end else # Openssl/libssl provides MD4, so we can use it. def self.digest(string) OpenSSL::Digest::MD4.digest(string) end end end end end rubyntlm-0.6.5/lib/net/ntlm/message/0000755000004100000410000000000014634001700017353 5ustar www-datawww-datarubyntlm-0.6.5/lib/net/ntlm/message/type1.rb0000644000004100000410000000102614634001700020741 0ustar www-datawww-datamodule Net module NTLM class Message # @private false class Type1 < Message string :sign, {:size => 8, :value => SSP_SIGN} int32LE :type, {:value => 1} int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] } security_buffer :domain, {:value => ""} security_buffer :workstation, {:value => Socket.gethostname } string :os_version, {:size => 8, :value => "", :active => false } end end end end rubyntlm-0.6.5/lib/net/ntlm/message/type0.rb0000644000004100000410000000037114634001700020742 0ustar www-datawww-datamodule Net module NTLM class Message # sub class definitions class Type0 < Message string :sign, {:size => 8, :value => SSP_SIGN} int32LE :type, {:value => 0} end end end end rubyntlm-0.6.5/lib/net/ntlm/message/type3.rb0000644000004100000410000001055214634001700020747 0ustar www-datawww-datamodule Net module NTLM class Message # @private false class Type3 < Message string :sign, {:size => 8, :value => SSP_SIGN} int32LE :type, {:value => 3} security_buffer :lm_response, {:value => ""} security_buffer :ntlm_response, {:value => ""} security_buffer :domain, {:value => ""} security_buffer :user, {:value => ""} security_buffer :workstation, {:value => ""} security_buffer :session_key, {:value => "", :active => false } int32LE :flag, {:value => 0, :active => false } string :os_version, {:size => 8, :active => false } class << Type3 # Builds a Type 3 packet # @note All options must be properly encoded with either unicode or oem encoding # @return [Type3] # @option arg [String] :lm_response The LM hash # @option arg [String] :ntlm_response The NTLM hash # @option arg [String] :domain The domain to authenticate to # @option arg [String] :workstation The name of the calling workstation # @option arg [String] :session_key The session key # @option arg [Integer] :flag Flags for the packet def create(arg, opt ={}) t = new t.lm_response = arg[:lm_response] t.ntlm_response = arg[:ntlm_response] t.domain = arg[:domain] t.user = arg[:user] if arg[:workstation] t.workstation = arg[:workstation] end if arg[:session_key] t.enable(:session_key) t.session_key = arg[:session_key] end if arg[:flag] t.enable(:session_key) t.enable(:flag) t.flag = arg[:flag] end t end end # @param server_challenge (see #password?) def blank_password?(server_challenge) password?('', server_challenge) end # @param password [String] # @param server_challenge [String] The server's {Type2#challenge challenge} from the # {Type2} message for which this object is a response. # @return [true] if +password+ was the password used to generate this # {Type3} message # @return [false] otherwise def password?(password, server_challenge) case ntlm_version when :ntlm2_session ntlm2_session_password?(password, server_challenge) when :ntlmv2 ntlmv2_password?(password, server_challenge) else raise end end # @return [Symbol] def ntlm_version if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16 :ntlm2_session elsif ntlm_response.size == 24 :ntlmv1 elsif ntlm_response.size > 24 :ntlmv2 end end private def ntlm2_session_password?(password, server_challenge) hash = ntlm_response _lm, empty_hash = NTLM.ntlm2_session( { :ntlm_hash => NTLM.ntlm_hash(password), :challenge => server_challenge, }, { :client_challenge => lm_response[0,8] } ) hash == empty_hash end def ntlmv2_password?(password, server_challenge) # The first 16 bytes of the ntlm_response are the HMAC of the blob # that follows it. blob = Blob.new blob.parse(ntlm_response[16..-1]) empty_hash = NTLM.ntlmv2_response( { # user and domain came from the serialized data here, so # they're already unicode :ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true), :challenge => server_challenge, :target_info => blob.target_info }, { :client_challenge => blob.challenge, # The blob's timestamp is already in milliseconds since 1601, # so convert it back to epoch time first :timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET, } ) empty_hash == ntlm_response end end end end end rubyntlm-0.6.5/lib/net/ntlm/message/type2.rb0000644000004100000410000000742114634001700020747 0ustar www-datawww-datamodule Net module NTLM class Message # @private false class Type2 < Message string :sign, { :size => 8, :value => SSP_SIGN } int32LE :type, { :value => 2 } security_buffer :target_name, { :size => 0, :value => "" } int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] } int64LE :challenge, { :value => 0} int64LE :context, { :value => 0, :active => false } security_buffer :target_info, { :value => "", :active => false } string :os_version, { :size => 8, :value => "", :active => false } # Generates a Type 3 response based on the Type 2 Information # @return [Type3] # @option arg [String] :username The username to authenticate with # @option arg [String] :password The user's password # @option arg [String] :domain ('') The domain to authenticate to # @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation # @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet # @note An empty :domain option authenticates to the local machine. # @note The :use_default_target has precedence over the :domain option def response(arg, opt = {}) usr = arg[:user] pwd = arg[:password] domain = arg[:domain] ? arg[:domain].upcase : "" if usr.nil? or pwd.nil? raise ArgumentError, "user and password have to be supplied" end if opt[:workstation] ws = opt[:workstation] else ws = Socket.gethostname end if opt[:client_challenge] cc = opt[:client_challenge] else cc = rand(MAX64) end cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer) opt[:client_challenge] = cc if has_flag?(:OEM) and opt[:unicode] usr = NTLM::EncodeUtil.decode_utf16le(usr) pwd = NTLM::EncodeUtil.decode_utf16le(pwd) ws = NTLM::EncodeUtil.decode_utf16le(ws) domain = NTLM::EncodeUtil.decode_utf16le(domain) opt[:unicode] = false end if has_flag?(:UNICODE) and !opt[:unicode] usr = NTLM::EncodeUtil.encode_utf16le(usr) pwd = NTLM::EncodeUtil.encode_utf16le(pwd) ws = NTLM::EncodeUtil.encode_utf16le(ws) domain = NTLM::EncodeUtil.encode_utf16le(domain) opt[:unicode] = true end if opt[:use_default_target] domain = self.target_name end ti = self.target_info chal = self[:challenge].serialize if opt[:ntlmv2] ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti} lm_res = NTLM::lmv2_response(ar, opt) ntlm_res = NTLM::ntlmv2_response(ar, opt) elsif has_flag?(:NTLM2_KEY) ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal} lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt) else ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal} lm_res = NTLM::lm_response(ar) ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal} ntlm_res = NTLM::ntlm_response(ar) end Type3.create({ :lm_response => lm_res, :ntlm_response => ntlm_res, :domain => domain, :user => usr, :workstation => ws, :flag => self.flag }) end end end end end rubyntlm-0.6.5/lib/net/ntlm/exceptions.rb0000644000004100000410000000035514634001700020440 0ustar www-datawww-datamodule Net module NTLM class NtlmError < StandardError; end class InvalidTargetDataError < NtlmError attr_reader :data def initialize(msg, data) @data = data super(msg) end end end end rubyntlm-0.6.5/lib/net/ntlm/int16_le.rb0000644000004100000410000000062014634001700017673 0ustar www-datawww-datamodule Net module NTLM class Int16LE < Field def initialize(opt) super(opt) @size = 2 end def parse(str, offset=0) if @active and str.size >= offset + @size @value = str[offset, @size].unpack("v")[0] @size else 0 end end def serialize [@value].pack("v") end end end endrubyntlm-0.6.5/lib/net/ntlm/security_buffer.rb0000644000004100000410000000160614634001700021457 0ustar www-datawww-data module Net module NTLM class SecurityBuffer < FieldSet int16LE :length, {:value => 0} int16LE :allocated, {:value => 0} int32LE :offset, {:value => 0} attr_accessor :active def initialize(opts={}) super() @value = opts[:value] @active = opts[:active].nil? ? true : opts[:active] @size = 8 end def parse(str, offset=0) if @active and str.size >= offset + @size super(str, offset) @value = str[self.offset, self.length] @size else 0 end end def serialize super if @active end def value @value end def value=(val) @value = val self.length = self.allocated = val.size end def data_size @active ? @value.size : 0 end end end endrubyntlm-0.6.5/lib/net/ntlm/int32_le.rb0000644000004100000410000000064014634001700017673 0ustar www-datawww-datamodule Net module NTLM class Int32LE < Field def initialize(opt) super(opt) @size = 4 end def parse(str, offset=0) if @active and str.size >= offset + @size @value = str.slice(offset, @size).unpack("V")[0] @size else 0 end end def serialize [@value].pack("V") if @active end end end endrubyntlm-0.6.5/lib/net/ntlm/field_set.rb0000644000004100000410000000551514634001700020220 0ustar www-datawww-datamodule Net module NTLM # base class of data structure class FieldSet class << FieldSet # @macro string_security_buffer # @method $1 # @method $1= # @return [String] def string(name, opts) add_field(name, Net::NTLM::String, opts) end # @macro int16le_security_buffer # @method $1 # @method $1= # @return [Int16LE] def int16LE(name, opts) add_field(name, Net::NTLM::Int16LE, opts) end # @macro int32le_security_buffer # @method $1 # @method $1= # @return [Int32LE] def int32LE(name, opts) add_field(name, Net::NTLM::Int32LE, opts) end # @macro int64le_security_buffer # @method $1 # @method $1= # @return [Int64] def int64LE(name, opts) add_field(name, Net::NTLM::Int64LE, opts) end # @macro security_buffer # @method $1 # @method $1= # @return [SecurityBuffer] def security_buffer(name, opts) add_field(name, Net::NTLM::SecurityBuffer, opts) end def prototypes @proto end def names return [] if @proto.nil? @proto.map{|n, t, o| n} end def types return [] if @proto.nil? @proto.map{|n, t, o| t} end def opts return [] if @proto.nil? @proto.map{|n, t, o| o} end private def add_field(name, type, opts) (@proto ||= []).push [name, type, opts] define_accessor name end def define_accessor(name) module_eval(<<-End, __FILE__, __LINE__ + 1) def #{name} self['#{name}'].value end def #{name}=(val) self['#{name}'].value = val end End end end def initialize @alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] } end def parse(str, offset=0) @alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)} end def serialize @alist.map{|n, f| f.serialize }.join end def size @alist.inject(0){|sum, a| sum += a[1].size} end def [](name) a = @alist.assoc(name.to_s.intern) raise ArgumentError, "no such field: #{name}" unless a a[1] end def []=(name, val) a = @alist.assoc(name.to_s.intern) raise ArgumentError, "no such field: #{name}" unless a a[1] = val end def enable(name) self[name].active = true end def disable(name) self[name].active = false end def has_disabled_fields? @alist.any? { |name, field| !field.active } end end end end rubyntlm-0.6.5/lib/net/ntlm/field.rb0000644000004100000410000000125214634001700017337 0ustar www-datawww-datamodule Net module NTLM # base classes for primitives # @private class Field attr_accessor :active, :value def initialize(opts) @value = opts[:value] @active = opts[:active].nil? ? true : opts[:active] @size = opts[:size].nil? ? 0 : opts[:size] end def size @active ? @size : 0 end # Serializer function for field data # Exists in this class to be overridden by child classes def serialize raise NotImplementedError end # Parser function for field data # Exists in this class to be overridden by child classes def parse(str, offset=0) raise NotImplementedError end end end endrubyntlm-0.6.5/lib/net/ntlm/version.rb0000644000004100000410000000025314634001700017741 0ustar www-datawww-datamodule Net module NTLM # @private module VERSION MAJOR = 0 MINOR = 6 TINY = 5 STRING = [MAJOR, MINOR, TINY].join('.') end end end rubyntlm-0.6.5/lib/net/ntlm/client.rb0000644000004100000410000000374014634001700017536 0ustar www-datawww-datamodule Net module NTLM class Client DEFAULT_FLAGS = NTLM::FLAGS[:UNICODE] | NTLM::FLAGS[:OEM] | NTLM::FLAGS[:SIGN] | NTLM::FLAGS[:SEAL] | NTLM::FLAGS[:REQUEST_TARGET] | NTLM::FLAGS[:NTLM] | NTLM::FLAGS[:ALWAYS_SIGN] | NTLM::FLAGS[:NTLM2_KEY] | NTLM::FLAGS[:KEY128] | NTLM::FLAGS[:KEY_EXCHANGE] | NTLM::FLAGS[:KEY56] attr_reader :username, :password, :domain, :workstation, :flags # @note All string parameters should be encoded in UTF-8. The proper # final encoding for placing in the various {Message messages} will be # chosen based on negotiation with the server. # # @param username [String] # @param password [String] # @option opts [String] :domain where we're authenticating to # @option opts [String] :workstation local workstation name # @option opts [Fixnum] :flags (DEFAULT_FLAGS) see Net::NTLM::Message::Type1.flag def initialize(username, password, opts = {}) @username = username @password = password @domain = opts[:domain] || nil @workstation = opts[:workstation] || nil @flags = opts[:flags] || DEFAULT_FLAGS end # @return [NTLM::Message] def init_context(resp = nil, channel_binding = nil) if resp.nil? @session = nil type1_message else @session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding) @session.authenticate! end end # @return [Client::Session] def session @session end def session_key @session.exported_session_key end private # @return [Message::Type1] def type1_message type1 = Message::Type1.new type1[:flag].value = flags type1.domain = domain if domain type1.workstation = workstation if workstation type1 end end end end require "net/ntlm/client/session" rubyntlm-0.6.5/lib/net/ntlm/message.rb0000644000004100000410000000702514634001700017704 0ustar www-datawww-datamodule Net module NTLM SSP_SIGN = "NTLMSSP\0" # See [2.2.2.5 NEGOTIATE](https://msdn.microsoft.com/en-us/library/cc236650.aspx) FLAGS = { :UNICODE => 0x00000001, :OEM => 0x00000002, :REQUEST_TARGET => 0x00000004, :SIGN => 0x00000010, :SEAL => 0x00000020, :NEG_DATAGRAM => 0x00000040, :NEG_LM_KEY => 0x00000080, :NTLM => 0x00000200, :NEG_ANONYMOUS => 0x00000800, :DOMAIN_SUPPLIED => 0x00001000, :WORKSTATION_SUPPLIED => 0x00002000, :ALWAYS_SIGN => 0x00008000, :TARGET_TYPE_DOMAIN => 0x00010000, :TARGET_TYPE_SERVER => 0x00020000, :NTLM2_KEY => 0x00080000, :NEG_IDENTIFY => 0x00100000, :NON_NT_SESSION_KEY => 0x00400000, :TARGET_INFO => 0x00800000, :NEG_VERSION => 0x02000000, :KEY128 => 0x20000000, :KEY_EXCHANGE => 0x40000000, :KEY56 => 0x80000000, # Undocumented flags: :MBZ9 => 0x00000008, :NETWARE => 0x00000100, :NEG_NT_ONLY => 0x00000400, :MBZ7 => 0x00000800, # alias for :NEG_ANONYMOUS :LOCAL_CALL => 0x00004000, }.freeze FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] } DEFAULT_FLAGS = { :TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY], :TYPE2 => FLAGS[:UNICODE], :TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY] } # @private false class Message < FieldSet class << Message def parse(str) m = Type0.new m.parse(str) case m.type when 1 t = Type1.new.parse(str) when 2 t = Type2.new.parse(str) when 3 t = Type3.new.parse(str) else raise ArgumentError, "unknown type: #{m.type}" end t end def decode64(str) parse(Base64.decode64(str)) end end # @return [self] def parse(str) super while has_disabled_fields? && serialize.size < str.size # enable the next disabled field self.class.names.find { |name| !self[name].active && enable(name) } super end self end def has_flag?(flag) (self[:flag].value & FLAGS[flag]) == FLAGS[flag] end def set_flag(flag) self[:flag].value |= FLAGS[flag] end def dump_flags FLAG_KEYS.each{ |k| print(k, "=", has_flag?(k), "\n") } end def serialize deflag super + security_buffers.map{|n, f| f.value + (has_flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '')}.join end def encode64 Base64.encode64(serialize).gsub(/\n/, '') end def decode64(str) parse(Base64.decode64(str)) end alias head_size size def data_size security_buffers.inject(0){|sum, a| sum += a[1].data_size} end def size head_size + data_size end def security_buffers @alist.find_all{|n, f| f.instance_of?(SecurityBuffer)} end def deflag security_buffers.inject(head_size){|cur, a| a[1].offset = cur cur += a[1].data_size has_flag?(:UNICODE) ? cur + cur % 2 : cur } end def data_edge security_buffers.map{ |n, f| f.active ? f.offset : size}.min end end end end rubyntlm-0.6.5/spec/0000755000004100000410000000000014634001700014353 5ustar www-datawww-datarubyntlm-0.6.5/spec/spec_helper.rb0000644000004100000410000000106414634001700017172 0ustar www-datawww-datarequire 'simplecov' require 'pathname' SimpleCov.start do add_filter '/spec/' add_filter '/config/' add_filter '/vendor/' end if ENV["COVERAGE"] require 'rspec' require 'net/ntlm' # add project lib directory to load path spec_pathname = Pathname.new(__FILE__).dirname root_pathname = spec_pathname.join('..').expand_path # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. support_glob = root_pathname.join('spec', 'support', '**', '*.rb') Dir.glob(support_glob) do |path| require path end rubyntlm-0.6.5/spec/lib/0000755000004100000410000000000014634001700015121 5ustar www-datawww-datarubyntlm-0.6.5/spec/lib/net/0000755000004100000410000000000014634001700015707 5ustar www-datawww-datarubyntlm-0.6.5/spec/lib/net/ntlm_spec.rb0000644000004100000410000001117614634001700020226 0ustar www-datawww-datarequire "spec_helper" describe Net::NTLM do let(:passwd) {"SecREt01"} let(:user) {"user"} let(:domain) {"DOMAIN"} let(:challenge) {["0123456789abcdef"].pack("H*")} let(:client_ch) {["ffffff0011223344"].pack("H*")} let(:timestamp) {1055844000} let(:trgt_info) {[ "02000c0044004f004d00410049004e00" + "01000c00530045005200560045005200" + "0400140064006f006d00610069006e00" + "2e0063006f006d000300220073006500" + "72007600650072002e0064006f006d00" + "610069006e002e0063006f006d000000" + "0000" ].pack("H*")} let(:padded_pwd) { passwd.upcase.ljust(14, "\0")} let(:keys) { Net::NTLM.gen_keys(padded_pwd)} it 'should convert a value to 64-bit LE Integer' do expect(Net::NTLM.pack_int64le(42)).to eq("\x2A\x00\x00\x00\x00\x00\x00\x00") end it 'should split a string into an array of slices, 7 chars or less' do expect(Net::NTLM.split7("HelloWorld!")).to eq([ 'HelloWo', 'rld!']) end it 'should generate DES keys from the supplied string' do first_key = ["52a2516b252a5161"].pack('H*') second_key = ["3180010101010101"].pack('H*') expect(Net::NTLM.gen_keys(padded_pwd)).to eq([first_key, second_key]) end it 'should encrypt the string with DES for each key supplied' do first_crypt = ["ff3750bcc2b22412"].pack('H*') second_crypt = ["c2265b23734e0dac"].pack('H*') expect(Net::NTLM::apply_des(Net::NTLM::LM_MAGIC, keys)).to eq([first_crypt, second_crypt]) end it 'should generate an lm_hash' do expect(Net::NTLM::lm_hash(passwd)).to eq(["ff3750bcc2b22412c2265b23734e0dac"].pack("H*")) end it 'should generate an ntlm_hash' do expect(Net::NTLM::ntlm_hash(passwd)).to eq(["cd06ca7c7e10c99b1d33b7485a2ed808"].pack("H*")) end it 'should generate an ntlmv2_hash' do expect(Net::NTLM::ntlmv2_hash(user, passwd, domain)).to eq(["04b8e0ba74289cc540826bab1dee63ae"].pack("H*")) end context 'when a user passes an NTLM hash for pass-the-hash' do let(:passwd) { Net::NTLM::EncodeUtil.encode_utf16le('ff3750bcc2b22412c2265b23734e0dac:cd06ca7c7e10c99b1d33b7485a2ed808') } it 'should return the correct ntlmv2 hash' do expect(Net::NTLM::ntlmv2_hash(user, passwd, domain)).to eq(["04b8e0ba74289cc540826bab1dee63ae"].pack("H*")) end end context 'when the username contains non-ASCI characters' do let(:user) { 'юзер' } it 'should return the correct ntlmv2 hash' do expect(Net::NTLM::ntlmv2_hash(user, passwd, domain, { unicode: true })).to eq(["a0f4b914a37faeaee884b6b04a20faf0"].pack("H*")) end end it 'should generate an lm_response' do expect(Net::NTLM::lm_response( { :lm_hash => Net::NTLM::lm_hash(passwd), :challenge => challenge } )).to eq(["c337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56"].pack("H*")) end it 'should generate an ntlm_response' do ntlm_hash = Net::NTLM::ntlm_hash(passwd) expect(Net::NTLM::ntlm_response( { :ntlm_hash => ntlm_hash, :challenge => challenge } )).to eq(["25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6"].pack("H*")) end it 'should generate a lvm2_response' do expect(Net::NTLM::lmv2_response( { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, passwd, domain), :challenge => challenge }, { :client_challenge => client_ch } )).to eq(["d6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344"].pack("H*")) end it 'should generate a ntlmv2_response' do expect(Net::NTLM::ntlmv2_response( { :ntlmv2_hash => Net::NTLM::ntlmv2_hash(user, passwd, domain), :challenge => challenge, :target_info => trgt_info }, { :timestamp => timestamp, :client_challenge => client_ch } )).to eq([ "cbabbca713eb795d04c97abc01ee4983" + "01010000000000000090d336b734c301" + "ffffff00112233440000000002000c00" + "44004f004d00410049004e0001000c00" + "53004500520056004500520004001400" + "64006f006d00610069006e002e006300" + "6f006d00030022007300650072007600" + "650072002e0064006f006d0061006900" + "6e002e0063006f006d00000000000000" + "0000" ].pack("H*")) end it 'should generate a ntlm2_session' do session = Net::NTLM::ntlm2_session( { :ntlm_hash => Net::NTLM::ntlm_hash(passwd), :challenge => challenge }, { :client_challenge => client_ch } ) expect(session[0]).to eq(["ffffff001122334400000000000000000000000000000000"].pack("H*")) expect(session[1]).to eq(["10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455"].pack("H*")) end end rubyntlm-0.6.5/spec/lib/net/ntlm/0000755000004100000410000000000014634001700016661 5ustar www-datawww-datarubyntlm-0.6.5/spec/lib/net/ntlm/channel_binding_spec.rb0000644000004100000410000000115414634001700023323 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::ChannelBinding do let(:certificates_path) { 'spec/support/certificates' } let(:sha_256_path) { File.join(certificates_path, 'sha_256_hash.pem') } let(:sha_256_cert) { OpenSSL::X509::Certificate.new(File.read(sha_256_path)) } let(:cert_hash) { "\x04\x0E\x56\x28\xEC\x4A\x98\x29\x91\x70\x73\x62\x03\x7B\xB2\x3C".force_encoding(Encoding::ASCII_8BIT) } subject { Net::NTLM::ChannelBinding.create(sha_256_cert) } describe '#channel_binding_token' do it 'returns the correct hash' do expect(subject.channel_binding_token).to eq cert_hash end end end rubyntlm-0.6.5/spec/lib/net/ntlm/client/0000755000004100000410000000000014634001700020137 5ustar www-datawww-datarubyntlm-0.6.5/spec/lib/net/ntlm/client/session_spec.rb0000644000004100000410000000640314634001700023164 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Client::Session do let(:t2_challenge) { Net::NTLM::Message.decode64 "TlRMTVNTUAACAAAADAAMADgAAAA1goriAAyk1DmJUnUAAAAAAAAAAFAAUABEAAAABgLwIwAAAA9TAEUAUgBWAEUAUgACAAwAUwBFAFIAVgBFAFIAAQAMAFMARQBSAFYARQBSAAQADABzAGUAcgB2AGUAcgADAAwAcwBlAHIAdgBlAHIABwAIADd7mrNaB9ABAAAAAA==" } let(:inst) { Net::NTLM::Client::Session.new(nil, t2_challenge) } let(:user_session_key) {["3c4918ff0b33e2603e5d7ceaf34bb7d5"].pack("H*")} let(:client_sign_key) {["f7f97a82ec390f9c903dac4f6aceb132"].pack("H*")} let(:client_seal_key) {["6f0d99535033951cbe499cd1914fe9ee"].pack("H*")} let(:server_sign_key) {["f7f97a82ec390f9c903dac4f6aceb132"].pack("H*")} let(:server_seal_key) {["6f0d99535033951cbe499cd1914fe9ee"].pack("H*")} describe "#sign_message" do it "signs a message and when KEY_EXCHANGE is true" do expect(inst).to receive(:client_sign_key).and_return(client_sign_key) expect(inst).to receive(:client_seal_key).and_return(client_seal_key) expect(inst).to receive(:negotiate_key_exchange?).and_return(true) sm = inst.sign_message("Test Message") str = "01000000b35ccd60c110c52f00000000" expect(sm.unpack("H*")[0]).to eq(str) end end describe "#verify_signature" do it "verifies a message signature" do expect(inst).to receive(:server_sign_key).and_return(server_sign_key) expect(inst).to receive(:server_seal_key).and_return(server_seal_key) expect(inst).to receive(:negotiate_key_exchange?).and_return(true) sig = "01000000b35ccd60c110c52f00000000" sm = inst.verify_signature([sig].pack("H*"), "Test Message") expect(sm).to be true end end describe "#seal_message" do it "should seal the message" do expect(inst).to receive(:client_seal_key).and_return(client_seal_key) emsg = inst.seal_message("rubyntlm") expect(emsg.unpack("H*")[0]).to eq("d7389b9604f6274f") end end describe "#unseal_message" do it "should unseal the message" do expect(inst).to receive(:server_seal_key).and_return(server_seal_key) msg = inst.unseal_message(["d7389b9604f6274f"].pack("H*")) expect(msg).to eq("rubyntlm") end end describe "#exported_session_key" do it "returns a random 16-byte key when negotiate_key_exchange? is true" do expect(inst).to receive(:negotiate_key_exchange?).and_return(true) expect(inst).not_to receive(:user_session_key) inst.exported_session_key end it "returns the user_session_key when negotiate_key_exchange? is false" do expect(inst).to receive(:negotiate_key_exchange?).and_return(false) expect(inst).to receive(:user_session_key).and_return(user_session_key) inst.exported_session_key end end context 'when authenticating anonymously' do let(:inst) { Net::NTLM::Client::Session.new(Net::NTLM::Client.new('', ''), t2_challenge) } describe "#authenticate!" do it "should set the response fields correctly" do t3 = inst.authenticate! expect(t3).to be_a(Net::NTLM::Message::Type3) expect(t3.lm_response).to eq("\x00".b) expect(t3.ntlm_response).to eq('') end end describe "#is_anonymous?" do it "should be true" do expect(inst.is_anonymous?).to be_truthy end end end end rubyntlm-0.6.5/spec/lib/net/ntlm/client_spec.rb0000644000004100000410000000507014634001700021500 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Client do let(:inst) { Net::NTLM::Client.new("test", "test01", :workstation => "testhost") } let(:user_session_key) {["3c4918ff0b33e2603e5d7ceaf34bb7d5"].pack("H*")} describe "#init_context" do it "returns a default Type1 message" do t1 = inst.init_context expect(t1).to be_instance_of Net::NTLM::Message::Type1 expect(t1.domain).to eq("") expect(t1.workstation).to eq("testhost") expect(t1).to have_flag(:UNICODE) expect(t1).to have_flag(:OEM) expect(t1).to have_flag(:SIGN) expect(t1).to have_flag(:SEAL) expect(t1).to have_flag(:REQUEST_TARGET) expect(t1).to have_flag(:NTLM) expect(t1).to have_flag(:ALWAYS_SIGN) expect(t1).to have_flag(:NTLM2_KEY) expect(t1).to have_flag(:KEY128) expect(t1).to have_flag(:KEY_EXCHANGE) expect(t1).to have_flag(:KEY56) end it "clears session variable on new init_context" do inst.instance_variable_set :@session, "BADSESSION" expect(inst.session).to eq("BADSESSION") inst.init_context expect(inst.session).to be_nil end it "returns a Type1 message with custom flags" do flags = Net::NTLM::FLAGS[:UNICODE] | Net::NTLM::FLAGS[:REQUEST_TARGET] | Net::NTLM::FLAGS[:NTLM] inst = Net::NTLM::Client.new("test", "test01", :workstation => "testhost", :flags => flags) t1 = inst.init_context expect(t1).to be_instance_of Net::NTLM::Message::Type1 expect(t1.domain).to eq("") expect(t1.workstation).to eq("testhost") expect(t1).to have_flag(:UNICODE) expect(t1).not_to have_flag(:OEM) expect(t1).not_to have_flag(:SIGN) expect(t1).not_to have_flag(:SEAL) expect(t1).to have_flag(:REQUEST_TARGET) expect(t1).to have_flag(:NTLM) expect(t1).not_to have_flag(:ALWAYS_SIGN) expect(t1).not_to have_flag(:NTLM2_KEY) expect(t1).not_to have_flag(:KEY128) expect(t1).not_to have_flag(:KEY_EXCHANGE) expect(t1).not_to have_flag(:KEY56) end it "calls authenticate! when we receive a Challenge Message" do t2_challenge = "TlRMTVNTUAACAAAADAAMADgAAAA1goriAAyk1DmJUnUAAAAAAAAAAFAAUABEAAAABgLwIwAAAA9TAEUAUgBWAEUAUgACAAwAUwBFAFIAVgBFAFIAAQAMAFMARQBSAFYARQBSAAQADABzAGUAcgB2AGUAcgADAAwAcwBlAHIAdgBlAHIABwAIADd7mrNaB9ABAAAAAA==" session = double("session") expect(session).to receive(:authenticate!) expect(Net::NTLM::Client::Session).to receive(:new).with(inst, instance_of(Net::NTLM::Message::Type2), nil).and_return(session) inst.init_context t2_challenge end end end rubyntlm-0.6.5/spec/lib/net/ntlm/field_set_spec.rb0000644000004100000410000000152614634001700022162 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::FieldSet do fields = [] it_behaves_like 'a fieldset', fields subject(:fieldset_class) do Class.new(Net::NTLM::FieldSet) end context 'an instance' do subject(:fieldset_object) do fieldset_class.string(:test_string, { :value => 'Test', :active => true, :size => 4}) fieldset_class.string(:test_string2, { :value => 'Foo', :active => true, :size => 3}) fieldset_class.new end it 'should serialize all the fields' do expect(fieldset_object.serialize).to eq('TestFoo') end it 'should parse a string across the fields' do fieldset_object.parse('FooBarBaz') expect(fieldset_object.serialize).to eq('FooBarB') end it 'should return an aggregate size of all the fields' do expect(fieldset_object.size).to eq(7) end end end rubyntlm-0.6.5/spec/lib/net/ntlm/field_spec.rb0000644000004100000410000000162614634001700021310 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Field do it_behaves_like 'a field', 'Foo', false context 'with no size specified' do let (:field_without_size) { Net::NTLM::Field.new({ :value => 'Foo', :active => true }) } it 'should set size to 0 if not active' do expect(field_without_size.size).to eq(0) end it 'should return 0 if active but no size specified' do field_without_size.active = true expect(field_without_size.size).to eq(0) end end context 'with a size specified' do let (:field_with_size) { Net::NTLM::Field.new({ :value => 'Foo', :active => true, :size => 100 }) } it 'should return the size provided in the initialize options if active' do expect(field_with_size.size).to eq(100) end it 'should still return 0 if not active' do field_with_size.active = false expect(field_with_size.size).to eq(0) end end end rubyntlm-0.6.5/spec/lib/net/ntlm/message/0000755000004100000410000000000014634001700020305 5ustar www-datawww-datarubyntlm-0.6.5/spec/lib/net/ntlm/message/type1_spec.rb0000644000004100000410000001102514634001700022705 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Message::Type1 do fields = [ { :name => :sign, :class => Net::NTLM::String, :value => Net::NTLM::SSP_SIGN, :active => true }, { :name => :type, :class => Net::NTLM::Int32LE, :value => 1, :active => true }, { :name => :flag, :class => Net::NTLM::Int32LE, :value => Net::NTLM::DEFAULT_FLAGS[:TYPE1], :active => true }, { :name => :domain, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true }, { :name => :workstation, :class => Net::NTLM::SecurityBuffer, :value => Socket.gethostname, :active => true }, { :name => :os_version, :class => Net::NTLM::String, :value => '', :active => false }, ] flags = [ :UNICODE, :OEM, :REQUEST_TARGET, :NTLM, :ALWAYS_SIGN, :NTLM2_KEY ] it_behaves_like 'a fieldset', fields it_behaves_like 'a message', flags let(:type1_packet) {"TlRMTVNTUAABAAAAB4IIAAAAAAAgAAAAAAAAACAAAAA="} it 'should deserialize' do t1 = Net::NTLM::Message.decode64(type1_packet) expect(t1.class).to eq(Net::NTLM::Message::Type1) expect(t1.domain).to eq('') expect(t1.flag).to eq(557575) expect(t1.os_version).to eq('') expect(t1.sign).to eq("NTLMSSP\0") expect(t1.type).to eq(1) expect(t1.workstation).to eq('') end it 'should serialize' do t1 = Net::NTLM::Message::Type1.new t1.workstation = '' expect(t1.encode64).to eq(type1_packet) end describe '.parse' do subject(:message) { described_class.parse(data) } # http://davenport.sourceforge.net/ntlm.html#appendixC7 context 'NTLM2 Session Response Authentication; NTLM2 Signing and Sealing Using the 128-bit NTLM2 Session Response User Session Key With Key Exchange Negotiated' do let(:data) do ['4e544c4d5353500001000000b78208e000000000000000000000000000000000'].pack('H*') end it 'should set the magic' do expect(message.sign).to eql(Net::NTLM::SSP_SIGN) end it 'should set the type' do expect(message.type).to eq(1) end it 'should set the flags' do expect(message.flag).to eq(0xe00882b7) expect(message).to have_flag(:UNICODE) expect(message).to have_flag(:OEM) expect(message).to have_flag(:REQUEST_TARGET) expect(message).to have_flag(:SIGN) expect(message).to have_flag(:SEAL) expect(message).to have_flag(:NTLM) expect(message).to have_flag(:ALWAYS_SIGN) expect(message).to have_flag(:NTLM2_KEY) expect(message).to have_flag(:KEY128) expect(message).to have_flag(:KEY_EXCHANGE) expect(message).to have_flag(:KEY56) end it 'should have empty workstation' do expect(message.workstation).to be_empty end it 'should have empty domain' do expect(message.domain).to be_empty end end # http://davenport.sourceforge.net/ntlm.html#appendixC9 context 'NTLMv2 Authentication; NTLM1 Signing and Sealing Using the 40-bit NTLMv2 User Session Key' do let(:data) { ['4e544c4d53535000010000003782000000000000000000000000000000000000'].pack('H*') } it 'should set the magic' do expect(message.sign).to eql(Net::NTLM::SSP_SIGN) end it 'should set the type' do expect(message.type).to eq(1) end it 'should set the flags' do expect(message.flag).to eq(0x00008237) expect(message).to have_flag(:UNICODE) expect(message).to have_flag(:OEM) expect(message).to have_flag(:REQUEST_TARGET) expect(message).to have_flag(:SIGN) expect(message).to have_flag(:SEAL) expect(message).to have_flag(:NTLM) expect(message).to have_flag(:ALWAYS_SIGN) end it 'should have empty workstation' do expect(message.workstation).to be_empty end it 'should have empty domain' do expect(message.domain).to be_empty end end context 'NTLMv2 with OS version' do let(:data) { ['4e544c4d5353500001000000978208e2000000000000000000000000000000000602f0230000000f'].pack('H*') } it 'should set the magic' do expect(message.sign).to eql(Net::NTLM::SSP_SIGN) end it 'should set the type' do expect(message.type).to eq(1) end it 'should have empty workstation' do expect(message.workstation).to be_empty end it 'should have empty domain' do expect(message.domain).to be_empty end it 'should set OS version info' do expect(message.os_version).to eq(['0602f0230000000f'].pack('H*')) end end end end rubyntlm-0.6.5/spec/lib/net/ntlm/message/type2_spec.rb0000644000004100000410000001332214634001700022710 0ustar www-datawww-data# encoding: UTF-8 require 'spec_helper' describe Net::NTLM::Message::Type2 do fields = [ { :name => :sign, :class => Net::NTLM::String, :value => Net::NTLM::SSP_SIGN, :active => true }, { :name => :type, :class => Net::NTLM::Int32LE, :value => 2, :active => true }, { :name => :challenge, :class => Net::NTLM::Int64LE, :value => 0, :active => true }, { :name => :context, :class => Net::NTLM::Int64LE, :value => 0, :active => false }, { :name => :flag, :class => Net::NTLM::Int32LE, :value => Net::NTLM::DEFAULT_FLAGS[:TYPE2], :active => true }, { :name => :target_name, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true }, { :name => :target_info, :class => Net::NTLM::SecurityBuffer, :value => '', :active => false }, { :name => :os_version, :class => Net::NTLM::String, :value => '', :active => false }, ] flags = [ :UNICODE ] it_behaves_like 'a fieldset', fields it_behaves_like 'a message', flags let(:type2_packet) {"TlRMTVNTUAACAAAAHAAcADgAAAAFgooCJ+UA1//+ZM4AAAAAAAAAAJAAkABUAAAABgGxHQAAAA9WAEEARwBSAEEATgBUAC0AMgAwADAAOABSADIAAgAcAFYAQQBHAFIAQQBOAFQALQAyADAAMAA4AFIAMgABABwAVgBBAEcAUgBBAE4AVAAtADIAMAAwADgAUgAyAAQAHAB2AGEAZwByAGEAbgB0AC0AMgAwADAAOABSADIAAwAcAHYAYQBnAHIAYQBuAHQALQAyADAAMAA4AFIAMgAHAAgAZBMdFHQnzgEAAAAA"} let(:type3_packet) {"TlRMTVNTUAADAAAAGAAYAEQAAADAAMAAXAAAAAAAAAAcAQAADgAOABwBAAAUABQAKgEAAAAAAAA+AQAABYKKAgAAAADVS27TfQGmWxSSbXmolTUQyxJmD8ISQuBKKHFKC8GksUZISYc8Ps9RAQEAAAAAAAAANasTdCfOAcsSZg/CEkLgAAAAAAIAHABWAEEARwBSAEEATgBUAC0AMgAwADAAOABSADIAAQAcAFYAQQBHAFIAQQBOAFQALQAyADAAMAA4AFIAMgAEABwAdgBhAGcAcgBhAG4AdAAtADIAMAAwADgAUgAyAAMAHAB2AGEAZwByAGEAbgB0AC0AMgAwADAAOABSADIABwAIAGQTHRR0J84BAAAAAAAAAAB2AGEAZwByAGEAbgB0AGsAbwBiAGUALgBsAG8AYwBhAGwA"} it 'should deserialize' do t2 = Net::NTLM::Message.decode64(type2_packet) expect(t2.class).to eq(Net::NTLM::Message::Type2) expect(t2.challenge).to eq(14872292244261496103) expect(t2.context).to eq(0) expect(t2.flag).to eq(42631685) expect(t2.os_version).to eq(['0601b11d0000000f'].pack('H*')) expect(t2.sign).to eq("NTLMSSP\0") t2_target_info = Net::NTLM::EncodeUtil.decode_utf16le(t2.target_info) if RUBY_VERSION == "1.8.7" expect(t2_target_info).to eq("\x02\x1CVAGRANT-2008R2\x01\x1CVAGRANT-2008R2\x04\x1Cvagrant-2008R2\x03\x1Cvagrant-2008R2\a\b\e$(D+&\e(B\0\0") else expect(t2_target_info).to eq("\u0002\u001CVAGRANT-2008R2\u0001\u001CVAGRANT-2008R2\u0004\u001Cvagrant-2008R2\u0003\u001Cvagrant-2008R2\a\b፤ᐝ❴ǎ\0\0") end expect(Net::NTLM::EncodeUtil.decode_utf16le(t2.target_name)).to eq("VAGRANT-2008R2") expect(t2.type).to eq(2) end it 'should serialize' do source = Net::NTLM::Message.decode64(type2_packet) t2 = Net::NTLM::Message::Type2.new t2.challenge = source.challenge t2.context = source.context t2.flag = source.flag t2.os_version = source.os_version t2.sign = source.sign t2.target_info = source.target_info t2.target_name = source.target_name t2.type = source.type t2.enable(:context) t2.enable(:target_info) t2.enable(:os_version) expect(t2.encode64).to eq(type2_packet) end it 'should generate a type 3 response' do t2 = Net::NTLM::Message.decode64(type2_packet) type3_known = Net::NTLM::Message.decode64(type3_packet) type3_known.flag = 0x028a8205 type3_known.enable(:session_key) type3_known.enable(:flag) t3 = t2.response({:user => 'vagrant', :password => 'vagrant', :domain => ''}, {:ntlmv2 => true, :workstation => 'kobe.local'}) expect(t3.domain).to eq(type3_known.domain) expect(t3.flag).to eq(type3_known.flag) expect(t3.sign).to eq("NTLMSSP\0") expect(t3.workstation).to eq("k\0o\0b\0e\0.\0l\0o\0c\0a\0l\0") expect(t3.user).to eq("v\0a\0g\0r\0a\0n\0t\0") expect(t3.session_key).to eq('') end it 'should upcase domain when provided' do t2 = Net::NTLM::Message.decode64(type2_packet) t3 = t2.response({:user => 'vagrant', :password => 'vagrant', :domain => 'domain'}, {:ntlmv2 => true, :workstation => 'kobe.local'}) expect(t3.domain).to eq("D\0O\0M\0A\0I\0N\0") end describe '.parse' do subject(:message) { described_class.parse(data) } # http://davenport.sourceforge.net/ntlm.html#appendixC7 context 'NTLM2 Session Response Authentication; NTLM2 Signing and Sealing Using the 128-bit NTLM2 Session Response User Session Key With Key Exchange Negotiated' do let(:data) do [ '4e544c4d53535000020000000c000c0030000000358289e0677f1c557a5ee96c' \ '0000000000000000460046003c00000054004500530054004e00540002000c00' \ '54004500530054004e00540001000c004d0045004d0042004500520003001e00' \ '6d0065006d006200650072002e0074006500730074002e0063006f006d000000' \ '0000' ].pack('H*') end it 'should set the magic' do expect(message.sign).to eql(Net::NTLM::SSP_SIGN) end it 'should set the type' do expect(message.type).to eq(2) end it 'should set the target name' do # TESTNT expect(message.target_name).to eq(["54004500530054004e005400"].pack('H*')) end it 'should set the flags' do expect(message.flag).to eq(0xe0898235) end it 'should set the challenge' do expect(message.challenge).to eq(0x6ce95e7a551c7f67) end it 'should set an empty context' do expect(message.context).to be_zero end it 'should set target info' do ti = [ '02000c0054004500530054004e00540001000c004d0045004d00420045005200' \ '03001e006d0065006d006200650072002e0074006500730074002e0063006f00' \ '6d0000000000' ].pack('H*') expect(message.target_info).to eq(ti) end end end end rubyntlm-0.6.5/spec/lib/net/ntlm/message/type3_spec.rb0000644000004100000410000002412614634001700022715 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Message::Type3 do fields = [ { :name => :sign, :class => Net::NTLM::String, :value => Net::NTLM::SSP_SIGN, :active => true }, { :name => :type, :class => Net::NTLM::Int32LE, :value => 3, :active => true }, { :name => :lm_response, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true }, { :name => :ntlm_response, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true }, { :name => :domain, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true }, { :name => :user, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true }, { :name => :workstation, :class => Net::NTLM::SecurityBuffer, :value => '', :active => true }, { :name => :session_key, :class => Net::NTLM::SecurityBuffer, :value => '', :active => false }, { :name => :flag, :class => Net::NTLM::Int32LE, :value => 0, :active => false }, ] flags = [] it_behaves_like 'a fieldset', fields it_behaves_like 'a message', flags describe '.parse' do subject(:message) { described_class.parse(data) } context 'with NTLMv2 data' do let(:data) do # Captured NTLMSSP blob from smbclient with username 'administrator' # and a blank password, i.e.: # smbclient -U 'administrator%' -L //192.168.100.140/ [ '4e544c4d53535000030000001800180040000000c400c4005800000012001200' \ '1c0100001a001a002e0100001a001a0048010000100010006201000015820860' \ 'ced203d860b80c7350050754b238202a8c1c63134f0ae0f086a3fb147e8b2f9f' \ 'de3ef1b1b43c83dc010100000000000080512dba020ed0011c5bc2c8339fd29a' \ '0000000002001e00570049004e002d00420035004a004e003300520048004700' \ '46003300310001001e00570049004e002d00420035004a004e00330052004800' \ '470046003300310004001e00570049004e002d00420035004a004e0033005200' \ '4800470046003300310003001e00570049004e002d00420035004a004e003300' \ '52004800470046003300310007000800a209e5ba020ed0010000000057004f00' \ '52004b00470052004f0055005000610064006d0069006e006900730074007200' \ '610074006f0072004100550053002d004c004500450054002d00310030003300' \ '31007036615cd6d9b19a685ded4312311cd7' ].pack('H*') end let(:server_challenge) { ['f588469dc96fe809'].pack('H*') } it 'should set the magic' do expect(message.sign).to eql(Net::NTLM::SSP_SIGN) end it 'should set the type' do expect(message.type).to eq(3) end it 'should set the LM response' do lm_response = ['ced203d860b80c7350050754b238202a8c1c63134f0ae0f0'].pack('H*') expect(message.lm_response).to eq(lm_response) end it 'should set the NTLM response' do ntlm_response = [ '86a3fb147e8b2f9fde3ef1b1b43c83dc010100000000000080512dba020ed001' \ '1c5bc2c8339fd29a0000000002001e00570049004e002d00420035004a004e00' \ '330052004800470046003300310001001e00570049004e002d00420035004a00' \ '4e00330052004800470046003300310004001e00570049004e002d0042003500' \ '4a004e00330052004800470046003300310003001e00570049004e002d004200' \ '35004a004e00330052004800470046003300310007000800a209e5ba020ed001' \ '00000000' ].pack('H*') expect(message.ntlm_response).to eq(ntlm_response) end it 'should set the user' do # administrator user = ['610064006d0069006e006900730074007200610074006f007200'].pack('H*') expect(message.user).to eq(user) end it 'should set the domain' do # WORKGROUP domain = ['57004f0052004b00470052004f0055005000'].pack('H*') expect(message.domain).to eq(domain) end it 'should set the workstation' do # AUS-LEET-1031 workstation = ['4100550053002d004c004500450054002d003100300033003100'].pack('H*') expect(message.workstation).to eq(workstation) end it 'should set the session key' do session_key = ['7036615cd6d9b19a685ded4312311cd7'].pack('H*') expect(message.session_key).to eq(session_key) end it 'should set the flags' do expect(message.flag).to eq(0x60088215) end it 'should NOT set the OS version structure' do expect(message.os_version).to be_nil end describe '#blank_password?' do it 'should be true' do expect(message.blank_password?(server_challenge)).to be true end end describe '#ntlm_version' do let(:ver) { message.ntlm_version } it 'should be :ntlmv2' do expect(ver).to eq(:ntlmv2) end end end # http://davenport.sourceforge.net/ntlm.html#appendixC7 context 'NTLM2 Session Response Authentication; NTLM2 Signing and Sealing Using the 128-bit NTLM2 Session Response User Session Key With Key Exchange Negotiated' do let(:data) do [ '4e544c4d5353500003000000180018006000000018001800780000000c000c00' \ '40000000080008004c0000000c000c00540000001000100090000000358288e0' \ '54004500530054004e00540074006500730074004d0045004d00420045005200' \ '404d1b6f6915258000000000000000000000000000000000ea8cc49f24da157f' \ '13436637f77693d8b992d619e584c7ee727a5240822ec7af4e9100c43e6fee7f' ].pack('H*') end it 'should set the LM response' do lm_response = ['404d1b6f6915258000000000000000000000000000000000'].pack('H*') expect(message.lm_response).to eq(lm_response) end it 'should set the NTLM response' do ntlm_response = [ 'ea8cc49f24da157f13436637f77693d8b992d619e584c7ee' ].pack('H*') expect(message.ntlm_response).to eq(ntlm_response) end it 'should set the domain' do # TESTNT domain = ['54004500530054004e005400'].pack('H*') expect(message.domain).to eq(domain) end it 'should set the user' do # test user = ['7400650073007400'].pack('H*') expect(message.user).to eq(user) end it 'should set the workstation' do # MEMBER workstation = ['4d0045004d00420045005200'].pack('H*') expect(message.workstation).to eq(workstation) end it 'should set the session key' do session_key = ['727a5240822ec7af4e9100c43e6fee7f'].pack('H*') expect(message.session_key).to eq(session_key) end let(:server_challenge) { ['677f1c557a5ee96c'].pack('H*') } describe '#password?' do it 'should be true for "test1234"' do expect(message.password?('test1234', server_challenge)).to be true end end describe '#blank_password?' do it 'should be false' do expect(message.blank_password?(server_challenge)).to be false end end describe '#ntlm_version' do let(:ver) { message.ntlm_version } it 'should be :ntlm2_session' do expect(ver).to eq(:ntlm2_session) end end end # http://davenport.sourceforge.net/ntlm.html#appendixC9 context 'NTLMv2 Authentication; NTLM1 Signing and Sealing Using the 40-bit NTLMv2 User Session Key' do let(:data) do [ '4e544c4d5353500003000000180018006000000076007600780000000c000c00' \ '40000000080008004c0000000c000c005400000000000000ee00000035828000' \ '54004500530054004e00540074006500730074004d0045004d00420045005200' \ '5d55a02b60a40526ac9a1e4d15fa45a0f2e6329726c598e8f77c67dad00b9321' \ '6242b197fe6addfa0101000000000000502db638677bc301f2e6329726c598e8' \ '0000000002000c0054004500530054004e00540001000c004d0045004d004200' \ '4500520003001e006d0065006d006200650072002e0074006500730074002e00' \ '63006f006d000000000000000000' ].pack 'H*' end it 'should set the NTLM response' do ntlm_response = [ 'f77c67dad00b93216242b197fe6addfa0101000000000000502db638677bc301' \ 'f2e6329726c598e80000000002000c0054004500530054004e00540001000c00' \ '4d0045004d0042004500520003001e006d0065006d006200650072002e007400' \ '6500730074002e0063006f006d000000000000000000' ].pack 'H*' expect(message.ntlm_response).to eq(ntlm_response) end it 'should set the domain' do # TESTNT domain = ['54004500530054004e005400'].pack('H*') expect(message.domain).to eq(domain) end it 'should set the user' do # test user = ['7400650073007400'].pack('H*') expect(message.user).to eq(user) end it 'should set the workstation' do # MEMBER workstation = ['4d0045004d00420045005200'].pack('H*') expect(message.workstation).to eq(workstation) end describe '#ntlm_version' do let(:ver) { message.ntlm_version } it 'should be :ntlmv2' do expect(ver).to eq(:ntlmv2) end end end end describe '#serialize' do context 'when the username contains non-ASCI characters' do let(:t3) { t2 = Net::NTLM::Message::Type2.new t2.response( { :user => 'Hélène', :password => '123456', :domain => '' }, { :ntlmv2 => true, :workstation => 'testlab.local' } ) } it 'serializes without error' do expect { t3.serialize }.not_to raise_error end end subject(:message) { described_class.create(opts) } context 'with the UNICODE flag set' do let(:opts) { {lm_response: "\x00".b, ntlm_response: '', domain: '', workstation: '', user: '', flag: Net::NTLM::DEFAULT_FLAGS[:TYPE3] | Net::NTLM::FLAGS[:UNICODE] } } it 'should pad the domain field to a multiple of 2' do message.serialize expect(message[:domain][:offset].value % 2).to eq 0 end it 'should pad the user field to a multiple of 2' do message.serialize expect(message[:user][:offset].value % 2).to eq 0 end it 'should pad the workstation field to a multiple of 2' do message.serialize expect(message[:workstation][:offset].value % 2).to eq 0 end end end end rubyntlm-0.6.5/spec/lib/net/ntlm/message/type0_spec.rb0000644000004100000410000000072214634001700022706 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Message::Type0 do fields = [ { :name => :sign, :class => Net::NTLM::String, :value => Net::NTLM::SSP_SIGN, :active => true }, { :name => :type, :class => Net::NTLM::Int32LE, :value => 0, :active => true }, ] flags = [ :UNICODE, :OEM, :REQUEST_TARGET, :NTLM, :ALWAYS_SIGN, :NTLM2_KEY ] it_behaves_like 'a fieldset', fields it_behaves_like 'a message', flags endrubyntlm-0.6.5/spec/lib/net/ntlm/int64_le_spec.rb0000644000004100000410000000077514634001700021655 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Int64LE do int_values = { :default => 5294967295, :default_hex => [5294967295 & 0x00000000ffffffff, 5294967295 >> 32].pack("V2"), :alt => 5294967294, :alt_hex => [5294967294 & 0x00000000ffffffff, 5294967294 >> 32].pack("V2"), :small => "\x5C\x24\x10\x0f", :size => 8, :bits => 64 } it_behaves_like 'a field', 252716124, false it_behaves_like 'an integer field', int_values endrubyntlm-0.6.5/spec/lib/net/ntlm/version_spec.rb0000644000004100000410000000145314634001700021710 0ustar www-datawww-datarequire 'spec_helper' require File.expand_path("#{File.dirname(__FILE__)}/../../../../lib/net/ntlm/version") describe Net::NTLM::VERSION do it 'should contain an integer value for Major Version' do expect(Net::NTLM::VERSION::MAJOR).to be_an Integer end it 'should contain an integer value for Minor Version' do expect(Net::NTLM::VERSION::MINOR).to be_an Integer end it 'should contain an integer value for Patch Version' do expect(Net::NTLM::VERSION::TINY).to be_an Integer end it 'should contain an aggregate version string' do string = [ Net::NTLM::VERSION::MAJOR, Net::NTLM::VERSION::MINOR, Net::NTLM::VERSION::TINY ].join('.') expect(Net::NTLM::VERSION::STRING).to be_a String expect(Net::NTLM::VERSION::STRING).to eq(string) end end rubyntlm-0.6.5/spec/lib/net/ntlm/encode_util_spec.rb0000644000004100000410000000073314634001700022515 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::EncodeUtil do context '#encode_utf16le' do it 'should convert an ASCII string to UTF' do expect(Net::NTLM::EncodeUtil.encode_utf16le('Test'.encode(::Encoding::ASCII_8BIT).freeze)).to eq("T\x00e\x00s\x00t\x00") end end context '#decode_utf16le' do it 'should convert a UTF string to ASCII' do expect(Net::NTLM::EncodeUtil.decode_utf16le("T\x00e\x00s\x00t\x00".freeze)).to eq('Test') end end end rubyntlm-0.6.5/spec/lib/net/ntlm/int16_le_spec.rb0000644000004100000410000000054314634001700021643 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Int16LE do int_values = { :default => 15, :default_hex => "\x0F\x00", :alt => 14, :alt_hex => "\x0E\x00", :small => "\x0F", :size => 2, :bits => 16 } it_behaves_like 'a field', 15, false it_behaves_like 'an integer field', int_values endrubyntlm-0.6.5/spec/lib/net/ntlm/int32_le_spec.rb0000644000004100000410000000063314634001700021641 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Int32LE do int_values = { :default => 252716124, :default_hex => "\x5C\x24\x10\x0f", :alt => 235938908, :alt_hex => "\x5C\x24\x10\x0e", :small => "\x0F\x00", :size => 4, :bits => 32 } it_behaves_like 'a field', 252716124, false it_behaves_like 'an integer field', int_values endrubyntlm-0.6.5/spec/lib/net/ntlm/message_spec.rb0000644000004100000410000000041214634001700021641 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Message do fields = [] flags = [ :UNICODE, :OEM, :REQUEST_TARGET, :NTLM, :ALWAYS_SIGN, :NTLM2_KEY ] it_behaves_like 'a fieldset', fields it_behaves_like 'a message', flags endrubyntlm-0.6.5/spec/lib/net/ntlm/target_info_spec.rb0000644000004100000410000000366014634001700022526 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::TargetInfo do let(:key1) { Net::NTLM::TargetInfo::MSV_AV_NB_COMPUTER_NAME } let(:value1) { 'some data' } let(:key2) { Net::NTLM::TargetInfo::MSV_AV_NB_DOMAIN_NAME } let(:value2) { 'some other data' } let(:data) do dt = key1.dup dt << [value1.length].pack('S') dt << value1 dt << key2.dup dt << [value2.length].pack('S') dt << value2 dt << Net::NTLM::TargetInfo::MSV_AV_EOL dt << [0].pack('S') dt.force_encoding(Encoding::ASCII_8BIT) end subject { Net::NTLM::TargetInfo.new(data) } describe 'invalid data' do context 'invalid pair id' do let(:data) { "\xFF\x00" } it 'returns an error' do expect{subject}.to raise_error Net::NTLM::InvalidTargetDataError end end end describe '#av_pairs' do it 'returns the pair values with the given keys' do expect(subject.av_pairs[key1]).to eq value1 expect(subject.av_pairs[key2]).to eq value2 end context "target data is nil" do subject { Net::NTLM::TargetInfo.new(nil) } it 'returns the pair values with the given keys' do expect(subject.av_pairs).to be_empty end end end describe '#to_s' do let(:data) do dt = key1.dup dt << [value1.length].pack('S') dt << value1 dt << key2.dup dt << [value2.length].pack('S') dt << value2 dt.force_encoding(Encoding::ASCII_8BIT) end let(:new_key) { Net::NTLM::TargetInfo::MSV_AV_CHANNEL_BINDINGS } let(:new_value) { 'bindings' } let(:new_data) do dt = data dt << new_key dt << [new_value.length].pack('S') dt << new_value dt << Net::NTLM::TargetInfo::MSV_AV_EOL dt << [0].pack('S') dt.force_encoding(Encoding::ASCII_8BIT) end it 'returns bytes with any new data added' do subject.av_pairs[new_key] = new_value expect(subject.to_s).to eq new_data end end end rubyntlm-0.6.5/spec/lib/net/ntlm/security_buffer_spec.rb0000644000004100000410000000355414634001700023427 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::SecurityBuffer do fields = [ { :name => :length, :class => Net::NTLM::Int16LE, :value => 0, :active => true }, { :name => :allocated, :class => Net::NTLM::Int16LE, :value => 0, :active => true }, { :name => :offset, :class => Net::NTLM::Int32LE, :value => 0, :active => true }, ] it_behaves_like 'a fieldset', fields it_behaves_like 'a field', 'WORKSTATION', true subject(:domain_security_buffer) do Net::NTLM::SecurityBuffer.new({ :value => 'WORKSTATION', :active => true }) end context 'when setting the value directly' do before(:each) do domain_security_buffer.value = 'DOMAIN1' end it 'should change the value' do expect(domain_security_buffer.value).to eq('DOMAIN1') end it 'should adjust the length field to the size of the new value' do expect(domain_security_buffer.length).to eq(7) end it 'should adjust the allocated field to the size of the new value' do expect(domain_security_buffer.allocated).to eq(7) end end context '#data_size' do it 'should return the size of the value if active' do expect(domain_security_buffer.data_size).to eq(11) end it 'should return 0 if inactive' do domain_security_buffer.active = false expect(domain_security_buffer.data_size).to eq(0) end end context '#parse' do it 'should read in a properly formatted string' do # Length of the string is 8 length = "\x08\x00" # Space allocated is 8 allocated = "\x08\x00" # The offset that the actual value begins at is also 8 offset = "\x08\x00\x00\x00" string_to_parse = "#{length}#{allocated}#{offset}FooBarBaz" expect(domain_security_buffer.parse(string_to_parse)).to eq(8) expect(domain_security_buffer.value).to eq('FooBarBa') end end end rubyntlm-0.6.5/spec/lib/net/ntlm/string_spec.rb0000644000004100000410000000322714634001700021532 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::String do it_behaves_like 'a field', 'Foo', false let(:active) { Net::NTLM::String.new({ :value => 'Test', :active => true, :size => 4 }) } let(:inactive) { Net::NTLM::String.new({ :value => 'Test', :active => false, :size => 4 }) } context '#serialize' do it 'should return the value when active' do expect(active.serialize).to eq('Test') end it 'should return an empty string when inactive' do expect(inactive.serialize).to eq('') end it 'should coerce non-string values into strings' do active.value = 15 expect(active.serialize).to eq('15') end it 'should return empty string on a nil' do active.value = nil expect(active.serialize).to eq('') end end context '#value=' do it 'should set active to false if it empty' do active.value = '' expect(active.active).to eq(false) end it 'should adjust the size based on the value set' do expect(active.size).to eq(4) active.value = 'Foobar' expect(active.size).to eq(6) end end context '#parse' do it 'should read in a string of the proper size' do expect(active.parse('tseT')).to eq(4) expect(active.value).to eq('tseT') end it 'should not read in a string that is too small' do expect(active.parse('B')).to eq(0) expect(active.value).to eq('Test') end it 'should be able to read from an offset and only for the given size' do expect(active.parse('FooBarBaz',3)).to eq(4) expect(active.value).to eq('BarB') end end end rubyntlm-0.6.5/spec/lib/net/ntlm/blob_spec.rb0000644000004100000410000000136414634001700021142 0ustar www-datawww-datarequire 'spec_helper' describe Net::NTLM::Blob do fields = [ { :name => :blob_signature, :class => Net::NTLM::Int32LE, :value => 257, :active => true }, { :name => :reserved, :class => Net::NTLM::Int32LE, :value => 0, :active => true }, { :name => :timestamp, :class => Net::NTLM::Int64LE, :value => 0, :active => true }, { :name => :challenge, :class => Net::NTLM::String, :value => '', :active => true }, { :name => :unknown1, :class => Net::NTLM::Int32LE, :value => 0, :active => true }, { :name => :target_info, :class => Net::NTLM::String, :value => '', :active => true }, { :name => :unknown2, :class => Net::NTLM::Int32LE, :value => 0, :active => true }, ] it_behaves_like 'a fieldset', fields end rubyntlm-0.6.5/spec/support/0000755000004100000410000000000014634001700016067 5ustar www-datawww-datarubyntlm-0.6.5/spec/support/shared/0000755000004100000410000000000014634001700017335 5ustar www-datawww-datarubyntlm-0.6.5/spec/support/shared/examples/0000755000004100000410000000000014634001700021153 5ustar www-datawww-datarubyntlm-0.6.5/spec/support/shared/examples/net/0000755000004100000410000000000014634001700021741 5ustar www-datawww-datarubyntlm-0.6.5/spec/support/shared/examples/net/ntlm/0000755000004100000410000000000014634001700022713 5ustar www-datawww-datarubyntlm-0.6.5/spec/support/shared/examples/net/ntlm/fieldset_shared.rb0000644000004100000410000001540314634001700026370 0ustar www-datawww-datashared_examples_for 'a fieldset' do |fields| subject(:fieldset_class) do Class.new(described_class) end context 'the class' do it { should respond_to :string } it { should respond_to :int16LE } it { should respond_to :int32LE } it { should respond_to :int64LE } it { should respond_to :security_buffer } it { should respond_to :prototypes } it { should respond_to :names } it { should respond_to :types } it { should respond_to :opts } context 'adding a String Field' do before(:each) do fieldset_class.string(:test_string, { :value => 'Test'}) end it 'should set the prototypes correctly' do expect(fieldset_class.prototypes).to include([:test_string, Net::NTLM::String, {:value=>"Test"}]) end it 'should set the names correctly' do expect(fieldset_class.names).to include(:test_string) end it 'should set the types correctly' do expect(fieldset_class.types).to include(Net::NTLM::String) end it 'should set the opts correctly' do expect(fieldset_class.opts).to include({:value => 'Test'}) end context 'when creating an instance' do let(:fieldset_object) do fieldset_class.new end it 'should have the new accessor' do expect(fieldset_object).to respond_to(:test_string) end it 'should have the correct default value' do expect(fieldset_object.test_string).to eq('Test') end end end context 'adding a Int16LE Field' do before(:each) do fieldset_class.int16LE(:test_int, { :value => 15}) end it 'should set the prototypes correctly' do expect(fieldset_class.prototypes).to include([:test_int, Net::NTLM::Int16LE, {:value=>15}]) end it 'should set the names correctly' do expect(fieldset_class.names).to include(:test_int) end it 'should set the types correctly' do expect(fieldset_class.types).to include(Net::NTLM::Int16LE) end it 'should set the opts correctly' do expect(fieldset_class.opts).to include({:value => 15}) end context 'when creating an instance' do let(:fieldset_object) do fieldset_class.new end it 'should have the new accessor' do expect(fieldset_object).to respond_to(:test_int) end it 'should have the correct default value' do expect(fieldset_object.test_int).to eq(15) end end end context 'adding a Int32LE Field' do before(:each) do fieldset_class.int32LE(:test_int, { :value => 15}) end it 'should set the prototypes correctly' do expect(fieldset_class.prototypes).to include([:test_int, Net::NTLM::Int32LE, {:value=>15}]) end it 'should set the names correctly' do expect(fieldset_class.names).to include(:test_int) end it 'should set the types correctly' do expect(fieldset_class.types).to include(Net::NTLM::Int32LE) end it 'should set the opts correctly' do expect(fieldset_class.opts).to include({:value => 15}) end context 'when creating an instance' do let(:fieldset_object) do fieldset_class.new end it 'should have the new accessor' do expect(fieldset_object).to respond_to(:test_int) end it 'should have the correct default value' do expect(fieldset_object.test_int).to eq(15) end end end context 'adding a Int64LE Field' do before(:each) do fieldset_class.int64LE(:test_int, { :value => 15}) end it 'should set the prototypes correctly' do expect(fieldset_class.prototypes).to include([:test_int, Net::NTLM::Int64LE, {:value=>15}]) end it 'should set the names correctly' do expect(fieldset_class.names).to include(:test_int) end it 'should set the types correctly' do expect(fieldset_class.types).to include(Net::NTLM::Int64LE) end it 'should set the opts correctly' do expect(fieldset_class.opts).to include({:value => 15}) end context 'when creating an instance' do let(:fieldset_object) do fieldset_class.new end it 'should have the new accessor' do expect(fieldset_object).to respond_to(:test_int) end it 'should have the correct default value' do expect(fieldset_object.test_int).to eq(15) end end end context 'adding a SecurityBuffer Field' do before(:each) do fieldset_class.security_buffer(:test_buffer, { :value => 15}) end it 'should set the prototypes correctly' do expect(fieldset_class.prototypes).to include([:test_buffer, Net::NTLM::SecurityBuffer, {:value=>15}]) end it 'should set the names correctly' do expect(fieldset_class.names).to include(:test_buffer) end it 'should set the types correctly' do expect(fieldset_class.types).to include(Net::NTLM::SecurityBuffer) end it 'should set the opts correctly' do expect(fieldset_class.opts).to include({:value => 15}) end context 'when creating an instance' do let(:fieldset_object) do fieldset_class.new end it 'should have the new accessor' do expect(fieldset_object).to respond_to :test_buffer end it 'should have the correct default value' do expect(fieldset_object.test_buffer).to eq(15) end end end end context 'an instance' do subject(:fieldset_object) do # FieldSet Base Class and Message Base Class # have no fields by default and thus cannot be initialized # currently. Clumsy workaround for now. if described_class.names.empty? described_class.string(:test_string, { :value => 'Test', :active => true, :size => 4}) end described_class.new end it { should respond_to :serialize } it { should respond_to :parse } it { should respond_to :size } it { should respond_to :enable } it { should respond_to :disable } context 'fields' do fields.each do |field| it { should respond_to field[:name] } context "#{field[:name]}" do it "should be a #{field[:class].to_s}" do expect(fieldset_object[field[:name]].class).to eq(field[:class]) end it "should have a default value of #{field[:value]}" do expect(fieldset_object[field[:name]].value).to eq(field[:value]) end it "should have active set to #{field[:active]}" do expect(fieldset_object[field[:name]].active).to eq(field[:active]) end end end end end end rubyntlm-0.6.5/spec/support/shared/examples/net/ntlm/int_shared.rb0000644000004100000410000000234214634001700025361 0ustar www-datawww-datashared_examples_for 'an integer field' do |values| subject do described_class.new({ :value => values[:default], :active => true }) end context '#serialize' do it 'should serialize properly with an integer value' do expect(subject.serialize).to eq(values[:default_hex]) end it 'should raise an Exception for a String' do subject.value = 'A' expect {subject.serialize}.to raise_error end it 'should raise an Exception for Nil' do subject.value = nil expect {subject.serialize}.to raise_error end end context '#parse' do it "should parse a raw #{values[:bits].to_s}-bit integer from a string" do expect(subject.parse(values[:alt_hex])).to eq(values[:size]) expect(subject.value).to eq(values[:alt]) end it "should use an offset to find the #{values[:bits].to_s}-bit integer in the string" do expect(subject.parse("Value:#{values[:alt_hex]}",6)).to eq(values[:size]) expect(subject.value).to eq(values[:alt]) end it 'should return 0 and not change the value if the string is not big enough' do expect(subject.parse(values[:small])).to eq(0) expect(subject.value).to eq(values[:default]) end end end rubyntlm-0.6.5/spec/support/shared/examples/net/ntlm/message_shared.rb0000644000004100000410000000172514634001700026217 0ustar www-datawww-datashared_examples_for 'a message' do |flags| subject(:test_message) do unless described_class.names.include?(:flag) described_class.int32LE(:flag, {:value => Net::NTLM::DEFAULT_FLAGS[:TYPE1] }) end described_class.new end it { should respond_to :has_flag? } it { should respond_to :set_flag } it { should respond_to :dump_flags } it { should respond_to :encode64 } it { should respond_to :decode64 } it { should respond_to :head_size } it { should respond_to :data_size } it { should respond_to :size } it { should respond_to :security_buffers } it { should respond_to :deflag } it { should respond_to :data_edge } flags.each do |flag| it "should be able to check if the #{flag} flag is set" do expect(test_message.has_flag?(flag)).to be(true) end end it 'should be able to set a new flag' do test_message.set_flag(:DOMAIN_SUPPLIED) expect(test_message.has_flag?(:DOMAIN_SUPPLIED)).to be(true) end end rubyntlm-0.6.5/spec/support/shared/examples/net/ntlm/field_shared.rb0000644000004100000410000000103214634001700025645 0ustar www-datawww-datashared_examples_for 'a field' do | value, active| subject do described_class.new({ :value => value, :active => active }) end it { should respond_to :active } it { should respond_to :value } it { should respond_to :size } it { should respond_to :parse } it { should respond_to :serialize } it 'should set the value from initialize options' do expect(subject.value).to eq(value) end it 'should set active from initialize options' do expect(subject.active).to eq(active) end end rubyntlm-0.6.5/spec/support/certificates/0000755000004100000410000000000014634001700020534 5ustar www-datawww-datarubyntlm-0.6.5/spec/support/certificates/sha_256_hash.pem0000644000004100000410000000220714634001700023412 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDKjCCAhKgAwIBAgIQV0qwsk2MCoxI2Do7IQ6eQzANBgkqhkiG9w0BAQsFADAa MRgwFgYDVQQDDA8xOTIuMTY4LjEzNy4xNjEwHhcNMTYwMTI3MjIyMzA5WhcNMTcw MTI3MjI0MzA5WjAaMRgwFgYDVQQDDA8xOTIuMTY4LjEzNy4xNjEwggEiMA0GCSqG SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+bGWZQFYjF+bV1WJ1L/MGVNmJR89aJ44Z rKI/IXKFdbn5wjQPWng/DcaHR6xtMXQkc22boe58GK/uzl84ofbRa6qtboa5djdZ 9CGsd4Yf6CnVz4mhKSi+BnLi80ydhIRByxoX5bGcCSW6dixR5XiNMaMKzhCjQ+of TU+PBNt7doXB7p0mO4AZz42v4rorRiPNasETj6wlKhFKCMvPLePTwphCgCQsLvgG NQKtFD7TXvrZwplPSeCPhnzd1vHoZMisMn8ZVQ5dAfSEGGkPkOLO0htbUbdaNMoU DPyo7Bu62Q/dqqo1MNbMYM5Ilw8mxe4drOs9UupH0eMovFhVMO0LAgMBAAGjbDBq MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEw GgYDVR0RBBMwEYIPMTkyLjE2OC4xMzcuMTYxMB0GA1UdDgQWBBSLuqyHonSmdm8m 9R+z2obO/X3/+TANBgkqhkiG9w0BAQsFAAOCAQEAH4pDGBclTHrwF+Bkbfj81ibK E2SJSHbdhSx6YCsR28jXUOESfaik5ZPPMXscJqVc1FPpsU9clPFnGiAf0Kt48gsR twfrRSGgJRv1ZgQyJ4dEQkXbQf2+8uY25Rv4kkFDSvPrE6E9o9Jf9bjqefUYski1 YoYdWzgrh/2qoNhnM34wizZgE1bWYbWA9MlUuWH9q/OBEx9uP/K53SXOR7DRzYcY Kg1Z7hV86nvc0WutjEadgdtvJ7eUlg8vAWZqWo5SIdp69l0OEWUlHiaRsPImS5Hd pX3W8n0wHCxBSntDww7U3SHg6DrYf72taBIQW7xFf63S37yLP4CNss68GqPdyQ== -----END CERTIFICATE----- rubyntlm-0.6.5/.rspec0000644000004100000410000000003714634001700014536 0ustar www-datawww-data--format documentation --color rubyntlm-0.6.5/Rakefile0000644000004100000410000000100314634001700015060 0ustar www-datawww-datarequire "bundler/gem_tasks" require 'github_changelog_generator/task' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task :default => :spec desc "Generate code coverage" task :coverage do ENV['COVERAGE'] = 'true' Rake::Task["spec"].execute end desc "Open a Pry console for this library" task :console do require 'pry' require 'net/ntlm' ARGV.clear Pry.start end GitHubChangelogGenerator::RakeTask.new :changelog do |config| config.future_release = Net::NTLM::VERSION::STRING end rubyntlm-0.6.5/Gemfile0000644000004100000410000000004714634001700014715 0ustar www-datawww-datasource 'https://rubygems.org' gemspec rubyntlm-0.6.5/LICENSE0000644000004100000410000000212414634001700014425 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2013 Paul Morton, Matt Zukowski, Kohei Kajimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.rubyntlm-0.6.5/README.md0000644000004100000410000000223214634001700014677 0ustar www-datawww-data# Ruby/NTLM -- NTLM Authentication Library for Ruby [![CI status](https://github.com/WinRb/rubyntlm/actions/workflows/build.yml/badge.svg)](https://github.com/WinRb/rubyntlm/actions/workflows/build.yml) Ruby/NTLM provides message creator and parser for the NTLM authentication. __100% Ruby__ How to install -------------- ```ruby require 'rubyntlm' ``` Simple Example -------------- ### Creating NTLM Type 1 message ```ruby t1 = Net::NTLM::Message::Type1.new() ``` ### Parsing NTLM Type 2 message from server ```ruby t2 = Net::NTLM::Message.parse(message_from_server) ``` ### Creating NTLM Type 3 message ```ruby t3 = t2.response({:user => 'user', :password => 'passwd'}) ``` Support ------- https://groups.google.com/forum/?fromgroups#!forum/rubyntlm Contributing ------------ 1. Fork it. 2. Create a branch (git checkout -b my_feature_branch) 3. Commit your changes (git commit -am "Added a sweet feature") 4. Push to the branch (git push origin my_feature_branch) 5. Create a pull requst from your branch into master (Please be sure to provide enough detail for us to cipher what this change is doing) rubyntlm-0.6.5/examples/0000755000004100000410000000000014634001700015237 5ustar www-datawww-datarubyntlm-0.6.5/examples/http.rb0000644000004100000410000000333414634001700016546 0ustar www-datawww-data# $Id: http.rb,v 1.2 2006/10/05 01:36:52 koheik Exp $ require 'socket' $:.unshift(File.dirname(__FILE__) + '/../lib') require 'net/ntlm' $user = nil $passwd = nil $host = "www" $port = 80 def header(f, host) f.print "GET / HTTP/1.1\r\n" f.print "Host: #{host}\r\n" f.print "Keep-Alive: 300\r\n" f.print "Connection: keep-alive\r\n" end def main s = TCPSocket.new($host, $port) # client -> server t1 = Net::NTLM::Message::Type1.new() header(s, $host) s.print "Authorization: NTLM " + t1.encode64 + "\r\n" s.print "\r\n" # server -> client length = 0 while(line = s.gets) if /^WWW-Authenticate: (NTLM|Negotiate) (.+)\r\n/ =~ line msg = $2 end if /^Content-Length: (\d+)\r\n/ =~ line length = $1.to_i end if /^\r\n/ =~ line if length > 0 cont = s.read(length) end break end end t2 = Net::NTLM::Message.decode64(msg) unless $user and $passwd target = t2.target_name target = Net::NTLM::EncodeUtil.decode_utf16le(target) if t2.has_flag?(:UNICODE) puts "Target: #{target}" print "User name: " ($user = $stdin.readline).chomp! print "Password: " ($passwd = $stdin.readline).chomp! end # client -> server, again t3 = t2.response({:user => $user, :password => $passwd}, {:ntlmv2 => true}) header(s, $host) s.print "Authorization: NTLM " + t3.encode64 + "\r\n" s.print "\r\n" # server -> client length = 0 while(line = s.gets) if /^WWW-Authenticate: (NTLM|Negotiate) (.+)\r\n/ =~ line msg = $2 end if /^Content-Length: (\d+)\r\n/ =~ line length = $1.to_i end if /^\r\n/ =~ line if length > 0 p cont = s.read(length) end break end end s.close end main rubyntlm-0.6.5/examples/imap.rb0000644000004100000410000000350514634001700016515 0ustar www-datawww-data# $Id: imap.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $ require "net/imap" $:.unshift(File.dirname(__FILE__) + '/../lib') require "net/ntlm" Net::IMAP::debug = true $host = "localhost" $port = 143 $ssl = false $user = nil $passwd = nil module Net class IMAP class NtlmAuthenticator def process(data) case @state when 1 @state = 2 t1 = Net::NTLM::Message::Type1.new() return t1.serialize when 2 @state = 3 t2 = Net::NTLM::Message.parse(data) t3 = t2.response({:user => @user, :password => @password}, {:ntlmv2 => (@ntlm_type == "ntlmv2")}) return t3.serialize end end private def initialize(user, password, ntlm_type = "ntlmv2") @user = user @password = password @ntlm_type = @ntlm_type @state = 1 end end add_authenticator "NTLM", NtlmAuthenticator class ResponseParser def continue_req match(T_PLUS) if lookahead.symbol == T_CRLF # means empty message return ContinuationRequest.new(ResponseText.new(nil, ""), @str) end match(T_SPACE) return ContinuationRequest.new(resp_text, @str) end end end end unless $user and $passwd print "User name: " ($user = $stdin.readline).chomp! print "Password: " ($passwd = $stdin.readline).chomp! end imap = Net::IMAP.new($host, $port, $ssl) imap.authenticate("NTLM", $user, $passwd) imap.examine("Inbox") # imap.search(["RECENT"]).each do |message_id| # envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"] # from = envelope.from.nil? ? "" : envelope.from[0].name # subject = envelope.subject # puts "#{message_id} #{from}: \t#{subject}" # end imap.logout # imap.disconnect rubyntlm-0.6.5/examples/smtp.rb0000644000004100000410000000400414634001700016545 0ustar www-datawww-data# $Id: smtp.rb,v 1.2 2006/10/05 01:36:52 koheik Exp $ require 'socket' $:.unshift(File.dirname(__FILE__) + '/../lib') require 'net/ntlm' $user = nil $passwd = nil $host = "localhost" $port = 25 $debug = true def readline(f) (l = f.gets).chomp! puts "srv> " + l if $debug l end def writeline(f, str) puts "cli> " + str if $debug f.print str + "\r\n" end def main s = TCPSocket.new($host, $port) # greetings readline s writeline s, "EHLO #{$host}" while(line = readline(s)) login = true if /^250-AUTH=LOGIN/ =~ line ntlm = true if /^250-AUTH.+NTLM.*/ =~ line break if /^250 OK/ =~ line end unless ntlm and login raise RuntimeError, "it looks like the server doesn't support NTLM Login" end # send Type1 Message t1 = Net::NTLM::Message::Type1.new() writeline s, "AUTH NTLM " + t1.encode64 # receive Type2 Message, i hope line = readline s unless /334 (.+)/ =~ line raise RuntimeError, "i don't recognize this: #{line}" end t2 = Net::NTLM::Message.decode64($1) unless $user and $passwd target = t2.target_name target = Net::NTLM::decode_utf16le(target) if t2.has_flag?(:UNICODE) puts "Target: #{target}" print "User name: " ($user = $stdin.readline).chomp! print "Password: " ($passwd = $stdin.readline).chomp! end # send Type3 Message t3 = t2.response({:user => $user, :password => $passwd}, {:ntlmv2 => true}) writeline s, t3.encode64 # and result is... line = readline s unless /^235(.+)Authentication successful./i =~ line raise RuntimeError, "sorry, authentication failed." end # do real job here like... # from = $user # to = "billg" # writeline s, "MAIL FROM: #{from}" # readline s # writeline s, "RCPT TO: #{to}" # readline s # writeline s, "DATA" # readline s # writeline s, "From: #{from}" # writeline s, "To: #{to}" # writeline s, "blab blab blab..." # writeline s, "#{from}" # writeline s, "." # readline s # say bye writeline s, "QUIT" s.close end main rubyntlm-0.6.5/CHANGELOG.md0000644000004100000410000001663614634001700015246 0ustar www-datawww-data# Change Log ## 0.6.5 (2024-06-11) * Update available NegotiateFlags during authentication * Fix NTLMv2 hash when username contains non-ASCII characters by @cdelafuente-r7 in https://github.com/WinRb/rubyntlm/pull/56 ## 0.6.4 (2024-06-06) * Fix applying DES-CBC when using OpenSSL 3 by @paulvt in https://github.com/WinRb/rubyntlm/pull/51 * Add dependency to `base64` gem by @yahonda in https://github.com/WinRb/rubyntlm/pull/62 * Avoid usage of legacy algorithms on libssl-3.0+ by @larskanis in https://github.com/WinRb/rubyntlm/pull/53 * Add anonymous authentication support by @zeroSteiner in https://github.com/WinRb/rubyntlm/pull/45 * Update minimum supported ruby to 2.6. Add support for ruby 3.2 and 3.3 ## [0.6.3](https://github.com/WinRb/rubyntlm/tree/0.6.3) (2021-01-26) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.6.2...0.6.3) **Closed issues:** - Timeout issues with mailcatcher [\#18](https://github.com/WinRb/rubyntlm/issues/18) **Merged pull requests:** - Fix an error showing the key not being set on the DES cypher [\#41](https://github.com/WinRb/rubyntlm/pull/41) ([Castone22](https://github.com/Castone22)) ## [v0.6.2](https://github.com/WinRb/rubyntlm/tree/v0.6.2) (2017-04-24) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.6.1...v0.6.2) **Merged pull requests:** - preps 0.6.2 release and adds a changelog generator [\#35](https://github.com/WinRb/rubyntlm/pull/35) ([mwrock](https://github.com/mwrock)) - Support Ruby 2.4 [\#34](https://github.com/WinRb/rubyntlm/pull/34) ([fwininger](https://github.com/fwininger)) - ignore pkg directory in git [\#33](https://github.com/WinRb/rubyntlm/pull/33) ([mwrock](https://github.com/mwrock)) ## [v0.6.1](https://github.com/WinRb/rubyntlm/tree/v0.6.1) (2016-09-15) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.6.0...v0.6.1) **Merged pull requests:** - Release 0.6.1 [\#32](https://github.com/WinRb/rubyntlm/pull/32) ([mwrock](https://github.com/mwrock)) - only test supported rubies and do not test twice [\#31](https://github.com/WinRb/rubyntlm/pull/31) ([mwrock](https://github.com/mwrock)) ## [v0.6.0](https://github.com/WinRb/rubyntlm/tree/v0.6.0) (2016-02-16) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.5.3...v0.6.0) **Closed issues:** - support Extended Protection for Authentication \(Channel Binding Tokens\) [\#27](https://github.com/WinRb/rubyntlm/issues/27) - RubyNTLM is not documented [\#20](https://github.com/WinRb/rubyntlm/issues/20) ## [v0.5.3](https://github.com/WinRb/rubyntlm/tree/v0.5.3) (2016-01-22) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/0.5.3...v0.5.3) ## [0.5.3](https://github.com/WinRb/rubyntlm/tree/0.5.3) (2016-01-22) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/0.5.2...0.5.3) **Merged pull requests:** - fix session.workstation when passing only domain [\#26](https://github.com/WinRb/rubyntlm/pull/26) ([mwrock](https://github.com/mwrock)) ## [0.5.2](https://github.com/WinRb/rubyntlm/tree/0.5.2) (2015-07-20) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/0.5.1...0.5.2) **Merged pull requests:** - Add Pass the Hash capability to the NTLM client [\#24](https://github.com/WinRb/rubyntlm/pull/24) ([thelightcosine](https://github.com/thelightcosine)) ## [0.5.1](https://github.com/WinRb/rubyntlm/tree/0.5.1) (2015-06-23) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/0.5.0...0.5.1) **Merged pull requests:** - fix NTLM1 auth - NTLM::lm\_response\(pwd, chal\) and NTLM::ntlm\_response… [\#23](https://github.com/WinRb/rubyntlm/pull/23) ([marek-veber](https://github.com/marek-veber)) - Make the session key available to clients [\#21](https://github.com/WinRb/rubyntlm/pull/21) ([jlee-r7](https://github.com/jlee-r7)) ## [0.5.0](https://github.com/WinRb/rubyntlm/tree/0.5.0) (2015-02-22) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.4.0...0.5.0) **Closed issues:** - require 'net/ntlm/version' in spec/lib/net/ntlm/version\_spec.rb [\#12](https://github.com/WinRb/rubyntlm/issues/12) - License missing from gemspec [\#5](https://github.com/WinRb/rubyntlm/issues/5) **Merged pull requests:** - Protect against mutating frozen strings [\#30](https://github.com/WinRb/rubyntlm/pull/30) ([mwrock](https://github.com/mwrock)) - Support Extended Protection for Authentication \(Channel binding\) [\#28](https://github.com/WinRb/rubyntlm/pull/28) ([mwrock](https://github.com/mwrock)) - Encode client and domain in oem/unicode in `Client\#authenticate!` [\#19](https://github.com/WinRb/rubyntlm/pull/19) ([jlee-r7](https://github.com/jlee-r7)) - require version to fix specs [\#17](https://github.com/WinRb/rubyntlm/pull/17) ([sneal](https://github.com/sneal)) - Initial go at an NTLM Client that will do session signing/sealing [\#16](https://github.com/WinRb/rubyntlm/pull/16) ([zenchild](https://github.com/zenchild)) - Verify passwords in Type3 messages [\#15](https://github.com/WinRb/rubyntlm/pull/15) ([jlee-r7](https://github.com/jlee-r7)) - RSpect should =\> expect modernization [\#14](https://github.com/WinRb/rubyntlm/pull/14) ([zenchild](https://github.com/zenchild)) - update http example with EncodeUtil class [\#11](https://github.com/WinRb/rubyntlm/pull/11) ([stensonb](https://github.com/stensonb)) - update readme with how to use and the correct namespacing for using the gem [\#10](https://github.com/WinRb/rubyntlm/pull/10) ([stensonb](https://github.com/stensonb)) ## [v0.4.0](https://github.com/WinRb/rubyntlm/tree/v0.4.0) (2013-09-12) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.3.4...v0.4.0) **Closed issues:** - The domain should always be capitalized otherwise domain authentication fails [\#7](https://github.com/WinRb/rubyntlm/issues/7) **Merged pull requests:** - Add licensing information and clean up attributions to provide licensing... [\#9](https://github.com/WinRb/rubyntlm/pull/9) ([pmorton](https://github.com/pmorton)) - Upcase the domain [\#8](https://github.com/WinRb/rubyntlm/pull/8) ([pmorton](https://github.com/pmorton)) - Refactor/refactor classes [\#6](https://github.com/WinRb/rubyntlm/pull/6) ([thelightcosine](https://github.com/thelightcosine)) ## [v0.3.4](https://github.com/WinRb/rubyntlm/tree/v0.3.4) (2013-08-08) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.3.3...v0.3.4) ## [v0.3.3](https://github.com/WinRb/rubyntlm/tree/v0.3.3) (2013-07-23) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.3.2...v0.3.3) **Merged pull requests:** - Typo in NTLM namespace calls [\#4](https://github.com/WinRb/rubyntlm/pull/4) ([thelightcosine](https://github.com/thelightcosine)) ## [v0.3.2](https://github.com/WinRb/rubyntlm/tree/v0.3.2) (2013-06-24) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.3.1...v0.3.2) **Closed issues:** - Gem is locked at 1.9.2 [\#1](https://github.com/WinRb/rubyntlm/issues/1) ## [v0.3.1](https://github.com/WinRb/rubyntlm/tree/v0.3.1) (2013-03-29) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.3.0...v0.3.1) **Merged pull requests:** - Fix gemspec for the proper ruby version and bump the version [\#2](https://github.com/WinRb/rubyntlm/pull/2) ([pmorton](https://github.com/pmorton)) ## [v0.3.0](https://github.com/WinRb/rubyntlm/tree/v0.3.0) (2013-03-25) [Full Changelog](https://github.com/WinRb/rubyntlm/compare/v0.2.0...v0.3.0) ## [v0.2.0](https://github.com/WinRb/rubyntlm/tree/v0.2.0) (2013-03-22) \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* rubyntlm-0.6.5/.devcontainer/0000755000004100000410000000000014634001700016160 5ustar www-datawww-datarubyntlm-0.6.5/.devcontainer/devcontainer.json0000644000004100000410000000155214634001700021537 0ustar www-datawww-data// For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/ruby { "name": "Ruby", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "image": "mcr.microsoft.com/devcontainers/ruby:1-3.3-bullseye" // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "ruby --version", // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" }