|
|
1.1 root 1: /****************************************************************************\
2: *
1.1.1.2 ! root 3: * Microsoft Developer Support
! 4: * Copyright (c) 1992 Microsoft Corporation
! 5: *
1.1 root 6: * MODULE: sidclean.c
7: *
8: * NT never deletes SIDs, so the name of this sample is most
9: * accurately be intrepreted as "Clean up SID ownership and
10: * ACE's that relate to SIDs that still (and always will)
11: * exist, but for which the corresponding user account has been
12: * deleted"
13: *
14: *
15: * PURPOSE: Demonstrate some of the Win32 security api(s), and provide a
16: * sample of how a utility could be written that recovers
17: * on-disk resources remaining allocated to deleted user
18: * accounts. The on-disk resources recovered are 1) Files that
19: * are still owned by accounts that have been deleted are
20: * assigned ownership to the account logged on when this sample
21: * is run, and 2) ACE's for deleted accounts are edited
22: * (deleted) out of the ACLs of files to which the deleted
23: * accounts had been granted authorizations (eg., Read access)
24: *
25: * It may be that running this sample as a utility has no
26: * practical value in many environments, as the number of files
27: * belonging to deleted user accounts will often be quite
28: * small, and the number of bytes recovered on disk by editing
29: * out ACEs for deleted accounts may well not be worth the time
30: * it takes to run this sample. The time it takes to run this
31: * sample may be quite significant when processing an entire
32: * hard disk or partition
33: *
34: * This sample is not a supported utility
35: *
36: *
37: * TO RUN: You must log on using an account, such as Administrator, that
38: * has the priviledges to take file ownership and edit ACls
39: *
40: * The ACL editing part of this sample can only be excercised for
41: * files on a partition that has ACLs NT processes: NTFS
42: *
43: * Typical test scenario: Create a user account or two, log on
44: * as each of these accounts in turn, while logged on for each
45: * account, go to an NTFS partition, create a couple of files
46: * so the test accounts each own a few files, use the file
47: * manager to edit permissions for those files so that each
48: * test user has some authorities (e.g., Read) explicitly
49: * granted for those files. Logon as Administrator, authorize
50: * each test user to a few Administrator-owned files. Delete
51: * the test accounts. Run the sample in the directories where
52: * you put the files the test accounts owned or were authorized
53: * to
54: *
55: *
56: * OVERALL APPROACH: The command line interface is kept inflexible to simplify
57: * it's parsing in this sample. The user must pass in a switch
58: * argument, a directory spec, and a file search pattern
59: *
60: * The sample positions the current directory (of the process the
61: * sample runs in) to the dir spec, and uses FindFirstFile and
62: * FindNextFile to walk through the directory specified looking
63: * for files that match the file pattern specified
64: *
65: * The switch argument can cause subdirectories to be searched
66: * recursively
67: *
68: * The switch argument lets the user choose only to take
69: * ownerships, only to edit ACLs, do both, or do neither, in
70: * which case the sample merely reports on what ownerships
71: * would have been taken, and what ACE's would have been
72: * deleted
73: *
74: * As the directories are walked, each file that matches the
75: * file pattern is processed right then
76: *
77: * Note that we process files in a directory, and we also process
78: * the directory itself that contains the files. We process
79: * directories because they can also be owned by deleted
80: * accounts, or could have ACEs that will no longer be used
81: *
82: * Note also that we process all directories that we check for
83: * files, regardless of the spelling of the directory name
84: *
85: * Counters are kept of file ownerships taken, ACEs deleted and
86: * total files checked, to print a summary line at the end of
87: * the run
88: *
89: * The sample considers it perfectly acceptable if 0 files match
90: * the file pattern for the entire run
91: *
92: *
93: * FUNCTIONS: DoMatchingFilesInOneDir
94: *
95: * Look in one dir or sub-dir for files that match the file
96: * pattern. For each match call DoOneFileOrDir
97: *
98: * DoAllDirsInOneDir
99: *
100: * For all the sub-dirs in a dir, set the current directory to be
101: * that directory, check for files that match the file pattern,
102: * and if any match, call DoMatchingFilesInOneDir to process
103: * those. Then reset the current directory
104: *
105: * GetFullFileOrDirName
106: *
107: * Get the full name of the file or dir for simplified processing
108: * (and for display on the console)
109: *
110: * DoOneFileOrDir
111: *
112: * Get the file's SD (Security Descriptor), and call
113: * TakeOwnershipIfAppropriate and/or DeleteACEsAsAppropriate as
114: * needed
115: *
116: * TakeOwnershipIfAppropriate
117: *
118: * Get the owning SID of the file from the file's SD that
119: * DoOneFileOrDir passed in, check that SID to see if the
120: * account is deleted. If so, edit into the file's SD a new
121: * owning SID (the SID of the process running the sample).
122: * Then write the modified file SD to disk
123: *
124: * DeleteACEsAsAppropriate
125: *
126: * Get the DACL of the file from the file's SD that
127: * DoOneFileOrDir passed in. Walk through the ACE list for
128: * that DACL, checking each ACE to see what SID the ACE refers
129: * to. For the SID referred to, check to see if the account is
130: * deleted. If so, delete that ACE from the DACL. When all
131: * ACE's have been examined, write the new DACL into the file's
132: * SD. Then write the modified file SD to disk
133: *
134: * GetProcessSid
135: *
136: * Retrieve into a global variable the SID of the user account
137: * logged on when this sample is run. This is the SID used by
138: * TakeOwnershipIfAppropriate
139: *
140: * CrackArgs
141: *
142: * Process the command line, cracking (parsing/decoding) the
143: * switch argument into boolean global variables (see below).
144: * Call DisplayHelp if anything illegal is found in the command
145: * line, or if the user asked for help
146: *
147: * DisplayHelp
148: *
149: * Display help text on the console
150: *
151: *
152: * GLOBAL VARS:
153: * BOOL bTakeOwnership
154: * BOOL bEditACLs
155: * BOOL bRecurse
156: * BOOL bJustCount
157: *
158: * These store the values the user specified on the command
159: * line's first argument (the switches argument).
160: * Respectively, they record whether we are to do the
161: * processing to Take Ownerships, Edit ACLs, whether we are to
162: * recurse into all subdirectories, and whether we are just
163: * counting up what would be processed (in which case we take
164: * no ownerships and edit no ACLs)
165: *
166: * DWORD dwFilesChecked
167: * DWORD dwFilesOwned
168: * DWORD dwACEsDeleted
169: *
170: * These count, respecively, the total files we checked, the
171: * number of files we took ownership of (or would have if we
172: * had not been told only to count), and the number of ACEs we
173: * deleted (or would have if we had not been told only to
174: * count). Note that the total number of files checked does
175: * not include files in the directories we process that do not
176: * match the file pattern
177: *
178: * Note, however, that we process directories regardless of
179: * whether they match the file pattern
180: *
181: * UCHAR ucProcessSIDBuf
182: * PSID psidProcessOwnerSID
183: *
184: * These store the SID of the account logged on as this sample
185: * runs, and a pointer to that SID
186: *
187: \****************************************************************************/
188:
189:
190: /****************************************************************************\
191: * INCLUDES, DEFINES, TYPEDEFS
192: \****************************************************************************/
1.1.1.2 ! root 193: #define STRICT
1.1 root 194: #include <windows.h>
195: #include <stdio.h>
196: #include <string.h>
1.1.1.2 ! root 197: #include <stdlib.h>
1.1 root 198:
199: #define PERR(api) printf("%s: Error %d from %s on line %d\n", \
200: __FILE__, GetLastError(), api, __LINE__);
201: #define PMSG(msg) printf("%s line %d: %s\n", \
202: __FILE__, __LINE__, msg);
203:
204: #define PrintAppStyleAPIError(ApiTxt,MsgTxt) { \
205: DWORD dwLastError; \
206: dwLastError = GetLastError(); \
207: switch (dwLastError) \
208: { case ERROR_FILE_NOT_FOUND : \
209: printf("\nFile not found (%s) line %d",MsgTxt,__LINE__); \
210: break; \
211: case ERROR_INVALID_NAME : \
212: printf("\nInvalid name (%s) line %d",MsgTxt,__LINE__); \
213: break; \
214: case ERROR_PATH_NOT_FOUND : \
215: printf("\nError path not found (%s) line %d",MsgTxt,__LINE__); \
216: break; \
217: case ERROR_SHARING_VIOLATION : \
218: printf("\nSharing violation - shut down net and/or stop other sessions (%s) line %d",MsgTxt,__LINE__); \
219: break; \
220: case ERROR_ACCESS_DENIED : \
221: printf("\nAccess denied (%s) line %d",MsgTxt,__LINE__); \
222: break; \
223: default : \
224: printf("\n" #ApiTxt " - unexpected return code=%d (%s) line %d",dwLastError,MsgTxt,__LINE__); \
225: break; \
226: } \
227: }
228:
229: /****************************************************************************\
230: * GLOBAL VARIABLES
231: \****************************************************************************/
232:
233: BOOL bTakeOwnership = FALSE;
234: BOOL bEditACLs = FALSE;
235: BOOL bRecurse = FALSE;
236: BOOL bJustCount = FALSE;
237:
238: DWORD dwFilesChecked = 0;
239: DWORD dwFilesOwned = 0;
240: DWORD dwACEsDeleted = 0;
241:
1.1.1.2 ! root 242: PSID psidProcessOwnerSID;
1.1 root 243:
244:
245: /****************************************************************************\
246: * FUNCTION PROTOTYPES
247: \****************************************************************************/
248:
249: BOOL DoMatchingFilesInOneDir(HANDLE hFound,
250: WIN32_FIND_DATA ffdFoundData);
251: BOOL DoAllDirsInOneDir(char *FilePattern);
252: BOOL GetFullFileOrDirName(LPTSTR lpszFileName);
253: BOOL DoOneFileOrDir(LPTSTR lpszFullName);
254: BOOL TakeOwnershipIfAppropriate(PSECURITY_DESCRIPTOR psdFileSD,
255: LPTSTR lpszFullName);
256: BOOL DeleteACEsAsAppropriate (PSECURITY_DESCRIPTOR psdFileSD,
257: LPTSTR lpszFullName);
258: BOOL GetProcessSid(VOID);
259: BOOL CrackArgs(UINT argc, char *argv[]);
260: VOID DisplayHelp(VOID);
261:
262: /****************************************************************************\
263: *
264: * FUNCTION: Main
265: *
266: \****************************************************************************/
267:
268: UINT main(UINT argc, char *argv[])
269: {
270: WIN32_FIND_DATA ffdFoundData;
271: HANDLE hFound;
272: #define SZ_NAME_BUF MAX_PATH
273: UCHAR ucPathBuf[SZ_NAME_BUF];
274: LPTSTR lpszFullName = (LPTSTR)&ucPathBuf;
275:
276: /**************************************************************************\
277: *
278: * Store the process's SID in a global variable for later use (in taking
279: * ownership).
280: *
281: \**************************************************************************/
282:
283: if (!GetProcessSid())
284: { PERR("Can't proceed without process SID - see earlier error messages");
285: return(1);
286: }
287:
288: if (!CrackArgs(argc,argv))
289: return(1);
290:
291: /**************************************************************************\
292: *
293: * CrackArgs has set our global processing switches, and proven argv[2] and
294: * argv[3] are our non-blank dir-spec and file-pattern strings. Now we
295: * must see that the file-spec is acceptable to the Win32 api's. Argv[2]
296: * is the file-spec to pass to SetCurrentDirectory, and argv[3] is the
297: * file-pattern to pass to FindFirstFile
298: *
299: * First we have to expand the dir-spec in argv[2], because if we set the
300: * current directory to it before expansion,the expansion will have a
301: * different result if argv[2] is something like ..\..
302: *
303: \**************************************************************************/
304:
305: strcpy(lpszFullName,argv[2]);
306:
307: if (!GetFullFileOrDirName(lpszFullName))
1.1.1.2 ! root 308: { PERR("Failed to expand to full name the 2nd argument (directory specification)");
1.1 root 309: return(1);
310: }
311:
312: /**************************************************************************\
313: *
314: * Now we pass the un-expanded argv[2] to SetCurrentDirectory for validity
315: * checking. GetFullPathName (called by GetFullFileOrDirName) does not
316: * validity check
317: *
318: \**************************************************************************/
319:
320: if (!SetCurrentDirectory(argv[2]))
321: { PrintAppStyleAPIError(SetCurrentDirectory,"2nd argument (directory specification)");
322: return(1);
323: }
324:
325: /**************************************************************************\
326: *
327: * We begin processing with the current directory, using the expanded form we
328: * got before. We have to use the expanded form, because if we set to
329: * ..\.. and then try to process the string ..\.. as a dir name, instead of
330: * processing the dir two levels up from where we are we'll process the dir
331: * four levels up
332: *
333: \**************************************************************************/
334:
335: if (!DoOneFileOrDir(lpszFullName))
336: return(1);
337:
338: /**************************************************************************\
339: *
340: * It's OK to get no hits. The files-checked counter will show how many
341: * files we looked at, and it's OK to look at 0
342: *
343: * On the else branch, Argv[3] has been verified, and we have a good handle.
344: * We now pass to DoMatchingFilesInOneDir for processing the handle and
345: * found data we just got from FindFirstFile
346: *
347: \**************************************************************************/
348:
349: hFound = FindFirstFile(argv[3],
350: (LPWIN32_FIND_DATA)&ffdFoundData);
351: if ((HANDLE)(-1) == hFound)
352: { if (GetLastError() != ERROR_FILE_NOT_FOUND)
353: { PrintAppStyleAPIError(FindFirstFile,"3rd argument");
354: return(1);
355: }
356: }
357: else if (!DoMatchingFilesInOneDir(hFound,ffdFoundData))
358: return(1);
359:
360: /**************************************************************************\
361: *
362: * Pass the original file pattern for recursive calling to DoAllDirsInOneDir
363: *
364: \**************************************************************************/
365:
366: if (!DoAllDirsInOneDir(argv[3]))
367: return(1);
368:
369: if (bJustCount)
370: printf("\nChecked %d files, would have taken ownership of %d files, would have deleted %d ACEs\n",
371: dwFilesChecked,dwFilesOwned,dwACEsDeleted);
372: else
373: printf("\nChecked %d files, took ownership of %d files, deleted %d ACEs\n",
374: dwFilesChecked,dwFilesOwned,dwACEsDeleted);
375:
1.1.1.2 ! root 376: free(psidProcessOwnerSID);
! 377:
1.1 root 378: return(0);
379: }
380:
381: /****************************************************************************\
382: *
383: * FUNCTION: DoMatchingFilesInOneDir
384: *
385: \****************************************************************************/
386:
387: BOOL DoMatchingFilesInOneDir(HANDLE hFound,
388: WIN32_FIND_DATA ffdFoundData)
389: {
390: BOOL bDoneWithHandle = FALSE;
391:
392: /**************************************************************************\
393: *
394: * Process all files referred to by the handle, but not including
395: * directories, because directories are handled with separate calls to
396: * DoOneFileOrDir. Such separate calls are made as we are setting the
397: * current directory to be the directory to be processed
398: *
399: \**************************************************************************/
400:
401: while (!bDoneWithHandle)
402: {
403: if (!(FILE_ATTRIBUTE_DIRECTORY & ffdFoundData.dwFileAttributes))
404: {
405: if (!DoOneFileOrDir(ffdFoundData.cFileName))
406: return(FALSE);
407: }
408:
409: if (!FindNextFile(hFound,
410: (LPWIN32_FIND_DATA)&ffdFoundData))
411: if (GetLastError() == ERROR_NO_MORE_FILES)
412: bDoneWithHandle = TRUE;
413: else
414: { PrintAppStyleAPIError(FindNextFile,"on FindNext");
415: return(FALSE);
416: }
417: }
418: }
419:
420: /****************************************************************************\
421: *
422: * FUNCTION: DoAllDirsInOneDir
423: *
424: \****************************************************************************/
425:
426: BOOL DoAllDirsInOneDir(char *FilePattern)
427: {
428: HANDLE hFound;
429: WIN32_FIND_DATA ffdFoundData;
430: BOOL bDoneWithHandle = FALSE;
431:
432: /**************************************************************************\
433: *
434: * If not recursing into dirs, simply return
435: *
436: \**************************************************************************/
437:
438: if (!bRecurse)
439: return TRUE;
440:
441: /**************************************************************************\
442: *
443: * Since we are recursing, get a handle that points to entire directory, and
444: * walk the handle picking off only directories to recurse into
445: *
446: \**************************************************************************/
447:
448: hFound = FindFirstFile("*.*",
449: (LPWIN32_FIND_DATA)&ffdFoundData);
450: if ((HANDLE)(-1) == hFound)
451: { PrintAppStyleAPIError(FindFirstFile,"on dir *.* FindFirst");
452: return(FALSE);
453: }
454:
455: while (!bDoneWithHandle)
456: {
457: /************************************************************************\
458: *
459: * We only do dirs here, and we only do directories with textual names
460: * (i.e., not "." and not "..")
461: *
462: \************************************************************************/
463:
464: if ( (FILE_ATTRIBUTE_DIRECTORY & ffdFoundData.dwFileAttributes)
465: && (0 != strcmp("." ,ffdFoundData.cFileName))
466: && (0 != strcmp("..",ffdFoundData.cFileName)))
467: {
468: HANDLE hFile2;
469: WIN32_FIND_DATA ffdFound2;
470:
471: /**********************************************************************\
472: *
473: * We begin processing the new current directory by processing it itself,
474: * then setting the current dir to be the dir itself
475: *
476: \**********************************************************************/
477:
478: if (!DoOneFileOrDir(ffdFoundData.cFileName))
479: return(FALSE);
480:
481: if (!SetCurrentDirectory(ffdFoundData.cFileName))
482: { PrintAppStyleAPIError(SetCurrentDirectory,"recursive set");
483: return(FALSE);
484: }
485:
486: /**********************************************************************\
487: *
488: * It's OK to get no hits. The files-checked counter will show how many
489: * files we looked at, and it's OK to look at 0
490: *
491: \**********************************************************************/
492:
493: hFile2 = FindFirstFile(FilePattern,
494: (LPWIN32_FIND_DATA)&ffdFound2);
495: if ((HANDLE)(-1) == hFile2)
496: { if (GetLastError() != ERROR_FILE_NOT_FOUND)
497: { PrintAppStyleAPIError(FindFirstFile,"during recursion");
498: return(FALSE);
499: }
500: }
501: else if (!DoMatchingFilesInOneDir(hFile2,ffdFound2))
502: return(FALSE);
503:
504: if (!DoAllDirsInOneDir(FilePattern))
505: return(FALSE);
506:
507: if (!SetCurrentDirectory(".."))
508: { PrintAppStyleAPIError(SetCurrentDirectory,"un-recursive set");
509: return(FALSE);
510: }
511: }
512:
513: /************************************************************************\
514: *
515: * Get next recursion candidate (file or dir at this point, however at loop
516: * top files are screened out)
517: *
518: \************************************************************************/
519:
520: if (!FindNextFile(hFound,
521: (LPWIN32_FIND_DATA)&ffdFoundData))
522: if (GetLastError() == ERROR_NO_MORE_FILES)
523: bDoneWithHandle = TRUE;
524: else
525: { PrintAppStyleAPIError(FindNextFile,"on dir *.* FindNext");
526: return(FALSE);
527: }
528: }
529: return(TRUE);
530: }
531:
532: /****************************************************************************\
533: *
534: * FUNCTION: GetFullFileOrDirName
535: *
536: \****************************************************************************/
537:
538: BOOL GetFullFileOrDirName(LPTSTR lpszFileName)
539: {
540: UCHAR ucPathBuf[SZ_NAME_BUF];
541: DWORD dwSzReturned;
542: LPTSTR lpszLastNamePart;
543: LPTSTR lpszFullName;
544:
545: dwSzReturned = GetFullPathName
546: (lpszFileName,
547: (DWORD)SZ_NAME_BUF,
548: (LPTSTR)&ucPathBuf,
549: (LPTSTR *)&lpszLastNamePart);
550: if (0 == dwSzReturned)
551: switch (GetLastError())
552: { case ERROR_INVALID_NAME :
553: printf("\nError invalid file full-name (on GetFullPathName)");
554: return(FALSE);
555: default :
556: PERR("GetFullPathName - unexpected return code");
557: return(FALSE);
558: }
559:
560: if (dwSzReturned > SZ_NAME_BUF)
561: { PERR("GetFullPathName - buffer too small");
562: return(FALSE);
563: }
564:
565: lpszFullName = CharLower((LPTSTR)&ucPathBuf);
566:
567: if (!lpszFullName)
568: { PERR("CharLower failure");
569: return(FALSE);
570: }
571:
572: /**************************************************************************\
573: *
574: * Copy the expanded and upper-case-shifted name to the buffer pointed to by
575: * the input argument
576: *
577: \**************************************************************************/
578:
579: strcpy(lpszFileName,lpszFullName);
580: }
581:
582: /****************************************************************************\
583: *
584: * FUNCTION: DoOneFileOrDir
585: *
586: \****************************************************************************/
587:
588: BOOL DoOneFileOrDir(LPTSTR lpszFullName)
589: {
590: #define SZ_SD_BUF 1000
591: UCHAR ucBuf [SZ_SD_BUF];
592: DWORD dwSDLength = SZ_SD_BUF;
593: DWORD dwSDLengthNeeded;
594: PSECURITY_DESCRIPTOR psdFileSD;
595:
596: if (!GetFullFileOrDirName(lpszFullName))
597: return(FALSE);
598:
599: /**************************************************************************\
600: *
601: * Now the input argument's name is accurate: it is expanded and lower-case
602: *
603: \**************************************************************************/
604:
605: printf("\nChecking %s",lpszFullName);
606:
607: dwFilesChecked++;
608:
609: psdFileSD = (PSECURITY_DESCRIPTOR)&ucBuf;
610:
611: if (!GetFileSecurity
612: (lpszFullName,
613: (SECURITY_INFORMATION)(OWNER_SECURITY_INFORMATION
614: | DACL_SECURITY_INFORMATION),
615: psdFileSD,
616: dwSDLength,
617: (LPDWORD)&dwSDLengthNeeded))
618: { PERR("GetFileSecurity");
619: return(FALSE);
620: }
621:
622: /**************************************************************************\
623: *
624: * This validity check is here for demonstration pruposes. It's not likely a
625: * real app would need to check the validity of this returned SD. The
626: * validity check APIs are more intended to check validity after app code
627: * has manipulated the structure and is about to hand it back to the system
628: *
629: \**************************************************************************/
630:
631: if (!IsValidSecurityDescriptor(psdFileSD))
632: { PERR("IsValidSecurityDescriptor said bad SD");
633: return(FALSE);
634: }
635:
636: if (bTakeOwnership)
637: if (!TakeOwnershipIfAppropriate(psdFileSD,lpszFullName))
638: return(FALSE);
639:
640: if (bEditACLs)
641: if (!DeleteACEsAsAppropriate (psdFileSD,lpszFullName))
642: return(FALSE);
643:
644: return(TRUE);
645: }
646:
647: /****************************************************************************\
648: *
649: * FUNCTION: TakeOwnershipIfAppropriate
650: *
651: \****************************************************************************/
652:
653: BOOL TakeOwnershipIfAppropriate(PSECURITY_DESCRIPTOR psdFileSD,
654: LPTSTR lpszFullName)
655: {
656: PSID psidFileOwnerSID;
657: {
658: BOOL bOwnerDefaulted;
659:
660: if (!GetSecurityDescriptorOwner
661: (psdFileSD,
662: (PSID *)&psidFileOwnerSID,
663: (LPBOOL)&bOwnerDefaulted))
664: { PERR("GetSecurityDescriptorOwner");
665: return(FALSE);
666: }
667:
668: /************************************************************************\
669: *
670: * This validity check is here for demonstration pruposes. It's not likely
671: * a real app would need to check the validity of this returned SID. The
672: * validity check APIs are more intended to check validity after app code
673: * has manipulated the structure and is about to hand it back to the
674: * system
675: *
676: \************************************************************************/
677:
678: if (!IsValidSid(psidFileOwnerSID))
679: { PERR("IsValidSid said bad SID!");
680: return(FALSE);
681: }
682: }
683:
684: {
685: DWORD dwLastError = NO_ERROR;
686: #define SZ_ACCT_NAME_BUF 1000
687: UCHAR ucNameBuf [SZ_ACCT_NAME_BUF];
688: DWORD dwNameLength = SZ_ACCT_NAME_BUF;
689: #define SZ_DMN_NAME_BUF 1000
690: UCHAR ucDomainNmBuf [SZ_DMN_NAME_BUF ];
691: DWORD dwDNameLength = SZ_DMN_NAME_BUF ;
692: SID_NAME_USE peAcctNameUse;
693:
694: if (!LookupAccountSid
695: ((LPTSTR)"", // Look on local machine
696: psidFileOwnerSID,
697: (LPTSTR)&ucNameBuf,
698: (LPDWORD)&dwNameLength,
699: (LPTSTR)&ucDomainNmBuf,
700: (LPDWORD)&dwDNameLength,
701: (PSID_NAME_USE)&peAcctNameUse))
702: { dwLastError = GetLastError();
703: if (ERROR_NONE_MAPPED != dwLastError)
704: { PERR("LookupAccountSID");
705: return(FALSE);
706: }
707: }
708:
709: /************************************************************************\
710: *
711: * If deleted account, take ownership. This routine's caller checked the
712: * global switches that said we are in take ownership mode
713: *
714: * In some cases, the account lookup will fail with ERROR_NONE_MAPPED,
715: * meaning there is no deleted account mapped to the SID, in which case
716: * we also take ownership
717: *
718: * We check that first to avoid referencing peAcctNameUse, which in that
719: * case is not set
720: *
721: \************************************************************************/
722:
723: if ( (ERROR_NONE_MAPPED == dwLastError)
724: || (SidTypeDeletedAccount == peAcctNameUse))
725: {
726: dwFilesOwned++;
727:
728: if (bJustCount)
729: { printf(" - would have taken ownership");
730: return(TRUE);
731: }
732: else
733: {
734: /********************************************************************\
735: *
736: * Modify the SD in virtual memory. No check on the new owning SID
737: * here, because we validity checked it when we fetched it in
738: * GetProcessSid
739: *
740: \********************************************************************/
741:
742: if (!SetSecurityDescriptorOwner
743: (psdFileSD,
744: psidProcessOwnerSID,
745: FALSE)) // New owner explicitly specified
746: { PERR("SetSecurityDescriptorOwner");
747: return(FALSE);
748: }
749:
750: /********************************************************************\
751: *
752: * This validity check is something a real app might actually like to
753: * do. We manupulated the SD, so before we write it back out to the
754: * file system, a check is worth considering.
755: *
756: \********************************************************************/
757:
758: if (!IsValidSecurityDescriptor(psdFileSD))
759: { PERR("IsValidSecurityDescriptor said bad SD");
760: return(FALSE);
761: }
762:
763: /********************************************************************\
764: *
765: * Modify the SD on the hard disk
766: *
767: \********************************************************************/
768:
769: if (!SetFileSecurity
770: (lpszFullName,
771: (SECURITY_INFORMATION)OWNER_SECURITY_INFORMATION,
772: psdFileSD))
773: { PERR("SetFileSecurity");
774: return(FALSE);
775: }
776:
777: printf(" - took ownership");
778: }
779: }
780: }
781:
782: return(TRUE);
783:
784: }
785:
786: /****************************************************************************\
787: *
788: * FUNCTION: DeleteACEsAsAppropriate
789: *
790: \****************************************************************************/
791:
792: BOOL DeleteACEsAsAppropriate(PSECURITY_DESCRIPTOR psdFileSD,
793: LPTSTR lpszFullName)
794: {
795: PACL paclFile;
796: BOOL bHasACL;
797: BOOL bOwnerDefaulted;
798: DWORD dwAcl_i;
799: DWORD dwACEsDeletedBeforeNow;
800:
801: ACL_SIZE_INFORMATION asiAclSize;
802: DWORD dwBufLength = sizeof(asiAclSize);
803: ACCESS_ALLOWED_ACE *paaAllowedAce;
804:
805: if (!GetSecurityDescriptorDacl(psdFileSD,
806: (LPBOOL)&bHasACL,
807: (PACL *)&paclFile,
808: (LPBOOL)&bOwnerDefaulted))
809: { PERR("GetSecurityDescriptorDacl");
810: return(FALSE);
811: }
812:
813: if (!bHasACL) // No ACL to process, so OK, return
814: return(TRUE);
815:
816: /**************************************************************************\
817: *
818: * This validity check is here for demonstration pruposes. It's not likely a
819: * real app would need to check the validity of this returned ACL. The
820: * validity check APIs are more intended to check validity after app code
821: * has manipulated the structure and is about to hand it back to the system
822: *
823: \**************************************************************************/
824:
825: if (!IsValidAcl(paclFile))
826: { PERR("IsValidAcl said bad ACL!");
827: return(FALSE);
828: }
829:
830: if (!GetAclInformation(paclFile,
831: (LPVOID)&asiAclSize,
832: (DWORD)dwBufLength,
833: (ACL_INFORMATION_CLASS)AclSizeInformation))
834: { PERR("GetAclInformation");
835: return(FALSE);
836: }
837:
838: dwACEsDeletedBeforeNow = dwACEsDeleted;
839:
840: /**************************************************************************\
841: *
842: * We loop through in reverse order, because that's simpler, given that we
843: * potentially delete ACEs as we loop through. If started at 0 and went
844: * up, if we deleted the 0th ACE, then the 1th ACE would become the 0th,
845: * and we'd have to check the 0th ACE again
846: *
847: \**************************************************************************/
848:
849: for (dwAcl_i = asiAclSize.AceCount-1; ((int)dwAcl_i) >= 0; dwAcl_i--)
850: {
851: /************************************************************************\
852: *
853: * It doesn't matter for this sample that we don't yet know the ACE type,
854: * because they all start with the header field and that's what we need
855: *
856: \************************************************************************/
857:
858: if (!GetAce(paclFile,
859: dwAcl_i,
860: (LPVOID *)&paaAllowedAce))
861: { PERR("GetAce");
862: return(FALSE);
863: }
864:
865: /************************************************************************\
866: *
867: * There are only four Ace Types pre-defined, so this next check is
868: * redundant in a real app, but useful as a sanity check and a
869: * demonstration in a sample
870: *
871: \************************************************************************/
872:
873: if (!( (paaAllowedAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
874: ||(paaAllowedAce->Header.AceType == ACCESS_DENIED_ACE_TYPE )
875: ||(paaAllowedAce->Header.AceType == SYSTEM_AUDIT_ACE_TYPE )
876: ||(paaAllowedAce->Header.AceType == SYSTEM_ALARM_ACE_TYPE )))
877: { PERR("Invalid AceType");
878: return(FALSE);
879: }
880:
881: { // Find SID of ACE, check if acct deleted
882:
883: UCHAR ucNameBuf [SZ_ACCT_NAME_BUF];
884: DWORD dwNameLength = SZ_ACCT_NAME_BUF;
885: UCHAR ucDomainNmBuf [SZ_DMN_NAME_BUF];
886: DWORD dwDNameLength = SZ_DMN_NAME_BUF;
887: SID_NAME_USE peAcctNameUse;
888: DWORD dwLastError = NO_ERROR;
889:
890: /**********************************************************************\
891: *
892: * This validity check is here for demonstration pruposes. It's not
893: * likely a real app would need to check the validity of the SID
894: * contained in the returned ACL. The validity check APIs are more
895: * intended to check validity after app code has manipulated the
896: * structure and is about to hand it back to the system
897: *
898: \**********************************************************************/
899:
900: if (!IsValidSid((PSID)&(paaAllowedAce->SidStart)))
1.1.1.2 ! root 901: { PERR("IsValidSid said bad SID!");
! 902: return(FALSE);
! 903: }
1.1 root 904:
905: if (!LookupAccountSid
906: ((LPTSTR)"", // Look on local machine
907: (PSID)&(paaAllowedAce->SidStart),
908: (LPTSTR)&ucNameBuf,
909: (LPDWORD)&dwNameLength,
910: (LPTSTR)&ucDomainNmBuf,
911: (LPDWORD)&dwDNameLength,
912: (PSID_NAME_USE)&peAcctNameUse))
913: { dwLastError = GetLastError();
914: if (ERROR_NONE_MAPPED != dwLastError)
915: { PERR("LookupAccountSID");
916: return(FALSE);
917: }
918: }
919:
920: if ( (ERROR_NONE_MAPPED == dwLastError)
921: || (SidTypeDeletedAccount == peAcctNameUse))
922: {
923: dwACEsDeleted++;
924:
925: if (bJustCount)
926: { printf(" - would have edited ACL");
927: return(TRUE);
928: }
929:
930: if (!DeleteAce(paclFile,dwAcl_i))
931: { PERR("DeleteAce");
932: return(FALSE);
933: }
934: }
935: }
936: }
937:
938: if (dwACEsDeletedBeforeNow < dwACEsDeleted)
939: {
940: /************************************************************************\
941: *
942: * This validity check is something a real app might actually like to do.
943: * We manupulated the ACL, so before we write it back into an SD, a check
944: * is worth considering
945: *
946: \************************************************************************/
947:
948: if (!IsValidAcl(paclFile))
949: { PERR("IsValidAcl said bad ACL!");
950: return(FALSE);
951: }
952:
953: /************************************************************************\
954: *
955: * Modify the SD in virtual memory
956: *
957: \************************************************************************/
958:
959: if (!SetSecurityDescriptorDacl
960: (psdFileSD,
961: TRUE, // Yes, set the DACL
962: paclFile,
963: FALSE)) // New DACL explicitly specified
964: { PERR("SetSecurityDescriptorDacl");
965: return(FALSE);
966: }
967:
968: /************************************************************************\
969: *
970: * This validity check is something a real app might actually like to do.
971: * We manupulated the SD, so before we write it back out to the file
972: * system, a check is worth considering
973: *
974: \************************************************************************/
975:
976: if (!IsValidSecurityDescriptor(psdFileSD))
977: { PERR("IsValidSecurityDescriptor said bad SD");
978: return(FALSE);
979: }
980:
981: /************************************************************************\
982: *
983: * Modify the SD on the hard disk
984: *
985: \************************************************************************/
986:
987: if (!SetFileSecurity
988: (lpszFullName,
989: (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
990: psdFileSD))
991: { PERR("SetFileSecurity");
992: return(FALSE);
993: }
994:
995: printf(" - edited ACL");
996: }
997:
998: return(TRUE);
999:
1000: }
1001:
1002: /****************************************************************************\
1003: *
1004: * FUNCTION: GetProcessSid
1005: *
1006: \****************************************************************************/
1007:
1008: BOOL GetProcessSid(VOID)
1009: {
1010: HANDLE hProcess;
1011: PSECURITY_DESCRIPTOR psdProcessSD;
1012: PSID psidProcessOwnerSIDTemp;
1013:
1014: UCHAR ucBuf [SZ_SD_BUF];
1015: DWORD dwSDLength = SZ_SD_BUF;
1016: DWORD dwSDLengthNeeded;
1017: BOOL bOwnerDefaulted;
1018:
1019: hProcess = GetCurrentProcess();
1020:
1021: if (!hProcess)
1.1.1.2 ! root 1022: { PERR("GetCurrentProcess");
! 1023: return(FALSE);
! 1024: }
1.1 root 1025:
1026: psdProcessSD = (PSECURITY_DESCRIPTOR)ucBuf;
1027:
1028: if (!GetKernelObjectSecurity
1029: (hProcess,
1030: (SECURITY_INFORMATION)(OWNER_SECURITY_INFORMATION),
1031: psdProcessSD,
1032: dwSDLength,
1033: (LPDWORD)&dwSDLengthNeeded))
1.1.1.2 ! root 1034: { PERR("GetKernelObjectSecurity on current process handle");
! 1035: return(FALSE);
! 1036: }
1.1 root 1037:
1038: /**************************************************************************\
1039: *
1040: * This validity check is here for demonstration purposes. It's not likely a
1041: * real app would need to check the validity of this returned SD. The
1042: * validity check APIs are more intended to check validity after app code
1043: * has manipulated the structure and is about to hand it back to the system
1044: *
1045: \**************************************************************************/
1046:
1047: if (!IsValidSecurityDescriptor(psdProcessSD))
1048: { PERR("IsValidSecurityDescriptor said bad SD");
1049: return(FALSE);
1050: }
1051:
1052: if (!GetSecurityDescriptorOwner
1053: (psdProcessSD,
1054: (PSID *)&psidProcessOwnerSIDTemp,
1055: (LPBOOL)&bOwnerDefaulted))
1.1.1.2 ! root 1056: { PERR("GetSecurityDescriptorOwner of current process");
! 1057: return(FALSE);
! 1058: }
1.1 root 1059:
1060: /**************************************************************************\
1061: *
1062: * This validity check is here for demonstration pruposes. It's not likely a
1063: * real app would need to check the validity of this returned SID. The
1064: * validity check APIs are more intended to check validity after app code
1065: * has manipulated the structure and is about to hand it back to the system
1066: *
1067: \**************************************************************************/
1068:
1069: if (!IsValidSid(psidProcessOwnerSIDTemp))
1.1.1.2 ! root 1070: { PERR("IsValidSid said bad process SID!");
! 1071: return(FALSE);
! 1072: }
1.1 root 1073:
1074: /**************************************************************************\
1075: *
1076: * On the other hand, we are about to call GetLengthSid on the returned SID,
1077: * and calling GetLengthSid with an invalid SID is a bad idea, since then
1078: * GetLengthSid's result is undefined, and an undefined result is hard to
1.1.1.2 ! root 1079: * handle cleanly. So, even in a real app, the above check on SID validity
! 1080: * is a good idea to ensure the result GetLengthSid returns is valid
1.1 root 1081: *
1.1.1.2 ! root 1082: * It should be clear that the reason why the CopySid below is needed is that
! 1083: * in the current routine the SID of the current process is on the stack
! 1084: * (in the SD structure), so we have to copy the SID to static storage
! 1085: * before the current routine returns
1.1 root 1086: *
1087: \**************************************************************************/
1088:
1.1.1.2 ! root 1089: { DWORD dwSIDLengthNeeded;
! 1090:
! 1091: dwSIDLengthNeeded = GetLengthSid(psidProcessOwnerSIDTemp);
1.1 root 1092:
1.1.1.2 ! root 1093: psidProcessOwnerSID = malloc(dwSIDLengthNeeded);
! 1094:
! 1095: if (NULL == psidProcessOwnerSID)
! 1096: PERR("GetProcessSid - ran out of heap space");
! 1097:
! 1098: if (!CopySid(dwSIDLengthNeeded,
! 1099: psidProcessOwnerSID,
! 1100: psidProcessOwnerSIDTemp))
1.1 root 1101: { PERR("CopySid");
1102: return(FALSE);
1103: }
1.1.1.2 ! root 1104: }
! 1105:
1.1 root 1106:
1107: /**************************************************************************\
1108: *
1109: * This validity check is here for demonstration pruposes only (see above).
1110: *
1111: \**************************************************************************/
1112:
1113: if (!IsValidSid(psidProcessOwnerSID))
1.1.1.2 ! root 1114: { PERR("IsValidSid said bad process SID!");
! 1115: return(FALSE);
! 1116: }
1.1 root 1117: }
1118:
1119: /****************************************************************************\
1120: *
1121: * FUNCTION: CrackArgs
1122: *
1123: \****************************************************************************/
1124:
1125: BOOL CrackArgs(UINT argc, char *argv[])
1126: {
1127: char *p;
1128:
1129: /**************************************************************************\
1130: *
1131: * There must be three arguments
1132: *
1133: \**************************************************************************/
1134:
1135: if (argc != 4)
1136: { DisplayHelp();
1137: return(FALSE);
1138: }
1139:
1140: p=argv[1];
1141:
1142: /**************************************************************************\
1143: *
1144: * The switch argument must be 2-5 chars long
1145: *
1146: \**************************************************************************/
1147:
1148: if ((strlen(p) < 2) || (strlen(p) > 5))
1149: { DisplayHelp();
1150: return(FALSE);
1151: }
1152:
1153: /**************************************************************************\
1154: *
1155: * The first char in the switch argument must be /
1156: *
1157: \**************************************************************************/
1158:
1159: if ('/' != *p)
1160: { DisplayHelp();
1161: return(FALSE);
1162: }
1163:
1164: /**************************************************************************\
1165: *
1166: * Chars 2-5 of the switch argument must be O or A or R or C
1167: *
1168: \**************************************************************************/
1169:
1170: for (p=p+1; *p; p++)
1171: switch (*p)
1172: { case 'o':
1173: case 'O':
1174: bTakeOwnership = TRUE;
1175: break;
1176: case 'a':
1177: case 'A':
1178: bEditACLs = TRUE;
1179: break;
1180: case 'r':
1181: case 'R':
1182: bRecurse = TRUE;
1183: break;
1184: case 'c':
1185: case 'C':
1186: bJustCount = TRUE;
1187: break;
1188: default :
1189: DisplayHelp();
1190: return(FALSE);
1191: }
1192:
1193: /**************************************************************************\
1194: *
1195: * Have to say one of O or A
1196: *
1197: \**************************************************************************/
1198:
1199: if (!(bTakeOwnership || bEditACLs))
1200: { DisplayHelp();
1201: return(FALSE);
1202: }
1203:
1204: return(TRUE);
1205: }
1206:
1207: /****************************************************************************\
1208: *
1209: * FUNCTION: DisplayHelp
1210: *
1211: \****************************************************************************/
1212:
1213: VOID DisplayHelp(VOID)
1214: {
1215: printf("\nTo run type SIDCLEAN and 3 parameters. Syntax:");
1216: printf("\n SIDCLEAN /roah dirspec filepattern");
1217: printf("\n /r Recursively process subdirectories");
1218: printf("\n /o For any files matching filepattern: Take ownership if");
1219: printf("\n file currently owned by any deleted SID");
1220: printf("\n /a For any files matching filepattern: Edit ACL, deleting");
1221: printf("\n ACEs associated with any deleted SID");
1222: printf("\n /c Overrides /o and /a, causes counts of /a or /o actions that");
1223: printf("\n would take place if /c not used. Counts always displayed");
1224: printf("\n /h Override other switch values, just display this message\n");
1225: printf("\n . and .. syntax allowed in dirspec");
1226: printf("\n * and ? wildcards allowed in filepattern");
1227: printf("\n Switch letters can be in any order, upper or lower case");
1228: printf("\nExamples:");
1229: printf("\n SIDCLEAN /o . *.* Take ownership of all files (but not subdirs) in ");
1230: printf("\n current dir that are owned by any deleted SID");
1231: printf("\n SIDCLEAN /a . *.* For any file in current dir (but not subdirs), delete");
1232: printf("\n any ACL info that is associated with any deleted SID");
1233: printf("\n SIDCLEAN /ro . *.* Same as first example, but also recursively process");
1234: printf("\n subdirectories");
1235: printf("\n SIDCLEAN /ar . *.* Same as second example, but also recursively process");
1236: printf("\n subdirectories");
1237: printf("\n SIDCLEAN /O \\ *.* Same as first example, but process files in root");
1238: printf("\n of current drive");
1239: printf("\n SIDCLEAN /oC .. *.* Same as first example, but looks at files in dir");
1240: printf("\n containing current dir, processes nothing, just counts");
1241: printf("\n SIDCLEAN /A d:\\ *.* Same as second example, but process files in root");
1242: printf("\n of D: drive");
1243: printf("\n SIDCLEAN Displays this message");
1244: printf("\n SIDCLEAN /h Displays this message (so do ? -? /? -h -H /H)\n");
1245: printf("\nThis utility must be run while logged on as Administrator\n");
1246:
1247: return;
1248: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.