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