/* $Id$ */ /* ** Copyright (C) 2002 Chris Reid ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * win32_service.c v1.0 - 20 February 2002 * * Purpose: Lets Snort register as a Win32 Service. This includes both * an installation an uninstallation aspect. * * Author: Chris Reid (chris.reid@codecraftconsulants.com) * * Notes: The Snort command-line arguments need to be * saved into the registry when the snort service is * being installed. They are stored in: * HKLM \ SOFTWARE \ Snort * * Usage: * snort.exe /SERVICE /INSTALL [regular command-line params] * * snort.exe /SERVICE /UNINSTALL * * snort.exe /SERVICE /SHOW * * References * Microsoft has full docs on programming Win32 Services in their * MSDN (Microsoft Developer Network) library. * http://msdn.microsoft.com/ */ #ifdef ENABLE_WIN32_SERVICE /* * Enable the next line to automatically assign a description to the Service. * According to the Microsoft documentation, the call to ChangeServiceConfig2() * which sets the description is only available on Windows 2000 or newer. * * #define SET_SERVICE_DESCRIPTION */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include /* for Service stuff */ #include /* for printf(), etc */ #include /* for _getcwd() */ #include "snort.h" #include "debug.h" #include "util.h" static LPTSTR g_lpszServiceName = "SnortSvc"; static LPTSTR g_lpszServiceDisplayName = "Snort"; static LPTSTR g_lpszServiceDescription = "The Open Source Network Intrusion Detection System"; static LPTSTR g_lpszRegistryKey = "SOFTWARE\\Snort"; static LPTSTR g_lpszRegistryCmdFormat = "CmdLineParam_%03d"; static LPTSTR g_lpszRegistryCountFormat= "CmdLineParamCount"; static SERVICE_STATUS g_SnortServiceStatus; static SERVICE_STATUS_HANDLE g_SnortServiceStatusHandle; #define MAX_REGISTRY_KEY_LENGTH 255 #define MAX_REGISTRY_DATA_LENGTH 1000 static VOID SvcDebugOut(LPSTR String, DWORD Status); static VOID SvcFormatMessage(LPSTR szString, int iCount); static VOID ReadServiceCommandLineParams( int * piArgCounter, char** * pargvDynamic ); static VOID WINAPI SnortServiceStart (DWORD argc, LPTSTR *argv); static VOID WINAPI SnortServiceCtrlHandler (DWORD opcode); static DWORD SnortServiceInitialization (DWORD argc, LPTSTR *argv, DWORD *specificError); static VOID InstallSnortService(int argc, char* argv[]); static VOID UninstallSnortService(); static VOID ShowSnortServiceParams(); /******************************************************************************* * (This documentation was taken from Microsoft's own doc's on how to create * a Win32 Service.) * * Writing a Service Program's main Function * ----------------------------------------------------------------------------- * * The main function of a service program calls the StartServiceCtrlDispatcher * function to connect to the SCM and start the control dispatcher thread. The * dispatcher thread loops, waiting for incoming control requests for the * services specified in the dispatch table. This thread does not return until * there is an error or all of the services in the process have terminated. When * all services in a process have terminated, the SCM sends a control request * to the dispatcher thread telling it to shut down. The thread can then return * from the StartServiceCtrlDispatcher call and the process can terminate. * * The following example is a service process that supports only one service. It * takes two parameters: a string that can contain one formatted output * character and a numeric value to be used as the formatted character. The * SvcDebugOut function prints informational messages and errors to the debugger. * For information on writing the SnortServiceStart and SnortServiceInitialization * functions, see Writing a ServiceMain Function. For information on writing the * SnortServiceCtrlHandler function, see Writing a Control Handler Function. *******************************************************************************/ /* this is the entry point which is called from main() */ int SnortServiceMain(int argc, char* argv[]) { int i; /* SERVICE_TABLE_ENTRY steDispatchTable[] = { { g_lpszServiceName, SnortServiceStart }, { NULL, NULL } }; */ SERVICE_TABLE_ENTRY steDispatchTable[2]; steDispatchTable[0].lpServiceName = g_lpszServiceName; steDispatchTable[0].lpServiceProc = SnortServiceStart; steDispatchTable[1].lpServiceName = NULL; steDispatchTable[1].lpServiceProc = NULL; for( i=1; i0) { memset(szString, 0, iCount); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &lpMsgBuf, 0, NULL ); strncpy(szString, (LPCTSTR) lpMsgBuf, iCount-1); szString[iCount-1]=0; /* Free the buffer. */ LocalFree( lpMsgBuf ); lpMsgBuf = NULL; } } VOID ReadServiceCommandLineParams( int * piArgCounter, char** * pargvDynamic ) { HKEY hkSnort = NULL; long lRegRC = 0; DWORD dwType; DWORD dwDataSize; BYTE byData[MAX_REGISTRY_DATA_LENGTH]; int i; /********** * Read the registry entries for Snort command line parameters **********/ lRegRC = RegOpenKeyEx( HKEY_LOCAL_MACHINE, /* handle to open key */ g_lpszRegistryKey, /* subkey name */ 0, /* reserved (must be zero) */ KEY_READ, /* desired security access */ &hkSnort /* key handle */ ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to open Snort registry entry. " " Perhaps Snort has not been installed as a service." " %s", szMsg); } memset(byData, 0, sizeof(byData)); dwDataSize = sizeof(byData); lRegRC = RegQueryValueEx( hkSnort, /* handle to key */ g_lpszRegistryCountFormat, /* value name */ NULL, /* reserved */ &dwType, /* type buffer */ byData, /* data buffer */ &dwDataSize /* size of data buffer */ ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to read Snort registry entry '%s'." " Perhaps Snort has not been installed as a service." " %s", g_lpszRegistryCountFormat, szMsg); } (*piArgCounter) = * ((int*)&byData); (*pargvDynamic) = SnortAlloc( ((*piArgCounter) + 2) * sizeof(char *) ); (*pargvDynamic)[0] = SnortStrdup(g_lpszServiceName); DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Preparing to use the following command-line arguments:\n");); for( i=1; i<=(*piArgCounter); i++ ) { TCHAR szName[MAX_REGISTRY_KEY_LENGTH]; sprintf(szName, g_lpszRegistryCmdFormat, i); memset(byData, 0, sizeof(byData)); dwDataSize = sizeof(byData); lRegRC = RegQueryValueEx( hkSnort, /* handle to key */ szName, /* value name */ NULL, /* reserved */ &dwType, /* type buffer */ byData, /* data buffer */ &dwDataSize /* size of data buffer */ ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to read Snort registry entry '%s'." " Perhaps Snort has not been installed as a service." " %s", szName, szMsg); } (*pargvDynamic)[i] = _strdup( (char*) byData ); DEBUG_WRAP(DebugMessage(DEBUG_INIT, " %s\n", (*pargvDynamic)[i]);); } lRegRC = RegCloseKey( hkSnort ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to close Snort registry entry." " Perhaps Snort has not been installed as a service." " %s", szMsg); } hkSnort = NULL; } /******************************************************************************* * (This documentation was taken from Microsoft's own doc's on how to create * a Win32 Service.) * * Writing a ServiceMain Function * ----------------------------------------------------------------------------- * * The SnortServiceStart function in the following example is the entry point for * the service. SnortServiceStart has access to the command-line arguments, in the * way that the main function of a console application does. The first parameter * contains the number of arguments being passed to the service. There will * always be at least one argument. The second parameter is a pointer to an * array of string pointers. The first item in the array always points to the * service name. * * The SnortServiceStart function first fills in the SERVICE_STATUS structure * including the control codes that it accepts. Although this service accepts * SERVICE_CONTROL_PAUSE and SERVICE_CONTROL_CONTINUE, it does nothing * significant when told to pause. The flags SERVICE_ACCEPT_PAUSE_CONTINUE was * included for illustration purposes only; if pausing does not add value to * your service, do not support it. * * The SnortServiceStart function then calls the RegisterServiceCtrlHandler * function to register SnortService as the service's Handler function and begin * initialization. The following sample initialization function, * SnortServiceInitialization, is included for illustration purposes; it does not * perform any initialization tasks such as creating additional threads. If * your service's initialization performs tasks that are expected to take longer * than one second, your code must call the SetServiceStatus function * periodically to send out wait hints and check points indicating that progress * is being made. * * When initialization has completed successfully, the example calls * SetServiceStatus with a status of SERVICE_RUNNING and the service continues * with its work. If an error has occurred in initialization, SnortServiceStart * reports SERVICE_STOPPED with the SetServiceStatus function and returns. * * Because this sample service does not complete any real tasks, SnortServiceStart * simply returns control to the caller. However, your service should use this * thread to complete whatever tasks it was designed to do. If a service does not * need a thread to do its work (such as a service that only processes RPC * requests), its ServiceMain function should return control to the caller. It is * important for the function to return, rather than call the ExitThread * function, because returning allows for cleanup of the memory allocated for the * arguments. * * To output debugging information, SnortServiceStart calls SvcDebugOut. The source * code for SvcDebugOut is given in Writing a Service Program's main Function. *******************************************************************************/ void logmsg(char* msg) { FILE *pFile; if( (pFile=fopen("c:\\snortlog.txt", "a")) != NULL ) { if( msg != NULL ) { fprintf(pFile,"%s",msg); } else { fprintf(pFile,"%s","Message String is NULL\n"); } fclose(pFile); pFile = NULL; } } void logadapternames( char* interfacenames, char* errorbuf ) { char AdaptersName[8192]; int i; memset(AdaptersName, 0x00, sizeof(AdaptersName)); for( i=0; i MAX_REGISTRY_DATA_LENGTH ) { FatalError(" [SNORT_SERVICE] A single command line parameter cannot exceed %d characters.", MAX_REGISTRY_DATA_LENGTH); } else { char szSubkeyName[30]; dwWriteCounter++; sprintf(szSubkeyName, g_lpszRegistryCmdFormat, dwWriteCounter); lRegRC = RegSetValueEx( hkSnort, /* handle to key to set value for */ szSubkeyName, /* name of the value to set */ 0, /* reserved */ REG_SZ, /* flag for value type */ (LPBYTE) argv[iArgCounter], /* address of value data */ strlen(argv[iArgCounter]) /* size of value data */ ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to write Snort registry entry. %s", szMsg); } } } /* end for() */ lRegRC = RegSetValueEx( hkSnort, /* handle to key to set value for */ g_lpszRegistryCountFormat, /* name of the value to set */ 0, /* reserved */ REG_DWORD, /* flag for value type */ (LPBYTE) &dwWriteCounter, /* address of value data */ sizeof(dwWriteCounter) /* size of value data */ ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to write Snort registry entry. %s", szMsg); } lRegRC = RegCloseKey( hkSnort ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to close Snort registry entry. %s", szMsg); } printf("\n"); printf(" [SNORT_SERVICE] Successfully added registry keys to:\n"); printf(" \\HKEY_LOCAL_MACHINE\\%s\\\n", g_lpszRegistryKey); /********** * Add Snort to the Services database **********/ schSCManager = OpenSCManager(NULL, /* local machine */ NULL, /* defaults to SERVICES_ACTIVE_DATABASE */ SC_MANAGER_ALL_ACCESS); /* full access rights */ if (schSCManager == NULL) { DWORD dwErr = GetLastError(); LPCTSTR lpszBasicMessage = "Unable to open a connection to the Services database."; TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); switch(dwErr) { case ERROR_ACCESS_DENIED: FatalError(" [SNORT_SERVICE] %s Access is denied. %s", lpszBasicMessage, szMsg); break; case ERROR_DATABASE_DOES_NOT_EXIST: FatalError(" [SNORT_SERVICE] %s Services database does not exist. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_PARAMETER: FatalError(" [SNORT_SERVICE] %s Invalid parameter. %s", lpszBasicMessage, szMsg); break; default: FatalError(" [SNORT_SERVICE] %s Unrecognized error (%d). %s", lpszBasicMessage, dwErr, szMsg); break; } } schService = CreateService( schSCManager, /* SCManager database */ g_lpszServiceName, /* name of service */ g_lpszServiceDisplayName, /* service name to display */ SERVICE_ALL_ACCESS, /* desired access */ SERVICE_WIN32_OWN_PROCESS, /* service type */ SERVICE_DEMAND_START, /* start type */ SERVICE_ERROR_NORMAL, /* error control type */ lpszBinaryPathName, /* service's binary */ NULL, /* no load ordering group */ NULL, /* no tag identifier */ NULL, /* no dependencies */ NULL, /* LocalSystem account */ NULL); /* no password */ if (schService == NULL) { DWORD dwErr = GetLastError(); LPCTSTR lpszBasicMessage = "Error while adding the Snort service to the Services database."; TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); switch(dwErr) { case ERROR_ACCESS_DENIED: FatalError(" [SNORT_SERVICE] %s Access is denied. %s", lpszBasicMessage, szMsg); break; case ERROR_CIRCULAR_DEPENDENCY: FatalError(" [SNORT_SERVICE] %s Circular dependency. %s", lpszBasicMessage, szMsg); break; case ERROR_DUP_NAME: FatalError(" [SNORT_SERVICE] %s The display name (\"%s\") is already in use. %s", lpszBasicMessage , g_lpszServiceDisplayName , szMsg); break; case ERROR_INVALID_HANDLE: FatalError(" [SNORT_SERVICE] %s Invalid handle. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_NAME: FatalError(" [SNORT_SERVICE] %s Invalid service name. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_PARAMETER: FatalError(" [SNORT_SERVICE] %s Invalid parameter. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_SERVICE_ACCOUNT: FatalError(" [SNORT_SERVICE] %s Invalid service account. %s", lpszBasicMessage, szMsg); break; case ERROR_SERVICE_EXISTS: FatalError(" [SNORT_SERVICE] %s Service already exists. %s", lpszBasicMessage, szMsg); break; default: FatalError(" [SNORT_SERVICE] %s Unrecognized error (%d). %s", lpszBasicMessage, dwErr, szMsg); break; } } #ifdef SET_SERVICE_DESCRIPTION /* Apparently, the call to ChangeServiceConfig2() only works on Windows >= 2000 */ sdBuf.lpDescription = g_lpszServiceDescription; if( !ChangeServiceConfig2(schService, /* handle to service */ SERVICE_CONFIG_DESCRIPTION, /* change: description */ &sdBuf) ) /* value: new description */ { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); FatalError(" [SNORT_SERVICE] Unable to add a description to the Snort service. %s", szMsg); } #endif printf("\n"); printf(" [SNORT_SERVICE] Successfully added the Snort service to the Services database.\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } /******************************************************************************* * (This documentation was taken from Microsoft's own doc's on how to create * a Win32 Service.) * * Deleting a Service * ----------------------------------------------------------------------------- * * In the following example, a service configuration program uses the * OpenService function to get a handle with DELETE access to an installed * service object. The program then uses the service object handle in the * DeleteService function to remove the service from the SCM database. *******************************************************************************/ VOID UninstallSnortService() { SC_HANDLE schSCManager, schService; //HKEY hkSnort = NULL; long lRegRC = 0; printf("\n\n"); printf(" [SNORT_SERVICE] Attempting to uninstall the Snort service.\n"); /********** * Removing the registry entries for Snort command line parameters **********/ lRegRC = RegDeleteKey( HKEY_LOCAL_MACHINE, /* handle to open key */ g_lpszRegistryKey /* subkey name */ ); if( lRegRC != ERROR_SUCCESS ) { TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); printf(" [SNORT_SERVICE] Warning. Unable to remove root Snort registry entry. %s", szMsg); } printf("\n"); printf(" [SNORT_SERVICE] Successfully removed registry keys from:\n"); printf(" \\HKEY_LOCAL_MACHINE\\%s\\\n", g_lpszRegistryKey); /********** * Remove Snort from the Services database **********/ schSCManager = OpenSCManager(NULL, /* local machine */ NULL, /* ServicesActive database */ SC_MANAGER_ALL_ACCESS); /* full access rights */ if (schSCManager == NULL) { DWORD dwErr = GetLastError(); LPCTSTR lpszBasicMessage = "Unable to open a connection to the Services database."; TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); switch(dwErr) { case ERROR_ACCESS_DENIED: FatalError(" [SNORT_SERVICE] %s Access is denied. %s", lpszBasicMessage, szMsg); break; case ERROR_DATABASE_DOES_NOT_EXIST: FatalError(" [SNORT_SERVICE] %s Services database does not exist. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_PARAMETER: FatalError(" [SNORT_SERVICE] %s Invalid parameter. %s", lpszBasicMessage, szMsg); break; default: FatalError(" [SNORT_SERVICE] %s Unrecognized error (%d). %s", lpszBasicMessage, dwErr, szMsg); break; } } schService = OpenService(schSCManager, /* SCManager database */ g_lpszServiceName, /* name of service */ DELETE); /* only need DELETE access */ if (schService == NULL) { DWORD dwErr = GetLastError(); LPCTSTR lpszBasicMessage = "Unable to locate Snort in the Services database."; TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); switch(dwErr) { case ERROR_ACCESS_DENIED: FatalError(" [SNORT_SERVICE] %s Access is denied. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_HANDLE: FatalError(" [SNORT_SERVICE] %s Invalid handle. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_NAME: FatalError(" [SNORT_SERVICE] %s Invalid name. %s", lpszBasicMessage, szMsg); break; case ERROR_SERVICE_DOES_NOT_EXIST: FatalError(" [SNORT_SERVICE] %s Service does not exist. %s", lpszBasicMessage, szMsg); break; default: FatalError(" [SNORT_SERVICE] %s Unrecognized error (%d). %s", lpszBasicMessage, dwErr, szMsg); break; } } if (! DeleteService(schService) ) { DWORD dwErr = GetLastError(); LPCTSTR lpszBasicMessage = "Unable to remove Snort from the Services database."; TCHAR szMsg[1000]; SvcFormatMessage(szMsg, sizeof(szMsg)); switch(dwErr) { case ERROR_ACCESS_DENIED: FatalError(" [SNORT_SERVICE] %s Access is denied. %s", lpszBasicMessage, szMsg); break; case ERROR_INVALID_HANDLE: FatalError(" [SNORT_SERVICE] %s Invalid handle. %s", lpszBasicMessage, szMsg); break; case ERROR_SERVICE_MARKED_FOR_DELETE: FatalError(" [SNORT_SERVICE] %s Service already marked for delete. %s", lpszBasicMessage, szMsg); break; default: FatalError(" [SNORT_SERVICE] %s Unrecognized error (%d). %s", lpszBasicMessage, dwErr, szMsg); break; } } printf("\n"); printf(" [SNORT_SERVICE] Successfully removed the Snort service from the Services database.\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } VOID ShowSnortServiceParams() { int argc; char ** argv; int i; ReadServiceCommandLineParams( &argc, &argv ); printf("\n" "Snort is currently configured to run as a Windows service using the following\n" "command-line parameters:\n\n" " "); for( i=1; i<=argc; i++ ) { if( argv[i] != NULL ) { printf(" %s", argv[i]); free( argv[i] ); argv[i] = NULL; } } free( argv ); argv = NULL; printf("\n"); } #endif /* ENABLE_WIN32_SERVICE */