diff options
Diffstat (limited to 'src/VBox/HostServices/auth')
-rw-r--r-- | src/VBox/HostServices/auth/Makefile.kmk | 66 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/directoryservice/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/directoryservice/directoryservice.cpp | 328 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/pam/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/pam/VBoxAuthPAM.c | 402 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/simple/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp | 137 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc | 51 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/winlogon/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/winlogon/VBoxAuth.rc | 51 | ||||
-rw-r--r-- | src/VBox/HostServices/auth/winlogon/winlogon.cpp | 171 |
11 files changed, 1206 insertions, 0 deletions
diff --git a/src/VBox/HostServices/auth/Makefile.kmk b/src/VBox/HostServices/auth/Makefile.kmk new file mode 100644 index 00000000..71de4316 --- /dev/null +++ b/src/VBox/HostServices/auth/Makefile.kmk @@ -0,0 +1,66 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the VBox RDP authentication plugins. +# + +# +# Copyright (C) 2006-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# The plugin. +ifndef VBOX_ONLY_SDK + if ("$(KBUILD_TARGET)" != "linux" && "$(KBUILD_TARGET)" != "solaris") || defined(VBOX_WITH_PAM) + DLLS += VBoxAuth + endif +endif +VBoxAuth_TEMPLATE = VBOXR3 +VBoxAuth_SOURCES.linux = pam/VBoxAuthPAM.c +VBoxAuth_SOURCES.solaris = pam/VBoxAuthPAM.c +VBoxAuth_SOURCES.freebsd = pam/VBoxAuthPAM.c +VBoxAuth_SOURCES.win = winlogon/winlogon.cpp winlogon/VBoxAuth.rc +VBoxAuth_SOURCES.darwin = directoryservice/directoryservice.cpp +VBoxAuth_CXXFLAGS.darwin = -Wno-deprecated-declarations +VBoxAuth_LIBS.linux = $(LIB_RUNTIME) dl +VBoxAuth_LIBS.solaris = $(LIB_RUNTIME) dl +VBoxAuth_LIBS.freebsd = $(LIB_RUNTIME) +VBoxAuth_LIBS.darwin = $(LIB_RUNTIME) +VBoxAuth_LDFLAGS.darwin = -framework DirectoryService + +# The simple plugin. +ifndef VBOX_ONLY_SDK + if defined(VBOX_WITH_MAIN) + DLLS += VBoxAuthSimple + endif +endif +VBoxAuthSimple_TEMPLATE = VBOXMAINCLIENTDLL +VBoxAuthSimple_SOURCES = simple/VBoxAuthSimple.cpp +VBoxAuthSimple_SOURCES.win = simple/VBoxAuthSimple.rc + +# Install the SDK samples. +INSTALLS += VBoxAuth-samples +VBoxAuth-samples_INST = $(INST_SDK)bindings/auth/ +VBoxAuth-samples_MODE = a+r,u+w +VBoxAuth-samples_SOURCES = simple/VBoxAuthSimple.cpp +VBoxAuth-samples_SOURCES.linux = pam/VBoxAuthPAM.c +VBoxAuth-samples_SOURCES.win = winlogon/winlogon.cpp + +# Install the SDK header. +INSTALLS += VBoxAuth-sdkhdr +VBoxAuth-sdkhdr_INST = $(INST_SDK)bindings/auth/include/ +VBoxAuth-sdkhdr_MODE = a+r,u+w +VBoxAuth-sdkhdr_SOURCES = $(PATH_ROOT)/include/VBox/VBoxAuth.h=>VBoxAuth.h + + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostServices/auth/directoryservice/Makefile.kup b/src/VBox/HostServices/auth/directoryservice/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/directoryservice/Makefile.kup diff --git a/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp new file mode 100644 index 00000000..5a65a848 --- /dev/null +++ b/src/VBox/HostServices/auth/directoryservice/directoryservice.cpp @@ -0,0 +1,328 @@ +/** @file + * + * VirtualBox External Authentication Library: + * Mac OS X Authentication. This is based on + * http://developer.apple.com/mac/library/samplecode/CryptNoMore/ + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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; +} + diff --git a/src/VBox/HostServices/auth/pam/Makefile.kup b/src/VBox/HostServices/auth/pam/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/pam/Makefile.kup diff --git a/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c new file mode 100644 index 00000000..dd969f0e --- /dev/null +++ b/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c @@ -0,0 +1,402 @@ +/** @file + * + * VirtualBox External Authentication Library: + * Linux PAM Authentication. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/* The PAM service name. + * + * The service name is the name of a file in the /etc/pam.d which contains + * authentication rules. It is possible to use an existing service + * name, like "login" for example. But if different set of rules + * is required, one can create a new file /etc/pam.d/vrdpauth + * specially for VRDP authentication. Note that the name of the + * service must be lowercase. See PAM documentation for details. + * + * The Auth module takes the PAM service name from the + * environment variable VBOX_AUTH_PAM_SERVICE. If the variable + * is not specified, then the 'login' PAM service is used. + */ +#define VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD "VRDP_AUTH_PAM_SERVICE" +#define VBOX_AUTH_PAM_SERVICE_NAME_ENV "VBOX_AUTH_PAM_SERVICE" +#define VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME "login" + + +/* The debug log file name. + * + * If defined, debug messages will be written to the file specified in the + * VBOX_AUTH_DEBUG_FILENAME (or deprecated VRDP_AUTH_DEBUG_FILENAME) environment + * variable: + * + * export VBOX_AUTH_DEBUG_FILENAME=pam.log + * + * The above will cause writing to the pam.log. + */ +#define VBOX_AUTH_DEBUG_FILENAME_ENV_OLD "VRDP_AUTH_DEBUG_FILENAME" +#define VBOX_AUTH_DEBUG_FILENAME_ENV "VBOX_AUTH_DEBUG_FILENAME" + + +/* Dynamic loading of the PAM library. + * + * If defined, the libpam.so is loaded dynamically. + * Enabled by default since it is often required, + * and does not harm. + */ +#define VBOX_AUTH_USE_PAM_DLLOAD + + +#ifdef VBOX_AUTH_USE_PAM_DLLOAD +/* The name of the PAM library */ +# ifdef RT_OS_SOLARIS +# define PAM_LIB_NAME "libpam.so.1" +# elif defined(RT_OS_FREEBSD) +# define PAM_LIB_NAME "libpam.so" +# else +# define PAM_LIB_NAME "libpam.so.0" +# endif +#endif /* VBOX_AUTH_USE_PAM_DLLOAD */ + + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#ifndef RT_OS_FREEBSD +# include <malloc.h> +#endif + +#include <security/pam_appl.h> + +#include <VBox/VBoxAuth.h> + +#ifdef VBOX_AUTH_USE_PAM_DLLOAD +#include <dlfcn.h> + +static int (*fn_pam_start)(const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh); +static int (*fn_pam_authenticate)(pam_handle_t *pamh, int flags); +static int (*fn_pam_acct_mgmt)(pam_handle_t *pamh, int flags); +static int (*fn_pam_end)(pam_handle_t *pamh, int pam_status); +static const char * (*fn_pam_strerror)(pam_handle_t *pamh, int errnum); +#else +#define fn_pam_start pam_start +#define fn_pam_authenticate pam_authenticate +#define fn_pam_acct_mgmt pam_acct_mgmt +#define fn_pam_end pam_end +#define fn_pam_strerror pam_strerror +#endif /* VBOX_AUTH_USE_PAM_DLLOAD */ + +static void debug_printf(const char *fmt, ...) +{ +#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV) || defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD) + va_list va; + + char buffer[1024]; + + const char *filename = NULL; + + va_start(va, fmt); + +#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV) + filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV); +#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV */ + +#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD) + if (filename == NULL) + { + filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV_OLD); + } +#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */ + + if (filename) + { + FILE *f; + + vsnprintf (buffer, sizeof (buffer), fmt, va); + + f = fopen (filename, "ab"); + if (f != NULL) + { + fprintf (f, "%s", buffer); + fclose (f); + } + } + + va_end (va); +#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV || VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */ +} + +#ifdef VBOX_AUTH_USE_PAM_DLLOAD + +static void *gpvLibPam = NULL; + +typedef struct _SymMap +{ + void **ppfn; + const char *pszName; +} SymMap; + +static SymMap symmap[] = +{ + { (void **)&fn_pam_start, "pam_start" }, + { (void **)&fn_pam_authenticate, "pam_authenticate" }, + { (void **)&fn_pam_acct_mgmt, "pam_acct_mgmt" }, + { (void **)&fn_pam_end, "pam_end" }, + { (void **)&fn_pam_strerror, "pam_strerror" }, + { NULL, NULL } +}; + +static int auth_pam_init(void) +{ + SymMap *iter; + + gpvLibPam = dlopen(PAM_LIB_NAME, RTLD_LAZY | RTLD_GLOBAL); + + if (!gpvLibPam) + { + debug_printf("auth_pam_init: dlopen %s failed\n", PAM_LIB_NAME); + return PAM_SYSTEM_ERR; + } + + iter = &symmap[0]; + + while (iter->pszName != NULL) + { + void *pv = dlsym (gpvLibPam, iter->pszName); + + if (pv == NULL) + { + debug_printf("auth_pam_init: dlsym %s failed\n", iter->pszName); + + dlclose(gpvLibPam); + gpvLibPam = NULL; + + return PAM_SYSTEM_ERR; + } + + *iter->ppfn = pv; + + iter++; + } + + return PAM_SUCCESS; +} + +static void auth_pam_close(void) +{ + if (gpvLibPam) + { + dlclose(gpvLibPam); + gpvLibPam = NULL; + } + + return; +} +#else +static int auth_pam_init(void) +{ + return PAM_SUCCESS; +} + +static void auth_pam_close(void) +{ + return; +} +#endif /* VBOX_AUTH_USE_PAM_DLLOAD */ + +static const char *auth_get_pam_service (void) +{ + const char *service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV); + + if (service == NULL) + { + service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD); + + if (service == NULL) + { + service = VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME; + } + } + + debug_printf ("Using PAM service: %s\n", service); + + return service; +} + +typedef struct _PamContext +{ + char *pszUser; + char *pszPassword; +} PamContext; + +static int conv (int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + int i; + struct pam_response *r; + + PamContext *ctx = (PamContext *)appdata_ptr; + + if (ctx == NULL) + { + debug_printf("conv: ctx is NULL\n"); + return PAM_CONV_ERR; + } + + debug_printf("conv: num %d u[%s] p[%d]\n", num_msg, ctx->pszUser, ctx->pszPassword? strlen (ctx->pszPassword): 0); + + r = (struct pam_response *) calloc (num_msg, sizeof (struct pam_response)); + + if (r == NULL) + { + return PAM_CONV_ERR; + } + + for (i = 0; i < num_msg; i++) + { + r[i].resp_retcode = 0; + + if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) + { + r[i].resp = strdup (ctx->pszPassword); + debug_printf("conv: %d returning password [%d]\n", i, r[i].resp? strlen (r[i].resp): 0); + } + else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) + { + r[i].resp = strdup (ctx->pszUser); + debug_printf("conv: %d returning name [%s]\n", i, r[i].resp); + } + else + { + debug_printf("conv: %d style %d: [%s]\n", i, msg[i]->msg_style, msg[i]->msg? msg[i]->msg: "(null)"); + r[i].resp = NULL; + } + } + + *resp = r; + return PAM_SUCCESS; +} + +/* The entry point must be visible. */ +#if defined(_MSC_VER) || defined(__OS2__) +# define DECLEXPORT(type) __declspec(dllexport) type +#else +# ifdef VBOX_HAVE_VISIBILITY_HIDDEN +# define DECLEXPORT(type) __attribute__((visibility("default"))) type +# else +# define DECLEXPORT(type) type +# endif +#endif + +/* prototype to prevent gcc warning */ +DECLEXPORT(AUTHENTRY3) AuthEntry; + +DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller, + PAUTHUUID pUuid, + AuthGuestJudgement guestJudgement, + const char *pszUser, + const char *pszPassword, + const char *pszDomain, + int fLogon, + unsigned clientId) +{ + AuthResult result = AuthResultAccessDenied; + int rc; + PamContext ctx; + struct pam_conv pam_conversation; + pam_handle_t *pam_handle = NULL; + + (void)pszCaller; + (void)pUuid; + (void)guestJudgement; + (void)clientId; + + /* Only process logon requests. */ + if (!fLogon) + return result; /* Return value is ignored by the caller. */ + + debug_printf("u[%s], d[%s], p[%d]\n", pszUser, pszDomain, pszPassword ? strlen(pszPassword) : 0); + + ctx.pszUser = (char *)pszUser; + ctx.pszPassword = (char *)pszPassword; + + pam_conversation.conv = conv; + pam_conversation.appdata_ptr = &ctx; + + rc = auth_pam_init (); + + if (rc == PAM_SUCCESS) + { + debug_printf("init ok\n"); + + rc = fn_pam_start(auth_get_pam_service (), pszUser, &pam_conversation, &pam_handle); + + if (rc == PAM_SUCCESS) + { + debug_printf("start ok\n"); + + rc = fn_pam_authenticate(pam_handle, 0); + + if (rc == PAM_SUCCESS) + { + debug_printf("auth ok\n"); + + rc = fn_pam_acct_mgmt(pam_handle, 0); + if (rc == PAM_AUTHINFO_UNAVAIL + && + getenv("VBOX_PAM_ALLOW_INACTIVE") != NULL) + { + debug_printf("PAM_AUTHINFO_UNAVAIL\n"); + rc = PAM_SUCCESS; + } + + if (rc == PAM_SUCCESS) + { + debug_printf("access granted\n"); + + result = AuthResultAccessGranted; + } + else + { + debug_printf("pam_acct_mgmt failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc)); + } + } + else + { + debug_printf("pam_authenticate failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc)); + } + + fn_pam_end(pam_handle, rc); + } + else + { + debug_printf("pam_start failed %d\n", rc); + } + + auth_pam_close (); + + debug_printf("auth_pam_close completed\n"); + } + else + { + debug_printf("auth_pam_init failed %d\n", rc); + } + + return result; +} + diff --git a/src/VBox/HostServices/auth/simple/Makefile.kup b/src/VBox/HostServices/auth/simple/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/simple/Makefile.kup diff --git a/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp new file mode 100644 index 00000000..436d4b41 --- /dev/null +++ b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.cpp @@ -0,0 +1,137 @@ +/* $Id: VBoxAuthSimple.cpp $ */ +/** @file + * VirtualBox External Authentication Library - Simple Authentication. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <iprt/cdefs.h> +#include <iprt/uuid.h> +#include <iprt/sha.h> + +#include <VBox/VBoxAuth.h> + +#include <VBox/com/com.h> +#include <VBox/com/string.h> +#include <VBox/com/Guid.h> +#include <VBox/com/VirtualBox.h> + +using namespace com; + +/* If defined, debug messages will be written to the specified file. */ +//#define AUTH_DEBUG_FILE_NAME "/tmp/VBoxAuth.log" + + +static void dprintf(const char *pszFormat, ...) +{ +#ifdef AUTH_DEBUG_FILE_NAME + FILE *f = fopen(AUTH_DEBUG_FILE_NAME, "ab"); + if (f) + { + va_list va; + va_start(va, pszFormat); + vfprintf(f, pszFormat, va); + va_end(va); + fclose(f); + } +#else + RT_NOREF(pszFormat); +#endif +} + +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, guestJudgement, pszDomain, clientId); + + /* default is failed */ + AuthResult result = AuthResultAccessDenied; + + /* only interested in logon */ + if (!fLogon) + /* return value ignored */ + return result; + + char uuid[RTUUID_STR_LENGTH] = {0}; + if (pUuid) + RTUuidToStr((PCRTUUID)pUuid, (char*)uuid, RTUUID_STR_LENGTH); + + /* the user might contain a domain name, split it */ + const char *user = strchr(pszUser, '\\'); + if (user) + user++; + else + user = (char*)pszUser; + + dprintf("VBoxAuth: uuid: %s, user: %s, pszPassword: %s\n", uuid, user, pszPassword); + + ComPtr<IVirtualBoxClient> virtualBoxClient; + ComPtr<IVirtualBox> virtualBox; + HRESULT rc; + + rc = virtualBoxClient.createInprocObject(CLSID_VirtualBoxClient); + if (SUCCEEDED(rc)) + { + rc = virtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam()); + if (SUCCEEDED(rc)) + { + Bstr key = BstrFmt("VBoxAuthSimple/users/%s", user); + Bstr password; + + /* lookup in VM's extra data? */ + if (pUuid) + { + ComPtr<IMachine> machine; + virtualBox->FindMachine(Bstr(uuid).raw(), machine.asOutParam()); + if (machine) + machine->GetExtraData(key.raw(), password.asOutParam()); + } + else + /* lookup global extra data */ + virtualBox->GetExtraData(key.raw(), password.asOutParam()); + + if (!password.isEmpty()) + { + /* calculate hash */ + uint8_t abDigest[RTSHA256_HASH_SIZE]; + RTSha256(pszPassword, strlen(pszPassword), abDigest); + char pszDigest[RTSHA256_DIGEST_LEN + 1]; + RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest)); + + if (password == pszDigest) + result = AuthResultAccessGranted; + } + } + else + dprintf("VBoxAuth: failed to get VirtualBox object reference: %#x\n", rc); + } + else + dprintf("VBoxAuth: failed to get VirtualBoxClient object reference: %#x\n", rc); + + return result; +} + diff --git a/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc new file mode 100644 index 00000000..616187ec --- /dev/null +++ b/src/VBox/HostServices/auth/simple/VBoxAuthSimple.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxAuthSimple.rc $ */ +/** @file + * VBoxAuthSimple - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Simple Authentication Host Service\0" + VALUE "InternalName", "VBoxAuthSimple\0" + VALUE "OriginalFilename", "VBoxAuthSimple.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/auth/winlogon/Makefile.kup b/src/VBox/HostServices/auth/winlogon/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostServices/auth/winlogon/Makefile.kup diff --git a/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc b/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc new file mode 100644 index 00000000..b24ed0fe --- /dev/null +++ b/src/VBox/HostServices/auth/winlogon/VBoxAuth.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxAuth.rc $ */ +/** @file + * VBoxAuth - Resource file containing version info and icon. + */ + +/* + * Copyright (C) 2015-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <windows.h> +#include <VBox/version.h> + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VBOX_RC_FILE_VERSION + PRODUCTVERSION VBOX_RC_FILE_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS VBOX_RC_FILE_FLAGS + FILEOS VBOX_RC_FILE_OS + FILETYPE VBOX_RC_TYPE_DLL + FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // Lang=US English, CharSet=Unicode + BEGIN + VALUE "FileDescription", "VirtualBox Authentication Host Service\0" + VALUE "InternalName", "VBoxAuth\0" + VALUE "OriginalFilename", "VBoxAuth.dll\0" + VALUE "CompanyName", VBOX_RC_COMPANY_NAME + VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR + VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT + VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR + VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR + VBOX_RC_MORE_STRINGS + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/VBox/HostServices/auth/winlogon/winlogon.cpp b/src/VBox/HostServices/auth/winlogon/winlogon.cpp new file mode 100644 index 00000000..4db9b7a5 --- /dev/null +++ b/src/VBox/HostServices/auth/winlogon/winlogon.cpp @@ -0,0 +1,171 @@ +/* $Id: winlogon.cpp $ */ +/** @file + * VirtualBox External Authentication Library - Windows Logon Authentication. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* If defined, debug messages will be written to the debugger. */ +// #define AUTH_DEBUG + +#include <iprt/win/windows.h> +#include <VBox/VBoxAuth.h> +#include <iprt/cdefs.h> + +#ifdef AUTH_DEBUG +# include <stdio.h> + +static void dprintfw(const WCHAR *fmt, ...) +{ + va_list va; + va_start(va, fmt); + + WCHAR buffer[1024]; + + _vsnwprintf(buffer, sizeof (buffer), fmt, va); + + OutputDebugStringW(buffer); + + va_end(va); +} +# define DBGAUTH(a) dprintfw a +#else +# define DBGAUTH(a) +#endif + +static WCHAR g_wszEmpty[] = { L"" }; + +static void freeWideChar(WCHAR *pwszString) +{ + if (pwszString && pwszString != &g_wszEmpty[0]) + { + size_t cb = (wcslen(pwszString) + 1) * sizeof(WCHAR); + SecureZeroMemory(pwszString, cb); + free(pwszString); + } +} + +static WCHAR *utf8ToWideChar(const char *pszString) +{ + /* + * Shortcut for empty strings. + */ + if (!pszString || *pszString == 0) + return &g_wszEmpty[0]; + + /* + * Return NULL on errors. + */ + WCHAR *pwszString = NULL; + + /* + * First calc result string length. + */ + const DWORD dwFlags = MB_ERR_INVALID_CHARS; + int cwc = MultiByteToWideChar(CP_UTF8, dwFlags, pszString, -1, NULL, 0); + if (cwc > 0) + { + /* + * Alloc space for result buffer. + */ + pwszString = (WCHAR *)malloc(cwc * sizeof(WCHAR)); + if (pwszString) + { + /* + * Do the translation. + */ + if (MultiByteToWideChar(CP_UTF8, dwFlags, pszString, -1, pwszString, cwc) <= 0) + { + /* translation error */ + free(pwszString); + pwszString = NULL; + } + } + } + + return pwszString; +} + +/* Prototype it to make sure we've got the right prototype. */ +extern "C" +#if defined(_MSC_VER) +__declspec(dllexport) +#endif +FNAUTHENTRY3 AuthEntry; + +/** + * @callback_method_impl{FNAUTHENTRY3} + */ +extern "C" +AuthResult AUTHCALL AuthEntry(const char *pszCaller, + PAUTHUUID pUuid, + AuthGuestJudgement guestJudgement, + const char *pszUser, + const char *pszPassword, + const char *pszDomain, + int fLogon, + unsigned clientId) +{ + RT_NOREF4(pszCaller, pUuid, guestJudgement, clientId); + if (!fLogon) + { + /* Nothing to cleanup. The return code does not matter. */ + return AuthResultAccessDenied; + } + + LPWSTR pwszUsername = utf8ToWideChar(pszUser); + LPWSTR pwszDomain = utf8ToWideChar(pszDomain); + LPWSTR pwszPassword = utf8ToWideChar(pszPassword); + + DBGAUTH((L"u[%ls], d[%ls], p[%ls]\n", lpwszUsername, lpwszDomain, lpwszPassword)); + + AuthResult result = AuthResultAccessDenied; + + if (pwszUsername && pwszDomain && pwszPassword) + { + /* LOGON32_LOGON_INTERACTIVE is intended for users who will be interactively using the computer, + * such as a user being logged on by a terminal server, remote shell, or similar process. + */ + DWORD dwLogonType = LOGON32_LOGON_INTERACTIVE; + DWORD dwLogonProvider = LOGON32_PROVIDER_DEFAULT; + + HANDLE hToken; + + BOOL fSuccess = LogonUserW(pwszUsername, + pwszDomain, + pwszPassword, + dwLogonType, + dwLogonProvider, + &hToken); + + if (fSuccess) + { + DBGAUTH((L"LogonUser success. hToken = %p\n", hToken)); + + result = AuthResultAccessGranted; + + CloseHandle(hToken); + } + else + { + DBGAUTH((L"LogonUser failed %08X\n", GetLastError())); + } + } + + freeWideChar(pwszUsername); + freeWideChar(pwszDomain); + freeWideChar(pwszPassword); + + return result; +} + |