FreeRDP
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 
42 struct winpr_sam
43 {
44  FILE* fp;
45  char* line;
46  char* buffer;
47  char* context;
48  BOOL readOnly;
49 };
50 
51 static 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 
66 static 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 
91 WINPR_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 
136 static 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 
180 static void SamLookupFinish(WINPR_SAM* sam)
181 {
182  free(sam->buffer);
183  sam->buffer = NULL;
184  sam->line = NULL;
185 }
186 
187 static 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 
256 void SamFreeEntry(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 
270 void 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 
291 WINPR_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 
330 out_fail:
331  SamLookupFinish(sam);
332 fail:
333  SamFreeEntry(sam, search);
334 
335  if (!found)
336  {
337  SamFreeEntry(sam, entry);
338  return NULL;
339  }
340 
341  return entry;
342 }
343 
344 WINPR_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);
363 fail:
364  free(utfUser);
365  free(utfDomain);
366  return entry;
367 }
368 
369 void SamClose(WINPR_SAM* sam)
370 {
371  if (sam != NULL)
372  {
373  if (sam->fp)
374  (void)fclose(sam->fp);
375  free(sam);
376  }
377 }