1 /*
2  * regexpl - Console Registry Explorer
3  *
4  * Copyright (C) 2000-2005 Nedko Arnaudov <nedko@users.sourceforge.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 // ShellCommandSACL.cpp: implementation of the CShellCommandSACL class.
23 //
24 //////////////////////////////////////////////////////////////////////
25 
26 #include "ph.h"
27 #include "ShellCommandSACL.h"
28 #include "RegistryExplorer.h"
29 #include "SecurityDescriptor.h"
30 
31 #define SACL_CMD			_T("SACL")
32 #define SACL_CMD_LENGTH		COMMAND_LENGTH(SACL_CMD)
33 #define SACL_CMD_SHORT_DESC	SACL_CMD _T(" command is used to view")/*"/edit"*/_T(" key's SACL.\n")
34 
35 //////////////////////////////////////////////////////////////////////
36 // Construction/Destruction
37 //////////////////////////////////////////////////////////////////////
38 
39 CShellCommandSACL::CShellCommandSACL(CRegistryTree& rTree):m_rTree(rTree)
40 {
41 
42 }
43 
44 CShellCommandSACL::~CShellCommandSACL()
45 {
46 
47 }
48 
49 BOOL CShellCommandSACL::Match(const TCHAR *pchCommand)
50 {
51 	if (_tcsicmp(pchCommand,SACL_CMD) == 0)
52 		return TRUE;
53 	if (_tcsnicmp(pchCommand,SACL_CMD _T(".."),SACL_CMD_LENGTH+2*sizeof(TCHAR)) == 0)
54 		return TRUE;
55 	if (_tcsnicmp(pchCommand,SACL_CMD _T("/") ,SACL_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
56 		return TRUE;
57 	if (_tcsnicmp(pchCommand,SACL_CMD _T("\\"),SACL_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
58 		return TRUE;
59 	return FALSE;
60 }
61 
62 int CShellCommandSACL::Execute(CConsole &rConsole, CArgumentParser& rArguments)
63 {
64 #define ERROR_MSG_BUFFER_SIZE 1024
65   TCHAR pszError_msg[ERROR_MSG_BUFFER_SIZE];
66   pszError_msg[ERROR_MSG_BUFFER_SIZE-1] = 0;
67 
68   rArguments.ResetArgumentIteration();
69 
70   const TCHAR *pszKey = NULL;
71   BOOL blnDo = TRUE;
72   BOOL blnBadParameter = FALSE;
73   BOOL blnHelp = FALSE;
74   const TCHAR *pszParameter;
75   const TCHAR *pszCommandItself = rArguments.GetNextArgument();
76   DWORD dwError;
77   PISECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
78   CSecurityDescriptor sd;
79   HANDLE hThreadToken = INVALID_HANDLE_VALUE;
80 
81   if ((_tcsnicmp(pszCommandItself,SACL_CMD _T(".."),SACL_CMD_LENGTH+2*sizeof(TCHAR)) == 0)||
82       (_tcsnicmp(pszCommandItself,SACL_CMD _T("\\"),SACL_CMD_LENGTH+1*sizeof(TCHAR)) == 0))
83   {
84     pszKey = pszCommandItself + SACL_CMD_LENGTH;
85   }
86   else if (_tcsnicmp(pszCommandItself,SACL_CMD _T("/"),SACL_CMD_LENGTH+1*sizeof(TCHAR)) == 0)
87   {
88     pszParameter = pszCommandItself + SACL_CMD_LENGTH;
89     goto CheckSACLArgument;
90   }
91 
92   while((pszParameter = rArguments.GetNextArgument()) != NULL)
93   {
94 CheckSACLArgument:
95     blnBadParameter = FALSE;
96     if ((_tcsicmp(pszParameter,_T("/?")) == 0)||
97         (_tcsicmp(pszParameter,_T("-?")) == 0))
98     {
99       blnHelp = TRUE;
100       blnDo = pszKey != NULL;
101     }
102     else if (!pszKey)
103     {
104       pszKey = pszParameter;
105       blnDo = TRUE;
106     }
107     else
108     {
109       blnBadParameter = TRUE;
110     }
111     if (blnBadParameter)
112     {
113       rConsole.Write(_T("Bad parameter: "));
114       rConsole.Write(pszParameter);
115       rConsole.Write(_T("\n"));
116     }
117   }
118 
119   CRegistryKey Key;
120 
121   ASSERT(hThreadToken == INVALID_HANDLE_VALUE);
122 
123   // Open thread token
124   if (!OpenThreadToken(GetCurrentThread(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,FALSE,&hThreadToken))
125   {	// OpenThreadToken failed
126     dwError = GetLastError();
127     if (dwError != ERROR_NO_TOKEN)
128     {
129       _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot open thread token.\nOpenThreadToken fails with error: %u\n"),(unsigned int)dwError);
130       goto Error;
131     }
132 
133     // If the thread does not have an access token, we'll examine the
134     // access token associated with the process.
135     if (!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hThreadToken))
136     {
137       _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot open process token.\nOpenProcessToken fails with error: %u\n"),(unsigned int)GetLastError());
138       goto Error;
139     }
140   }
141 
142   ASSERT(hThreadToken != INVALID_HANDLE_VALUE);
143 
144   // enable SeSecurityPrivilege privilege
145   TOKEN_PRIVILEGES priv;
146   priv.PrivilegeCount = 1;
147   priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
148   if (!LookupPrivilegeValue(
149       NULL,							// lookup privilege on local system
150       SE_SECURITY_NAME,				// privilege to lookup
151       &(priv.Privileges[0].Luid)))	// receives LUID of privilege
152   {
153     _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot retrieve the locally unique identifier for %s privilege.\nLookupPrivilegeValue error: %u\n"),SE_SECURITY_NAME,(unsigned int)GetLastError());
154     goto Error;
155   }
156 
157   BOOL blnAdjRet;
158   blnAdjRet = AdjustTokenPrivileges(
159       hThreadToken,
160       FALSE,
161       &priv,
162       sizeof(TOKEN_PRIVILEGES),
163       (PTOKEN_PRIVILEGES)NULL,
164       (PDWORD)NULL);
165   dwError = GetLastError();
166   if (!blnAdjRet)
167   {
168     _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot enable %s privilege.\nAdjustTokenPrivileges fails with error: %u%s\n"),SE_SECURITY_NAME,(unsigned int)dwError,(dwError == 5)?_T(" (Access denied)"):_T(""));
169     goto Error;
170   }
171 
172   if (dwError != ERROR_SUCCESS)
173   {
174     if (dwError == ERROR_NOT_ALL_ASSIGNED)
175     {
176       _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot enable %s privilege.\nThe token does not have the %s privilege\n"),SE_SECURITY_NAME,SE_SECURITY_NAME);
177     }
178     else
179     {
180       _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot enable %s privilege.\nAdjustTokenPrivileges succeds with error: %u\n"),SE_SECURITY_NAME,(unsigned int)dwError);
181     }
182 
183     goto Error;
184   }
185 
186   if (!m_rTree.GetKey(pszKey?pszKey:_T("."),KEY_QUERY_VALUE|READ_CONTROL|ACCESS_SYSTEM_SECURITY,Key))
187   {
188     rConsole.Write(m_rTree.GetLastErrorDescription());
189     blnDo = FALSE;
190   }
191 
192   if (blnHelp)
193   {
194     rConsole.Write(GetHelpString());
195   }
196 
197   if (blnDo&&blnHelp)
198     rConsole.Write(_T("\n"));
199 
200   if (!blnDo)
201     return 0;
202 
203   if (Key.IsRoot())
204   {
205     _tcsncpy(pszError_msg,SACL_CMD COMMAND_NA_ON_ROOT,ERROR_MSG_BUFFER_SIZE-1);
206     goto Error;
207   }
208 
209   DWORD dwSecurityDescriptorLength;
210   rConsole.Write(_T("Key : "));
211   rConsole.Write(_T("\\"));
212 
213   rConsole.Write(Key.GetKeyName());
214   rConsole.Write(_T("\n"));
215   dwError = Key.GetSecurityDescriptorLength(&dwSecurityDescriptorLength);
216   if (dwError != ERROR_SUCCESS)
217   {
218     _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot get security descriptor's length for current key.\nError: %u\n"),(unsigned int)dwError);
219     goto Error;
220   }
221 
222   pSecurityDescriptor = (PISECURITY_DESCRIPTOR) new (std::nothrow) unsigned char [dwSecurityDescriptorLength];
223   if (!pSecurityDescriptor)
224   {
225       _tcsncpy(pszError_msg,_T("\nOut of memory.\n"),ERROR_MSG_BUFFER_SIZE-1);
226       goto Error;
227   }
228 
229   DWORD dwSecurityDescriptorLength1;
230   dwSecurityDescriptorLength1 = dwSecurityDescriptorLength;
231   dwError = Key.GetSecurityDescriptor((SECURITY_INFORMATION)SACL_SECURITY_INFORMATION,pSecurityDescriptor,&dwSecurityDescriptorLength1);
232   if (dwError != ERROR_SUCCESS)
233   {
234     _sntprintf(pszError_msg,ERROR_MSG_BUFFER_SIZE-1,_T("\nCannot get security descriptor for current key.\nError: %u%s\n"),(unsigned int)dwError,(dwError == 1314)?_T("(A required privilege is not held by the client.)\n"):_T(""));
235     goto Error;
236   }
237 
238   sd.AssociateDescriptor(pSecurityDescriptor);
239   sd.BeginSACLInteration();
240 
241   if ((!sd.DescriptorContainsSACL())||(sd.HasNULLSACL()))
242   {
243     _tcsncpy(pszError_msg,_T("Key has not SACL.\n"),ERROR_MSG_BUFFER_SIZE-1);
244     goto Error;
245   }
246 
247   if (!sd.HasValidSACL())
248   {
249     _tcsncpy(pszError_msg,_T("Invalid SACL.\n"),ERROR_MSG_BUFFER_SIZE-1);
250     goto Error;
251   }
252 
253   DWORD nACECount;
254   nACECount = sd.GetSACLEntriesCount();
255   rConsole.Write(_T("SACL has "));
256   TCHAR Buffer[256];
257   rConsole.Write(_itoa(nACECount,Buffer,10));
258   rConsole.Write(_T(" ACEs.\n"));
259   ASSERT(sizeof(ACL) == 8);
260   rConsole.Write(_T("\n"));
261   for (DWORD i = 0 ; i < nACECount ; i++)
262   {
263     rConsole.Write(_T("\n"));
264     rConsole.Write(_T("\tACE Index: "));
265     rConsole.Write(_itoa(i,Buffer,10));
266     rConsole.Write(_T("\n"));
267     rConsole.Write(_T("\tAudit Type: "));
268     BOOL blnFailed, blnSuccessful;
269     if (sd.GetSACLEntry(i,blnFailed,blnSuccessful) != CSecurityDescriptor::SystemAudit)
270     {
271       rConsole.Write(_T("Unknown ACE type.\nCannot continue ACE list dump.\n"));
272       goto AbortDumpSACL;
273     }
274 
275     if (blnFailed)
276       rConsole.Write(_T("Failed access"));
277 
278     if (blnFailed && blnSuccessful)
279       rConsole.Write(_T(" & "));
280     if (blnSuccessful)
281       rConsole.Write(_T("Successful access"));
282     rConsole.Write(_T("\n"));
283 
284     PSID pSID = sd.GetCurrentACE_SID();
285     if ((pSID == NULL)||(!IsValidSid(pSID)))
286     {
287       rConsole.Write(_T("\tInvalid SID.\n"));
288     }
289 
290     DWORD dwSIDStringSize = 0;
291     BOOL blnRet = GetTextualSid(pSID,NULL,&dwSIDStringSize);
292     ASSERT(!blnRet);
293     ASSERT(GetLastError() == ERROR_INSUFFICIENT_BUFFER);
294     TCHAR *pszSID = new (std::nothrow) TCHAR[dwSIDStringSize];
295 
296     if (!pszSID)
297     {
298       _tcsncpy(pszError_msg,_T("\nOut of memory.\n"),ERROR_MSG_BUFFER_SIZE-1);
299       goto Error;
300     }
301 
302     if(!GetTextualSid(pSID,pszSID,&dwSIDStringSize))
303     {
304       dwError = GetLastError();
305       ASSERT(dwError != ERROR_INSUFFICIENT_BUFFER);
306       rConsole.Write(_T("Error "));
307       TCHAR Buffer[256];
308       rConsole.Write(_itoa(dwError,Buffer,10));
309       rConsole.Write(_T("\nGetting string representation of SID\n"));
310     }
311     else
312     {
313       rConsole.Write(_T("\tSID: "));
314       rConsole.Write(pszSID);
315       rConsole.Write(_T("\n"));
316     }
317     delete[] pszSID;
318 
319     TCHAR *pszName, *pszDomainName;
320     DWORD dwNameBufferLength, dwDomainNameBufferLength;
321     dwNameBufferLength = 1024;
322     dwDomainNameBufferLength = 1024;
323 
324     pszName = new (std::nothrow) TCHAR [dwNameBufferLength];
325     if (!pszName)
326     {
327       _tcsncpy(pszError_msg,_T("\nOut of memory.\n"),ERROR_MSG_BUFFER_SIZE-1);
328       goto Error;
329     }
330 
331     pszDomainName = new (std::nothrow) TCHAR [dwDomainNameBufferLength];
332     if (!pszDomainName)
333     {
334       _tcsncpy(pszError_msg,_T("\nOut of memory.\n"),ERROR_MSG_BUFFER_SIZE-1);
335       delete[] pszName;
336       goto Error;
337     }
338 
339     DWORD dwNameLength = dwNameBufferLength, dwDomainNameLength = dwDomainNameBufferLength;
340     SID_NAME_USE Use;
341     if (!LookupAccountSid(NULL,pSID,pszName,&dwNameLength,pszDomainName,&dwDomainNameLength,&Use))
342     {
343       rConsole.Write(_T("Error "));
344       TCHAR Buffer[256];
345       rConsole.Write(_itoa(GetLastError(),Buffer,10));
346       rConsole.Write(_T("\n"));
347     }
348     else
349     {
350       rConsole.Write(_T("\tTrustee Domain: "));
351       rConsole.Write(pszDomainName);
352       rConsole.Write(_T("\n"));
353       rConsole.Write(_T("\tTrustee Name: "));
354       rConsole.Write(pszName);
355       rConsole.Write(_T("\n\tSID type: "));
356       rConsole.Write(GetSidTypeName(Use));
357       rConsole.Write(_T("\n"));
358     }
359     DWORD dwAccessMask;
360     sd.GetCurrentACE_AccessMask(dwAccessMask);
361     wsprintf(Buffer,_T("\tAccess Mask: 0x%08lX\n"),dwAccessMask);
362     rConsole.Write(Buffer);
363     if (dwAccessMask & GENERIC_READ)
364     {
365       rConsole.Write(_T("\t\tGENERIC_READ\n"));
366     }
367     if (dwAccessMask & GENERIC_WRITE)
368     {
369       rConsole.Write(_T("\t\tGENERIC_WRITE\n"));
370     }
371     if (dwAccessMask & GENERIC_EXECUTE)
372     {
373       rConsole.Write(_T("\t\tGENERIC_EXECUTE\n"));
374     }
375     if (dwAccessMask & GENERIC_ALL)
376     {
377       rConsole.Write(_T("\t\tGENERIC_ALL\n"));
378     }
379     if (dwAccessMask & SYNCHRONIZE)
380     {
381       rConsole.Write(_T("\t\tSYNCHRONIZE\n"));
382     }
383     if (dwAccessMask & WRITE_OWNER)
384     {
385       rConsole.Write(_T("\t\tWRITE_OWNER\n"));
386     }
387     if (dwAccessMask & WRITE_DAC)
388     {
389       rConsole.Write(_T("\t\tWRITE_DAC\n"));
390     }
391     if (dwAccessMask & READ_CONTROL)
392     {
393       rConsole.Write(_T("\t\tREAD_CONTROL\n"));
394     }
395     if (dwAccessMask & DELETE)
396     {
397       rConsole.Write(_T("\t\tDELETE\n"));
398     }
399     if (dwAccessMask & KEY_CREATE_LINK)
400     {
401       rConsole.Write(_T("\t\tKEY_CREATE_LINK\n"));
402     }
403     if (dwAccessMask & KEY_NOTIFY)
404     {
405       rConsole.Write(_T("\t\tKEY_NOTIFY\n"));
406     }
407     if (dwAccessMask & KEY_ENUMERATE_SUB_KEYS)
408     {
409       rConsole.Write(_T("\t\tKEY_ENUMERATE_SUB_KEYS\n"));
410     }
411     if (dwAccessMask & KEY_CREATE_SUB_KEY)
412     {
413       rConsole.Write(_T("\t\tKEY_CREATE_SUB_KEY\n"));
414     }
415     if (dwAccessMask & KEY_SET_VALUE)
416     {
417       rConsole.Write(_T("\t\tKEY_SET_VALUE\n"));
418     }
419     if (dwAccessMask & KEY_QUERY_VALUE)
420     {
421       rConsole.Write(_T("\t\tKEY_QUERY_VALUE\n"));
422     }
423 
424     delete[] pszName;
425     delete[] pszDomainName;
426   }	// for
427 
428 AbortDumpSACL:
429   ASSERT(pSecurityDescriptor);
430   delete pSecurityDescriptor;
431 
432   VERIFY(CloseHandle(hThreadToken));
433 
434 	return 0;
435 Error:
436   if (pSecurityDescriptor)
437     delete pSecurityDescriptor;
438 
439   if (hThreadToken != INVALID_HANDLE_VALUE)
440     VERIFY(CloseHandle(hThreadToken));
441 
442   rConsole.Write(pszError_msg);
443   return 0;
444 }
445 
446 const TCHAR * CShellCommandSACL::GetHelpString()
447 {
448 	return SACL_CMD_SHORT_DESC
449 			_T("Syntax: ") SACL_CMD _T(" [<KEY>] [/?]\n\n")
450 			_T("    <KEY> - Optional relative path of desired key.\n")
451 			_T("    /?    - This help.\n\n")
452 			_T("Without parameters, command displays SACL of current key.\n");
453 }
454 
455 const TCHAR * CShellCommandSACL::GetHelpShortDescriptionString()
456 {
457 	return SACL_CMD_SHORT_DESC;
458 }
459