net-ssh-kerberos 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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Joe Khoobyar
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.
@@ -0,0 +1,7 @@
1
+ = net-ssh-kerberos
2
+
3
+ Adds Kerberos support to Net::SSH
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2009 Joe Khoobyar. See LICENSE for details.
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "net-ssh-kerberos"
8
+ gem.summary = %Q{Add Kerberos support to Net::SSH}
9
+ gem.description = <<-EOTEXT
10
+ Adds support for Microsoft Kerberos (SSPI) with the Net:SSH gem.
11
+ EOTEXT
12
+ gem.email = "joe@ankhcraft.com"
13
+ gem.homepage = "http://github.com/joekhoobyar/net-ssh-kerberos"
14
+ gem.authors = ["Joe Khoobyar"]
15
+ gem.rubyforge_project = 'net-ssh-krb'
16
+ gem.add_runtime_dependency(%q<net-ssh>, [">= 2.0"])
17
+ gem.add_runtime_dependency(%q<rubysspi>, [">= 1.3"])
18
+
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ rescue LoadError
22
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
23
+ end
24
+
25
+ require 'rake/testtask'
26
+ Rake::TestTask.new(:test) do |test|
27
+ test.libs << 'lib' << 'test'
28
+ test.pattern = 'test/**/*_test.rb'
29
+ test.verbose = true
30
+ end
31
+
32
+ begin
33
+ require 'rcov/rcovtask'
34
+ Rcov::RcovTask.new do |test|
35
+ test.libs << 'test'
36
+ test.pattern = 'test/**/*_test.rb'
37
+ test.verbose = true
38
+ end
39
+ rescue LoadError
40
+ task :rcov do
41
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
42
+ end
43
+ end
44
+
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/rdoctask'
49
+ Rake::RDocTask.new do |rdoc|
50
+ if File.exist?('VERSION.yml')
51
+ config = YAML.load(File.read('VERSION.yml'))
52
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
53
+ else
54
+ version = ""
55
+ end
56
+
57
+ rdoc.rdoc_dir = 'rdoc'
58
+ rdoc.title = "Net-ssh-kerberos #{version}"
59
+ rdoc.rdoc_files.include('README*')
60
+ rdoc.rdoc_files.include('lib/**/*.rb')
61
+ end
62
+
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
@@ -0,0 +1,109 @@
1
+ require 'net/ssh/authentication/methods/abstract'
2
+ require 'net/ssh/kerberos/constants'
3
+
4
+ module Net
5
+ module SSH
6
+ module Authentication
7
+ module Methods
8
+
9
+ # Implements the Kerberos 5 SSH authentication method.
10
+ class GssapiWithMic < Abstract
11
+ include Net::SSH::Kerberos::Constants
12
+
13
+ # OID 1.2.840.113554.1.2.2
14
+ SUPPORTED_OID = #"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
15
+ [ 0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x1, 0x2, 0x2 ].pack("C*")
16
+
17
+ # Attempts to perform gssapi-with-mic Kerberos authentication
18
+ def authenticate(next_service, username, password=nil)
19
+ sspi = nil
20
+
21
+ # Try to start gssapi-with-mic authentication.
22
+ debug { "trying kerberos authentication" }
23
+ req = userauth_request(username, next_service, "gssapi-with-mic")
24
+ req.write_long 1
25
+ req.write_string SUPPORTED_OID
26
+ send_message req
27
+ message = session.next_message
28
+ case message.type
29
+ when USERAUTH_GSSAPI_RESPONSE
30
+ debug { "gssapi-with-mic proceeding" }
31
+ when USERAUTH_FAILURE
32
+ info { "gssapi-with-mic failed (USERAUTH_FAILURE)" }
33
+ return false
34
+ else
35
+ raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
36
+ end
37
+
38
+ # Try to match the OID.
39
+ oid = message.read_string
40
+ if oid != SUPPORTED_OID
41
+ info { "gssapi-with-mic failed (USERAUTH_GSSAPI_RESPONSE)" }
42
+ return false
43
+ end
44
+
45
+ # Try to complete the handshake.
46
+ sspi = Net::SSH::Kerberos::SSPI::GSSContext.new
47
+ sspi.create username, hostname
48
+ debug { "gssapi-with-mic handshaking" }
49
+ until sspi.established?
50
+ token = sspi.init(token)
51
+ if token && token.length > 0
52
+ send_message Net::SSH::Buffer.from(:byte, USERAUTH_GSSAPI_TOKEN, :string, token)
53
+ unless sspi.established?
54
+ message = session.next_message
55
+ case message.type
56
+ when USERAUTH_GSSAPI_ERROR
57
+ message = session.next_message
58
+ message.get_long
59
+ message.get_long
60
+ info { "gssapi-with-mic error (USERAUTH_GSSAPI_ERROR) (#{message.read_string})" }
61
+ when USERAUTH_GSSAPI_ERRTOK
62
+ message = session.next_message
63
+ info { "gssapi-with-mic error (USERAUTH_GSSAPI_ERRTOK) (#{message.read_string})" }
64
+ when USERAUTH_FAILURE
65
+ info { "gssapi-with-mic failed (USERAUTH_FAILURE)" }
66
+ return false
67
+ end
68
+ token = message.read_string
69
+ end
70
+ end
71
+ end
72
+
73
+ # Attempt the actual authentication.
74
+ debug { "gssapi-with-mic authenticating" }
75
+ mic = sspi.get_mic Net::SSH::Buffer.from(:string, session_id, :byte, USERAUTH_REQUEST, :string, username,
76
+ :string, next_service, :string, "gssapi-with-mic").to_s
77
+ if mic.nil?
78
+ info { "gssapi-with-mic failed (context#get_mic)" }
79
+ return false
80
+ end
81
+ send_message Net::SSH::Buffer.from(:byte, USERAUTH_GSSAPI_MIC, :string, mic)
82
+ message = session.next_message
83
+ case message.type
84
+ when USERAUTH_SUCCESS
85
+ info { "gssapi-with-mic success" }
86
+ return true
87
+ when USERAUTH_FAILURE
88
+ info { "gssapi-with-mic partial failure (USERAUTH_FAILURE)" }
89
+ return false
90
+ else
91
+ raise Net::SSH::Exception, "unexpected server response to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
92
+ end
93
+ ensure
94
+ sspi and sspi.dispose
95
+ end
96
+
97
+ private
98
+
99
+ # Returns the hostname as reported by the underlying socket.
100
+ def hostname
101
+ session.transport.host
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,12 @@
1
+ require 'net/ssh'
2
+ require 'net/ssh/kerberos/constants'
3
+ #require 'net/ssh/kerberos/kex'
4
+ require 'net/ssh/kerberos/sspi'
5
+ require 'net/ssh/authentication/methods/gssapi_with_mic'
6
+
7
+ module Net
8
+ module SSH
9
+ module Kerberos
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ module Net; module SSH; module Kerberos
2
+ module Constants
3
+
4
+
5
+ #--
6
+ # GSSAPI Key exchange method specific messages
7
+ #++
8
+
9
+ KEXGSS_INIT = 30
10
+ KEXGSS_CONTINUE = 31
11
+ KEXGSS_COMPLETE = 32
12
+ KEXGSS_HOSTKEY = 33
13
+ KEXGSS_ERROR = 34
14
+ KEXGSS_GROUPREQ = 40
15
+ KEXGSS_GROUP = 41
16
+
17
+ #--
18
+ # GSSAPI User authentication method specific messages
19
+ #++
20
+
21
+ USERAUTH_GSSAPI_RESPONSE = 60
22
+ USERAUTH_GSSAPI_TOKEN = 61
23
+ USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63
24
+ USERAUTH_GSSAPI_ERROR = 64
25
+ USERAUTH_GSSAPI_ERRTOK = 65
26
+ USERAUTH_GSSAPI_MIC = 66
27
+
28
+ end
29
+ end; end; end
30
+
@@ -0,0 +1,17 @@
1
+ require 'net/ssh/kerberos/kex/krb5_diffie_hellman_group1_sha1'
2
+ require 'net/ssh/kerberos/kex/krb5_diffie_hellman_group_exchange_sha1'
3
+
4
+ module Net; module SSH; module Kerberos
5
+ module Kex
6
+
7
+ GSS_MAP = {
8
+ 'gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==' => KRB5DiffieHellmanGroup1SHA1,
9
+ 'gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==' => KRB5DiffieHellmanGroupExchangeSHA1
10
+ }
11
+
12
+ Net::SSH::Transport::Kex::MAP.update GSS_MAP
13
+ Net::SSH::Transport::ALGORITHMS[:kex] << GSS_MAP.keys
14
+
15
+ end
16
+ end; end; end
17
+
@@ -0,0 +1,9 @@
1
+ module Net; module SSH; module Kerberos; module Kex
2
+
3
+ class KRB5DiffieHellmanGroup1SHA1 < Net::SSH::Transport::Kex::DiffieHellmanGroup1SHA1
4
+
5
+ end
6
+
7
+
8
+ end; end; end; end
9
+
@@ -0,0 +1,9 @@
1
+ module Net; module SSH; module Kerberos; module Kex
2
+
3
+ class KRB5DiffieHellmanGroupExchangeSHA1 < Net::SSH::Transport::Kex::DiffieHellmanGroupExchangeSHA1
4
+
5
+ end
6
+
7
+
8
+ end; end; end; end
9
+
@@ -0,0 +1,54 @@
1
+ require 'net/ssh/kerberos/sspi/api'
2
+
3
+ module Net; module SSH; module Kerberos
4
+ module SSPI
5
+
6
+ class State
7
+ attr_accessor :name, :realm, :valid
8
+ alias :valid? :valid
9
+ end
10
+
11
+ # Acquires credentials for SSPI (GSSAPI) authentication, and determines
12
+ # the credential's username and realm.
13
+ def sspi_acquire_credentials(state)
14
+ #SecPkgCredentials_Names names
15
+ #char *delimiter, *cp
16
+
17
+ state.valid? and return true
18
+
19
+ #sspi->from_server_token.BufferType = SECBUFFER_TOKEN | SECBUFFER_READONLY;
20
+
21
+ # Acquire credentials
22
+ sspi_acquire_credentials_handle
23
+ names = sspi_query_credentials_names
24
+ state.name, state.realm = *names.user_name.split('@')
25
+
26
+ #debug((" FreeContextBuffer(%x)\n", names.sUserName));
27
+ #result = SSPIResult.new(API::FreeContextBuffer(names.user.to_p))
28
+ sspi_free_context_buffer names
29
+ sspi_free_credentials_handle
30
+
31
+ # Sometimes, Microsoft SSPI returns a UPN of the form "user@REALM@",
32
+ # (Seen under WinXP pro after ksetup to a MIT realm). Deal with that.
33
+ realm.chop if realm[-1] == '@'
34
+
35
+ # Initialise the request flags.
36
+ #sspi->request_flags = ISC_REQ_MUTUAL_AUTH |
37
+ # ISC_REQ_INTEGRITY |
38
+ # ISC_REQ_CONFIDENTIALITY |
39
+ # ISC_REQ_ALLOCATE_MEMORY;
40
+ #if (ssh->cfg.sspi_fwd_ticket)
41
+ # sspi->request_flags |= ISC_REQ_DELEGATE;
42
+
43
+ if ! sspi_construct_service_name state
44
+ state.valid = true
45
+ #if (!sspi_construct_service_name(ssh, sspi)) {
46
+ #debug((" FreeCredentialsHandle(%s)\n", HDL(&sspi->credentials)));
47
+ else
48
+ sspi_free_credentials_handle
49
+ end
50
+ end
51
+ end
52
+ end; end; end
53
+
54
+
@@ -0,0 +1,282 @@
1
+ require 'win32/sspi'
2
+ require 'dl'
3
+
4
+ module Win32; module SSPI
5
+
6
+ SECPKG_CRED_ATTR_NAMES = 1
7
+
8
+ ISC_REQ_DELEGATE = 0x00000001
9
+ ISC_REQ_MUTUAL_AUTH = 0x00000002
10
+ ISC_REQ_INTEGRITY = 0x00010000
11
+
12
+ SECPKG_ATTR_AUTHORITY = 6
13
+ SECPKG_ATTR_CONNECTION_INFO = 90
14
+ SECPKG_ATTR_ISSUER_LIST = 80
15
+ SECPKG_ATTR_ISSUER_LIST_EX = 89
16
+ SECPKG_ATTR_KEY_INFO = 5
17
+ SECPKG_ATTR_LIFESPAN = 2
18
+ SECPKG_ATTR_LOCAL_CERT_CONTEXT = 84
19
+ SECPKG_ATTR_LOCAL_CRED = 82
20
+ SECPKG_ATTR_NAMES = 1
21
+ SECPKG_ATTR_PROTO_INFO = 7
22
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT = 83
23
+ SECPKG_ATTR_REMOTE_CRED = 81
24
+ SECPKG_ATTR_SIZES = 0
25
+ SECPKG_ATTR_STREAM_SIZES = 4
26
+
27
+ # Buffer types
28
+ SECBUFFER_EMPTY = 0
29
+ SECBUFFER_DATA = 1
30
+ SECBUFFER_TOKEN = 2
31
+ SECBUFFER_PKG_PARAMS = 3
32
+ SECBUFFER_MISSING = 4
33
+ SECBUFFER_EXTRA = 5
34
+ SECBUFFER_STREAM_TRAILER = 6
35
+ SECBUFFER_STREAM_HEADER = 7
36
+ SECBUFFER_PADDING = 9
37
+ SECBUFFER_STREAM = 10
38
+ SECBUFFER_MECHLIST = 11
39
+ SECBUFFER_MECHLIST_SIGNATURE = 12
40
+ SECBUFFER_TARGET = 13
41
+ SECBUFFER_CHANNEL_BINDINGS = 14
42
+ SECBUFFER_CHANGE_PASS_RESPONSE = 15
43
+ SECBUFFER_TARGET_HOST = 16
44
+ SECBUFFER_READONLY = 0x80000000
45
+ SECBUFFER_READONLY_WITH_CHECKSUM = 0x10000000
46
+ SECBUFFER_ATTRMASK = 0xf0000000
47
+
48
+ # Good results
49
+ SEC_E_OK = 0x00000000
50
+ SEC_I_RENEGOTIATE = 590625;
51
+ SEC_I_COMPLETE_AND_CONTINUE = 590612;
52
+ SEC_I_COMPLETE_NEEDED = 590611;
53
+ SEC_I_CONTINUE_NEEDED = 590610;
54
+ SEC_I_INCOMPLETE_CREDENTIALS = 590624;
55
+
56
+ # These are generally returned by InitializeSecurityContext
57
+ SEC_E_INSUFFICIENT_MEMORY = 0x80090300
58
+ SEC_E_INTERNAL_ERROR = 0x80090304
59
+ SEC_E_INVALID_HANDLE = 0x80090301
60
+ SEC_E_INVALID_TOKEN = 0x80090308
61
+ SEC_E_LOGON_DENIED = 0x8009030C
62
+ SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311
63
+ SEC_E_NO_CREDENTIALS = 0x8009030E
64
+ SEC_E_TARGET_UNKNOWN = 0x80090303
65
+ SEC_E_UNSUPPORTED_FUNCTION = 0x80090302
66
+ SEC_E_WRONG_PRINCIPAL = 0x80090322
67
+
68
+ # These are generally returned by AcquireCredentialsHandle
69
+ SEC_E_NOT_OWNER = 0x80090306
70
+ SEC_E_SECPKG_NOT_FOUND = 0x80090305
71
+ SEC_E_UNKNOWN_CREDENTIALS = 0x8009030D
72
+
73
+ module API
74
+ QuerySecurityPackageInfo = Win32API.new("secur32", "QuerySecurityPackageInfoA", 'pp', 'L')
75
+ QueryCredentialsAttributes = Win32API.new("secur32", "QueryCredentialsAttributesA", 'pLp', 'L')
76
+ QueryContextAttributes = Win32API.new("secur32", "QueryContextAttributesA", 'pLp', 'L')
77
+ CompleteAuthToken = Win32API.new("secur32", "CompleteAuthToken", 'pp', 'L')
78
+ MakeSignature = Win32API.new("secur32", "MakeSignature", 'pLpL', 'L')
79
+ FreeContextBuffer = Win32API.new("secur32", "FreeContextBuffer", 'P', 'L')
80
+ end
81
+
82
+ SecPkgCredentialsNames = Struct.new(:user_name)
83
+
84
+ class SecPkgInfo
85
+ attr_reader :struct
86
+
87
+ def capabilities; unpacked[0] end
88
+ def max_token; unpacked[2] end
89
+ def name; unpacked[3] end
90
+ def comment; unpacked[4] end
91
+
92
+ def unpacked;
93
+ @unpacked ||= @struct.to_ptr.ptr.to_a("LLL", 3) + (@struct.to_ptr.ptr + 12).to_a("SS", 2)
94
+ end
95
+
96
+ def to_p; @struct ||= "\0" * 4 end
97
+ end
98
+
99
+ class SecPkgSizes
100
+ attr_reader :struct
101
+
102
+ def max_token; unpacked[0] end
103
+ def max_signature; unpacked[1] end
104
+ def block_size; unpacked[2] end
105
+ def security_trailer; unpacked[3] end
106
+
107
+ def unpacked;
108
+ @unpacked ||= @struct.unpack("LLLL")
109
+ end
110
+
111
+ def to_p; @struct ||= "\0" * 16 end
112
+ end
113
+
114
+ # Creates binary representaiton of a SecBufferDesc structure,
115
+ # including the SecBuffer contained inside.
116
+ class SecurityBuffer
117
+
118
+ def initialize(buffers=nil)
119
+ case buffers
120
+ when String
121
+ @bufferTokens = [ buffers.dup ]
122
+ @bufferSizes = [ buffers.length ]
123
+ @bufferTypes = [ SECBUFFER_TOKEN ]
124
+ when Fixnum
125
+ @bufferTokens = [ "\0" * TOKENBUFSIZE ] * buffers
126
+ @bufferSizes = [ TOKENBUFSIZE ] * buffers
127
+ @bufferTypes = [ SECBUFFER_TOKEN ] * buffers
128
+ when NilClass
129
+ @bufferTokens = [ "\0" * TOKENBUFSIZE ]
130
+ @bufferSizes = [ TOKENBUFSIZE ]
131
+ @bufferTypes = [ SECBUFFER_TOKEN ]
132
+ else
133
+ raise ArgumentError
134
+ end
135
+ end
136
+
137
+ def bufferSize(n=0)
138
+ unpack
139
+ @bufferSizes[n]
140
+ end
141
+
142
+ def bufferType(n=0)
143
+ unpack
144
+ @bufferTypes[n]
145
+ end
146
+
147
+ def token(n=0)
148
+ unpack
149
+ @bufferTokens[n]
150
+ end
151
+
152
+ def set_buffer(n=0, type=SECBUFFER_TOKEN, token=nil, size=nil)
153
+ @bufferTypes[n] = type
154
+ @bufferSizes[n] = size || (token.nil? ? 0 : token.length)
155
+ @bufferTokens[n] = (token.nil? && size && size > 0) ? "\0" * (size+1) : token
156
+ end
157
+
158
+ def to_p
159
+ # Assumption is that when to_p is called we are going to get a packed structure. Therefore,
160
+ # set @unpacked back to nil so we know to unpack when accessors are next accessed.
161
+ @unpacked = nil
162
+ # Assignment of inner structure to variable is very important here. Without it,
163
+ # will not be able to unpack changes to the structure. Alternative, nested unpacks,
164
+ # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer")
165
+ @sec_buffers ||= @bufferTokens.inject([]) do |v,t|
166
+ v.push @bufferSizes[v.size / 3], @bufferTypes[v.size / 3], t
167
+ end.pack("LLP" * @bufferTokens.size)
168
+ @struct ||= [SECBUFFER_VERSION, @bufferTokens.size, @sec_buffers].pack("LLP")
169
+ end
170
+
171
+ private
172
+
173
+ # Unpacks the SecurityBufferDesc structure into member variables. We
174
+ # only want to do this once per struct, so the struct is deleted
175
+ # after unpacking.
176
+ def unpack
177
+ if ! @unpacked && @sec_buffers && @struct
178
+ d = @sec_buffers.unpack("LLL" * @bufferTokens.size)
179
+ k = ''; 0.upto(@bufferTokens.size - 1) do |n| k << "LLP#{d[n * 3]}" end
180
+ d = @sec_buffers.unpack(k)
181
+ 0.upto(@bufferTokens.size - 1) do |n| @bufferSizes[n] = d[n * 3] end
182
+ 0.upto(@bufferTokens.size - 1) do |n| @bufferTypes[n] = d[n * 3 + 1] end
183
+ 0.upto(@bufferTokens.size - 1) do |n| @bufferTokens[n] = d[n * 3 + 2] end
184
+ @struct = nil
185
+ @sec_buffers = nil
186
+ @unpacked = true
187
+ end
188
+ end
189
+ end
190
+
191
+ end; end
192
+
193
+ module Net; module SSH; module Kerberos; module SSPI; class GSSContext
194
+
195
+ class GeneralError < StandardError; end
196
+
197
+ include Win32::SSPI
198
+
199
+ def create(user, host)
200
+ dispose if @credentials or @handle
201
+ @credentials = CredHandle.new
202
+ ts=TimeStamp.new
203
+
204
+ result = SSPIResult.new(API::AcquireCredentialsHandle.call(
205
+ nil, "Kerberos", SECPKG_CRED_OUTBOUND, nil, nil,
206
+ nil, nil, @credentials.to_p, ts.to_p
207
+ ))
208
+ unless result.ok?
209
+ @credentials = nil
210
+ raise GeneralError, "Error acquiring credentials: #{result}"
211
+ end
212
+
213
+ buff = "\0\0\0\0"
214
+ result = SSPIResult.new(API::QueryCredentialsAttributes.call(@credentials.to_p, SECPKG_CRED_ATTR_NAMES, buff))
215
+ if result.ok?
216
+ names = buff.to_ptr.ptr
217
+ begin
218
+ @cred_name = names.to_s.sub /^.*\\/, ''
219
+ @cred_krb_name = @cred_name.gsub '@', '/';
220
+ @server_name = Socket.gethostbyname(host)[0]
221
+ @server_krb_name = "host/" + @server_name
222
+ ensure
223
+ API::FreeContextBuffer.call(names)
224
+ end
225
+ end
226
+ cred_names = SecPkgCredentialsNames.new(names)
227
+ end
228
+
229
+ def init(token=nil)
230
+ ctx = CtxtHandle.new
231
+ ts = TimeStamp.new
232
+ prev = @state[:handle].to_p if @state and @state[:handle]
233
+ req = ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH | ISC_REQ_INTEGRITY
234
+ output = SecurityBuffer.new
235
+ input = SecurityBuffer.new(token) if token
236
+ ctxAttr = "\0" * 4
237
+ result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, prev, @server_krb_name,
238
+ req, 0, SECURITY_NATIVE_DREP, input ? input.to_p : nil,
239
+ 0, ctx.to_p, output.to_p, ctxAttr, ts.to_p))
240
+ if SEC_I_COMPLETE_NEEDED == result || SEC_I_COMPLETE_AND_CONTINUE == result
241
+ result = SSPIResult.new(API::CompleteAuthToken.call(ctx.to_p, output.to_p))
242
+ end
243
+ unless result.ok?
244
+ input.token
245
+ raise GeneralError, "Error initializing security context: #{result} #{input.inspect}"
246
+ end
247
+ @state = { :handle => ctx, :result => result, :token => output.token, :stamp => ts }
248
+ if result.value == 0
249
+ @sizes = SecPkgSizes.new
250
+ result = SSPIResult.new(API::QueryContextAttributes.call(ctx.to_p, SECPKG_ATTR_SIZES, @sizes.to_p))
251
+ @handle = @state[:handle]
252
+ end
253
+ @state[:token]
254
+ end
255
+
256
+ def established?
257
+ @handle && (@handle.upper.nonzero? || @handle.lower.nonzero?) && (@state.nil? || @state[:result].value.zero?)
258
+ end
259
+
260
+ def get_mic(token=nil)
261
+ buffers = SecurityBuffer.new 2
262
+ buffers.set_buffer 0, SECBUFFER_DATA, token
263
+ buffers.set_buffer 1, SECBUFFER_TOKEN, nil, @sizes.max_signature
264
+ @state[:result] = SSPIResult.new(API::MakeSignature.call(@handle.to_p, 0, buffers.to_p, 0))
265
+ unless @state[:result].ok?
266
+ raise GeneralError, "Error creating the signature: #{result}"
267
+ end
268
+ return buffers.token(1).dup
269
+ end
270
+
271
+ def dispose()
272
+ if @credentials
273
+ API::FreeCredentialsHandle.call(@credentials.to_p)
274
+ @credentials = nil
275
+ end
276
+ if @handle
277
+ API::DeleteSecurityContext.call(@handle.to_p)
278
+ @handle = nil
279
+ end
280
+ end
281
+
282
+ end; end; end; end; end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class NetSshKerberosTest < Test::Unit::TestCase
4
+ def test_something_for_real
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'net_ssh_kerberos'
7
+
8
+ class Test::Unit::TestCase
9
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: net-ssh-kerberos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Joe Khoobyar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-13 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: net-ssh
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "2.0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rubysspi
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "1.3"
34
+ version:
35
+ description: |
36
+ Adds support for Microsoft Kerberos (SSPI) with the Net:SSH gem.
37
+
38
+ email: joe@ankhcraft.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - LICENSE
45
+ - README.rdoc
46
+ files:
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION.yml
51
+ - lib/net/ssh/authentication/methods/gssapi_with_mic.rb
52
+ - lib/net/ssh/kerberos.rb
53
+ - lib/net/ssh/kerberos/constants.rb
54
+ - lib/net/ssh/kerberos/kex.rb
55
+ - lib/net/ssh/kerberos/kex/krb5_diffie_hellman_group1_sha1.rb
56
+ - lib/net/ssh/kerberos/kex/krb5_diffie_hellman_group_exchange_sha1.rb
57
+ - lib/net/ssh/kerberos/sspi.rb
58
+ - lib/net/ssh/kerberos/sspi/api.rb
59
+ - test/net_ssh_kerberos_test.rb
60
+ - test/test_helper.rb
61
+ has_rdoc: true
62
+ homepage: http://github.com/joekhoobyar/net-ssh-kerberos
63
+ licenses: []
64
+
65
+ post_install_message:
66
+ rdoc_options:
67
+ - --charset=UTF-8
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ requirements: []
83
+
84
+ rubyforge_project: net-ssh-krb
85
+ rubygems_version: 1.3.4
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Add Kerberos support to Net::SSH
89
+ test_files:
90
+ - test/net_ssh_kerberos_test.rb
91
+ - test/test_helper.rb