1 package ntlmssp
2
3 import (
4 "bytes"
5 "crypto/rand"
6 "encoding/binary"
7 "encoding/hex"
8 "errors"
9 "strings"
10 "time"
11 )
12
13 type authenicateMessage struct {
14 LmChallengeResponse []byte
15 NtChallengeResponse []byte
16
17 TargetName string
18 UserName string
19
20
21 EncryptedRandomSessionKey []byte
22
23 NegotiateFlags negotiateFlags
24
25 MIC []byte
26 }
27
28 type authenticateMessageFields struct {
29 messageHeader
30 LmChallengeResponse varField
31 NtChallengeResponse varField
32 TargetName varField
33 UserName varField
34 Workstation varField
35 _ [8]byte
36 NegotiateFlags negotiateFlags
37 }
38
39 func (m authenicateMessage) MarshalBinary() ([]byte, error) {
40 if !m.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEUNICODE) {
41 return nil, errors.New("Only unicode is supported")
42 }
43
44 target, user := toUnicode(m.TargetName), toUnicode(m.UserName)
45 workstation := toUnicode("")
46
47 ptr := binary.Size(&authenticateMessageFields{})
48 f := authenticateMessageFields{
49 messageHeader: newMessageHeader(3),
50 NegotiateFlags: m.NegotiateFlags,
51 LmChallengeResponse: newVarField(&ptr, len(m.LmChallengeResponse)),
52 NtChallengeResponse: newVarField(&ptr, len(m.NtChallengeResponse)),
53 TargetName: newVarField(&ptr, len(target)),
54 UserName: newVarField(&ptr, len(user)),
55 Workstation: newVarField(&ptr, len(workstation)),
56 }
57
58 f.NegotiateFlags.Unset(negotiateFlagNTLMSSPNEGOTIATEVERSION)
59
60 b := bytes.Buffer{}
61 if err := binary.Write(&b, binary.LittleEndian, &f); err != nil {
62 return nil, err
63 }
64 if err := binary.Write(&b, binary.LittleEndian, &m.LmChallengeResponse); err != nil {
65 return nil, err
66 }
67 if err := binary.Write(&b, binary.LittleEndian, &m.NtChallengeResponse); err != nil {
68 return nil, err
69 }
70 if err := binary.Write(&b, binary.LittleEndian, &target); err != nil {
71 return nil, err
72 }
73 if err := binary.Write(&b, binary.LittleEndian, &user); err != nil {
74 return nil, err
75 }
76 if err := binary.Write(&b, binary.LittleEndian, &workstation); err != nil {
77 return nil, err
78 }
79
80 return b.Bytes(), nil
81 }
82
83
84
85 func ProcessChallenge(challengeMessageData []byte, user, password string, domainNeeded bool) ([]byte, error) {
86 if user == "" && password == "" {
87 return nil, errors.New("Anonymous authentication not supported")
88 }
89
90 var cm challengeMessage
91 if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
92 return nil, err
93 }
94
95 if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
96 return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
97 }
98 if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
99 return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
100 }
101
102 if !domainNeeded {
103 cm.TargetName = ""
104 }
105
106 am := authenicateMessage{
107 UserName: user,
108 TargetName: cm.TargetName,
109 NegotiateFlags: cm.NegotiateFlags,
110 }
111
112 timestamp := cm.TargetInfo[avIDMsvAvTimestamp]
113 if timestamp == nil {
114 ft := uint64(time.Now().UnixNano()) / 100
115 ft += 116444736000000000
116 timestamp = make([]byte, 8)
117 binary.LittleEndian.PutUint64(timestamp, ft)
118 }
119
120 clientChallenge := make([]byte, 8)
121 rand.Reader.Read(clientChallenge)
122
123 ntlmV2Hash := getNtlmV2Hash(password, user, cm.TargetName)
124
125 am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
126 cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
127
128 if cm.TargetInfoRaw == nil {
129 am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash,
130 cm.ServerChallenge[:], clientChallenge)
131 }
132 return am.MarshalBinary()
133 }
134
135 func ProcessChallengeWithHash(challengeMessageData []byte, user, hash string) ([]byte, error) {
136 if user == "" && hash == "" {
137 return nil, errors.New("Anonymous authentication not supported")
138 }
139
140 var cm challengeMessage
141 if err := cm.UnmarshalBinary(challengeMessageData); err != nil {
142 return nil, err
143 }
144
145 if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATELMKEY) {
146 return nil, errors.New("Only NTLM v2 is supported, but server requested v1 (NTLMSSP_NEGOTIATE_LM_KEY)")
147 }
148 if cm.NegotiateFlags.Has(negotiateFlagNTLMSSPNEGOTIATEKEYEXCH) {
149 return nil, errors.New("Key exchange requested but not supported (NTLMSSP_NEGOTIATE_KEY_EXCH)")
150 }
151
152 am := authenicateMessage{
153 UserName: user,
154 TargetName: cm.TargetName,
155 NegotiateFlags: cm.NegotiateFlags,
156 }
157
158 timestamp := cm.TargetInfo[avIDMsvAvTimestamp]
159 if timestamp == nil {
160 ft := uint64(time.Now().UnixNano()) / 100
161 ft += 116444736000000000
162 timestamp = make([]byte, 8)
163 binary.LittleEndian.PutUint64(timestamp, ft)
164 }
165
166 clientChallenge := make([]byte, 8)
167 rand.Reader.Read(clientChallenge)
168
169 hashParts := strings.Split(hash, ":")
170 if len(hashParts) > 1 {
171 hash = hashParts[1]
172 }
173 hashBytes, err := hex.DecodeString(hash)
174 if err != nil {
175 return nil, err
176 }
177 ntlmV2Hash := hmacMd5(hashBytes, toUnicode(strings.ToUpper(user)+cm.TargetName))
178
179 am.NtChallengeResponse = computeNtlmV2Response(ntlmV2Hash,
180 cm.ServerChallenge[:], clientChallenge, timestamp, cm.TargetInfoRaw)
181
182 if cm.TargetInfoRaw == nil {
183 am.LmChallengeResponse = computeLmV2Response(ntlmV2Hash,
184 cm.ServerChallenge[:], clientChallenge)
185 }
186 return am.MarshalBinary()
187 }
188
View as plain text