...

Source file src/github.com/Azure/go-ntlmssp/authenticate_message.go

Documentation: github.com/Azure/go-ntlmssp

     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  	// only set if negotiateFlag_NTLMSSP_NEGOTIATE_KEY_EXCH
    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  //ProcessChallenge crafts an AUTHENTICATE message in response to the CHALLENGE message
    84  //that was received from the server
    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 { // no time sent, take current time
   114  		ft := uint64(time.Now().UnixNano()) / 100
   115  		ft += 116444736000000000 // add time between unix & windows offset
   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 { // no time sent, take current time
   160  		ft := uint64(time.Now().UnixNano()) / 100
   161  		ft += 116444736000000000 // add time between unix & windows offset
   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