xref: /reactos/base/services/svchost/security.cxx (revision ede7a20a)
1 /*
2  * PROJECT:     ReactOS Service Host
3  * LICENSE:     BSD - See COPYING.ARM in the top level directory
4  * FILE:        base/services/svchost/security.cxx
5  * PURPOSE:     Initializes the COM Object Security Model and Parameters
6  * PROGRAMMERS: ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 extern "C"
12 {
13 #include "svchost.h"
14 
15 #include <aclapi.h>
16 #include <objidl.h>
17 }
18 
19 /* GLOBALS *******************************************************************/
20 
21 SID NtSid = { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SYSTEM_RID } };
22 
23 /* FUNCTIONS *****************************************************************/
24 
25 DWORD
26 WINAPI
27 DwInitializeSdFromThreadToken (
28     _Out_ PVOID *ppSecurityDescriptor,
29     _Out_ PACL *ppAcl
30     )
31 {
32     HANDLE hToken;
33     DWORD dwGroupLength, dwUserLength, dwError, dwAlignLength;
34     PTOKEN_PRIMARY_GROUP pTokenGroup;
35     PTOKEN_USER pTokenUser;
36     EXPLICIT_ACCESS_W pListOfExplicitEntries;
37     PACL pAcl = NULL;
38     PISECURITY_DESCRIPTOR pSd;
39 
40     /* Assume failure */
41     *ppSecurityDescriptor = NULL;
42     *ppAcl = NULL;
43 
44     /* Open the token of the current thread */
45     if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, 0, &hToken) == FALSE)
46     {
47         /* The thread is not impersonating, use the process token */
48         if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) == FALSE)
49         {
50             /* No token could be queried, fail */
51             return GetLastError();
52         }
53     }
54 
55     /* Get the size of the token's user */
56     if ((GetTokenInformation(hToken, TokenUser, NULL, 0, &dwUserLength) != FALSE) ||
57         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
58     {
59         return GetLastError();
60     }
61 
62     /* Get the size of the token's primary group */
63     if ((GetTokenInformation(hToken, TokenPrimaryGroup, NULL, 0, &dwGroupLength) != FALSE) ||
64         (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
65     {
66         return GetLastError();
67     }
68 
69     /* Allocate an SD large enough to hold the SIDs for the above */
70     dwAlignLength = ALIGN_UP(dwUserLength, ULONG);
71     pSd = (PISECURITY_DESCRIPTOR)MemAlloc(0,
72                                           dwAlignLength +
73                                           dwGroupLength +
74                                           sizeof(*pSd));
75     if (pSd == NULL) return ERROR_OUTOFMEMORY;
76 
77     /* Assume success for now */
78     dwError = ERROR_SUCCESS;
79 
80     /* We'll put them right after the SD itself */
81     pTokenUser = (PTOKEN_USER)(pSd + 1);
82     pTokenGroup = (PTOKEN_PRIMARY_GROUP)((ULONG_PTR)pTokenUser + dwAlignLength);
83 
84     /* Now initialize it */
85     if (InitializeSecurityDescriptor(pSd, SECURITY_DESCRIPTOR_REVISION) == FALSE)
86     {
87         dwError = GetLastError();
88     }
89 
90     /* And do the actual query for the user */
91     if (GetTokenInformation(hToken,
92                             TokenUser,
93                             pTokenUser,
94                             dwUserLength,
95                             &dwUserLength) == FALSE)
96     {
97         dwError = GetLastError();
98     }
99 
100     /* And then the actual query for the primary group */
101     if (GetTokenInformation(hToken,
102                             TokenPrimaryGroup,
103                             pTokenGroup,
104                             dwGroupLength,
105                             &dwGroupLength) == FALSE)
106     {
107         dwError = GetLastError();
108     }
109 
110     /* Set the user as owner */
111     if (SetSecurityDescriptorOwner(pSd, pTokenUser->User.Sid, FALSE) == FALSE)
112     {
113         dwError = GetLastError();
114     }
115 
116     /* Set the group as primary */
117     if (SetSecurityDescriptorGroup(pSd, pTokenGroup->PrimaryGroup, FALSE) == FALSE)
118     {
119         dwError = GetLastError();
120     }
121 
122     /* Did everything so far work out? */
123     if (dwError == ERROR_SUCCESS)
124     {
125         /* Yes, create an ACL granting the SYSTEM account access */
126         pListOfExplicitEntries.grfAccessMode = SET_ACCESS;
127         pListOfExplicitEntries.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
128         pListOfExplicitEntries.grfAccessPermissions = 1;
129         pListOfExplicitEntries.grfInheritance = 0;
130         pListOfExplicitEntries.Trustee.pMultipleTrustee = 0;
131         pListOfExplicitEntries.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
132         pListOfExplicitEntries.Trustee.TrusteeForm = TRUSTEE_IS_SID;
133         pListOfExplicitEntries.Trustee.ptstrName = (LPWSTR)&NtSid;
134         dwError = SetEntriesInAclW(1, &pListOfExplicitEntries, NULL, &pAcl);
135         if (dwError == ERROR_SUCCESS)
136         {
137             /* Make that ACL the DACL of the SD we just built */
138             if (SetSecurityDescriptorDacl(pSd, 1, pAcl, FALSE) == FALSE)
139             {
140                 /* We failed, bail out */
141                 LocalFree(pAcl);
142                 dwError = GetLastError();
143             }
144             else
145             {
146                 /* Now we have the SD and the ACL all ready to go */
147                 *ppSecurityDescriptor = pSd;
148                 *ppAcl = pAcl;
149                 return ERROR_SUCCESS;
150             }
151         }
152     }
153 
154     /* Failure path, we'll free the SD since the caller can't use it */
155     MemFree(pSd);
156     return dwError;
157 }
158 
159 BOOL
160 WINAPI
161 InitializeSecurity (
162     _In_ DWORD dwParam,
163     _In_ DWORD dwAuthnLevel,
164     _In_ DWORD dwImpLevel,
165     _In_ DWORD dwCapabilities
166     )
167 {
168     HRESULT hr;
169     PACL pAcl;
170     PSECURITY_DESCRIPTOR pSecurityDescriptor;
171     IGlobalOptions *pGlobalOptions;
172     ASSERT(dwParam != 0);
173 
174     /* Create a valid SD and ACL based on the current thread's token */
175     if (DwInitializeSdFromThreadToken(&pSecurityDescriptor, &pAcl) == ERROR_SUCCESS)
176     {
177         /* It worked -- initialize COM without DDE support */
178         hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE);
179     }
180     else
181     {
182         /* Don't keep going if we don't have an SD */
183         hr = E_FAIL;
184     }
185 
186     /* Did we make it? */
187     if (SUCCEEDED(hr))
188     {
189         /* Indeed, initialize COM security now */
190         DBG_TRACE("Calling CoInitializeSecurity (dwAuthCapabilities = 0x%08x)\n",
191                   dwCapabilities);
192         hr = CoInitializeSecurity(pSecurityDescriptor,
193                                   -1,
194                                   NULL,
195                                   NULL,
196                                   dwAuthnLevel,
197                                   dwImpLevel,
198                                   NULL,
199                                   dwCapabilities,
200                                   NULL);
201         if (FAILED(hr)) DBG_ERR("CoInitializeSecurity returned hr=0x%08x\n", hr);
202     }
203 
204     /* Free the SD and ACL since we no longer need it */
205     MemFree(pSecurityDescriptor);
206     LocalFree(pAcl);
207 
208     /* Did we initialize COM correctly? */
209     if (SUCCEEDED(hr))
210     {
211         /* Get the COM Global Options Interface */
212         hr = CoCreateInstance(CLSID_GlobalOptions,
213                               NULL,
214                               CLSCTX_INPROC_SERVER,
215                               IID_IGlobalOptions,
216                               (LPVOID*)&pGlobalOptions);
217         if (SUCCEEDED(hr))
218         {
219             /* Use it to disable COM exception handling */
220             hr = pGlobalOptions->Set(COMGLB_EXCEPTION_HANDLING,
221                                      COMGLB_EXCEPTION_DONOT_HANDLE);
222             pGlobalOptions->Release();
223             ASSERT(SUCCEEDED(hr));
224         }
225     }
226 
227     /* Return whether all COM calls were successful or not */
228     return SUCCEEDED(hr);
229 }
230