ruby_smb 3.0.6 → 3.1.2

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.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/examples/file_server.rb +8 -1
  4. data/examples/virtual_file_server.rb +143 -0
  5. data/lib/ruby_smb/client/encryption.rb +16 -4
  6. data/lib/ruby_smb/client/negotiation.rb +10 -8
  7. data/lib/ruby_smb/fscc/file_information/file_access_information.rb +15 -0
  8. data/lib/ruby_smb/fscc/file_information/file_alignment_information.rb +45 -0
  9. data/lib/ruby_smb/fscc/file_information/file_all_information.rb +23 -0
  10. data/lib/ruby_smb/fscc/file_information/file_basic_information.rb +20 -0
  11. data/lib/ruby_smb/fscc/file_information/file_both_directory_information.rb +3 -3
  12. data/lib/ruby_smb/fscc/file_information/file_directory_information.rb +3 -3
  13. data/lib/ruby_smb/fscc/file_information/file_ea_information.rb +1 -0
  14. data/lib/ruby_smb/fscc/file_information/file_full_directory_information.rb +3 -3
  15. data/lib/ruby_smb/fscc/file_information/file_id_both_directory_information.rb +3 -3
  16. data/lib/ruby_smb/fscc/file_information/file_id_full_directory_information.rb +3 -3
  17. data/lib/ruby_smb/fscc/file_information/file_internal_information.rb +15 -0
  18. data/lib/ruby_smb/fscc/file_information/file_mode_information.rb +29 -0
  19. data/lib/ruby_smb/fscc/file_information/file_name_information.rb +16 -0
  20. data/lib/ruby_smb/fscc/file_information/file_names_information.rb +1 -1
  21. data/lib/ruby_smb/fscc/file_information/file_normalized_name_information.rb +16 -0
  22. data/lib/ruby_smb/fscc/file_information/file_position_information.rb +15 -0
  23. data/lib/ruby_smb/fscc/file_information/file_rename_information.rb +1 -1
  24. data/lib/ruby_smb/fscc/file_information/file_standard_information.rb +20 -0
  25. data/lib/ruby_smb/fscc/file_information/file_stream_information.rb +3 -0
  26. data/lib/ruby_smb/fscc/file_information.rb +43 -6
  27. data/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information.rb +1 -0
  28. data/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information.rb +1 -0
  29. data/lib/ruby_smb/fscc/file_system_information.rb +4 -0
  30. data/lib/ruby_smb/gss/provider/ntlm.rb +20 -3
  31. data/lib/ruby_smb/gss/provider.rb +10 -1
  32. data/lib/ruby_smb/server/server_client/encryption.rb +66 -0
  33. data/lib/ruby_smb/server/server_client/negotiation.rb +14 -3
  34. data/lib/ruby_smb/server/server_client/session_setup.rb +21 -4
  35. data/lib/ruby_smb/server/server_client/share_io.rb +17 -0
  36. data/lib/ruby_smb/server/server_client/tree_connect.rb +40 -3
  37. data/lib/ruby_smb/server/server_client.rb +156 -38
  38. data/lib/ruby_smb/server/session.rb +5 -1
  39. data/lib/ruby_smb/server/share/provider/disk/file_system.rb +28 -0
  40. data/lib/ruby_smb/server/share/provider/disk/processor/close.rb +46 -0
  41. data/lib/ruby_smb/server/share/provider/disk/processor/create.rb +143 -0
  42. data/lib/ruby_smb/server/share/provider/disk/processor/query.rb +359 -0
  43. data/lib/ruby_smb/server/share/provider/disk/processor/read.rb +70 -0
  44. data/lib/ruby_smb/server/share/provider/disk/processor.rb +223 -0
  45. data/lib/ruby_smb/server/share/provider/disk.rb +12 -418
  46. data/lib/ruby_smb/server/share/provider/pipe.rb +2 -2
  47. data/lib/ruby_smb/server/share/provider/processor.rb +16 -0
  48. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_file.rb +85 -0
  49. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname.rb +196 -0
  50. data/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat.rb +175 -0
  51. data/lib/ruby_smb/server/share/provider/virtual_disk.rb +116 -0
  52. data/lib/ruby_smb/server/share/provider.rb +1 -0
  53. data/lib/ruby_smb/server.rb +13 -3
  54. data/lib/ruby_smb/signing.rb +18 -4
  55. data/lib/ruby_smb/smb1/commands.rb +1 -0
  56. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +11 -1
  57. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +1 -1
  58. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +5 -4
  59. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +12 -4
  60. data/lib/ruby_smb/smb1/packet/trans2/data_block.rb +9 -1
  61. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +52 -51
  62. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +37 -37
  63. data/lib/ruby_smb/smb1/packet/trans2/find_information_level/find_file_both_directory_info.rb +48 -0
  64. data/lib/ruby_smb/smb1/packet/trans2/find_information_level.rb +28 -15
  65. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +51 -51
  66. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +36 -36
  67. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +40 -39
  68. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +40 -40
  69. data/lib/ruby_smb/smb1/packet/trans2/query_file_information_request.rb +60 -0
  70. data/lib/ruby_smb/smb1/packet/trans2/query_file_information_response.rb +59 -0
  71. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level/query_fs_attribute_info.rb +31 -0
  72. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_level.rb +40 -0
  73. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request.rb +46 -0
  74. data/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response.rb +59 -0
  75. data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_basic_info.rb +23 -0
  76. data/lib/ruby_smb/smb1/packet/trans2/query_information_level/query_file_standard_info.rb +22 -0
  77. data/lib/ruby_smb/smb1/packet/trans2/query_information_level.rb +62 -0
  78. data/lib/ruby_smb/smb1/packet/trans2/query_path_information_request.rb +65 -0
  79. data/lib/ruby_smb/smb1/packet/trans2/query_path_information_response.rb +59 -0
  80. data/lib/ruby_smb/smb1/packet/trans2/request.rb +24 -8
  81. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +4 -4
  82. data/lib/ruby_smb/smb1/packet/trans2/response.rb +29 -20
  83. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +42 -42
  84. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +23 -23
  85. data/lib/ruby_smb/smb1/packet/trans2/subcommands.rb +23 -5
  86. data/lib/ruby_smb/smb1/packet/trans2.rb +4 -0
  87. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +4 -1
  88. data/lib/ruby_smb/smb2/negotiate_context.rb +10 -1
  89. data/lib/ruby_smb/smb2/packet/transform_header.rb +7 -7
  90. data/lib/ruby_smb/smb2/tree.rb +1 -0
  91. data/lib/ruby_smb/smb2.rb +1 -0
  92. data/lib/ruby_smb/version.rb +1 -1
  93. data/spec/lib/ruby_smb/client_spec.rb +31 -8
  94. data/spec/lib/ruby_smb/fscc/file_information/file_access_information_spec.rb +21 -0
  95. data/spec/lib/ruby_smb/fscc/file_information/file_alignment_information_spec.rb +21 -0
  96. data/spec/lib/ruby_smb/fscc/file_information/file_all_information_spec.rb +61 -0
  97. data/spec/lib/ruby_smb/fscc/file_information/file_basic_information_spec.rb +41 -0
  98. data/spec/lib/ruby_smb/fscc/file_information/file_both_directory_information_spec.rb +59 -10
  99. data/spec/lib/ruby_smb/fscc/file_information/file_directory_information_spec.rb +30 -12
  100. data/spec/lib/ruby_smb/fscc/file_information/file_ea_information_spec.rb +21 -0
  101. data/spec/lib/ruby_smb/fscc/file_information/file_full_directory_information_spec.rb +30 -12
  102. data/spec/lib/ruby_smb/fscc/file_information/file_id_both_directory_information_spec.rb +63 -10
  103. data/spec/lib/ruby_smb/fscc/file_information/file_id_full_directory_information_spec.rb +30 -12
  104. data/spec/lib/ruby_smb/fscc/file_information/file_internal_information_spec.rb +21 -0
  105. data/spec/lib/ruby_smb/fscc/file_information/file_mode_information_spec.rb +21 -0
  106. data/spec/lib/ruby_smb/fscc/file_information/file_name_information_spec.rb +44 -0
  107. data/spec/lib/ruby_smb/fscc/file_information/file_names_information_spec.rb +30 -12
  108. data/spec/lib/ruby_smb/fscc/file_information/file_network_open_information_spec.rb +51 -0
  109. data/spec/lib/ruby_smb/fscc/file_information/file_normalized_name_information_spec.rb +44 -0
  110. data/spec/lib/ruby_smb/fscc/file_information/file_position_information_spec.rb +21 -0
  111. data/spec/lib/ruby_smb/fscc/file_information/file_rename_information_spec.rb +1 -1
  112. data/spec/lib/ruby_smb/fscc/file_information/file_standard_information_spec.rb +41 -0
  113. data/spec/lib/ruby_smb/fscc/file_information/file_stream_information_spec.rb +51 -0
  114. data/spec/lib/ruby_smb/fscc/file_information_spec.rb +14 -0
  115. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_attribute_information_spec.rb +46 -0
  116. data/spec/lib/ruby_smb/fscc/file_system_information/file_fs_volume_information_spec.rb +51 -0
  117. data/spec/lib/ruby_smb/fscc/file_system_information_spec.rb +14 -0
  118. data/spec/lib/ruby_smb/server/server_client_spec.rb +15 -0
  119. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_pathname_spec.rb +581 -0
  120. data/spec/lib/ruby_smb/server/share/provider/virtual_disk/virtual_stat_spec.rb +207 -0
  121. data/spec/lib/ruby_smb/server/share/provider/virtual_disk_spec.rb +122 -0
  122. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_request_spec.rb +2 -2
  123. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +36 -2
  124. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_request_spec.rb +2 -2
  125. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +35 -1
  126. data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_request_spec.rb +74 -0
  127. data/spec/lib/ruby_smb/smb1/packet/trans2/query_file_information_response_spec.rb +96 -0
  128. data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_request_spec.rb +62 -0
  129. data/spec/lib/ruby_smb/smb1/packet/trans2/query_fs_information_response_spec.rb +88 -0
  130. data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_request_spec.rb +79 -0
  131. data/spec/lib/ruby_smb/smb1/packet/trans2/query_path_information_response_spec.rb +96 -0
  132. data/spec/lib/ruby_smb/smb1/packet/trans2/request_spec.rb +2 -2
  133. data/spec/lib/ruby_smb/smb1/packet/trans2/response_spec.rb +3 -3
  134. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_request_spec.rb +3 -2
  135. data/spec/lib/ruby_smb/smb1/packet/trans2/set_file_information_response_spec.rb +7 -2
  136. data/spec/lib/ruby_smb/smb1/tree_spec.rb +3 -3
  137. data/spec/lib/ruby_smb/smb2/packet/transform_header_spec.rb +2 -2
  138. data.tar.gz.sig +0 -0
  139. metadata +88 -2
  140. metadata.gz.sig +0 -0
@@ -4,7 +4,10 @@ module RubySMB
4
4
  # The FileStreamInformation
5
5
  # [2.4.43 FileStreamInformation](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/f8762be6-3ab9-411e-a7d6-5cc68f70c78d)
6
6
  class FileStreamInformation < BinData::Record
7
+ CLASS_LEVEL = Fscc::FileInformation::FILE_STREAM_INFORMATION
8
+
7
9
  endian :little
10
+
8
11
  uint32 :next_entry_offset, label: 'Next Entry Offset'
9
12
  uint32 :stream_name_length, label: 'Stream Name Length', initial_value: -> { stream_name.do_num_bytes }
10
13
  int64 :stream_size, label: 'Stream Size'
@@ -17,10 +17,26 @@ module RubySMB
17
17
  # contents of a directory.
18
18
  FILE_BOTH_DIRECTORY_INFORMATION = 0x03
19
19
 
20
+ # Information class used to query or set file information.
21
+ FILE_BASIC_INFORMATION = 0x04
22
+
23
+ # Information class is used to query file information.
24
+ FILE_STANDARD_INFORMATION = 0x05
25
+
26
+ # Information class used to query for the file system's 64-bit file ID.
27
+ FILE_INTERNAL_INFORMATION = 0x06
28
+
20
29
  # Information class used to query for the size of the extended attributes
21
30
  # (EA) for a file.
22
31
  FILE_EA_INFORMATION = 0x07
23
32
 
33
+ # Information class used to query the access rights of a file that were
34
+ # granted when the file was opened.
35
+ FILE_ACCESS_INFORMATION = 0x08
36
+
37
+ # Information class is used locally to query the name of a file.
38
+ FILE_NAME_INFORMATION = 0x09
39
+
24
40
  # Information class used to rename a file.
25
41
  FILE_RENAME_INFORMATION = 0x0A
26
42
 
@@ -31,6 +47,17 @@ module RubySMB
31
47
  # Information class used to mark a file for deletion.
32
48
  FILE_DISPOSITION_INFORMATION = 0x0D
33
49
 
50
+ # Information class used to query or set the position of the file pointer
51
+ # within a file.
52
+ FILE_POSITION_INFORMATION = 0x0E
53
+
54
+ # Information class used to query or set the mode of the file.
55
+ FILE_MODE_INFORMATION = 0x10
56
+
57
+ # Information class used to query the buffer alignment required by the
58
+ # underlying device.
59
+ FILE_ALIGNMENT_INFORMATION = 0x11
60
+
34
61
  # Information class used to enumerate the data streams of a file or a
35
62
  # directory.
36
63
  FILE_STREAM_INFORMATION = 0x16
@@ -57,6 +84,10 @@ module RubySMB
57
84
  FILE_NORMALIZED_NAME_INFORMATION = 0x30
58
85
 
59
86
 
87
+ # Information class is used to query a collection of file information
88
+ # structures.
89
+ FILE_ALL_INFORMATION = 0x12
90
+
60
91
  # These Information Classes can be used by SMB1 using the pass-through
61
92
  # Information Levels when available on the server (CAP_INFOLEVEL_PASSTHRU
62
93
  # capability flag in an SMB_COM_NEGOTIATE server response). The constant
@@ -65,12 +96,8 @@ module RubySMB
65
96
  # [2.2.2.3.5 Pass-through Information Level Codes](https://msdn.microsoft.com/en-us/library/ff470158.aspx)
66
97
  SMB_INFO_PASSTHROUGH = 0x03e8
67
98
 
68
- # The FILE_NAME_INFORMATION type as defined in
69
- # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/20406fb1-605f-4629-ba9a-c67ee25f23d2
70
- class FileNameInformation < BinData::Record
71
- endian :little
72
- uint32 :file_name_length, label: 'File Name Length', initial_value: -> { file_name.do_num_bytes }
73
- string16 :file_name, label: 'File Name', read_length: -> { file_name_length }
99
+ def self.name(value)
100
+ constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
74
101
  end
75
102
 
76
103
  require 'ruby_smb/fscc/file_information/file_directory_information'
@@ -79,11 +106,21 @@ module RubySMB
79
106
  require 'ruby_smb/fscc/file_information/file_id_full_directory_information'
80
107
  require 'ruby_smb/fscc/file_information/file_both_directory_information'
81
108
  require 'ruby_smb/fscc/file_information/file_id_both_directory_information'
109
+ require 'ruby_smb/fscc/file_information/file_name_information'
82
110
  require 'ruby_smb/fscc/file_information/file_names_information'
111
+ require 'ruby_smb/fscc/file_information/file_normalized_name_information'
83
112
  require 'ruby_smb/fscc/file_information/file_rename_information'
84
113
  require 'ruby_smb/fscc/file_information/file_network_open_information'
85
114
  require 'ruby_smb/fscc/file_information/file_ea_information'
86
115
  require 'ruby_smb/fscc/file_information/file_stream_information'
116
+ require 'ruby_smb/fscc/file_information/file_basic_information'
117
+ require 'ruby_smb/fscc/file_information/file_standard_information'
118
+ require 'ruby_smb/fscc/file_information/file_internal_information'
119
+ require 'ruby_smb/fscc/file_information/file_access_information'
120
+ require 'ruby_smb/fscc/file_information/file_position_information'
121
+ require 'ruby_smb/fscc/file_information/file_mode_information'
122
+ require 'ruby_smb/fscc/file_information/file_alignment_information'
123
+ require 'ruby_smb/fscc/file_information/file_all_information'
87
124
  end
88
125
  end
89
126
  end
@@ -7,6 +7,7 @@ module RubySMB
7
7
  CLASS_LEVEL = FileSystemInformation::FILE_FS_ATTRIBUTE_INFORMATION
8
8
 
9
9
  endian :little
10
+
10
11
  struct :file_system_attributes, label: 'File System Attributes' do
11
12
  bit1 :file_supports_reparse_points, label: 'FS Supports Reparse Points'
12
13
  bit1 :file_supports_sparse_files, label: 'FS Supports Sparse Files'
@@ -7,6 +7,7 @@ module RubySMB
7
7
  CLASS_LEVEL = FileSystemInformation::FILE_FS_VOLUME_INFORMATION
8
8
 
9
9
  endian :little
10
+
10
11
  file_time :volume_creation_time, label: 'Volume Creation Time'
11
12
  uint32 :volume_serial_number, label: 'Volume Serial Number'
12
13
  uint32 :volume_label_length, label: 'Volume Label Length', initial_value: -> { volume_label.do_num_bytes }
@@ -15,6 +15,10 @@ module RubySMB
15
15
  FILE_FS_VOLUME_FLAGS_INFORMATION = 10
16
16
  FILE_FS_SECTOR_SIZE_INFORMATION = 11
17
17
 
18
+ def self.name(value)
19
+ constants.select { |c| c.upcase == c }.find { |c| const_get(c) == value }
20
+ end
21
+
18
22
  require 'ruby_smb/fscc/file_system_information/file_fs_attribute_information'
19
23
  require 'ruby_smb/fscc/file_system_information/file_fs_volume_information'
20
24
  end
@@ -78,6 +78,10 @@ module RubySMB
78
78
  msg.flag |= NTLM::NEGOTIATE_FLAGS.fetch(flag)
79
79
  end
80
80
 
81
+ if type1_msg.flag & NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY] == NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
82
+ msg.flag |= NTLM::NEGOTIATE_FLAGS[:EXTENDED_SECURITY]
83
+ end
84
+
81
85
  @server_challenge = @provider.generate_server_challenge
82
86
  msg.challenge = @server_challenge.unpack1('Q<') # 64-bit unsigned, little endian (uint64_t)
83
87
  target_info = Net::NTLM::TargetInfo.new('')
@@ -109,6 +113,7 @@ module RubySMB
109
113
  def process_ntlm_type3(type3_msg)
110
114
  if type3_msg.user == '' && type3_msg.domain == ''
111
115
  if @provider.allow_anonymous
116
+ @session_key = "\x00".b * 16 # see MS-NLMP section 3.4
112
117
  return WindowsError::NTStatus::STATUS_SUCCESS
113
118
  end
114
119
 
@@ -122,6 +127,12 @@ module RubySMB
122
127
  domain: type3_msg.domain
123
128
  )
124
129
  if account.nil?
130
+ if @provider.allow_guests
131
+ logger.info("NTLM authentication request succeeded for #{dbg_string} (guest)")
132
+ @session_key = "\x00".b * 16 # see MS-NLMP section 3.4
133
+ return WindowsError::NTStatus::STATUS_SUCCESS
134
+ end
135
+
125
136
  logger.info("NTLM authentication request failed for #{dbg_string} (no account)")
126
137
  return WindowsError::NTStatus::STATUS_LOGON_FAILURE
127
138
  end
@@ -229,25 +240,31 @@ module RubySMB
229
240
  domain: type3_msg.domain
230
241
  )
231
242
  if account.nil?
232
- if @provider.allow_anonymous
243
+ if type3_msg.user == ''
244
+ is_guest = false
233
245
  identity = IDENTITY_ANONYMOUS
246
+ else
247
+ is_guest = true
248
+ identity = Account.new(type3_msg.user.encode(''.encoding), '', type3_msg.domain.encode(''.encoding)).to_s
234
249
  end
235
250
  else
251
+ is_guest = false
236
252
  identity = account.to_s
237
253
  end
238
254
  end
239
255
 
240
- Result.new(buffer, nt_status, identity)
256
+ Result.new(buffer, nt_status, identity, is_guest)
241
257
  end
242
258
  end
243
259
 
244
260
  # @param [Boolean] allow_anonymous whether or not to allow anonymous authentication attempts
245
261
  # @param [String] default_domain the default domain to use for authentication, unless specified 'WORKGROUP' will
246
262
  # be used
247
- def initialize(allow_anonymous: false, default_domain: 'WORKGROUP')
263
+ def initialize(allow_anonymous: false, allow_guests: false, default_domain: 'WORKGROUP')
248
264
  raise ArgumentError, 'Must specify a default domain' unless default_domain
249
265
 
250
266
  @allow_anonymous = allow_anonymous
267
+ @allow_guests = allow_guests
251
268
  @default_domain = default_domain
252
269
  @accounts = []
253
270
  @generate_server_challenge = -> { SecureRandom.bytes(8) }
@@ -7,7 +7,11 @@ module RubySMB
7
7
  # A special constant implying that the authenticated user is anonymous.
8
8
  IDENTITY_ANONYMOUS = :anonymous
9
9
  # The result of a processed GSS request.
10
- Result = Struct.new(:buffer, :nt_status, :identity)
10
+ Result = Struct.new(:buffer, :nt_status, :identity, :is_guest) do
11
+ def is_anonymous
12
+ identity == Gss::Provider::IDENTITY_ANONYMOUS
13
+ end
14
+ end
11
15
 
12
16
  #
13
17
  # The base class for a GSS authentication provider. This class defines a common interface and is not usable as a
@@ -26,6 +30,11 @@ module RubySMB
26
30
  # Whether or not anonymous authentication attempts should be permitted.
27
31
  #
28
32
  attr_accessor :allow_anonymous
33
+
34
+ #
35
+ # Whether or not unknown users should be allowed to authenticate as guests.
36
+ #
37
+ attr_accessor :allow_guests
29
38
  end
30
39
  end
31
40
  end
@@ -0,0 +1,66 @@
1
+ module RubySMB
2
+ class Server
3
+ class ServerClient
4
+ # Contains the methods for handling encryption / decryption
5
+ module Encryption
6
+ def smb3_encrypt(data, session)
7
+ encryption_algorithm = SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[@cipher_id]
8
+ raise RubySMB::Error::EncryptionError.new('The encryption algorithm has not been set') if encryption_algorithm.nil?
9
+
10
+ key_bit_len = OpenSSL::Cipher.new(encryption_algorithm).key_len * 8
11
+
12
+ case @dialect
13
+ when '0x0300', '0x0302'
14
+ server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
15
+ session.key,
16
+ "SMB2AESCCM\x00",
17
+ "ServerOut\x00",
18
+ length: key_bit_len
19
+ )
20
+ when '0x0311'
21
+ server_encryption_key = RubySMB::Crypto::KDF.counter_mode(
22
+ session.key,
23
+ "SMBS2CCipherKey\x00",
24
+ @preauth_integrity_hash_value,
25
+ length: key_bit_len
26
+ )
27
+ else
28
+ raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 decryption')
29
+ end
30
+
31
+ th = RubySMB::SMB2::Packet::TransformHeader.new(flags: 1, session_id: session.id)
32
+ th.encrypt(data, server_encryption_key, algorithm: encryption_algorithm)
33
+ th
34
+ end
35
+
36
+ def smb3_decrypt(encrypted_request, session)
37
+ encryption_algorithm = SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP[@cipher_id]
38
+ raise RubySMB::Error::EncryptionError.new('The encryption algorithm has not been set') if encryption_algorithm.nil?
39
+
40
+ key_bit_len = OpenSSL::Cipher.new(encryption_algorithm).key_len * 8
41
+
42
+ case @dialect
43
+ when '0x0300', '0x0302'
44
+ client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
45
+ session.key,
46
+ "SMB2AESCCM\x00",
47
+ "ServerIn \x00",
48
+ length: key_bit_len
49
+ )
50
+ when '0x0311'
51
+ client_encryption_key = RubySMB::Crypto::KDF.counter_mode(
52
+ session.key,
53
+ "SMBC2SCipherKey\x00",
54
+ @preauth_integrity_hash_value,
55
+ length: key_bit_len
56
+ )
57
+ else
58
+ raise RubySMB::Error::EncryptionError.new('Dialect is incompatible with SMBv3 encryption')
59
+ end
60
+
61
+ encrypted_request.decrypt(client_encryption_key, algorithm: encryption_algorithm)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -119,14 +119,25 @@ module RubySMB
119
119
  )
120
120
 
121
121
  nc = request.find_negotiate_context(SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES)
122
- cipher = nc&.data&.ciphers&.first
123
- cipher = 0 unless SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP.include? cipher
122
+ ciphers = nc&.data&.ciphers
123
+ if ciphers
124
+ cipher = ciphers.find { |cipher| SMB2::EncryptionCapabilities::ENCRYPTION_ALGORITHM_MAP.include?(cipher) }
125
+ @cipher_id = cipher unless cipher.nil?
126
+ end
127
+
124
128
  contexts << SMB2::NegotiateContext.new(
125
129
  context_type: SMB2::NegotiateContext::SMB2_ENCRYPTION_CAPABILITIES,
126
130
  data: {
127
- ciphers: [ cipher ]
131
+ ciphers: [ @cipher_id ]
128
132
  }
129
133
  )
134
+ elsif dialect == '0x0300' || dialect == '0x0302'
135
+ if request.capabilities.encryption == 1
136
+ response.capabilities.encryption = 1
137
+ @cipher_id = SMB2::EncryptionCapabilities::AES_128_CCM
138
+ else
139
+ response.capabilities = 0
140
+ end
130
141
  end
131
142
 
132
143
  # the order in which the response is built is important to ensure it is valid
@@ -2,7 +2,7 @@ module RubySMB
2
2
  class Server
3
3
  class ServerClient
4
4
  module SessionSetup
5
- def do_session_setup_smb1(request, session)
5
+ def do_session_setup_andx_smb1(request, session)
6
6
  session_id = request.smb_header.uid
7
7
  if session_id == 0
8
8
  session_id = rand(1..0x10000)
@@ -41,6 +41,17 @@ module RubySMB
41
41
  response
42
42
  end
43
43
 
44
+ alias :do_session_setup_smb1 :do_session_setup_andx_smb1
45
+
46
+ def do_logoff_andx_smb1(request, session)
47
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/00fc0299-496c-4330-9089-67358994f272
48
+ @session_table.delete(request.smb_header.uid)
49
+ session.logoff!
50
+
51
+ response = SMB1::Packet::LogoffResponse.new
52
+ response
53
+ end
54
+
44
55
  def do_session_setup_smb2(request, session)
45
56
  session_id = request.smb2_header.session_id
46
57
  if session_id == 0
@@ -67,11 +78,16 @@ module RubySMB
67
78
 
68
79
  update_preauth_hash(request) if @dialect == '0x0311'
69
80
  if gss_result.nt_status == WindowsError::NTStatus::STATUS_SUCCESS
70
- response.smb2_header.credits = 32
71
81
  session.state = :valid
72
82
  session.user_id = gss_result.identity
83
+ session.is_guest = !!gss_result.is_guest
73
84
  session.key = @gss_authenticator.session_key
74
- session.signing_required = request.security_mode.signing_required == 1
85
+ session.signing_required = request.security_mode.signing_required == 1 || (!session.is_guest && !session.is_anonymous)
86
+
87
+ response.smb2_header.credits = 32
88
+ @cipher_id = 0 if session.is_anonymous || session.is_guest # disable encryption for anonymous users and guest users which have a null session key
89
+ response.session_flags.encrypt_data = 1 unless @cipher_id == 0
90
+ response.session_flags.guest = session.is_guest
75
91
  elsif gss_result.nt_status == WindowsError::NTStatus::STATUS_MORE_PROCESSING_REQUIRED && @dialect == '0x0311'
76
92
  update_preauth_hash(response)
77
93
  end
@@ -80,7 +96,8 @@ module RubySMB
80
96
  end
81
97
 
82
98
  def do_logoff_smb2(request, session)
83
- session = @session_table.delete(session.id)
99
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/a6fbc502-75a5-42ef-a88c-c67b44817850
100
+ @session_table.delete(session.id)
84
101
  session.logoff!
85
102
 
86
103
  response = SMB2::Packet::LogoffResponse.new
@@ -2,6 +2,23 @@ module RubySMB
2
2
  class Server
3
3
  class ServerClient
4
4
  module ShareIO
5
+ def proxy_share_io_smb1(request, session)
6
+ share_processor = session.tree_connect_table[request.smb_header.tid]
7
+ if share_processor.nil?
8
+ response = SMB1::Packet::EmptyPacket.new
9
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_NAME_DELETED
10
+ return response
11
+ end
12
+
13
+ logger.debug("Received #{SMB1::Commands.name(request.smb_header.command)} request for share: #{share_processor.provider.name}")
14
+ share_processor.send(__callee__, request)
15
+ end
16
+
17
+ alias :do_close_smb1 :proxy_share_io_smb1
18
+ alias :do_nt_create_andx_smb1 :proxy_share_io_smb1
19
+ alias :do_read_andx_smb1 :proxy_share_io_smb1
20
+ alias :do_transactions2_smb1 :proxy_share_io_smb1
21
+
5
22
  def proxy_share_io_smb2(request, session)
6
23
  # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/9a639360-87be-4d49-a1dd-4c6be0c020bd
7
24
  share_processor = session.tree_connect_table[request.smb2_header.tree_id]
@@ -3,6 +3,43 @@ module RubySMB
3
3
  class ServerClient
4
4
  MAX_TREE_CONNECTIONS = 1000
5
5
  module TreeConnect
6
+ def do_tree_connect_smb1(request, session)
7
+ # see: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/b062f3e3-1b65-4a9a-854a-0ee432499d8f
8
+ response = RubySMB::SMB1::Packet::TreeConnectResponse.new
9
+
10
+ share_name = request.data_block.path.encode('UTF-8').split('\\', 4).last
11
+ share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
12
+ if share_provider.nil?
13
+ logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
14
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_OBJECT_PATH_NOT_FOUND
15
+ return response
16
+ end
17
+ logger.debug("Received TREE_CONNECT request for share: #{share_name}")
18
+
19
+ tree_id = rand(1..0xfffe)
20
+ tree_id = rand(1..0xfffe) while session.tree_connect_table.include?(tree_id)
21
+
22
+ response.smb_header.tid = tree_id
23
+ session.tree_connect_table[tree_id] = share_processor = share_provider.new_processor(self, session)
24
+ response.parameter_block.access_rights = share_processor.maximal_access
25
+
26
+ response
27
+ end
28
+
29
+ def do_tree_disconnect_smb1(request, session)
30
+ share_processor = session.tree_connect_table.delete(request.smb_header.tid)
31
+ if share_processor.nil?
32
+ response = RubySMB::SMB1::Packet::EmptyPacket.new
33
+ response.smb_header.nt_status = WindowsError::NTStatus::STATUS_NETWORK_NAME_DELETED
34
+ return response
35
+ end
36
+
37
+ logger.debug("Received TREE_DISCONNECT request for share: #{share_processor.provider.name}")
38
+ share_processor.disconnect!
39
+ response = RubySMB::SMB1::Packet::TreeDisconnectResponse.new
40
+ response
41
+ end
42
+
6
43
  def do_tree_connect_smb2(request, session)
7
44
  response = RubySMB::SMB2::Packet::TreeConnectResponse.new
8
45
  response.smb2_header.credits = 1
@@ -13,7 +50,7 @@ module RubySMB
13
50
  end
14
51
 
15
52
  share_name = request.path.encode('UTF-8').split('\\', 4).last
16
- share_provider = @server.shares[share_name]
53
+ share_provider = @server.shares.transform_keys(&:downcase)[share_name.downcase]
17
54
 
18
55
  if share_provider.nil?
19
56
  logger.warn("Received TREE_CONNECT request for non-existent share: #{share_name}")
@@ -31,8 +68,8 @@ module RubySMB
31
68
  RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT
32
69
  end
33
70
 
34
- tree_id = rand(0xffffffff)
35
- tree_id = rand(0xffffffff) while session.tree_connect_table.include?(tree_id)
71
+ tree_id = rand(1..0xfffffffe)
72
+ tree_id = rand(1..0xfffffffe) while session.tree_connect_table.include?(tree_id)
36
73
 
37
74
  response.smb2_header.tree_id = tree_id
38
75
  session.tree_connect_table[tree_id] = share_processor = share_provider.new_processor(self, session)