github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/upstreamproxy/go-ntlm/ntlm/message_challenge.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 "encoding/binary" 8 "encoding/hex" 9 "errors" 10 "fmt" 11 ) 12 13 type ChallengeMessage struct { 14 // sig - 8 bytes 15 Signature []byte 16 // message type - 4 bytes 17 MessageType uint32 18 // targetname - 12 bytes 19 TargetName *PayloadStruct 20 // negotiate flags - 4bytes 21 NegotiateFlags uint32 22 // server challenge - 8 bytes 23 ServerChallenge []byte 24 25 // MS-NLMP and Davenport disagree a little on the next few fields and how optional they are 26 // This is what Davenport has to say: 27 // As with the Type 1 message, there are a few versions of the Type 2 that have been observed: 28 // 29 // Version 1 -- The Context, Target Information, and OS Version structure are all omitted. The data block 30 // (containing only the contents of the Target Name security buffer) begins at offset 32. This form 31 // is seen in older Win9x-based systems, and is roughly documented in the Open Group's ActiveX reference 32 // documentation (Section 11.2.3). 33 // 34 // Version 2 -- The Context and Target Information fields are present, but the OS Version structure is not. 35 // The data block begins after the Target Information header, at offset 48. This form is seen in most out-of-box 36 // shipping versions of Windows. 37 // 38 // Version 3 -- The Context, Target Information, and OS Version structure are all present. The data block begins 39 // after the OS Version structure, at offset 56. Again, the buffers may be empty (yielding a zero-length data block). 40 // This form was introduced in a relatively recent Service Pack, and is seen on currently-patched versions of Windows 2000, 41 // Windows XP, and Windows 2003. 42 43 // reserved - 8 bytes (set to 0). This field is also known as 'context' in the davenport documentation 44 Reserved []byte 45 46 // targetinfo - 12 bytes 47 TargetInfoPayloadStruct *PayloadStruct 48 TargetInfo *AvPairs 49 50 // version - 8 bytes 51 Version *VersionStruct 52 // payload - variable 53 Payload []byte 54 } 55 56 func ParseChallengeMessage(body []byte) (*ChallengeMessage, error) { 57 challenge := new(ChallengeMessage) 58 59 // [Psiphon] 60 // Don't panic on malformed remote input. 61 if len(body) < 40 { 62 return nil, errors.New("invalid challenge message") 63 } 64 65 challenge.Signature = body[0:8] 66 if !bytes.Equal(challenge.Signature, []byte("NTLMSSP\x00")) { 67 return challenge, errors.New("Invalid NTLM message signature") 68 } 69 70 challenge.MessageType = binary.LittleEndian.Uint32(body[8:12]) 71 if challenge.MessageType != 2 { 72 return challenge, errors.New("Invalid NTLM message type should be 0x00000002 for challenge message") 73 } 74 75 var err error 76 77 challenge.TargetName, err = ReadStringPayload(12, body) 78 if err != nil { 79 return nil, err 80 } 81 82 challenge.NegotiateFlags = binary.LittleEndian.Uint32(body[20:24]) 83 84 challenge.ServerChallenge = body[24:32] 85 86 challenge.Reserved = body[32:40] 87 88 challenge.TargetInfoPayloadStruct, err = ReadBytePayload(40, body) 89 if err != nil { 90 return nil, err 91 } 92 93 challenge.TargetInfo, err = ReadAvPairs(challenge.TargetInfoPayloadStruct.Payload) 94 if err != nil { 95 return nil, err 96 } 97 98 offset := 48 99 100 if NTLMSSP_NEGOTIATE_VERSION.IsSet(challenge.NegotiateFlags) { 101 102 // [Psiphon] 103 // Don't panic on malformed remote input. 104 if len(body) < offset+8 { 105 return nil, errors.New("invalid challenge message") 106 } 107 108 challenge.Version, err = ReadVersionStruct(body[offset : offset+8]) 109 if err != nil { 110 return nil, err 111 } 112 offset = offset + 8 113 } 114 115 // [Psiphon] 116 // Don't panic on malformed remote input. 117 if len(body) < offset { 118 return nil, errors.New("invalid challenge message") 119 } 120 121 challenge.Payload = body[offset:] 122 123 return challenge, nil 124 } 125 126 func (c *ChallengeMessage) Bytes() []byte { 127 payloadLen := int(c.TargetName.Len + c.TargetInfoPayloadStruct.Len) 128 messageLen := 8 + 4 + 8 + 4 + 8 + 8 + 8 + 8 129 payloadOffset := uint32(messageLen) 130 131 messageBytes := make([]byte, 0, messageLen+payloadLen) 132 buffer := bytes.NewBuffer(messageBytes) 133 134 buffer.Write(c.Signature) 135 binary.Write(buffer, binary.LittleEndian, c.MessageType) 136 137 c.TargetName.Offset = payloadOffset 138 buffer.Write(c.TargetName.Bytes()) 139 payloadOffset += uint32(c.TargetName.Len) 140 141 binary.Write(buffer, binary.LittleEndian, c.NegotiateFlags) 142 buffer.Write(c.ServerChallenge) 143 buffer.Write(make([]byte, 8)) 144 145 c.TargetInfoPayloadStruct.Offset = payloadOffset 146 buffer.Write(c.TargetInfoPayloadStruct.Bytes()) 147 payloadOffset += uint32(c.TargetInfoPayloadStruct.Len) 148 149 // if(c.Version != nil) { 150 buffer.Write(c.Version.Bytes()) 151 // } else { 152 // buffer.Write(make([]byte, 8)) 153 //} 154 155 // Write out the payloads 156 buffer.Write(c.TargetName.Payload) 157 buffer.Write(c.TargetInfoPayloadStruct.Payload) 158 159 return buffer.Bytes() 160 } 161 162 func (c *ChallengeMessage) getLowestPayloadOffset() int { 163 payloadStructs := [...]*PayloadStruct{c.TargetName, c.TargetInfoPayloadStruct} 164 165 // Find the lowest offset value 166 lowest := 9999 167 for i := range payloadStructs { 168 p := payloadStructs[i] 169 if p != nil && p.Offset > 0 && int(p.Offset) < lowest { 170 lowest = int(p.Offset) 171 } 172 } 173 174 return lowest 175 } 176 177 func (c *ChallengeMessage) String() string { 178 var buffer bytes.Buffer 179 180 buffer.WriteString("Challenge NTLM Message") 181 buffer.WriteString(fmt.Sprintf("\nPayload Offset: %d Length: %d", c.getLowestPayloadOffset(), len(c.Payload))) 182 buffer.WriteString(fmt.Sprintf("\nTargetName: %s", c.TargetName.String())) 183 buffer.WriteString(fmt.Sprintf("\nServerChallenge: %s", hex.EncodeToString(c.ServerChallenge))) 184 if c.Version != nil { 185 buffer.WriteString(fmt.Sprintf("\nVersion: %s\n", c.Version.String())) 186 } 187 buffer.WriteString("\nTargetInfo") 188 buffer.WriteString(c.TargetInfo.String()) 189 buffer.WriteString(fmt.Sprintf("\nFlags %d\n", c.NegotiateFlags)) 190 buffer.WriteString(FlagsToString(c.NegotiateFlags)) 191 192 return buffer.String() 193 }