FreeRDP
Loading...
Searching...
No Matches
sam.c
1
20#include <winpr/config.h>
21#include <winpr/path.h>
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <winpr/wtypes.h>
28#include <winpr/crt.h>
29#include <winpr/sam.h>
30#include <winpr/cast.h>
31#include <winpr/print.h>
32#include <winpr/file.h>
33
34#include "../log.h"
35#include "../utils.h"
36
37#ifdef WINPR_HAVE_UNISTD_H
38#include <unistd.h>
39#endif
40
41#define TAG WINPR_TAG("utils")
42
43struct winpr_sam
44{
45 FILE* fp;
46 char* line;
47 char* buffer;
48 size_t bufferlen;
49 char* context;
50 BOOL readOnly;
51};
52
53WINPR_ATTR_MALLOC(SamFreeEntry, 1)
54static WINPR_SAM_ENTRY* SamEntryFromDataA(LPCSTR User, DWORD UserLength, LPCSTR Domain,
55 DWORD DomainLength)
56{
57 WINPR_SAM_ENTRY* entry = calloc(1, sizeof(WINPR_SAM_ENTRY));
58 if (!entry)
59 return nullptr;
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;
66 return entry;
67}
68
69WINPR_ATTR_NODISCARD
70static BOOL SamAreEntriesEqual(const WINPR_SAM_ENTRY* a, const WINPR_SAM_ENTRY* b)
71{
72 if (!a || !b)
73 return FALSE;
74 if (a->UserLength != b->UserLength)
75 return FALSE;
76 if (a->DomainLength != b->DomainLength)
77 return FALSE;
78 if (a->UserLength > 0)
79 {
80 if (!a->User || !b->User)
81 return FALSE;
82 if (strncmp(a->User, b->User, a->UserLength) != 0)
83 return FALSE;
84 }
85 if (a->DomainLength > 0)
86 {
87 if (!a->Domain || !b->Domain)
88 return FALSE;
89 if (strncmp(a->Domain, b->Domain, a->DomainLength) != 0)
90 return FALSE;
91 }
92 return TRUE;
93}
94
95WINPR_SAM* SamOpen(const char* filename, BOOL readOnly)
96{
97 FILE* fp = nullptr;
98 WINPR_SAM* sam = nullptr;
99 char* allocatedFileName = nullptr;
100
101 if (!filename)
102 {
103 allocatedFileName = winpr_GetConfigFilePath(TRUE, "SAM");
104 filename = allocatedFileName;
105 }
106
107 if (readOnly)
108 fp = winpr_fopen(filename, "r");
109 else
110 {
111 fp = winpr_fopen(filename, "r+");
112
113 if (!fp)
114 fp = winpr_fopen(filename, "w+");
115 }
116 winpr_zfree(allocatedFileName);
117
118 if (fp)
119 {
120 sam = (WINPR_SAM*)calloc(1, sizeof(WINPR_SAM));
121
122 if (!sam)
123 {
124 (void)fclose(fp);
125 return nullptr;
126 }
127
128 sam->readOnly = readOnly;
129 sam->fp = fp;
130 }
131 else
132 {
133 WLog_DBG(TAG, "Could not open SAM file!");
134 return nullptr;
135 }
136
137 return sam;
138}
139
140static void SamLookupFinish(WINPR_SAM* sam)
141{
142 winpr_znfree(sam->buffer, sam->bufferlen);
143 sam->buffer = nullptr;
144 sam->bufferlen = 0;
145 sam->line = nullptr;
146}
147
148WINPR_ATTR_NODISCARD
149static BOOL SamLookupStart(WINPR_SAM* sam)
150{
151 size_t readSize = 0;
152 INT64 fileSize = 0;
153
154 if (!sam || !sam->fp)
155 return FALSE;
156
157 if (_fseeki64(sam->fp, 0, SEEK_END) != 0)
158 return FALSE;
159 fileSize = _ftelli64(sam->fp);
160 if (_fseeki64(sam->fp, 0, SEEK_SET) != 0)
161 return FALSE;
162
163 if (fileSize < 1)
164 return FALSE;
165
166 sam->context = nullptr;
167 const size_t allocsize = WINPR_ASSERTING_INT_CAST(size_t, fileSize) + 2ull;
168 sam->buffer = (char*)calloc(allocsize, 1);
169
170 if (!sam->buffer)
171 return FALSE;
172 sam->bufferlen = allocsize;
173
174 readSize = fread(sam->buffer, sam->bufferlen - 2ull, 1, sam->fp);
175
176 if (!readSize)
177 {
178 if (!ferror(sam->fp))
179 readSize = (size_t)fileSize;
180 }
181
182 if (readSize < 1)
183 {
184 SamLookupFinish(sam);
185 return FALSE;
186 }
187
188 sam->buffer[fileSize] = '\n';
189 sam->buffer[fileSize + 1] = '\0';
190 sam->line = strtok_s(sam->buffer, "\n", &sam->context);
191 return TRUE;
192}
193
194static void SamResetEntryUser(WINPR_SAM_ENTRY* entry)
195{
196 if (!entry)
197 return;
198
199 if (entry->UserLength > 0)
200 winpr_znfree(entry->User, entry->UserLength);
201 entry->User = nullptr;
202 entry->UserLength = 0;
203
204 if (entry->DomainLength > 0)
205 winpr_znfree(entry->Domain, entry->DomainLength);
206 entry->Domain = nullptr;
207 entry->DomainLength = 0;
208}
209
210WINPR_ATTR_NODISCARD
211static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
212{
213 char* p[5] = WINPR_C_ARRAY_INIT;
214 size_t count = 0;
215
216 if (!sam || !entry || !sam->line)
217 return FALSE;
218
219 char* cur = sam->line;
220
221 while ((cur = strchr(cur, ':')) != nullptr)
222 {
223 count++;
224 cur++;
225 }
226
227 if (count < 4)
228 goto fail;
229
230 p[0] = sam->line;
231 p[1] = strchr(p[0], ':') + 1;
232 p[2] = strchr(p[1], ':') + 1;
233 p[3] = strchr(p[2], ':') + 1;
234 p[4] = strchr(p[3], ':') + 1;
235 const size_t LmHashLength = WINPR_ASSERTING_INT_CAST(size_t, (p[3] - p[2] - 1));
236 const size_t NtHashLength = WINPR_ASSERTING_INT_CAST(size_t, (p[4] - p[3] - 1));
237
238 if ((LmHashLength != 0) && (LmHashLength != 32))
239 goto fail;
240
241 if ((NtHashLength != 0) && (NtHashLength != 32))
242 goto fail;
243
244 entry->UserLength = (UINT32)(p[1] - p[0] - 1);
245 entry->User = (LPSTR)malloc(entry->UserLength + 1);
246
247 if (!entry->User)
248 goto fail;
249
250 entry->User[entry->UserLength] = '\0';
251 entry->DomainLength = (UINT32)(p[2] - p[1] - 1);
252 memcpy(entry->User, p[0], entry->UserLength);
253
254 if (entry->DomainLength > 0)
255 {
256 entry->Domain = (LPSTR)malloc(entry->DomainLength + 1);
257
258 if (!entry->Domain)
259 goto fail;
260
261 memcpy(entry->Domain, p[1], entry->DomainLength);
262 entry->Domain[entry->DomainLength] = '\0';
263 }
264 else
265 entry->Domain = nullptr;
266
267 if (LmHashLength == 32)
268 {
269 const size_t rc =
270 winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash));
271 if (rc != 16)
272 goto fail;
273 }
274
275 if (NtHashLength == 32)
276 {
277 const size_t rc = winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash,
278 sizeof(entry->NtHash));
279 if (rc != 16)
280 goto fail;
281 }
282
283 return TRUE;
284
285fail:
286 SamResetEntryUser(entry);
287 return FALSE;
288}
289
290void SamFreeEntry(WINPR_ATTR_UNUSED WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
291{
292 if (!entry)
293 return;
294 SamResetEntry(entry);
295 winpr_znfree(entry, sizeof(WINPR_SAM_ENTRY));
296}
297
298void SamResetEntry(WINPR_SAM_ENTRY* entry)
299{
300 if (!entry)
301 return;
302
303 SamResetEntryUser(entry);
304
305 ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
306 ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
307}
308
309WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
310 UINT32 DomainLength)
311{
312 size_t length = 0;
313 BOOL found = FALSE;
314 WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
315 WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY));
316
317 if (!entry || !search)
318 goto fail;
319
320 if (!SamLookupStart(sam))
321 goto fail;
322
323 while (sam->line != nullptr)
324 {
325 length = strlen(sam->line);
326
327 if (length > 1)
328 {
329 if (sam->line[0] != '#')
330 {
331 if (!SamReadEntry(sam, entry))
332 {
333 goto out_fail;
334 }
335
336 if (SamAreEntriesEqual(entry, search))
337 {
338 found = 1;
339 break;
340 }
341 }
342 }
343
344 SamResetEntry(entry);
345 sam->line = strtok_s(nullptr, "\n", &sam->context);
346 }
347
348out_fail:
349 SamLookupFinish(sam);
350fail:
351 SamFreeEntry(sam, search);
352
353 if (!found)
354 {
355 SamFreeEntry(sam, entry);
356 return nullptr;
357 }
358
359 return entry;
360}
361
362WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
363 UINT32 DomainLength)
364{
365 WINPR_SAM_ENTRY* entry = nullptr;
366 char* utfUser = nullptr;
367 char* utfDomain = nullptr;
368 size_t userCharLen = 0;
369 size_t domainCharLen = 0;
370
371 utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen);
372 if (!utfUser)
373 goto fail;
374 if (DomainLength > 0)
375 {
376 utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen);
377 if (!utfDomain)
378 goto fail;
379 }
380 entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
381 if (entry)
382 {
383 SamResetEntryUser(entry);
384
385 if (User)
386 entry->User = (char*)winpr_wcsndup(User, UserLength / sizeof(WCHAR));
387 entry->UserLength = UserLength;
388 if (Domain)
389 entry->Domain = (char*)winpr_wcsndup(Domain, DomainLength / sizeof(WCHAR));
390 entry->DomainLength = DomainLength;
391 }
392fail:
393 winpr_znfree(utfUser, userCharLen);
394 winpr_znfree(utfDomain, domainCharLen);
395 return entry;
396}
397
398void SamClose(WINPR_SAM* sam)
399{
400 if (!sam)
401 return;
402
403 if (sam->fp)
404 (void)fclose(sam->fp);
405 winpr_znfree(sam, sizeof(WINPR_SAM));
406}