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