ruby_smb 3.1.2 → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - checksums.yaml.gz.sig +0 -0
 - data/examples/file_server.rb +6 -68
 - data/examples/virtual_file_server.rb +10 -62
 - data/lib/ruby_smb/client.rb +2 -2
 - data/lib/ruby_smb/ntlm/client.rb +74 -0
 - data/lib/ruby_smb/ntlm.rb +1 -0
 - data/lib/ruby_smb/server/cli.rb +121 -0
 - data/lib/ruby_smb/server.rb +1 -0
 - data/lib/ruby_smb/version.rb +1 -1
 - data/spec/lib/ruby_smb/ntlm/client/session_spec.rb +114 -0
 - data/spec/lib/ruby_smb/ntlm/client_spec.rb +36 -0
 - data.tar.gz.sig +0 -0
 - metadata +8 -2
 - metadata.gz.sig +0 -0
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 0b10829db92d5746b069916440f0e91ca006c370053fdc02621ed75723efbf40
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: aeeeb396277c47b16ff977e33344ab08da275a0632d6a9a220aac7e733e91311
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 7269f0664dd840a83d39d5d1b3a953f87d050bcffbea0b9993685ef1ec91c9047a9117d88ee305d2c467555b475a7c15e82f0f8bfa1b98d077ea00fd401de200
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a5997a65e48cd66585896c55cd8a01fbbee4a8b04280a15ecacdd79501fdd42caf772ac712a32228ea6c1991a6db27c9657d8829ca412691df0834e392c45284
         
     | 
    
        checksums.yaml.gz.sig
    CHANGED
    
    | 
         Binary file 
     | 
    
        data/examples/file_server.rb
    CHANGED
    
    | 
         @@ -3,81 +3,19 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'bundler/setup'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'optparse'
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'ruby_smb'
         
     | 
| 
       6 
     | 
    
         
            -
            require 'ruby_smb/gss/provider/ntlm'
         
     | 
| 
       7 
6 
     | 
    
         | 
| 
       8 
7 
     | 
    
         
             
            # we just need *a* default encoding to handle the strings from the NTLM messages
         
     | 
| 
       9 
8 
     | 
    
         
             
            Encoding.default_internal = 'UTF-8' if Encoding.default_internal.nil?
         
     | 
| 
       10 
9 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
            options = {
         
     | 
| 
       12 
     | 
    
         
            -
               
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
               
     | 
| 
       15 
     | 
    
         
            -
              username: 'RubySMB',
         
     | 
| 
       16 
     | 
    
         
            -
              password: 'password',
         
     | 
| 
       17 
     | 
    
         
            -
              share_name: 'home',
         
     | 
| 
       18 
     | 
    
         
            -
              share_path: '.',
         
     | 
| 
       19 
     | 
    
         
            -
              smbv1: true,
         
     | 
| 
       20 
     | 
    
         
            -
              smbv2: true,
         
     | 
| 
       21 
     | 
    
         
            -
              smbv3: true
         
     | 
| 
       22 
     | 
    
         
            -
            }
         
     | 
| 
       23 
     | 
    
         
            -
            OptionParser.new do |opts|
         
     | 
| 
       24 
     | 
    
         
            -
              opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
         
     | 
| 
       25 
     | 
    
         
            -
              opts.on("--path PATH", "The path to share (default: #{options[:share_path]})") do |path|
         
     | 
| 
      
 10 
     | 
    
         
            +
            options = RubySMB::Server::Cli.parse(defaults: { share_path: '.' }) do |options, parser|
         
     | 
| 
      
 11 
     | 
    
         
            +
              parser.banner = "Usage: #{File.basename(__FILE__)} [options]"
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              parser.on("--share-path SHARE_PATH", "The path to share (default: #{options[:share_path]})") do |path|
         
     | 
| 
       26 
14 
     | 
    
         
             
                options[:share_path] = path
         
     | 
| 
       27 
15 
     | 
    
         
             
              end
         
     | 
| 
       28 
     | 
    
         
            -
              opts.on("--share SHARE", "The share name (default: #{options[:share_name]})") do |share|
         
     | 
| 
       29 
     | 
    
         
            -
                options[:share_name] = share
         
     | 
| 
       30 
     | 
    
         
            -
              end
         
     | 
| 
       31 
     | 
    
         
            -
              opts.on("--[no-]anonymous", "Allow anonymous access (default: #{options[:allow_anonymous]})") do |allow_anonymous|
         
     | 
| 
       32 
     | 
    
         
            -
                options[:allow_anonymous] = allow_anonymous
         
     | 
| 
       33 
     | 
    
         
            -
              end
         
     | 
| 
       34 
     | 
    
         
            -
              opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
         
     | 
| 
       35 
     | 
    
         
            -
                options[:smbv1] = smbv1
         
     | 
| 
       36 
     | 
    
         
            -
              end
         
     | 
| 
       37 
     | 
    
         
            -
              opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
         
     | 
| 
       38 
     | 
    
         
            -
                options[:smbv2] = smbv2
         
     | 
| 
       39 
     | 
    
         
            -
              end
         
     | 
| 
       40 
     | 
    
         
            -
              opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
         
     | 
| 
       41 
     | 
    
         
            -
                options[:smbv3] = smbv3
         
     | 
| 
       42 
     | 
    
         
            -
              end
         
     | 
| 
       43 
     | 
    
         
            -
              opts.on("--[no-]guests", "Allow guest accounts (default: #{options[:allow_guests]})") do |allow_guests|
         
     | 
| 
       44 
     | 
    
         
            -
                options[:allow_guests] = allow_guests
         
     | 
| 
       45 
     | 
    
         
            -
              end
         
     | 
| 
       46 
     | 
    
         
            -
              opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
         
     | 
| 
       47 
     | 
    
         
            -
                if username.include?('\\')
         
     | 
| 
       48 
     | 
    
         
            -
                  options[:domain], options[:username] = username.split('\\', 2)
         
     | 
| 
       49 
     | 
    
         
            -
                else
         
     | 
| 
       50 
     | 
    
         
            -
                  options[:username] = username
         
     | 
| 
       51 
     | 
    
         
            -
                end
         
     | 
| 
       52 
     | 
    
         
            -
              end
         
     | 
| 
       53 
     | 
    
         
            -
              opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
         
     | 
| 
       54 
     | 
    
         
            -
                options[:password] = password
         
     | 
| 
       55 
     | 
    
         
            -
              end
         
     | 
| 
       56 
     | 
    
         
            -
            end.parse!
         
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
            ntlm_provider = RubySMB::Gss::Provider::NTLM.new(
         
     | 
| 
       59 
     | 
    
         
            -
              allow_anonymous: options[:allow_anonymous],
         
     | 
| 
       60 
     | 
    
         
            -
              allow_guests: options[:allow_guests]
         
     | 
| 
       61 
     | 
    
         
            -
            )
         
     | 
| 
       62 
     | 
    
         
            -
            ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain])  # password can also be an NTLM hash
         
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
            server = RubySMB::Server.new(
         
     | 
| 
       65 
     | 
    
         
            -
              gss_provider: ntlm_provider,
         
     | 
| 
       66 
     | 
    
         
            -
              logger: :stdout
         
     | 
| 
       67 
     | 
    
         
            -
            )
         
     | 
| 
       68 
     | 
    
         
            -
            server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
         
     | 
| 
       69 
     | 
    
         
            -
            server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
         
     | 
| 
       70 
     | 
    
         
            -
            server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
            if server.dialects.empty?
         
     | 
| 
       73 
     | 
    
         
            -
              puts "at least one version must be enabled"
         
     | 
| 
       74 
     | 
    
         
            -
              exit false
         
     | 
| 
       75 
16 
     | 
    
         
             
            end
         
     | 
| 
       76 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
            server = RubySMB::Server::Cli.build(options)
         
     | 
| 
       77 
19 
     | 
    
         
             
            server.add_share(RubySMB::Server::Share::Provider::Disk.new(options[:share_name], options[:share_path]))
         
     | 
| 
       78 
     | 
    
         
            -
            puts "server is running"
         
     | 
| 
       79 
     | 
    
         
            -
            server.run do
         
     | 
| 
       80 
     | 
    
         
            -
              puts "received connection"
         
     | 
| 
       81 
     | 
    
         
            -
              true
         
     | 
| 
       82 
     | 
    
         
            -
            end
         
     | 
| 
       83 
20 
     | 
    
         | 
| 
      
 21 
     | 
    
         
            +
            RubySMB::Server::Cli.run(server)
         
     | 
| 
         @@ -3,7 +3,6 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'bundler/setup'
         
     | 
| 
       4 
4 
     | 
    
         
             
            require 'optparse'
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'ruby_smb'
         
     | 
| 
       6 
     | 
    
         
            -
            require 'ruby_smb/gss/provider/ntlm'
         
     | 
| 
       7 
6 
     | 
    
         | 
| 
       8 
7 
     | 
    
         
             
            # we just need *a* default encoding to handle the strings from the NTLM messages
         
     | 
| 
       9 
8 
     | 
    
         
             
            Encoding.default_internal = 'UTF-8' if Encoding.default_internal.nil?
         
     | 
| 
         @@ -32,70 +31,23 @@ MAGIC_8_BALL_ANSWERS = [ 
     | 
|
| 
       32 
31 
     | 
    
         
             
              'Very doubtful'
         
     | 
| 
       33 
32 
     | 
    
         
             
            ]
         
     | 
| 
       34 
33 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
            options =  
     | 
| 
       36 
     | 
    
         
            -
               
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
               
     | 
| 
       39 
     | 
    
         
            -
              password: 'password',
         
     | 
| 
       40 
     | 
    
         
            -
              share_name: 'home',
         
     | 
| 
       41 
     | 
    
         
            -
              smbv1: true,
         
     | 
| 
       42 
     | 
    
         
            -
              smbv2: true,
         
     | 
| 
       43 
     | 
    
         
            -
              smbv3: true
         
     | 
| 
       44 
     | 
    
         
            -
            }
         
     | 
| 
       45 
     | 
    
         
            -
            OptionParser.new do |opts|
         
     | 
| 
       46 
     | 
    
         
            -
              opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
         
     | 
| 
       47 
     | 
    
         
            -
              opts.on("--share SHARE", "The share name (default: #{options[:share_name]})") do |share|
         
     | 
| 
       48 
     | 
    
         
            -
                options[:share_name] = share
         
     | 
| 
       49 
     | 
    
         
            -
              end
         
     | 
| 
       50 
     | 
    
         
            -
              opts.on("--[no-]anonymous", "Allow anonymous access (default: #{options[:allow_anonymous]})") do |allow_anonymous|
         
     | 
| 
       51 
     | 
    
         
            -
                options[:allow_anonymous] = allow_anonymous
         
     | 
| 
       52 
     | 
    
         
            -
              end
         
     | 
| 
       53 
     | 
    
         
            -
              opts.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{options[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
         
     | 
| 
       54 
     | 
    
         
            -
                options[:smbv1] = smbv1
         
     | 
| 
       55 
     | 
    
         
            -
              end
         
     | 
| 
       56 
     | 
    
         
            -
              opts.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{options[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
         
     | 
| 
       57 
     | 
    
         
            -
                options[:smbv2] = smbv2
         
     | 
| 
       58 
     | 
    
         
            -
              end
         
     | 
| 
       59 
     | 
    
         
            -
              opts.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{options[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
         
     | 
| 
       60 
     | 
    
         
            -
                options[:smbv3] = smbv3
         
     | 
| 
       61 
     | 
    
         
            -
              end
         
     | 
| 
       62 
     | 
    
         
            -
              opts.on("--username USERNAME", "The account's username (default: #{options[:username]})") do |username|
         
     | 
| 
       63 
     | 
    
         
            -
                if username.include?('\\')
         
     | 
| 
       64 
     | 
    
         
            -
                  options[:domain], options[:username] = username.split('\\', 2)
         
     | 
| 
       65 
     | 
    
         
            -
                else
         
     | 
| 
       66 
     | 
    
         
            -
                  options[:username] = username
         
     | 
| 
       67 
     | 
    
         
            -
                end
         
     | 
| 
       68 
     | 
    
         
            -
              end
         
     | 
| 
       69 
     | 
    
         
            -
              opts.on("--password PASSWORD", "The account's password (default: #{options[:password]})") do |password|
         
     | 
| 
       70 
     | 
    
         
            -
                options[:password] = password
         
     | 
| 
       71 
     | 
    
         
            -
              end
         
     | 
| 
       72 
     | 
    
         
            -
              opts.on("--virtual-content CONTENT", "The virtual share contents") do |virtual_content|
         
     | 
| 
      
 34 
     | 
    
         
            +
            options = RubySMB::Server::Cli.parse do |options, parser|
         
     | 
| 
      
 35 
     | 
    
         
            +
              parser.banner = "Usage: #{File.basename(__FILE__)} [options]"
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              parser.on("--virtual-content CONTENT", "The virtual share contents") do |virtual_content|
         
     | 
| 
       73 
38 
     | 
    
         
             
                options[:virtual_content] = virtual_content
         
     | 
| 
       74 
39 
     | 
    
         
             
              end
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              parser.on("--virtual-name NAME", "The virtual share file name") do |virtual_name|
         
     | 
| 
       76 
42 
     | 
    
         
             
                options[:virtual_name] = virtual_name
         
     | 
| 
       77 
43 
     | 
    
         
             
              end
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              parser.on("--virtual-type TYPE", "The virtual share type") do |virtual_type|
         
     | 
| 
       79 
46 
     | 
    
         
             
                options[:virtual_type] = virtual_type
         
     | 
| 
       80 
47 
     | 
    
         
             
              end
         
     | 
| 
       81 
     | 
    
         
            -
            end.parse!
         
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
            ntlm_provider = RubySMB::Gss::Provider::NTLM.new(allow_anonymous: options[:allow_anonymous])
         
     | 
| 
       84 
     | 
    
         
            -
            ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain])  # password can also be an NTLM hash
         
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
            server = RubySMB::Server.new(
         
     | 
| 
       87 
     | 
    
         
            -
              gss_provider: ntlm_provider,
         
     | 
| 
       88 
     | 
    
         
            -
              logger: :stdout
         
     | 
| 
       89 
     | 
    
         
            -
            )
         
     | 
| 
       90 
     | 
    
         
            -
            server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
         
     | 
| 
       91 
     | 
    
         
            -
            server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
         
     | 
| 
       92 
     | 
    
         
            -
            server.dialects.select! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
            if server.dialects.empty?
         
     | 
| 
       95 
     | 
    
         
            -
              puts "at least one version must be enabled"
         
     | 
| 
       96 
     | 
    
         
            -
              exit false
         
     | 
| 
       97 
48 
     | 
    
         
             
            end
         
     | 
| 
       98 
49 
     | 
    
         | 
| 
      
 50 
     | 
    
         
            +
            server = RubySMB::Server::Cli.build(options)
         
     | 
| 
       99 
51 
     | 
    
         
             
            virtual_disk = RubySMB::Server::Share::Provider::VirtualDisk.new(options[:share_name])
         
     | 
| 
       100 
52 
     | 
    
         | 
| 
       101 
53 
     | 
    
         
             
            # greeting is a static text file
         
     | 
| 
         @@ -135,9 +87,5 @@ elsif options[:virtual_content] || options[:virtual_name] || options[:virtual_ty 
     | 
|
| 
       135 
87 
     | 
    
         
             
            end
         
     | 
| 
       136 
88 
     | 
    
         | 
| 
       137 
89 
     | 
    
         
             
            server.add_share(virtual_disk)
         
     | 
| 
       138 
     | 
    
         
            -
            puts "server is running"
         
     | 
| 
       139 
     | 
    
         
            -
            server.run do |server_client|
         
     | 
| 
       140 
     | 
    
         
            -
              puts "received connection"
         
     | 
| 
       141 
     | 
    
         
            -
              true
         
     | 
| 
       142 
     | 
    
         
            -
            end
         
     | 
| 
       143 
90 
     | 
    
         | 
| 
      
 91 
     | 
    
         
            +
            RubySMB::Server::Cli.run(server)
         
     | 
    
        data/lib/ruby_smb/client.rb
    CHANGED
    
    | 
         @@ -332,7 +332,7 @@ module RubySMB 
     | 
|
| 
       332 
332 
     | 
    
         
             
                  # session setup response is received
         
     | 
| 
       333 
333 
     | 
    
         
             
                  @session_encrypt_data = always_encrypt
         
     | 
| 
       334 
334 
     | 
    
         | 
| 
       335 
     | 
    
         
            -
                  @ntlm_client =  
     | 
| 
      
 335 
     | 
    
         
            +
                  @ntlm_client = RubySMB::NTLM::Client.new(
         
     | 
| 
       336 
336 
     | 
    
         
             
                    @username,
         
     | 
| 
       337 
337 
     | 
    
         
             
                    @password,
         
     | 
| 
       338 
338 
     | 
    
         
             
                    workstation: @local_workstation,
         
     | 
| 
         @@ -404,7 +404,7 @@ module RubySMB 
     | 
|
| 
       404 
404 
     | 
    
         
             
                  @password          = pass.encode('utf-8') || ''.encode('utf-8')
         
     | 
| 
       405 
405 
     | 
    
         
             
                  @username          = user.encode('utf-8') || ''.encode('utf-8')
         
     | 
| 
       406 
406 
     | 
    
         | 
| 
       407 
     | 
    
         
            -
                  @ntlm_client =  
     | 
| 
      
 407 
     | 
    
         
            +
                  @ntlm_client = RubySMB::NTLM::Client.new(
         
     | 
| 
       408 
408 
     | 
    
         
             
                      @username,
         
     | 
| 
       409 
409 
     | 
    
         
             
                      @password,
         
     | 
| 
       410 
410 
     | 
    
         
             
                      workstation: @local_workstation,
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module RubySMB::NTLM
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Message
         
     | 
| 
      
 3 
     | 
    
         
            +
                def deflag
         
     | 
| 
      
 4 
     | 
    
         
            +
                  security_buffers.inject(head_size) do |cur, a|
         
     | 
| 
      
 5 
     | 
    
         
            +
                    a[1].offset = cur
         
     | 
| 
      
 6 
     | 
    
         
            +
                    cur += a[1].data_size
         
     | 
| 
      
 7 
     | 
    
         
            +
                    has_flag?(:UNICODE) ? cur + cur % 2 : cur
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def serialize
         
     | 
| 
      
 12 
     | 
    
         
            +
                  deflag
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @alist.map { |n, f| f.serialize }.join + security_buffers.map { |n, f| f.value + (has_flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '') }.join
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              class Client < Net::NTLM::Client
         
     | 
| 
      
 18 
     | 
    
         
            +
                class Session < Net::NTLM::Client::Session
         
     | 
| 
      
 19 
     | 
    
         
            +
                  def authenticate!
         
     | 
| 
      
 20 
     | 
    
         
            +
                    calculate_user_session_key!
         
     | 
| 
      
 21 
     | 
    
         
            +
                    type3_opts = {
         
     | 
| 
      
 22 
     | 
    
         
            +
                      :lm_response   => is_anonymous? ? "\x00".b : lmv2_resp,
         
     | 
| 
      
 23 
     | 
    
         
            +
                      :ntlm_response => is_anonymous? ? '' : ntlmv2_resp,
         
     | 
| 
      
 24 
     | 
    
         
            +
                      :domain        => domain,
         
     | 
| 
      
 25 
     | 
    
         
            +
                      :user          => username,
         
     | 
| 
      
 26 
     | 
    
         
            +
                      :workstation   => workstation,
         
     | 
| 
      
 27 
     | 
    
         
            +
                      :flag          => (challenge_message.flag & client.flags)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    }
         
     | 
| 
      
 29 
     | 
    
         
            +
                    t3 = Net::NTLM::Message::Type3.create type3_opts
         
     | 
| 
      
 30 
     | 
    
         
            +
                    t3.extend(Message)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    if negotiate_key_exchange?
         
     | 
| 
      
 32 
     | 
    
         
            +
                      t3.enable(:session_key)
         
     | 
| 
      
 33 
     | 
    
         
            +
                      rc4 = OpenSSL::Cipher.new("rc4")
         
     | 
| 
      
 34 
     | 
    
         
            +
                      rc4.encrypt
         
     | 
| 
      
 35 
     | 
    
         
            +
                      rc4.key = user_session_key
         
     | 
| 
      
 36 
     | 
    
         
            +
                      sk = rc4.update exported_session_key
         
     | 
| 
      
 37 
     | 
    
         
            +
                      sk << rc4.final
         
     | 
| 
      
 38 
     | 
    
         
            +
                      t3.session_key = sk
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                    t3
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def is_anonymous?
         
     | 
| 
      
 44 
     | 
    
         
            +
                    username == '' && password == ''
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  private
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def use_oem_strings?
         
     | 
| 
      
 50 
     | 
    
         
            +
                    # @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/99d90ff4-957f-4c8a-80e4-5bfe5a9a9832
         
     | 
| 
      
 51 
     | 
    
         
            +
                    !challenge_message.has_flag?(:UNICODE) && challenge_message.has_flag?(:OEM)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def calculate_user_session_key!
         
     | 
| 
      
 55 
     | 
    
         
            +
                    if is_anonymous?
         
     | 
| 
      
 56 
     | 
    
         
            +
                      # see MS-NLMP section 3.4
         
     | 
| 
      
 57 
     | 
    
         
            +
                      @user_session_key = "\x00".b * 16
         
     | 
| 
      
 58 
     | 
    
         
            +
                    else
         
     | 
| 
      
 59 
     | 
    
         
            +
                      @user_session_key = OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmv2_hash, nt_proof_str)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def init_context(resp = nil, channel_binding = nil)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  if resp.nil?
         
     | 
| 
      
 66 
     | 
    
         
            +
                    @session = nil
         
     | 
| 
      
 67 
     | 
    
         
            +
                    type1_message
         
     | 
| 
      
 68 
     | 
    
         
            +
                  else
         
     | 
| 
      
 69 
     | 
    
         
            +
                    @session = Client::Session.new(self, Net::NTLM::Message.decode64(resp), channel_binding)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    @session.authenticate!
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/ruby_smb/ntlm.rb
    CHANGED
    
    
| 
         @@ -0,0 +1,121 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RubySMB
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Server
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Cli
         
     | 
| 
      
 6 
     | 
    
         
            +
                  DEFAULT_OPTIONS = {
         
     | 
| 
      
 7 
     | 
    
         
            +
                    allow_anonymous: true,
         
     | 
| 
      
 8 
     | 
    
         
            +
                    allow_guests: false,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    domain: nil,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    username: 'RubySMB',
         
     | 
| 
      
 11 
     | 
    
         
            +
                    password: 'password',
         
     | 
| 
      
 12 
     | 
    
         
            +
                    share_name: 'home',
         
     | 
| 
      
 13 
     | 
    
         
            +
                    smbv1: true,
         
     | 
| 
      
 14 
     | 
    
         
            +
                    smbv2: true,
         
     | 
| 
      
 15 
     | 
    
         
            +
                    smbv3: true
         
     | 
| 
      
 16 
     | 
    
         
            +
                  }.freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # Parse options from the command line. The resulting option hash is suitable for passing to {build}.
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @yield [options, parser] A block that can be used to update the built option parser.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @yieldparam [Hash<Symbol => >] options The options hash that should be assigned to.
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @yieldparam [OptionParser] parser The built option parser.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @param [Hash] defaults Default option values to use.
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [Hash<Symbol => >] The options hash.
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   * :allow_anonymous [Boolean] Whether or not to allow anonymous authentication.
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   * :allow_guest [Boolean] Whether or not to allow guest authentication.
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #   * :username [String] The username of the account to add for authentication.
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #   * :password [String] The password of the account to add for authentication.
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #   * :domain [String] The domain of the account to add for authentication.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #   * :smbv1 [Boolean] Whether or not to enable SMBv1 dialects.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #   * :smbv2 [Boolean] Whether or not to enable SMBv2 dialects.
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #   * :smbv3 [Boolean] Whether or not to enable SMBv3 dialects.
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #   * :share_name [String] The name of the share to add.
         
     | 
| 
      
 34 
     | 
    
         
            +
                  def self.parse(defaults: {}, &block)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    defaults = DEFAULT_OPTIONS.merge(defaults)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    options = defaults.clone
         
     | 
| 
      
 37 
     | 
    
         
            +
                    OptionParser.new do |parser|
         
     | 
| 
      
 38 
     | 
    
         
            +
                      parser.on("--share-name SHARE_NAME", "The share name (default: #{defaults[:share_name]})") do |share|
         
     | 
| 
      
 39 
     | 
    
         
            +
                        options[:share_name] = share
         
     | 
| 
      
 40 
     | 
    
         
            +
                      end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                      parser.on("--[no-]anonymous", "Allow anonymous access (default: #{defaults[:allow_anonymous]})") do |allow_anonymous|
         
     | 
| 
      
 43 
     | 
    
         
            +
                        options[:allow_anonymous] = allow_anonymous
         
     | 
| 
      
 44 
     | 
    
         
            +
                      end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                      parser.on("--[no-]guests", "Allow guest accounts (default: #{defaults[:allow_guests]})") do |allow_guests|
         
     | 
| 
      
 47 
     | 
    
         
            +
                        options[:allow_guests] = allow_guests
         
     | 
| 
      
 48 
     | 
    
         
            +
                      end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                      parser.on("--[no-]smbv1", "Enable or disable SMBv1 (default: #{defaults[:smbv1] ? 'Enabled' : 'Disabled'})") do |smbv1|
         
     | 
| 
      
 51 
     | 
    
         
            +
                        options[:smbv1] = smbv1
         
     | 
| 
      
 52 
     | 
    
         
            +
                      end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                      parser.on("--[no-]smbv2", "Enable or disable SMBv2 (default: #{defaults[:smbv2] ? 'Enabled' : 'Disabled'})") do |smbv2|
         
     | 
| 
      
 55 
     | 
    
         
            +
                        options[:smbv2] = smbv2
         
     | 
| 
      
 56 
     | 
    
         
            +
                      end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                      parser.on("--[no-]smbv3", "Enable or disable SMBv3 (default: #{defaults[:smbv3] ? 'Enabled' : 'Disabled'})") do |smbv3|
         
     | 
| 
      
 59 
     | 
    
         
            +
                        options[:smbv3] = smbv3
         
     | 
| 
      
 60 
     | 
    
         
            +
                      end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                      parser.on("--username USERNAME", "The account's username (default: #{defaults[:username]})") do |username|
         
     | 
| 
      
 63 
     | 
    
         
            +
                        if username.include?('\\')
         
     | 
| 
      
 64 
     | 
    
         
            +
                          options[:domain], options[:username] = username.split('\\', 2)
         
     | 
| 
      
 65 
     | 
    
         
            +
                        else
         
     | 
| 
      
 66 
     | 
    
         
            +
                          options[:username] = username
         
     | 
| 
      
 67 
     | 
    
         
            +
                        end
         
     | 
| 
      
 68 
     | 
    
         
            +
                      end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                      parser.on("--password PASSWORD", "The account's password (default: #{defaults[:password]})") do |password|
         
     | 
| 
      
 71 
     | 
    
         
            +
                        options[:password] = password
         
     | 
| 
      
 72 
     | 
    
         
            +
                      end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                      block.call(options, parser) if block_given?
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end.parse!
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    options
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # Build a server instance from the specified options. The NTLM provider will be used for authentication.
         
     | 
| 
      
 81 
     | 
    
         
            +
                  #
         
     | 
| 
      
 82 
     | 
    
         
            +
                  # @param [Hash] options the options to use while building the server. See the return value of {parse} for a
         
     | 
| 
      
 83 
     | 
    
         
            +
                  #   comprehensive list of keys.
         
     | 
| 
      
 84 
     | 
    
         
            +
                  def self.build(options)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    ntlm_provider = RubySMB::Gss::Provider::NTLM.new(
         
     | 
| 
      
 86 
     | 
    
         
            +
                      allow_anonymous: options[:allow_anonymous],
         
     | 
| 
      
 87 
     | 
    
         
            +
                      allow_guests: options[:allow_guests]
         
     | 
| 
      
 88 
     | 
    
         
            +
                    )
         
     | 
| 
      
 89 
     | 
    
         
            +
                    ntlm_provider.put_account(options[:username], options[:password], domain: options[:domain])  # password can also be an NTLM hash
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    server = RubySMB::Server.new(
         
     | 
| 
      
 92 
     | 
    
         
            +
                      gss_provider: ntlm_provider,
         
     | 
| 
      
 93 
     | 
    
         
            +
                      logger: :stdout
         
     | 
| 
      
 94 
     | 
    
         
            +
                    )
         
     | 
| 
      
 95 
     | 
    
         
            +
                    server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB1 } unless options[:smbv1]
         
     | 
| 
      
 96 
     | 
    
         
            +
                    server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB2 } unless options[:smbv2]
         
     | 
| 
      
 97 
     | 
    
         
            +
                    server.dialects.filter! { |dialect| RubySMB::Dialect[dialect].family != RubySMB::Dialect::FAMILY_SMB3 } unless options[:smbv3]
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    server
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                  # Run the server forever. At least 1 SMB dialect must be enabled.
         
     | 
| 
      
 103 
     | 
    
         
            +
                  #
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # @param [RubySMB::Server] server The server instance to run.
         
     | 
| 
      
 105 
     | 
    
         
            +
                  # @param [#puts] out The stream to write standard output messages to.
         
     | 
| 
      
 106 
     | 
    
         
            +
                  # @param [#puts] err The stream to write error messages to.
         
     | 
| 
      
 107 
     | 
    
         
            +
                  def self.run(server, out: $stdout, err: $stderr)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    if server.dialects.empty?
         
     | 
| 
      
 109 
     | 
    
         
            +
                      err.puts 'at least one version must be enabled'
         
     | 
| 
      
 110 
     | 
    
         
            +
                      return
         
     | 
| 
      
 111 
     | 
    
         
            +
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    out.puts 'server is running'
         
     | 
| 
      
 114 
     | 
    
         
            +
                    server.run do |server_client|
         
     | 
| 
      
 115 
     | 
    
         
            +
                      out.puts 'received connection'
         
     | 
| 
      
 116 
     | 
    
         
            +
                      true
         
     | 
| 
      
 117 
     | 
    
         
            +
                    end
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
              end
         
     | 
| 
      
 121 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/ruby_smb/server.rb
    CHANGED
    
    | 
         @@ -6,6 +6,7 @@ module RubySMB 
     | 
|
| 
       6 
6 
     | 
    
         
             
              # Currently, the server only supports negotiating and authenticating requests. No other server functionality is
         
     | 
| 
       7 
7 
     | 
    
         
             
              # available at this time. The negotiating and authentication is supported for SMB versions 1 through 3.1.1.
         
     | 
| 
       8 
8 
     | 
    
         
             
              class Server
         
     | 
| 
      
 9 
     | 
    
         
            +
                require 'ruby_smb/server/cli'
         
     | 
| 
       9 
10 
     | 
    
         
             
                require 'ruby_smb/server/server_client'
         
     | 
| 
       10 
11 
     | 
    
         
             
                require 'ruby_smb/server/session'
         
     | 
| 
       11 
12 
     | 
    
         
             
                require 'ruby_smb/server/share'
         
     | 
    
        data/lib/ruby_smb/version.rb
    CHANGED
    
    
| 
         @@ -0,0 +1,114 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe RubySMB::NTLM::Client::Session do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:message) { Net::NTLM::Message.decode64(%Q{
         
     | 
| 
      
 5 
     | 
    
         
            +
                TlRMTVNTUAACAAAADAAMADgAAAA1goni+fNfw+cInOgAAAAAAAAAAJoAmgBE
         
     | 
| 
      
 6 
     | 
    
         
            +
                AAAACgBjRQAAAA9NAFMARgBMAEEAQgACAAwATQBTAEYATABBAEIAAQAeAFcA
         
     | 
| 
      
 7 
     | 
    
         
            +
                SQBOAC0AMwBNAFMAUAA4AEsAMgBMAEMARwBDAAQAGABtAHMAZgBsAGEAYgAu
         
     | 
| 
      
 8 
     | 
    
         
            +
                AGwAbwBjAGEAbAADADgAVwBJAE4ALQAzAE0AUwBQADgASwAyAEwAQwBHAEMA
         
     | 
| 
      
 9 
     | 
    
         
            +
                LgBtAHMAZgBsAGEAYgAuAGwAbwBjAGEAbAAHAAgAS6UAWjxl2AEAAAAA
         
     | 
| 
      
 10 
     | 
    
         
            +
              }) }
         
     | 
| 
      
 11 
     | 
    
         
            +
              subject(:client) { RubySMB::NTLM::Client.new('rubysmb', 'rubysmb', flags: RubySMB::NTLM::DEFAULT_CLIENT_FLAGS) }
         
     | 
| 
      
 12 
     | 
    
         
            +
              subject(:session) { described_class.new(client, message) }
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              describe '#authenticate!' do
         
     | 
| 
      
 15 
     | 
    
         
            +
                it 'calculates the user session key' do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  expect(session).to receive(:calculate_user_session_key!).and_call_original
         
     | 
| 
      
 17 
     | 
    
         
            +
                  session.authenticate!
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                it 'checks if it is anonymous' do
         
     | 
| 
      
 21 
     | 
    
         
            +
                  expect(session).to receive(:is_anonymous?).at_least(1).times.and_call_original
         
     | 
| 
      
 22 
     | 
    
         
            +
                  session.authenticate!
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                it 'returns a Type3 message' do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  expect(session.authenticate!).to be_a Net::NTLM::Message::Type3
         
     | 
| 
      
 27 
     | 
    
         
            +
                  expect(session.authenticate!).to be_a RubySMB::NTLM::Message
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                context 'when it is anonymous' do
         
     | 
| 
      
 31 
     | 
    
         
            +
                  before(:each) { allow(session).to receive(:is_anonymous?).and_return(true) }
         
     | 
| 
      
 32 
     | 
    
         
            +
                  after(:each) { session.authenticate! }
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  it 'uses the correct lm response' do
         
     | 
| 
      
 35 
     | 
    
         
            +
                    expect(session).to_not receive(:lmv2_resp)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
         
     | 
| 
      
 37 
     | 
    
         
            +
                      expect(params).to include :lm_response
         
     | 
| 
      
 38 
     | 
    
         
            +
                      expect(params[:lm_response]).to eq "\x00".b
         
     | 
| 
      
 39 
     | 
    
         
            +
                      method.call(params)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  it 'uses the correct ntlm response' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                    expect(session).to_not receive(:ntlmv2_resp)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
         
     | 
| 
      
 46 
     | 
    
         
            +
                      expect(params).to include :ntlm_response
         
     | 
| 
      
 47 
     | 
    
         
            +
                      expect(params[:ntlm_response]).to eq ''
         
     | 
| 
      
 48 
     | 
    
         
            +
                      method.call(params)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                context 'when it is not anonymous' do
         
     | 
| 
      
 54 
     | 
    
         
            +
                  before(:each) { allow(session).to receive(:is_anonymous?).and_return(false) }
         
     | 
| 
      
 55 
     | 
    
         
            +
                  after(:each) { session.authenticate! }
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  it 'uses the correct lm response' do
         
     | 
| 
      
 58 
     | 
    
         
            +
                    expect(session).to receive(:lmv2_resp).and_call_original
         
     | 
| 
      
 59 
     | 
    
         
            +
                    expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
         
     | 
| 
      
 60 
     | 
    
         
            +
                      expect(params).to include :lm_response
         
     | 
| 
      
 61 
     | 
    
         
            +
                      expect(params[:lm_response].length).to be > 16
         
     | 
| 
      
 62 
     | 
    
         
            +
                      method.call(params)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  it 'uses the correct ntlm response' do
         
     | 
| 
      
 67 
     | 
    
         
            +
                    expect(session).to receive(:ntlmv2_resp).and_call_original
         
     | 
| 
      
 68 
     | 
    
         
            +
                    expect(Net::NTLM::Message::Type3).to receive(:create).and_wrap_original do |method, params|
         
     | 
| 
      
 69 
     | 
    
         
            +
                      expect(params).to include :ntlm_response
         
     | 
| 
      
 70 
     | 
    
         
            +
                      expect(params[:ntlm_response].length).to be > 16
         
     | 
| 
      
 71 
     | 
    
         
            +
                      method.call(params)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
              end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
              describe '#calculate_user_session_key!' do
         
     | 
| 
      
 78 
     | 
    
         
            +
                it 'returns an all zero key when it is anonymous' do
         
     | 
| 
      
 79 
     | 
    
         
            +
                  expect(session).to receive(:is_anonymous?).and_return(true)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  expect(session.send(:calculate_user_session_key!)).to eq "\x00".b * 16
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                it 'returns a session key' do
         
     | 
| 
      
 84 
     | 
    
         
            +
                  expect(session).to receive(:is_anonymous?).and_return(false)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  expect(session.send(:calculate_user_session_key!)).to_not eq "\x00".b * 16
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
              describe '#is_anonymous?' do
         
     | 
| 
      
 90 
     | 
    
         
            +
                it 'returns false when the username is not blank' do
         
     | 
| 
      
 91 
     | 
    
         
            +
                  allow(session).to receive(:username).and_return('username')
         
     | 
| 
      
 92 
     | 
    
         
            +
                  allow(session).to receive(:password).and_return('')
         
     | 
| 
      
 93 
     | 
    
         
            +
                  expect(session.is_anonymous?).to be false
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                it 'returns false when the password is not blank' do
         
     | 
| 
      
 97 
     | 
    
         
            +
                  allow(session).to receive(:username).and_return('')
         
     | 
| 
      
 98 
     | 
    
         
            +
                  allow(session).to receive(:password).and_return('password')
         
     | 
| 
      
 99 
     | 
    
         
            +
                  expect(session.is_anonymous?).to be false
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                it 'returns false when the username is not blank and the password is not blank' do
         
     | 
| 
      
 103 
     | 
    
         
            +
                  allow(session).to receive(:username).and_return('username')
         
     | 
| 
      
 104 
     | 
    
         
            +
                  allow(session).to receive(:password).and_return('password')
         
     | 
| 
      
 105 
     | 
    
         
            +
                  expect(session.is_anonymous?).to be false
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                it 'returns true when the username is blank and the password is blank' do
         
     | 
| 
      
 109 
     | 
    
         
            +
                  allow(session).to receive(:username).and_return('')
         
     | 
| 
      
 110 
     | 
    
         
            +
                  allow(session).to receive(:password).and_return('')
         
     | 
| 
      
 111 
     | 
    
         
            +
                  expect(session.is_anonymous?).to be true
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
              end
         
     | 
| 
      
 114 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe RubySMB::NTLM::Client do
         
     | 
| 
      
 4 
     | 
    
         
            +
              subject(:client) { described_class.new('rubysmb', 'rubysmb', flags: RubySMB::NTLM::DEFAULT_CLIENT_FLAGS) }
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              describe '#init_context' do
         
     | 
| 
      
 7 
     | 
    
         
            +
                context 'when a response is provided' do
         
     | 
| 
      
 8 
     | 
    
         
            +
                  let(:resp) { %Q{
         
     | 
| 
      
 9 
     | 
    
         
            +
                    TlRMTVNTUAACAAAADAAMADgAAAA1goni+fNfw+cInOgAAAAAAAAAAJoAmgBE
         
     | 
| 
      
 10 
     | 
    
         
            +
                    AAAACgBjRQAAAA9NAFMARgBMAEEAQgACAAwATQBTAEYATABBAEIAAQAeAFcA
         
     | 
| 
      
 11 
     | 
    
         
            +
                    SQBOAC0AMwBNAFMAUAA4AEsAMgBMAEMARwBDAAQAGABtAHMAZgBsAGEAYgAu
         
     | 
| 
      
 12 
     | 
    
         
            +
                    AGwAbwBjAGEAbAADADgAVwBJAE4ALQAzAE0AUwBQADgASwAyAEwAQwBHAEMA
         
     | 
| 
      
 13 
     | 
    
         
            +
                    LgBtAHMAZgBsAGEAYgAuAGwAbwBjAGEAbAAHAAgAS6UAWjxl2AEAAAAA
         
     | 
| 
      
 14 
     | 
    
         
            +
                  } }
         
     | 
| 
      
 15 
     | 
    
         
            +
                  it 'returns a Type3 message' do
         
     | 
| 
      
 16 
     | 
    
         
            +
                    expect(client.init_context(resp)).to be_a Net::NTLM::Message::Type3
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  it 'creates a new session object' do
         
     | 
| 
      
 20 
     | 
    
         
            +
                    expect(RubySMB::NTLM::Client::Session).to receive(:new).and_call_original
         
     | 
| 
      
 21 
     | 
    
         
            +
                    client.init_context(resp)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                context 'when a response is not provided' do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  it 'returns a Type1 message' do
         
     | 
| 
      
 27 
     | 
    
         
            +
                    expect(client.init_context).to be_a Net::NTLM::Message::Type1
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  it 'does not create a new session object' do
         
     | 
| 
      
 31 
     | 
    
         
            +
                    expect(RubySMB::NTLM::Client::Session).to_not receive(:new)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    client.init_context
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
    
        data.tar.gz.sig
    CHANGED
    
    | 
         Binary file 
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: ruby_smb
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 3.1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 3.1.3
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Metasploit Hackers
         
     | 
| 
         @@ -97,7 +97,7 @@ cert_chain: 
     | 
|
| 
       97 
97 
     | 
    
         
             
              EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
         
     | 
| 
       98 
98 
     | 
    
         
             
              9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
         
     | 
| 
       99 
99 
     | 
    
         
             
              -----END CERTIFICATE-----
         
     | 
| 
       100 
     | 
    
         
            -
            date: 2022-05- 
     | 
| 
      
 100 
     | 
    
         
            +
            date: 2022-05-24 00:00:00.000000000 Z
         
     | 
| 
       101 
101 
     | 
    
         
             
            dependencies:
         
     | 
| 
       102 
102 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       103 
103 
     | 
    
         
             
              name: redcarpet
         
     | 
| 
         @@ -473,7 +473,9 @@ files: 
     | 
|
| 
       473 
473 
     | 
    
         
             
            - lib/ruby_smb/nbss/session_header.rb
         
     | 
| 
       474 
474 
     | 
    
         
             
            - lib/ruby_smb/nbss/session_request.rb
         
     | 
| 
       475 
475 
     | 
    
         
             
            - lib/ruby_smb/ntlm.rb
         
     | 
| 
      
 476 
     | 
    
         
            +
            - lib/ruby_smb/ntlm/client.rb
         
     | 
| 
       476 
477 
     | 
    
         
             
            - lib/ruby_smb/server.rb
         
     | 
| 
      
 478 
     | 
    
         
            +
            - lib/ruby_smb/server/cli.rb
         
     | 
| 
       477 
479 
     | 
    
         
             
            - lib/ruby_smb/server/server_client.rb
         
     | 
| 
       478 
480 
     | 
    
         
             
            - lib/ruby_smb/server/server_client/encryption.rb
         
     | 
| 
       479 
481 
     | 
    
         
             
            - lib/ruby_smb/server/server_client/negotiation.rb
         
     | 
| 
         @@ -802,6 +804,8 @@ files: 
     | 
|
| 
       802 
804 
     | 
    
         
             
            - spec/lib/ruby_smb/nbss/netbios_name_spec.rb
         
     | 
| 
       803 
805 
     | 
    
         
             
            - spec/lib/ruby_smb/nbss/session_header_spec.rb
         
     | 
| 
       804 
806 
     | 
    
         
             
            - spec/lib/ruby_smb/nbss/session_request_spec.rb
         
     | 
| 
      
 807 
     | 
    
         
            +
            - spec/lib/ruby_smb/ntlm/client/session_spec.rb
         
     | 
| 
      
 808 
     | 
    
         
            +
            - spec/lib/ruby_smb/ntlm/client_spec.rb
         
     | 
| 
       805 
809 
     | 
    
         
             
            - spec/lib/ruby_smb/server/server_client_spec.rb
         
     | 
| 
       806 
810 
     | 
    
         
             
            - spec/lib/ruby_smb/server/session_spec.rb
         
     | 
| 
       807 
811 
     | 
    
         
             
            - spec/lib/ruby_smb/server/share/provider/disk_spec.rb
         
     | 
| 
         @@ -1112,6 +1116,8 @@ test_files: 
     | 
|
| 
       1112 
1116 
     | 
    
         
             
            - spec/lib/ruby_smb/nbss/netbios_name_spec.rb
         
     | 
| 
       1113 
1117 
     | 
    
         
             
            - spec/lib/ruby_smb/nbss/session_header_spec.rb
         
     | 
| 
       1114 
1118 
     | 
    
         
             
            - spec/lib/ruby_smb/nbss/session_request_spec.rb
         
     | 
| 
      
 1119 
     | 
    
         
            +
            - spec/lib/ruby_smb/ntlm/client/session_spec.rb
         
     | 
| 
      
 1120 
     | 
    
         
            +
            - spec/lib/ruby_smb/ntlm/client_spec.rb
         
     | 
| 
       1115 
1121 
     | 
    
         
             
            - spec/lib/ruby_smb/server/server_client_spec.rb
         
     | 
| 
       1116 
1122 
     | 
    
         
             
            - spec/lib/ruby_smb/server/session_spec.rb
         
     | 
| 
       1117 
1123 
     | 
    
         
             
            - spec/lib/ruby_smb/server/share/provider/disk_spec.rb
         
     | 
    
        metadata.gz.sig
    CHANGED
    
    | 
         Binary file 
     |