Coverage Report

Created: 2024-09-08 06:20

/src/FreeRDP/winpr/libwinpr/utils/sam.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Security Accounts Manager (SAM)
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include <winpr/wtypes.h>
27
#include <winpr/crt.h>
28
#include <winpr/sam.h>
29
#include <winpr/print.h>
30
#include <winpr/file.h>
31
32
#include "../log.h"
33
34
#ifdef WINPR_HAVE_UNISTD_H
35
#include <unistd.h>
36
#endif
37
38
#ifdef _WIN32
39
#define WINPR_SAM_FILE "C:\\SAM"
40
#else
41
0
#define WINPR_SAM_FILE "/etc/winpr/SAM"
42
#endif
43
#define TAG WINPR_TAG("utils")
44
45
struct winpr_sam
46
{
47
  FILE* fp;
48
  char* line;
49
  char* buffer;
50
  char* context;
51
  BOOL readOnly;
52
};
53
54
static WINPR_SAM_ENTRY* SamEntryFromDataA(LPCSTR User, DWORD UserLength, LPCSTR Domain,
55
                                          DWORD DomainLength)
56
0
{
57
0
  WINPR_SAM_ENTRY* entry = calloc(1, sizeof(WINPR_SAM_ENTRY));
58
0
  if (!entry)
59
0
    return NULL;
60
0
  if (User && (UserLength > 0))
61
0
    entry->User = _strdup(User);
62
0
  entry->UserLength = UserLength;
63
0
  if (Domain && (DomainLength > 0))
64
0
    entry->Domain = _strdup(Domain);
65
0
  entry->DomainLength = DomainLength;
66
0
  return entry;
67
0
}
68
69
static BOOL SamAreEntriesEqual(const WINPR_SAM_ENTRY* a, const WINPR_SAM_ENTRY* b)
70
0
{
71
0
  if (!a || !b)
72
0
    return FALSE;
73
0
  if (a->UserLength != b->UserLength)
74
0
    return FALSE;
75
0
  if (a->DomainLength != b->DomainLength)
76
0
    return FALSE;
77
0
  if (a->UserLength > 0)
78
0
  {
79
0
    if (!a->User || !b->User)
80
0
      return FALSE;
81
0
    if (strncmp(a->User, b->User, a->UserLength) != 0)
82
0
      return FALSE;
83
0
  }
84
0
  if (a->DomainLength > 0)
85
0
  {
86
0
    if (!a->Domain || !b->Domain)
87
0
      return FALSE;
88
0
    if (strncmp(a->Domain, b->Domain, a->DomainLength) != 0)
89
0
      return FALSE;
90
0
  }
91
0
  return TRUE;
92
0
}
93
94
WINPR_SAM* SamOpen(const char* filename, BOOL readOnly)
95
0
{
96
0
  FILE* fp = NULL;
97
0
  WINPR_SAM* sam = NULL;
98
99
0
  if (!filename)
100
0
    filename = WINPR_SAM_FILE;
101
102
0
  if (readOnly)
103
0
    fp = winpr_fopen(filename, "r");
104
0
  else
105
0
  {
106
0
    fp = winpr_fopen(filename, "r+");
107
108
0
    if (!fp)
109
0
      fp = winpr_fopen(filename, "w+");
110
0
  }
111
112
0
  if (fp)
113
0
  {
114
0
    sam = (WINPR_SAM*)calloc(1, sizeof(WINPR_SAM));
115
116
0
    if (!sam)
117
0
    {
118
0
      (void)fclose(fp);
119
0
      return NULL;
120
0
    }
121
122
0
    sam->readOnly = readOnly;
123
0
    sam->fp = fp;
124
0
  }
125
0
  else
126
0
  {
127
0
    WLog_DBG(TAG, "Could not open SAM file!");
128
0
    return NULL;
129
0
  }
130
131
0
  return sam;
132
0
}
133
134
static BOOL SamLookupStart(WINPR_SAM* sam)
135
0
{
136
0
  size_t readSize = 0;
137
0
  INT64 fileSize = 0;
138
139
0
  if (!sam || !sam->fp)
140
0
    return FALSE;
141
142
0
  _fseeki64(sam->fp, 0, SEEK_END);
143
0
  fileSize = _ftelli64(sam->fp);
144
0
  _fseeki64(sam->fp, 0, SEEK_SET);
145
146
0
  if (fileSize < 1)
147
0
    return FALSE;
148
149
0
  sam->context = NULL;
150
0
  sam->buffer = (char*)calloc((size_t)fileSize + 2, 1);
151
152
0
  if (!sam->buffer)
153
0
    return FALSE;
154
155
0
  readSize = fread(sam->buffer, (size_t)fileSize, 1, sam->fp);
156
157
0
  if (!readSize)
158
0
  {
159
0
    if (!ferror(sam->fp))
160
0
      readSize = (size_t)fileSize;
161
0
  }
162
163
0
  if (readSize < 1)
164
0
  {
165
0
    free(sam->buffer);
166
0
    sam->buffer = NULL;
167
0
    return FALSE;
168
0
  }
169
170
0
  sam->buffer[fileSize] = '\n';
171
0
  sam->buffer[fileSize + 1] = '\0';
172
0
  sam->line = strtok_s(sam->buffer, "\n", &sam->context);
173
0
  return TRUE;
174
0
}
175
176
static void SamLookupFinish(WINPR_SAM* sam)
177
0
{
178
0
  free(sam->buffer);
179
0
  sam->buffer = NULL;
180
0
  sam->line = NULL;
181
0
}
182
183
static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
184
0
{
185
0
  char* p[5];
186
0
  size_t LmHashLength = 0;
187
0
  size_t NtHashLength = 0;
188
0
  size_t count = 0;
189
0
  char* cur = NULL;
190
191
0
  if (!sam || !entry || !sam->line)
192
0
    return FALSE;
193
194
0
  cur = sam->line;
195
196
0
  while ((cur = strchr(cur, ':')) != NULL)
197
0
  {
198
0
    count++;
199
0
    cur++;
200
0
  }
201
202
0
  if (count < 4)
203
0
    return FALSE;
204
205
0
  p[0] = sam->line;
206
0
  p[1] = strchr(p[0], ':') + 1;
207
0
  p[2] = strchr(p[1], ':') + 1;
208
0
  p[3] = strchr(p[2], ':') + 1;
209
0
  p[4] = strchr(p[3], ':') + 1;
210
0
  LmHashLength = (p[3] - p[2] - 1);
211
0
  NtHashLength = (p[4] - p[3] - 1);
212
213
0
  if ((LmHashLength != 0) && (LmHashLength != 32))
214
0
    return FALSE;
215
216
0
  if ((NtHashLength != 0) && (NtHashLength != 32))
217
0
    return FALSE;
218
219
0
  entry->UserLength = (UINT32)(p[1] - p[0] - 1);
220
0
  entry->User = (LPSTR)malloc(entry->UserLength + 1);
221
222
0
  if (!entry->User)
223
0
    return FALSE;
224
225
0
  entry->User[entry->UserLength] = '\0';
226
0
  entry->DomainLength = (UINT32)(p[2] - p[1] - 1);
227
0
  memcpy(entry->User, p[0], entry->UserLength);
228
229
0
  if (entry->DomainLength > 0)
230
0
  {
231
0
    entry->Domain = (LPSTR)malloc(entry->DomainLength + 1);
232
233
0
    if (!entry->Domain)
234
0
    {
235
0
      free(entry->User);
236
0
      entry->User = NULL;
237
0
      return FALSE;
238
0
    }
239
240
0
    memcpy(entry->Domain, p[1], entry->DomainLength);
241
0
    entry->Domain[entry->DomainLength] = '\0';
242
0
  }
243
0
  else
244
0
    entry->Domain = NULL;
245
246
0
  if (LmHashLength == 32)
247
0
    winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash));
248
249
0
  if (NtHashLength == 32)
250
0
    winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash, sizeof(entry->NtHash));
251
252
0
  return TRUE;
253
0
}
254
255
void SamFreeEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
256
0
{
257
0
  if (entry)
258
0
  {
259
0
    if (entry->UserLength > 0)
260
0
      free(entry->User);
261
262
0
    if (entry->DomainLength > 0)
263
0
      free(entry->Domain);
264
265
0
    free(entry);
266
0
  }
267
0
}
268
269
void SamResetEntry(WINPR_SAM_ENTRY* entry)
270
0
{
271
0
  if (!entry)
272
0
    return;
273
274
0
  if (entry->UserLength)
275
0
  {
276
0
    free(entry->User);
277
0
    entry->User = NULL;
278
0
  }
279
280
0
  if (entry->DomainLength)
281
0
  {
282
0
    free(entry->Domain);
283
0
    entry->Domain = NULL;
284
0
  }
285
286
0
  ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
287
0
  ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
288
0
}
289
290
WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
291
                                UINT32 DomainLength)
292
0
{
293
0
  size_t length = 0;
294
0
  BOOL found = FALSE;
295
0
  WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
296
0
  WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY));
297
298
0
  if (!entry || !search)
299
0
    goto fail;
300
301
0
  if (!SamLookupStart(sam))
302
0
    goto fail;
303
304
0
  while (sam->line != NULL)
305
0
  {
306
0
    length = strlen(sam->line);
307
308
0
    if (length > 1)
309
0
    {
310
0
      if (sam->line[0] != '#')
311
0
      {
312
0
        if (!SamReadEntry(sam, entry))
313
0
        {
314
0
          goto out_fail;
315
0
        }
316
317
0
        if (SamAreEntriesEqual(entry, search))
318
0
        {
319
0
          found = 1;
320
0
          break;
321
0
        }
322
0
      }
323
0
    }
324
325
0
    SamResetEntry(entry);
326
0
    sam->line = strtok_s(NULL, "\n", &sam->context);
327
0
  }
328
329
0
out_fail:
330
0
  SamLookupFinish(sam);
331
0
fail:
332
0
  SamFreeEntry(sam, search);
333
334
0
  if (!found)
335
0
  {
336
0
    SamFreeEntry(sam, entry);
337
0
    return NULL;
338
0
  }
339
340
0
  return entry;
341
0
}
342
343
WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
344
                                UINT32 DomainLength)
345
0
{
346
0
  WINPR_SAM_ENTRY* entry = NULL;
347
0
  char* utfUser = NULL;
348
0
  char* utfDomain = NULL;
349
0
  size_t userCharLen = 0;
350
0
  size_t domainCharLen = 0;
351
352
0
  utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen);
353
0
  if (!utfUser)
354
0
    goto fail;
355
0
  if (DomainLength > 0)
356
0
  {
357
0
    utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen);
358
0
    if (!utfDomain)
359
0
      goto fail;
360
0
  }
361
0
  entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
362
0
fail:
363
0
  free(utfUser);
364
0
  free(utfDomain);
365
0
  return entry;
366
0
}
367
368
void SamClose(WINPR_SAM* sam)
369
0
{
370
0
  if (sam != NULL)
371
0
  {
372
0
    if (sam->fp)
373
0
      (void)fclose(sam->fp);
374
0
    free(sam);
375
0
  }
376
0
}