www.pudn.com > tdi_fw.zip > tdi_fw_svc.c
// $Id: tdi_fw_svc.c,v 1.12 2002/12/05 13:03:27 dev Exp $ #include#include #include #include #include #include "flt_rule.h" #include "ipc.h" #include "tdi_fw_svc.h" #include "thread.h" #include "msg.h" #define DISP_BUF_SIZE 0x10000 HANDLE g_device = INVALID_HANDLE_VALUE; static HANDLE g_event = NULL; static HANDLE g_exit_event = NULL; static HANDLE g_pipe = INVALID_HANDLE_VALUE; static HANDLE g_dispatcher = NULL; static char *g_disp_buf = NULL; static DWORD WINAPI dispatcher(LPVOID param); static DWORD WINAPI restart_thread(LPVOID param); static void dispatch_request(struct flt_request *request); static FILE *g_logfile = NULL; static BOOL load_config(const char *config); static BOOL read_config(const char *config); static BOOL add_rules_name(const char *main_name, const char *config, int chain); static void add_rules(char *buf, const char *name, int chain); static BOOL get_pname_by_pid(u_long pid, char *buf, int buf_size); static void prepare_addr(char *buf, int size, u_long addr); static void my_GetLongPathName(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer); // some config switches static BOOL g_eventlog_allow = FALSE; static BOOL g_eventlog_deny = FALSE; static BOOL g_eventlog_error = FALSE; // used in flt_rule.c BOOL g_rules_resolve_addr = FALSE; static BOOL g_logs_resolve_addr = FALSE; static void log_msg(const char *msg, int type); enum { MSGTYPE_ALLOW, MSGTYPE_DENY, MSGTYPE_ERROR }; int start(const char *config) { static char device_name[] = "\\\\.\\tdi_fw"; int result = FALSE; WSADATA wsd; DWORD thread_id; // startup sockets for names resolution WSAStartup(MAKEWORD(1, 1), &wsd); /* connect with driver */ g_device = CreateFile(device_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (g_device == INVALID_HANDLE_VALUE) { winerr(device_name); goto done; } /* open event for driver communication */ g_event = OpenEvent(SYNCHRONIZE, FALSE, "tdi_fw_request"); if (g_event == NULL) { winerr("start: OpenEvent"); goto done; } /* load config & rules */ if (!load_config(config)) goto done; /* start dispatcher thread */ g_exit_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (g_exit_event == NULL) { winerr("start: CreateEvent"); goto done; } g_disp_buf = (char *)malloc(DISP_BUF_SIZE); if (g_disp_buf == NULL) { liberr("start: malloc"); goto done; } g_dispatcher = lib_CreateThread(NULL, 0, dispatcher, (LPVOID)config, 0, &thread_id); if (g_dispatcher == NULL) { winerr("start: lib_CreateThread"); goto done; } result = TRUE; done: if (!result) stop(); return result; } void stop(void) { // disconnect from driver if (g_device != INVALID_HANDLE_VALUE) { HANDLE old_h = g_device; g_device = INVALID_HANDLE_VALUE; CancelIo(old_h); CloseHandle(old_h); } // stop dispatcher thread if (g_exit_event != NULL) SetEvent(g_exit_event); if (g_dispatcher != NULL) { WaitForSingleObject(g_dispatcher, INFINITE); g_dispatcher = NULL; } // close logfile if (g_logfile != NULL) { fprintf(g_logfile, "--- end ---\n"); fclose(g_logfile); g_logfile = NULL; } if (g_exit_event != NULL) CloseHandle(g_exit_event); if (g_event != NULL) CloseHandle(g_event); if (g_disp_buf != NULL) { free(g_disp_buf); g_disp_buf = NULL; } } void wait(void) { if (g_exit_event != NULL) WaitForSingleObject(g_exit_event, INFINITE); } /* output functions */ void error(const char *fmt, ...) { va_list ap; char message[1024]; // prepare message va_start(ap, fmt); if (_vsnprintf(message, sizeof(message), fmt, ap) == -1) message[sizeof(message) - 1] = '\0'; va_end(ap); // got message log_msg(message, MSGTYPE_ERROR); } void winerr(const char *fn) { char *win_msg = NULL; DWORD code = GetLastError(); if (code == 0) error("WINERR\t%s:", fn); else { FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL,code, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&win_msg,0,NULL); if (win_msg != NULL) { // kill \r\n int len = strlen(win_msg); if (len >= 1) win_msg[len-1]=0; if (len >= 2) win_msg[len-2]=0; error("WINERR\t%s: %s", fn, win_msg); LocalFree(win_msg); } else error("WINERR\t%s: %d (0x%x)", fn, code, code); } // save error code SetLastError(code); } void liberr(const char *fn) { int code = errno; if (code != 0) error("LIBERR\t%s: %s", fn, strerror(code)); else error("LIBERR\t%s:", fn); } /* dispatcher thread */ DWORD WINAPI dispatcher(LPVOID param) { char *config = (char *)param; HANDLE handles[2]; DWORD i, n; handles[0] = g_event; handles[1] = g_exit_event; for (;;) { if (!DeviceIoControl(g_device, IOCTL_CMD_GETREQUEST, NULL, 0, g_disp_buf, DISP_BUF_SIZE, &n, NULL)) { winerr("dispatcher: DeviceIoControl"); break; } if (n == 0) { // wait for data DWORD wait = WaitForMultipleObjects(2, handles, FALSE, INFINITE); if (wait == WAIT_OBJECT_0 + 1) break; else if (wait != WAIT_OBJECT_0) { winerr("dispatcher: WaitForSingleObject"); break; } continue; } for (i = 0; i < n;) { struct flt_request *request; if (n - i < sizeof(*request)) break; request = (struct flt_request *)(g_disp_buf + i); dispatch_request(request); i += request->struct_size; } } if (g_device != INVALID_HANDLE_VALUE) { DWORD thread_id; error("dispatcher: unexpected exit!"); // restart tdi_fw_svc lib_CreateThread(NULL, 0, restart_thread, config, 0, &thread_id); } return 0; } DWORD WINAPI restart_thread(LPVOID param) { char *config = (char *)param; error("restarting..."); stop(); start(config); return 0; } /* log filter request from filter driver */ void dispatch_request(struct flt_request *request) { u_long from_ip, to_ip; u_short from_port, to_port; char msg[1024], pname_buf[MAX_PATH], addr_from[100], addr_to[100], *pname; if (request->pname == NULL) { // try to resolve pid to pname if (get_pname_by_pid(request->pid, pname_buf + sizeof(DWORD), sizeof(pname_buf) - sizeof(DWORD))) { // send message to driver to send process name next time DWORD n; pname = pname_buf + sizeof(DWORD); *(DWORD *)pname_buf = request->pid; if (!DeviceIoControl(g_device, IOCTL_CMD_SETPNAME, pname_buf, sizeof(DWORD) + strlen(pname) + 1, NULL, 0, &n, NULL)) winerr("DeviceIoControl"); } else { error("PROCESS\tCan't resolve pid %u!\n", request->pid); sprintf(pname_buf, "pid:%u", request->pid); pname = pname_buf; } } else pname = (char *)&request[1]; // check is it request type "TYPE_RESOLVE_PID" if (request->type == TYPE_RESOLVE_PID) { if (_snprintf(msg, sizeof(msg), "PROCESS\t%u\t%s", request->pid, pname) == -1) msg[sizeof(msg) - 1] = '\0'; log_msg(msg, request->result == FILTER_ALLOW ? MSGTYPE_ALLOW : MSGTYPE_DENY); } else { // prepare message from_ip = ((struct sockaddr_in *)&request->addr.from)->sin_addr.s_addr; from_port = ntohs(((struct sockaddr_in *)&request->addr.from)->sin_port); to_ip = ((struct sockaddr_in *)&request->addr.to)->sin_addr.s_addr; to_port = ntohs(((struct sockaddr_in *)&request->addr.to)->sin_port); // prepare address "from" & "to" prepare_addr(addr_from, sizeof(addr_from), from_ip); prepare_addr(addr_to, sizeof(addr_to), to_ip); // log it! if (_snprintf(msg, sizeof(msg), "%s\t%s\t%s\t%s:%d\t%s:%d\t%s", (request->result == FILTER_ALLOW) ? "ALLOW" : "DENY", (request->proto == IPPROTO_TCP) ? "TCP" : (request->proto == IPPROTO_UDP ? "UDP" : "RawIP"), (request->direction == DIRECTION_IN) ? "IN" : "OUT", addr_from, from_port, addr_to, to_port, pname) == -1) msg[sizeof(msg) - 1] = '\0'; log_msg(msg, request->result == FILTER_ALLOW ? MSGTYPE_ALLOW : MSGTYPE_DENY); } } /* * write message to console, log file or probably to eventlog * * don't call any error functions! to cause infinite recurse. */ void log_msg(const char *msg, int type) { static int file_day = 0; // for midnight checking static ULONG event_num = 1; // number of event in log file // if working in console mode write message to console if (g_console) { if (type == MSGTYPE_ERROR) fprintf(stderr, "%s\n", msg); else printf("%s\n", msg); } // write to eventlog if filter is set if ((type == MSGTYPE_ALLOW && g_eventlog_allow) || (type == MSGTYPE_DENY && g_eventlog_deny) || (type == MSGTYPE_ERROR && g_eventlog_error)) { HANDLE hEventSource; const char *strings; // write message to event log // Use event logging to log the error. // hEventSource = RegisterEventSource(NULL, "tdi_fw_svc"); if (hEventSource != NULL) { WORD event_type; strings = msg; // write message without timestamp event_type = (type == MSGTYPE_ALLOW) ? EVENTLOG_AUDIT_SUCCESS : (type == MSGTYPE_DENY ? EVENTLOG_AUDIT_FAILURE : EVENTLOG_ERROR_TYPE); if (!ReportEvent(hEventSource, // handle of event source event_type, 0, // event category MSG, // event ID NULL, // current user's SID 1, // strings in lpszStrings 0, // no bytes of raw data &strings, // array of error strings NULL)) { // no raw data #ifdef _DEBUG MessageBeep(0); OutputDebugString("log_msg: ReportEvent\n"); #endif } DeregisterEventSource(hEventSource); } } else { // write message to log time_t tv; struct tm *tm; char tm_msg[1024]; time(&tv); tm = localtime(&tv); if (tm == NULL) { #ifdef _DEBUG MessageBeep(0); OutputDebugString("log_msg: localtime\n"); #endif return; } if (g_logfile == NULL || tm->tm_mday != file_day) { char fname[MAX_PATH]; if (g_logfile != NULL) { // wow! we're working at midnight! change log file! fprintf(g_logfile, "--- midnight ---\n"); fclose(g_logfile); g_logfile = NULL; } file_day = tm->tm_mday; // open logfile if (_snprintf(fname, sizeof(fname), "%s\\system32\\LogFiles\\tdi_fw\\%04d%02d%02d.log", getenv("SystemRoot"), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday) == -1) { #ifdef _DEBUG MessageBeep(0); OutputDebugString("log_msg: _snprintf overflow!\n"); #endif return; } g_logfile = fopen(fname, "a"); if (g_logfile == NULL) { #ifdef _DEBUG MessageBeep(0); OutputDebugString("log_msg: fopen error!\n"); #endif return; } fprintf(g_logfile, "--- begin ---\n"); } if (_snprintf(tm_msg, sizeof(tm_msg), "%010u %02d:%02d:%02d\t%s", event_num++, tm->tm_hour, tm->tm_min, tm->tm_sec, msg) == -1) tm_msg[sizeof(tm_msg) - 1] = '\0'; // write it to log file fprintf(g_logfile, "%s\n", tm_msg); fflush(g_logfile); } } #define MAX_SECTION_SIZE (16 * 1024) BOOL load_config(const char *config) { char *section, *p; DWORD n, pids[100]; BOOL result = FALSE; int line, chain; if (!read_config(config)) return FALSE; // parse & add rules default and process-related section = (char *)malloc(MAX_SECTION_SIZE); if (section == NULL) { liberr("malloc"); goto done; } GetPrivateProfileSection("_main_", section, MAX_SECTION_SIZE, config); // get lines chain = 0; for (p = section, line = 1; *p != '\0';p += strlen(p) + 1, line++) { char *p2; if (*p == ';') continue; // comment p2 = strchr(p, '='); if (p2 == NULL) { error("%s:[_main_]:%d: invalid line format (no '=' character)"); continue; } if (chain >= MAX_CHAINS_COUNT) { error("%s:[_main_]:%d: too many rules lines!"); break; } *p2 = '\0'; // temporary kill '=' add_rules_name(p, config, chain); *p2 = '='; // recover '=' to make strlen(p) works right chain++; } // try to get names for existing processes if (EnumProcesses(pids, sizeof(pids), &n)) { DWORD i; n /= sizeof(DWORD); for (i = 0; i < n; i++) { char pname[MAX_PATH]; if (get_pname_by_pid(pids[i], pname + sizeof(DWORD), sizeof(pname) - sizeof(DWORD))) { // send information to driver about pid and pname DWORD nn; *(DWORD *)pname = pids[i]; if (!DeviceIoControl(g_device, IOCTL_CMD_SETPNAME, pname, sizeof(DWORD) + strlen(pname + sizeof(DWORD)) + 1, NULL, 0, &nn, NULL)) winerr("DeviceIoControl"); } } } result = TRUE; done: free(section); return result; } BOOL read_config(const char *config) { static char config_name[] = "_config_"; static char signature_name[] = "_signature_"; static char signature_value[] = "$tdi_fw$"; char buf[100]; // first, check config file signature GetPrivateProfileString(signature_name, signature_name, "", buf, sizeof(buf), config); if (strcmp(buf, signature_value) != 0) { error("\"%s\": invalid configuration file", config); return FALSE; } // get g_eventlog_xxx values g_eventlog_allow = GetPrivateProfileInt(config_name, "eventlog_allow", FALSE, config); g_eventlog_deny = GetPrivateProfileInt(config_name, "eventlog_deny", FALSE, config); g_eventlog_error = GetPrivateProfileInt(config_name, "eventlog_error", FALSE, config); // get other values g_rules_resolve_addr = GetPrivateProfileInt(config_name, "rules_resolve_addr", FALSE, config); g_logs_resolve_addr = GetPrivateProfileInt(config_name, "logs_resolve_addr", FALSE, config); return TRUE; } BOOL add_rules_name(const char *main_name, const char *config, int chain) { char buf[1024], *p, *p2, *section = NULL; BOOL result = FALSE; DWORD n; section = (char *)malloc(MAX_SECTION_SIZE); if (section == NULL) { liberr("malloc"); goto done; } /* 1. get ruleset string */ GetPrivateProfileString("_main_", main_name, "", buf, sizeof(buf), config); if (*buf == '\0') goto done; // no rules // reset all rules for chain if (!DeviceIoControl(g_device, IOCTL_CMD_CLEARCHAIN, &chain, sizeof(chain), NULL, 0, &n, NULL)) { winerr("DeviceIoControl"); goto done; } if (chain != 0) { // set chain name int len = sizeof(int) + strlen(main_name) + 1; char *data = (char *)malloc(len); if (data == NULL) { liberr("malloc"); goto done; } *(int *)data = chain; strcpy(data + sizeof(int), main_name); if (!DeviceIoControl(g_device, IOCTL_CMD_SETCHAINPNAME, data, len, NULL, 0, &n, NULL)) { winerr("DeviceIoControl"); free(data); goto done; } free(data); } /* 2. for each word in main_name string */ p = buf; while (p != NULL) { p2 = strchr(p, ' '); if (p2 != NULL) { while (*p2 == ' ') *(p2++) = '\0'; } if (*p != '\0') { // get section by name in p GetPrivateProfileSection(p, section, MAX_SECTION_SIZE, config); if (*section == '\0') error("\"%s\": unexistant or empty section %s", config, p); else add_rules(section, p, chain); } p = p2; } result = TRUE; done: free(section); return result; } void add_rules(char *buf, const char *name, int chain) { char *p, *p2; int n_str; for (p = buf, n_str = 1; *p != '\0'; p = p2, n_str++) { struct flt_rule rule; DWORD n; p2 = p + strlen(p) + 1; if (*buf == ';' || *buf == '\0') continue; // empty line or comment memset(&rule, 0, sizeof(rule)); if (!parse_rule(p, &rule)) { error("Error in line #%d of section [%s]", n_str, name); continue; } // set chain rule.chain = chain; // append rule if (!DeviceIoControl(g_device, IOCTL_CMD_APPENDRULE, &rule, sizeof(rule), NULL, 0, &n, NULL)) { winerr("start: DeviceIoControl"); return; } } } BOOL get_pname_by_pid(u_long pid, char *buf, int buf_size) { BOOL result; HANDLE h_process; HMODULE h_module; DWORD n; // try to resolve pid to pname h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (h_process != NULL) EnumProcessModules(h_process, &h_module, sizeof(h_module), &n); // on error write pid if (h_module == NULL || GetModuleFileNameEx(h_process, h_module, buf, buf_size) == 0) { // for "System" process last error value is: // * ERROR_PARTIAL_COPY (on 2k) // * ERROR_NOACCESS (on NT4) // ??? on other (TODO: think about another way) if (GetLastError() == ERROR_PARTIAL_COPY || GetLastError() == ERROR_NOACCESS) { strncpy(buf, "System", buf_size); buf[buf_size - 1] = '\0'; result = TRUE; } else { *buf = '\0'; result = FALSE; } } else { if (strchr(buf, '~') != NULL) { // XXX is it a right way? // try to convert long name to short name char long_name[MAX_PATH]; my_GetLongPathName(buf, long_name, sizeof(long_name)); strncpy(buf, long_name, buf_size - 1); buf[buf_size - 1] = '\0'; } result = TRUE; } return result; } void prepare_addr(char *buf, int size, u_long addr) { if (g_logs_resolve_addr) { // TODO: doesn't work correctly (maybe remove it?) struct hostent *he; if (addr == INADDR_ANY) { strcpy(buf, "ANY"); return; } // try to resolve addr he = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); if (he != NULL) { // set name in buffer (and reserve space for ) size_t len = size - sizeof(" "); strncpy(buf, he->h_name, len); buf[len - 1] = '\0'; } else buf[0] = '\0'; // add strcat(buf, "["); strcat(buf, inet_ntoa(*(struct in_addr *)&addr)); strcat(buf, "]"); } else strcpy(buf, inet_ntoa(*(struct in_addr *)&addr)); } // GetLongPathName is in Windows 2000 only so make it for NT4 void my_GetLongPathName(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer) { LPSTR p_l; LPCSTR p_s; lpszLongPath[0] = lpszShortPath[0]; lpszLongPath[1] = lpszShortPath[1]; lpszLongPath[2] = lpszShortPath[2]; lpszLongPath[3] = '\0'; p_l = &lpszLongPath[3]; p_s = &lpszShortPath[3]; for (;;) { LPSTR p_s2; HANDLE search; WIN32_FIND_DATA ffd; size_t len; BOOL overflow; p_s2 = strchr(p_s, '\\'); if (p_s2 != NULL) len = p_s2 - p_s; else len = strlen(p_s); // check the buffer if (len + (p_l - lpszLongPath) >= cchBuffer) { // buffer overflow: copy and exit len = cchBuffer - (p_l - lpszLongPath) - 1; overflow = TRUE; } else overflow = FALSE; memcpy(p_l, p_s, len); p_l[len] = '\0'; if (overflow) break; search = FindFirstFile(lpszLongPath, &ffd); if (search != INVALID_HANDLE_VALUE) { len = strlen(ffd.cFileName); if (p_s2 != NULL) len++; // check the buffer if (len + (p_l - lpszLongPath) >= cchBuffer) { // buffer overflow: copy and exit len = cchBuffer - (p_l - lpszLongPath) - 1; p_s2 = NULL; } memcpy(p_l, ffd.cFileName, len); if (p_s2 != NULL) { p_l[len - 1] = '\\'; p_l[len] = '\0'; } else p_l[len] = '\0'; FindClose(search); } if (p_s2 == NULL) break; p_l += strlen(p_l); p_s = p_s2 + 1; } // ignore status }