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