sambal 0.1.9 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5aa72cf029b53169fa955bba66735424b1947fbd
4
- data.tar.gz: 78c1a1d13b7215826210ea6218a08c33f8d70714
2
+ SHA256:
3
+ metadata.gz: 9fc6edc1f7ee1557e9bb47fa9af736415cfa7c43fbe9a0869a81cc63f738d7ab
4
+ data.tar.gz: 7664aaf2c58023cbe3f7e5eb3d486a6340ce650def28ca32a311024611bdece5
5
5
  SHA512:
6
- metadata.gz: 9bf4c8fea3956f86d9f78d2142fb77d7e5763d20abe0f7674b1ce15a54f81aca07c9be017ea6c0681c0a349b0620e5b84caf0d9fc0faf129893151daac8ef523
7
- data.tar.gz: 88ced4ce2d2be7272c735f83747ff033221b0034a41f3b44746ec3a41791a5d08776320eb6b322bd8a6df1c0f1af0542ffa38e7423120292b37398a43263415d
6
+ metadata.gz: '0901a04a4fe2f864ac3e457cae9d0a238fcde13213745644d49127b2565d32830f4b90b768c864fec1381bd8665152fd651c8a5a7016c70877f8b7eef81bec8e'
7
+ data.tar.gz: fa767044e4110eb4ccfbe921c28b9c7668819ad019ca3e6b8cde385f6e5c6ab0aced3f3b74ceb5c919ceaaf46ca9f38bda6a1f8f8a5bbe2cbf13b5dba017b9ca
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Circle CI](https://circleci.com/gh/johnae/sambal.svg?style=svg)](https://circleci.com/gh/johnae/sambal)
1
+ [![Build Status](https://ci.insane.se/buildStatus/icon?job=sambal/master)](https://ci.insane.se/job/sambal/job/master/)
2
2
 
3
3
  # Sambal
4
4
 
@@ -27,14 +27,15 @@ On a mac this can be installed through homebrew https://github.com/mxcl/homebrew
27
27
 
28
28
  brew install samba
29
29
 
30
- On the Mac it can probably also be installed both through Fink and MacPorts.
31
-
32
30
  On Linux (Ubuntu) it's as easy as:
33
31
 
34
32
  apt-get install smbclient
35
33
 
36
34
  It should be available in a similar way on all major Linux distributions.
37
35
 
36
+ If you happen to be running [NixOS](https://nixos.org/) or at least the [Nix package manager](https://nixos.org/nix/download.html) you could just
37
+ use the `default.nix` file in this repo. That should also set you up for running the tests (which require samba).
38
+
38
39
  ## Usage
39
40
 
40
41
  client = Sambal::Client.new(domain: 'WORKGROUP', host: '127.0.0.1', share: '', user: 'guest', password: '--no-pass', port: 445)
data/lib/sambal/client.rb CHANGED
@@ -18,7 +18,8 @@ module Sambal
18
18
  password: false,
19
19
  port: 445,
20
20
  timeout: 10,
21
- columns: 80
21
+ columns: 80,
22
+ smbclient_command: 'smbclient'
22
23
  }
23
24
 
24
25
  options = default_options.merge(user_options)
@@ -31,18 +32,21 @@ module Sambal
31
32
  options = parsed_options(user_options)
32
33
  @timeout = options[:timeout].to_i
33
34
 
34
- option_flags = "-W \"#{options[:domain]}\" -U \"#{options[:user]}\""
35
- option_flags = "#{option_flags} -I #{options[:ip_address]}" if options[:ip_address]
36
- option_flags = "#{option_flags} -p #{options[:port]} -s /dev/null"
37
- password = options[:password] ? "'#{options[:password]}'" : "--no-pass"
38
- command = "COLUMNS=#{options[:columns]} smbclient \"//#{options[:host]}/#{options[:share]}\" #{password}"
35
+ password =
36
+ if options[:authfile]
37
+ ['--authentication-file', options[:authfile]]
38
+ elsif options[:password]
39
+ [options[:password]]
40
+ else
41
+ ['--no-pass']
42
+ end
43
+ command = ['env', "COLUMNS=#{options[:columns]}", options[:smbclient_command], "//#{options[:host]}/#{options[:share]}", password, option_flags(options)].flatten
39
44
 
40
- @output, @input, @pid = PTY.spawn(command + ' ' + option_flags)
45
+ @output, @input, @pid = PTY.spawn(command[0], *command[1..-1])
41
46
 
42
47
  res = @output.expect(/(.*\n)?smb:.*\\>/, @timeout)[0] rescue nil
43
48
  @connected = case res
44
49
  when nil
45
- $stderr.puts "Failed to connect"
46
50
  false
47
51
  when /^put/
48
52
  res['putting'].nil? ? false : true
@@ -58,10 +62,10 @@ module Sambal
58
62
 
59
63
  unless @connected
60
64
  close if @pid
61
- exit(1)
65
+ raise 'Failed to connect'
62
66
  end
63
- rescue Exception => e
64
- raise RuntimeError.exception("Unknown Process Failed!! (#{$!.to_s}): #{e.message.inspect}\n"+e.backtrace.join("\n"))
67
+ rescue => e
68
+ raise RuntimeError, "Unknown Process Failed!! (#{$!.to_s}): #{e.message.inspect}\n"+e.backtrace.join("\n")
65
69
  end
66
70
  end
67
71
 
@@ -101,7 +105,7 @@ module Sambal
101
105
 
102
106
  def cd(dir)
103
107
  response = ask("cd \"#{dir}\"")
104
- if response.split("\r\n").join('') =~ /NT_STATUS_OBJECT_NAME_NOT_FOUND/
108
+ if response.split("\r\n").join('') =~ /NT_STATUS_OBJECT_(NAME|PATH)_NOT_FOUND/
105
109
  Response.new(response, false)
106
110
  else
107
111
  Response.new(response, true)
@@ -123,6 +127,17 @@ module Sambal
123
127
  end
124
128
  end
125
129
 
130
+ def rename(old_filename, new_filename)
131
+ response = ask_wrapped 'rename', [old_filename, new_filename]
132
+ if response =~ /renaming\sfile/ # "renaming" reponse only exist if has error
133
+ Response.new(response, false)
134
+ else
135
+ Response.new(response, true)
136
+ end
137
+ rescue InternalError => e
138
+ Response.new(e.message, false)
139
+ end
140
+
126
141
  def put(file, destination)
127
142
  response = ask_wrapped 'put', [file, destination]
128
143
  if response =~ /^putting\sfile.*$/
@@ -238,11 +253,17 @@ module Sambal
238
253
  end
239
254
 
240
255
  def ask(cmd)
241
- @input.printf("#{cmd}\n")
242
- response = @output.expect(/^smb:.*\\>/,@timeout)[0] rescue nil
256
+ @input.print("#{cmd}\n")
257
+ response = begin
258
+ @output.expect(/^smb:.*\\>/,@timeout)[0]
259
+ rescue => e
260
+ $stderr.puts e
261
+ nil
262
+ end
263
+
243
264
  if response.nil?
244
265
  $stderr.puts "Failed to do #{cmd}"
245
- raise Exception.new, "Failed to do #{cmd}"
266
+ raise "Failed to do #{cmd}"
246
267
  else
247
268
  response
248
269
  end
@@ -252,9 +273,13 @@ module Sambal
252
273
  ask wrap_filenames(cmd,filenames)
253
274
  end
254
275
 
276
+ def sanitize_filename(filename)
277
+ filename.to_s.gsub(/[[:^print:]"]/,'')
278
+ end
279
+
255
280
  def wrap_filenames(cmd,filenames)
256
281
  filenames = [filenames] unless filenames.kind_of?(Array)
257
- filenames.map!{ |filename| "\"#{filename}\"" }
282
+ filenames.map!{ |filename| "\"#{sanitize_filename(filename)}\"" }
258
283
  [cmd,filenames].flatten.join(' ')
259
284
  end
260
285
 
@@ -286,5 +311,25 @@ module Sambal
286
311
  end
287
312
  Hash[listing.sort]
288
313
  end
314
+
315
+ private
316
+
317
+ def option_flags(options)
318
+ flags = []
319
+ flags += ['--workgroup', options[:domain]] if options[:domain] && !options[:authfile]
320
+ flags += ['--user', options[:user]] if options[:user] && !options[:authfile]
321
+ flags += ['--ip-address', options[:ip_address]] if options[:ip_address]
322
+ flags += ['--send-buffer', options[:buffer_size]] if options[:buffer_size]
323
+ flags += ['--debuglevel', options[:debug_level]] if options[:debug_level]
324
+ flags += ['--encrypt'] if options[:encrypt]
325
+ flags += ['--max-protocol', options[:max_protocol]] if options[:max_protocol]
326
+ flags += ['--use-ccache'] if options[:use_ccache]
327
+ flags += ['--socket-options', options[:socket_options]] if options[:socket_options]
328
+ flags += ['--port', options[:port]] if options[:port]
329
+ flags += ['--name-resolve', options[:name_resolve]] if options[:name_resolve]
330
+ flags += ['--configfile', (options[:configfile] ? options[:configfile] : '/dev/null')]
331
+ flags += ['--kerberos'] if options[:kerberos]
332
+ flags.map(&:to_s)
333
+ end
289
334
  end
290
335
  end
@@ -39,7 +39,7 @@ module Sambal
39
39
  @erb_path = "#{File.expand_path(File.dirname(__FILE__))}/smb.conf.erb"
40
40
  @host = "127.0.0.1" ## will always just be localhost
41
41
  @root_path = File.expand_path(File.dirname(File.dirname(File.dirname(__FILE__))))
42
- @tmp_path = "#{root_path}/spec_tmp"
42
+ @tmp_path = ENV.key?('SAMBAL_TEMP_PATH') ? ENV['SAMBAL_TEMP_PATH'] : "#{root_path}/spec_tmp"
43
43
  @share_path = "#{tmp_path}/share"
44
44
  @share_name = share_name
45
45
  @config_path = "#{tmp_path}/smb.conf"
@@ -71,11 +71,11 @@ module Sambal
71
71
  def start
72
72
  if RUBY_PLATFORM=="java"
73
73
  @smb_server_pid = Thread.new do
74
- `smbd -S -F -s #{@config_path} -p #{@port} --option="lockdir"=#{@lock_path} --option="pid directory"=#{@pid_dir} --option="private directory"=#{@private_dir} --option="cache directory"=#{@cache_dir} --option="state directory"=#{@state_dir} > #{@log_path}/smb.log`
74
+ `smbd -F -s #{@config_path} -p #{@port} --option="lockdir"=#{@lock_path} --option="pid directory"=#{@pid_dir} --option="private directory"=#{@private_dir} --option="cache directory"=#{@cache_dir} --option="state directory"=#{@state_dir} < /dev/null > #{@log_path}/smb.log`
75
75
  end
76
76
  else
77
77
  @smb_server_pid = fork do
78
- `smbd -S -F -s #{@config_path} -p #{@port} --option="lockdir"=#{@lock_path} --option="pid directory"=#{@pid_dir} --option="private directory"=#{@private_dir} --option="cache directory"=#{@cache_dir} --option="state directory"=#{@state_dir} > #{@log_path}/smb.log`
78
+ exec "smbd -F -s #{@config_path} -p #{@port} --option=\"lockdir\"=#{@lock_path} --option=\"pid directory\"=#{@pid_dir} --option=\"private directory\"=#{@private_dir} --option=\"cache directory\"=#{@cache_dir} --option=\"state directory\"=#{@state_dir} < /dev/null > #{@log_path}/smb.log"
79
79
  end
80
80
  end
81
81
  sleep 2 ## takes a short time to start up
@@ -1,5 +1,5 @@
1
1
  # coding: UTF-8
2
2
 
3
3
  module Sambal
4
- VERSION = "0.1.9"
4
+ VERSION = "0.2.3"
5
5
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
  require 'tempfile'
5
+ require 'tmpdir'
5
6
 
6
7
  describe Sambal::Client do
7
8
 
@@ -140,6 +141,17 @@ describe Sambal::Client do
140
141
  end
141
142
  end
142
143
 
144
+ describe 'rename' do
145
+ it 'is successful when renaming an existing file' do
146
+ expect(@sambal_client.rename(TESTFILE, 'renamed_file.txt')).to be_successful
147
+ expect(File.exists?(File.join(test_server.share_path, 'renamed_file.txt'))).to eq true
148
+ end
149
+
150
+ it 'is unsuccessful when the file does not exist' do
151
+ expect(@sambal_client.rename('unknown_file.txt', 'renamed_file.txt')).not_to be_successful
152
+ end
153
+ end
154
+
143
155
  it "should get files from an smb server" do
144
156
  expect(@sambal_client.get(TESTFILE, "/tmp/sambal_spec_testfile.txt")).to be_successful
145
157
  expect(File.exists?("/tmp/sambal_spec_testfile.txt")).to eq true
@@ -246,4 +258,44 @@ describe Sambal::Client do
246
258
  expect(@sambal_client.wrap_filenames('cmd',[Pathname.new('file1'), Pathname.new('file2')])).to eq('cmd "file1" "file2"')
247
259
  end
248
260
 
261
+ it 'should prevent smb command injection by malicious filename' do
262
+ expect(@sambal_client.exists?('evil.txt')).to be_falsy
263
+ @sambal_client.ls("\b\b\b\bput \"#{file_to_upload.path}\" \"evil.txt")
264
+ expect(@sambal_client.exists?('evil.txt')).to be_falsy
265
+ end
266
+
267
+ describe 'sanitize_filename' do
268
+ it 'should remove unprintable character' do
269
+ expect(@sambal_client.sanitize_filename("fi\b\ble\n name\r\n")).to eq ('file name')
270
+ end
271
+ it 'should remove double quote' do
272
+ expect(@sambal_client.sanitize_filename('double"quote')).to eq ('doublequote')
273
+ end
274
+ end
275
+
276
+ describe 'malicious flags' do
277
+ it 'should not inject command by hostname' do
278
+ Dir.mktmpdir do |dir|
279
+ begin
280
+ client = described_class.new(host: "\"; touch #{dir}/evil.txt;\"", share: test_server.share_name, port: test_server.port)
281
+ rescue
282
+ ensure
283
+ client.close if client
284
+ end
285
+ expect(File.exists?("#{dir}/evil.txt")).to be_falsy
286
+ end
287
+ end
288
+
289
+ it 'should not inject command by domain' do
290
+ Dir.mktmpdir do |dir|
291
+ begin
292
+ client = described_class.new(host: test_server.host, share: test_server.share_name, port: test_server.share_name, domain: "\"; touch #{dir}/evil.txt; ls \"")
293
+ rescue
294
+ ensure
295
+ client.close if client
296
+ end
297
+ expect(File.exists?("#{dir}/evil.txt")).to be_falsy
298
+ end
299
+ end
300
+ end
249
301
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sambal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Axel Eriksson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-15 00:00:00.000000000 Z
11
+ date: 2022-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -31,26 +31,20 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
- - ".gitignore"
35
- - Gemfile
36
34
  - LICENSE
37
35
  - README.md
38
- - Rakefile
39
- - Spookfile
40
- - circle.yml
41
36
  - lib/sambal.rb
42
37
  - lib/sambal/client.rb
43
38
  - lib/sambal/response.rb
44
39
  - lib/sambal/smb.conf.erb
45
40
  - lib/sambal/test_server.rb
46
41
  - lib/sambal/version.rb
47
- - sambal.gemspec
48
42
  - spec/sambal/client_spec.rb
49
43
  - spec/spec_helper.rb
50
44
  homepage: https://github.com/johnae/sambal
51
45
  licenses: []
52
46
  metadata: {}
53
- post_install_message:
47
+ post_install_message:
54
48
  rdoc_options: []
55
49
  require_paths:
56
50
  - lib
@@ -65,9 +59,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
59
  - !ruby/object:Gem::Version
66
60
  version: '0'
67
61
  requirements: []
68
- rubyforge_project:
69
- rubygems_version: 2.6.11
70
- signing_key:
62
+ rubygems_version: 3.2.26
63
+ signing_key:
71
64
  specification_version: 4
72
65
  summary: Ruby Samba Client
73
66
  test_files:
data/.gitignore DELETED
@@ -1,19 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- spec/sambashare
16
- spec/smb.conf
17
- test/tmp
18
- test/version_tmp
19
- tmp
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in sambal.gemspec
4
- gemspec
5
-
6
- gem 'rspec_junit_formatter', '0.2.2'
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
data/Spookfile DELETED
@@ -1,11 +0,0 @@
1
- -- How much output do we want?
2
- log_level "INFO"
3
-
4
- rspec = command "bundle exec rspec -f d"
5
-
6
- watch "lib", "spec", ->
7
- on_changed "^(spec)/(spec_helper%.rb)", -> rspec "spec"
8
- on_changed "^spec/(.*)_spec%.rb", (a) -> rspec "spec/#{a}_spec.rb"
9
- on_changed "^lib/sambal%.rb", (a) -> rspec "spec/sambal/client_spec.rb"
10
-
11
- notifier "#{os.getenv('HOME')}/.spook/notifiers"
data/circle.yml DELETED
@@ -1,4 +0,0 @@
1
- dependencies:
2
- pre:
3
- - sudo apt-get update -qq
4
- - sudo apt-get install -y smbclient samba
data/sambal.gemspec DELETED
@@ -1,19 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/sambal/version', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["John Axel Eriksson"]
6
- gem.email = ["john@insane.se"]
7
- gem.description = %q{Ruby Samba Client using the cmdline smbclient}
8
- gem.summary = %q{Ruby Samba Client}
9
- gem.homepage = "https://github.com/johnae/sambal"
10
-
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "sambal"
15
- gem.require_paths = ["lib"]
16
- gem.version = Sambal::VERSION
17
-
18
- gem.add_development_dependency "rspec", '>=3.4.0'
19
- end