SIDCLEAN.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:58k
源码类别:

Windows编程

开发平台:

Visual C++

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