home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / security / winnt / sidcln / sidclean.c < prev    next >
Encoding:
Text File  |  1997-10-05  |  58.1 KB  |  1,574 lines

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