20#include <winpr/config.h>
21#include <winpr/path.h>
27#include <winpr/wtypes.h>
30#include <winpr/cast.h>
31#include <winpr/print.h>
32#include <winpr/file.h>
37#ifdef WINPR_HAVE_UNISTD_H
41#define TAG WINPR_TAG("utils")
53WINPR_ATTR_MALLOC(SamFreeEntry, 1)
54static WINPR_SAM_ENTRY* SamEntryFromDataA(LPCSTR User, DWORD UserLength, LPCSTR Domain,
57 WINPR_SAM_ENTRY* entry = calloc(1,
sizeof(WINPR_SAM_ENTRY));
60 if (User && (UserLength > 0))
61 entry->User = _strdup(User);
62 entry->UserLength = UserLength;
63 if (Domain && (DomainLength > 0))
64 entry->Domain = _strdup(Domain);
65 entry->DomainLength = DomainLength;
70static BOOL SamAreEntriesEqual(
const WINPR_SAM_ENTRY* a,
const WINPR_SAM_ENTRY* b)
74 if (a->UserLength != b->UserLength)
76 if (a->DomainLength != b->DomainLength)
78 if (a->UserLength > 0)
80 if (!a->User || !b->User)
82 if (strncmp(a->User, b->User, a->UserLength) != 0)
85 if (a->DomainLength > 0)
87 if (!a->Domain || !b->Domain)
89 if (strncmp(a->Domain, b->Domain, a->DomainLength) != 0)
95WINPR_SAM* SamOpen(
const char* filename, BOOL readOnly)
98 WINPR_SAM* sam =
nullptr;
99 char* allocatedFileName =
nullptr;
103 allocatedFileName = winpr_GetConfigFilePath(TRUE,
"SAM");
104 filename = allocatedFileName;
107 WLog_DBG(TAG,
"Trying to open SAM file '%s'", filename);
109 fp = winpr_fopen(filename,
"r");
112 fp = winpr_fopen(filename,
"r+");
115 fp = winpr_fopen(filename,
"w+");
121 sam = (WINPR_SAM*)calloc(1,
sizeof(WINPR_SAM));
125 sam->readOnly = readOnly;
130 WLog_DBG(TAG,
"Could not open SAM file '%s'", filename);
134 winpr_zfree(allocatedFileName);
138static void SamLookupFinish(WINPR_SAM* sam)
140 winpr_znfree(sam->buffer, sam->bufferlen);
141 sam->buffer =
nullptr;
147static BOOL SamLookupStart(WINPR_SAM* sam)
152 if (!sam || !sam->fp)
155 if (_fseeki64(sam->fp, 0, SEEK_END) != 0)
157 fileSize = _ftelli64(sam->fp);
158 if (_fseeki64(sam->fp, 0, SEEK_SET) != 0)
164 sam->context =
nullptr;
165 const size_t allocsize = WINPR_ASSERTING_INT_CAST(
size_t, fileSize) + 2ull;
166 sam->buffer = (
char*)calloc(allocsize, 1);
170 sam->bufferlen = allocsize;
172 readSize = fread(sam->buffer, sam->bufferlen - 2ull, 1, sam->fp);
176 if (!ferror(sam->fp))
177 readSize = (size_t)fileSize;
182 SamLookupFinish(sam);
186 sam->buffer[fileSize] =
'\n';
187 sam->buffer[fileSize + 1] =
'\0';
188 sam->line = strtok_s(sam->buffer,
"\n", &sam->context);
192static void SamResetEntryUser(WINPR_SAM_ENTRY* entry)
197 if (entry->UserLength > 0)
198 winpr_znfree(entry->User, entry->UserLength);
199 entry->User =
nullptr;
200 entry->UserLength = 0;
202 if (entry->DomainLength > 0)
203 winpr_znfree(entry->Domain, entry->DomainLength);
204 entry->Domain =
nullptr;
205 entry->DomainLength = 0;
209static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
211 char* p[5] = WINPR_C_ARRAY_INIT;
214 if (!sam || !entry || !sam->line)
217 char* cur = sam->line;
219 while ((cur = strchr(cur,
':')) !=
nullptr)
229 p[1] = strchr(p[0],
':') + 1;
230 p[2] = strchr(p[1],
':') + 1;
231 p[3] = strchr(p[2],
':') + 1;
232 p[4] = strchr(p[3],
':') + 1;
233 const size_t LmHashLength = WINPR_ASSERTING_INT_CAST(
size_t, (p[3] - p[2] - 1));
234 const size_t NtHashLength = WINPR_ASSERTING_INT_CAST(
size_t, (p[4] - p[3] - 1));
236 if ((LmHashLength != 0) && (LmHashLength != 32))
239 if ((NtHashLength != 0) && (NtHashLength != 32))
242 entry->UserLength = (UINT32)(p[1] - p[0] - 1);
243 entry->User = (LPSTR)malloc(entry->UserLength + 1);
248 entry->User[entry->UserLength] =
'\0';
249 entry->DomainLength = (UINT32)(p[2] - p[1] - 1);
250 memcpy(entry->User, p[0], entry->UserLength);
252 if (entry->DomainLength > 0)
254 entry->Domain = (LPSTR)malloc(entry->DomainLength + 1);
259 memcpy(entry->Domain, p[1], entry->DomainLength);
260 entry->Domain[entry->DomainLength] =
'\0';
263 entry->Domain =
nullptr;
265 if (LmHashLength == 32)
268 winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash,
sizeof(entry->LmHash));
273 if (NtHashLength == 32)
275 const size_t rc = winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash,
276 sizeof(entry->NtHash));
284 SamResetEntryUser(entry);
288void SamFreeEntry(WINPR_ATTR_UNUSED WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
292 SamResetEntry(entry);
293 winpr_znfree(entry,
sizeof(WINPR_SAM_ENTRY));
296void SamResetEntry(WINPR_SAM_ENTRY* entry)
301 SamResetEntryUser(entry);
303 ZeroMemory(entry->LmHash,
sizeof(entry->LmHash));
304 ZeroMemory(entry->NtHash,
sizeof(entry->NtHash));
307WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
312 WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
313 WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1,
sizeof(WINPR_SAM_ENTRY));
315 if (!entry || !search)
318 if (!SamLookupStart(sam))
321 while (sam->line !=
nullptr)
323 length = strlen(sam->line);
327 if (sam->line[0] !=
'#')
329 if (!SamReadEntry(sam, entry))
334 if (SamAreEntriesEqual(entry, search))
342 SamResetEntry(entry);
343 sam->line = strtok_s(
nullptr,
"\n", &sam->context);
347 SamLookupFinish(sam);
349 SamFreeEntry(sam, search);
353 SamFreeEntry(sam, entry);
360WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
363 WINPR_SAM_ENTRY* entry =
nullptr;
364 char* utfUser =
nullptr;
365 char* utfDomain =
nullptr;
366 size_t userCharLen = 0;
367 size_t domainCharLen = 0;
369 utfUser = ConvertWCharNToUtf8Alloc(User, UserLength /
sizeof(WCHAR), &userCharLen);
372 if (DomainLength > 0)
374 utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength /
sizeof(WCHAR), &domainCharLen);
378 entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
381 SamResetEntryUser(entry);
384 entry->User = (
char*)winpr_wcsndup(User, UserLength /
sizeof(WCHAR));
385 entry->UserLength = UserLength;
387 entry->Domain = (
char*)winpr_wcsndup(Domain, DomainLength /
sizeof(WCHAR));
388 entry->DomainLength = DomainLength;
391 winpr_znfree(utfUser, userCharLen);
392 winpr_znfree(utfDomain, domainCharLen);
396void SamClose(WINPR_SAM* sam)
402 (void)fclose(sam->fp);
403 winpr_znfree(sam,
sizeof(WINPR_SAM));