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; }
/* * 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; }