net-smtp-ntlm 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ce835096d258af930fc3a8bbea0f30751c91c734cb900c7dea399f17c23230b3
4
+ data.tar.gz: 7bc58f9680c6b5c5ad2995fcf793f99be819b1b17d7b569f3d968aaf29f08693
5
+ SHA512:
6
+ metadata.gz: a3587cced6b74a95f84ac44a9653d4dcd69a4a743278ba16e707738aae875a48f70f62994b29e4da689779f16e8e5c55800aa887d65c66b7371221371f9e3639
7
+ data.tar.gz: d299b436f46e4afb903e286295a709c1ad776794a0c77fe66bf827b1a7e1aa207abfa50b911c90d0c220a28d2053905cfd25e9ee781fbc378fd3a3fc08dd0422
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.0.1
2
+
3
+ - Базовая версия с поддержкой NTLM для SMTP
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Net::SMTP::NTLM
2
+
3
+ Add-on for [net-smtp](https://github.com/ruby/net-smtp/) gem to add NTLM support.
4
+
5
+ Based on [ruby-ntlm](https://github.com/macks/ruby-ntlm) gem.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'net-smtp-ntlm'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install net-smtp-ntlm
22
+
23
+ ## Usage
24
+
25
+ ## Contributing
26
+
27
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/net-smtp.
@@ -0,0 +1,16 @@
1
+ module Net::SMTP::NTLM
2
+ class AuthNtlm < Net::SMTP::Authenticator
3
+ auth_type :ntlm
4
+
5
+ def auth(user, secret)
6
+ if user.index('\\')
7
+ domain, user = user.split('\\', 2)
8
+ else
9
+ domain = ''
10
+ end
11
+
12
+ challenge = continue("AUTH NTLM #{Net::SMTP::NTLM.negotiate.to_base64}")
13
+ finish(Net::SMTP::NTLM.authenticate(challenge.unpack('m').first, user, domain, secret).to_base64)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,365 @@
1
+ require 'net/smtp/ntlm/util'
2
+
3
+ module Net::SMTP::NTLM
4
+ class Message
5
+
6
+ include Net::SMTP::NTLM::Util
7
+
8
+ SSP_SIGNATURE = "NTLMSSP\0"
9
+
10
+ # [MS-NLMP] 2.2.2.5
11
+ FLAGS = {
12
+ :NEGOTIATE_UNICODE => 0x00000001, # Unicode character set encoding
13
+ :NEGOTIATE_OEM => 0x00000002, # OEM character set encoding
14
+ :REQUEST_TARGET => 0x00000004, # TargetName is supplied in challenge message
15
+ :UNUSED10 => 0x00000008,
16
+ :NEGOTIATE_SIGN => 0x00000010, # Session key negotiation for message signatures
17
+ :NEGOTIATE_SEAL => 0x00000020, # Session key negotiation for message confidentiality
18
+ :NEGOTIATE_DATAGRAM => 0x00000040, # Connectionless authentication
19
+ :NEGOTIATE_LM_KEY => 0x00000080, # LAN Manager session key computation
20
+ :UNUSED9 => 0x00000100,
21
+ :NEGOTIATE_NTLM => 0x00000200, # NTLM v1 protocol
22
+ :UNUSED8 => 0x00000400,
23
+ :ANONYMOUS => 0x00000800, # Anonymous connection
24
+ :OEM_DOMAIN_SUPPLIED => 0x00001000, # Domain field is present
25
+ :OEM_WORKSTATION_SUPPLIED => 0x00002000, # Workstations field is present
26
+ :UNUSED7 => 0x00004000,
27
+ :NEGOTIATE_ALWAYS_SIGN => 0x00008000,
28
+ :TARGET_TYPE_DOMAIN => 0x00010000, # TargetName is domain name
29
+ :TARGET_TYPE_SERVER => 0x00020000, # TargetName is server name
30
+ :UNUSED6 => 0x00040000,
31
+ :NEGOTIATE_EXTENDED_SECURITY => 0x00080000, # NTLM v2 session security
32
+ :NEGOTIATE_IDENTIFY => 0x00100000, # Requests identify level token
33
+ :UNUSED5 => 0x00200000,
34
+ :REQUEST_NON_NT_SESSION_KEY => 0x00400000, # LM session key is used
35
+ :NEGOTIATE_TARGET_INFO => 0x00800000, # Requests TargetInfo
36
+ :UNUSED4 => 0x01000000,
37
+ :NEGOTIATE_VERSION => 0x02000000, # Version field is present
38
+ :UNUSED3 => 0x04000000,
39
+ :UNUSED2 => 0x08000000,
40
+ :UNUSED1 => 0x10000000,
41
+ :NEGOTIATE_128 => 0x20000000, # 128bit encryption
42
+ :NEGOTIATE_KEY_EXCH => 0x40000000, # Explicit key exchange
43
+ :NEGOTIATE_56 => 0x80000000, # 56bit encryption
44
+ }
45
+
46
+ # [MS-NLMP] 2.2.2.1
47
+ AV_PAIRS = {
48
+ :AV_EOL => 0,
49
+ :AV_NB_COMPUTER_NAME => 1,
50
+ :AV_NB_DOMAIN_NAME => 2,
51
+ :AV_DNS_COMPUTER_NAME => 3,
52
+ :AV_DNS_DOMAIN_NAME => 4,
53
+ :AV_DNS_TREE_NAME => 5,
54
+ :AV_FLAGS => 6,
55
+ :AV_TIMESTAMP => 7,
56
+ :AV_RESTRICTIONS => 8,
57
+ :AV_TARGET_NAME => 9,
58
+ :AV_CHANNEL_BINDINGS => 10,
59
+ }
60
+ AV_PAIR_NAMES = AV_PAIRS.invert
61
+
62
+ FLAGS.each do |name, val|
63
+ const_set(name, val)
64
+ end
65
+
66
+ AV_PAIRS.each do |name, val|
67
+ const_set(name, val)
68
+ end
69
+
70
+ class ParseError < StandardError; end
71
+
72
+ attr_accessor :flag
73
+
74
+
75
+ def self.parse(*args)
76
+ new.parse(*args)
77
+ end
78
+
79
+ def initialize(args = {})
80
+ @buffer = ''
81
+ @offset = 0
82
+ @flag = args[:flag] || self.class::DEFAULT_FLAGS
83
+ @domain = nil
84
+ @workstation = nil
85
+ @version = nil
86
+ @target_info = nil
87
+ @session_key = nil
88
+ @mic = nil
89
+
90
+ self.class::ATTRIBUTES.each do |key|
91
+ instance_variable_set("@#{key}", args[key]) if args[key]
92
+ end
93
+ end
94
+
95
+ def to_s
96
+ serialize
97
+ end
98
+
99
+ def serialize_to_base64
100
+ [serialize].pack('m').delete("\r\n")
101
+ end
102
+
103
+ alias to_base64 serialize_to_base64
104
+
105
+ def has_flag?(symbol)
106
+ (@flag & FLAGS[symbol]) != 0
107
+ end
108
+
109
+ def set(symbol)
110
+ @flag |= FLAGS[symbol]
111
+ end
112
+
113
+ def clear(symbol)
114
+ @flag &= ~FLAGS[symbol]
115
+ end
116
+
117
+ def unicode?
118
+ has_flag?(:NEGOTIATE_UNICODE)
119
+ end
120
+
121
+ def inspect_flags
122
+ flags = []
123
+ FLAGS.sort_by(&:last).each do |name, val|
124
+ flags << name if (@flag & val).nonzero?
125
+ end
126
+ "[#{flags.join(', ')}]"
127
+ end
128
+
129
+ def inspect
130
+ variables = (instance_variables.map(&:to_sym) - [:@offset, :@buffer, :@flag]).sort.map {|name| "#{name}=#{instance_variable_get(name).inspect}, " }.join
131
+ "\#<#{self.class.name} #{variables}@flag=#{inspect_flags}>"
132
+ end
133
+
134
+ private
135
+
136
+ def parse(string)
137
+ @buffer = string
138
+ signature, type = string.unpack('a8V')
139
+ raise ParseError, 'Unknown signature' if signature != SSP_SIGNATURE
140
+ raise ParseError, "Wrong type (expected #{self.class::TYPE}, but got #{type})" if type != self.class::TYPE
141
+ end
142
+
143
+ def append_payload(string, allocation_size = nil)
144
+ size = string.size
145
+ allocation_size ||= (size + 1) & ~1
146
+ string = string.ljust(allocation_size, "\0")
147
+ @buffer << string[0, allocation_size]
148
+ result = [size, allocation_size, @offset].pack('vvV')
149
+ @offset += allocation_size
150
+ result
151
+ end
152
+
153
+ def fetch_payload(fields)
154
+ size, _, offset = fields.unpack('vvV')
155
+ return nil if size.zero?
156
+ @buffer[offset, size]
157
+ end
158
+
159
+ def encode_version(array)
160
+ array.pack('CCvx3C') # major, minor, build, ntlm revision
161
+ end
162
+
163
+ def decode_version(string)
164
+ string.unpack('CCvx3C') # major, minor, build, ntlm revision
165
+ end
166
+
167
+ def decode_av_pair(string)
168
+ result = []
169
+ string = string.dup
170
+ while true
171
+ id, length = string.slice!(0, 4).unpack('vv')
172
+ value = string.slice!(0, length)
173
+
174
+ case sym = AV_PAIR_NAMES[id]
175
+ when :AV_EOL
176
+ break
177
+ when :AV_NB_COMPUTER_NAME, :AV_NB_DOMAIN_NAME, :AV_DNS_COMPUTER_NAME, :AV_DNS_DOMAIN_NAME, :AV_DNS_TREE_NAME, :AV_TARGET_NAME
178
+ value = decode_utf16(value)
179
+ when :AV_FLAGS
180
+ value = data.unpack('V').first
181
+ end
182
+
183
+ result << [sym, value]
184
+ end
185
+ result
186
+ end
187
+
188
+ def encode_av_pair(av_pair)
189
+ result = ''
190
+ av_pair.each do |(id, value)|
191
+ case id
192
+ when :AV_NB_COMPUTER_NAME, :AV_NB_DOMAIN_NAME, :AV_DNS_COMPUTER_NAME, :AV_DNS_DOMAIN_NAME, :AV_DNS_TREE_NAME, :AV_TARGET_NAME
193
+ value = encode_utf16(value)
194
+ when :AV_FLAGS
195
+ value = [data].pack('V')
196
+ end
197
+ result << [AV_PAIRS[id], value.size, value].pack('vva*')
198
+ end
199
+
200
+ result << [AV_EOL, 0].pack('vv')
201
+ end
202
+
203
+
204
+ # [MS-NLMP] 2.2.1.1
205
+ class Negotiate < Message
206
+
207
+ TYPE = 1
208
+ ATTRIBUTES = [:domain, :workstation, :version]
209
+ DEFAULT_FLAGS = [NEGOTIATE_UNICODE, NEGOTIATE_OEM, REQUEST_TARGET, NEGOTIATE_NTLM, NEGOTIATE_ALWAYS_SIGN, NEGOTIATE_EXTENDED_SECURITY].inject(:|)
210
+
211
+ attr_accessor(*ATTRIBUTES)
212
+
213
+ def parse(string)
214
+ super
215
+ @flag, domain, workstation, version = string.unpack('x12Va8a8a8')
216
+ @domain = fetch_payload(domain) if has_flag?(:OEM_DOMAIN_SUPPLIED)
217
+ @workstation = fetch_payload(workstation) if has_flag?(:OEM_WORKSTATION_SUPPLIED)
218
+ @version = decode_version(version) if has_flag?(:NEGOTIATE_VERSION)
219
+ self
220
+ end
221
+
222
+ def serialize
223
+ @buffer = ''
224
+ @offset = 40 # (8 + 4) + 4 + (8 * 3)
225
+
226
+ if @domain
227
+ set(:OEM_DOMAIN_SUPPLIED)
228
+ domain = append_payload(@domain)
229
+ end
230
+
231
+ if @workstation
232
+ set(:OEM_WORKSTATION_SUPPLIED)
233
+ workstation = append_payload(@workstation)
234
+ end
235
+
236
+ if @version
237
+ set(:NEGOTIATE_VERSION)
238
+ version = encode_version(@version)
239
+ end
240
+
241
+ [SSP_SIGNATURE, TYPE, @flag, domain, workstation, version].pack('a8VVa8a8a8') + @buffer
242
+ end
243
+
244
+ end # Negotiate
245
+
246
+
247
+ # [MS-NLMP] 2.2.1.2
248
+ class Challenge < Message
249
+
250
+ TYPE = 2
251
+ ATTRIBUTES = [:target_name, :challenge, :target_info, :version]
252
+ DEFAULT_FLAGS = 0
253
+
254
+ attr_accessor(*ATTRIBUTES)
255
+
256
+ def parse(string)
257
+ super
258
+ target_name, @flag, @challenge, target_info, version = string.unpack('x12a8Va8x8a8a8')
259
+ @target_name = fetch_payload(target_name) if has_flag?(:REQUEST_TARGET)
260
+ @target_info = fetch_payload(target_info) if has_flag?(:NEGOTIATE_TARGET_INFO)
261
+ @version = decode_version(version) if has_flag?(:NEGOTIATE_VERSION)
262
+
263
+ @target_name &&= decode_utf16(@target_name) if unicode?
264
+ @target_info &&= decode_av_pair(@target_info)
265
+
266
+ self
267
+ end
268
+
269
+ def serialize
270
+ @buffer = ''
271
+ @offset = 56 # (8 + 4) + 8 + 4 + (8 * 4)
272
+
273
+ @challenge ||= OpenSSL::Random.random_bytes(8)
274
+
275
+ if @target_name
276
+ set(:REQUEST_TARGET)
277
+ if unicode?
278
+ target_name = append_payload(encode_utf16(@target_name))
279
+ else
280
+ target_name = append_payload(@target_name)
281
+ end
282
+ end
283
+
284
+ if @target_info
285
+ set(:NEGOTIATE_TARGET_INFO)
286
+ target_info = append_payload(encode_av_pair(@target_info))
287
+ end
288
+
289
+ if @version
290
+ set(:NEGOTIATE_VERSION)
291
+ version = encode_version(@version)
292
+ end
293
+
294
+ [SSP_SIGNATURE, TYPE, target_name, @flag, @challenge, target_info, version].pack('a8Va8Va8x8a8a8') + @buffer
295
+ end
296
+
297
+ end # Challenge
298
+
299
+
300
+ # [MS-NLMP] 2.2.1.3
301
+ class Authenticate < Message
302
+
303
+ TYPE = 3
304
+ ATTRIBUTES = [:lm_response, :nt_response, :domain, :user, :workstation, :session_key, :version, :mic]
305
+ DEFAULT_FLAGS = [NEGOTIATE_UNICODE, REQUEST_TARGET, NEGOTIATE_NTLM, NEGOTIATE_ALWAYS_SIGN, NEGOTIATE_EXTENDED_SECURITY].inject(:|)
306
+
307
+ attr_accessor(*ATTRIBUTES)
308
+
309
+ def parse(string)
310
+ super
311
+ lm_response, nt_response, domain, user, workstation, session_key, @flag, version, mic = \
312
+ string.unpack('x12a8a8a8a8a8a8Va8a16')
313
+
314
+ @lm_response = fetch_payload(lm_response)
315
+ @nt_response = fetch_payload(nt_response)
316
+ @domain = fetch_payload(domain)
317
+ @user = fetch_payload(user)
318
+ @workstation = fetch_payload(workstation)
319
+ @session_key = fetch_payload(session_key) if has_flag?(:NEGOTIATE_KEY_EXCH)
320
+ @version = decode_version(version) if has_flag?(:NEGOTIATE_VERSION)
321
+ @mic = mic
322
+
323
+ if unicode?
324
+ @domain = decode_utf16(@domain)
325
+ @user = decode_utf16(@user)
326
+ @workstation = decode_utf16(@workstation)
327
+ end
328
+
329
+ self
330
+ end
331
+
332
+ def serialize
333
+ @buffer = ''
334
+ @offset = 88 # (8 + 4) + (8 * 6) + 4 + 8 + 16
335
+
336
+ lm_response = append_payload(@lm_response)
337
+ nt_response = append_payload(@nt_response)
338
+
339
+ if unicode?
340
+ domain = append_payload(encode_utf16(@domain))
341
+ user = append_payload(encode_utf16(@user))
342
+ workstation = append_payload(encode_utf16(@workstation))
343
+ else
344
+ domain = append_payload(@domain)
345
+ user = append_payload(@user)
346
+ workstation = append_payload(@workstation)
347
+ end
348
+
349
+ if @session_key
350
+ set(:NEGOTIATE_KEY_EXCH)
351
+ session_key = append_payload(@session_key)
352
+ end
353
+
354
+ if @version
355
+ set(:NEGOTIATE_VERSION)
356
+ version = encode_version(@version)
357
+ end
358
+
359
+ [SSP_SIGNATURE, TYPE, lm_response, nt_response, domain, user, workstation, session_key, @flag, version, @mic].pack('a8Va8a8a8a8a8a8Va8a16') + @buffer
360
+ end
361
+
362
+ end # Authenticate
363
+
364
+ end # Message
365
+ end # NTLM
@@ -0,0 +1,108 @@
1
+ require 'openssl'
2
+
3
+ module Net::SMTP::NTLM
4
+ module Util
5
+
6
+ LM_MAGIC_TEXT = 'KGS!@#$%'
7
+
8
+ module_function
9
+
10
+ if RUBY_VERSION >= '1.9'
11
+
12
+ def decode_utf16(str)
13
+ str.encode(Encoding::UTF_8, Encoding::UTF_16LE)
14
+ end
15
+
16
+ def encode_utf16(str)
17
+ str.to_s.encode(Encoding::UTF_16LE).force_encoding(Encoding::ASCII_8BIT)
18
+ end
19
+
20
+ else
21
+
22
+ require 'iconv'
23
+
24
+ def decode_utf16(str)
25
+ Iconv.conv('UTF-8', 'UTF-16LE', str)
26
+ end
27
+
28
+ def encode_utf16(str)
29
+ Iconv.conv('UTF-16LE', 'UTF-8', str)
30
+ end
31
+
32
+ end
33
+
34
+ def create_des_keys(string)
35
+ keys = []
36
+ string = string.dup
37
+ until (key = string.slice!(0, 7)).empty?
38
+ # key is 56 bits
39
+ key = key.unpack('B*').first
40
+ str = ''
41
+ until (bits = key.slice!(0, 7)).empty?
42
+ str << bits
43
+ str << (bits.count('1').even? ? '1' : '0') # parity
44
+ end
45
+ keys << [str].pack('B*')
46
+ end
47
+ keys
48
+ end
49
+
50
+ def encrypt(plain_text, key, key_length)
51
+ key = key.ljust(key_length, "\0")
52
+ keys = create_des_keys(key[0, key_length])
53
+
54
+ result = ''
55
+ cipher = OpenSSL::Cipher::DES.new
56
+ keys.each do |k|
57
+ cipher.encrypt
58
+ cipher.key = k
59
+
60
+ encrypted_text = cipher.update(plain_text)
61
+ encrypted_text << cipher.final
62
+ result << encrypted_text[0...8]
63
+ end
64
+
65
+ result
66
+ end
67
+
68
+ # [MS-NLMP] 3.3.1
69
+ def lm_v1_hash(password)
70
+ encrypt(LM_MAGIC_TEXT, password.upcase, 14)
71
+ end
72
+
73
+ # [MS-NLMP] 3.3.1
74
+ def nt_v1_hash(password)
75
+ OpenSSL::Digest::MD4.digest(encode_utf16(password))
76
+ end
77
+
78
+ # [MS-NLMP] 3.3.1
79
+ def ntlm_v1_response(challenge, password, options = {})
80
+ if options[:ntlm_v2_session]
81
+ challenge = challenge.b if challenge.respond_to?(:b)
82
+ client_challenge = options[:client_challenge] || OpenSSL::Random.random_bytes(8)
83
+ client_challenge = client_challenge.b if client_challenge.respond_to?(:b)
84
+ hash = OpenSSL::Digest::MD5.digest(challenge + client_challenge)[0, 8]
85
+ nt_response = encrypt(hash, nt_v1_hash(password), 21)
86
+ lm_response = client_challenge + ("\0" * 16)
87
+ else
88
+ nt_response = encrypt(challenge, nt_v1_hash(password), 21)
89
+ lm_response = encrypt(challenge, lm_v1_hash(password), 21)
90
+ end
91
+
92
+ [nt_response, lm_response]
93
+ end
94
+
95
+
96
+ # [MS-NLMP] 3.3.2
97
+ def nt_v2_hash(user, password, domain)
98
+ user_domain = encode_utf16(user.upcase + domain)
99
+ OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, nt_v1_hash(password), user_domain)
100
+ end
101
+
102
+ # [MS-NLMP] 3.3.2
103
+ def ntlm_v2_response(*)
104
+ raise NotImplemnetedError
105
+ end
106
+
107
+ end # Util
108
+ end # NTLM
@@ -0,0 +1,5 @@
1
+ require 'net/smtp'
2
+
3
+ module Net::SMTP::NTLM
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,27 @@
1
+ require 'net/smtp'
2
+ require 'net/smtp/ntlm/util'
3
+ require 'net/smtp/ntlm/message'
4
+ require 'net/smtp/ntlm/auth_ntlm'
5
+
6
+ module Net::SMTP::NTLM
7
+ def self.negotiate(args = {})
8
+ Message::Negotiate.new(args)
9
+ end
10
+
11
+ def self.authenticate(challenge_message, user, domain, password, options = {})
12
+ challenge = Net::SMTP::NTLM::Message::Challenge.parse(challenge_message)
13
+
14
+ opt = options.merge({
15
+ :ntlm_v2_session => challenge.has_flag?(:NEGOTIATE_EXTENDED_SECURITY),
16
+ })
17
+ nt_response, lm_response = Net::SMTP::NTLM::Util.ntlm_v1_response(challenge.challenge, password, opt)
18
+
19
+ Net::SMTP::NTLM::Message::Authenticate.new(
20
+ :user => user,
21
+ :domain => domain,
22
+ :lm_response => lm_response,
23
+ :nt_response => nt_response
24
+ )
25
+ end
26
+
27
+ end # NTLM
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), 'lib')
2
+ require 'net/smtp/ntlm/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'net-smtp-ntlm'
6
+ spec.version = Net::SMTP::NTLM::VERSION
7
+ spec.summary = 'Add-on for net-smtp gem to add NTLM support.'
8
+ spec.description = 'Add-on for net-smtp gem to add NTLM support.'
9
+
10
+ spec.authors = ['Fedosov Sergey (RnD Soft)']
11
+ spec.email = 'info@rnds.pro'
12
+ spec.homepage = 'https://rnds.pro'
13
+
14
+ spec.add_runtime_dependency 'net-smtp', '~> 0.4.0'
15
+
16
+ spec.add_development_dependency "rake"
17
+ spec.add_development_dependency "test-unit"
18
+
19
+ spec.files = %w[README.md CHANGELOG.md net-smtp-ntlm.gemspec] + Dir['lib/**/*.rb']
20
+ spec.require_paths = ['lib']
21
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: net-smtp-ntlm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Fedosov Sergey (RnD Soft)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date:
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: net-smtp
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Add-on for net-smtp gem to add NTLM support.
56
+ email: info@rnds.pro
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - README.md
62
+ - CHANGELOG.md
63
+ - net-smtp-ntlm.gemspec
64
+ - lib/net/smtp/ntlm/auth_ntlm.rb
65
+ - lib/net/smtp/ntlm/message.rb
66
+ - lib/net/smtp/ntlm/util.rb
67
+ - lib/net/smtp/ntlm/version.rb
68
+ - lib/net/smtp/ntlm.rb
69
+ homepage: https://rnds.pro
70
+ licenses: []
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 3.3.26
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Add-on for net-smtp gem to add NTLM support.
91
+ test_files: []