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

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

unix.superglobalmegacorp.com

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