FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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 NULL;
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 = NULL;
94 WINPR_SAM* sam = NULL;
95 char* allocatedFileName = NULL;
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 NULL;
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 NULL;
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 = NULL;
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 = NULL;
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 = NULL;
184 sam->line = NULL;
185}
186
187static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
188{
189 char* p[5] = { 0 };
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, ':')) != NULL)
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 = NULL;
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 = NULL;
246
247 if (LmHashLength == 32)
248 winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash));
249
250 if (NtHashLength == 32)
251 winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash, sizeof(entry->NtHash));
252
253 return TRUE;
254}
255
256void SamFreeEntry(WINPR_ATTR_UNUSED WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
257{
258 if (entry)
259 {
260 if (entry->UserLength > 0)
261 free(entry->User);
262
263 if (entry->DomainLength > 0)
264 free(entry->Domain);
265
266 free(entry);
267 }
268}
269
270void SamResetEntry(WINPR_SAM_ENTRY* entry)
271{
272 if (!entry)
273 return;
274
275 if (entry->UserLength)
276 {
277 free(entry->User);
278 entry->User = NULL;
279 }
280
281 if (entry->DomainLength)
282 {
283 free(entry->Domain);
284 entry->Domain = NULL;
285 }
286
287 ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
288 ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
289}
290
291WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
292 UINT32 DomainLength)
293{
294 size_t length = 0;
295 BOOL found = FALSE;
296 WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
297 WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY));
298
299 if (!entry || !search)
300 goto fail;
301
302 if (!SamLookupStart(sam))
303 goto fail;
304
305 while (sam->line != NULL)
306 {
307 length = strlen(sam->line);
308
309 if (length > 1)
310 {
311 if (sam->line[0] != '#')
312 {
313 if (!SamReadEntry(sam, entry))
314 {
315 goto out_fail;
316 }
317
318 if (SamAreEntriesEqual(entry, search))
319 {
320 found = 1;
321 break;
322 }
323 }
324 }
325
326 SamResetEntry(entry);
327 sam->line = strtok_s(NULL, "\n", &sam->context);
328 }
329
330out_fail:
331 SamLookupFinish(sam);
332fail:
333 SamFreeEntry(sam, search);
334
335 if (!found)
336 {
337 SamFreeEntry(sam, entry);
338 return NULL;
339 }
340
341 return entry;
342}
343
344WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
345 UINT32 DomainLength)
346{
347 WINPR_SAM_ENTRY* entry = NULL;
348 char* utfUser = NULL;
349 char* utfDomain = NULL;
350 size_t userCharLen = 0;
351 size_t domainCharLen = 0;
352
353 utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen);
354 if (!utfUser)
355 goto fail;
356 if (DomainLength > 0)
357 {
358 utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen);
359 if (!utfDomain)
360 goto fail;
361 }
362 entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
363fail:
364 free(utfUser);
365 free(utfDomain);
366 return entry;
367}
368
369void SamClose(WINPR_SAM* sam)
370{
371 if (sam != NULL)
372 {
373 if (sam->fp)
374 (void)fclose(sam->fp);
375 free(sam);
376 }
377}