double-bag-ftps 0.1.0

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Bryan Nix
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ DoubleBagFTPS
2
+ =============
3
+
4
+ DoubleBagFTPS extends the core Net::FTP class to provide implicit and explicit FTPS support.
5
+
6
+ Install
7
+ -------
8
+
9
+ $ [sudo] gem install double-bag-ftps
10
+
11
+ **Note**: Your Ruby installation must have OpenSSL support.
12
+
13
+ Usage
14
+ -----
15
+ require 'double_bag_ftps'
16
+
17
+ Example 1:
18
+
19
+ # Connect to a host using explicit FTPS and do not verify the host's cert
20
+ ftps = DoubleBagFTPS.new
21
+ ftps.ssl_context = DoubleBagFTPS.create_ssl_context(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
22
+ ftps.connect('some host')
23
+ ftps.login('usr', 'passwd')
24
+
25
+ Example 2:
26
+
27
+ DoubleBagFTPS.open('host', 'usr', 'passwd', nil, DoubleBagFTPS::IMPLICIT) do |ftps|
28
+ ...
29
+ end
30
+
31
+ Interface
32
+ ---------
33
+
34
+ # Constants used for setting FTPS mode
35
+ DoubleBagFTPS::EXPLICIT
36
+ DoubleBagFTPS::IMPLICIT
37
+
38
+ DoubleBagFTPS.new(host = nil, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {})
39
+ DoubleBagFTPS.open(host, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {})
40
+
41
+ # Returns an OpenSSL::SSL::SSLContext using params to set set the corresponding SSLContext attributes.
42
+ DoubleBagFTPS.create_ssl_context(params = {})
43
+
44
+ # Set the FTPS mode to implicit (DoubleBagFTPS::IMPLICIT) or explicit (DoubleBagFTPS::EXPLICIT).
45
+ # The default FTPS mode is explicit.
46
+ ftps_mode=(ftps_mode)
47
+
48
+ # Same as Net::FTP.connect, but will use port 990 when using implicit FTPS and a port is not specified.
49
+ connect(host, port = ftps_implicit? ? IMPLICIT_PORT : FTP_PORT)
50
+
51
+ # Same as Net::FTP.login, but with optional auth param to control the value that is sent with the AUTH command.
52
+ login(user = 'anonymous', passwd = nil, acct = nil, auth = 'TLS')
53
+
54
+ ftps_explicit?
55
+ ftps_implicit?
56
+
57
+ More Information
58
+ ----------------
59
+
60
+ * [Net::FTP RDoc](http://ruby-doc.org/stdlib/libdoc/net/ftp/rdoc/index.html)
61
+ * [OpenSSL RDoc](http://ruby-doc.org/stdlib/libdoc/openssl/rdoc/index.html)
62
+
63
+ License
64
+ -------
65
+ Copyright © 2011, Bryan Nix. DoubleBagFTPS is released under the MIT license. See LICENSE file for details.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.rspec_opts = %w[--color]
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "double-bag-ftps"
6
+ s.version = "0.1.0"
7
+ s.author = "Bryan Nix"
8
+ s.homepage = "https://github.com/bnix/double-bag-ftps"
9
+ s.summary = "Provides a child class of Net::FTP to support implicit and explicit FTPS."
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.require_paths = ["lib"]
15
+
16
+ s.add_development_dependency "bundler"
17
+ s.add_development_dependency "rspec"
18
+ end
@@ -0,0 +1,164 @@
1
+ require 'net/ftp'
2
+ begin
3
+ require 'openssl'
4
+ rescue LoadError
5
+ end
6
+
7
+ class DoubleBagFTPS < Net::FTP
8
+ EXPLICIT = :explicit
9
+ IMPLICIT = :implicit
10
+ IMPLICIT_PORT = 990
11
+
12
+ # The form of FTPS that should be used. Either EXPLICIT or IMPLICIT.
13
+ # Defaults to EXPLICIT.
14
+ attr_reader :ftps_mode
15
+
16
+ # The OpenSSL::SSL::SSLContext to use for creating all OpenSSL::SSL::SSLSocket objects.
17
+ attr_accessor :ssl_context
18
+
19
+ def initialize(host = nil, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {})
20
+ raise ArgumentError unless valid_ftps_mode?(ftps_mode)
21
+ @ftps_mode = ftps_mode
22
+ @ssl_context = DoubleBagFTPS.create_ssl_context(ssl_context_params)
23
+ super(host, user, passwd, acct)
24
+ end
25
+
26
+ def DoubleBagFTPS.open(host, user = nil, passwd = nil, acct = nil, ftps_mode = EXPLICIT, ssl_context_params = {})
27
+ if block_given?
28
+ ftps = new(host, user, passwd, acct, ftps_mode, ssl_context_params)
29
+ begin
30
+ yield ftps
31
+ ensure
32
+ ftps.close
33
+ end
34
+ else
35
+ new(host, user, passwd, acct, ftps_mode, ssl_context_params)
36
+ end
37
+ end
38
+
39
+ #
40
+ # Allow @ftps_mode to be set when @sock is not connected
41
+ #
42
+ def ftps_mode=(ftps_mode)
43
+ # Ruby 1.8.7/1.9.2 compatible check
44
+ if (defined?(NullSocket) && @sock.kind_of?(NullSocket)) || @sock.nil? || @sock.closed?
45
+ raise ArgumentError unless valid_ftps_mode?(ftps_mode)
46
+ @ftps_mode = ftps_mode
47
+ else
48
+ raise 'Cannot set ftps_mode while connected'
49
+ end
50
+ end
51
+
52
+ #
53
+ # Establishes the command channel.
54
+ # Override parent to record host name for verification, and allow default implicit port.
55
+ #
56
+ def connect(host, port = ftps_implicit? ? IMPLICIT_PORT : FTP_PORT)
57
+ @hostname = host
58
+ super
59
+ end
60
+
61
+ def login(user = 'anonymous', passwd = nil, acct = nil, auth = 'TLS')
62
+ if ftps_explicit?
63
+ synchronize do
64
+ sendcmd('AUTH ' + auth) # Set the security mechanism
65
+ @sock = ssl_socket(@sock)
66
+ end
67
+ end
68
+
69
+ super(user, passwd, acct)
70
+ voidcmd('PBSZ 0') # The expected value for Protection Buffer Size (PBSZ) is 0 for TLS/SSL
71
+ voidcmd('PROT P') # Set data channel protection level to Private
72
+ end
73
+
74
+ #
75
+ # Override parent to allow an OpenSSL::SSL::SSLSocket to be returned
76
+ # when using implicit FTPS
77
+ #
78
+ def open_socket(host, port, defer_implicit_ssl = false)
79
+ if defined? SOCKSSocket and ENV["SOCKS_SERVER"]
80
+ @passive = true
81
+ sock = SOCKSSocket.open(host, port)
82
+ else
83
+ sock = TCPSocket.open(host, port)
84
+ end
85
+ return (!defer_implicit_ssl && ftps_implicit?) ? ssl_socket(sock) : sock
86
+ end
87
+ private :open_socket
88
+
89
+ #
90
+ # Override parent to support ssl sockets
91
+ #
92
+ def transfercmd(cmd, rest_offset = nil)
93
+ if @passive
94
+ host, port = makepasv
95
+
96
+ if @resume and rest_offset
97
+ resp = sendcmd('REST ' + rest_offset.to_s)
98
+ if resp[0] != ?3
99
+ raise FTPReplyError, resp
100
+ end
101
+ end
102
+ conn = open_socket(host, port, true)
103
+ resp = sendcmd(cmd)
104
+ # skip 2XX for some ftp servers
105
+ resp = getresp if resp[0] == ?2
106
+ if resp[0] != ?1
107
+ raise FTPReplyError, resp
108
+ end
109
+ conn = ssl_socket(conn) # SSL connection now possible after cmd sent
110
+ else
111
+ sock = makeport
112
+ if @resume and rest_offset
113
+ resp = sendcmd('REST ' + rest_offset.to_s)
114
+ if resp[0] != ?3
115
+ raise FTPReplyError, resp
116
+ end
117
+ end
118
+ resp = sendcmd(cmd)
119
+ # skip 2XX for some ftp servers
120
+ resp = getresp if resp[0] == ?2
121
+ if resp[0] != ?1
122
+ raise FTPReplyError, resp
123
+ end
124
+
125
+ temp_ssl_sock = ssl_socket(sock)
126
+ conn = temp_ssl_sock.accept
127
+ temp_ssl_sock.close
128
+ end
129
+ return conn
130
+ end
131
+ private :transfercmd
132
+
133
+ def ftps_explicit?; @ftps_mode == EXPLICIT end
134
+ def ftps_implicit?; @ftps_mode == IMPLICIT end
135
+
136
+ def valid_ftps_mode?(mode)
137
+ mode == EXPLICIT || mode == IMPLICIT
138
+ end
139
+ private :valid_ftps_mode?
140
+
141
+ #
142
+ # Returns a connected OpenSSL::SSL::SSLSocket
143
+ #
144
+ def ssl_socket(sock)
145
+ raise 'SSL extension not installed' unless defined?(OpenSSL)
146
+ sock = OpenSSL::SSL::SSLSocket.new(sock, @ssl_context)
147
+ sock.sync_close = true
148
+ sock.connect
149
+ print "get: #{sock.peer_cert.to_text}" if @debug_mode
150
+ unless @ssl_context.verify_mode == OpenSSL::SSL::VERIFY_NONE
151
+ sock.post_connection_check(@hostname)
152
+ end
153
+ return sock
154
+ end
155
+ private :ssl_socket
156
+
157
+ def DoubleBagFTPS.create_ssl_context(params = {})
158
+ raise 'SSL extension not installed' unless defined?(OpenSSL)
159
+ context = OpenSSL::SSL::SSLContext.new
160
+ context.set_params(params)
161
+ return context
162
+ end
163
+
164
+ end
@@ -0,0 +1,72 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ shared_examples_for "DoubleBagFTPS" do
4
+ it "connects to a remote host" do
5
+ lambda {@ftp.connect(HOST)}.should_not raise_error
6
+ end
7
+
8
+ it "logs in with a user name and password" do
9
+ @ftp.connect(HOST)
10
+ lambda {@ftp.login(USR, PASSWD)}.should_not raise_error
11
+ end
12
+
13
+ it "can open a secure data channel" do
14
+ @ftp.connect(HOST)
15
+ @ftp.login(USR, PASSWD)
16
+ @ftp.send(:transfercmd, 'nlst').should be_an_instance_of OpenSSL::SSL::SSLSocket
17
+ end
18
+
19
+ it "prevents setting the FTPS mode while connected" do
20
+ @ftp.connect(HOST)
21
+ lambda {@ftp.ftps_mode = 'dummy value'}.should raise_error
22
+ end
23
+
24
+ it "prevents setting the FTPS mode to an unrecognized value" do
25
+ lambda {@ftp.ftps_mode = 'dummy value'}.should raise_error
26
+ end
27
+
28
+ end
29
+
30
+ describe DoubleBagFTPS do
31
+ context "implicit" do
32
+ before(:each) do
33
+ @ftp = DoubleBagFTPS.new
34
+ @ftp.ftps_mode = DoubleBagFTPS::IMPLICIT
35
+ @ftp.passive = true
36
+ @ftp.ssl_context = DoubleBagFTPS.create_ssl_context(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
37
+ end
38
+
39
+ after(:each) do
40
+ @ftp.close unless @ftp.welcome.nil?
41
+ end
42
+
43
+ it "uses an SSLSocket when first connected" do
44
+ @ftp.connect(HOST)
45
+ @ftp.instance_eval {def socket; @sock; end}
46
+ @ftp.socket.should be_an_instance_of OpenSSL::SSL::SSLSocket
47
+ end
48
+
49
+ it_should_behave_like "DoubleBagFTPS"
50
+ end
51
+
52
+ context "explicit" do
53
+ before(:each) do
54
+ @ftp = DoubleBagFTPS.new
55
+ @ftp.ftps_mode = DoubleBagFTPS::EXPLICIT
56
+ @ftp.passive = true
57
+ @ftp.ssl_context = DoubleBagFTPS.create_ssl_context(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
58
+ end
59
+
60
+ after(:each) do
61
+ @ftp.close unless @ftp.welcome.nil?
62
+ end
63
+
64
+ it "does not use an SSLSocket when first connected" do
65
+ @ftp.connect(HOST)
66
+ @ftp.instance_eval {def socket; @sock; end}
67
+ @ftp.socket.should_not be_an_instance_of OpenSSL::SSL::SSLSocket
68
+ end
69
+
70
+ it_should_behave_like "DoubleBagFTPS"
71
+ end
72
+ end
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'double_bag_ftps'
3
+
4
+ HOST = 'ftp.secureftp-test.com'
5
+ USR = 'test'
6
+ PASSWD = 'test'
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: double-bag-ftps
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bryan Nix
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-03 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: &75184820 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *75184820
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &75184600 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *75184600
36
+ description:
37
+ email:
38
+ executables: []
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - .gitignore
43
+ - LICENSE
44
+ - README.md
45
+ - Rakefile
46
+ - double_bag_ftps.gemspec
47
+ - lib/double_bag_ftps.rb
48
+ - spec/double_bag_ftps_spec.rb
49
+ - spec/spec_helper.rb
50
+ homepage: https://github.com/bnix/double-bag-ftps
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.10
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Provides a child class of Net::FTP to support implicit and explicit FTPS.
74
+ test_files: []