Annotation of mstools/samples/sidcln/sidclean.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.