void ot_cleanup_ote(struct ot_entry *ote) { struct ot_entry *saved_next; PFILE_OBJECT saved_fileobj; unsigned int i; // set all fields to zero except "next" and "fileobj" (cleanup listen/conn_entry if any) saved_next = ote->next; saved_fileobj = ote->fileobj; if (ote->type == FILEOBJ_ADDROBJ && ote->listen_entry != NULL) { del_listen_obj(ote->listen_entry, FALSE); } else if (ote->type == FILEOBJ_CONNOBJ && ote->conn_entry != NULL) { if (ote->ipproto == IPPROTO_TCP && ote->log_disconnect) log_disconnect(ote); del_tcp_conn_obj(ote->conn_entry, FALSE); } memset(ote, 0, sizeof(*ote)); ote->next = saved_next; ote->fileobj = saved_fileobj; // restore fileobjs for (i = 0; i < MAX_EVENT; i++) ote->ctx[i].fileobj = saved_fileobj; }
NTSTATUS ot_del_fileobj(PFILE_OBJECT fileobj, int *fileobj_type) { ULONG hash = CALC_HASH(fileobj); KIRQL irql; struct ot_entry *ote, *prev_ote; NTSTATUS status; if (fileobj == NULL) return STATUS_INVALID_PARAMETER_1; KeAcquireSpinLock(&g_ot_hash_guard, &irql); prev_ote = NULL; for (ote = g_ot_hash[hash]; ote; ote = ote->next) { if (ote->fileobj == fileobj) break; prev_ote = ote; } if (ote == NULL) { KdPrint(("[tdi_fw] ot_del_fileobj: fileobj 0x%x not found!\n", fileobj)); status = STATUS_OBJECT_NAME_NOT_FOUND; goto done; } if (ote->type == FILEOBJ_ADDROBJ && ote->listen_entry != NULL) del_listen_obj(ote->listen_entry, FALSE); else if (ote->type == FILEOBJ_CONNOBJ && ote->conn_entry != NULL) { if (ote->ipproto == IPPROTO_TCP && ote->log_disconnect) log_disconnect(ote); del_tcp_conn_obj(ote->conn_entry, FALSE); } if (fileobj_type != NULL) *fileobj_type = ote->type; if (prev_ote != NULL) prev_ote->next = ote->next; else g_ot_hash[hash] = ote->next; if (ote->sid_a != NULL) free(ote->sid_a); free(ote); status = STATUS_SUCCESS; done: KeReleaseSpinLock(&g_ot_hash_guard, irql); return status; }
NTSTATUS tdi_event_connect( IN PVOID TdiEventContext, IN LONG RemoteAddressLength, IN PVOID RemoteAddress, IN LONG UserDataLength, IN PVOID UserData, IN LONG OptionsLength, IN PVOID Options, OUT CONNECTION_CONTEXT *ConnectionContext, OUT PIRP *AcceptIrp) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)RemoteAddress)->Address, *local_addr; struct ot_entry *ote_addr = NULL, *ote_conn = NULL; KIRQL irql; struct flt_request request; struct flt_rule rule; int result = FILTER_DENY; NTSTATUS status; PIO_STACK_LOCATION irps = NULL; struct accept_param *param = NULL; memset(&request, 0, sizeof(request)); KdPrint(("[tdi_fw] tdi_event_connect: addrobj 0x%x\n", ctx->fileobj)); ote_addr = ot_find_fileobj(ctx->fileobj, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", ctx->fileobj)); goto done; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); KdPrint(("[tdi_fw] tdi_event_connect(pid:%u): %x:%u -> %x:%u\n", ote_addr->pid, ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port))); /* * request quick filter */ request.struct_size = sizeof(request); request.type = TYPE_CONNECT; request.direction = DIRECTION_IN; request.proto = IPPROTO_TCP; request.pid = ote_addr->pid; // get user SID & attributes! if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &remote_addr->AddressType, sizeof(struct sockaddr)); memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); // log request later if (result == FILTER_DENY) goto done; result = FILTER_DENY; // leave spinlock before calling original handler KeReleaseSpinLock(&g_ot_hash_guard, irql); ote_addr = NULL; /* * run original handler */ status = ((PTDI_IND_CONNECT)(ctx->old_handler)) (ctx->old_context, RemoteAddressLength, RemoteAddress, UserDataLength, UserData, OptionsLength, Options, ConnectionContext, AcceptIrp); if (status != STATUS_MORE_PROCESSING_REQUIRED || *AcceptIrp == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: status from original handler: 0x%x\n", status)); goto done; } /* * reinitialize connobj */ irps = IoGetCurrentIrpStackLocation(*AcceptIrp); KdPrint(("[tdi_fw] tdi_event_connect: connobj 0x%x\n", irps->FileObject)); // patch *AcceptIrp to change completion routine param = (struct accept_param *)malloc_np(sizeof(*param)); if (param == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: malloc_np!\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } param->old_cr = irps->CompletionRoutine; param->old_context = irps->Context; param->fileobj = irps->FileObject; param->old_control = irps->Control; // can't use IoSetCompletionRoutine because it uses next not current stack location irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; irps->CompletionRoutine = tdi_evconn_accept_complete; irps->Context = param; param = NULL; // find connobj for changing ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", irps->FileObject)); status = STATUS_OBJECT_NAME_NOT_FOUND; goto done; } ASSERT(ote_conn->type == FILEOBJ_CONNOBJ); // connobj must be associated with addrobj! if (ote_conn->associated_fileobj != ctx->fileobj) { KdPrint(("[tdi_fw] tdi_event_connect: 0x%x != 0x%x\n", ote_conn->associated_fileobj, ctx->fileobj)); status = STATUS_INVALID_PARAMETER; goto done; } // change conn_ctx (if needed) if (ote_conn->conn_ctx != *ConnectionContext) { // update (conn_ctx, addrobj)->connobj status = ot_del_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: ot_del_conn_ctx: 0x%x\n", status)); goto done; } ote_conn->conn_ctx = *ConnectionContext; status = ot_add_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx, irps->FileObject); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: ot_add_conn_ctx: 0x%x\n", status)); goto done; } } // clear listen & conn entries in connobj (fileobject can be reused) ASSERT(ote_conn->listen_entry == NULL); if (ote_conn->listen_entry != NULL) del_listen_obj(ote_conn->listen_entry, FALSE); // free build case if (ote_conn->conn_entry != NULL) { if (ote_conn->ipproto == IPPROTO_TCP && ote_conn->log_disconnect) log_disconnect(ote_conn); del_tcp_conn_obj(ote_conn->conn_entry, FALSE); } // clear bytes count ote_conn->bytes_in = ote_conn->bytes_out = 0; // setup log_disconnect flag from rule ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); // sanity check if (local_addr->AddressLength != remote_addr->AddressLength) { KdPrint(("[tdi_fw] tdi_event_connect: different addr lengths! (%u != %u)\n", local_addr->AddressLength, remote_addr->AddressLength)); status = STATUS_INFO_LENGTH_MISMATCH; goto done; } // associate remote address with connobj if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", remote_addr->AddressLength)); status = STATUS_BUFFER_TOO_SMALL; goto done; } memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); // associate local address with connobj if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", local_addr->AddressLength)); status = STATUS_BUFFER_TOO_SMALL; goto done; } memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); // create connection with "SYN_RCVD" state status = add_tcp_conn(ote_conn, TCP_STATE_SYN_RCVD); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: add_tcp_conn: 0x%x\n", status)); goto done; } result = FILTER_ALLOW; done: // if logging is needed log request if (rule.log >= RULE_LOG_LOG) { if (result != FILTER_ALLOW && rule.result == FILTER_ALLOW) { request.type = TYPE_CONNECT_ERROR; // error has been occured request.status = status; } log_request(&request); } if (result != FILTER_ALLOW) { // deny incoming connection KdPrint(("[tdi_fw] tdi_event_connect: deny on reason 0x%x\n", status)); if (irps != NULL) { // delete connection if (ote_conn != NULL && ote_conn->conn_entry != NULL) { del_tcp_conn_obj(ote_conn->conn_entry, FALSE); ote_conn->conn_entry = NULL; } // release spinlock before IoCompleteRequest to avoid completion call inside spinlock if (ote_addr != NULL || ote_conn != NULL) { KeReleaseSpinLock(&g_ot_hash_guard, irql); ote_addr = NULL; ote_conn = NULL; } // destroy accepted IRP (*AcceptIrp)->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest(*AcceptIrp, IO_NO_INCREMENT); } *AcceptIrp = NULL; status = STATUS_CONNECTION_REFUSED; } else status = STATUS_MORE_PROCESSING_REQUIRED; // cleanup if (ote_addr != NULL || ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (param != NULL) free(param); if (request.sid_a != NULL) free(request.sid_a); return status; }
int tdi_set_event_handler(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { PTDI_REQUEST_KERNEL_SET_EVENT r = (PTDI_REQUEST_KERNEL_SET_EVENT)&irps->Parameters; NTSTATUS status; struct ot_entry *ote_addr = NULL; KIRQL irql; int result = FILTER_DENY; TDI_EVENT_CONTEXT *ctx; KdPrint(("[tdi_fw] tdi_set_event_handler: [%s] devobj 0x%x; addrobj 0x%x; EventType: %d\n", r->EventHandler ? "(+)ADD" : "(-)REMOVE", irps->DeviceObject, irps->FileObject, r->EventType)); ote_addr = ot_find_fileobj(irps->FileObject, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_set_event_handler: ot_find_fileobj(0x%x)\n", irps->FileObject)); if (r->EventHandler == NULL) { // for fileobjects loaded earlier than our driver allow removing result = FILTER_ALLOW; } goto done; } if (r->EventType < 0 || r->EventType >= MAX_EVENT) { KdPrint(("[tdi_fw] tdi_set_event_handler: unknown EventType %d!\n", r->EventType)); result = FILTER_ALLOW; goto done; } ctx = &ote_addr->ctx[r->EventType]; if (r->EventHandler != NULL) { /* add EventHandler */ int i; for (i = 0; g_tdi_event_handlers[i].event != (ULONG)-1; i++) if (g_tdi_event_handlers[i].event == r->EventType) break; if (g_tdi_event_handlers[i].event == (ULONG)-1) { KdPrint(("[tdi_fw] tdi_set_event_handler: unknown EventType %d!\n", r->EventType)); result = FILTER_ALLOW; goto done; } ctx->old_handler = r->EventHandler; ctx->old_context = r->EventContext; if (g_tdi_event_handlers[i].handler != NULL) { r->EventHandler = g_tdi_event_handlers[i].handler; r->EventContext = ctx; } else { r->EventHandler = NULL; r->EventContext = NULL; } KdPrint(("[tdi_fw] tdi_set_event_handler: old_handler 0x%x; old_context 0x%x\n", r->EventHandler, r->EventContext)); } else { /* remove EventHandler */ ctx->old_handler = NULL; ctx->old_context = NULL; } // change LISTEN state if (r->EventType == TDI_EVENT_CONNECT) { TA_ADDRESS *local_addr; if (r->EventHandler != NULL) { // add "LISTEN" info status = add_listen(ote_addr); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_set_event_handler: add_listen: 0x%x!\n", status)); goto done; } } else if (ote_addr->listen_entry != NULL) { // remove "LISTEN" info del_listen_obj(ote_addr->listen_entry, FALSE); ote_addr->listen_entry = NULL; } // log it if address is not 127.0.0.1 local_addr = (TA_ADDRESS *)(ote_addr->local_addr); if (ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr) != 0x7f000001) { struct flt_request request; memset(&request, 0, sizeof(request)); request.struct_size = sizeof(request); request.type = (r->EventHandler != NULL) ? TYPE_LISTEN : TYPE_NOT_LISTEN; request.proto = IPPROTO_TCP; // correct? if (r->EventHandler != NULL) { // for removing event handler ProcessNotifyProc can be already called request.pid = (ULONG)PsGetCurrentProcessId(); if (request.pid == 0) { // avoid idle process pid (XXX do we need this?) request.pid = ote_addr->pid; } } else request.pid = (ULONG)-1; // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); log_request(&request); if (request.sid_a != NULL) free(request.sid_a); } } result = FILTER_ALLOW; done: // cleanup if (ote_addr != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); return result; }
void ot_free(void) { KIRQL irql; int i; struct ot_entry *ote; if (g_ot_hash == NULL) return; // have nothing to do // do cleanup for address & connection objects for (i = 0; i < HASH_SIZE; i++) { for (;;) { // do it again and again PFILE_OBJECT connobj = NULL, event_addrobj; PVOID event_handler = NULL, event_context; int event_type; PDEVICE_OBJECT devobj; KeAcquireSpinLock(&g_ot_hash_guard, &irql); for (ote = g_ot_hash[i]; ote != NULL; ote = ote->next) { if (ote->fileobj == NULL) continue; // skip processed items #ifdef _USE_TDI_HOOKING devobj = ote->devobj; #else devobj = get_original_devobj(ote->devobj, NULL); #endif if (ote->type == FILEOBJ_ADDROBJ) { // find at least one event handler & remove it @ PASSIVE level int j; event_addrobj = ote->fileobj; for (j = 0; j < MAX_EVENT; j++) if (ote->ctx[j].old_handler != NULL) { event_type = j; event_handler = ote->ctx[j].old_handler; event_context = ote->ctx[j].old_context; KdPrint(("[tdi_fw] ot_free: got event handler to restore (addrobj: 0x%x; type: %d; handler: 0x%x; context: 0x%x\n", event_addrobj, event_type, event_handler, event_context)); ote->ctx[j].old_handler = NULL; break; } if (event_handler != NULL) break; KdPrint(("[tdi_fw] ot_free: no event handlers for addrobj: 0x%x\n", ote->fileobj)); // remove this addrobj from "LISTEN" state if (ote->listen_entry != NULL) { del_listen_obj(ote->listen_entry, FALSE); ote->listen_entry = NULL; } // no event handlers ote->fileobj = NULL; } else if (ote->type == FILEOBJ_CONNOBJ) { // check connobj is connected (remote addr is not 0) TA_ADDRESS *remote_addr = (TA_ADDRESS *)(ote->remote_addr); if (((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr == 0) { KdPrint(("[tdi_fw] ot_free: connobj 0x%x is not connected\n", connobj)); } else { // disconnect this connection using TDI_DISCONNECT @ PASSIVE level connobj = ote->fileobj; KdPrint(("[tdi_fw] ot_free: got connobj 0x%x (%x:%x) to disconnect\n", connobj, ((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr, ((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port)); } // remove this connobj from "CONNECTED" state if (ote->conn_entry != NULL) { del_tcp_conn_obj(ote->conn_entry, FALSE); ote->conn_entry = NULL; // TODO: check if state TCP_STATE_TIME_WAIT don't delete it } // skip this object next time ote->fileobj = NULL; } } KeReleaseSpinLock(&g_ot_hash_guard, irql); // we're at PASSIVE level if (event_handler != NULL) { // set old event handler PIRP query_irp; NTSTATUS status; KdPrint(("[tdi_fw] ot_free: got event handler to restore (addrobj: 0x%x; type: %d; handler: 0x%x; context: 0x%x\n", event_addrobj, event_type, event_handler, event_context)); query_irp = TdiBuildInternalDeviceControlIrp(TDI_SET_EVENT_HANDLER, devobj, event_addrobj, NULL, NULL); if (query_irp == NULL) { KdPrint(("[tdi_fw] ot_free: TdiBuildInternalDeviceControlIrp\n")); continue; } TdiBuildSetEventHandler(query_irp, devobj, event_addrobj, NULL, NULL, event_type, event_handler, event_context); status = IoCallDriver(devobj, query_irp); if (status != STATUS_SUCCESS) KdPrint(("[tdi_fw] ot_free: IoCallDriver(set_event_handler): 0x%x\n", status)); } else if (connobj != NULL) { // disconnect connection PIRP query_irp; NTSTATUS status; KdPrint(("[tdi_fw] ot_free: disconnecting connobj 0x%x\n", connobj)); query_irp = TdiBuildInternalDeviceControlIrp(TDI_DISCONNECT, devobj, connobj, NULL, NULL); if (query_irp == NULL) { KdPrint(("[tdi_fw] ot_free: TdiBuildInternalDeviceControlIrp\n")); continue; } // using TDI_DISCONNECT_RELEASE to make ClientEventDisconnect to be called TdiBuildDisconnect(query_irp, devobj, connobj, NULL, NULL, NULL, TDI_DISCONNECT_RELEASE, NULL, NULL); status = IoCallDriver(devobj, query_irp); if (status != STATUS_SUCCESS) KdPrint(("[tdi_fw] ot_free: IoCallDriver(disconnect): 0x%x\n", status)); } else { // no objects to process. break! break; } } } // clear it! KeAcquireSpinLock(&g_ot_hash_guard, &irql); for (i = 0; i < HASH_SIZE; i++) { struct ot_entry *ote = g_ot_hash[i]; while (ote != NULL) { struct ot_entry *ote2 = ote->next; KdPrint(("[tdi_fw] ot_free: Warning! fileobj 0x%x type %d exists!\n", ote->fileobj, ote->type)); if (ote->sid_a != NULL) free(ote->sid_a); free(ote); ote = ote2; } } free(g_ot_hash); g_ot_hash = NULL; KeReleaseSpinLock(&g_ot_hash_guard, irql); // and cleanup cte_hash if (g_cte_hash != NULL) { KeAcquireSpinLock(&g_cte_hash_guard, &irql); for (i = 0; i < HASH_SIZE; i++) { struct ctx_entry *cte = g_cte_hash[i]; while (cte) { struct ctx_entry *cte2 = cte->next; free(cte); cte = cte2; } } free(g_cte_hash); g_cte_hash = NULL; KeReleaseSpinLock(&g_cte_hash_guard, irql); } }