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 return FALSE;
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 return FALSE;
217
218 if ((NtHashLength != 0) && (NtHashLength != 32))
219 return FALSE;
220
221 entry->UserLength = (UINT32)(p[1] - p[0] - 1);
222 entry->User = (LPSTR)malloc(entry->UserLength + 1);
223
224 if (!entry->User)
225 return FALSE;
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 {
237 free(entry->User);
238 entry->User = nullptr;
239 return FALSE;
240 }
241
242 memcpy(entry->Domain, p[1], entry->DomainLength);
243 entry->Domain[entry->DomainLength] = '\0';
244 }
245 else
246 entry->Domain = nullptr;
247
248 if (LmHashLength == 32)
249 {
250 const size_t rc =
251 winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash));
252 if (rc != 16)
253 return FALSE;
254 }
255
256 if (NtHashLength == 32)
257 {
258 const size_t rc = winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash,
259 sizeof(entry->NtHash));
260 if (rc != 16)
261 return FALSE;
262 }
263
264 return TRUE;
265}
266
267void SamFreeEntry(WINPR_ATTR_UNUSED WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
268{
269 SamResetEntry(entry);
270 free(entry);
271}
272
273void SamResetEntry(WINPR_SAM_ENTRY* entry)
274{
275 if (!entry)
276 return;
277
278 if (entry->UserLength)
279 {
280 free(entry->User);
281 entry->User = nullptr;
282 }
283
284 if (entry->DomainLength)
285 {
286 free(entry->Domain);
287 entry->Domain = nullptr;
288 }
289
290 ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
291 ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
292}
293
294WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
295 UINT32 DomainLength)
296{
297 size_t length = 0;
298 BOOL found = FALSE;
299 WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
300 WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY));
301
302 if (!entry || !search)
303 goto fail;
304
305 if (!SamLookupStart(sam))
306 goto fail;
307
308 while (sam->line != nullptr)
309 {
310 length = strlen(sam->line);
311
312 if (length > 1)
313 {
314 if (sam->line[0] != '#')
315 {
316 if (!SamReadEntry(sam, entry))
317 {
318 goto out_fail;
319 }
320
321 if (SamAreEntriesEqual(entry, search))
322 {
323 found = 1;
324 break;
325 }
326 }
327 }
328
329 SamResetEntry(entry);
330 sam->line = strtok_s(nullptr, "\n", &sam->context);
331 }
332
333out_fail:
334 SamLookupFinish(sam);
335fail:
336 SamFreeEntry(sam, search);
337
338 if (!found)
339 {
340 SamFreeEntry(sam, entry);
341 return nullptr;
342 }
343
344 return entry;
345}
346
347WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
348 UINT32 DomainLength)
349{
350 WINPR_SAM_ENTRY* entry = nullptr;
351 char* utfUser = nullptr;
352 char* utfDomain = nullptr;
353 size_t userCharLen = 0;
354 size_t domainCharLen = 0;
355
356 utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen);
357 if (!utfUser)
358 goto fail;
359 if (DomainLength > 0)
360 {
361 utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen);
362 if (!utfDomain)
363 goto fail;
364 }
365 entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
366 if (entry)
367 {
368 free(entry->User);
369 free(entry->Domain);
370 entry->User = nullptr;
371 entry->Domain = nullptr;
372 if (User)
373 entry->User = (char*)winpr_wcsndup(User, UserLength / sizeof(WCHAR));
374 entry->UserLength = UserLength;
375 if (Domain)
376 entry->Domain = (char*)winpr_wcsndup(Domain, DomainLength / sizeof(WCHAR));
377 entry->DomainLength = DomainLength;
378 }
379fail:
380 free(utfUser);
381 free(utfDomain);
382 return entry;
383}
384
385void SamClose(WINPR_SAM* sam)
386{
387 if (sam != nullptr)
388 {
389 if (sam->fp)
390 (void)fclose(sam->fp);
391 free(sam);
392 }
393}