summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/HostServices/auth/directoryservice/directoryservice.cpp338
1 files changed, 338 insertions, 0 deletions
diff --git a/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp
new file mode 100644
index 00000000..ea245961
--- /dev/null
+++ b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp
@@ -0,0 +1,338 @@
+/** @file
+ *
+ * VirtualBox External Authentication Library:
+ * Mac OS X Authentication. This is based on
+ * http://developer.apple.com/mac/library/samplecode/CryptNoMore/
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <iprt/cdefs.h>
+#include <iprt/assert.h>
+
+#include <VBox/VBoxAuth.h>
+
+#include <DirectoryService/DirectoryService.h>
+
+/* Globals */
+static const size_t s_cBufferSize = 32 * 1024;
+
+tDirStatus defaultSearchNodePath(tDirReference pDirRef, tDataListPtr *pdsNodePath)
+{
+ tDirStatus dsErr = eDSNoErr;
+ /* Create a buffer for the resulting nodes */
+ tDataBufferPtr pTmpBuf = NULL;
+ pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
+ if (pTmpBuf)
+ {
+ /* Try to find the default search node for local names */
+ UInt32 cNodes;
+ tContextData hCtx = 0;
+ dsErr = dsFindDirNodes(pDirRef, pTmpBuf, NULL, eDSLocalNodeNames, &cNodes, &hCtx);
+ /* Any nodes found? */
+ if ( dsErr == eDSNoErr
+ && cNodes >= 1)
+ /* The first path of the node list is what we looking for. */
+ dsErr = dsGetDirNodeName(pDirRef, pTmpBuf, 1, pdsNodePath);
+ else
+ dsErr = eDSNodeNotFound;
+
+ if (hCtx) /* (DSoNodeConfig.m from DSTools-162 does exactly the same free if not-zero-regardless-of-return-code.) */
+ dsReleaseContinueData(pDirRef, hCtx);
+ dsDataBufferDeAllocate(pDirRef, pTmpBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+
+ return dsErr;
+}
+
+tDirStatus userAuthInfo(tDirReference pDirRef, tDirNodeReference pNodeRef, const char *pszUsername, tDataListPtr *ppAuthNodeListOut)
+{
+ tDirStatus dsErr = eDSNoErr;
+ tDirStatus dsCleanErr = eDSNoErr;
+ /* Create a buffer for the resulting authentication info */
+ tDataBufferPtr pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
+ if (pTmpBuf)
+ {
+ /* Create the necessary lists for kDSNAttrMetaNodeLocation and kDSNAttrRecordName. */
+ tDataListPtr pRecordType = dsBuildListFromStrings(pDirRef, kDSStdRecordTypeUsers, NULL);
+ tDataListPtr pRecordName = dsBuildListFromStrings(pDirRef, pszUsername, NULL);
+ tDataListPtr pRequestedAttributes = dsBuildListFromStrings(pDirRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL);
+ if (!( pRecordType == NULL
+ || pRecordName == NULL
+ || pRequestedAttributes == NULL))
+ {
+ /* Now search for the first matching record */
+ UInt32 cRecords = 1;
+ tContextData hCtx = 0;
+ dsErr = dsGetRecordList(pNodeRef,
+ pTmpBuf,
+ pRecordName,
+ eDSExact,
+ pRecordType,
+ pRequestedAttributes,
+ false,
+ &cRecords,
+ &hCtx);
+ if ( dsErr == eDSNoErr
+ && cRecords >= 1)
+ {
+ /* Process the first found record. Look at any attribute one by one. */
+ tAttributeListRef hRecAttrListRef = 0;
+ tRecordEntryPtr pRecEntry = NULL;
+ tDataListPtr pAuthNodeList = NULL;
+ dsErr = dsGetRecordEntry(pNodeRef, pTmpBuf, 1, &hRecAttrListRef, &pRecEntry);
+ if (dsErr == eDSNoErr)
+ {
+ for (size_t i = 1; i <= pRecEntry->fRecordAttributeCount; ++i)
+ {
+ tAttributeValueListRef hAttrValueListRef = 0;
+ tAttributeEntryPtr pAttrEntry = NULL;
+ /* Get the information for this attribute. */
+ dsErr = dsGetAttributeEntry(pNodeRef, pTmpBuf, hRecAttrListRef, i,
+ &hAttrValueListRef, &pAttrEntry);
+ if (dsErr == eDSNoErr)
+ {
+ tAttributeValueEntryPtr pValueEntry = NULL;
+ /* Has any value? */
+ if (pAttrEntry->fAttributeValueCount > 0)
+ {
+ dsErr = dsGetAttributeValue(pNodeRef, pTmpBuf, 1, hAttrValueListRef, &pValueEntry);
+ if (dsErr == eDSNoErr)
+ {
+ /* Check for kDSNAttrMetaNodeLocation */
+ if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0)
+ {
+ /* Convert the meta location attribute to a path node list */
+ pAuthNodeList = dsBuildFromPath(pDirRef,
+ pValueEntry->fAttributeValueData.fBufferData,
+ "/");
+ if (pAuthNodeList == NULL)
+ dsErr = eDSAllocationFailed;
+ }
+ }
+ }
+
+ if (pValueEntry != NULL)
+ dsDeallocAttributeValueEntry(pDirRef, pValueEntry);
+ if (hAttrValueListRef)
+ dsCloseAttributeValueList(hAttrValueListRef);
+ if (pAttrEntry != NULL)
+ dsDeallocAttributeEntry(pDirRef, pAttrEntry);
+
+ if (dsErr != eDSNoErr)
+ break;
+ }
+ }
+ }
+ /* Copy the results */
+ if (dsErr == eDSNoErr)
+ {
+ if (pAuthNodeList != NULL)
+ {
+ /* Copy out results. */
+ *ppAuthNodeListOut = pAuthNodeList;
+ pAuthNodeList = NULL;
+ }
+ else
+ dsErr = eDSAttributeNotFound;
+ }
+
+ if (pAuthNodeList != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pAuthNodeList);
+ if (dsCleanErr == eDSNoErr)
+ free(pAuthNodeList);
+ }
+ if (hRecAttrListRef)
+ dsCloseAttributeList(hRecAttrListRef);
+ if (pRecEntry != NULL)
+ dsDeallocRecordEntry(pDirRef, pRecEntry);
+ }
+ else
+ dsErr = eDSRecordNotFound;
+ if (hCtx)
+ dsReleaseContinueData(pDirRef, hCtx);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ if (pRequestedAttributes != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pRequestedAttributes);
+ if (dsCleanErr == eDSNoErr)
+ free(pRequestedAttributes);
+ }
+ if (pRecordName != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pRecordName);
+ if (dsCleanErr == eDSNoErr)
+ free(pRecordName);
+ }
+ if (pRecordType != NULL)
+ {
+ dsCleanErr = dsDataListDeallocate(pDirRef, pRecordType);
+ if (dsCleanErr == eDSNoErr)
+ free(pRecordType);
+ }
+ dsDataBufferDeAllocate(pDirRef, pTmpBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+
+ return dsErr;
+}
+
+tDirStatus authWithNode(tDirReference pDirRef, tDataListPtr pAuthNodeList, const char *pszUsername, const char *pszPassword)
+{
+ tDirStatus dsErr = eDSNoErr;
+ /* Open the authentication node. */
+ tDirNodeReference hAuthNodeRef = 0;
+ dsErr = dsOpenDirNode(pDirRef, pAuthNodeList, &hAuthNodeRef);
+ if (dsErr == eDSNoErr)
+ {
+ /* How like we to authenticate! */
+ tDataNodePtr pAuthMethod = dsDataNodeAllocateString(pDirRef, kDSStdAuthNodeNativeClearTextOK);
+ if (pAuthMethod)
+ {
+ /* Create the memory holding the authentication data. The data
+ * structure consists of 4 byte length of the username + zero byte,
+ * the username itself, a 4 byte length of the password & the
+ * password itself + zero byte. */
+ tDataBufferPtr pAuthOutBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize);
+ if (pAuthOutBuf)
+ {
+ size_t cUserName = strlen(pszUsername) + 1;
+ size_t cPassword = strlen(pszPassword) + 1;
+ unsigned long cLen = 0;
+ tDataBufferPtr pAuthInBuf = dsDataBufferAllocate(pDirRef, sizeof(cLen) + cUserName + sizeof(cLen) + cPassword);
+ if (pAuthInBuf)
+ {
+ /* Move the data into the buffer. */
+ pAuthInBuf->fBufferLength = 0;
+ /* Length of the username */
+ cLen = cUserName;
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen));
+ pAuthInBuf->fBufferLength += sizeof(cLen);
+ /* The username itself */
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszUsername, cUserName);
+ pAuthInBuf->fBufferLength += cUserName;
+ /* Length of the password */
+ cLen = cPassword;
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], &cLen, sizeof(cLen));
+ pAuthInBuf->fBufferLength += sizeof(cLen);
+ /* The password itself */
+ memcpy(&pAuthInBuf->fBufferData[pAuthInBuf->fBufferLength], pszPassword, cPassword);
+ pAuthInBuf->fBufferLength += cPassword;
+ /* Now authenticate */
+ dsErr = dsDoDirNodeAuth(hAuthNodeRef, pAuthMethod, true, pAuthInBuf, pAuthOutBuf, NULL);
+ /* Clean up. */
+ dsDataBufferDeAllocate(pDirRef, pAuthInBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ dsDataBufferDeAllocate(pDirRef, pAuthOutBuf);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ dsDataNodeDeAllocate(pDirRef, pAuthMethod);
+ }
+ else
+ dsErr = eDSAllocationFailed;
+ dsCloseDirNode(hAuthNodeRef);
+ }
+
+ return dsErr;
+}
+
+RT_C_DECLS_BEGIN
+DECLEXPORT(FNAUTHENTRY3) AuthEntry;
+RT_C_DECLS_END
+
+DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller,
+ PAUTHUUID pUuid,
+ AuthGuestJudgement guestJudgement,
+ const char *pszUser,
+ const char *pszPassword,
+ const char *pszDomain,
+ int fLogon,
+ unsigned clientId)
+{
+ RT_NOREF(pszCaller, pUuid, guestJudgement, pszDomain, clientId);
+
+ /* Validate input */
+ AssertPtrReturn(pszUser, AuthResultAccessDenied);
+ AssertPtrReturn(pszPassword, AuthResultAccessDenied);
+
+ /* Result to a default value */
+ AuthResult result = AuthResultAccessDenied;
+
+ /* Only process logon requests. */
+ if (!fLogon)
+ return result; /* Return value is ignored by the caller. */
+
+ tDirStatus dsErr = eDSNoErr;
+ tDirStatus dsCleanErr = eDSNoErr;
+ tDirReference hDirRef = 0;
+ /* Connect to the Directory Service. */
+ dsErr = dsOpenDirService(&hDirRef);
+ if (dsErr == eDSNoErr)
+ {
+ /* Fetch the default search node */
+ tDataListPtr pSearchNodeList = NULL;
+ dsErr = defaultSearchNodePath(hDirRef, &pSearchNodeList);
+ if (dsErr == eDSNoErr)
+ {
+ /* Open the default search node */
+ tDirNodeReference hSearchNodeRef = 0;
+ dsErr = dsOpenDirNode(hDirRef, pSearchNodeList, &hSearchNodeRef);
+ if (dsErr == eDSNoErr)
+ {
+ /* Search for the user info, fetch the authentication node &
+ * the authentication user name. This allows the client to
+ * specify a long user name even if the name which is used to
+ * authenticate has the short form. */
+ tDataListPtr pAuthNodeList = NULL;
+ dsErr = userAuthInfo(hDirRef, hSearchNodeRef, pszUser, &pAuthNodeList);
+ if (dsErr == eDSNoErr)
+ {
+ /* Open the authentication node and do the authentication. */
+ dsErr = authWithNode(hDirRef, pAuthNodeList, pszUser, pszPassword);
+ if (dsErr == eDSNoErr)
+ result = AuthResultAccessGranted;
+ dsCleanErr = dsDataListDeallocate(hDirRef, pAuthNodeList);
+ if (dsCleanErr == eDSNoErr)
+ free(pAuthNodeList);
+ }
+ dsCloseDirNode(hSearchNodeRef);
+ }
+ dsCleanErr = dsDataListDeallocate(hDirRef, pSearchNodeList);
+ if (dsCleanErr == eDSNoErr)
+ free(pSearchNodeList);
+ }
+ dsCloseDirService(hDirRef);
+ }
+
+ return result;
+}
+