FLOPLOCK.C
/****************************************************************************\ 
*  INCLUDES, DEFINES 
\****************************************************************************/ 
#define STRICT 
#include <windows.h> 
#include <stdio.h> 
#include <stdlib.h> 
 
#define PERR(api) printf("\n%s: Error %d from %s on line %d",  \ 
    __FILE__, GetLastError(), api, __LINE__); 
#define PMSG(msg) printf("\n%s line %d: %s",  \ 
    __FILE__, __LINE__, msg); 
 
// this event is signalled when the 
//  worker thread ends 
// 
HANDLE                  hServDoneEvent = NULL; 
SERVICE_STATUS          ssStatus;       // current status of the service 
 
SERVICE_STATUS_HANDLE   sshStatusHandle; 
DWORD                   dwGlobalErr; 
DWORD                   TID = 0; 
HANDLE                  threadHandle = NULL; 
HANDLE                  pipeHandle; 
 
 
/****************************************************************************\ 
* FUNCTION PROTOTYPES 
\****************************************************************************/ 
 
VOID    WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv); 
VOID    WINAPI service_ctrl(DWORD dwCtrlCode); 
BOOL    ReportStatusToSCMgr(DWORD dwCurrentState, 
                            DWORD dwWin32ExitCode, 
                            DWORD dwCheckPoint, 
                            DWORD dwWaitHint); 
VOID    die(char *reason); 
VOID    worker_thread(VOID *notUsed); 
VOID    StopSimpleService(LPTSTR lpszMsg); 
BOOL    WriteSD_ToA_File(PSECURITY_DESCRIPTOR psdAbsoluteSD, LPTSTR lpszFileName); 
 
 
/****************************************************************************\ 
* GLOBAL VARIABLES AND TYPEDEFS 
\****************************************************************************/ 
 
#define                               SZ_SD_BUF   100 
#define                               SZ_SID_BUF   75 
#define                               SZ_ACL_BUF  150 
 
UCHAR                ucAbsSDBuf      [SZ_SD_BUF]  = ""; 
UCHAR                ucEvrSDBuf      [SZ_SD_BUF]  = ""; 
UCHAR                ucSIDBuf        [SZ_SID_BUF] = ""; 
UCHAR                ucPwrUsrsSIDBuf [SZ_SID_BUF] = ""; 
UCHAR                ucACLBuf        [SZ_ACL_BUF] = ""; 
 
DWORD                dwSID          = SZ_SID_BUF; 
DWORD                dwDACL         = SZ_ACL_BUF; 
BOOL                 bFloppiesAreLocked; 
 
PSECURITY_DESCRIPTOR psdAbsoluteSD      = (PSECURITY_DESCRIPTOR)&ucAbsSDBuf; 
PSECURITY_DESCRIPTOR psdEveryoneSD      = (PSECURITY_DESCRIPTOR)&ucEvrSDBuf; 
PSID                 psidAdministrators = (PSID)&ucSIDBuf; 
PSID                 psidPowerUsers     = (PSID)&ucPwrUsrsSIDBuf; 
PACL                 pNewDACL           = (PACL)&ucACLBuf; 
 
 
 
//  main() -- 
//      all main does is call StartServiceCtrlDispatcher 
//      to register the main service thread.  When the 
//      API returns, the service has stopped, so exit. 
// 
int 
main() 
{ 
 
    SERVICE_TABLE_ENTRY dispatchTable[] = { 
        { TEXT("SimpleService"), (LPSERVICE_MAIN_FUNCTION)service_main }, 
        { NULL, NULL } 
    }; 
 
  // check if Win32s, if so, display notice and terminate 
        if( GetVersion() & 0x80000000 ) 
        { 
          MessageBox( NULL, 
             "This application cannot run on Windows 3.1 or Windows 95.\n" 
             "This application will now terminate.", 
             "SD_FLPPY", 
             MB_OK | MB_ICONSTOP | MB_SETFOREGROUND ); 
          return( 1 ); 
        } 
 
    #define FILE_TO_REDIRECT_STDOUT_TO "c:\\floplock.out" 
//  freopen(FILE_TO_REDIRECT_STDOUT_TO,"w+",stdout); 
 
    if (!StartServiceCtrlDispatcher(dispatchTable)) { 
        StopSimpleService("StartServiceCtrlDispatcher failed."); 
    } 
} 
 
 
 
//  service_main() -- 
//      this function takes care of actually starting the service, 
//      informing the service controller at each step along the way. 
//      After launching the worker thread, it waits on the event 
//      that the worker thread will signal at its termination. 
// 
VOID WINAPI 
service_main(DWORD dwArgc, LPTSTR *lpszArgv) 
{ 
    DWORD                   dwWait; 
    SECURITY_ATTRIBUTES     sa; 
 
    // register our service control handler: 
    // 
    sshStatusHandle = RegisterServiceCtrlHandler( 
                                    TEXT("SimpleService"), 
                                    service_ctrl); 
 
    if (!sshStatusHandle) 
        goto cleanup; 
 
    // SERVICE_STATUS members that don't change in example 
    // 
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
    ssStatus.dwServiceSpecificExitCode = 0; 
 
 
    // report the status to Service Control Manager. 
    // 
    if (!ReportStatusToSCMgr( 
        SERVICE_START_PENDING, // service state 
        NO_ERROR,              // exit code 
        1,                     // checkpoint 
        3000))                 // wait hint 
        goto cleanup; 
 
    // create the event object. The control handler function signals 
    // this event when it receives the "stop" control code. 
    // 
    hServDoneEvent = CreateEvent( 
        NULL,    // no security attributes 
        TRUE,    // manual reset event 
        FALSE,   // not-signalled 
        NULL);   // no name 
 
    if (hServDoneEvent == (HANDLE)NULL) 
        goto cleanup; 
 
    // report the status to the service control manager. 
    // 
    if (!ReportStatusToSCMgr( 
        SERVICE_START_PENDING, // service state 
        NO_ERROR,              // exit code 
        2,                     // checkpoint 
        3000))                 // wait hint 
        goto cleanup; 
 
    // Create a security descriptor that allows only local Administrators 
    //   to do anything with the pipe.  Since Domain administrators are 
    //   normally also local Administrators, this will serve most needs 
 
    /************************************************************************\ 
    * 
    * Build SIDs of local Administrators and Power Users.  Note that the Power 
    *   Users group is defined on Windows NT machines, but not on Advanced 
    *   Server machines.  This means that we will on Advanced Server machines 
    *   have a DACL that has an ACE (for Power Users) that will never be used 
    * 
    \************************************************************************/ 
 
    { 
      SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY; 
 
      InitializeSid(        psidAdministrators, &siaNtAuthority, 2 ); 
      InitializeSid(        psidPowerUsers,     &siaNtAuthority, 2 ); 
 
      *(GetSidSubAuthority( psidAdministrators, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; 
      *(GetSidSubAuthority( psidPowerUsers,     0 )) = SECURITY_BUILTIN_DOMAIN_RID; 
 
      *(GetSidSubAuthority( psidAdministrators, 1 )) = DOMAIN_ALIAS_RID_ADMINS; 
      *(GetSidSubAuthority( psidPowerUsers,     1 )) = DOMAIN_ALIAS_RID_POWER_USERS; 
    } 
 
 
    /************************************************************************\ 
    * 
    * Initialize new DACL 
    * 
    \************************************************************************/ 
 
    if (!InitializeAcl(pNewDACL, 
                       dwDACL, 
                       ACL_REVISION)) 
    { StopSimpleService("InitializeAcl"); 
    } 
 
    /************************************************************************\ 
    * 
    * Allow All access for local Administrators only 
    * 
    \************************************************************************/ 
 
    if (!AddAccessAllowedAce(pNewDACL, 
                             ACL_REVISION, 
                             FILE_ALL_ACCESS, 
                             psidAdministrators)) 
    { StopSimpleService("AddAccessAllowedAce"); 
    } 
 
    /************************************************************************\ 
    * 
    * If we unlock the floppies when the service stops, then for the sake of 
    *   consistency, we have to also allow Power Users on the Admin-only DACL, 
    *   since Power Users can stop services.  It would be inconsistent to try 
    *   to lock Power Users away from their floppies if Power Users could get 
    *   to the floppies simply by stopping the service 
    * 
    * It's still OK to use the same DACL for the pipe as for the floppies, 
    *   that is, it's OK to let Power Users on the DACL for the pipe too.  The 
    *   reason is that it is not generally (and certainly not by default) the 
    *   case that an account is a member of Power Users on more than their own 
    *   machines.  So, putting Power Users on the pipe let's Power Users admin 
    *   the floppies via the pipe only on the machines on which they are 
    *   actually Power Users, and again, on those machines they can stop the 
    *   floppy-locking service as well 
    * 
    \************************************************************************/ 
 
    #define UNLOCK_AT_SERVICE_STOP (0==0) 
    if     (UNLOCK_AT_SERVICE_STOP) 
    { if (!AddAccessAllowedAce(pNewDACL, 
                               ACL_REVISION, 
                               FILE_ALL_ACCESS, 
                               psidPowerUsers)) 
      { StopSimpleService("AddAccessAllowedAce"); 
      } 
    } 
 
    /************************************************************************\ 
    * 
    * Build SD in absolute format - first the Admins-only then the Everyone SD 
    * 
    \************************************************************************/ 
 
    if (!InitializeSecurityDescriptor(psdAbsoluteSD, 
                                      SECURITY_DESCRIPTOR_REVISION)) 
    { StopSimpleService("InitializeSecurityDescriptor"); 
    } 
 
    if (!InitializeSecurityDescriptor(psdEveryoneSD, 
                                      SECURITY_DESCRIPTOR_REVISION)) 
    { StopSimpleService("InitializeSecurityDescriptor"); 
    } 
 
    /************************************************************************\ 
    * 
    * Set DACL into SD - first the Admins-only then the Everyone SD 
    * 
    \************************************************************************/ 
 
    if (!SetSecurityDescriptorDacl(psdAbsoluteSD, 
                                   TRUE,      // fDaclPresent flag 
                                   pNewDACL, 
                                   FALSE))    // not a default DACL 
    { StopSimpleService("SetSecurityDescriptorDacl"); 
    } 
 
    if (!SetSecurityDescriptorDacl(psdEveryoneSD, 
                                   TRUE,      // fDaclPresent flag 
                                   (PACL)NULL, 
                                   FALSE))    // not a default DACL 
    { StopSimpleService("SetSecurityDescriptorDacl"); 
    } 
 
    /************************************************************************\ 
    * 
    * Check to see that SD is valid before attempting to write it to the file 
    * 
    \************************************************************************/ 
 
    if (!IsValidSecurityDescriptor(psdAbsoluteSD)) 
    { StopSimpleService("IsValidSecurityDescriptor"); 
    } 
 
    sa.nLength = sizeof(sa); 
    sa.lpSecurityDescriptor = psdAbsoluteSD; 
    sa.bInheritHandle = TRUE;  // why not... we spawn no processes 
 
    // open our named pipe... 
    // 
    pipeHandle = CreateNamedPipe( 
                    "\\\\.\\pipe\\sd_flppy",  // name of pipe 
                    PIPE_ACCESS_DUPLEX,     // pipe open mode 
                    PIPE_TYPE_MESSAGE | 
                    PIPE_READMODE_MESSAGE | 
                    PIPE_WAIT,              // pipe IO type 
                    1,                      // number of instances 
                    0,                      // size of outbuf (0 == allocate as necessary) 
                    0,                      // size of inbuf 
                    1000,                   // default time-out value 
                    &sa);                   // security attributes 
 
    if (!pipeHandle) { 
        StopSimpleService("CreateNamedPipe"); 
        return; 
    } 
 
    // Set the same DACL onto the floppies 
    // 
 
    /************************************************************************\ 
    * 
    * Write SD to file system - first for A: then B: 
    * 
    \************************************************************************/ 
 
    if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\A:")) 
    { StopSimpleService("Write of DACL to A: failed"); 
    } 
 
    if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\B:")) 
    { StopSimpleService("Write of DACL to B: failed"); 
    } 
 
    bFloppiesAreLocked = TRUE; 
 
    /************************************************************************\ 
    * 
    * only works for CDROM drives if you revoke the SeChangeNotify privilege 
    * from Everyone in user manager 
    * 
    \************************************************************************/ 
  /* 
    if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\E:")) 
    { StopSimpleService("Write of DACL to E: failed"); 
    } 
  */ 
    /************************************************************************\ 
    * 
    * Works for COM ports as well - commented out as this samples is floppy only 
    * 
    \************************************************************************/ 
  /* 
    if (!WriteSD_ToA_File(psdAbsoluteSD,"COM1:")) 
    { StopSimpleService("Write of DACL to COM1: failed"); 
    } 
  */ 
 
    // start the thread that performs the work of the service. 
    // 
    threadHandle = CreateThread( 
                    NULL,       // security attributes 
                    0,          // stack size (0 means inherit parent's stack size) 
                    (LPTHREAD_START_ROUTINE)worker_thread, 
                    NULL,       // argument to thread 
                    0,          // thread creation flags 
                    &TID);      // pointer to thread ID 
 
    if (!threadHandle) 
        goto cleanup; 
 
    // report the status to the service control manager. 
    // 
    if (!ReportStatusToSCMgr( 
        SERVICE_RUNNING, // service state 
        NO_ERROR,        // exit code 
        0,               // checkpoint 
        0))              // wait hint 
        goto cleanup; 
 
    // wait indefinitely until hServDoneEvent is signaled. 
    // 
    dwWait = WaitForSingleObject( 
        hServDoneEvent,  // event object 
        INFINITE);       // wait indefinitely 
 
cleanup: 
 
    if (hServDoneEvent != NULL) 
        CloseHandle(hServDoneEvent); 
 
 
    // try to report the stopped status to the service control manager. 
    // 
    if (sshStatusHandle != 0) 
        (VOID)ReportStatusToSCMgr( 
                            SERVICE_STOPPED, 
                            dwGlobalErr, 
                            0, 
                            0); 
 
    // When SERVICE MAIN FUNCTION returns in a single service 
    // process, the StartServiceCtrlDispatcher function in 
    // the main thread returns, terminating the process. 
    // 
    return; 
} 
 
 
 
//  service_ctrl() -- 
//      this function is called by the Service Controller whenever 
//      someone calls ControlService in reference to our service. 
// 
VOID WINAPI 
service_ctrl(DWORD dwCtrlCode) 
{ 
    DWORD  dwState = SERVICE_RUNNING; 
 
    // Handle the requested control code. 
    // 
    switch(dwCtrlCode) { 
 
        // Pause the service if it is running. 
        // 
        case SERVICE_CONTROL_PAUSE: 
 
            if (ssStatus.dwCurrentState == SERVICE_RUNNING) { 
                SuspendThread(threadHandle); 
                dwState = SERVICE_PAUSED; 
            } 
            break; 
 
        // Resume the paused service. 
        // 
        case SERVICE_CONTROL_CONTINUE: 
 
            if (ssStatus.dwCurrentState == SERVICE_PAUSED) { 
                ResumeThread(threadHandle); 
                dwState = SERVICE_RUNNING; 
            } 
            break; 
 
        // Stop the service. 
        // 
        case SERVICE_CONTROL_STOP: 
 
            dwState = SERVICE_STOP_PENDING; 
 
            // Report the status, specifying the checkpoint and waithint, 
            //  before setting the termination event. 
            // 
            ReportStatusToSCMgr( 
                    SERVICE_STOP_PENDING, // current state 
                    NO_ERROR,             // exit code 
                    1,                    // checkpoint 
                    3000);                // waithint 
 
            if     (UNLOCK_AT_SERVICE_STOP) 
            { if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\A:")) 
              { StopSimpleService("Unlock of A: failed, see log file"); 
              } 
              if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\B:")) 
              { StopSimpleService("Unlock of B: failed, see log file"); 
              } 
 
              bFloppiesAreLocked = FALSE; 
            } 
 
            SetEvent(hServDoneEvent); 
            return; 
 
        // Update the service status. 
        // 
        case SERVICE_CONTROL_INTERROGATE: 
            break; 
 
        // invalid control code 
        // 
        default: 
            break; 
 
    } 
 
    // send a status response. 
    // 
    ReportStatusToSCMgr(dwState, NO_ERROR, 0, 0); 
} 
 
 
 
//  worker_thread() -- 
//      this function does the actual nuts and bolts work that 
//      the service requires.  It will also Pause or Stop when 
//      asked by the service_ctrl function. 
// 
VOID 
worker_thread(VOID *notUsed) 
{ 
    char                 inbuf[180]; 
    char                 outbuf[180]; 
    BOOL                 ret; 
    DWORD                bytesRead; 
    DWORD                bytesWritten; 
    DWORD                dwLen; 
 
    // okay, our pipe has been created, let's enter the simple 
    //  processing loop... 
    // 
    while (1) { 
 
        // wait for a connection... 
        // 
        ConnectNamedPipe(pipeHandle, NULL); 
 
        // grab whatever's coming through the pipe... 
        // 
        ret = ReadFile( 
                    pipeHandle,     // file to read from 
                    inbuf,          // address of input buffer 
                    sizeof(inbuf),  // number of bytes to read 
                    &bytesRead,     // number of bytes read 
                    NULL);          // overlapped stuff, not needed 
 
        if (!ret) 
            // pipe's broken... go back and reconnect 
            // 
            continue; 
 
        switch (inbuf[0]) 
        { case 'U': 
            dwLen = sprintf(outbuf,"Floppies were unlocked"); 
 
            if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\A:")) 
            { dwLen += sprintf(outbuf+dwLen,", unlock of A: failed, see log file"); 
            } 
            if (!WriteSD_ToA_File(psdEveryoneSD,"\\\\.\\B:")) 
            { dwLen += sprintf(outbuf+dwLen,", unlock of B: failed, see log file"); 
            } 
 
            bFloppiesAreLocked = FALSE; 
            break; 
 
          case 'L': 
            dwLen = sprintf(outbuf,"Floppies were locked"); 
 
            if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\A:")) 
            { dwLen += sprintf(outbuf+dwLen,", lock of A: failed, see log file"); 
            } 
            if (!WriteSD_ToA_File(psdAbsoluteSD,"\\\\.\\B:")) 
            { dwLen += sprintf(outbuf+dwLen,", lock of B: failed, see log file"); 
            } 
 
            bFloppiesAreLocked = TRUE; 
            break; 
 
          case 'Q': 
            if (bFloppiesAreLocked) 
            { sprintf(outbuf,"Floppy status is: Locked"); 
            } 
            else 
            { sprintf(outbuf,"Floppy status is: Unlocked"); 
            } 
            break; 
 
          default : 
            sprintf(outbuf,"Bad operation passed in"); 
        } 
 
        // send it back out... 
        // 
        ret = WriteFile( 
                    pipeHandle,     // file to write to 
                    outbuf,         // address of output buffer 
                    sizeof(outbuf), // number of bytes to write 
                    &bytesWritten,  // number of bytes written 
                    NULL);          // overlapped stuff, not needed 
 
        if (!ret) 
            // pipe's broken... go back and reconnect 
            // 
            continue; 
 
        // drop the connection... 
        // 
        DisconnectNamedPipe(pipeHandle); 
    } 
} 
 
 
 
// utility functions... 
 
 
 
// ReportStatusToSCMgr() -- 
//      This function is called by the ServMainFunc() and 
//      ServCtrlHandler() functions to update the service's status 
//      to the service control manager. 
// 
BOOL 
ReportStatusToSCMgr(DWORD dwCurrentState, 
                    DWORD dwWin32ExitCode, 
                    DWORD dwCheckPoint, 
                    DWORD dwWaitHint) 
{ 
    BOOL fResult; 
 
    // Disable control requests until the service is started. 
    // 
    if (dwCurrentState == SERVICE_START_PENDING) 
        ssStatus.dwControlsAccepted = 0; 
    else 
        ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | 
            SERVICE_ACCEPT_PAUSE_CONTINUE; 
 
    // These SERVICE_STATUS members are set from parameters. 
    // 
    ssStatus.dwCurrentState = dwCurrentState; 
    ssStatus.dwWin32ExitCode = dwWin32ExitCode; 
    ssStatus.dwCheckPoint = dwCheckPoint; 
 
    ssStatus.dwWaitHint = dwWaitHint; 
 
    // Report the status of the service to the service control manager. 
    // 
    if (!(fResult = SetServiceStatus( 
                sshStatusHandle,    // service reference handle 
                &ssStatus))) {      // SERVICE_STATUS structure 
 
        // If an error occurs, stop the service. 
        // 
        StopSimpleService("SetServiceStatus"); 
    } 
    return fResult; 
} 
 
 
 
// The StopSimpleService function can be used by any thread to report an 
//  error, or stop the service. 
// 
VOID 
StopSimpleService(LPTSTR lpszMsg) 
{ 
    CHAR    chMsg[256]; 
    HANDLE  hEventSource; 
    LPTSTR  lpszStrings[2]; 
 
    dwGlobalErr = GetLastError(); 
 
    // Use event logging to log the error. 
    // 
    hEventSource = RegisterEventSource(NULL, 
                            TEXT("SimpleService")); 
 
    sprintf(chMsg, "SimpleService error: %d", dwGlobalErr); 
    lpszStrings[0] = chMsg; 
    lpszStrings[1] = lpszMsg; 
 
    if (hEventSource != NULL) { 
        ReportEvent(hEventSource, // handle of event source 
            EVENTLOG_ERROR_TYPE,  // event type 
            0,                    // event category 
            0,                    // event ID 
            NULL,                 // current user's SID 
            2,                    // strings in lpszStrings 
            0,                    // no bytes of raw data 
            lpszStrings,          // array of error strings 
            NULL);                // no raw data 
 
        (VOID) DeregisterEventSource(hEventSource); 
    } 
 
    // Set a termination event to stop SERVICE MAIN FUNCTION. 
    // 
    SetEvent(hServDoneEvent); 
} 
  
/****************************************************************************\ 
* 
* FUNCTION: WriteSD_ToA_File 
* 
\****************************************************************************/ 
 
BOOL WriteSD_ToA_File(PSECURITY_DESCRIPTOR psdAbsoluteSD, LPTSTR lpszFileName) 
{ 
  DWORD dwErrorMode; 
  BOOL  bStatus; 
 
  /**************************************************************************\ 
  * 
  * SetErrorMode so we don't get the error due to no floppy disk in the floppy 
  *   drive 
  * 
  \**************************************************************************/ 
 
  dwErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); 
 
  /**************************************************************************\ 
  * 
  * Write SD to file system 
  * 
  \**************************************************************************/ 
 
  bStatus = SetFileSecurity(lpszFileName, 
                            (SECURITY_INFORMATION)(DACL_SECURITY_INFORMATION), 
                            psdAbsoluteSD); 
 
  /**************************************************************************\ 
  * 
  * SetErrorMode back to its previous value 
  * 
  \**************************************************************************/ 
 
  SetErrorMode(dwErrorMode); 
 
  if (!bStatus) 
  { if (ERROR_FILE_NOT_FOUND == GetLastError()) 
    { printf("\nAttempted to lock %s, but it was not found",lpszFileName); 
    } 
    else 
    { PERR("SetFileSecurity"); 
      return(FALSE); 
    } 
  } 
 
  return(TRUE); 
}