xref: /reactos/dll/win32/msv1_0/ntlm/util.c (revision 99dcd6f7)
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