diff options
Diffstat (limited to 'sal/osl/w32/security.cxx')
-rw-r--r-- | sal/osl/w32/security.cxx | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/sal/osl/w32/security.cxx b/sal/osl/w32/security.cxx new file mode 100644 index 000000000..21ed64a78 --- /dev/null +++ b/sal/osl/w32/security.cxx @@ -0,0 +1,662 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you 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 . + */ + +#include "system.h" +#include <userenv.h> + +#include <cassert> +#include <osl/security.h> +#include <osl/diagnose.h> +#include <osl/thread.h> +#include <osl/file.h> +#include <systools/win32/uwinapi.h> +#include <sddl.h> +#include <sal/macros.h> +#include <sal/log.hxx> +#include <o3tl/char16_t2wchar_t.hxx> +#include "secimpl.hxx" + +/* To get an impersonation token we need to create an impersonation + duplicate so every access token has to be created with duplicate + access rights */ + +#define TOKEN_DUP_QUERY (TOKEN_QUERY|TOKEN_DUPLICATE) + +static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder); +// We use LPCTSTR here, because we use it with SE_foo_NAME constants +// which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName") +static bool Privilege(LPCTSTR pszPrivilege, bool bEnable); +static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain); + +oslSecurity SAL_CALL osl_getCurrentSecurity(void) +{ + oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl))); + if (pSecImpl) + { + pSecImpl->m_pNetResource = nullptr; + pSecImpl->m_User[0] = '\0'; + pSecImpl->m_hToken = nullptr; + pSecImpl->m_hProfile = nullptr; + } + return pSecImpl; +} + +oslSecurityError SAL_CALL osl_loginUser( rtl_uString *strUserName, rtl_uString *strPasswd, oslSecurity *pSecurity ) +{ + oslSecurityError ret; + + sal_Unicode* strUser; + sal_Unicode* strDomain = o3tl::toU(_wcsdup(o3tl::toW(rtl_uString_getStr(strUserName)))); + HANDLE hUserToken; + LUID luid; + + if (nullptr != (strUser = o3tl::toU(wcschr(o3tl::toW(strDomain), L'/')))) + *strUser++ = L'\0'; + else + { + strUser = strDomain; + strDomain = nullptr; + } + + // this process must have the right: 'act as a part of operatingsystem' + OSL_ASSERT(LookupPrivilegeValue(nullptr, SE_TCB_NAME, &luid)); + (void) luid; + + if (LogonUserW(o3tl::toW(strUser), strDomain ? o3tl::toW(strDomain) : L"", o3tl::toW(rtl_uString_getStr(strPasswd)), + LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, + &hUserToken)) + { + oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl))); + if (pSecImpl) + { + pSecImpl->m_pNetResource = nullptr; + pSecImpl->m_hToken = hUserToken; + pSecImpl->m_hProfile = nullptr; + wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(strUser)); + } + *pSecurity = pSecImpl; + ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown; + } + else + { + ret = osl_Security_E_UserUnknown; + } + + if (strDomain) + free(strDomain); + else + free(strUser); + + return ret; +} + +oslSecurityError SAL_CALL osl_loginUserOnFileServer(rtl_uString *strUserName, + rtl_uString *strPasswd, + rtl_uString *strFileServer, + oslSecurity *pSecurity) +{ + oslSecurityError ret; + DWORD err; + NETRESOURCEW netResource; + sal_Unicode* remoteName; + sal_Unicode* userName; + + remoteName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 4) * sizeof(sal_Unicode))); + userName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 2) * sizeof(sal_Unicode))); + + wcscpy(o3tl::toW(remoteName), L"\\\\"); + wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strFileServer))); + wcscat(o3tl::toW(remoteName), L"\\"); + wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strUserName))); + + wcscpy(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strFileServer))); + wcscat(o3tl::toW(userName), L"\\"); + wcscat(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strUserName))); + + netResource.dwScope = RESOURCE_GLOBALNET; + netResource.dwType = RESOURCETYPE_DISK; + netResource.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; + netResource.dwUsage = RESOURCEUSAGE_CONNECTABLE; + netResource.lpLocalName = nullptr; + netResource.lpRemoteName = o3tl::toW(remoteName); + netResource.lpComment = nullptr; + netResource.lpProvider = nullptr; + + err = WNetAddConnection2W(&netResource, o3tl::toW(rtl_uString_getStr(strPasswd)), o3tl::toW(userName), 0); + + if ((err == NO_ERROR) || (err == ERROR_ALREADY_ASSIGNED)) + { + oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl))); + if (pSecImpl) + { + pSecImpl->m_pNetResource = static_cast<NETRESOURCEW *>(malloc(sizeof(NETRESOURCE))); + if (pSecImpl->m_pNetResource) + { + *pSecImpl->m_pNetResource = netResource; + pSecImpl->m_hToken = nullptr; + pSecImpl->m_hProfile = nullptr; + wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(rtl_uString_getStr(strUserName))); + } + else + { + free(pSecImpl); + pSecImpl = nullptr; + } + } + *pSecurity = pSecImpl; + + ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown; + } + else + { + ret = osl_Security_E_UserUnknown; + } + + free(remoteName); + free(userName); + + return ret; +} + +sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security) +{ + if (!Security) + return false; + + HANDLE hImpersonationToken = nullptr; + PSID psidAdministrators; + SID_IDENTIFIER_AUTHORITY siaNtAuthority = { SECURITY_NT_AUTHORITY }; + bool bSuccess = false; + + /* If Security contains an access token we need to duplicate it to an impersonation + access token. NULL works with CheckTokenMembership() as the current effective + impersonation token + */ + + if ( static_cast<oslSecurityImpl*>(Security)->m_hToken ) + { + if ( !DuplicateToken (static_cast<oslSecurityImpl*>(Security)->m_hToken, SecurityImpersonation, &hImpersonationToken) ) + return false; + } + + /* CheckTokenMembership() can be used on W2K and higher (NT4 no longer supported by OOo) + and also works on Vista to retrieve the effective user rights. Just checking for + membership of Administrators group is not enough on Vista this would require additional + complicated checks as described in KB article Q118626: http://support.microsoft.com/kb/118626/en-us + */ + + if (AllocateAndInitializeSid(&siaNtAuthority, + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &psidAdministrators)) + { + BOOL fSuccess = FALSE; + + if (CheckTokenMembership(hImpersonationToken, psidAdministrators, &fSuccess) && fSuccess) + bSuccess = true; + + FreeSid(psidAdministrators); + } + + if (hImpersonationToken) + CloseHandle(hImpersonationToken); + + return bSuccess; +} + +void SAL_CALL osl_freeSecurityHandle(oslSecurity Security) +{ + if (!Security) + return; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + if (pSecImpl->m_pNetResource != nullptr) + { + WNetCancelConnection2W(pSecImpl->m_pNetResource->lpRemoteName, 0, true); + + free(pSecImpl->m_pNetResource->lpRemoteName); + free(pSecImpl->m_pNetResource); + } + + if (pSecImpl->m_hToken) + CloseHandle(pSecImpl->m_hToken); + + if ( pSecImpl->m_hProfile ) + CloseHandle(pSecImpl->m_hProfile); + + free (pSecImpl); +} + +sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **strIdent) +{ + if (!Security) + return false; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + HANDLE hAccessToken = pSecImpl->m_hToken; + + if (hAccessToken == nullptr) + OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken); + + if (hAccessToken) + { + DWORD nInfoBuffer = 512; + UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer)); + + while (!GetTokenInformation(hAccessToken, TokenUser, + pInfoBuffer, nInfoBuffer, &nInfoBuffer)) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer))) + pInfoBuffer = p; + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + + if (pSecImpl->m_hToken == nullptr) + CloseHandle(hAccessToken); + + if (pInfoBuffer) + { + PSID pSid = reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid; + + LPWSTR pSidStr = nullptr; + bool bResult = ConvertSidToStringSidW(pSid, &pSidStr); + if (bResult) + { + rtl_uString_newFromStr(strIdent, o3tl::toU(pSidStr)); + LocalFree(pSidStr); + } + else + { + const DWORD dwError = GetLastError(); + SAL_WARN( + "sal.osl", + "ConvertSidToStringSidW failed. GetLastError returned: " << dwError); + } + + free(pInfoBuffer); + + return bResult; + } + } + else + { + DWORD needed = 0; + + WNetGetUserW(nullptr, nullptr, &needed); + if (needed < 16) + needed = 16; + + if (auto Ident = static_cast<sal_Unicode *>(malloc(needed*sizeof(sal_Unicode)))) + { + if (WNetGetUserW(nullptr, o3tl::toW(Ident), &needed) != NO_ERROR) + { + wcscpy(o3tl::toW(Ident), L"unknown"); + Ident[7] = L'\0'; + } + + rtl_uString_newFromStr( strIdent, Ident); + free(Ident); + return true; + } + } + return false; +} + +sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **strName) +{ + return getUserNameImpl(Security, strName, true); +} + +sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **strName) +{ + return getUserNameImpl(Security, strName, false); +} + +sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory) +{ + if (!Security) + return false; + + rtl_uString *ustrSysDir = nullptr; + bool bSuccess = false; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + if (pSecImpl->m_pNetResource != nullptr) + { + rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName)); + + bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory ); + } + else + { + bSuccess = GetSpecialFolder(&ustrSysDir, FOLDERID_Documents) && + (osl_File_E_None == osl_getFileURLFromSystemPath(ustrSysDir, pustrDirectory)); + } + + if ( ustrSysDir ) + rtl_uString_release( ustrSysDir ); + + return bSuccess; +} + +sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory) +{ + if (!Security) + return false; + + bool bSuccess = false; + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + if (pSecImpl->m_pNetResource != nullptr) + { + rtl_uString *ustrSysDir = nullptr; + + rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName)); + bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory); + + if ( ustrSysDir ) + rtl_uString_release( ustrSysDir ); + } + else + { + if (pSecImpl->m_hToken) + { + /* not implemented */ + OSL_ASSERT(false); + } + else + { + rtl_uString *ustrFile = nullptr; + sal_Unicode sFile[_MAX_PATH]; + + if ( !GetSpecialFolder( &ustrFile, FOLDERID_RoamingAppData) ) + { + OSL_VERIFY(GetWindowsDirectoryW(o3tl::toW(sFile), _MAX_DIR) > 0); + + rtl_uString_newFromStr( &ustrFile, sFile); + } + + bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath(ustrFile, pustrDirectory); + + if ( ustrFile ) + rtl_uString_release( ustrFile ); + } + } + + return bSuccess; +} + +sal_Bool SAL_CALL osl_loadUserProfile(oslSecurity Security) +{ + /* CreateProcessAsUser does not load the specified user's profile + into the HKEY_USERS registry key. This means that access to information + in the HKEY_CURRENT_USER registry key may not produce results consistent + with a normal interactive logon. + It is your responsibility to load the user's registry hive into HKEY_USERS + with the LoadUserProfile function before calling CreateProcessAsUser. + */ + + RegCloseKey(HKEY_CURRENT_USER); + + if (!Privilege(SE_RESTORE_NAME, true)) + return false; + + bool bOk = false; + HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken; + + /* try to create user profile */ + if ( !hAccessToken ) + { + /* retrieve security handle if not done before e.g. osl_getCurrentSecurity() + */ + HANDLE hProcess = GetCurrentProcess(); + + if (hProcess != nullptr) + { + OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken); + CloseHandle(hProcess); + } + } + + rtl_uString *buffer = nullptr; + PROFILEINFOW pi; + + getUserNameImpl(Security, &buffer, false); + + ZeroMemory(&pi, sizeof(pi)); + pi.dwSize = sizeof(pi); + pi.lpUserName = o3tl::toW(rtl_uString_getStr(buffer)); + pi.dwFlags = PI_NOUI; + + if (LoadUserProfileW(hAccessToken, &pi)) + { + UnloadUserProfile(hAccessToken, pi.hProfile); + + bOk = true; + } + + rtl_uString_release(buffer); + + if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken)) + CloseHandle(hAccessToken); + + return bOk; +} + +void SAL_CALL osl_unloadUserProfile(oslSecurity Security) +{ + if ( static_cast<oslSecurityImpl*>(Security)->m_hProfile == nullptr ) + return; + + HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken; + + if ( !hAccessToken ) + { + /* retrieve security handle if not done before e.g. osl_getCurrentSecurity() + */ + HANDLE hProcess = GetCurrentProcess(); + + if (hProcess != nullptr) + { + OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken); + CloseHandle(hProcess); + } + } + + /* unloading the user profile */ + UnloadUserProfile(hAccessToken, static_cast<oslSecurityImpl*>(Security)->m_hProfile); + + static_cast<oslSecurityImpl*>(Security)->m_hProfile = nullptr; + + if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken)) + CloseHandle(hAccessToken); +} + +static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder) +{ + bool bRet = false; + PWSTR PathW; + if (SUCCEEDED(SHGetKnownFolderPath(rFolder, KF_FLAG_CREATE, nullptr, &PathW))) + { + rtl_uString_newFromStr(strPath, o3tl::toU(PathW)); + CoTaskMemFree(PathW); + bRet = true; + } + + return bRet; +} + +// We use LPCTSTR here, because we use it with SE_foo_NAME constants +// which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName") +static bool Privilege(LPCTSTR strPrivilege, bool bEnable) +{ + HANDLE hToken; + TOKEN_PRIVILEGES tp; + + // obtain the processes token + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_DUP_QUERY, &hToken)) + return false; + + // get the luid + if (!LookupPrivilegeValue(nullptr, strPrivilege, &tp.Privileges[0].Luid)) + return false; + + tp.PrivilegeCount = 1; + + if (bEnable) + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + else + tp.Privileges[0].Attributes = 0; + + // enable or disable the privilege + if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, nullptr, nullptr)) + return false; + + if (!CloseHandle(hToken)) + return false; + + return true; +} + +static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain) +{ + if (!Security) + return false; + + oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security); + + HANDLE hAccessToken = pSecImpl->m_hToken; + + if (hAccessToken == nullptr) + OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken); + + if (hAccessToken) + { + DWORD nInfoBuffer = 512; + UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer)); + + while (!GetTokenInformation(hAccessToken, TokenUser, + pInfoBuffer, nInfoBuffer, &nInfoBuffer)) + { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer))) + pInfoBuffer = p; + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + else + { + free(pInfoBuffer); + pInfoBuffer = nullptr; + break; + } + } + + if (pSecImpl->m_hToken == nullptr) + CloseHandle(hAccessToken); + + if (pInfoBuffer) + { + sal_Unicode UserName[128]; + sal_Unicode DomainName[128]; + sal_Unicode Name[257]; + DWORD nUserName = SAL_N_ELEMENTS(UserName); + DWORD nDomainName = SAL_N_ELEMENTS(DomainName); + SID_NAME_USE sUse; + + if (LookupAccountSidW(nullptr, reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid, + o3tl::toW(UserName), &nUserName, + o3tl::toW(DomainName), &nDomainName, &sUse)) + { + if (bIncludeDomain) + { + wcscpy(o3tl::toW(Name), o3tl::toW(DomainName)); + wcscat(o3tl::toW(Name), L"/"); + wcscat(o3tl::toW(Name), o3tl::toW(UserName)); + } + else + { + wcscpy(o3tl::toW(Name), o3tl::toW(UserName)); + } + + rtl_uString_newFromStr(strName, Name); + free(pInfoBuffer); + return true; + } + } + } + else + { + DWORD needed=0; + sal_Unicode *pNameW=nullptr; + + WNetGetUserW(nullptr, nullptr, &needed); + pNameW = static_cast<sal_Unicode *>(malloc (needed*sizeof(sal_Unicode))); + assert(pNameW); // Don't handle OOM conditions + + if (WNetGetUserW(nullptr, o3tl::toW(pNameW), &needed) == NO_ERROR) + { + rtl_uString_newFromStr( strName, pNameW); + + if (pNameW) + free(pNameW); + return true; + } + else if (pSecImpl->m_User[0] != '\0') + { + rtl_uString_newFromStr(strName, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName)); + + if (pNameW) + free(pNameW); + + return true; + } + + if (pNameW) + free(pNameW); + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |