ruby_smb 3.0.6 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
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)