Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sal / osl / w32 / security.cxx
blob0e9bc96c9b789eadc4b15a420d54f2dc668e2450
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/comtools.hxx>
29 #include <systools/win32/uwinapi.h>
30 #include <sddl.h>
31 #include <sal/macros.h>
32 #include <sal/log.hxx>
33 #include <o3tl/char16_t2wchar_t.hxx>
34 #include "secimpl.hxx"
36 /* To get an impersonation token we need to create an impersonation
37 duplicate so every access token has to be created with duplicate
38 access rights */
40 #define TOKEN_DUP_QUERY (TOKEN_QUERY|TOKEN_DUPLICATE)
42 static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder);
43 // We use LPCTSTR here, because we use it with SE_foo_NAME constants
44 // which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName")
45 static bool Privilege(LPCTSTR pszPrivilege, bool bEnable);
46 static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain);
48 oslSecurity SAL_CALL osl_getCurrentSecurity(void)
50 oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
51 if (pSecImpl)
53 pSecImpl->m_pNetResource = nullptr;
54 pSecImpl->m_User[0] = '\0';
55 pSecImpl->m_hToken = nullptr;
56 pSecImpl->m_hProfile = nullptr;
58 return pSecImpl;
61 oslSecurityError SAL_CALL osl_loginUser( rtl_uString *strUserName, rtl_uString *strPasswd, oslSecurity *pSecurity )
63 oslSecurityError ret;
65 sal_Unicode* strUser;
66 sal_Unicode* strDomain = o3tl::toU(_wcsdup(o3tl::toW(rtl_uString_getStr(strUserName))));
67 HANDLE hUserToken;
68 LUID luid;
70 if (nullptr != (strUser = o3tl::toU(wcschr(o3tl::toW(strDomain), L'/'))))
71 *strUser++ = L'\0';
72 else
74 strUser = strDomain;
75 strDomain = nullptr;
78 // this process must have the right: 'act as a part of operatingsystem'
79 OSL_ASSERT(LookupPrivilegeValue(nullptr, SE_TCB_NAME, &luid));
80 (void) luid;
82 if (LogonUserW(o3tl::toW(strUser), strDomain ? o3tl::toW(strDomain) : L"", o3tl::toW(rtl_uString_getStr(strPasswd)),
83 LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
84 &hUserToken))
86 oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
87 if (pSecImpl)
89 pSecImpl->m_pNetResource = nullptr;
90 pSecImpl->m_hToken = hUserToken;
91 pSecImpl->m_hProfile = nullptr;
92 wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(strUser));
94 *pSecurity = pSecImpl;
95 ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown;
97 else
99 ret = osl_Security_E_UserUnknown;
102 if (strDomain)
103 free(strDomain);
104 else
105 free(strUser);
107 return ret;
110 oslSecurityError SAL_CALL osl_loginUserOnFileServer(rtl_uString *strUserName,
111 rtl_uString *strPasswd,
112 rtl_uString *strFileServer,
113 oslSecurity *pSecurity)
115 oslSecurityError ret;
116 DWORD err;
117 NETRESOURCEW netResource;
118 sal_Unicode* remoteName;
119 sal_Unicode* userName;
121 remoteName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 4) * sizeof(sal_Unicode)));
122 userName = static_cast<sal_Unicode *>(malloc((rtl_uString_getLength(strFileServer) + rtl_uString_getLength(strUserName) + 2) * sizeof(sal_Unicode)));
124 wcscpy(o3tl::toW(remoteName), L"\\\\");
125 wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strFileServer)));
126 wcscat(o3tl::toW(remoteName), L"\\");
127 wcscat(o3tl::toW(remoteName), o3tl::toW(rtl_uString_getStr(strUserName)));
129 wcscpy(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strFileServer)));
130 wcscat(o3tl::toW(userName), L"\\");
131 wcscat(o3tl::toW(userName), o3tl::toW(rtl_uString_getStr(strUserName)));
133 netResource.dwScope = RESOURCE_GLOBALNET;
134 netResource.dwType = RESOURCETYPE_DISK;
135 netResource.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
136 netResource.dwUsage = RESOURCEUSAGE_CONNECTABLE;
137 netResource.lpLocalName = nullptr;
138 netResource.lpRemoteName = o3tl::toW(remoteName);
139 netResource.lpComment = nullptr;
140 netResource.lpProvider = nullptr;
142 err = WNetAddConnection2W(&netResource, o3tl::toW(rtl_uString_getStr(strPasswd)), o3tl::toW(userName), 0);
144 if ((err == NO_ERROR) || (err == ERROR_ALREADY_ASSIGNED))
146 oslSecurityImpl* pSecImpl = static_cast<oslSecurityImpl *>(malloc(sizeof(oslSecurityImpl)));
147 if (pSecImpl)
149 pSecImpl->m_pNetResource = static_cast<NETRESOURCEW *>(malloc(sizeof(NETRESOURCE)));
150 if (pSecImpl->m_pNetResource)
152 *pSecImpl->m_pNetResource = netResource;
153 pSecImpl->m_hToken = nullptr;
154 pSecImpl->m_hProfile = nullptr;
155 wcscpy(o3tl::toW(pSecImpl->m_User), o3tl::toW(rtl_uString_getStr(strUserName)));
157 else
159 free(pSecImpl);
160 pSecImpl = nullptr;
163 *pSecurity = pSecImpl;
165 ret = pSecImpl ? osl_Security_E_None : osl_Security_E_Unknown;
167 else
169 ret = osl_Security_E_UserUnknown;
172 free(remoteName);
173 free(userName);
175 return ret;
178 sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security)
180 if (!Security)
181 return false;
183 HANDLE hImpersonationToken = nullptr;
184 PSID psidAdministrators;
185 SID_IDENTIFIER_AUTHORITY siaNtAuthority = { SECURITY_NT_AUTHORITY };
186 bool bSuccess = false;
188 /* If Security contains an access token we need to duplicate it to an impersonation
189 access token. NULL works with CheckTokenMembership() as the current effective
190 impersonation token
193 if ( static_cast<oslSecurityImpl*>(Security)->m_hToken )
195 if ( !DuplicateToken (static_cast<oslSecurityImpl*>(Security)->m_hToken, SecurityImpersonation, &hImpersonationToken) )
196 return false;
199 /* CheckTokenMembership() can be used on W2K and higher (NT4 no longer supported by OOo)
200 and also works on Vista to retrieve the effective user rights. Just checking for
201 membership of Administrators group is not enough on Vista this would require additional
202 complicated checks as described in KB article Q118626: http://support.microsoft.com/kb/118626/en-us
205 if (AllocateAndInitializeSid(&siaNtAuthority,
207 SECURITY_BUILTIN_DOMAIN_RID,
208 DOMAIN_ALIAS_RID_ADMINS,
209 0, 0, 0, 0, 0, 0,
210 &psidAdministrators))
212 BOOL fSuccess = FALSE;
214 if (CheckTokenMembership(hImpersonationToken, psidAdministrators, &fSuccess) && fSuccess)
215 bSuccess = true;
217 FreeSid(psidAdministrators);
220 if (hImpersonationToken)
221 CloseHandle(hImpersonationToken);
223 return bSuccess;
226 void SAL_CALL osl_freeSecurityHandle(oslSecurity Security)
228 if (!Security)
229 return;
231 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
233 if (pSecImpl->m_pNetResource != nullptr)
235 WNetCancelConnection2W(pSecImpl->m_pNetResource->lpRemoteName, 0, true);
237 free(pSecImpl->m_pNetResource->lpRemoteName);
238 free(pSecImpl->m_pNetResource);
241 if (pSecImpl->m_hToken)
242 CloseHandle(pSecImpl->m_hToken);
244 if ( pSecImpl->m_hProfile )
245 CloseHandle(pSecImpl->m_hProfile);
247 free (pSecImpl);
250 sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **strIdent)
252 if (!Security)
253 return false;
255 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
257 HANDLE hAccessToken = pSecImpl->m_hToken;
259 if (hAccessToken == nullptr)
260 OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken);
262 if (hAccessToken)
264 DWORD nInfoBuffer = 512;
265 UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer));
267 while (!GetTokenInformation(hAccessToken, TokenUser,
268 pInfoBuffer, nInfoBuffer, &nInfoBuffer))
270 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
272 if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer)))
273 pInfoBuffer = p;
274 else
276 free(pInfoBuffer);
277 pInfoBuffer = nullptr;
278 break;
281 else
283 free(pInfoBuffer);
284 pInfoBuffer = nullptr;
285 break;
289 if (pSecImpl->m_hToken == nullptr)
290 CloseHandle(hAccessToken);
292 if (pInfoBuffer)
294 PSID pSid = reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid;
296 LPWSTR pSidStr = nullptr;
297 bool bResult = ConvertSidToStringSidW(pSid, &pSidStr);
298 if (bResult)
300 rtl_uString_newFromStr(strIdent, o3tl::toU(pSidStr));
301 LocalFree(pSidStr);
303 else
305 const DWORD dwError = GetLastError();
306 SAL_WARN(
307 "sal.osl",
308 "ConvertSidToStringSidW failed. GetLastError returned: " << dwError);
311 free(pInfoBuffer);
313 return bResult;
316 else
318 DWORD needed = 0;
320 WNetGetUserW(nullptr, nullptr, &needed);
321 if (needed < 16)
322 needed = 16;
324 if (auto Ident = static_cast<sal_Unicode *>(malloc(needed*sizeof(sal_Unicode))))
326 if (WNetGetUserW(nullptr, o3tl::toW(Ident), &needed) != NO_ERROR)
328 wcscpy(o3tl::toW(Ident), L"unknown");
329 Ident[7] = L'\0';
332 rtl_uString_newFromStr( strIdent, Ident);
333 free(Ident);
334 return true;
337 return false;
340 sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **strName)
342 return getUserNameImpl(Security, strName, true);
345 sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **strName)
347 return getUserNameImpl(Security, strName, false);
350 sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory)
352 if (!Security)
353 return false;
355 rtl_uString *ustrSysDir = nullptr;
356 bool bSuccess = false;
358 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
360 if (pSecImpl->m_pNetResource != nullptr)
362 rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
364 bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory );
366 else
368 bSuccess = GetSpecialFolder(&ustrSysDir, FOLDERID_Documents) &&
369 (osl_File_E_None == osl_getFileURLFromSystemPath(ustrSysDir, pustrDirectory));
372 if ( ustrSysDir )
373 rtl_uString_release( ustrSysDir );
375 return bSuccess;
378 sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory)
380 if (!Security)
381 return false;
383 bool bSuccess = false;
384 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
386 if (pSecImpl->m_pNetResource != nullptr)
388 rtl_uString *ustrSysDir = nullptr;
390 rtl_uString_newFromStr( &ustrSysDir, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
391 bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath( ustrSysDir, pustrDirectory);
393 if ( ustrSysDir )
394 rtl_uString_release( ustrSysDir );
396 else
398 if (pSecImpl->m_hToken)
400 /* not implemented */
401 OSL_ASSERT(false);
403 else
405 rtl_uString *ustrFile = nullptr;
406 sal_Unicode sFile[_MAX_PATH];
408 if ( !GetSpecialFolder( &ustrFile, FOLDERID_RoamingAppData) )
410 OSL_VERIFY(GetWindowsDirectoryW(o3tl::toW(sFile), _MAX_DIR) > 0);
412 rtl_uString_newFromStr( &ustrFile, sFile);
415 bSuccess = osl_File_E_None == osl_getFileURLFromSystemPath(ustrFile, pustrDirectory);
417 if ( ustrFile )
418 rtl_uString_release( ustrFile );
422 return bSuccess;
425 sal_Bool SAL_CALL osl_loadUserProfile(oslSecurity Security)
427 /* CreateProcessAsUser does not load the specified user's profile
428 into the HKEY_USERS registry key. This means that access to information
429 in the HKEY_CURRENT_USER registry key may not produce results consistent
430 with a normal interactive logon.
431 It is your responsibility to load the user's registry hive into HKEY_USERS
432 with the LoadUserProfile function before calling CreateProcessAsUser.
435 RegCloseKey(HKEY_CURRENT_USER);
437 if (!Privilege(SE_RESTORE_NAME, true))
438 return false;
440 bool bOk = false;
441 HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken;
443 /* try to create user profile */
444 if ( !hAccessToken )
446 /* retrieve security handle if not done before e.g. osl_getCurrentSecurity()
448 HANDLE hProcess = GetCurrentProcess();
450 if (hProcess != nullptr)
452 OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken);
453 CloseHandle(hProcess);
457 rtl_uString *buffer = nullptr;
458 PROFILEINFOW pi;
460 getUserNameImpl(Security, &buffer, false);
462 ZeroMemory(&pi, sizeof(pi));
463 pi.dwSize = sizeof(pi);
464 pi.lpUserName = o3tl::toW(rtl_uString_getStr(buffer));
465 pi.dwFlags = PI_NOUI;
467 if (LoadUserProfileW(hAccessToken, &pi))
469 UnloadUserProfile(hAccessToken, pi.hProfile);
471 bOk = true;
474 rtl_uString_release(buffer);
476 if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken))
477 CloseHandle(hAccessToken);
479 return bOk;
482 void SAL_CALL osl_unloadUserProfile(oslSecurity Security)
484 if ( static_cast<oslSecurityImpl*>(Security)->m_hProfile == nullptr )
485 return;
487 HANDLE hAccessToken = static_cast<oslSecurityImpl*>(Security)->m_hToken;
489 if ( !hAccessToken )
491 /* retrieve security handle if not done before e.g. osl_getCurrentSecurity()
493 HANDLE hProcess = GetCurrentProcess();
495 if (hProcess != nullptr)
497 OpenProcessToken(hProcess, TOKEN_IMPERSONATE, &hAccessToken);
498 CloseHandle(hProcess);
502 /* unloading the user profile */
503 UnloadUserProfile(hAccessToken, static_cast<oslSecurityImpl*>(Security)->m_hProfile);
505 static_cast<oslSecurityImpl*>(Security)->m_hProfile = nullptr;
507 if (hAccessToken && (hAccessToken != static_cast<oslSecurityImpl*>(Security)->m_hToken))
508 CloseHandle(hAccessToken);
511 static bool GetSpecialFolder(rtl_uString **strPath, REFKNOWNFOLDERID rFolder)
513 sal::systools::CoTaskMemAllocated<wchar_t> PathW;
514 if (SUCCEEDED(SHGetKnownFolderPath(rFolder, KF_FLAG_CREATE, nullptr, &PathW)))
516 rtl_uString_newFromStr(strPath, o3tl::toU(PathW));
517 return true;
520 return false;
523 // We use LPCTSTR here, because we use it with SE_foo_NAME constants
524 // which are defined in winnt.h as UNICODE-dependent TEXT("PrivilegeName")
525 static bool Privilege(LPCTSTR strPrivilege, bool bEnable)
527 HANDLE hToken;
528 TOKEN_PRIVILEGES tp;
530 // obtain the processes token
531 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_DUP_QUERY, &hToken))
532 return false;
534 // get the luid
535 if (!LookupPrivilegeValue(nullptr, strPrivilege, &tp.Privileges[0].Luid))
536 return false;
538 tp.PrivilegeCount = 1;
540 if (bEnable)
541 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
542 else
543 tp.Privileges[0].Attributes = 0;
545 // enable or disable the privilege
546 if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, nullptr, nullptr))
547 return false;
549 if (!CloseHandle(hToken))
550 return false;
552 return true;
555 static bool getUserNameImpl(oslSecurity Security, rtl_uString **strName, bool bIncludeDomain)
557 if (!Security)
558 return false;
560 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl*>(Security);
562 HANDLE hAccessToken = pSecImpl->m_hToken;
564 if (hAccessToken == nullptr)
565 OpenProcessToken(GetCurrentProcess(), TOKEN_DUP_QUERY, &hAccessToken);
567 if (hAccessToken)
569 DWORD nInfoBuffer = 512;
570 UCHAR* pInfoBuffer = static_cast<UCHAR *>(malloc(nInfoBuffer));
572 while (!GetTokenInformation(hAccessToken, TokenUser,
573 pInfoBuffer, nInfoBuffer, &nInfoBuffer))
575 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
577 if (auto p = static_cast<UCHAR *>(realloc(pInfoBuffer, nInfoBuffer)))
578 pInfoBuffer = p;
579 else
581 free(pInfoBuffer);
582 pInfoBuffer = nullptr;
583 break;
586 else
588 free(pInfoBuffer);
589 pInfoBuffer = nullptr;
590 break;
594 if (pSecImpl->m_hToken == nullptr)
595 CloseHandle(hAccessToken);
597 if (pInfoBuffer)
599 sal_Unicode UserName[128];
600 sal_Unicode DomainName[128];
601 sal_Unicode Name[257];
602 DWORD nUserName = SAL_N_ELEMENTS(UserName);
603 DWORD nDomainName = SAL_N_ELEMENTS(DomainName);
604 SID_NAME_USE sUse;
606 if (LookupAccountSidW(nullptr, reinterpret_cast<PTOKEN_USER>(pInfoBuffer)->User.Sid,
607 o3tl::toW(UserName), &nUserName,
608 o3tl::toW(DomainName), &nDomainName, &sUse))
610 if (bIncludeDomain)
612 wcscpy(o3tl::toW(Name), o3tl::toW(DomainName));
613 wcscat(o3tl::toW(Name), L"/");
614 wcscat(o3tl::toW(Name), o3tl::toW(UserName));
616 else
618 wcscpy(o3tl::toW(Name), o3tl::toW(UserName));
621 rtl_uString_newFromStr(strName, Name);
622 free(pInfoBuffer);
623 return true;
627 else
629 DWORD needed=0;
630 sal_Unicode *pNameW=nullptr;
632 WNetGetUserW(nullptr, nullptr, &needed);
633 pNameW = static_cast<sal_Unicode *>(malloc (needed*sizeof(sal_Unicode)));
634 assert(pNameW); // Don't handle OOM conditions
636 if (WNetGetUserW(nullptr, o3tl::toW(pNameW), &needed) == NO_ERROR)
638 rtl_uString_newFromStr( strName, pNameW);
640 if (pNameW)
641 free(pNameW);
642 return true;
644 else if (pSecImpl->m_User[0] != '\0')
646 rtl_uString_newFromStr(strName, o3tl::toU(pSecImpl->m_pNetResource->lpRemoteName));
648 if (pNameW)
649 free(pNameW);
651 return true;
654 if (pNameW)
655 free(pNameW);
658 return false;
661 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */