github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/upstreamproxy/go-ntlm/ntlm/ntlmv2.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  	"encoding/binary"
     9  	"errors"
    10  	"strings"
    11  	"time"
    12  )
    13  
    14  /*******************************
    15   Shared Session Data and Methods
    16  *******************************/
    17  
    18  type V2Session struct {
    19  	SessionData
    20  }
    21  
    22  func (n *V2Session) SetUserInfo(username string, password string, domain string) {
    23  	n.user = username
    24  	n.password = password
    25  	n.userDomain = domain
    26  }
    27  
    28  func (n *V2Session) GetUserInfo() (string, string, string) {
    29  	return n.user, n.password, n.userDomain
    30  }
    31  
    32  func (n *V2Session) SetMode(mode Mode) {
    33  	n.mode = mode
    34  }
    35  
    36  func (n *V2Session) Version() int {
    37  	return 2
    38  }
    39  
    40  func (n *V2Session) fetchResponseKeys() (err error) {
    41  	// Usually at this point we'd go out to Active Directory and get these keys
    42  	// Here we are assuming we have the information locally
    43  	n.responseKeyLM = lmowfv2(n.user, n.password, n.userDomain)
    44  	n.responseKeyNT = ntowfv2(n.user, n.password, n.userDomain)
    45  	return
    46  }
    47  
    48  func (n *V2ServerSession) GetSessionData() *SessionData {
    49  	return &n.SessionData
    50  }
    51  
    52  // Define ComputeResponse(NegFlg, ResponseKeyNT, ResponseKeyLM, CHALLENGE_MESSAGE.ServerChallenge, ClientChallenge, Time, ServerName)
    53  // ServerNameBytes - The NtChallengeResponseFields.NTLMv2_RESPONSE.NTLMv2_CLIENT_CHALLENGE.AvPairs field structure of the AUTHENTICATE_MESSAGE payload.
    54  func (n *V2Session) computeExpectedResponses(timestamp []byte, avPairBytes []byte) (err error) {
    55  	temp := concat([]byte{0x01}, []byte{0x01}, zeroBytes(6), timestamp, n.clientChallenge, zeroBytes(4), avPairBytes, zeroBytes(4))
    56  	ntProofStr := hmacMd5(n.responseKeyNT, concat(n.serverChallenge, temp))
    57  	n.ntChallengeResponse = concat(ntProofStr, temp)
    58  	n.lmChallengeResponse = concat(hmacMd5(n.responseKeyLM, concat(n.serverChallenge, n.clientChallenge)), n.clientChallenge)
    59  	n.sessionBaseKey = hmacMd5(n.responseKeyNT, ntProofStr)
    60  	return
    61  }
    62  
    63  func (n *V2Session) computeKeyExchangeKey() (err error) {
    64  	n.keyExchangeKey = n.sessionBaseKey
    65  	return
    66  }
    67  
    68  func (n *V2Session) calculateKeys(ntlmRevisionCurrent uint8) (err error) {
    69  	// This lovely piece of code comes courtesy of an the excellent Open Document support system from MSFT
    70  	// In order to calculate the keys correctly when the client has set the NTLMRevisionCurrent to 0xF (15)
    71  	// We must treat the flags as if NTLMSSP_NEGOTIATE_LM_KEY is set.
    72  	// This information is not contained (at least currently, until they correct it) in the MS-NLMP document
    73  	if ntlmRevisionCurrent == 15 {
    74  		n.NegotiateFlags = NTLMSSP_NEGOTIATE_LM_KEY.Set(n.NegotiateFlags)
    75  	}
    76  
    77  	n.ClientSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Client")
    78  	n.ServerSigningKey = signKey(n.NegotiateFlags, n.exportedSessionKey, "Server")
    79  	n.ClientSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Client")
    80  	n.ServerSealingKey = sealKey(n.NegotiateFlags, n.exportedSessionKey, "Server")
    81  	return
    82  }
    83  
    84  func (n *V2Session) Seal(message []byte) ([]byte, error) {
    85  	return nil, nil
    86  }
    87  func (n *V2Session) Sign(message []byte) ([]byte, error) {
    88  	return nil, nil
    89  }
    90  
    91  func NtlmVCommonMac(message []byte, sequenceNumber int, sealingKey, signingKey []byte, NegotiateFlags uint32) []byte {
    92  	var handle *rc4P.Cipher
    93  	// TODO: Need to keep track of the sequence number for connection oriented NTLM
    94  	if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) && NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(NegotiateFlags) {
    95  		handle, _ = reinitSealingKey(sealingKey, sequenceNumber)
    96  	} else if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) {
    97  		// CONOR: Reinitializing the rc4 cipher on every requst, but not using the
    98  		// algorithm as described in the MS-NTLM document. Just reinitialize it directly.
    99  		handle, _ = rc4Init(sealingKey)
   100  	}
   101  	sig := mac(NegotiateFlags, handle, signingKey, uint32(sequenceNumber), message)
   102  	return sig.Bytes()
   103  }
   104  
   105  func NtlmV2Mac(message []byte, sequenceNumber int, handle *rc4P.Cipher, sealingKey, signingKey []byte, NegotiateFlags uint32) []byte {
   106  	// TODO: Need to keep track of the sequence number for connection oriented NTLM
   107  	if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) && NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.IsSet(NegotiateFlags) {
   108  		handle, _ = reinitSealingKey(sealingKey, sequenceNumber)
   109  	} else if NTLMSSP_NEGOTIATE_DATAGRAM.IsSet(NegotiateFlags) {
   110  		// CONOR: Reinitializing the rc4 cipher on every requst, but not using the
   111  		// algorithm as described in the MS-NTLM document. Just reinitialize it directly.
   112  		handle, _ = rc4Init(sealingKey)
   113  	}
   114  	sig := mac(NegotiateFlags, handle, signingKey, uint32(sequenceNumber), message)
   115  	return sig.Bytes()
   116  }
   117  
   118  func (n *V2ServerSession) Mac(message []byte, sequenceNumber int) ([]byte, error) {
   119  	mac := NtlmV2Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags)
   120  	return mac, nil
   121  }
   122  
   123  func (n *V2ServerSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) {
   124  	mac := NtlmV2Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags)
   125  	return MacsEqual(mac, expectedMac), nil
   126  }
   127  
   128  func (n *V2ClientSession) Mac(message []byte, sequenceNumber int) ([]byte, error) {
   129  	mac := NtlmV2Mac(message, sequenceNumber, n.clientHandle, n.ClientSealingKey, n.ClientSigningKey, n.NegotiateFlags)
   130  	return mac, nil
   131  }
   132  
   133  func (n *V2ClientSession) VerifyMac(message, expectedMac []byte, sequenceNumber int) (bool, error) {
   134  	mac := NtlmV2Mac(message, sequenceNumber, n.serverHandle, n.ServerSealingKey, n.ServerSigningKey, n.NegotiateFlags)
   135  	return MacsEqual(mac, expectedMac), nil
   136  }
   137  
   138  /**************
   139   Server Session
   140  **************/
   141  
   142  type V2ServerSession struct {
   143  	V2Session
   144  }
   145  
   146  func (n *V2ServerSession) SetServerChallenge(challenge []byte) {
   147  	n.serverChallenge = challenge
   148  }
   149  
   150  func (n *V2ServerSession) ProcessNegotiateMessage(nm *NegotiateMessage) (err error) {
   151  	n.negotiateMessage = nm
   152  	return
   153  }
   154  
   155  func (n *V2ServerSession) GenerateChallengeMessage() (cm *ChallengeMessage, err error) {
   156  	cm = new(ChallengeMessage)
   157  	cm.Signature = []byte("NTLMSSP\x00")
   158  	cm.MessageType = uint32(2)
   159  	cm.TargetName, _ = CreateBytePayload(make([]byte, 0))
   160  
   161  	flags := uint32(0)
   162  	flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags)
   163  	flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags)
   164  	flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags)
   165  	flags = NTLMSSP_NEGOTIATE_TARGET_INFO.Set(flags)
   166  	flags = NTLMSSP_NEGOTIATE_IDENTIFY.Set(flags)
   167  	flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags)
   168  	flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags)
   169  	flags = NTLMSSP_NEGOTIATE_DATAGRAM.Set(flags)
   170  	flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags)
   171  	flags = NTLMSSP_REQUEST_TARGET.Set(flags)
   172  	flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags)
   173  	flags = NTLMSSP_NEGOTIATE_128.Set(flags)
   174  
   175  	cm.NegotiateFlags = flags
   176  
   177  	n.serverChallenge = randomBytes(8)
   178  	cm.ServerChallenge = n.serverChallenge
   179  	cm.Reserved = make([]byte, 8)
   180  
   181  	// Create the AvPairs we need
   182  	pairs := new(AvPairs)
   183  	pairs.AddAvPair(MsvAvNbDomainName, utf16FromString("REUTERS"))
   184  	pairs.AddAvPair(MsvAvNbComputerName, utf16FromString("UKBP-CBTRMFE06"))
   185  	pairs.AddAvPair(MsvAvDnsDomainName, utf16FromString("Reuters.net"))
   186  	pairs.AddAvPair(MsvAvDnsComputerName, utf16FromString("ukbp-cbtrmfe06.Reuters.net"))
   187  	pairs.AddAvPair(MsvAvDnsTreeName, utf16FromString("Reuters.net"))
   188  	pairs.AddAvPair(MsvAvEOL, make([]byte, 0))
   189  	cm.TargetInfo = pairs
   190  	cm.TargetInfoPayloadStruct, _ = CreateBytePayload(pairs.Bytes())
   191  
   192  	cm.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)}
   193  	return cm, nil
   194  }
   195  
   196  func (n *V2ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (err error) {
   197  	n.authenticateMessage = am
   198  	n.NegotiateFlags = am.NegotiateFlags
   199  	n.clientChallenge = am.ClientChallenge()
   200  	n.encryptedRandomSessionKey = am.EncryptedRandomSessionKey.Payload
   201  	// Ignore the values used in SetUserInfo and use these instead from the authenticate message
   202  	// They should always be correct (I hope)
   203  	n.user = am.UserName.String()
   204  	n.userDomain = am.DomainName.String()
   205  
   206  	err = n.fetchResponseKeys()
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	timestamp := am.NtlmV2Response.NtlmV2ClientChallenge.TimeStamp
   212  	avPairsBytes := am.NtlmV2Response.NtlmV2ClientChallenge.AvPairs.Bytes()
   213  
   214  	err = n.computeExpectedResponses(timestamp, avPairsBytes)
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	if !bytes.Equal(am.NtChallengeResponseFields.Payload, n.ntChallengeResponse) {
   220  		if !bytes.Equal(am.LmChallengeResponse.Payload, n.lmChallengeResponse) {
   221  			return errors.New("Could not authenticate")
   222  		}
   223  	}
   224  
   225  	err = n.computeKeyExchangeKey()
   226  	if err != nil {
   227  		return err
   228  	}
   229  
   230  	n.mic = am.Mic
   231  	am.Mic = zeroBytes(16)
   232  
   233  	err = n.computeExportedSessionKey()
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	if am.Version == nil {
   239  		//UGH not entirely sure how this could possibly happen, going to put this in for now
   240  		//TODO investigate if this ever is really happening
   241  		am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)}
   242  
   243  	}
   244  
   245  	err = n.calculateKeys(am.Version.NTLMRevisionCurrent)
   246  	if err != nil {
   247  		return err
   248  	}
   249  
   250  	n.clientHandle, err = rc4Init(n.ClientSealingKey)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	n.serverHandle, err = rc4Init(n.ServerSealingKey)
   255  	if err != nil {
   256  		return err
   257  	}
   258  
   259  	return nil
   260  }
   261  
   262  func (n *V2ServerSession) computeExportedSessionKey() (err error) {
   263  	if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) {
   264  		n.exportedSessionKey, err = rc4K(n.keyExchangeKey, n.encryptedRandomSessionKey)
   265  		if err != nil {
   266  			return err
   267  		}
   268  		// TODO: Calculate mic correctly. This calculation is not producing the right results now
   269  		// n.calculatedMic = HmacMd5(n.exportedSessionKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes))
   270  	} else {
   271  		n.exportedSessionKey = n.keyExchangeKey
   272  		// TODO: Calculate mic correctly. This calculation is not producing the right results now
   273  		// n.calculatedMic = HmacMd5(n.keyExchangeKey, concat(n.challengeMessage.Payload, n.authenticateMessage.Bytes))
   274  	}
   275  	return nil
   276  }
   277  
   278  /*************
   279   Client Session
   280  **************/
   281  
   282  type V2ClientSession struct {
   283  	V2Session
   284  }
   285  
   286  func (n *V2ClientSession) GenerateNegotiateMessage() (nm *NegotiateMessage, err error) {
   287  	nm = new(NegotiateMessage)
   288  	nm.Signature = []byte("NTLMSSP\x00")
   289  	nm.MessageType = uint32(1)
   290  	nm.NegotiateFlags = NEGOTIATE_FLAG_REQUEST_NTLMv1 |
   291  		NEGOTIATE_FLAG_REQUEST_NTLM2_SESSION |
   292  		NEGOTIATE_FLAG_REQUEST_VERSION |
   293  		NEGOTIATE_FLAG_REQUEST_ALWAYS_SIGN |
   294  		NEGOTIATE_FLAG_REQUEST_128BIT_KEY_EXCH |
   295  		NEGOTIATE_FLAG_REQUEST_56BIT_ENCRYPTION |
   296  		NEGOTIATE_FLAG_REQUEST_UNICODE_ENCODING
   297  
   298  	nm.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)}
   299  	return nm, nil
   300  }
   301  
   302  func (n *V2ClientSession) ProcessChallengeMessage(cm *ChallengeMessage) (err error) {
   303  	n.challengeMessage = cm
   304  	n.serverChallenge = cm.ServerChallenge
   305  	n.clientChallenge = randomBytes(8)
   306  
   307  	// Set up the default flags for processing the response. These are the flags that we will return
   308  	// in the authenticate message
   309  	flags := uint32(0)
   310  	flags = NTLMSSP_NEGOTIATE_KEY_EXCH.Set(flags)
   311  	flags = NTLMSSP_NEGOTIATE_VERSION.Set(flags)
   312  	flags = NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY.Set(flags)
   313  	flags = NTLMSSP_NEGOTIATE_TARGET_INFO.Set(flags)
   314  	flags = NTLMSSP_NEGOTIATE_IDENTIFY.Set(flags)
   315  	flags = NTLMSSP_NEGOTIATE_ALWAYS_SIGN.Set(flags)
   316  	flags = NTLMSSP_NEGOTIATE_NTLM.Set(flags)
   317  	flags = NTLMSSP_NEGOTIATE_DATAGRAM.Set(flags)
   318  	flags = NTLMSSP_NEGOTIATE_SIGN.Set(flags)
   319  	flags = NTLMSSP_REQUEST_TARGET.Set(flags)
   320  	flags = NTLMSSP_NEGOTIATE_UNICODE.Set(flags)
   321  	flags = NTLMSSP_NEGOTIATE_128.Set(flags)
   322  
   323  	n.NegotiateFlags = flags
   324  
   325  	err = n.fetchResponseKeys()
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	timestamp := timeToWindowsFileTime(time.Now())
   331  	err = n.computeExpectedResponses(timestamp, cm.TargetInfoPayloadStruct.Payload)
   332  	if err != nil {
   333  		return err
   334  	}
   335  
   336  	err = n.computeKeyExchangeKey()
   337  	if err != nil {
   338  		return err
   339  	}
   340  
   341  	err = n.computeEncryptedSessionKey()
   342  	if err != nil {
   343  		return err
   344  	}
   345  
   346  	// [Psiphon]
   347  	// Copied almost verbatim from ProcessAuthenticateMessage. We received a
   348  	// panic report due to a nil cm.Version.
   349  	if cm.Version == nil {
   350  		//UGH not entirely sure how this could possibly happen, going to put this in for now
   351  		//TODO investigate if this ever is really happening
   352  		cm.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)}
   353  
   354  	}
   355  
   356  	err = n.calculateKeys(cm.Version.NTLMRevisionCurrent)
   357  	if err != nil {
   358  		return err
   359  	}
   360  
   361  	n.clientHandle, err = rc4Init(n.ClientSealingKey)
   362  	if err != nil {
   363  		return err
   364  	}
   365  	n.serverHandle, err = rc4Init(n.ServerSealingKey)
   366  	if err != nil {
   367  		return err
   368  	}
   369  	return nil
   370  }
   371  
   372  func (n *V2ClientSession) GenerateAuthenticateMessage() (am *AuthenticateMessage, err error) {
   373  	am = new(AuthenticateMessage)
   374  	am.Signature = []byte("NTLMSSP\x00")
   375  	am.MessageType = uint32(3)
   376  	am.LmChallengeResponse, _ = CreateBytePayload(n.lmChallengeResponse)
   377  	am.NtChallengeResponseFields, _ = CreateBytePayload(n.ntChallengeResponse)
   378  	am.DomainName, _ = CreateStringPayload(n.userDomain)
   379  	am.UserName, _ = CreateStringPayload(n.user)
   380  
   381  	// [Psiphon]
   382  	// Set a blank workstation value, which is less distinguishable than the previous hard-coded value.
   383  	// See also: https://github.com/Azure/go-ntlmssp/commit/5e29b886690f00c76b876ae9ab8e31e7c3509203.
   384  
   385  	am.Workstation, _ = CreateStringPayload("")
   386  	am.EncryptedRandomSessionKey, _ = CreateBytePayload(n.encryptedRandomSessionKey)
   387  	am.NegotiateFlags = n.NegotiateFlags
   388  	am.Mic = make([]byte, 16)
   389  	am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: 0x0F}
   390  	return am, nil
   391  }
   392  
   393  func (n *V2ClientSession) computeEncryptedSessionKey() (err error) {
   394  	if NTLMSSP_NEGOTIATE_KEY_EXCH.IsSet(n.NegotiateFlags) {
   395  		n.exportedSessionKey = randomBytes(16)
   396  		n.encryptedRandomSessionKey, err = rc4K(n.keyExchangeKey, n.exportedSessionKey)
   397  		if err != nil {
   398  			return err
   399  		}
   400  	} else {
   401  		n.encryptedRandomSessionKey = n.keyExchangeKey
   402  	}
   403  	return nil
   404  }
   405  
   406  /********************************
   407   NTLM V2 Password hash functions
   408  *********************************/
   409  
   410  // Define ntowfv2(Passwd, User, UserDom) as
   411  func ntowfv2(user string, passwd string, userDom string) []byte {
   412  	concat := utf16FromString(strings.ToUpper(user) + userDom)
   413  	return hmacMd5(md4(utf16FromString(passwd)), concat)
   414  }
   415  
   416  // Define lmowfv2(Passwd, User, UserDom) as
   417  func lmowfv2(user string, passwd string, userDom string) []byte {
   418  	return ntowfv2(user, passwd, userDom)
   419  }
   420  
   421  /********************************
   422   Helper functions
   423  *********************************/
   424  
   425  func timeToWindowsFileTime(t time.Time) []byte {
   426  	var ll int64
   427  	ll = (int64(t.Unix()) * int64(10000000)) + int64(116444736000000000)
   428  	buffer := bytes.NewBuffer(make([]byte, 0, 8))
   429  	binary.Write(buffer, binary.LittleEndian, ll)
   430  	return buffer.Bytes()
   431  }