github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/upstreamproxy/go-ntlm/ntlm/ntlmv1.go (about) 1 //Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information 2 3 package ntlm 4 5 import ( 6 "bytes" 7 rc4P "crypto/rc4" 8 "errors" 9 "strings" 10 ) 11 12 /******************************* 13 Shared Session Data and Methods 14 *******************************/ 15 16 type V1Session struct { 17 SessionData 18 } 19 20 func (n *V1Session) SetUserInfo(username string, password string, domain string) { 21 n.user = username 22 n.password = password 23 n.userDomain = domain 24 } 25 26 func (n *V1Session) GetUserInfo() (string, string, string) { 27 return n.user, n.password, n.userDomain 28 } 29 30 func (n *V1Session) SetMode(mode Mode) { 31 n.mode = mode 32 } 33 34 func (n *V1Session) Version() int { 35 return 1 36 } 37 38 func (n *V1Session) fetchResponseKeys() (err error) { 39 n.responseKeyLM, err = lmowfv1(n.password) 40 if err != nil { 41 return err 42 } 43 n.responseKeyNT = ntowfv1(n.password) 44 return 45 } 46 47 func (n *V1Session) computeExpectedResponses() (err error) { 48 if NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(n.NegotiateFlags) { 49 n.ntChallengeResponse, err = desL(n.responseKeyNT, md5(concat(n.serverChallenge, n.clientChallenge))[0:8]) 50 if err != nil { 51 return err 52 } 53 n.lmChallengeResponse = concat(n.clientChallenge, make([]byte, 16)) 54 } else { 55 n.ntChallengeResponse, err = desL(n.responseKeyNT, n.serverChallenge) 56 if err != nil { 57 return err 58 } 59 // NoLMResponseNTLMv1: A Boolean setting that controls using the NTLM response for the LM 60 // response to the server challenge when NTLMv1 authentication is used.<30> 61 // <30> Section 3.1.1.1: The default value of this state variable is TRUE. Windows NT Server 4.0 SP3 62 // does not support providing NTLM instead of LM responses. 63 noLmResponseNtlmV1 := false 64 if noLmResponseNtlmV1 { 65 n.lmChallengeResponse = n.ntChallengeResponse 66 } else { 67 n.lmChallengeResponse, err = desL(n.responseKeyLM, n.serverChallenge) 68 if err != nil { 69 return err 70 } 71 } 72 } 73 74 return nil 75 } 76 77 func (n *V1Session) computeSessionBaseKey() (err error) { 78 n.sessionBaseKey = md4(n.responseKeyNT) 79 return 80 } 81 82 func (n *V1Session) computeKeyExchangeKey() (err error) { 83 if NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(n.NegotiateFlags) { 84 n.keyExchangeKey = hmacMd5(n.sessionBaseKey, concat(n.serverChallenge, n.lmChallengeResponse[0:8])) 85 } else { 86 n.keyExchangeKey, err = kxKey(n.NegotiateFlags, n.sessionBaseKey, n.lmChallengeResponse, n.serverChallenge, n.responseKeyLM) 87 } 88 return 89 } 90 91 func (n *V1Session) calculateKeys(ntlmRevisionCurrent uint8) (err error) { 92 // This lovely piece of code comes courtesy of an the excellent Open Document support system from MSFT 93 // In order to calculate the keys correctly when the client has set the NTLMRevisionCurrent to 0xF (15) 94 // We must treat the flags as if NTLMSSP_NEGOTIATE_LM_KEY is set. 95 // This information is not contained (at least currently, until they correct it) in the MS-NLMP document 96 if ntlmRevisionCurrent == 15 { 97 n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Set(n.NegotiateFlags) 98 } 99 100 n.ClientSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Client") 101 n.ServerSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Server") 102 n.ClientSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Client") 103 n.ServerSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Server") 104 return 105 } 106 107 func (n *V1Session) Seal(message []byte) ([]byte, error) { 108 return nil, nil 109 } 110 111 func (n *V1Session) Sign(message []byte) ([]byte, error) { 112 return nil, nil 113 } 114 115 func ntlmV1Mac(message []byte, sequenceNumber int, handle *rc4P.Cipher, sealingKey, signingKey []byte, NegotiateFlags uint32) []byte { 116 // TODO: Need to keep track of the sequence number for connection oriented NTLM 117 if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) && NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(NegotiateFlags) { 118 handle, _ = reinitSealingKey(sealingKey, sequenceNumber) 119 } else if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) { 120 // CONOR: Reinitializing the rc4 cipher on every requst, but not using the 121 // algorithm as described in the MS-NTLM document. Just reinitialize it directly. 122 handle, _ = rc4Init(sealingKey) 123 } 124 sig := mac(NegotiateFlags, handle, signingKey, uint32(sequenceNumber), message) 125 return sig.Bytes() 126 } 127 128 func (n *V1ServerSession) Mac(message []byte, sequenceNumber int) ([]byte, error) { 129 mac := ntlmV1Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags) 130 return mac, nil 131 } 132 133 func (n *V1ClientSession) Mac(message []byte, sequenceNumber int) ([]byte, error) { 134 mac := ntlmV1Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags) 135 return mac, nil 136 } 137 138 func (n *V1ServerSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) { 139 mac := ntlmV1Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags) 140 return MacsEqual(mac, expectedMac), nil 141 } 142 143 func (n *V1ClientSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) { 144 mac := ntlmV1Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags) 145 return MacsEqual(mac, expectedMac), nil 146 } 147 148 /************** 149 Server Session 150 **************/ 151 152 type V1ServerSession struct { 153 V1Session 154 } 155 156 func (n *V1ServerSession) ProcessNegotiateMessage(nm *NegotiateMessage) (err error) { 157 n.negotiateMessage = nm 158 return 159 } 160 161 func (n *V1ServerSession) GenerateChallengeMessage() (cm *ChallengeMessage, err error) { 162 // TODO: Generate this challenge message 163 return 164 } 165 166 func (n *V1ServerSession) SetServerChallenge(challenge []byte) { 167 n.serverChallenge = challenge 168 } 169 170 func (n *V1ServerSession) GetSessionData() *SessionData { 171 return &n.SessionData 172 } 173 174 func (n *V1ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (err error) { 175 n.authenticateMessage = am 176 n.NegotiateFlags = am.NegotiateFlags 177 n.clientChallenge = am.ClientChallenge() 178 n.encryptedRandomSessionKey = am.EncryptedRandomSessionKey.Payload 179 // Ignore the values used in SetUserInfo and use these instead from the authenticate message 180 // They should always be correct (I hope) 181 n.user = am.UserName.String() 182 n.userDomain = am.DomainName.String() 183 184 err = n.fetchResponseKeys() 185 if err != nil { 186 return err 187 } 188 189 err = n.computeExpectedResponses() 190 if err != nil { 191 return err 192 } 193 194 err = n.computeSessionBaseKey() 195 if err != nil { 196 return err 197 } 198 199 err = n.computeKeyExchangeKey() 200 if err != nil { 201 return err 202 } 203 204 if !bytes.Equal(am.NtChallengeResponseFields.Payload, n.ntChallengeResponse) { 205 // There is a bug with the steps in MS-NLMP. In section 3.2.5.1.2 it says you should fall through 206 // to compare the lmChallengeResponse if the ntChallengeRepsonse fails, but with extended session security 207 // this would *always* pass because the lmChallengeResponse and expectedLmChallengeRepsonse will always 208 // be the same 209 if !bytes.Equal(am.LmChallengeResponse.Payload, n.lmChallengeResponse) || NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(n.NegotiateFlags) { 210 return errors.New("Could not authenticate") 211 } 212 } 213 214 n.mic = am.Mic 215 am.Mic = zeroBytes(16) 216 217 err = n.computeExportedSessionKey() 218 if err != nil { 219 return err 220 } 221 222 if am.Version == nil { 223 //UGH not entirely sure how this could possibly happen, going to put this in for now 224 //TODO investigate if this ever is really happening 225 am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} 226 } 227 228 err = n.calculateKeys(am.Version.NTLMRevisionCurrent) 229 if err != nil { 230 return err 231 } 232 233 n.clientHandle, err = rc4Init(n.ClientSealingKey) 234 if err != nil { 235 return err 236 } 237 n.serverHandle, err = rc4Init(n.ServerSealingKey) 238 if err != nil { 239 return err 240 } 241 242 return nil 243 } 244 245 func (n *V1ServerSession) computeExportedSessionKey() (err error) { 246 if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) { 247 n.exportedSessionKey, err = rc4K(n.keyExchangeKey, n.encryptedRandomSessionKey) 248 if err != nil { 249 return err 250 } 251 // TODO: Calculate mic correctly. This calculation is not producing the right results now 252 // n.calculatedMic = HmacMd5(n.exportedSessionKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes)) 253 } else { 254 n.exportedSessionKey = n.keyExchangeKey 255 // TODO: Calculate mic correctly. This calculation is not producing the right results now 256 // n.calculatedMic = HmacMd5(n.keyExchangeKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes)) 257 } 258 return nil 259 } 260 261 /************* 262 Client Session 263 **************/ 264 265 type V1ClientSession struct { 266 V1Session 267 } 268 269 func (n *V1ClientSession) GenerateNegotiateMessage() (nm *NegotiateMessage, err error) { 270 return nil, nil 271 } 272 273 func (n *V1ClientSession) ProcessChallengeMessage(cm *ChallengeMessage) (err error) { 274 n.challengeMessage = cm 275 n.serverChallenge = cm.ServerChallenge 276 n.clientChallenge = randomBytes(8) 277 278 // Set up the default flags for processing the response. These are the flags that we will return 279 // in the authenticate message 280 flags := uint32(0) 281 flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags) 282 // NOTE: Unsetting this flag in order to get the server to generate the signatures we can recognize 283 flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags) 284 flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags) 285 flags = NTLMSSP_NEGOTIATE_TARGET_INFO.Set(flags) 286 flags = NTLMSSP_NEGOTIATE_IDENTIFY.Set(flags) 287 flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags) 288 flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags) 289 flags = NTLMSSP_NEGOTIATE_DATAGRAM.Set(flags) 290 flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags) 291 flags = NTLMSSP_REQUEST_TARGET.Set(flags) 292 flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags) 293 294 n.NegotiateFlags = flags 295 296 err = n.fetchResponseKeys() 297 if err != nil { 298 return err 299 } 300 301 err = n.computeExpectedResponses() 302 if err != nil { 303 return err 304 } 305 306 err = n.computeSessionBaseKey() 307 if err != nil { 308 return err 309 } 310 311 err = n.computeKeyExchangeKey() 312 if err != nil { 313 return err 314 } 315 316 err = n.computeEncryptedSessionKey() 317 if err != nil { 318 return err 319 } 320 321 err = n.calculateKeys(cm.Version.NTLMRevisionCurrent) 322 if err != nil { 323 return err 324 } 325 326 n.clientHandle, err = rc4Init(n.ClientSealingKey) 327 if err != nil { 328 return err 329 } 330 n.serverHandle, err = rc4Init(n.ServerSealingKey) 331 if err != nil { 332 return err 333 } 334 335 return nil 336 } 337 338 func (n *V1ClientSession) GenerateAuthenticateMessage() (am *AuthenticateMessage, err error) { 339 am = new(AuthenticateMessage) 340 am.Signature = []byte("NTLMSSP\x00") 341 am.MessageType = uint32(3) 342 am.LmChallengeResponse, _ = CreateBytePayload(n.lmChallengeResponse) 343 am.NtChallengeResponseFields, _ = CreateBytePayload(n.ntChallengeResponse) 344 am.DomainName, _ = CreateStringPayload(n.userDomain) 345 am.UserName, _ = CreateStringPayload(n.user) 346 347 // [Psiphon] 348 // Set a blank workstation value, which is less distinguishable than the previous hard-coded value. 349 // See also: https://github.com/Azure/go-ntlmssp/commit/5e29b886690f00c76b876ae9ab8e31e7c3509203. 350 351 am.Workstation, _ = CreateStringPayload("") 352 am.EncryptedRandomSessionKey, _ = CreateBytePayload(n.encryptedRandomSessionKey) 353 am.NegotiateFlags = n.NegotiateFlags 354 am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} 355 return am, nil 356 } 357 358 func (n *V1ClientSession) computeEncryptedSessionKey() (err error) { 359 if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) { 360 n.exportedSessionKey = randomBytes(16) 361 n.encryptedRandomSessionKey, err = rc4K(n.keyExchangeKey, n.exportedSessionKey) 362 if err != nil { 363 return err 364 } 365 } else { 366 n.encryptedRandomSessionKey = n.keyExchangeKey 367 } 368 return nil 369 } 370 371 /******************************** 372 NTLM V1 Password hash functions 373 *********************************/ 374 375 func ntowfv1(passwd string) []byte { 376 return md4(utf16FromString(passwd)) 377 } 378 379 // ConcatenationOf( DES( UpperCase( Passwd)[0..6],"KGS!@#$%"), DES( UpperCase( Passwd)[7..13],"KGS!@#$%")) 380 func lmowfv1(passwd string) ([]byte, error) { 381 asciiPassword := []byte(strings.ToUpper(passwd)) 382 keyBytes := zeroPaddedBytes(asciiPassword, 0, 14) 383 384 first, err := des(keyBytes[0:7], []byte("KGS!@#$%")) 385 if err != nil { 386 return nil, err 387 } 388 second, err := des(keyBytes[7:14], []byte("KGS!@#$%")) 389 if err != nil { 390 return nil, err 391 } 392 393 return append(first, second...), nil 394 }