/* * 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 packet filtering service : * this service uses the Packet Filtering API documented in the Microsoft * Platform SDK to manage filters in the kernel, via the packet filter * driver. */ #include #include #include #include #include #include "my_fltdefs.h" #include "pktflt.h" #include "filters.h" #include "rules.h" #include "rules_status.h" #include "filter_stats.h" #include "pktfltmsg.h" #define THE_PIPE "\\\\.\\pipe\\PktFltPipe" #define MAX_RULE_LENGTH 256 #define MIN_CLIENT_BUF_SIZE 256 #define FILTER_FAILURE 0 /* filter was correctly added */ #define FILTER_SUCCESS 1 /* filter had a syntax error */ #define FILTER_MESSAGE 2 /* informative message returned to the client */ /* globals for service status */ extern SERVICE_STATUS PktFltSrvStatus; extern SERVICE_STATUS_HANDLE PktFltSrvStatusHandle; extern HANDLE SystemLog; /* client_mode : 0 -> service is starting, rules are read from a file 1 -> rules are controlled interactively via the pktctl client */ char client_mode = 0; char syntax_error = 0; /* buffer containing messages returned to the client */ char *msg_buf; /* total length of the buffer */ unsigned long msg_buf_len; /* remaining length of the buffer */ unsigned long msg_buf_tail; extern struct pf_interface *interfaces; /* write a message in the buffer returned to the client */ void client_printf(char *format, ...) { va_list args; unsigned long to_write; unsigned long already_written; char *old_buf; char *current_pos; if (!client_mode) /* don't write to inexistant client */ return ; va_start(args, format); already_written = msg_buf_len - msg_buf_tail; current_pos = msg_buf + already_written; to_write = _vsnprintf(current_pos, msg_buf_tail, format, args); if (to_write >= msg_buf_tail) { /* we need a bigger buffer */ msg_buf_tail += msg_buf_len; msg_buf_len = 2 * msg_buf_len; old_buf = msg_buf; msg_buf = malloc(msg_buf_len); memset(msg_buf, 0, msg_buf_len); if (old_buf) { /* copy old buffer into new one */ memcpy(msg_buf, old_buf, already_written); free(old_buf); } current_pos = msg_buf + already_written; to_write = _vsnprintf(current_pos, msg_buf_tail, format, args); } msg_buf_tail -= to_write; } /* parse and load a rule in the packet filtering driver */ char load_rule(char *rule) { struct pf_filter *pf_filter; char parse_success; unsigned char parse_error; short status; pf_filter = malloc(sizeof(struct pf_filter)); pf_filter->filter = malloc(sizeof(PF_FILTER_DESCRIPTOR)); parse_success = parse_rule(rule, pf_filter, &parse_error); switch (parse_success) { case PF_RULE_DFLT: /* waiting for the second default rule ? */ if (pf_filter->iface->ih == NULL) { if ((pf_filter->iface->inAction != -1) && (pf_filter->iface->outAction != -1)) { /* we know both default input and output * filters so we can open the interface * with PfCreateInterface() */ status = open_interface(pf_filter); if (status != 1) client_printf("error: could not open specified interface \n"); } } else /* multiple default rules, ignoring */ client_printf("error: default rule already defined, ignoring\n"); /* success in any case */ return FILTER_SUCCESS; break; case PF_RULE_BRIEF_STATS: /* report brief statistics */ if (pf_filter->iface && pf_filter->iface->ih) { status = get_interface_statistics(pf_filter->iface, 0 /* brief */); if (status != 1) client_printf("error: could not get statistics on specified interface\n"); } else { /* no rule yet ! */ client_printf("error: no filter yet\n"); } return FILTER_SUCCESS; break; case PF_RULE_DETAILED_STATS: /* report detailed statistics */ if (pf_filter->iface && pf_filter->iface->ih) { status = get_interface_statistics(pf_filter->iface, 1 /* detailed */); if (status != 1) client_printf("error: could not get statistics on specified interface\n"); } else { /* no rule yet ! */ client_printf("error: no filter yet\n"); } return FILTER_SUCCESS; break; case PF_RULE_LIST: /* list all rules */ if (pf_filter->iface && pf_filter->iface->ih) { status = get_interface_rules(pf_filter->iface, 0 /* no rule numbers */); if (status != 1) client_printf("error: could not list rules on specified interface\n"); } else { /* no rule yet */ client_printf("error: no filter yet\n"); } return FILTER_SUCCESS; break; case PF_NUMBERED_RULE_LIST: /* list all rules with associated numbers */ if (pf_filter->iface && pf_filter->iface->ih) { status = get_interface_rules(pf_filter->iface, 1 /* rules numbers */); if (status != 1) client_printf("error: could not list rules on specified interface\n"); } else { /* no rule yet */ client_printf("error: no filter yet\n"); } return FILTER_SUCCESS; break; case PF_RULE_GF: /* global filter, nothing to do */ return FILTER_SUCCESS; break; case PF_RULE_DELETE: if (pf_filter->iface == NULL) { client_printf("error: this interface has no filter\n"); return FILTER_SUCCESS; break; } if (!delete_filter_on_interface(pf_filter->filter_flags, pf_filter)) client_printf("error: specified filter does not exist\n"); return FILTER_SUCCESS; case PF_RULE_FLUSH: /* flush filters */ if (pf_filter->iface == NULL) { client_printf("error: this interface has no filter\n"); return FILTER_SUCCESS; break; } if (pf_filter->iface == (struct pf_interface *) &interfaces) { /* flush on all */ status = close_all_interfaces(); if (status != 1) client_printf("error: could not flush filters on all interfaces\n"); } else { status = close_interface(pf_filter); if (status != 1) client_printf("error: could not flush filters on specified interface\n"); } return FILTER_SUCCESS; break; case PF_RULE_INVAL: /* filter is not valid, return error msg */ return FILTER_SUCCESS; break; case PF_RULE_ADD: status = add_filter_to_interface(pf_filter); if (status != 1) client_printf("error: could not add specified filter\n"); return FILTER_SUCCESS; break; case PF_RULE_ERR: /* syntax error */ syntax_error = parse_error; return FILTER_FAILURE; break; default: return FILTER_SUCCESS; break; } /* XXX not reached */ return FILTER_SUCCESS; } /* initial load of rules from a file */ char load_filters_from_file(char *filename) { FILE *filters; char line[MAX_RULE_LENGTH]; char success; LPCTSTR event_msg; filters = fopen(filename, "r"); if (filters == NULL) { /* report event */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_OPEN_FILTERS_FILE_FAILED_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); /* service terminates */ return NO_ERROR + 1; } while (fgets(line, MAX_RULE_LENGTH, filters) != NULL) { if (*line == '#') /* ignore comments line */ continue; success = load_rule(line); if (success != FILTER_SUCCESS) { /* syntax error */ event_msg = line; ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_RULE_SYNTAX_ERROR, NULL, 1, 0, &event_msg, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); fclose(filters); /* service terminates */ return NO_ERROR + 1; } } fclose(filters); client_mode = 1; /* turn on client mode */ return NO_ERROR; } /* loop to wait for client requests */ int main_loop(void) { HANDLE PktFltPipe; DWORD read; DWORD written; BOOL bool; #define PF_SUCCESS 0 char success = PF_SUCCESS; char rule[MAX_RULE_LENGTH]; /* XXX no rule longer than MAX_RULE_LENGTH bytes XXX */ char status; unsigned int to_write; DWORD last_error; /* allocate initial client messages buffer */ if (msg_buf) free(msg_buf); msg_buf_len = MIN_CLIENT_BUF_SIZE; msg_buf = malloc(msg_buf_len * sizeof(BYTE)); if (msg_buf == NULL) { /* stop service */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_ALLOC_FAILED, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); /* service terminates */ return NO_ERROR + 1; } /* create one pipe instance */ PktFltPipe = CreateNamedPipe(THE_PIPE, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, /* only one instance */ 0, /* default */ 0, /* default */ 3000, /* timeout */ NULL /* default security descriptor */ ); if (PktFltPipe == INVALID_HANDLE_VALUE) { /* report event and stop service */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_CLIENT_PIPE_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return 0; } /* waiting for events from pktctl */ while (1) { memset(msg_buf, 0, msg_buf_len); msg_buf_tail = msg_buf_len; /* waiting for a client to connect */ bool = ConnectNamedPipe(PktFltPipe, NULL); if (!bool) { last_error = GetLastError(); if (last_error != ERROR_PIPE_CONNECTED) { /* Connection with client failed: report event */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_CLIENT_PIPE_ERROR, NULL, 0, 0, NULL, NULL); PktFltSrvStatus.dwCurrentState = SERVICE_STOPPED; PktFltSrvStatus.dwWin32ExitCode = 0; SetServiceStatus(PktFltSrvStatusHandle, &PktFltSrvStatus); return 0; } /* else: although bool == 0, we are connected */ } /* read message from client */ status = ReadFile(PktFltPipe, rule, MAX_RULE_LENGTH, &read, NULL); if (read == MAX_RULE_LENGTH) { /* rule too long */ client_printf("error: rule too long\n"); } else { memset(rule + read, 0, MAX_RULE_LENGTH - read); status = load_rule(rule); } if (client_mode) { /* write answer to client */ to_write = msg_buf_len - msg_buf_tail; if (to_write) { /* informative message */ success = FILTER_MESSAGE; status = WriteFile(PktFltPipe, &success, 1, &written, NULL); /* write size of informative message */ status = WriteFile(PktFltPipe, &to_write, sizeof(to_write), &written, NULL); /* and then informative message */ status = WriteFile(PktFltPipe, msg_buf, to_write, &written, NULL); } else { if (status == FILTER_FAILURE) { /* write position of syntax error */ success = FILTER_FAILURE; status = WriteFile(PktFltPipe, &success, 1, &written, NULL); status = WriteFile(PktFltPipe, &syntax_error, 1, &written, NULL); } else { success = FILTER_SUCCESS; status = WriteFile(PktFltPipe, &success, 1, &written, NULL); } } if (status == 0) { /* can't write in pipe */ ReportEvent(SystemLog, EVENTLOG_ERROR_TYPE, 0, PKTFLT_CLIENT_PIPE_ERROR, NULL, 0, 0, NULL, NULL); } } /* disconnect this instance */ bool = DisconnectNamedPipe(PktFltPipe); } return 0; /* success */ }