File: type3.rb

package info (click to toggle)
ruby-ntlm 0.6.3-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 408 kB
  • sloc: ruby: 2,663; makefile: 6
file content (131 lines) | stat: -rw-r--r-- 4,458 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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
module Net
  module NTLM
    class Message

      # @private false
      class Type3 < Message

        string          :sign,          {:size => 8, :value => SSP_SIGN}
        int32LE         :type,          {:value => 3}
        security_buffer :lm_response,   {:value => ""}
        security_buffer :ntlm_response, {:value => ""}
        security_buffer :domain,        {:value => ""}
        security_buffer :user,          {:value => ""}
        security_buffer :workstation,   {:value => ""}
        security_buffer :session_key,   {:value => "", :active => false }
        int32LE         :flag,          {:value => 0, :active => false }
        string          :os_version,    {:size => 8, :active => false }

        class << Type3
          # Builds a Type 3 packet
          # @note All options must be properly encoded with either unicode or oem encoding
          # @return [Type3]
          # @option arg [String] :lm_response The LM hash
          # @option arg [String] :ntlm_response The NTLM hash
          # @option arg [String] :domain The domain to authenticate to
          # @option arg [String] :workstation The name of the calling workstation
          # @option arg [String] :session_key The session key
          # @option arg [Integer] :flag Flags for the packet
          def create(arg, opt ={})
            t = new
            t.lm_response = arg[:lm_response]
            t.ntlm_response = arg[:ntlm_response]
            t.domain = arg[:domain]
            t.user = arg[:user]

            if arg[:workstation]
              t.workstation = arg[:workstation]
            end

            if arg[:session_key]
              t.enable(:session_key)
              t.session_key = arg[:session_key]
            end

            if arg[:flag]
              t.enable(:session_key)
              t.enable(:flag)
              t.flag = arg[:flag]
            end
            t
          end
        end

        # @param server_challenge (see #password?)
        def blank_password?(server_challenge)
          password?('', server_challenge)
        end

        # @param password [String]
        # @param server_challenge [String] The server's {Type2#challenge challenge} from the
        #   {Type2} message for which this object is a response.
        # @return [true] if +password+ was the password used to generate this
        #   {Type3} message
        # @return [false] otherwise
        def password?(password, server_challenge)
          case ntlm_version
          when :ntlm2_session
            ntlm2_session_password?(password, server_challenge)
          when :ntlmv2
            ntlmv2_password?(password, server_challenge)
          else
            raise
          end
        end

        # @return [Symbol]
        def ntlm_version
          if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
            :ntlm2_session
          elsif ntlm_response.size == 24
            :ntlmv1
          elsif ntlm_response.size > 24
            :ntlmv2
          end
        end

        private

        def ntlm2_session_password?(password, server_challenge)
          hash = ntlm_response
          _lm, empty_hash = NTLM.ntlm2_session(
            {
              :ntlm_hash => NTLM.ntlm_hash(password),
              :challenge => server_challenge,
            },
            {
              :client_challenge => lm_response[0,8]
            }
          )
          hash == empty_hash
        end

        def ntlmv2_password?(password, server_challenge)

          # The first 16 bytes of the ntlm_response are the HMAC of the blob
          # that follows it.
          blob = Blob.new
          blob.parse(ntlm_response[16..-1])

          empty_hash = NTLM.ntlmv2_response(
            {
              # user and domain came from the serialized data here, so
              # they're already unicode
              :ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
              :challenge => server_challenge,
              :target_info => blob.target_info
            },
            {
              :client_challenge => blob.challenge,
              # The blob's timestamp is already in milliseconds since 1601,
              # so convert it back to epoch time first
              :timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
            }
          )

          empty_hash == ntlm_response
        end
      end
    end
  end
end