Пример #1
0
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;

}
Пример #2
0
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;
}
Пример #3
0
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;
}
Пример #4
0
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;
}
Пример #5
0
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);
	}
}