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; }
int tdi_connect(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { PTDI_REQUEST_KERNEL_CONNECT param = (PTDI_REQUEST_KERNEL_CONNECT)(&irps->Parameters); TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)(param->RequestConnectionInformation->RemoteAddress))->Address; PFILE_OBJECT addrobj; NTSTATUS status; TA_ADDRESS *local_addr; int result = FILTER_DENY, ipproto; struct ot_entry *ote_conn = NULL, *ote_addr; KIRQL irql; struct flt_request request; struct flt_rule rule; memset(&request, 0, sizeof(request)); KdPrint(("[tdi_fw] tdi_connect: connobj 0x%x, to address %x:%u\n", irps->FileObject, ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port))); // check device object: TCP or UDP if (irps->DeviceObject != g_tcpfltobj && irps->DeviceObject != g_udpfltobj) { KdPrint(("[tdi_fw] tdi_connect: unknown DeviceObject 0x%x!\n", irps->DeviceObject)); goto done; } ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", irps->FileObject)); goto done; } if (get_original_devobj(irps->DeviceObject, &ipproto) == NULL || (ipproto != IPPROTO_TCP && ipproto != IPPROTO_UDP)) { // invalid device object! KdPrint(("[tdi_fw] tdi_connect: invalid device object 0x%x!\n", irps->DeviceObject)); goto done; } if (ipproto == IPPROTO_TCP) { /* * For TCP: get addrobj by connobj and get local address by it */ addrobj = ote_conn->associated_fileobj; if (addrobj == NULL) { KdPrint(("[tdi_fw] tdi_connect: empty addrobj!\n")); goto done; } ote_addr = ot_find_fileobj(addrobj, NULL); // we're already in spinlock if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", addrobj)); goto done; } } else { /* * For UDP: connobj and addrobj are the same */ KdPrint(("[tdi_fw] tdi_connect: connected UDP socket detected\n")); // for connected UDP sockets connobj and addrobj are the same addrobj= irps->FileObject; ote_addr = ote_conn; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); // sanity check if (local_addr->AddressLength != remote_addr->AddressLength) { KdPrint(("[tdi_fw] tdi_connect: different addr lengths! (%u != %u)\n", local_addr->AddressLength, remote_addr->AddressLength)); goto done; } // set remote address with connobj if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { KdPrint(("[tdi_fw] tdi_connect: address too long! (%u)\n", remote_addr->AddressLength)); goto done; } memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); // set local address with connobj if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { KdPrint(("[tdi_fw] tdi_connect: address to long! (%u)\n", local_addr->AddressLength)); goto done; } memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); KdPrint(("[tdi_fw] tdi_connect(pid:%u/%u): %x:%u -> %x:%u (ipproto = %d)\n", ote_conn->pid, PsGetCurrentProcessId(), ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port), ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ipproto)); /* * Call quick_filter */ request.struct_size = sizeof(request); request.type = TYPE_CONNECT; request.direction = DIRECTION_OUT; request.proto = ipproto; // don't use ote_conn->pid because one process can create connection object // but another one can connect request.pid = (ULONG)PsGetCurrentProcessId(); if (request.pid == 0) { // avoid idle process pid (XXX do we need this?) request.pid = ote_addr->pid; } // 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)); memcpy(&request.addr.to, &remote_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); memset(&rule, 0, sizeof(rule)); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); if (result == FILTER_ALLOW && ipproto == IPPROTO_TCP) { struct flt_request *context_req = NULL; // add connection with state "SYN_SENT" status = add_tcp_conn(ote_conn, TCP_STATE_SYN_SENT); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_connect: add_conn: 0x%x!\n", status)); result = FILTER_DENY; goto done; // don't log this failure } if (rule.log >= RULE_LOG_LOG) { // set ote_conn->log_disconnect ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); // copy request for completion (LOG success or not) context_req = (struct flt_request *)malloc_np(sizeof(*context_req)); if (context_req != NULL) { memcpy(context_req, &request, sizeof(*context_req)); // don't free SID request.sid_a = NULL; // don't log request in this time rule.log = RULE_LOG_NOLOG; } } // set completion to add connection info to connection table completion->routine = tdi_connect_complete; completion->context = context_req; } // if logging is needed log request if (rule.log >= RULE_LOG_LOG) log_request(&request); done: // cleanup if (ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (request.sid_a != NULL) free(request.sid_a); if (result != FILTER_ALLOW) { irp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING; // set fake status } return result; }
NTSTATUS tdi_event_receive_datagram( IN PVOID TdiEventContext, IN LONG SourceAddressLength, IN PVOID SourceAddress, IN LONG OptionsLength, IN PVOID Options, IN ULONG ReceiveDatagramFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; struct ot_entry *ote_addr = NULL; KIRQL irql; TA_ADDRESS *remote_addr, *local_addr; NTSTATUS status; int ipproto, result = FILTER_DENY; struct flt_request request; struct flt_rule rule; // get local address of address object memset(&request, 0, sizeof(request)); ote_addr = ot_find_fileobj(ctx->fileobj, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_receive_datagram: ot_find_fileobj(0x%x)!\n", ctx->fileobj)); goto done; } KdPrint(("[tdi_fw] tdi_event_receive_datagram: addrobj 0x%x\n", ctx->fileobj)); // check device object: UDP or RawIP if (get_original_devobj(ote_addr->devobj, &ipproto) == NULL || (ipproto != IPPROTO_UDP && ipproto != IPPROTO_IP)) { // unknown device object! KdPrint(("[tdi_fw] tdi_event_receive_datagram: unknown DeviceObject 0x%x!\n", ote_addr)); goto done; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); remote_addr = ((TRANSPORT_ADDRESS *)SourceAddress)->Address; KdPrint(("[tdi_fw] tdi_event_receive_datagram(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))); // call quick filter for datagram request.struct_size = sizeof(request); request.type = TYPE_DATAGRAM; request.direction = DIRECTION_IN; request.proto = ipproto; request.pid = ote_addr->pid; // 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, &remote_addr->AddressType, sizeof(struct sockaddr)); memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); memset(&rule, 0, sizeof(rule)); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); if (rule.log >= RULE_LOG_LOG) { ULONG bytes = BytesAvailable; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_IN] += bytes; if (rule.log >= RULE_LOG_COUNT) { request.log_bytes_in = bytes; g_traffic[TRAFFIC_COUNTED_IN] += bytes; } else request.log_bytes_in = (ULONG)-1; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); log_request(&request); } done: // cleanup if (ote_addr != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (request.sid_a != NULL) free(request.sid_a); if (result == FILTER_ALLOW) { return ((PTDI_IND_RECEIVE_DATAGRAM)(ctx->old_handler)) (ctx->old_context, SourceAddressLength, SourceAddress, OptionsLength, Options, ReceiveDatagramFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket); } else return STATUS_DATA_NOT_ACCEPTED; }