/* * Copyright (c) 2001 Jean-Baptiste Marchand, Hervé Schauer Consultants. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jean-Baptiste Marchand * at Hervé Schauer Consultants. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * * Win32 service code * */ #include #include #include #include #include #include #include "pktfilter.h" #include "pktfltmsg.h" #define PKTFILTERSRV_NAME "PktFilter" #define PKTFILTERSRV_FULL_NAME "Stateless Packet Filtering" #define PKTFILTERSRV_DESCRIPTION "Configures Windows 2000/XP/Server 2003 IPv4 filtering driver" #define PKTFILTERSRV_REGISTRY "SYSTEM\\CurrentControlSet\\Services\\PktFilter" #define PKTFILTERSRV_MESSAGE_FILE "MSG00001.bin" #define EVENTLOG_SYSTEM_PKTFILTER "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\PktFilter" #define EVENTLOG_SYSTEM "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System\\" #define SILENT 0 #define VERBOSE 1 #define DEFAULT_LOG_BUF_SIZE 4096 DWORD log_buf_size = DEFAULT_LOG_BUF_SIZE; /* globals */ SERVICE_STATUS PktFltSrvStatus; SERVICE_STATUS_HANDLE PktFltSrvStatusHandle; HANDLE SystemLog; char Log_File[MAX_PATH]; void usage(char **argv) { char *progname = "pktfltsrv"; fprintf(stderr, "usage: %s [-i \"rules_file\" \"log_file\"]\n", progname); fprintf(stderr, " %s [-u]\n\n", progname); fprintf(stderr, "Typical usages:\n"); fprintf(stderr, " - install service specifying the rules file, log file and DNS log file:\n"); fprintf(stderr, "\t%s -i \"C:\\Program Files\\PktFilter\\pktctl\\rules.txt\"\n", progname); fprintf(stderr, "\t \"C:\\Program Files\\PktFilter\\pktctl\\PktFilter.log\"\n", progname); fprintf(stderr, "\t \"C:\\Program Files\\PktFilter\\pktctl\\DnsLookup.log\"\n", progname); fprintf(stderr, " - install service with default file locations, listed above:\n"); fprintf(stderr, "\t%s -d\n",progname); fprintf(stderr, " - uninstall service:\n"); fprintf(stderr, "\t%s -u\n",progname); } /* uninstall the service */ char uninstall_srv(char verbose) { SC_HANDLE scm, srv_handle; DWORD error; HKEY hk; scm = OpenSCManager( NULL /* local machine */, NULL /* SERVICE_ACTIVE_DATABASE */, SC_MANAGER_ALL_ACCESS); if (scm == NULL) { fprintf(stderr, "error: can't open a handle to the SCM\n"); return 1; } srv_handle = OpenService(scm, PKTFILTERSRV_NAME, DELETE); if (srv_handle == NULL) { fprintf(stderr, "error: could not open a handle to the Packet Filtering service\n"); return 1; } /* remove PktFilter subkey under EventLog\System */ error = RegCreateKeyEx(HKEY_LOCAL_MACHINE, EVENTLOG_SYSTEM, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hk, NULL); if (error != ERROR_SUCCESS) fprintf(stderr, "error: could not delete PktFilter key under EventLog\\System\\\n"); else { error = RegDeleteKey(hk, PKTFILTERSRV_NAME); if (error != ERROR_SUCCESS) fprintf(stderr, "error: could not delete PktFilter key under EventLog\\System\\\n"); } if (!DeleteService(srv_handle)) { error = GetLastError(); if (error == ERROR_SERVICE_MARKED_FOR_DELETE) fprintf(stderr, "error: service marked for deletion, will be deleted when stopped\n"); CloseServiceHandle(srv_handle); return 1; } else { if (verbose) printf("Uninstallation of Packet Filtering service was successful\n"); CloseServiceHandle(srv_handle); return 0; } } /* install the service */ char install_srv(char *rules_file, char *log_file, char * dnslog_file) { SC_HANDLE scm, srv; DWORD status; DWORD dwData; HKEY hk; DWORD ex_length; DWORD log_buffer_size = DEFAULT_LOG_BUF_SIZE; char ServiceExecutableName[MAX_PATH]; scm = OpenSCManager( NULL /* local machine */, NULL /* SERVICE_ACTIVE_DATABASE */, SC_MANAGER_CREATE_SERVICE); if (scm == NULL) { fprintf(stderr, "error: can't open a handle to the SCM\n"); return 1; } status = GetModuleFileName( NULL /* current executable */, ServiceExecutableName, MAX_PATH - 3); /* trailing " -s" */ if (!status) { fprintf(stderr, "error: can't determine path of service executable\n"); return 1; } ex_length = strlen(ServiceExecutableName); status = (DWORD) strncat(ServiceExecutableName, " -s", 3); if (!status) { fprintf(stderr, "error: service executable name too long\n"); return 1; } /* Create the service */ srv = CreateService( scm /* handle to SCM */, PKTFILTERSRV_NAME /* name of Win32 service */, PKTFILTERSRV_FULL_NAME /* full name */, 0 /* no permission needed */, SERVICE_WIN32_OWN_PROCESS /* one service only */, SERVICE_DEMAND_START, /* service configuration has to be changed explicitly if the administrator is satisfied with PktFilter and wants to launch it at startup */ SERVICE_ERROR_IGNORE, ServiceExecutableName, NULL /* not member of a group */, NULL /* no tag */, "IpFilterDriver\000", /* service depends on IP filter driver */ NULL /* LocalSystem account */, NULL /* LocalSystem */ ); if (srv != NULL) { /* add registry value RulesFile at the right place */ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, PKTFILTERSRV_REGISTRY, 0, KEY_WRITE, &hk); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not add registry value to specify rules file\n"); return 1; } /* add RulesFile value (contains name of the rules file) */ status = RegSetValueEx(hk, "RulesFile", 0, REG_SZ, rules_file, strlen(rules_file)); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not add registry value to specify rules file\n"); return 1; } /* add RulesFile value (contains name of the dns log file) */ status = RegSetValueEx(hk, "DnsLogFile", 0, REG_SZ, dnslog_file, strlen(dnslog_file)); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not add registry value to specify dns log file\n"); return 1; } /* add LogFile value (contains name of the log file) */ status = RegSetValueEx(hk, "LogFile", 0, REG_SZ, log_file, strlen(log_file)); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not add registry value to specify log file\n"); return 1; } /* add LogBufferSize value (contains size of the logging buffer) */ status = RegSetValueEx(hk, "LogBufferSize", 0, REG_DWORD, (BYTE *) &log_buffer_size, sizeof(log_buffer_size)); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not add registry value to specify logging buffer size\n"); return 1; } /* add Description value (contains the description of the service */ status = RegSetValueEx(hk, "Description", 0, REG_SZ, PKTFILTERSRV_DESCRIPTION, strlen(PKTFILTERSRV_DESCRIPTION)); if (status != ERROR_SUCCESS) fprintf(stderr, "Warning: could not set Description value of the Packet Filtering service\n"); RegCloseKey(hk); /* create PktFilter subkey under EventLog\System */ status = RegCreateKeyEx(HKEY_LOCAL_MACHINE, EVENTLOG_SYSTEM_PKTFILTER, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hk, NULL); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not create PktFilter key under System\\EventLog\\\n"); return 1; } ServiceExecutableName[ex_length] = 0; /* no trailing " -s" */ /* set the event message file */ status =RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE) ServiceExecutableName, strlen(ServiceExecutableName) + 1); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not add registry value to specify messages file\n"); return 1; } /* set the supported event types */ dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; status = RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwData, sizeof(DWORD)); if (status != ERROR_SUCCESS) { uninstall_srv(SILENT); fprintf(stderr, "error: could not add registry value to specify messages type \n"); return 1; } printf("Packet Filtering service installation was successful\n\n"); printf("IMPORTANT:\n\n\ Do _not_ forget to change the Startup Type of PktFilter to Automatic in\ the Services Manager if you want PktFilter to start automatically at system startup\ (recommended)\n"); return 0; } else { fprintf(stderr, "error: Packet Filtering service installation failed\n"); return 1; } } /* function handler for service control code */ void WINAPI PktFltSrvHandler(DWORD code) { switch(code) { case SERVICE_CONTROL_STOP: /* we have to stop */ PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; PktFltSrvStatus.dwCheckPoint = 0; PktFltSrvStatus.dwWaitHint = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return; break; default: /* no other controls accepted */ ; } return ; } /* test if the RRAS service is running and if there are filters managed by RRAS. * If yes, we logged an event, as RRAS filters could conflict with ours * Note that filters managed by our service are not visible by RRAS and vice * versa, as the interfaces are openened as non-shared (see documentation of * PfCreateInterface()) */ char test_rras_filters(void) { SC_HANDLE scm; SC_HANDLE srv; QUERY_SERVICE_CONFIG rras_conf; QUERY_SERVICE_CONFIG *p_rras_conf; DWORD needed; BOOL status; BYTE *pBuffer = NULL; scm = OpenSCManager( NULL /* local machine */, NULL /* SERVICE_ACTIVE_DATABASE */, GENERIC_READ); if (scm == NULL) /* can't open handle to SCM, return 1 so that our service won't start */ return 1; srv = OpenService(scm, "RemoteAccess", SERVICE_QUERY_CONFIG); if (srv == NULL) /* can't open handle to RRAS service, return 0 (maybe RRAS service doesn't exist on this machine... */ return 0; status = QueryServiceConfig(srv, &rras_conf, sizeof(rras_conf), &needed); if (!status) { if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { /* we need a bigger buffer */ pBuffer = (BYTE *) malloc(needed * sizeof(BYTE)); status = QueryServiceConfig(srv, (QUERY_SERVICE_CONFIG *) pBuffer, needed, &needed); if (!status) { /* error */ free(pBuffer); return 1; } else p_rras_conf = (QUERY_SERVICE_CONFIG *) pBuffer; } else /* another kind of error, abort */ return 1; } else p_rras_conf = &rras_conf; /* we only test if RRAS is supposed to auto-start. If yes, RRAS service may be currently running or will start a bit later. We don't test SERVICE_DEMAND_START, as we suppose administrator knows what he is doing if he starts RRAS on demand... */ if (p_rras_conf->dwStartType == SERVICE_AUTO_START) { /* RRAS is (or will) run(ning), so we refuse to start in case * there are filters managed by RRAS. We *could* accept to start * if there are no filter managed by RRAS *but* the format of the * block containing filtering rules of RRAS * (IP_{IN,OUT}_FILTER_INFO) is not documented as today */ if (pBuffer != NULL) free(pBuffer); return 1; } else { /* RRAS is not supposed to run, so our service can start and manage filters */ if (pBuffer != NULL) free(pBuffer); return 0; } } /* service entry's point */ void WINAPI PktFltSrvMain(DWORD argc, LPTSTR *argv) { HKEY hk; DWORD status; DWORD value_size; char rules_file[MAX_PATH]; char dnslog_file[MAX_PATH]; /* opening system log */ SystemLog = RegisterEventSource(NULL, TEXT("PktFilter")); /* setting service status */ PktFltSrvStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; PktFltSrvStatus.dwCurrentState = SERVICE_START_PENDING; PktFltSrvStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; PktFltSrvStatus.dwWin32ExitCode = 0; PktFltSrvStatus.dwServiceSpecificExitCode = 0; /* ignored */ PktFltSrvStatus.dwCheckPoint = 0; /* ignored */ PktFltSrvStatus.dwWaitHint = 0; /* ignored */ PktFltSrvStatusHandle = RegisterServiceCtrlHandler(PKTFILTERSRV_NAME, PktFltSrvHandler); if (test_rras_filters()) /* RRAS is running, we start with a warning */ ReportEvent(SystemLog, EVENTLOG_WARNING_TYPE, 0, PKTFLT_RRAS_RUNNING, NULL, 0, 0, NULL, NULL); if (PktFltSrvStatusHandle == (SERVICE_STATUS_HANDLE) 0) { /* failed to register service's control program */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_CTLHDL_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return ; } status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, PKTFILTERSRV_REGISTRY, 0, KEY_READ, &hk); if (status != ERROR_SUCCESS) { /* failed to read service registry key */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_READ_REGISTRY_KEY_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return ; } value_size = MAX_PATH; status = RegQueryValueEx(hk, "RulesFile", 0, NULL, rules_file, &value_size); if (status != ERROR_SUCCESS) { /* failed to read RulesFile registry value */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_READ_REGISTRY_RULES_FILE_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return ; } value_size = MAX_PATH; status = RegQueryValueEx(hk, "DnsLogFile", 0, NULL, dnslog_file, &value_size); if (status != ERROR_SUCCESS) { /* failed to read RulesFile registry value */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_READ_DNS_LOG_FILE_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return ; } /* read log file path */ memset(Log_File, 0, MAX_PATH); value_size = MAX_PATH; status = RegQueryValueEx(hk, "LogFile", 0, NULL, Log_File, &value_size); if (status != ERROR_SUCCESS) { /* failed to read LogFile registry value */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_READ_REGISTRY_LOG_FILE_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return ; } /* read logging buffer size */ value_size = sizeof(log_buf_size); status = RegQueryValueEx(hk, "LogBufferSize", 0, NULL, (BYTE *) &log_buf_size, &value_size); if (status != ERROR_SUCCESS) { /* default size is DEFAULT_LOG_BUF_SIZE */ log_buf_size = DEFAULT_LOG_BUF_SIZE; } CloseHandle(hk); status = load_filters_from_file(rules_file, dnslog_file); if (status != NO_ERROR) { /* failed to load filters from file */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_READ_REGISTRY_RULES_FILE_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return ; } /* service has started successfuly */ PktFltSrvStatus.dwCurrentState = SERVICE_RUNNING; PktFltSrvStatus.dwWin32ExitCode = NO_ERROR; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); main_loop(); } int main(int argc, char **argv) { char status; SERVICE_TABLE_ENTRY SvcEntry[2]; if ((argc == 2) && (strncmp(argv[1], "-s", 2) == 0)) { /* service start */ SvcEntry[0].lpServiceName = PKTFILTERSRV_NAME; SvcEntry[0].lpServiceProc = PktFltSrvMain; SvcEntry[1].lpServiceName = NULL; SvcEntry[1].lpServiceProc = NULL; StartServiceCtrlDispatcher(SvcEntry); } if ((argc == 2) && (strncmp(argv[1], "-u", 2) == 0)) { status = uninstall_srv(VERBOSE); return status; } if ((argc == 5) && (strncmp(argv[1], "-i", 2) == 0)) { status = install_srv(argv[2], argv[3], argv[4]); return status; } if ((argc == 2) && (strncmp(argv[1], "-d", 2) == 0)) { status = install_srv("C:\\Program Files\\PktFilter\\pktctl\\rules.txt", "C:\\Program Files\\PktFilter\\pktctl\\PktFilter.log", "C:\\Program Files\\PktFilter\\pktctl\\DnsLookup.log"); return status; } /* display usage in other cases */ usage(argv); return 1; }