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 WLog_DBG(TAG, "Trying to open SAM file '%s'", filename);
108 if (readOnly)
109 fp = winpr_fopen(filename, "r");
110 else
111 {
112 fp = winpr_fopen(filename, "r+");
113
114 if (!fp)
115 fp = winpr_fopen(filename, "w+");
116 }
117
118 if (!fp)
119 goto fail;
120
121 sam = (WINPR_SAM*)calloc(1, sizeof(WINPR_SAM));
122 if (!sam)
123 goto fail;
124
125 sam->readOnly = readOnly;
126 sam->fp = fp;
127
128fail:
129 if (!fp || !sam)
130 WLog_DBG(TAG, "Could not open SAM file '%s'", filename);
131
132 if (fp && !sam)
133 (void)fclose(fp);
134 winpr_zfree(allocatedFileName);
135 return sam;
136}
137
138static void SamLookupFinish(WINPR_SAM* sam)
139{
140 winpr_znfree(sam->buffer, sam->bufferlen);
141 sam->buffer = nullptr;
142 sam->bufferlen = 0;
143 sam->line = nullptr;
144}
145
146WINPR_ATTR_NODISCARD
147static BOOL SamLookupStart(WINPR_SAM* sam)
148{
149 size_t readSize = 0;
150 INT64 fileSize = 0;
151
152 if (!sam || !sam->fp)
153 return FALSE;
154
155 if (_fseeki64(sam->fp, 0, SEEK_END) != 0)
156 return FALSE;
157 fileSize = _ftelli64(sam->fp);
158 if (_fseeki64(sam->fp, 0, SEEK_SET) != 0)
159 return FALSE;
160
161 if (fileSize < 1)
162 return FALSE;
163
164 sam->context = nullptr;
165 const size_t allocsize = WINPR_ASSERTING_INT_CAST(size_t, fileSize) + 2ull;
166 sam->buffer = (char*)calloc(allocsize, 1);
167
168 if (!sam->buffer)
169 return FALSE;
170 sam->bufferlen = allocsize;
171
172 readSize = fread(sam->buffer, sam->bufferlen - 2ull, 1, sam->fp);
173
174 if (!readSize)
175 {
176 if (!ferror(sam->fp))
177 readSize = (size_t)fileSize;
178 }
179
180 if (readSize < 1)
181 {
182 SamLookupFinish(sam);
183 return FALSE;
184 }
185
186 sam->buffer[fileSize] = '\n';
187 sam->buffer[fileSize + 1] = '\0';
188 sam->line = strtok_s(sam->buffer, "\n", &sam->context);
189 return TRUE;
190}
191
192static void SamResetEntryUser(WINPR_SAM_ENTRY* entry)
193{
194 if (!entry)
195 return;
196
197 if (entry->UserLength > 0)
198 winpr_znfree(entry->User, entry->UserLength);
199 entry->User = nullptr;
200 entry->UserLength = 0;
201
202 if (entry->DomainLength > 0)
203 winpr_znfree(entry->Domain, entry->DomainLength);
204 entry->Domain = nullptr;
205 entry->DomainLength = 0;
206}
207
208WINPR_ATTR_NODISCARD
209static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
210{
211 char* p[5] = WINPR_C_ARRAY_INIT;
212 size_t count = 0;
213
214 if (!sam || !entry || !sam->line)
215 return FALSE;
216
217 char* cur = sam->line;
218
219 while ((cur = strchr(cur, ':')) != nullptr)
220 {
221 count++;
222 cur++;
223 }
224
225 if (count < 4)
226 goto fail;
227
228 p[0] = sam->line;
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));
235
236 if ((LmHashLength != 0) && (LmHashLength != 32))
237 goto fail;
238
239 if ((NtHashLength != 0) && (NtHashLength != 32))
240 goto fail;
241
242 entry->UserLength = (UINT32)(p[1] - p[0] - 1);
243 entry->User = (LPSTR)malloc(entry->UserLength + 1);
244
245 if (!entry->User)
246 goto fail;
247
248 entry->User[entry->UserLength] = '\0';
249 entry->DomainLength = (UINT32)(p[2] - p[1] - 1);
250 memcpy(entry->User, p[0], entry->UserLength);
251
252 if (entry->DomainLength > 0)
253 {
254 entry->Domain = (LPSTR)malloc(entry->DomainLength + 1);
255
256 if (!entry->Domain)
257 goto fail;
258
259 memcpy(entry->Domain, p[1], entry->DomainLength);
260 entry->Domain[entry->DomainLength] = '\0';
261 }
262 else
263 entry->Domain = nullptr;
264
265 if (LmHashLength == 32)
266 {
267 const size_t rc =
268 winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash));
269 if (rc != 16)
270 goto fail;
271 }
272
273 if (NtHashLength == 32)
274 {
275 const size_t rc = winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash,
276 sizeof(entry->NtHash));
277 if (rc != 16)
278 goto fail;
279 }
280
281 return TRUE;
282
283fail:
284 SamResetEntryUser(entry);
285 return FALSE;
286}
287
288void SamFreeEntry(WINPR_ATTR_UNUSED WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
289{
290 if (!entry)
291 return;
292 SamResetEntry(entry);
293 winpr_znfree(entry, sizeof(WINPR_SAM_ENTRY));
294}
295
296void SamResetEntry(WINPR_SAM_ENTRY* entry)
297{
298 if (!entry)
299 return;
300
301 SamResetEntryUser(entry);
302
303 ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
304 ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
305}
306
307WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
308 UINT32 DomainLength)
309{
310 size_t length = 0;
311 BOOL found = FALSE;
312 WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
313 WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY));
314
315 if (!entry || !search)
316 goto fail;
317
318 if (!SamLookupStart(sam))
319 goto fail;
320
321 while (sam->line != nullptr)
322 {
323 length = strlen(sam->line);
324
325 if (length > 1)
326 {
327 if (sam->line[0] != '#')
328 {
329 if (!SamReadEntry(sam, entry))
330 {
331 goto out_fail;
332 }
333
334 if (SamAreEntriesEqual(entry, search))
335 {
336 found = 1;
337 break;
338 }
339 }
340 }
341
342 SamResetEntry(entry);
343 sam->line = strtok_s(nullptr, "\n", &sam->context);
344 }
345
346out_fail:
347 SamLookupFinish(sam);
348fail:
349 SamFreeEntry(sam, search);
350
351 if (!found)
352 {
353 SamFreeEntry(sam, entry);
354 return nullptr;
355 }
356
357 return entry;
358}
359
360WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
361 UINT32 DomainLength)
362{
363 WINPR_SAM_ENTRY* entry = nullptr;
364 char* utfUser = nullptr;
365 char* utfDomain = nullptr;
366 size_t userCharLen = 0;
367 size_t domainCharLen = 0;
368
369 utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen);
370 if (!utfUser)
371 goto fail;
372 if (DomainLength > 0)
373 {
374 utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen);
375 if (!utfDomain)
376 goto fail;
377 }
378 entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
379 if (entry)
380 {
381 SamResetEntryUser(entry);
382
383 if (User)
384 entry->User = (char*)winpr_wcsndup(User, UserLength / sizeof(WCHAR));
385 entry->UserLength = UserLength;
386 if (Domain)
387 entry->Domain = (char*)winpr_wcsndup(Domain, DomainLength / sizeof(WCHAR));
388 entry->DomainLength = DomainLength;
389 }
390fail:
391 winpr_znfree(utfUser, userCharLen);
392 winpr_znfree(utfDomain, domainCharLen);
393 return entry;
394}
395
396void SamClose(WINPR_SAM* sam)
397{
398 if (!sam)
399 return;
400
401 if (sam->fp)
402 (void)fclose(sam->fp);
403 winpr_znfree(sam, sizeof(WINPR_SAM));
404}