summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/utils/sam.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/utils/sam.c366
1 files changed, 366 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/sam.c b/winpr/libwinpr/utils/sam.c
new file mode 100644
index 0000000..31163d9
--- /dev/null
+++ b/winpr/libwinpr/utils/sam.c
@@ -0,0 +1,366 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Security Accounts Manager (SAM)
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <winpr/wtypes.h>
+#include <winpr/crt.h>
+#include <winpr/sam.h>
+#include <winpr/print.h>
+#include <winpr/file.h>
+
+#include "../log.h"
+
+#ifdef WINPR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#define WINPR_SAM_FILE "C:\\SAM"
+#else
+#define WINPR_SAM_FILE "/etc/winpr/SAM"
+#endif
+#define TAG WINPR_TAG("utils")
+
+struct winpr_sam
+{
+ FILE* fp;
+ char* line;
+ char* buffer;
+ char* context;
+ BOOL readOnly;
+};
+
+static WINPR_SAM_ENTRY* SamEntryFromDataA(LPCSTR User, DWORD UserLength, LPCSTR Domain,
+ DWORD DomainLength)
+{
+ WINPR_SAM_ENTRY* entry = calloc(1, sizeof(WINPR_SAM_ENTRY));
+ if (!entry)
+ return NULL;
+ if (User && (UserLength > 0))
+ entry->User = _strdup(User);
+ entry->UserLength = UserLength;
+ if (Domain && (DomainLength > 0))
+ entry->Domain = _strdup(Domain);
+ entry->DomainLength = DomainLength;
+ return entry;
+}
+
+static BOOL SamAreEntriesEqual(const WINPR_SAM_ENTRY* a, const WINPR_SAM_ENTRY* b)
+{
+ if (!a || !b)
+ return FALSE;
+ if (a->UserLength != b->UserLength)
+ return FALSE;
+ if (a->DomainLength != b->DomainLength)
+ return FALSE;
+ if (strncmp(a->User, b->User, a->UserLength) != 0)
+ return FALSE;
+ if (strncmp(a->Domain, b->Domain, a->DomainLength) != 0)
+ return FALSE;
+ return TRUE;
+}
+
+WINPR_SAM* SamOpen(const char* filename, BOOL readOnly)
+{
+ FILE* fp = NULL;
+ WINPR_SAM* sam = NULL;
+
+ if (!filename)
+ filename = WINPR_SAM_FILE;
+
+ if (readOnly)
+ fp = winpr_fopen(filename, "r");
+ else
+ {
+ fp = winpr_fopen(filename, "r+");
+
+ if (!fp)
+ fp = winpr_fopen(filename, "w+");
+ }
+
+ if (fp)
+ {
+ sam = (WINPR_SAM*)calloc(1, sizeof(WINPR_SAM));
+
+ if (!sam)
+ {
+ fclose(fp);
+ return NULL;
+ }
+
+ sam->readOnly = readOnly;
+ sam->fp = fp;
+ }
+ else
+ {
+ WLog_DBG(TAG, "Could not open SAM file!");
+ return NULL;
+ }
+
+ return sam;
+}
+
+static BOOL SamLookupStart(WINPR_SAM* sam)
+{
+ size_t readSize = 0;
+ INT64 fileSize = 0;
+
+ if (!sam || !sam->fp)
+ return FALSE;
+
+ _fseeki64(sam->fp, 0, SEEK_END);
+ fileSize = _ftelli64(sam->fp);
+ _fseeki64(sam->fp, 0, SEEK_SET);
+
+ if (fileSize < 1)
+ return FALSE;
+
+ sam->context = NULL;
+ sam->buffer = (char*)calloc((size_t)fileSize + 2, 1);
+
+ if (!sam->buffer)
+ return FALSE;
+
+ readSize = fread(sam->buffer, (size_t)fileSize, 1, sam->fp);
+
+ if (!readSize)
+ {
+ if (!ferror(sam->fp))
+ readSize = (size_t)fileSize;
+ }
+
+ if (readSize < 1)
+ {
+ free(sam->buffer);
+ sam->buffer = NULL;
+ return FALSE;
+ }
+
+ sam->buffer[fileSize] = '\n';
+ sam->buffer[fileSize + 1] = '\0';
+ sam->line = strtok_s(sam->buffer, "\n", &sam->context);
+ return TRUE;
+}
+
+static void SamLookupFinish(WINPR_SAM* sam)
+{
+ free(sam->buffer);
+ sam->buffer = NULL;
+ sam->line = NULL;
+}
+
+static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
+{
+ char* p[5];
+ size_t LmHashLength = 0;
+ size_t NtHashLength = 0;
+ size_t count = 0;
+ char* cur = NULL;
+
+ if (!sam || !entry || !sam->line)
+ return FALSE;
+
+ cur = sam->line;
+
+ while ((cur = strchr(cur, ':')) != NULL)
+ {
+ count++;
+ cur++;
+ }
+
+ if (count < 4)
+ return FALSE;
+
+ p[0] = sam->line;
+ p[1] = strchr(p[0], ':') + 1;
+ p[2] = strchr(p[1], ':') + 1;
+ p[3] = strchr(p[2], ':') + 1;
+ p[4] = strchr(p[3], ':') + 1;
+ LmHashLength = (p[3] - p[2] - 1);
+ NtHashLength = (p[4] - p[3] - 1);
+
+ if ((LmHashLength != 0) && (LmHashLength != 32))
+ return FALSE;
+
+ if ((NtHashLength != 0) && (NtHashLength != 32))
+ return FALSE;
+
+ entry->UserLength = (UINT32)(p[1] - p[0] - 1);
+ entry->User = (LPSTR)malloc(entry->UserLength + 1);
+
+ if (!entry->User)
+ return FALSE;
+
+ entry->User[entry->UserLength] = '\0';
+ entry->DomainLength = (UINT32)(p[2] - p[1] - 1);
+ memcpy(entry->User, p[0], entry->UserLength);
+
+ if (entry->DomainLength > 0)
+ {
+ entry->Domain = (LPSTR)malloc(entry->DomainLength + 1);
+
+ if (!entry->Domain)
+ {
+ free(entry->User);
+ entry->User = NULL;
+ return FALSE;
+ }
+
+ memcpy(entry->Domain, p[1], entry->DomainLength);
+ entry->Domain[entry->DomainLength] = '\0';
+ }
+ else
+ entry->Domain = NULL;
+
+ if (LmHashLength == 32)
+ winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash));
+
+ if (NtHashLength == 32)
+ winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash, sizeof(entry->NtHash));
+
+ return TRUE;
+}
+
+void SamFreeEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
+{
+ if (entry)
+ {
+ if (entry->UserLength > 0)
+ free(entry->User);
+
+ if (entry->DomainLength > 0)
+ free(entry->Domain);
+
+ free(entry);
+ }
+}
+
+void SamResetEntry(WINPR_SAM_ENTRY* entry)
+{
+ if (!entry)
+ return;
+
+ if (entry->UserLength)
+ {
+ free(entry->User);
+ entry->User = NULL;
+ }
+
+ if (entry->DomainLength)
+ {
+ free(entry->Domain);
+ entry->Domain = NULL;
+ }
+
+ ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
+ ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
+}
+
+WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
+ UINT32 DomainLength)
+{
+ size_t length = 0;
+ BOOL found = FALSE;
+ WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
+ WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY));
+
+ if (!entry || !search)
+ goto fail;
+
+ if (!SamLookupStart(sam))
+ goto fail;
+
+ while (sam->line != NULL)
+ {
+ length = strlen(sam->line);
+
+ if (length > 1)
+ {
+ if (sam->line[0] != '#')
+ {
+ if (!SamReadEntry(sam, entry))
+ {
+ goto out_fail;
+ }
+
+ if (SamAreEntriesEqual(entry, search))
+ {
+ found = 1;
+ break;
+ }
+ }
+ }
+
+ SamResetEntry(entry);
+ sam->line = strtok_s(NULL, "\n", &sam->context);
+ }
+
+out_fail:
+ SamLookupFinish(sam);
+fail:
+ SamFreeEntry(sam, search);
+
+ if (!found)
+ {
+ SamFreeEntry(sam, entry);
+ return NULL;
+ }
+
+ return entry;
+}
+
+WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
+ UINT32 DomainLength)
+{
+ WINPR_SAM_ENTRY* entry = NULL;
+ char* utfUser = NULL;
+ char* utfDomain = NULL;
+ size_t userCharLen = 0;
+ size_t domainCharLen = 0;
+
+ utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen);
+ if (!utfUser)
+ goto fail;
+ if (DomainLength > 0)
+ {
+ utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen);
+ if (!utfDomain)
+ goto fail;
+ }
+ entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
+fail:
+ free(utfUser);
+ free(utfDomain);
+ return entry;
+}
+
+void SamClose(WINPR_SAM* sam)
+{
+ if (sam != NULL)
+ {
+ if (sam->fp)
+ fclose(sam->fp);
+ free(sam);
+ }
+}