Branch libreoffice-6-3
[LibreOffice.git] / sal / osl / w32 / security.cxx
blobd0872ef2c19c1df4d20e9cc678d5e948afb33be6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "system.h"
21 #include <userenv.h>
23 #include <cassert>
24 #include <osl/security.h>
25 #include <osl/diagnose.h>
26 #include <osl/thread.h>
27 #include <osl/file.h>
28 #include <systools/win32/uwinapi.h>
29 #include <sddl.h>
30 #include <sal/macros.h>
31 #include <sal/log.hxx>
32 #include <o3tl/char16_t2wchar_t.hxx>
33 #include "secimpl.hxx"
35 /* To get an impersonation token we need to create an impersonation
36 duplicate so every access token has to be created with duplicate
37 access rights */
39 #define TOKEN_DUP_QUERY (TOKEN_QUERY|TOKEN_DUPLICATE)
41 static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder);
42 // We use LPCTSTR here, because we use it with SE_foo_NAME constants
43 // which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName")
44 static BOOL Privilege(LPCTSTR pszPrivilege, BOOL bEnable);
45 static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain);
47 oslSecurity SAL_CALL osl_getCurrentSecurity(void)
49 oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
50 if (pSecImpl)
52 pSecImpl->m_pNetResource = nullptr;
53 pSecImpl->m_User[0] = '\0';
54 pSecImpl->m_hToken = nullptr;
55 pSecImpl->m_hProfile = nullptr;
57 return pSecImpl;
60 oslSecurityError SAL_CALL osl_loginUser( rtl_uString *strUserName, rtl_uString *strPasswd, oslSecurity *pSecurity )
62 oslSecurityError ret;
64 sal_Unicode* strUser;
65 sal_Unicode* strDomain = o3tl::toU(_wcsdup(o3tl::toW(rtl_uString_getStr(strUserName))));
66 HANDLE hUserToken;
67 LUID luid;
69 if (nullptr != (strUser = o3tl::toU(wcschr(o3tl::toW(strDomain), L'/'))))
70 *strUser++ = L'\0';
71 else
73 strUser = strDomain;
74 strDomain = nullptr;
77 // this process must have the right: 'act as a part of operatingsystem'
78 OSL_ASSERT(LookupPrivilegeValue(nullptr, SE_TCB_NAME, &luid));
79 (void) luid;
81 if (LogonUserW(o3tl::toW(strUser), strDomain ? o3tl::toW(strDomain) : L"", o3tl::toW(rtl_uString_getStr(strPasswd)),
82 LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
83 &hUserToken))
85 oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
86 if (pSecImpl)
88 pSecImpl->m_pNetResource = nullptr;
89 pSecImpl->m_hToken = hUserToken;
90 pSecImpl->m_hProfile = nullptr;
91 wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(strUser));
93 *pSecurity = pSecImpl;
94 ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown;
96 else
98 ret = osl_Security_E_UserUnknown;
101 if (strDomain)
102 free(strDomain);
103 else
104 free(strUser);
106 return ret;
109 oslSecurityError SAL_CALL osl_loginUserOnFileServer(rtl_uString *strUserName,
110 rtl_uString *strPasswd,
111 rtl_uString *strFileServer,
112 oslSecurity *pSecurity)
114 oslSecurityError ret;
115 DWORD err;
116 NETRESOURCEW netResource;
117 sal_Unicode* remoteName;
118 sal_Unicode* userName;
120 remoteName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 4) * sizeof(sal_Unicode)));
121 userName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 2) * sizeof(sal_Unicode)));
123 wcscpy(o3tl::toW(remoteName), L"\\\\");
124 wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strFileServer)));
125 wcscat(o3tl::toW(remoteName), L"\\");
126 wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strUserName)));
128 wcscpy(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strFileServer)));
129 wcscat(o3tl::toW(userName), L"\\");
130 wcscat(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strUserName)));
132 netResource.dwScope = RESOURCE_GLOBALNET;
133 netResource.dwType = RESOURCETYPE_DISK;
134 netResource.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
135 netResource.dwUsage = RESOURCEUSAGE_CONNECTABLE;
136 netResource.lpLocalName = nullptr;
137 netResource.lpRemoteName = o3tl::toW(remoteName);
138 netResource.lpComment = nullptr;
139 netResource.lpProvider = nullptr;
141 err = WNetAddConnection2W(&netResource, o3tl::toW(rtl_uString_getStr(strPasswd)), o3tl::toW(userName), 0);
143 if ((err == NO_ERROR) || (err == ERROR_ALREADY_ASSIGNED))
145 oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
146 if (pSecImpl)
148 pSecImpl->m_pNetResource = static_cast<NETRESOURCEW *>(malloc(sizeof(NETRESOURCE)));
149 if (pSecImpl->m_pNetResource)
151 *pSecImpl->m_pNetResource = netResource;
152 pSecImpl->m_hToken = nullptr;
153 pSecImpl->m_hProfile = nullptr;
154 wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(rtl_uString_getStr(strUserName)));
156 else
158 free(pSecImpl);
159 pSecImpl = nullptr;
162 *pSecurity = pSecImpl;
164 ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown;
166 else
168 ret = osl_Security_E_UserUnknown;
171 free(remoteName);
172 free(userName);
174 return ret;
177 sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security)
179 if (Security != nullptr)
181 HANDLE hImpersonationToken = nullptr;
182 PSID psidAdministrators;
183 SID_IDENTIFIER_AUTHORITY siaNtAuthority = { SECURITY_NT_AUTHORITY };
184 bool bSuccess = false;
186 /* If Security contains an access token we need to duplicate it to an impersonation
187 access token. NULL works with CheckTokenMembership() as the current effective
188 impersonation token
191 if ( static_cast<oslSecurityImpl*>(Security)->m_hToken )
193 if ( !DuplicateToken (static_cast<oslSecurityImpl*>(Security)->m_hToken, SecurityImpersonation, &hImpersonationToken) )
194 return false;
197 /* CheckTokenMembership() can be used on W2K and higher (NT4 no longer supported by OOo)
198 and also works on Vista to retrieve the effective user rights. Just checking for
199 membership of Administrators group is not enough on Vista this would require additional
200 complicated checks as described in KB article Q118626: http://support.microsoft.com/kb/118626/en-us
203 if (AllocateAndInitializeSid(&siaNtAuthority,
205 SECURITY_BUILTIN_DOMAIN_RID,
206 DOMAIN_ALIAS_RID_ADMINS,
207 0, 0, 0, 0, 0, 0,
208 &psidAdministrators))
210 BOOL fSuccess = FALSE;
212 if (CheckTokenMembership(hImpersonationToken, psidAdministrators, &fSuccess) && fSuccess)
213 bSuccess = true;
215 FreeSid(psidAdministrators);
218 if (hImpersonationToken)
219 CloseHandle(hImpersonationToken);
221 return bSuccess;
223 else
225 return false;
229 void SAL_CALL osl_freeSecurityHandle(oslSecurity Security)
231 if (Security)
233 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
235 if (pSecImpl->m_pNetResource != nullptr)
237 WNetCancelConnection2W(pSecImpl->m_pNetResource->lpRemoteName, 0, true);
239 free(pSecImpl->m_pNetResource->lpRemoteName);
240 free(pSecImpl->m_pNetResource);
243 if (pSecImpl->m_hToken)
244 CloseHandle(pSecImpl->m_hToken);
246 if ( pSecImpl->m_hProfile )
247 CloseHandle(pSecImpl->m_hProfile);
249 free (pSecImpl);
253 sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **strIdent)
255 if (Security != nullptr)
257 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
259 HANDLE hAccessToken = pSecImpl->m_hToken;
261 if (hAccessToken == nullptr)
262 OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken);
264 if (hAccessToken)
266 DWORD nInfoBuffer = 512;
267 UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer));
269 while (!GetTokenInformation(hAccessToken, TokenUser,
270 pInfoBuffer, nInfoBuffer, &nInfoBuffer))
272 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
274 if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer)))
275 pInfoBuffer = p;
276 else
278 free(pInfoBuffer);
279 pInfoBuffer = nullptr;
280 break;
283 else
285 free(pInfoBuffer);
286 pInfoBuffer = nullptr;
287 break;
291 if (pSecImpl->m_hToken == nullptr)
292 CloseHandle(hAccessToken);
294 if (pInfoBuffer)
296 PSID pSid = reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid;
298 LPWSTR pSidStr = nullptr;
299 BOOL bResult = ConvertSidToStringSidW(pSid, &pSidStr);
300 if (bResult)
302 rtl_uString_newFromStr(strIdent, o3tl::toU(pSidStr));
303 LocalFree(pSidStr);
305 else
307 const DWORD dwError = GetLastError();
308 SAL_WARN(
309 "sal.osl",
310 "ConvertSidToStringSidW failed. GetLastError returned: " << dwError);
313 free(pInfoBuffer);
315 return bResult != FALSE;
318 else
320 DWORD needed = 0;
322 WNetGetUserW(nullptr, nullptr, &needed);
323 if (needed < 16)
324 needed = 16;
326 if (auto Ident = static_cast<sal_Unicode *>(malloc(needed*sizeof(sal_Unicode))))
328 if (WNetGetUserW(nullptr, o3tl::toW(Ident), &needed) != NO_ERROR)
330 wcscpy(o3tl::toW(Ident), L"unknown");
331 Ident[7] = L'\0';
334 rtl_uString_newFromStr( strIdent, Ident);
335 free(Ident);
336 return true;
341 return false;
344 sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **strName)
346 return getUserNameImpl(Security, strName, true);
349 sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **strName)
351 return getUserNameImpl(Security, strName, false);
354 sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory)
356 rtl_uString *ustrSysDir = nullptr;
357 bool bSuccess = false;
359 if (Security != nullptr)
361 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
363 if (pSecImpl->m_pNetResource != nullptr)
365 rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
367 bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory );
369 else
371 bSuccess = GetSpecialFolder(&ustrSysDir, FOLDERID_Documents) &&
372 (osl_File_E_None == osl_getFileURLFromSystemPath(ustrSysDir, pustrDirectory));
376 if ( ustrSysDir )
377 rtl_uString_release( ustrSysDir );
379 return bSuccess;
382 sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory)
384 bool bSuccess = false;
386 if (Security != nullptr)
388 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
390 if (pSecImpl->m_pNetResource != nullptr)
392 rtl_uString *ustrSysDir = nullptr;
394 rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
395 bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory);
397 if ( ustrSysDir )
398 rtl_uString_release( ustrSysDir );
400 else
402 if (pSecImpl->m_hToken)
404 /* not implemented */
405 OSL_ASSERT(false);
407 else
409 rtl_uString *ustrFile = nullptr;
410 sal_Unicode sFile[_MAX_PATH];
412 if ( !GetSpecialFolder( &ustrFile, FOLDERID_RoamingAppData) )
414 OSL_VERIFY(GetWindowsDirectoryW(o3tl::toW(sFile), _MAX_DIR) > 0);
416 rtl_uString_newFromStr( &ustrFile, sFile);
419 bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath(ustrFile, pustrDirectory);
421 if ( ustrFile )
422 rtl_uString_release( ustrFile );
427 return bSuccess;
430 sal_Bool SAL_CALL osl_loadUserProfile(oslSecurity Security)
432 /* CreateProcessAsUser does not load the specified user's profile
433 into the HKEY_USERS registry key. This means that access to information
434 in the HKEY_CURRENT_USER registry key may not produce results consistent
435 with a normal interactive logon.
436 It is your responsibility to load the user's registry hive into HKEY_USERS
437 with the LoadUserProfile function before calling CreateProcessAsUser.
439 bool bOk = false;
441 RegCloseKey(HKEY_CURRENT_USER);
443 if (Privilege(SE_RESTORE_NAME, TRUE))
445 HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken;
447 /* try to create user profile */
448 if ( !hAccessToken )
450 /* retrieve security handle if not done before e.g. osl_getCurrentSecurity()
452 HANDLE hProcess = GetCurrentProcess();
454 if (hProcess != nullptr)
456 OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken);
457 CloseHandle(hProcess);
461 rtl_uString *buffer = nullptr;
462 PROFILEINFOW pi;
464 getUserNameImpl(Security, &buffer, false);
466 ZeroMemory(&pi, sizeof(pi));
467 pi.dwSize = sizeof(pi);
468 pi.lpUserName = o3tl::toW(rtl_uString_getStr(buffer));
469 pi.dwFlags = PI_NOUI;
471 if (LoadUserProfileW(hAccessToken, &pi))
473 UnloadUserProfile(hAccessToken, pi.hProfile);
475 bOk = true;
478 rtl_uString_release(buffer);
480 if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken))
481 CloseHandle(hAccessToken);
484 return bOk;
487 void SAL_CALL osl_unloadUserProfile(oslSecurity Security)
489 if ( static_cast<oslSecurityImpl*>(Security)->m_hProfile != nullptr )
491 HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken;
493 if ( !hAccessToken )
495 /* retrieve security handle if not done before e.g. osl_getCurrentSecurity()
497 HANDLE hProcess = GetCurrentProcess();
499 if (hProcess != nullptr)
501 OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken);
502 CloseHandle(hProcess);
506 /* unloading the user profile */
507 UnloadUserProfile(hAccessToken, static_cast<oslSecurityImpl*>(Security)->m_hProfile);
509 static_cast<oslSecurityImpl*>(Security)->m_hProfile = nullptr;
511 if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken))
512 CloseHandle(hAccessToken);
516 static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder)
518 bool bRet = false;
519 PWSTR PathW;
520 if (SUCCEEDED(SHGetKnownFolderPath(rFolder, KF_FLAG_CREATE, nullptr, &PathW)))
522 rtl_uString_newFromStr(strPath, o3tl::toU(PathW));
523 CoTaskMemFree(PathW);
524 bRet = true;
527 return bRet;
530 // We use LPCTSTR here, because we use it with SE_foo_NAME constants
531 // which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName")
532 static BOOL Privilege(LPCTSTR strPrivilege, BOOL bEnable)
534 HANDLE hToken;
535 TOKEN_PRIVILEGES tp;
537 // obtain the processes token
538 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_DUP_QUERY, &hToken))
539 return FALSE;
541 // get the luid
542 if (!LookupPrivilegeValue(nullptr, strPrivilege, &tp.Privileges[0].Luid))
543 return FALSE;
545 tp.PrivilegeCount = 1;
547 if (bEnable)
548 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
549 else
550 tp.Privileges[0].Attributes = 0;
552 // enable or disable the privilege
553 if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, nullptr, nullptr))
554 return FALSE;
556 if (!CloseHandle(hToken))
557 return FALSE;
559 return TRUE;
562 static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain)
564 if (Security != nullptr)
566 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
568 HANDLE hAccessToken = pSecImpl->m_hToken;
570 if (hAccessToken == nullptr)
571 OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken);
573 if (hAccessToken)
575 DWORD nInfoBuffer = 512;
576 UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer));
578 while (!GetTokenInformation(hAccessToken, TokenUser,
579 pInfoBuffer, nInfoBuffer, &nInfoBuffer))
581 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
583 if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer)))
584 pInfoBuffer = p;
585 else
587 free(pInfoBuffer);
588 pInfoBuffer = nullptr;
589 break;
592 else
594 free(pInfoBuffer);
595 pInfoBuffer = nullptr;
596 break;
600 if (pSecImpl->m_hToken == nullptr)
601 CloseHandle(hAccessToken);
603 if (pInfoBuffer)
605 sal_Unicode UserName[128];
606 sal_Unicode DomainName[128];
607 sal_Unicode Name[257];
608 DWORD nUserName = SAL_N_ELEMENTS(UserName);
609 DWORD nDomainName = SAL_N_ELEMENTS(DomainName);
610 SID_NAME_USE sUse;
612 if (LookupAccountSidW(nullptr, reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid,
613 o3tl::toW(UserName), &nUserName,
614 o3tl::toW(DomainName), &nDomainName, &sUse))
616 if (bIncludeDomain)
618 wcscpy(o3tl::toW(Name), o3tl::toW(DomainName));
619 wcscat(o3tl::toW(Name), L"/");
620 wcscat(o3tl::toW(Name), o3tl::toW(UserName));
622 else
624 wcscpy(o3tl::toW(Name), o3tl::toW(UserName));
627 rtl_uString_newFromStr(strName, Name);
628 free(pInfoBuffer);
629 return true;
633 else
635 DWORD needed=0;
636 sal_Unicode *pNameW=nullptr;
638 WNetGetUserW(nullptr, nullptr, &needed);
639 pNameW = static_cast<sal_Unicode *>(malloc (needed*sizeof(sal_Unicode)));
640 assert(pNameW); // Don't handle OOM conditions
642 if (WNetGetUserW(nullptr, o3tl::toW(pNameW), &needed) == NO_ERROR)
644 rtl_uString_newFromStr( strName, pNameW);
646 if (pNameW)
647 free(pNameW);
648 return true;
650 else if (pSecImpl->m_User[0] != '\0')
652 rtl_uString_newFromStr(strName, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
654 if (pNameW)
655 free(pNameW);
657 return true;
660 if (pNameW)
661 free(pNameW);
665 return false;
668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */