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