rubyntlm 0.3.1 → 0.3.2
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 +1 -0
- data/.travis.yml +5 -1
- data/lib/net/ntlm.rb +869 -845
- data/rubyntlm.gemspec +2 -2
- data/spec/unit/ntlm_spec.rb +14 -6
- metadata +61 -49
- data/Gemfile.lock +0 -27
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/lib/net/ntlm.rb
CHANGED
@@ -1,845 +1,869 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
#
|
3
|
-
# = net/ntlm.rb
|
4
|
-
#
|
5
|
-
# An NTLM Authentication Library for Ruby
|
6
|
-
#
|
7
|
-
# This code is a derivative of "dbf2.rb" written by yrock
|
8
|
-
# and Minero Aoki. You can find original code here:
|
9
|
-
# http://jp.rubyist.net/magazine/?0013-CodeReview
|
10
|
-
# -------------------------------------------------------------
|
11
|
-
# Copyright (c) 2005,2006 yrock
|
12
|
-
#
|
13
|
-
# This program is free software.
|
14
|
-
# You can distribute/modify this program under the terms of the
|
15
|
-
# Ruby License.
|
16
|
-
#
|
17
|
-
# 2006-02-11 refactored by Minero Aoki
|
18
|
-
# -------------------------------------------------------------
|
19
|
-
#
|
20
|
-
# All protocol information used to write this code stems from
|
21
|
-
# "The NTLM Authentication Protocol" by Eric Glass. The author
|
22
|
-
# would thank to him for this tremendous work and making it
|
23
|
-
# available on the net.
|
24
|
-
# http://davenport.sourceforge.net/ntlm.html
|
25
|
-
# -------------------------------------------------------------
|
26
|
-
# Copyright (c) 2003 Eric Glass
|
27
|
-
#
|
28
|
-
# Permission to use, copy, modify, and distribute this document
|
29
|
-
# for any purpose and without any fee is hereby granted,
|
30
|
-
# provided that the above copyright notice and this list of
|
31
|
-
# conditions appear in all copies.
|
32
|
-
# -------------------------------------------------------------
|
33
|
-
#
|
34
|
-
# The author also looked Mozilla-Firefox-1.0.7 source code,
|
35
|
-
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
|
36
|
-
# Jonathan Bastien-Filiatrault's libntlm-ruby.
|
37
|
-
# "http://x2a.org/websvn/filedetails.php?
|
38
|
-
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
|
39
|
-
# The latter has a minor bug in its separate_keys function.
|
40
|
-
# The third key has to begin from the 14th character of the
|
41
|
-
# input string instead of 13th:)
|
42
|
-
#--
|
43
|
-
# $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
|
44
|
-
#++
|
45
|
-
|
46
|
-
require 'base64'
|
47
|
-
require 'openssl'
|
48
|
-
require 'openssl/digest'
|
49
|
-
require 'socket'
|
50
|
-
|
51
|
-
module Net
|
52
|
-
module NTLM
|
53
|
-
# @private
|
54
|
-
module VERSION
|
55
|
-
MAJOR = 0
|
56
|
-
MINOR = 3
|
57
|
-
TINY =
|
58
|
-
STRING = [MAJOR, MINOR, TINY].join('.')
|
59
|
-
end
|
60
|
-
|
61
|
-
SSP_SIGN = "NTLMSSP\0"
|
62
|
-
BLOB_SIGN = 0x00000101
|
63
|
-
LM_MAGIC = "KGS!@\#$%"
|
64
|
-
TIME_OFFSET = 11644473600
|
65
|
-
MAX64 = 0xffffffffffffffff
|
66
|
-
|
67
|
-
FLAGS = {
|
68
|
-
:UNICODE => 0x00000001,
|
69
|
-
:OEM => 0x00000002,
|
70
|
-
:REQUEST_TARGET => 0x00000004,
|
71
|
-
:MBZ9 => 0x00000008,
|
72
|
-
:SIGN => 0x00000010,
|
73
|
-
:SEAL => 0x00000020,
|
74
|
-
:NEG_DATAGRAM => 0x00000040,
|
75
|
-
:NETWARE => 0x00000100,
|
76
|
-
:NTLM => 0x00000200,
|
77
|
-
:NEG_NT_ONLY => 0x00000400,
|
78
|
-
:MBZ7 => 0x00000800,
|
79
|
-
:DOMAIN_SUPPLIED => 0x00001000,
|
80
|
-
:WORKSTATION_SUPPLIED => 0x00002000,
|
81
|
-
:LOCAL_CALL => 0x00004000,
|
82
|
-
:ALWAYS_SIGN => 0x00008000,
|
83
|
-
:TARGET_TYPE_DOMAIN => 0x00010000,
|
84
|
-
:TARGET_INFO => 0x00800000,
|
85
|
-
:NTLM2_KEY => 0x00080000,
|
86
|
-
:KEY128 => 0x20000000,
|
87
|
-
:KEY56 => 0x80000000
|
88
|
-
}.freeze
|
89
|
-
|
90
|
-
FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
|
91
|
-
|
92
|
-
DEFAULT_FLAGS = {
|
93
|
-
:TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
|
94
|
-
:TYPE2 => FLAGS[:UNICODE],
|
95
|
-
:TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
|
96
|
-
}
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
end
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
end
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
def
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
end
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
0
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
# @macro
|
415
|
-
# @method $1
|
416
|
-
# @method $1=
|
417
|
-
# @return [
|
418
|
-
def
|
419
|
-
add_field(name,
|
420
|
-
end
|
421
|
-
|
422
|
-
# @macro
|
423
|
-
# @method $1
|
424
|
-
# @method $1=
|
425
|
-
# @return [
|
426
|
-
def
|
427
|
-
add_field(name,
|
428
|
-
end
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
def
|
435
|
-
|
436
|
-
end
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
def opts
|
443
|
-
|
444
|
-
end
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
end
|
493
|
-
|
494
|
-
def
|
495
|
-
|
496
|
-
end
|
497
|
-
|
498
|
-
def
|
499
|
-
|
500
|
-
end
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
@value
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
def
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
if
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
end
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
ws
|
751
|
-
|
752
|
-
|
753
|
-
end
|
754
|
-
|
755
|
-
if opt[:
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
if opt[:
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# = net/ntlm.rb
|
4
|
+
#
|
5
|
+
# An NTLM Authentication Library for Ruby
|
6
|
+
#
|
7
|
+
# This code is a derivative of "dbf2.rb" written by yrock
|
8
|
+
# and Minero Aoki. You can find original code here:
|
9
|
+
# http://jp.rubyist.net/magazine/?0013-CodeReview
|
10
|
+
# -------------------------------------------------------------
|
11
|
+
# Copyright (c) 2005,2006 yrock
|
12
|
+
#
|
13
|
+
# This program is free software.
|
14
|
+
# You can distribute/modify this program under the terms of the
|
15
|
+
# Ruby License.
|
16
|
+
#
|
17
|
+
# 2006-02-11 refactored by Minero Aoki
|
18
|
+
# -------------------------------------------------------------
|
19
|
+
#
|
20
|
+
# All protocol information used to write this code stems from
|
21
|
+
# "The NTLM Authentication Protocol" by Eric Glass. The author
|
22
|
+
# would thank to him for this tremendous work and making it
|
23
|
+
# available on the net.
|
24
|
+
# http://davenport.sourceforge.net/ntlm.html
|
25
|
+
# -------------------------------------------------------------
|
26
|
+
# Copyright (c) 2003 Eric Glass
|
27
|
+
#
|
28
|
+
# Permission to use, copy, modify, and distribute this document
|
29
|
+
# for any purpose and without any fee is hereby granted,
|
30
|
+
# provided that the above copyright notice and this list of
|
31
|
+
# conditions appear in all copies.
|
32
|
+
# -------------------------------------------------------------
|
33
|
+
#
|
34
|
+
# The author also looked Mozilla-Firefox-1.0.7 source code,
|
35
|
+
# namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
|
36
|
+
# Jonathan Bastien-Filiatrault's libntlm-ruby.
|
37
|
+
# "http://x2a.org/websvn/filedetails.php?
|
38
|
+
# repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
|
39
|
+
# The latter has a minor bug in its separate_keys function.
|
40
|
+
# The third key has to begin from the 14th character of the
|
41
|
+
# input string instead of 13th:)
|
42
|
+
#--
|
43
|
+
# $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
|
44
|
+
#++
|
45
|
+
|
46
|
+
require 'base64'
|
47
|
+
require 'openssl'
|
48
|
+
require 'openssl/digest'
|
49
|
+
require 'socket'
|
50
|
+
|
51
|
+
module Net
|
52
|
+
module NTLM
|
53
|
+
# @private
|
54
|
+
module VERSION
|
55
|
+
MAJOR = 0
|
56
|
+
MINOR = 3
|
57
|
+
TINY = 2
|
58
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
59
|
+
end
|
60
|
+
|
61
|
+
SSP_SIGN = "NTLMSSP\0"
|
62
|
+
BLOB_SIGN = 0x00000101
|
63
|
+
LM_MAGIC = "KGS!@\#$%"
|
64
|
+
TIME_OFFSET = 11644473600
|
65
|
+
MAX64 = 0xffffffffffffffff
|
66
|
+
|
67
|
+
FLAGS = {
|
68
|
+
:UNICODE => 0x00000001,
|
69
|
+
:OEM => 0x00000002,
|
70
|
+
:REQUEST_TARGET => 0x00000004,
|
71
|
+
:MBZ9 => 0x00000008,
|
72
|
+
:SIGN => 0x00000010,
|
73
|
+
:SEAL => 0x00000020,
|
74
|
+
:NEG_DATAGRAM => 0x00000040,
|
75
|
+
:NETWARE => 0x00000100,
|
76
|
+
:NTLM => 0x00000200,
|
77
|
+
:NEG_NT_ONLY => 0x00000400,
|
78
|
+
:MBZ7 => 0x00000800,
|
79
|
+
:DOMAIN_SUPPLIED => 0x00001000,
|
80
|
+
:WORKSTATION_SUPPLIED => 0x00002000,
|
81
|
+
:LOCAL_CALL => 0x00004000,
|
82
|
+
:ALWAYS_SIGN => 0x00008000,
|
83
|
+
:TARGET_TYPE_DOMAIN => 0x00010000,
|
84
|
+
:TARGET_INFO => 0x00800000,
|
85
|
+
:NTLM2_KEY => 0x00080000,
|
86
|
+
:KEY128 => 0x20000000,
|
87
|
+
:KEY56 => 0x80000000
|
88
|
+
}.freeze
|
89
|
+
|
90
|
+
FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
|
91
|
+
|
92
|
+
DEFAULT_FLAGS = {
|
93
|
+
:TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
|
94
|
+
:TYPE2 => FLAGS[:UNICODE],
|
95
|
+
:TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
|
96
|
+
}
|
97
|
+
|
98
|
+
class EncodeUtil
|
99
|
+
if RUBY_VERSION == "1.8.7"
|
100
|
+
require "kconv"
|
101
|
+
|
102
|
+
# Decode a UTF16 string to a ASCII string
|
103
|
+
# @param [String] str The string to convert
|
104
|
+
def self.decode_utf16le(str)
|
105
|
+
Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Encodes a ASCII string to a UTF16 string
|
109
|
+
# @param [String] str The string to convert
|
110
|
+
def self.encode_utf16le(str)
|
111
|
+
swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
|
112
|
+
end
|
113
|
+
|
114
|
+
# Taggle the strings endianness between big/little and little/big
|
115
|
+
# @param [String] str The string to swap the endianness on
|
116
|
+
def self.swap16(str)
|
117
|
+
str.unpack("v*").pack("n*")
|
118
|
+
end
|
119
|
+
else # Use native 1.9 string encoding functions
|
120
|
+
|
121
|
+
# Decode a UTF16 string to a ASCII string
|
122
|
+
# @param [String] str The string to convert
|
123
|
+
def self.decode_utf16le(str)
|
124
|
+
str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
|
125
|
+
end
|
126
|
+
|
127
|
+
# Encodes a ASCII string to a UTF16 string
|
128
|
+
# @param [String] str The string to convert
|
129
|
+
# @note This implementation may seem stupid but the problem is that UTF16-LE and UTF-8 are incompatiable
|
130
|
+
# encodings. This library uses string contatination to build the packet bytes. The end result is that
|
131
|
+
# you can either marshal the encodings elsewhere of simply know that each time you call encode_utf16le
|
132
|
+
# the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte
|
133
|
+
# concatination works seamlessly.
|
134
|
+
def self.encode_utf16le(str)
|
135
|
+
str = str.force_encoding('UTF-8') if [::Encoding::ASCII_8BIT,::Encoding::US_ASCII].include?(str.encoding)
|
136
|
+
str.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('UTF-8')
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class << self
|
142
|
+
|
143
|
+
# Conver the value to a 64-Bit Little Endian Int
|
144
|
+
# @param [String] val The string to convert
|
145
|
+
def pack_int64le(val)
|
146
|
+
[val & 0x00000000ffffffff, val >> 32].pack("V2")
|
147
|
+
end
|
148
|
+
|
149
|
+
# Builds an array of strings that are 7 characters long
|
150
|
+
# @param [String] str The string to split
|
151
|
+
# @api private
|
152
|
+
def split7(str)
|
153
|
+
s = str.dup
|
154
|
+
until s.empty?
|
155
|
+
(ret ||= []).push s.slice!(0, 7)
|
156
|
+
end
|
157
|
+
ret
|
158
|
+
end
|
159
|
+
|
160
|
+
# Not sure what this is doing
|
161
|
+
# @param [String] str String to generate keys for
|
162
|
+
# @api private
|
163
|
+
def gen_keys(str)
|
164
|
+
split7(str).map{ |str7|
|
165
|
+
bits = split7(str7.unpack("B*")[0]).inject('')\
|
166
|
+
{|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
|
167
|
+
[bits].pack("B*")
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
def apply_des(plain, keys)
|
172
|
+
dec = OpenSSL::Cipher::DES.new
|
173
|
+
keys.map {|k|
|
174
|
+
dec.key = k
|
175
|
+
dec.encrypt.update(plain)
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
# Generates a Lan Manager Hash
|
180
|
+
# @param [String] password The password to base the hash on
|
181
|
+
def lm_hash(password)
|
182
|
+
keys = gen_keys password.upcase.ljust(14, "\0")
|
183
|
+
apply_des(LM_MAGIC, keys).join
|
184
|
+
end
|
185
|
+
|
186
|
+
# Generate a NTLM Hash
|
187
|
+
# @param [String] password The password to base the hash on
|
188
|
+
# @option opt :unicode (false) Unicode encode the password
|
189
|
+
def ntlm_hash(password, opt = {})
|
190
|
+
pwd = password.dup
|
191
|
+
unless opt[:unicode]
|
192
|
+
pwd = EncodeUtil.encode_utf16le(pwd)
|
193
|
+
end
|
194
|
+
OpenSSL::Digest::MD4.digest pwd
|
195
|
+
end
|
196
|
+
|
197
|
+
# Generate a NTLMv2 Hash
|
198
|
+
# @param [String] user The username
|
199
|
+
# @param [String] password The password
|
200
|
+
# @param [String] target The domain or workstaiton to authenticate to
|
201
|
+
# @option opt :unicode (false) Unicode encode the domain
|
202
|
+
def ntlmv2_hash(user, password, target, opt={})
|
203
|
+
ntlmhash = ntlm_hash(password, opt)
|
204
|
+
userdomain = (user + target).upcase
|
205
|
+
unless opt[:unicode]
|
206
|
+
userdomain = EncodeUtil.encode_utf16le(userdomain)
|
207
|
+
end
|
208
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
|
209
|
+
end
|
210
|
+
|
211
|
+
def lm_response(arg)
|
212
|
+
begin
|
213
|
+
hash = arg[:lm_hash]
|
214
|
+
chal = arg[:challenge]
|
215
|
+
rescue
|
216
|
+
raise ArgumentError
|
217
|
+
end
|
218
|
+
chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
|
219
|
+
keys = gen_keys hash.ljust(21, "\0")
|
220
|
+
apply_des(chal, keys).join
|
221
|
+
end
|
222
|
+
|
223
|
+
def ntlm_response(arg)
|
224
|
+
hash = arg[:ntlm_hash]
|
225
|
+
chal = arg[:challenge]
|
226
|
+
chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
|
227
|
+
keys = gen_keys hash.ljust(21, "\0")
|
228
|
+
apply_des(chal, keys).join
|
229
|
+
end
|
230
|
+
|
231
|
+
def ntlmv2_response(arg, opt = {})
|
232
|
+
begin
|
233
|
+
key = arg[:ntlmv2_hash]
|
234
|
+
chal = arg[:challenge]
|
235
|
+
ti = arg[:target_info]
|
236
|
+
rescue
|
237
|
+
raise ArgumentError
|
238
|
+
end
|
239
|
+
chal = NTL::pack_int64le(chal) if chal.is_a?(Integer)
|
240
|
+
|
241
|
+
if opt[:client_challenge]
|
242
|
+
cc = opt[:client_challenge]
|
243
|
+
else
|
244
|
+
cc = rand(MAX64)
|
245
|
+
end
|
246
|
+
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
|
247
|
+
|
248
|
+
if opt[:timestamp]
|
249
|
+
ts = opt[:timestamp]
|
250
|
+
else
|
251
|
+
ts = Time.now.to_i
|
252
|
+
end
|
253
|
+
# epoch -> milsec from Jan 1, 1601
|
254
|
+
ts = 10000000 * (ts + TIME_OFFSET)
|
255
|
+
|
256
|
+
blob = Blob.new
|
257
|
+
blob.timestamp = ts
|
258
|
+
blob.challenge = cc
|
259
|
+
blob.target_info = ti
|
260
|
+
|
261
|
+
bb = blob.serialize
|
262
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
|
263
|
+
end
|
264
|
+
|
265
|
+
def lmv2_response(arg, opt = {})
|
266
|
+
key = arg[:ntlmv2_hash]
|
267
|
+
chal = arg[:challenge]
|
268
|
+
|
269
|
+
chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
|
270
|
+
|
271
|
+
if opt[:client_challenge]
|
272
|
+
cc = opt[:client_challenge]
|
273
|
+
else
|
274
|
+
cc = rand(MAX64)
|
275
|
+
end
|
276
|
+
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
|
277
|
+
|
278
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
|
279
|
+
end
|
280
|
+
|
281
|
+
def ntlm2_session(arg, opt = {})
|
282
|
+
begin
|
283
|
+
passwd_hash = arg[:ntlm_hash]
|
284
|
+
chal = arg[:challenge]
|
285
|
+
rescue
|
286
|
+
raise ArgumentError
|
287
|
+
end
|
288
|
+
|
289
|
+
if opt[:client_challenge]
|
290
|
+
cc = opt[:client_challenge]
|
291
|
+
else
|
292
|
+
cc = rand(MAX64)
|
293
|
+
end
|
294
|
+
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
|
295
|
+
|
296
|
+
keys = gen_keys passwd_hash.ljust(21, "\0")
|
297
|
+
session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
|
298
|
+
response = apply_des(session_hash, keys).join
|
299
|
+
[cc.ljust(24, "\0"), response]
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
# base classes for primitives
|
305
|
+
# @private
|
306
|
+
class Field
|
307
|
+
attr_accessor :active, :value
|
308
|
+
|
309
|
+
def initialize(opts)
|
310
|
+
@value = opts[:value]
|
311
|
+
@active = opts[:active].nil? ? true : opts[:active]
|
312
|
+
end
|
313
|
+
|
314
|
+
def size
|
315
|
+
@active ? @size : 0
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
class String < Field
|
320
|
+
def initialize(opts)
|
321
|
+
super(opts)
|
322
|
+
@size = opts[:size]
|
323
|
+
end
|
324
|
+
|
325
|
+
def parse(str, offset=0)
|
326
|
+
if @active and str.size >= offset + @size
|
327
|
+
@value = str[offset, @size]
|
328
|
+
@size
|
329
|
+
else
|
330
|
+
0
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def serialize
|
335
|
+
if @active
|
336
|
+
@value
|
337
|
+
else
|
338
|
+
""
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def value=(val)
|
343
|
+
@value = val
|
344
|
+
@size = @value.nil? ? 0 : @value.size
|
345
|
+
@active = (@size > 0)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
class Int16LE < Field
|
350
|
+
def initialize(opt)
|
351
|
+
super(opt)
|
352
|
+
@size = 2
|
353
|
+
end
|
354
|
+
def parse(str, offset=0)
|
355
|
+
if @active and str.size >= offset + @size
|
356
|
+
@value = str[offset, @size].unpack("v")[0]
|
357
|
+
@size
|
358
|
+
else
|
359
|
+
0
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def serialize
|
364
|
+
[@value].pack("v")
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
class Int32LE < Field
|
369
|
+
def initialize(opt)
|
370
|
+
super(opt)
|
371
|
+
@size = 4
|
372
|
+
end
|
373
|
+
|
374
|
+
def parse(str, offset=0)
|
375
|
+
if @active and str.size >= offset + @size
|
376
|
+
@value = str.slice(offset, @size).unpack("V")[0]
|
377
|
+
@size
|
378
|
+
else
|
379
|
+
0
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
def serialize
|
384
|
+
[@value].pack("V") if @active
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
class Int64LE < Field
|
389
|
+
def initialize(opt)
|
390
|
+
super(opt)
|
391
|
+
@size = 8
|
392
|
+
end
|
393
|
+
|
394
|
+
def parse(str, offset=0)
|
395
|
+
if @active and str.size >= offset + @size
|
396
|
+
d, u = str.slice(offset, @size).unpack("V2")
|
397
|
+
@value = (u * 0x100000000 + d)
|
398
|
+
@size
|
399
|
+
else
|
400
|
+
0
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
def serialize
|
405
|
+
[@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# base class of data structure
|
410
|
+
class FieldSet
|
411
|
+
class << FieldSet
|
412
|
+
|
413
|
+
|
414
|
+
# @macro string_security_buffer
|
415
|
+
# @method $1
|
416
|
+
# @method $1=
|
417
|
+
# @return [String]
|
418
|
+
def string(name, opts)
|
419
|
+
add_field(name, String, opts)
|
420
|
+
end
|
421
|
+
|
422
|
+
# @macro int16le_security_buffer
|
423
|
+
# @method $1
|
424
|
+
# @method $1=
|
425
|
+
# @return [Int16LE]
|
426
|
+
def int16LE(name, opts)
|
427
|
+
add_field(name, Int16LE, opts)
|
428
|
+
end
|
429
|
+
|
430
|
+
# @macro int32le_security_buffer
|
431
|
+
# @method $1
|
432
|
+
# @method $1=
|
433
|
+
# @return [Int32LE]
|
434
|
+
def int32LE(name, opts)
|
435
|
+
add_field(name, Int32LE, opts)
|
436
|
+
end
|
437
|
+
|
438
|
+
# @macro int64le_security_buffer
|
439
|
+
# @method $1
|
440
|
+
# @method $1=
|
441
|
+
# @return [Int64]
|
442
|
+
def int64LE(name, opts)
|
443
|
+
add_field(name, Int64LE, opts)
|
444
|
+
end
|
445
|
+
|
446
|
+
# @macro security_buffer
|
447
|
+
# @method $1
|
448
|
+
# @method $1=
|
449
|
+
# @return [SecurityBuffer]
|
450
|
+
def security_buffer(name, opts)
|
451
|
+
add_field(name, SecurityBuffer, opts)
|
452
|
+
end
|
453
|
+
|
454
|
+
def prototypes
|
455
|
+
@proto
|
456
|
+
end
|
457
|
+
|
458
|
+
def names
|
459
|
+
@proto.map{|n, t, o| n}
|
460
|
+
end
|
461
|
+
|
462
|
+
def types
|
463
|
+
@proto.map{|n, t, o| t}
|
464
|
+
end
|
465
|
+
|
466
|
+
def opts
|
467
|
+
@proto.map{|n, t, o| o}
|
468
|
+
end
|
469
|
+
|
470
|
+
private
|
471
|
+
|
472
|
+
def add_field(name, type, opts)
|
473
|
+
(@proto ||= []).push [name, type, opts]
|
474
|
+
define_accessor name
|
475
|
+
end
|
476
|
+
|
477
|
+
def define_accessor(name)
|
478
|
+
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
479
|
+
def #{name}
|
480
|
+
self['#{name}'].value
|
481
|
+
end
|
482
|
+
|
483
|
+
def #{name}=(val)
|
484
|
+
self['#{name}'].value = val
|
485
|
+
end
|
486
|
+
End
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def initialize
|
491
|
+
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
|
492
|
+
end
|
493
|
+
|
494
|
+
def serialize
|
495
|
+
@alist.map{|n, f| f.serialize }.join
|
496
|
+
end
|
497
|
+
|
498
|
+
def parse(str, offset=0)
|
499
|
+
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
|
500
|
+
end
|
501
|
+
|
502
|
+
def size
|
503
|
+
@alist.inject(0){|sum, a| sum += a[1].size}
|
504
|
+
end
|
505
|
+
|
506
|
+
def [](name)
|
507
|
+
a = @alist.assoc(name.to_s.intern)
|
508
|
+
raise ArgumentError, "no such field: #{name}" unless a
|
509
|
+
a[1]
|
510
|
+
end
|
511
|
+
|
512
|
+
def []=(name, val)
|
513
|
+
a = @alist.assoc(name.to_s.intern)
|
514
|
+
raise ArgumentError, "no such field: #{name}" unless a
|
515
|
+
a[1] = val
|
516
|
+
end
|
517
|
+
|
518
|
+
def enable(name)
|
519
|
+
self[name].active = true
|
520
|
+
end
|
521
|
+
|
522
|
+
def disable(name)
|
523
|
+
self[name].active = false
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class Blob < FieldSet
|
528
|
+
int32LE :blob_signature, {:value => BLOB_SIGN}
|
529
|
+
int32LE :reserved, {:value => 0}
|
530
|
+
int64LE :timestamp, {:value => 0}
|
531
|
+
string :challenge, {:value => "", :size => 8}
|
532
|
+
int32LE :unknown1, {:value => 0}
|
533
|
+
string :target_info, {:value => "", :size => 0}
|
534
|
+
int32LE :unknown2, {:value => 0}
|
535
|
+
end
|
536
|
+
|
537
|
+
class SecurityBuffer < FieldSet
|
538
|
+
|
539
|
+
int16LE :length, {:value => 0}
|
540
|
+
int16LE :allocated, {:value => 0}
|
541
|
+
int32LE :offset, {:value => 0}
|
542
|
+
|
543
|
+
attr_accessor :active
|
544
|
+
def initialize(opts)
|
545
|
+
super()
|
546
|
+
@value = opts[:value]
|
547
|
+
@active = opts[:active].nil? ? true : opts[:active]
|
548
|
+
@size = 8
|
549
|
+
end
|
550
|
+
|
551
|
+
def parse(str, offset=0)
|
552
|
+
if @active and str.size >= offset + @size
|
553
|
+
super(str, offset)
|
554
|
+
@value = str[self.offset, self.length]
|
555
|
+
@size
|
556
|
+
else
|
557
|
+
0
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def serialize
|
562
|
+
super if @active
|
563
|
+
end
|
564
|
+
|
565
|
+
def value
|
566
|
+
@value
|
567
|
+
end
|
568
|
+
|
569
|
+
def value=(val)
|
570
|
+
@value = val
|
571
|
+
self.length = self.allocated = val.size
|
572
|
+
end
|
573
|
+
|
574
|
+
def data_size
|
575
|
+
@active ? @value.size : 0
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
# @private false
|
580
|
+
class Message < FieldSet
|
581
|
+
class << Message
|
582
|
+
def parse(str)
|
583
|
+
m = Type0.new
|
584
|
+
m.parse(str)
|
585
|
+
case m.type
|
586
|
+
when 1
|
587
|
+
t = Type1.parse(str)
|
588
|
+
when 2
|
589
|
+
t = Type2.parse(str)
|
590
|
+
when 3
|
591
|
+
t = Type3.parse(str)
|
592
|
+
else
|
593
|
+
raise ArgumentError, "unknown type: #{m.type}"
|
594
|
+
end
|
595
|
+
t
|
596
|
+
end
|
597
|
+
|
598
|
+
def decode64(str)
|
599
|
+
parse(Base64.decode64(str))
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
def has_flag?(flag)
|
604
|
+
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
|
605
|
+
end
|
606
|
+
|
607
|
+
def set_flag(flag)
|
608
|
+
self[:flag].value |= FLAGS[flag]
|
609
|
+
end
|
610
|
+
|
611
|
+
def dump_flags
|
612
|
+
FLAG_KEYS.each{ |k| print(k, "=", flag?(k), "\n") }
|
613
|
+
end
|
614
|
+
|
615
|
+
def serialize
|
616
|
+
deflag
|
617
|
+
super + security_buffers.map{|n, f| f.value}.join
|
618
|
+
end
|
619
|
+
|
620
|
+
def encode64
|
621
|
+
Base64.encode64(serialize).gsub(/\n/, '')
|
622
|
+
end
|
623
|
+
|
624
|
+
def decode64(str)
|
625
|
+
parse(Base64.decode64(str))
|
626
|
+
end
|
627
|
+
|
628
|
+
alias head_size size
|
629
|
+
|
630
|
+
def data_size
|
631
|
+
security_buffers.inject(0){|sum, a| sum += a[1].data_size}
|
632
|
+
end
|
633
|
+
|
634
|
+
def size
|
635
|
+
head_size + data_size
|
636
|
+
end
|
637
|
+
|
638
|
+
|
639
|
+
def security_buffers
|
640
|
+
@alist.find_all{|n, f| f.instance_of?(SecurityBuffer)}
|
641
|
+
end
|
642
|
+
|
643
|
+
def deflag
|
644
|
+
security_buffers.inject(head_size){|cur, a|
|
645
|
+
a[1].offset = cur
|
646
|
+
cur += a[1].data_size
|
647
|
+
}
|
648
|
+
end
|
649
|
+
|
650
|
+
def data_edge
|
651
|
+
security_buffers.map{ |n, f| f.active ? f.offset : size}.min
|
652
|
+
end
|
653
|
+
|
654
|
+
# sub class definitions
|
655
|
+
class Type0 < Message
|
656
|
+
string :sign, {:size => 8, :value => SSP_SIGN}
|
657
|
+
int32LE :type, {:value => 0}
|
658
|
+
end
|
659
|
+
|
660
|
+
# @private false
|
661
|
+
class Type1 < Message
|
662
|
+
|
663
|
+
string :sign, {:size => 8, :value => SSP_SIGN}
|
664
|
+
int32LE :type, {:value => 1}
|
665
|
+
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
|
666
|
+
security_buffer :domain, {:value => ""}
|
667
|
+
security_buffer :workstation, {:value => Socket.gethostname }
|
668
|
+
string :padding, {:size => 0, :value => "", :active => false }
|
669
|
+
|
670
|
+
class << Type1
|
671
|
+
# Parses a Type 1 Message
|
672
|
+
# @param [String] str A string containing Type 1 data
|
673
|
+
# @return [Type1] The parsed Type 1 message
|
674
|
+
def parse(str)
|
675
|
+
t = new
|
676
|
+
t.parse(str)
|
677
|
+
t
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
# @!visibility private
|
682
|
+
def parse(str)
|
683
|
+
super(str)
|
684
|
+
enable(:domain) if has_flag?(:DOMAIN_SUPPLIED)
|
685
|
+
enable(:workstation) if has_flag?(:WORKSTATION_SUPPLIED)
|
686
|
+
super(str)
|
687
|
+
if ( (len = data_edge - head_size) > 0)
|
688
|
+
self.padding = "\0" * len
|
689
|
+
super(str)
|
690
|
+
end
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
|
695
|
+
# @private false
|
696
|
+
class Type2 < Message
|
697
|
+
|
698
|
+
string :sign, {:size => 8, :value => SSP_SIGN}
|
699
|
+
int32LE :type, {:value => 2}
|
700
|
+
security_buffer :target_name, {:size => 0, :value => ""}
|
701
|
+
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE2]}
|
702
|
+
int64LE :challenge, {:value => 0}
|
703
|
+
int64LE :context, {:value => 0, :active => false}
|
704
|
+
security_buffer :target_info, {:value => "", :active => false}
|
705
|
+
string :padding, {:size => 0, :value => "", :active => false }
|
706
|
+
|
707
|
+
class << Type2
|
708
|
+
# Parse a Type 2 packet
|
709
|
+
# @param [String] str A string containing Type 2 data
|
710
|
+
# @return [Type2]
|
711
|
+
def parse(str)
|
712
|
+
t = new
|
713
|
+
t.parse(str)
|
714
|
+
t
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
# @!visibility private
|
719
|
+
def parse(str)
|
720
|
+
super(str)
|
721
|
+
if has_flag?(:TARGET_INFO)
|
722
|
+
enable(:context)
|
723
|
+
enable(:target_info)
|
724
|
+
super(str)
|
725
|
+
end
|
726
|
+
if ( (len = data_edge - head_size) > 0)
|
727
|
+
self.padding = "\0" * len
|
728
|
+
super(str)
|
729
|
+
end
|
730
|
+
end
|
731
|
+
|
732
|
+
# Generates a Type 3 response based on the Type 2 Information
|
733
|
+
# @return [Type3]
|
734
|
+
# @option arg [String] :username The username to authenticate with
|
735
|
+
# @option arg [String] :password The user's password
|
736
|
+
# @option arg [String] :domain ('') The domain to authenticate to
|
737
|
+
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
|
738
|
+
# @option opt [Boolean] :use_default_target (False) Use the domain supplied by the server in the Type 2 packet
|
739
|
+
# @note An empty :domain option authenticates to the local machine.
|
740
|
+
# @note The :use_default_target has presidence over the :domain option
|
741
|
+
def response(arg, opt = {})
|
742
|
+
usr = arg[:user]
|
743
|
+
pwd = arg[:password]
|
744
|
+
domain = arg[:domain] ? arg[:domain] : ""
|
745
|
+
if usr.nil? or pwd.nil?
|
746
|
+
raise ArgumentError, "user and password have to be supplied"
|
747
|
+
end
|
748
|
+
|
749
|
+
if opt[:workstation]
|
750
|
+
ws = opt[:workstation]
|
751
|
+
else
|
752
|
+
ws = Socket.gethostname
|
753
|
+
end
|
754
|
+
|
755
|
+
if opt[:client_challenge]
|
756
|
+
cc = opt[:client_challenge]
|
757
|
+
else
|
758
|
+
cc = rand(MAX64)
|
759
|
+
end
|
760
|
+
cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
|
761
|
+
opt[:client_challenge] = cc
|
762
|
+
|
763
|
+
if has_flag?(:OEM) and opt[:unicode]
|
764
|
+
usr = NTLM::EncodeUtil.decode_utf16le(usr)
|
765
|
+
pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
|
766
|
+
ws = NTLM::EncodeUtil.decode_utf16le(ws)
|
767
|
+
domain = NTLM::EncodeUtil.decode_utf16le(domain)
|
768
|
+
opt[:unicode] = false
|
769
|
+
end
|
770
|
+
|
771
|
+
if has_flag?(:UNICODE) and !opt[:unicode]
|
772
|
+
usr = NTLM::EncodeUtil.encode_utf16le(usr)
|
773
|
+
pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
|
774
|
+
ws = NTLM::EncodeUtil.encode_utf16le(ws)
|
775
|
+
domain = NTLM::EncodeUtil.encode_utf16le(domain)
|
776
|
+
opt[:unicode] = true
|
777
|
+
end
|
778
|
+
|
779
|
+
if opt[:use_default_target]
|
780
|
+
domain = self.target_name
|
781
|
+
end
|
782
|
+
|
783
|
+
ti = self.target_info
|
784
|
+
|
785
|
+
chal = self[:challenge].serialize
|
786
|
+
|
787
|
+
if opt[:ntlmv2]
|
788
|
+
ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
|
789
|
+
lm_res = NTLM::lmv2_response(ar, opt)
|
790
|
+
ntlm_res = NTLM::ntlmv2_response(ar, opt)
|
791
|
+
elsif has_flag?(:NTLM2_KEY)
|
792
|
+
ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
|
793
|
+
lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
|
794
|
+
else
|
795
|
+
lm_res = NTLM::lm_response(pwd, chal)
|
796
|
+
ntlm_res = NTLM::ntlm_response(pwd, chal)
|
797
|
+
end
|
798
|
+
|
799
|
+
Type3.create({
|
800
|
+
:lm_response => lm_res,
|
801
|
+
:ntlm_response => ntlm_res,
|
802
|
+
:domain => domain,
|
803
|
+
:user => usr,
|
804
|
+
:workstation => ws,
|
805
|
+
:flag => self.flag
|
806
|
+
})
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
# @private false
|
811
|
+
class Type3 < Message
|
812
|
+
|
813
|
+
string :sign, {:size => 8, :value => SSP_SIGN}
|
814
|
+
int32LE :type, {:value => 3}
|
815
|
+
security_buffer :lm_response, {:value => ""}
|
816
|
+
security_buffer :ntlm_response, {:value => ""}
|
817
|
+
security_buffer :domain, {:value => ""}
|
818
|
+
security_buffer :user, {:value => ""}
|
819
|
+
security_buffer :workstation, {:value => ""}
|
820
|
+
security_buffer :session_key, {:value => "", :active => false }
|
821
|
+
int64LE :flag, {:value => 0, :active => false }
|
822
|
+
|
823
|
+
class << Type3
|
824
|
+
# Parse a Type 3 packet
|
825
|
+
# @param [String] str A string containing Type 3 data
|
826
|
+
# @return [Type2]
|
827
|
+
def parse(str)
|
828
|
+
t = new
|
829
|
+
t.parse(str)
|
830
|
+
t
|
831
|
+
end
|
832
|
+
|
833
|
+
# Builds a Type 3 packet
|
834
|
+
# @note All options must be properly encoded with either unicode or oem encoding
|
835
|
+
# @return [Type3]
|
836
|
+
# @option arg [String] :lm_response The LM hash
|
837
|
+
# @option arg [String] :ntlm_response The NTLM hash
|
838
|
+
# @option arg [String] :domain The domain to authenticate to
|
839
|
+
# @option arg [String] :workstation The name of the calling workstation
|
840
|
+
# @option arg [String] :session_key The session key
|
841
|
+
# @option arg [Integer] :flag Flags for the packet
|
842
|
+
def create(arg, opt ={})
|
843
|
+
t = new
|
844
|
+
t.lm_response = arg[:lm_response]
|
845
|
+
t.ntlm_response = arg[:ntlm_response]
|
846
|
+
t.domain = arg[:domain]
|
847
|
+
t.user = arg[:user]
|
848
|
+
|
849
|
+
if arg[:workstation]
|
850
|
+
t.workstation = arg[:workstation]
|
851
|
+
end
|
852
|
+
|
853
|
+
if arg[:session_key]
|
854
|
+
t.enable(:session_key)
|
855
|
+
t.session_key = arg[session_key]
|
856
|
+
end
|
857
|
+
|
858
|
+
if arg[:flag]
|
859
|
+
t.enable(:session_key)
|
860
|
+
t.enable(:flag)
|
861
|
+
t.flag = arg[:flag]
|
862
|
+
end
|
863
|
+
t
|
864
|
+
end
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
end
|
869
|
+
end
|