- Sync with trunk r58248 to bring the latest changes from Amine (headers) and others...
[reactos.git] / base / applications / cacls / cacls.c
1 /*
2 * ReactOS Control ACLs Program
3 * Copyright (C) 2006 Thomas Weidenmueller
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "precomp.h"
21
22 static GENERIC_MAPPING FileGenericMapping =
23 {
24 FILE_GENERIC_READ,
25 FILE_GENERIC_WRITE,
26 FILE_GENERIC_EXECUTE,
27 FILE_ALL_ACCESS
28 };
29
30
31 static
32 INT
33 LengthOfStrResource(IN HINSTANCE hInst,
34 IN UINT uID)
35 {
36 HRSRC hrSrc;
37 HGLOBAL hRes;
38 LPWSTR lpName, lpStr;
39
40 if (hInst == NULL)
41 {
42 hInst = GetModuleHandle(NULL);
43 }
44
45 /* There are always blocks of 16 strings */
46 lpName = (LPWSTR)MAKEINTRESOURCE((uID >> 4) + 1);
47
48 /* Find the string table block */
49 hrSrc = FindResourceW(hInst, lpName, (LPWSTR)RT_STRING);
50 if (hrSrc)
51 {
52 hRes = LoadResource(hInst, hrSrc);
53 if (hRes)
54 {
55 lpStr = LockResource(hRes);
56 if (lpStr)
57 {
58 UINT x;
59
60 /* Find the string we're looking for */
61 uID &= 0xF; /* position in the block, same as % 16 */
62 for (x = 0; x < uID; x++)
63 {
64 lpStr += (*lpStr) + 1;
65 }
66
67 /* Found the string */
68 return (int)(*lpStr);
69 }
70 }
71 }
72 return -1;
73 }
74
75
76 static
77 INT
78 AllocAndLoadString(OUT LPTSTR *lpTarget,
79 IN HINSTANCE hInst,
80 IN UINT uID)
81 {
82 INT ln;
83
84 ln = LengthOfStrResource(hInst,
85 uID);
86 if (ln++ > 0)
87 {
88 (*lpTarget) = (LPTSTR)HeapAlloc(GetProcessHeap(),
89 0,
90 ln * sizeof(TCHAR));
91 if ((*lpTarget) != NULL)
92 {
93 INT Ret;
94 Ret = LoadString(hInst,
95 uID,
96 *lpTarget,
97 ln);
98 if (!Ret)
99 {
100 HeapFree(GetProcessHeap(),
101 0,
102 *lpTarget);
103 }
104 return Ret;
105 }
106 }
107 return 0;
108 }
109
110
111 static
112 VOID
113 PrintHelp(VOID)
114 {
115 LPTSTR szHelp;
116
117 if (AllocAndLoadString(&szHelp,
118 NULL,
119 IDS_HELP) != 0)
120 {
121 _tprintf(_T("%s"),
122 szHelp);
123
124 HeapFree(GetProcessHeap(),
125 0,
126 szHelp);
127 }
128 }
129
130
131 static
132 VOID
133 PrintErrorMessage(IN DWORD dwError)
134 {
135 LPTSTR szError;
136
137 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
138 FORMAT_MESSAGE_IGNORE_INSERTS |
139 FORMAT_MESSAGE_FROM_SYSTEM,
140 NULL,
141 dwError,
142 MAKELANGID(LANG_NEUTRAL,
143 SUBLANG_DEFAULT),
144 (LPTSTR)&szError,
145 0,
146 NULL) != 0)
147 {
148 _tprintf(_T("%s"),
149 szError);
150 LocalFree((HLOCAL)szError);
151 }
152 }
153
154
155 static
156 DWORD
157 LoadAndPrintString(IN HINSTANCE hInst,
158 IN UINT uID)
159 {
160 TCHAR szTemp[255];
161 DWORD Len;
162
163 Len = (DWORD)LoadString(hInst,
164 uID,
165 szTemp,
166 sizeof(szTemp) / sizeof(szTemp[0]));
167
168 if (Len != 0)
169 {
170 _tprintf(_T("%s"),
171 szTemp);
172 }
173
174 return Len;
175 }
176
177
178 static
179 BOOL
180 PrintFileDacl(IN LPTSTR FilePath,
181 IN LPTSTR FileName)
182 {
183 SIZE_T Length;
184 PSECURITY_DESCRIPTOR SecurityDescriptor;
185 DWORD SDSize = 0;
186 TCHAR FullFileName[MAX_PATH + 1];
187 BOOL Error = FALSE, Ret = FALSE;
188
189 Length = _tcslen(FilePath) + _tcslen(FileName);
190 if (Length > MAX_PATH)
191 {
192 /* file name too long */
193 SetLastError(ERROR_FILE_NOT_FOUND);
194 return FALSE;
195 }
196
197 _tcscpy(FullFileName,
198 FilePath);
199 _tcscat(FullFileName,
200 FileName);
201
202 /* find out how much memory we need */
203 if (!GetFileSecurity(FullFileName,
204 DACL_SECURITY_INFORMATION,
205 NULL,
206 0,
207 &SDSize) &&
208 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
209 {
210 return FALSE;
211 }
212
213 SecurityDescriptor = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
214 0,
215 SDSize);
216 if (SecurityDescriptor != NULL)
217 {
218 if (GetFileSecurity(FullFileName,
219 DACL_SECURITY_INFORMATION,
220 SecurityDescriptor,
221 SDSize,
222 &SDSize))
223 {
224 PACL Dacl;
225 BOOL DaclPresent;
226 BOOL DaclDefaulted;
227
228 if (GetSecurityDescriptorDacl(SecurityDescriptor,
229 &DaclPresent,
230 &Dacl,
231 &DaclDefaulted))
232 {
233 if (DaclPresent)
234 {
235 PACCESS_ALLOWED_ACE Ace;
236 DWORD AceIndex = 0;
237
238 /* dump the ACL */
239 while (GetAce(Dacl,
240 AceIndex,
241 (PVOID*)&Ace))
242 {
243 SID_NAME_USE Use;
244 DWORD NameSize = 0;
245 DWORD DomainSize = 0;
246 LPTSTR Name = NULL;
247 LPTSTR Domain = NULL;
248 LPTSTR SidString = NULL;
249 DWORD IndentAccess;
250 DWORD AccessMask = Ace->Mask;
251 PSID Sid = (PSID)&Ace->SidStart;
252
253 /* attempt to translate the SID into a readable string */
254 if (!LookupAccountSid(NULL,
255 Sid,
256 Name,
257 &NameSize,
258 Domain,
259 &DomainSize,
260 &Use))
261 {
262 if (GetLastError() == ERROR_NONE_MAPPED || NameSize == 0)
263 {
264 goto BuildSidString;
265 }
266 else
267 {
268 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
269 {
270 Error = TRUE;
271 break;
272 }
273
274 Name = (LPTSTR)HeapAlloc(GetProcessHeap(),
275 0,
276 (NameSize + DomainSize) * sizeof(TCHAR));
277 if (Name == NULL)
278 {
279 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
280 Error = TRUE;
281 break;
282 }
283
284 Domain = Name + NameSize;
285 Name[0] = _T('\0');
286 if (DomainSize != 0)
287 Domain[0] = _T('\0');
288 if (!LookupAccountSid(NULL,
289 Sid,
290 Name,
291 &NameSize,
292 Domain,
293 &DomainSize,
294 &Use))
295 {
296 HeapFree(GetProcessHeap(),
297 0,
298 Name);
299 Name = NULL;
300 goto BuildSidString;
301 }
302 }
303 }
304 else
305 {
306 BuildSidString:
307 if (!ConvertSidToStringSid(Sid,
308 &SidString))
309 {
310 Error = TRUE;
311 break;
312 }
313 }
314
315 /* print the file name or space */
316 _tprintf(_T("%s "),
317 FullFileName);
318
319 /* attempt to map the SID to a user name */
320 if (AceIndex == 0)
321 {
322 DWORD i = 0;
323
324 /* overwrite the full file name with spaces so we
325 only print the file name once */
326 while (FullFileName[i] != _T('\0'))
327 FullFileName[i++] = _T(' ');
328 }
329
330 /* print the domain and/or user if possible, or the SID string */
331 if (Name != NULL && Domain[0] != _T('\0'))
332 {
333 _tprintf(_T("%s\\%s:"),
334 Domain,
335 Name);
336 IndentAccess = (DWORD)_tcslen(Domain) + _tcslen(Name);
337 }
338 else
339 {
340 LPTSTR DisplayString = (Name != NULL ? Name : SidString);
341
342 _tprintf(_T("%s:"),
343 DisplayString);
344 IndentAccess = (DWORD)_tcslen(DisplayString);
345 }
346
347 /* print the ACE Flags */
348 if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE)
349 {
350 IndentAccess += LoadAndPrintString(NULL,
351 IDS_ABBR_CI);
352 }
353 if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE)
354 {
355 IndentAccess += LoadAndPrintString(NULL,
356 IDS_ABBR_OI);
357 }
358 if (Ace->Header.AceFlags & INHERIT_ONLY_ACE)
359 {
360 IndentAccess += LoadAndPrintString(NULL,
361 IDS_ABBR_IO);
362 }
363
364 IndentAccess += 2;
365
366 /* print the access rights */
367 MapGenericMask(&AccessMask,
368 &FileGenericMapping);
369 if (Ace->Header.AceType & ACCESS_DENIED_ACE_TYPE)
370 {
371 if (AccessMask == FILE_ALL_ACCESS)
372 {
373 LoadAndPrintString(NULL,
374 IDS_ABBR_NONE);
375 }
376 else
377 {
378 LoadAndPrintString(NULL,
379 IDS_DENY);
380 goto PrintSpecialAccess;
381 }
382 }
383 else
384 {
385 if (AccessMask == FILE_ALL_ACCESS)
386 {
387 LoadAndPrintString(NULL,
388 IDS_ABBR_FULL);
389 }
390 else if (!(Ace->Mask & (GENERIC_READ | GENERIC_EXECUTE)) &&
391 AccessMask == (FILE_GENERIC_READ | FILE_EXECUTE))
392 {
393 LoadAndPrintString(NULL,
394 IDS_ABBR_READ);
395 }
396 else if (AccessMask == (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE))
397 {
398 LoadAndPrintString(NULL,
399 IDS_ABBR_CHANGE);
400 }
401 else if (AccessMask == FILE_GENERIC_WRITE)
402 {
403 LoadAndPrintString(NULL,
404 IDS_ABBR_WRITE);
405 }
406 else
407 {
408 DWORD x, x2;
409 static const struct
410 {
411 DWORD Access;
412 UINT uID;
413 }
414 AccessRights[] =
415 {
416 {FILE_WRITE_ATTRIBUTES, IDS_FILE_WRITE_ATTRIBUTES},
417 {FILE_READ_ATTRIBUTES, IDS_FILE_READ_ATTRIBUTES},
418 {FILE_DELETE_CHILD, IDS_FILE_DELETE_CHILD},
419 {FILE_EXECUTE, IDS_FILE_EXECUTE},
420 {FILE_WRITE_EA, IDS_FILE_WRITE_EA},
421 {FILE_READ_EA, IDS_FILE_READ_EA},
422 {FILE_APPEND_DATA, IDS_FILE_APPEND_DATA},
423 {FILE_WRITE_DATA, IDS_FILE_WRITE_DATA},
424 {FILE_READ_DATA, IDS_FILE_READ_DATA},
425 {FILE_GENERIC_EXECUTE, IDS_FILE_GENERIC_EXECUTE},
426 {FILE_GENERIC_WRITE, IDS_FILE_GENERIC_WRITE},
427 {FILE_GENERIC_READ, IDS_FILE_GENERIC_READ},
428 {GENERIC_ALL, IDS_GENERIC_ALL},
429 {GENERIC_EXECUTE, IDS_GENERIC_EXECUTE},
430 {GENERIC_WRITE, IDS_GENERIC_WRITE},
431 {GENERIC_READ, IDS_GENERIC_READ},
432 {MAXIMUM_ALLOWED, IDS_MAXIMUM_ALLOWED},
433 {ACCESS_SYSTEM_SECURITY, IDS_ACCESS_SYSTEM_SECURITY},
434 {SPECIFIC_RIGHTS_ALL, IDS_SPECIFIC_RIGHTS_ALL},
435 {STANDARD_RIGHTS_REQUIRED, IDS_STANDARD_RIGHTS_REQUIRED},
436 {SYNCHRONIZE, IDS_SYNCHRONIZE},
437 {WRITE_OWNER, IDS_WRITE_OWNER},
438 {WRITE_DAC, IDS_WRITE_DAC},
439 {READ_CONTROL, IDS_READ_CONTROL},
440 {DELETE, IDS_DELETE},
441 {STANDARD_RIGHTS_ALL, IDS_STANDARD_RIGHTS_ALL},
442 };
443
444 LoadAndPrintString(NULL,
445 IDS_ALLOW);
446
447 PrintSpecialAccess:
448 LoadAndPrintString(NULL,
449 IDS_SPECIAL_ACCESS);
450
451 /* print the special access rights */
452 x = sizeof(AccessRights) / sizeof(AccessRights[0]);
453 while (x-- != 0)
454 {
455 if ((Ace->Mask & AccessRights[x].Access) == AccessRights[x].Access)
456 {
457 _tprintf(_T("\n%s "),
458 FullFileName);
459 for (x2 = 0;
460 x2 < IndentAccess;
461 x2++)
462 {
463 _tprintf(_T(" "));
464 }
465
466 LoadAndPrintString(NULL,
467 AccessRights[x].uID);
468 }
469 }
470
471 _tprintf(_T("\n"));
472 }
473 }
474
475 _tprintf(_T("\n"));
476
477 /* free up all resources */
478 if (Name != NULL)
479 {
480 HeapFree(GetProcessHeap(),
481 0,
482 Name);
483 }
484
485 if (SidString != NULL)
486 {
487 LocalFree((HLOCAL)SidString);
488 }
489
490 AceIndex++;
491 }
492
493 if (!Error)
494 Ret = TRUE;
495 }
496 else
497 {
498 SetLastError(ERROR_NO_SECURITY_ON_OBJECT);
499 }
500 }
501 }
502
503 HeapFree(GetProcessHeap(),
504 0,
505 SecurityDescriptor);
506 }
507 else
508 {
509 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
510 }
511
512 return Ret;
513 }
514
515
516 int
517 __cdecl
518 _tmain(int argc, const TCHAR *argv[])
519 {
520 if (argc < 2)
521 {
522 PrintHelp();
523 return 1;
524 }
525 else
526 {
527 TCHAR FullPath[MAX_PATH + 1];
528 TCHAR *FilePart = NULL;
529 WIN32_FIND_DATA FindData;
530 HANDLE hFind;
531 DWORD LastError;
532
533 if (argc > 2)
534 {
535 /* FIXME - parse arguments */
536 }
537
538 /* get the full path of where we're searching in */
539 if (GetFullPathName(argv[1],
540 sizeof(FullPath) / sizeof(FullPath[0]),
541 FullPath,
542 &FilePart) != 0)
543 {
544 if (FilePart != NULL)
545 *FilePart = _T('\0');
546 }
547 else
548 goto Error;
549
550 /* find the file(s) */
551 hFind = FindFirstFile(argv[1],
552 &FindData);
553 if (hFind != INVALID_HANDLE_VALUE)
554 {
555 do
556 {
557 if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
558 (_tcscmp(FindData.cFileName,
559 _T(".")) &&
560 _tcscmp(FindData.cFileName,
561 _T(".."))))
562 {
563 if (argc > 2)
564 {
565 /* FIXME - edit or replace the descriptor */
566 }
567 else
568 {
569 if (!PrintFileDacl(FullPath,
570 FindData.cFileName))
571 {
572 LastError = GetLastError();
573
574 if (LastError == ERROR_ACCESS_DENIED)
575 {
576 PrintErrorMessage(LastError);
577 }
578 else
579 break;
580 }
581 else
582 _tprintf(_T("\n"));
583 }
584 }
585 } while (FindNextFile(hFind,
586 &FindData));
587
588 FindClose(hFind);
589
590 if (GetLastError() != ERROR_NO_MORE_FILES)
591 {
592 goto Error;
593 }
594 }
595 else
596 {
597 Error:
598 PrintErrorMessage(GetLastError());
599 return 1;
600 }
601 }
602
603 return 0;
604 }