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 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 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 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 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 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 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 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 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 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 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 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