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  }