1 /*
2 * PROJECT: Authentication Package DLL
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Utils for msv1_0
5 * COPYRIGHT: Copyright 2011 Samuel Serapión
6 * Copyright 2020 Andreas Maier <staubim@quantentunnel.de>
7 */
8
9 #include "../precomp.h"
10
11 #include "wine/debug.h"
12 WINE_DEFAULT_DEBUG_CHANNEL(ntlm);
13
14 #define NTLM_ALLOC_TAG "NTLM"
15 #define NTLM_ALLOC_TAG_SIZE strlen(NTLM_ALLOC_TAG)
16
17 PVOID
NtlmAllocate(_In_ size_t Size,_In_ bool UsePrivateLsaHeap)18 NtlmAllocate(
19 _In_ size_t Size,
20 _In_ bool UsePrivateLsaHeap)
21 {
22 PVOID buffer = NULL;
23
24 if (Size == 0)
25 {
26 ERR("Allocating 0 bytes!\n");
27 return NULL;
28 }
29
30 Size += NTLM_ALLOC_TAG_SIZE;
31
32 switch (NtlmMode)
33 {
34 case NtlmLsaMode:
35 {
36 if (UsePrivateLsaHeap)
37 buffer = LsaFunctions->AllocatePrivateHeap(Size);
38 else
39 buffer = LsaFunctions->AllocateLsaHeap(Size);
40
41 if (buffer != NULL)
42 RtlZeroMemory(buffer, Size);
43 break;
44 }
45 case NtlmUserMode:
46 {
47 buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
48 break;
49 }
50 default:
51 {
52 ERR("NtlmState unknown!\n");
53 break;
54 }
55 }
56
57 memcpy(buffer, NTLM_ALLOC_TAG, NTLM_ALLOC_TAG_SIZE);
58 buffer = (PBYTE)buffer + NTLM_ALLOC_TAG_SIZE;
59
60 return buffer;
61 }
62
63 VOID
NtlmFree(_In_ PVOID Buffer,_In_ bool FromPrivateLsaHeap)64 NtlmFree(
65 _In_ PVOID Buffer,
66 _In_ bool FromPrivateLsaHeap)
67 {
68 if (Buffer)
69 {
70 Buffer = (PBYTE)Buffer - NTLM_ALLOC_TAG_SIZE;
71 ASSERT(memcmp(Buffer, NTLM_ALLOC_TAG, NTLM_ALLOC_TAG_SIZE) == 0);
72 *(char*)Buffer = 'D';
73
74 switch (NtlmMode)
75 {
76 case NtlmLsaMode:
77 {
78 if (FromPrivateLsaHeap)
79 LsaFunctions->FreePrivateHeap(Buffer);
80 else
81 LsaFunctions->FreeLsaHeap(Buffer);
82 break;
83 }
84 case NtlmUserMode:
85 {
86 HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, Buffer);
87 break;
88 }
89 default:
90 {
91 ERR("NtlmState unknown!\n");
92 break;
93 }
94 }
95 }
96 else
97 {
98 ERR("Trying to free NULL!\n");
99 }
100 }
101
102 bool
NtlmUStrAlloc(_Inout_ PUNICODE_STRING Dst,_In_ UINT16 SizeInBytes,_In_ UINT16 InitLength)103 NtlmUStrAlloc(
104 _Inout_ PUNICODE_STRING Dst,
105 _In_ UINT16 SizeInBytes,
106 _In_ UINT16 InitLength)
107 {
108 Dst->Length = InitLength;
109 Dst->MaximumLength = SizeInBytes;
110 Dst->Buffer = NtlmAllocate(SizeInBytes, false);
111 return (Dst->Buffer != NULL);
112 }
113
114 VOID
NtlmUStrFree(_In_ PUNICODE_STRING String)115 NtlmUStrFree(
116 _In_ PUNICODE_STRING String)
117 {
118 if (String == NULL || String->Buffer == NULL || String->MaximumLength == 0)
119 return;
120
121 NtlmFree(String->Buffer, false);
122 String->Buffer = NULL;
123 String->MaximumLength = 0;
124 }
125
126 /**
127 * @brief Helper to fill a WCHAR-String in a struct.
128 * The stringdata is appended to the struct.
129 * The function does not allocate memory.
130 *
131 * @param[in] DataStart
132 * Start address of the struct
133 *
134 * @param[in] DataSize
135 * Size of allocated memory (including payload)
136 *
137 * @param[out] DstDataWPtr
138 * Pointer to the WCHAR* datafield. The address of the data will be written to it.
139 *
140 * @param[in] SrcDataW
141 * Data to write/append at pOffset (payload). pOffset will be increased after writing data.
142 *
143 * @param[in] SrcDataLen
144 * SrcDataLen is the length in bytes without terminator.
145 * if 0 it will be autodetected by assuming a 0-terminating string.
146 *
147 * @param[in,out] AbsoluteOffsetPtr
148 * Current absolute offset. Will be increased by data length.
149 *
150 * @param[in] TerminateWith0
151 * Whether to terminate the string with a NULL-char.
152 *
153 * @return FALSE if something went wrong
154 */
155 static
156 bool
NtlmStructWriteStrW(_In_ PVOID DataStart,_In_ ULONG DataSize,_Out_ PWCHAR * DstDataWPtr,_In_ const WCHAR * SrcDataW,_In_ ULONG SrcDataLen,_Inout_ PBYTE * AbsoluteOffsetPtr,_In_ bool TerminateWith0)157 NtlmStructWriteStrW(
158 _In_ PVOID DataStart,
159 _In_ ULONG DataSize,
160 _Out_ PWCHAR* DstDataWPtr,
161 _In_ const WCHAR* SrcDataW,
162 _In_ ULONG SrcDataLen,
163 _Inout_ PBYTE* AbsoluteOffsetPtr,
164 _In_ bool TerminateWith0)
165 {
166 ULONG SrcDataMaxLen;
167
168 if (SrcDataLen == 0)
169 SrcDataLen = wcslen(SrcDataW) * sizeof(WCHAR);
170
171 SrcDataMaxLen = SrcDataLen;
172 if (TerminateWith0)
173 SrcDataMaxLen += sizeof(WCHAR);
174
175 if (*AbsoluteOffsetPtr < (PBYTE)DataStart)
176 {
177 ERR("Invalid offset\n");
178 return false;
179 }
180
181 if (*AbsoluteOffsetPtr + SrcDataMaxLen > (PBYTE)DataStart + DataSize)
182 {
183 ERR("Out of bounds!\n");
184 return false;
185 }
186
187 memcpy(*AbsoluteOffsetPtr, SrcDataW, SrcDataLen);
188 *DstDataWPtr = (WCHAR*)*AbsoluteOffsetPtr;
189 if (TerminateWith0)
190 (*DstDataWPtr)[SrcDataLen / sizeof(WCHAR)] = 0;
191 *AbsoluteOffsetPtr += SrcDataMaxLen;
192
193 return true;
194 }
195
196 bool
NtlmUStrWriteToStruct(_In_ PVOID DataStart,_In_ ULONG DataSize,_Out_ PUNICODE_STRING DstData,_In_ const PUNICODE_STRING SrcData,_Inout_ PBYTE * AbsoluteOffsetPtr,_In_ bool TerminateWith0)197 NtlmUStrWriteToStruct(
198 _In_ PVOID DataStart,
199 _In_ ULONG DataSize,
200 _Out_ PUNICODE_STRING DstData,
201 _In_ const PUNICODE_STRING SrcData,
202 _Inout_ PBYTE* AbsoluteOffsetPtr,
203 _In_ bool TerminateWith0)
204 {
205 if (!NtlmStructWriteStrW(DataStart,
206 DataSize,
207 &DstData->Buffer,
208 SrcData->Buffer,
209 SrcData->Length,
210 AbsoluteOffsetPtr,
211 TerminateWith0))
212 return false;
213
214 DstData->Length = SrcData->Length;
215 DstData->MaximumLength = SrcData->Length;
216 if (TerminateWith0)
217 SrcData->MaximumLength += sizeof(WCHAR);
218
219 return true;
220 }
221
222 bool
NtlmFixupAndValidateUStr(_Inout_ PUNICODE_STRING String,_In_ ULONG_PTR FixupOffset)223 NtlmFixupAndValidateUStr(
224 _Inout_ PUNICODE_STRING String,
225 _In_ ULONG_PTR FixupOffset)
226 {
227 NTSTATUS Status;
228
229 if (String->Length)
230 {
231 String->Buffer = FIXUP_POINTER(String->Buffer, FixupOffset);
232 String->MaximumLength = String->Length;
233 }
234 else
235 {
236 String->Buffer = NULL;
237 String->MaximumLength = 0;
238 }
239
240 Status = RtlValidateUnicodeString(0, String);
241 return NT_SUCCESS(Status);
242 }
243
244 bool
NtlmFixupAStr(_Inout_ PSTRING String,_In_ ULONG_PTR FixupOffset)245 NtlmFixupAStr(
246 _Inout_ PSTRING String,
247 _In_ ULONG_PTR FixupOffset)
248 {
249 if (String->Length)
250 {
251 String->Buffer = (PCHAR)FIXUP_POINTER(String->Buffer, FixupOffset);
252 String->MaximumLength = String->Length;
253 }
254 else
255 {
256 String->Buffer = NULL;
257 String->MaximumLength = 0;
258 }
259
260 return true;
261 }
262
263 NTSTATUS
NtlmAllocateClientBuffer(_In_ PLSA_CLIENT_REQUEST ClientRequest,_In_ ULONG BufferLength,_Inout_ PNTLM_CLIENT_BUFFER Buffer)264 NtlmAllocateClientBuffer(
265 _In_ PLSA_CLIENT_REQUEST ClientRequest,
266 _In_ ULONG BufferLength,
267 _Inout_ PNTLM_CLIENT_BUFFER Buffer)
268 {
269 NTSTATUS Status = STATUS_SUCCESS;
270
271 if (!Buffer)
272 return STATUS_NO_MEMORY;
273
274 Buffer->LocalBuffer = NtlmAllocate(BufferLength, false);
275 if (!Buffer->LocalBuffer)
276 return STATUS_NO_MEMORY;
277
278 if ((HANDLE)ClientRequest == INVALID_HANDLE_VALUE)
279 {
280 Buffer->ClientBaseAddress = Buffer->LocalBuffer;
281 //if (!ClientBaseAddress)
282 // return STATUS_INSUFFICIENT_RESOURCES;
283 }
284 else
285 {
286 Status = DispatchTable.AllocateClientBuffer(ClientRequest,
287 BufferLength,
288 &Buffer->ClientBaseAddress);
289 if (!NT_SUCCESS(Status))
290 {
291 NtlmFree(Buffer->LocalBuffer, false);
292 Buffer->LocalBuffer = NULL;
293 }
294 //FIXME: Maybe we have to free ClientBaseAddress if something
295 // goes wrong ...? I'm not sure about that ...
296 }
297 return Status;
298 }
299
300 NTSTATUS
NtlmCopyToClientBuffer(_In_ PLSA_CLIENT_REQUEST ClientRequest,_In_ ULONG BufferLength,_Inout_ PNTLM_CLIENT_BUFFER Buffer)301 NtlmCopyToClientBuffer(
302 _In_ PLSA_CLIENT_REQUEST ClientRequest,
303 _In_ ULONG BufferLength,
304 _Inout_ PNTLM_CLIENT_BUFFER Buffer)
305 {
306 NTSTATUS Status = STATUS_SUCCESS;
307
308 if ((HANDLE)ClientRequest == INVALID_HANDLE_VALUE)
309 {
310 // If ClientRequest ist INVALID_HANDLE_VALUE
311 // Buffer->LocalBuffer == Buffer->ClientBaseAddress
312 if (Buffer->ClientBaseAddress != Buffer->LocalBuffer)
313 {
314 ERR("Buffer->ClientBaseAddress != Buffer->LocalBuffer (something must be wrong!)\n");
315 return STATUS_INTERNAL_ERROR;
316 }
317 }
318 else
319 {
320 if (!Buffer->ClientBaseAddress ||
321 !Buffer->LocalBuffer)
322 {
323 ERR("Invalid Buffer - not allocated!\n");
324 return STATUS_NO_MEMORY;
325 }
326 Status = DispatchTable.CopyToClientBuffer(ClientRequest,
327 BufferLength,
328 Buffer->ClientBaseAddress,
329 Buffer->LocalBuffer);
330 }
331 return Status;
332 }
333
334 VOID
NtlmFreeClientBuffer(_In_ PLSA_CLIENT_REQUEST ClientRequest,_In_ bool FreeClientBuffer,_Inout_ PNTLM_CLIENT_BUFFER Buffer)335 NtlmFreeClientBuffer(
336 _In_ PLSA_CLIENT_REQUEST ClientRequest,
337 _In_ bool FreeClientBuffer,
338 _Inout_ PNTLM_CLIENT_BUFFER Buffer)
339 {
340 if (!Buffer->ClientBaseAddress)
341 return;
342
343 if ((HANDLE)ClientRequest == INVALID_HANDLE_VALUE)
344 {
345 if (Buffer->ClientBaseAddress != Buffer->LocalBuffer)
346 {
347 ERR("Buffer->ClientBaseAddress != Buffer->LocalBuffer (something must be wrong!)\n");
348 return;
349 }
350 // LocalBuffer and ClientBaseAddress is the same
351 // so we have only to free it if FreeClientBuffer is TRUE.
352 Buffer->LocalBuffer = NULL;
353 if (FreeClientBuffer)
354 {
355 NtlmFree(Buffer->ClientBaseAddress, false);
356 Buffer->ClientBaseAddress = NULL;
357 }
358 }
359 else
360 {
361 NtlmFree(Buffer->LocalBuffer, false);
362 Buffer->LocalBuffer = NULL;
363 if (FreeClientBuffer)
364 DispatchTable.FreeClientBuffer(ClientRequest, Buffer->ClientBaseAddress);
365 Buffer->ClientBaseAddress = NULL;
366 }
367 }
368