Ejemplo n.º 1
0
NTKERNELAPI
NTSTATUS
NTAPI
FsRtlValidateReparsePointBuffer(IN ULONG BufferLength,
                                IN PREPARSE_DATA_BUFFER ReparseBuffer)
{
    USHORT DataLength;
    ULONG ReparseTag;
    PREPARSE_GUID_DATA_BUFFER GuidBuffer;

    /* Validate data size range */
    if (BufferLength < REPARSE_DATA_BUFFER_HEADER_SIZE || BufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
    {
        return STATUS_IO_REPARSE_DATA_INVALID;
    }

    GuidBuffer = (PREPARSE_GUID_DATA_BUFFER)ReparseBuffer;
    DataLength = ReparseBuffer->ReparseDataLength;
    ReparseTag = ReparseBuffer->ReparseTag;

    /* Validate size consistency */
    if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE != BufferLength && DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE != BufferLength)
    {
        return STATUS_IO_REPARSE_DATA_INVALID;
    }

    /* REPARSE_DATA_BUFFER is reserved for MS tags */
    if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE == BufferLength && !IsReparseTagMicrosoft(ReparseTag))
    {
        return STATUS_IO_REPARSE_DATA_INVALID;
    }

    /* If that a GUID data buffer, its GUID cannot be null, and it cannot contain a MS tag */
    if (DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE == BufferLength && ((!IsReparseTagMicrosoft(ReparseTag)
        && IsNullGuid(&GuidBuffer->ReparseGuid)) || (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT || ReparseTag == IO_REPARSE_TAG_SYMLINK)))
    {
        return STATUS_IO_REPARSE_DATA_INVALID;
    }

    /* Check the data for MS non reserved tags */
    if (!(ReparseTag & 0xFFF0000) && ReparseTag != IO_REPARSE_TAG_RESERVED_ZERO && ReparseTag != IO_REPARSE_TAG_RESERVED_ONE)
    {
        /* If that's a mount point, validate the MountPointReparseBuffer branch */
        if (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
        {
            /* We need information */
            if (DataLength >= REPARSE_DATA_BUFFER_HEADER_SIZE)
            {
                /* Substitue must be the first in row */
                if (!ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset)
                {
                    /* Substitude must be null-terminated */
                    if (ReparseBuffer->MountPointReparseBuffer.PrintNameOffset == ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL))
                    {
                        /* There must just be the Offset/Length fields + buffer + 2 null chars */
                        if (DataLength == ReparseBuffer->MountPointReparseBuffer.PrintNameLength + ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.SubstituteNameOffset)) + 2 * sizeof(UNICODE_NULL))
                        {
                            return STATUS_SUCCESS;
                        }
                    }
                }
            }
        }
        else
        {
#define FIELDS_SIZE (FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset))

            /* If that's not a symlink, accept the MS tag as it */
            if (ReparseTag != IO_REPARSE_TAG_SYMLINK)
            {
                return STATUS_SUCCESS;
            }

            /* We need information */
            if (DataLength >= FIELDS_SIZE)
            {
                /* Validate lengths */
                if (ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength && ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength)
                {
                    /* Validate unicode strings */
                    if (IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength) &&
                        IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset))
                    {
                        if ((DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset + ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE)
                            && (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength + ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE))
                        {
                            return STATUS_SUCCESS;
                        }
                    }
                }
            }
#undef FIELDS_SIZE
        }

        return STATUS_IO_REPARSE_DATA_INVALID;
    }

    return STATUS_IO_REPARSE_TAG_INVALID;
}
Ejemplo n.º 2
0
/*
 * Perform RPC client bind. Accepts a connected client socket.
 * Returns 0 on success. RPC binding is required before any payload can be
 * exchanged. It negotiates about protocol details.
 */
static RpcStatus rpcBindOrAlterClientContext(const RpcCtx sock, const BYTE packetType, const int_fast8_t verbose)
{
	RPC_HEADER *RequestHeader, ResponseHeader;
	RPC_BIND_REQUEST *bindRequest;
	RPC_BIND_RESPONSE *bindResponse;
	int status;
	WORD ctxItems = 1 + (packetType == RPC_PT_BIND_REQ ? UseClientRpcNDR64 + UseClientRpcBTFN : 0);
	size_t rpcBindSize = (sizeof(RPC_HEADER) + sizeof(RPC_BIND_REQUEST) + (ctxItems - 1) * sizeof(bindRequest->CtxItems[0]));
	WORD ctxIndex = 0;
	WORD i;
	WORD CtxBTFN = RPC_INVALID_CTX, CtxNDR64 = RPC_INVALID_CTX;
	BYTE* request = (BYTE*)alloca(rpcBindSize);

	RequestHeader = (RPC_HEADER*)request;
	bindRequest = (RPC_BIND_REQUEST*)(request + sizeof(RPC_HEADER));

	createRpcHeader(RequestHeader, packetType, (WORD)rpcBindSize);
	RequestHeader->PacketFlags |= UseMultiplexedRpc ? RPC_PF_MULTIPLEX : 0;

	bindRequest->AssocGroup = 0;
	bindRequest->MaxRecvFrag = bindRequest->MaxXmitFrag = LE16(5840);
	bindRequest->NumCtxItems = LE32(ctxItems);

	// data that is identical in all Ctx items
	for (i = 0; i < ctxItems; i++)
	{
		struct CtxItem* ctxItem = bindRequest->CtxItems + i;
		ctxItem->ContextId = LE16(i);
		ctxItem->InterfaceVerMajor = LE16(1);
		ctxItem->InterfaceVerMinor = 0;
		ctxItem->NumTransItems = LE16(1);
		ctxItem->SyntaxVersion = i ? LE32(1) : LE32(2);

		memcpy(&ctxItem->InterfaceUUID, InterfaceUuid, sizeof(GUID));
	}

	memcpy(&bindRequest->CtxItems[0].TransferSyntax, TransferSyntaxNDR32, sizeof(GUID));

	if (UseClientRpcNDR64 && packetType == RPC_PT_BIND_REQ)
	{
		memcpy(&bindRequest->CtxItems[++ctxIndex].TransferSyntax, TransferSyntaxNDR64, sizeof(GUID));
		CtxNDR64 = ctxIndex;
	}

	if (UseClientRpcBTFN && packetType == RPC_PT_BIND_REQ)
	{
		memcpy(&bindRequest->CtxItems[++ctxIndex].TransferSyntax, BindTimeFeatureNegotiation, sizeof(GUID));
		CtxBTFN = ctxIndex;
	}

	if (!_send(sock, request, (int)rpcBindSize))
	{
		printerrorf("\nFatal: Sending RPC bind request failed\n");
		return RPC_S_COMM_FAILURE;
	}

	if (!_recv(sock, &ResponseHeader, sizeof(RPC_HEADER)))
	{
		printerrorf("\nFatal: Did not receive a response from server\n");
		return RPC_S_COMM_FAILURE;
	}

	if ((status = checkRpcResponseHeader
	(
		&ResponseHeader,
		RequestHeader,
		packetType == RPC_PT_BIND_REQ ? RPC_PT_BIND_ACK : RPC_PT_ALTERCONTEXT_ACK,
		&printerrorf
	)))
	{
		return status;
	}

	bindResponse = (RPC_BIND_RESPONSE*)vlmcsd_malloc(LE16(ResponseHeader.FragLength) - sizeof(RPC_HEADER));
	BYTE* bindResponseBytePtr = (BYTE*)bindResponse;

	if (!_recv(sock, bindResponse, LE16(ResponseHeader.FragLength) - sizeof(RPC_HEADER)))
	{
		printerrorf("\nFatal: Incomplete RPC bind acknowledgement received\n");
		free(bindResponseBytePtr);
		return RPC_S_COMM_FAILURE;
	}

	/*
	 * checking, whether a bind or alter context response is as expected.
	 * This check is very strict and checks whether a KMS emulator behaves exactly the same way
	 * as Microsoft's RPC does.
	 */
	status = 0;

	if (bindResponse->SecondaryAddressLength < LE16(3))
		bindResponse = (RPC_BIND_RESPONSE*)(bindResponseBytePtr - 4);

	if (bindResponse->NumResults != bindRequest->NumCtxItems)
	{
		printerrorf("\nFatal: Expected %u CTX items but got %u\n",
			(uint32_t)LE32(bindRequest->NumCtxItems),
			(uint32_t)LE32(bindResponse->NumResults)
		);

		status = RPC_S_PROTOCOL_ERROR;
	}

	for (i = 0; i < ctxItems; i++)
	{
		const char* transferSyntaxName =
			i == CtxBTFN ? "BTFN" : i == CtxNDR64 ? "NDR64" : "NDR32";

		struct CtxResults* ctxResult = bindResponse->Results + i;
		struct CtxItem* ctxItem = bindRequest->CtxItems + i;
		if (ctxResult->AckResult == RPC_BIND_NACK) // transfer syntax was declined
		{
			if (!IsNullGuid((BYTE*)&ctxResult->TransferSyntax))
			{
				printerrorf(
					"\nWarning: Rejected transfer syntax %s did not return NULL Guid\n",
					transferSyntaxName
				);
			}

			if (ctxResult->SyntaxVersion)
			{
				printerrorf(
					"\nWarning: Rejected transfer syntax %s did not return syntax version 0 but %u\n",
					transferSyntaxName,
					LE32(ctxResult->SyntaxVersion)
				);
			}

			if (ctxResult->AckReason == RPC_ABSTRACTSYNTAX_UNSUPPORTED)
			{
				printerrorf(
					"\nWarning: Transfer syntax %s does not support KMS activation\n",
					transferSyntaxName
				);
			}
			else if (ctxResult->AckReason != RPC_SYNTAX_UNSUPPORTED)
			{
				printerrorf(
					"\nWarning: Rejected transfer syntax %s did not return ack reason RPC_SYNTAX_UNSUPPORTED\n",
					transferSyntaxName
				);
			}

			continue;
		}

		if (i == CtxBTFN) // BTFN
		{
			if (ctxResult->AckResult != RPC_BIND_ACK)
			{
				printerrorf("\nWarning: BTFN did not respond with RPC_BIND_ACK or RPC_BIND_NACK\n");
			}

			if (ctxResult->AckReason != LE16(3))
			{
				printerrorf("\nWarning: BTFN did not return expected feature mask 0x3 but 0x%X\n", (unsigned int)LE16(ctxResult->AckReason));
			}

			if (verbose) printf("... BTFN ");
			RpcFlags.HasBTFN = TRUE;

			continue;
		}

		// NDR32 or NDR64 Ctx
		if (ctxResult->AckResult != RPC_BIND_ACCEPT)
		{
			printerrorf(
				"\nFatal: transfer syntax %s returned an invalid status, neither RPC_BIND_ACCEPT nor RPC_BIND_NACK\n",
				transferSyntaxName
			);

			status = RPC_S_PROTOCOL_ERROR;
		}

		if (!IsEqualGUID(&ctxResult->TransferSyntax, &ctxItem->TransferSyntax))
		{
			printerrorf(
				"\nFatal: Transfer syntax of RPC bind request and response does not match\n"
			);

			status = RPC_S_UNSUPPORTED_TRANS_SYN;
		}

		if (ctxResult->SyntaxVersion != ctxItem->SyntaxVersion)
		{
			printerrorf("\nFatal: Expected transfer syntax version %u for %s but got %u\n",
				(uint32_t)LE32(ctxItem->SyntaxVersion),
				transferSyntaxName,
				(uint32_t)LE32(ctxResult->SyntaxVersion)
			);

			status = RPC_S_UNSUPPORTED_TRANS_SYN;
		}

		// The ack reason field is actually undefined here but Microsoft sets this to 0
		if (ctxResult->AckReason != 0)
		{
			printerrorf(
				"\nWarning: Ack reason should be 0 but is %u\n",
				LE16(ctxResult->AckReason)
			);
		}

		if (!status)
		{
			if (i == CtxNDR64)
			{
				RpcFlags.HasNDR64 = TRUE;
				if (verbose) printf("... NDR64 ");
			}
			if (!i)
			{
				RpcFlags.HasNDR32 = TRUE;
				if (verbose) printf("... NDR32 ");
			}

		}
	}

	free(bindResponseBytePtr);

	if (!RpcFlags.HasNDR64 && !RpcFlags.HasNDR32)
	{
		printerrorf("\nFatal: Could neither negotiate NDR32 nor NDR64 with the RPC server\n");
		status = RPC_S_NO_PROTSEQS;
	}

	return status;
}