You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1813 lines
55 KiB
1813 lines
55 KiB
#include "nt.h"
|
|
#include "ntdef.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "sxs-rtl.h"
|
|
#include "fasterxml.h"
|
|
#include "skiplist.h"
|
|
#include "namespacemanager.h"
|
|
#include "xmlstructure.h"
|
|
#include "xmlassert.h"
|
|
#include "manifestinspection.h"
|
|
#include "analyzerxmldsig.h"
|
|
#include "manifestcooked.h"
|
|
#include "ntrtlstringandbuffer.h"
|
|
#include "stdlib.h"
|
|
#include "limits.h"
|
|
|
|
NTSTATUS
|
|
RtlpValidateXmlDeclaration(
|
|
PXML_TOKENIZATION_STATE pState,
|
|
PXMLDOC_THING pDocThing
|
|
);
|
|
|
|
//
|
|
// Some strings that we'll need later
|
|
//
|
|
const XML_SPECIAL_STRING sc_ss_xmldecl_version_10 = MAKE_SPECIAL_STRING("1.0");
|
|
const XML_SPECIAL_STRING sc_ss_xmldecl_yes = MAKE_SPECIAL_STRING("yes");
|
|
const XML_SPECIAL_STRING sc_ss_xmlnamespace_default = MAKE_SPECIAL_STRING("urn:schemas-microsoft-com:asm.v1");
|
|
|
|
|
|
NTSTATUS
|
|
Rtl_InspectManifest_AssemblyIdentity(
|
|
PXML_LOGICAL_STATE pLogicalState,
|
|
PRTL_MANIFEST_CONTENT_RAW pManifestContent,
|
|
PXMLDOC_THING pDocumentThing,
|
|
PRTL_GROWING_LIST pAttributes,
|
|
MANIFEST_ELEMENT_CALLBACK_REASON Reason,
|
|
const struct _XML_ELEMENT_DEFINITION *pElementDefinition
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
Rtl_InspectManifest_Assembly(
|
|
PXML_LOGICAL_STATE pLogicalState,
|
|
PRTL_MANIFEST_CONTENT_RAW pManifestContent,
|
|
PXMLDOC_THING pDocumentThing,
|
|
PRTL_GROWING_LIST pAttributes,
|
|
MANIFEST_ELEMENT_CALLBACK_REASON Reason,
|
|
const struct _XML_ELEMENT_DEFINITION *pElementDefinition
|
|
);
|
|
|
|
NTSTATUS
|
|
Rtl_InspectManifest_File(
|
|
PXML_LOGICAL_STATE pLogicalState,
|
|
PRTL_MANIFEST_CONTENT_RAW pManifestContent,
|
|
PXMLDOC_THING pDocumentThing,
|
|
PRTL_GROWING_LIST pAttributes,
|
|
MANIFEST_ELEMENT_CALLBACK_REASON Reason,
|
|
const struct _XML_ELEMENT_DEFINITION *pElementDefinition
|
|
);
|
|
|
|
|
|
DECLARE_ELEMENT(assembly);
|
|
DECLARE_ELEMENT(assembly_file);
|
|
DECLARE_ELEMENT(assembly_assemblyIdentity);
|
|
DECLARE_ELEMENT(assembly_description);
|
|
|
|
|
|
//
|
|
// The "assembly" root document element
|
|
//
|
|
enum {
|
|
eAttribs_assembly_manifestVersion = 0,
|
|
eAttribs_assembly_Count
|
|
};
|
|
|
|
|
|
XML_ELEMENT_DEFINITION rgs_Element_assembly =
|
|
{
|
|
XML_ELEMENT_FLAG_ALLOW_ANY_CHILDREN,
|
|
eManifestState_assembly,
|
|
NULL,
|
|
&sc_ss_xmlnamespace_default,
|
|
MAKE_SPECIAL_STRING("assembly"),
|
|
&Rtl_InspectManifest_Assembly,
|
|
rgs_Element_assembly_Children,
|
|
eAttribs_assembly_Count,
|
|
{
|
|
{ XML_ATTRIBUTE_FLAG_REQUIRED, NULL, MAKE_SPECIAL_STRING("manifestVersion") },
|
|
}
|
|
};
|
|
|
|
PCXML_ELEMENT_DEFINITION rgs_Element_assembly_Children[] = {
|
|
ELEMENT_NAMED(assembly_file),
|
|
ELEMENT_NAMED(assembly_assemblyIdentity),
|
|
NULL
|
|
};
|
|
|
|
//
|
|
// The "file" element
|
|
//
|
|
enum {
|
|
eAttribs_assembly_file_digestMethod,
|
|
eAttribs_assembly_file_hash,
|
|
eAttribs_assembly_file_hashalg,
|
|
eAttribs_assembly_file_loadFrom,
|
|
eAttribs_assembly_file_name,
|
|
eAttribs_assembly_file_size,
|
|
eAttribs_assembly_file_Count
|
|
};
|
|
|
|
ELEMENT_DEFINITION_DEFNS(assembly, file, Rtl_InspectManifest_File, XML_ELEMENT_FLAG_ALLOW_ANY_CHILDREN)
|
|
ATTRIBUTE_DEFINITION_NONS_NODEFAULT(digestMethod),
|
|
ATTRIBUTE_DEFINITION_NONS_NODEFAULT(hash),
|
|
ATTRIBUTE_DEFINITION_NONS_NODEFAULT(hashalg),
|
|
ATTRIBUTE_DEFINITION_NONS_NODEFAULT(loadFrom),
|
|
ATTRIBUTE_DEFINITION_NONS_NODEFAULT(name),
|
|
ATTRIBUTE_DEFINITION_NONS_NODEFAULT(size),
|
|
ELEMENT_DEFINITION_DEFNS_END();
|
|
|
|
ELEMENT_DEFINITION_CHILD_ELEMENTS(assembly, file)
|
|
ELEMENT_DEFINITION_CHILD_ELEMENTS_END();
|
|
|
|
int unscrew_si[] = {3};
|
|
|
|
//
|
|
// Assembly identities
|
|
//
|
|
enum {
|
|
eAttribs_assembly_assemblyIdentity_language = 0,
|
|
eAttribs_assembly_assemblyIdentity_name,
|
|
eAttribs_assembly_assemblyIdentity_processorArchitecture,
|
|
eAttribs_assembly_assemblyIdentity_publicKeyToken,
|
|
eAttribs_assembly_assemblyIdentity_type,
|
|
eAttribs_assembly_assemblyIdentity_version,
|
|
eAttribs_assembly_assemblyIdentity_Count
|
|
};
|
|
|
|
ELEMENT_DEFINITION_DEFNS(assembly, assemblyIdentity, Rtl_InspectManifest_AssemblyIdentity, XML_ELEMENT_FLAG_NO_ELEMENTS | XML_ELEMENT_FLAG_ALLOW_ANY_ATTRIBUTES)
|
|
ATTRIBUTE_DEFINITION_NONS_NODEFAULT(empty),
|
|
ELEMENT_DEFINITION_DEFNS_END();
|
|
|
|
// This is an "extendo-element" - all attributes here are legal, some are just more legal than others.
|
|
ELEMENT_DEFINITION_CHILD_ELEMENTS(assembly, assemblyIdentity)
|
|
ELEMENT_DEFINITION_CHILD_ELEMENTS_END();
|
|
|
|
|
|
// Please leave this in ... my poor editor has issues with the above for some reason
|
|
int unconfuse_sourceinsight[] = {4};
|
|
|
|
PCXML_ELEMENT_DEFINITION
|
|
RtlpFindElementInDefinition(
|
|
PCXML_ELEMENT_DEFINITION CurrentNode,
|
|
PXML_TOKENIZATION_STATE TokenizerState,
|
|
PXMLDOC_ELEMENT FoundElement
|
|
)
|
|
{
|
|
ULONG i = 0;
|
|
PCXML_ELEMENT_DEFINITION ThisChild;
|
|
BOOLEAN fMatches;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Technically this isn't an error, but let's not give them any ideas
|
|
//
|
|
if (CurrentNode->ChildElements == NULL)
|
|
return NULL;
|
|
|
|
while (TRUE) {
|
|
ThisChild = CurrentNode->ChildElements[i];
|
|
|
|
if (ThisChild == NULL)
|
|
break;
|
|
|
|
status = RtlXmlMatchLogicalElement(
|
|
TokenizerState,
|
|
FoundElement,
|
|
ThisChild->Namespace,
|
|
&ThisChild->Name,
|
|
&fMatches);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return NULL;
|
|
}
|
|
else if (fMatches) {
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
return (fMatches ? CurrentNode->ChildElements[i] : NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// The meat of the matter
|
|
//
|
|
NTSTATUS
|
|
RtlInspectManifestStream(
|
|
ULONG ulFlags,
|
|
PVOID pvManifest,
|
|
SIZE_T cbManifest,
|
|
PRTL_MANIFEST_CONTENT_RAW pContent,
|
|
PXML_TOKENIZATION_STATE pTargetTokenState
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
XML_LOGICAL_STATE ParseState;
|
|
BOOLEAN fFoundAssemblyTag = FALSE;
|
|
NS_MANAGER Namespaces;
|
|
RTL_GROWING_LIST Attributes;
|
|
ULONG ulHitElement;
|
|
XMLDOC_THING LogicalPiece;
|
|
PCXML_ELEMENT_DEFINITION CurrentElement = NULL;
|
|
PCXML_ELEMENT_DEFINITION DocumentRoot = ELEMENT_NAMED(assembly);
|
|
PCXML_ELEMENT_DEFINITION FloatingElementParent = NULL;
|
|
PCXML_ELEMENT_DEFINITION FloatingElement = NULL;
|
|
|
|
//
|
|
// Must give us a pointer to the manifest, a content structure to fill out, and a
|
|
// hashing context w/callback.
|
|
//
|
|
if ((pvManifest == NULL) || (pContent == NULL))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// Do normal startup-type stuff
|
|
//
|
|
status = RtlXmlInitializeNextLogicalThing(&ParseState, pvManifest, cbManifest, &g_DefaultAllocator);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlInitializeGrowingList(&Attributes, sizeof(XMLDOC_ATTRIBUTE), 20, NULL, 0, &g_DefaultAllocator);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlNsInitialize(&Namespaces, RtlXmlDefaultCompareStrings, &ParseState.ParseState, &g_DefaultAllocator);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
//
|
|
// See if we've got an xmldecl
|
|
//
|
|
status = RtlXmlNextLogicalThing(&ParseState, &Namespaces, &LogicalPiece, &Attributes);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
//
|
|
// Validate the first thing in the document. It's either an xmldecl or the <assembly> element,
|
|
// both of which are validatable.
|
|
//
|
|
if (LogicalPiece.ulThingType == XMLDOC_THING_XMLDECL) {
|
|
status = RtlpValidateXmlDeclaration(&ParseState.ParseState, &LogicalPiece);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
//
|
|
// If it's an element, then it must be the <assembly> element.
|
|
//
|
|
else if (LogicalPiece.ulThingType == XMLDOC_THING_ELEMENT) {
|
|
fFoundAssemblyTag = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we've found the assembly tag, then we should set our original document state to
|
|
// being the Assembly state, rather than the DocumentRoot state.
|
|
//
|
|
if (fFoundAssemblyTag) {
|
|
CurrentElement = DocumentRoot;
|
|
}
|
|
|
|
//
|
|
// Now let's zip through all the elements we find, using the filter along the way.
|
|
//
|
|
while (TRUE) {
|
|
|
|
status = RtlXmlNextLogicalThing(&ParseState, &Namespaces, &LogicalPiece, &Attributes);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
if (LogicalPiece.ulThingType == XMLDOC_THING_ELEMENT) {
|
|
|
|
// Special case - this is the first element we've found, so we have to make sure
|
|
// it matches the supposed document root
|
|
if (CurrentElement == NULL) {
|
|
|
|
CurrentElement = DocumentRoot;
|
|
|
|
if (CurrentElement->pfnWorkerCallback) {
|
|
|
|
status = (*CurrentElement->pfnWorkerCallback)(
|
|
&ParseState,
|
|
pContent,
|
|
&LogicalPiece,
|
|
&Attributes,
|
|
eElementNotify_Open,
|
|
CurrentElement);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
}
|
|
else {
|
|
PCXML_ELEMENT_DEFINITION NextElement;
|
|
|
|
NextElement = RtlpFindElementInDefinition(
|
|
CurrentElement,
|
|
&ParseState.ParseState,
|
|
&LogicalPiece.Element);
|
|
|
|
//
|
|
// Look in the small list of valid "floating" fragments
|
|
//
|
|
if ((NextElement == NULL) && (FloatingElementParent == NULL)) {
|
|
PCXML_ELEMENT_DEFINITION SignatureElement = ELEMENT_NAMED(Signature);
|
|
BOOLEAN fMatches = FALSE;
|
|
|
|
status = RtlXmlMatchLogicalElement(
|
|
&ParseState.ParseState,
|
|
&LogicalPiece.Element,
|
|
SignatureElement->Namespace,
|
|
&SignatureElement->Name,
|
|
&fMatches);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
if (fMatches) {
|
|
FloatingElementParent = CurrentElement;
|
|
FloatingElement = SignatureElement;
|
|
NextElement = SignatureElement;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we didn't find an element, this might be the 'signature' element.
|
|
// See if we're looking for signatures, and if so, set the "next element" to be
|
|
// the Signature element and continue looping.
|
|
//
|
|
if (NextElement == NULL) {
|
|
|
|
if (CurrentElement->ulFlags & XML_ELEMENT_FLAG_ALLOW_ANY_CHILDREN) {
|
|
// TODO: There ought to be some default callback, but for now, skip ahead
|
|
// in the document until we find the close of this new child, then continue
|
|
// in the current context as if nothing happened.
|
|
status = RtlXmlSkipElement(&ParseState, &LogicalPiece.Element);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
else {
|
|
// TODO: Report an error here
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto Exit;
|
|
}
|
|
}
|
|
//
|
|
// Otherwise, this is a valid child element, so call its worker
|
|
//
|
|
else {
|
|
|
|
if (NextElement->pfnWorkerCallback) {
|
|
|
|
status = (*NextElement->pfnWorkerCallback)(
|
|
&ParseState,
|
|
pContent,
|
|
&LogicalPiece,
|
|
&Attributes,
|
|
eElementNotify_Open,
|
|
NextElement);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
// TODO: Report an error here
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Spiffy, let's go move into this new state, if that's
|
|
// what we're supposed to do. Empty elements don't affect
|
|
// the state of the world at all.
|
|
//
|
|
if (!LogicalPiece.Element.fElementEmpty)
|
|
CurrentElement = NextElement;
|
|
else
|
|
{
|
|
//
|
|
// Notify this element that we're closing it.
|
|
//
|
|
if (NextElement->pfnWorkerCallback) {
|
|
|
|
status = (*NextElement->pfnWorkerCallback)(
|
|
&ParseState,
|
|
pContent,
|
|
&LogicalPiece,
|
|
&Attributes,
|
|
eElementNotify_Close,
|
|
NextElement);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
// TODO: Log an error here saying the callback failed
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Found the end of the current element. "Pop" it by walking up one on
|
|
// the stack
|
|
else if (LogicalPiece.ulThingType == XMLDOC_THING_END_ELEMENT) {
|
|
|
|
if ((CurrentElement->ParentElement == NULL) && (FloatingElementParent == NULL)) {
|
|
// TODO: We found the end of this document structure, stop
|
|
// looking for more elements.
|
|
break;
|
|
}
|
|
else {
|
|
|
|
if (CurrentElement->pfnWorkerCallback) {
|
|
|
|
status = (*CurrentElement->pfnWorkerCallback)(
|
|
&ParseState,
|
|
pContent,
|
|
&LogicalPiece,
|
|
&Attributes,
|
|
eElementNotify_Close,
|
|
CurrentElement);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
// TODO: Log an error here saying the callback failed
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (FloatingElementParent && (CurrentElement == FloatingElement)) {
|
|
CurrentElement = FloatingElementParent;
|
|
FloatingElementParent = NULL;
|
|
FloatingElement = NULL;
|
|
}
|
|
else {
|
|
CurrentElement = CurrentElement->ParentElement;
|
|
}
|
|
}
|
|
|
|
}
|
|
// PCData in the input? Ok, if the element allows it
|
|
else if (LogicalPiece.ulThingType == XMLDOC_THING_HYPERSPACE) {
|
|
|
|
if (CurrentElement && CurrentElement->ulFlags & XML_ELEMENT_FLAG_NO_PCDATA) {
|
|
|
|
// TODO: Issue an error here
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto Exit;
|
|
}
|
|
else {
|
|
if (CurrentElement && (CurrentElement->pfnWorkerCallback)) {
|
|
status = (*CurrentElement->pfnWorkerCallback)(
|
|
&ParseState,
|
|
pContent,
|
|
&LogicalPiece,
|
|
&Attributes,
|
|
eElementNotify_Hyperspace,
|
|
CurrentElement);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
// TODO: Log an error here saying the callback failed
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Error in the input stream? Ok, stop.
|
|
else if (LogicalPiece.ulThingType == XMLDOC_THING_ERROR) {
|
|
// TODO: Issue an error here
|
|
status = LogicalPiece.Error.Code;
|
|
goto Exit;
|
|
}
|
|
// End of stream? Spiffy, we're done
|
|
else if (LogicalPiece.ulThingType == XMLDOC_THING_END_OF_STREAM) {
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
status = RtlXmlCloneTokenizationState(&ParseState.ParseState, pTargetTokenState);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
Exit:
|
|
RtlXmlDestroyNextLogicalThing(&ParseState);
|
|
RtlDestroyGrowingList(&Attributes);
|
|
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlpValidateXmlDeclaration(
|
|
PXML_TOKENIZATION_STATE pState,
|
|
PXMLDOC_THING pDocThing
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
XML_STRING_COMPARE fMatch;
|
|
|
|
if ((pState == NULL) || (pDocThing == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
else if (pDocThing->ulThingType != XMLDOC_THING_XMLDECL) {
|
|
return STATUS_MANIFEST_MISSING_XML_DECL;
|
|
}
|
|
|
|
|
|
status = pState->pfnCompareSpecialString(
|
|
pState,
|
|
&pDocThing->XmlDecl.Standalone,
|
|
&sc_ss_xmldecl_yes,
|
|
&fMatch);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
else if (fMatch != XML_STRING_COMPARE_EQUALS) {
|
|
return STATUS_MANIFEST_NOT_STANDALONE;
|
|
}
|
|
|
|
|
|
status = pState->pfnCompareSpecialString(
|
|
pState,
|
|
&pDocThing->XmlDecl.Version,
|
|
&sc_ss_xmldecl_version_10,
|
|
&fMatch);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
else if (fMatch != XML_STRING_COMPARE_EQUALS) {
|
|
return STATUS_MANIFEST_NOT_VERSION_1_0;
|
|
}
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
typedef struct _SEARCH_ATTRIBUTES_CONTEXT {
|
|
PXML_TOKENIZATION_STATE State;
|
|
PXMLDOC_ATTRIBUTE SearchKey;
|
|
} SEARCH_ATTRIBUTES_CONTEXT;
|
|
|
|
typedef int (__cdecl *bsearchcompare)(const void*, const void*);
|
|
|
|
int __cdecl SearchForAttribute(
|
|
const SEARCH_ATTRIBUTES_CONTEXT* Context,
|
|
PCXML_VALID_ELEMENT_ATTRIBUTE ValidAttribute
|
|
)
|
|
{
|
|
XML_STRING_COMPARE Compare;
|
|
|
|
RtlXmlMatchAttribute(
|
|
Context->State,
|
|
Context->SearchKey,
|
|
ValidAttribute->Attribute.Namespace,
|
|
&ValidAttribute->Attribute.Name,
|
|
&Compare);
|
|
|
|
//
|
|
// Note: this logic is intentionally backwards.
|
|
//
|
|
return -(int)Compare;
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlValidateAttributesAndOrganize(
|
|
PXML_TOKENIZATION_STATE State,
|
|
PXMLDOC_ELEMENT Element,
|
|
PRTL_GROWING_LIST Attributes,
|
|
PCXML_ELEMENT_DEFINITION ThisElement,
|
|
PXMLDOC_ATTRIBUTE *OrderedList
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ul;
|
|
BOOLEAN Compare;
|
|
PXMLDOC_ATTRIBUTE pThisAttribute;
|
|
SEARCH_ATTRIBUTES_CONTEXT SearchContext = { State };
|
|
|
|
RtlZeroMemory(OrderedList, ThisElement->AttributeCount * sizeof(PXMLDOC_ATTRIBUTE));
|
|
|
|
for (ul = 0; ul < Element->ulAttributeCount; ul++) {
|
|
|
|
PCXML_VALID_ELEMENT_ATTRIBUTE MatchingAttribute = NULL;
|
|
|
|
status = RtlIndexIntoGrowingList(
|
|
Attributes,
|
|
ul,
|
|
(PVOID*)&SearchContext.SearchKey,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
MatchingAttribute = bsearch(
|
|
&SearchContext,
|
|
ThisElement->AttributeList,
|
|
ThisElement->AttributeCount,
|
|
sizeof(ThisElement->AttributeList[0]),
|
|
(bsearchcompare)SearchForAttribute);
|
|
|
|
if (MatchingAttribute) {
|
|
// TODO: Fix this up a little bit so that we can call off to the validator
|
|
OrderedList[MatchingAttribute - ThisElement->AttributeList] = SearchContext.SearchKey;
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static XML_SPECIAL_STRING s_us_ValidManifestVersions[] = {
|
|
MAKE_SPECIAL_STRING("1.0"),
|
|
MAKE_SPECIAL_STRING("1.5")
|
|
};
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
Rtl_InspectManifest_Assembly(
|
|
PXML_LOGICAL_STATE pLogicalState,
|
|
PRTL_MANIFEST_CONTENT_RAW pManifestContent,
|
|
PXMLDOC_THING pDocumentThing,
|
|
PRTL_GROWING_LIST pAttributes,
|
|
MANIFEST_ELEMENT_CALLBACK_REASON Reason,
|
|
const struct _XML_ELEMENT_DEFINITION *pElementDefinition
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
ULONG u;
|
|
|
|
PXMLDOC_ATTRIBUTE FoundAttributes[eAttribs_assembly_Count];
|
|
|
|
//
|
|
// Potentially this should be an ASSERT with an INTERNAL_ERROR_CHECK, since this function
|
|
// has internal-only linkage.
|
|
//
|
|
if (!pLogicalState || !pManifestContent || !pDocumentThing || !pAttributes || !pElementDefinition)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// We don't care about anything other than 'open' tha
|
|
//
|
|
if (Reason != eElementNotify_Open)
|
|
return STATUS_SUCCESS;
|
|
|
|
ASSERT(pDocumentThing->ulThingType == XMLDOC_THING_ELEMENT);
|
|
|
|
status = RtlValidateAttributesAndOrganize(
|
|
&pLogicalState->ParseState,
|
|
&pDocumentThing->Element,
|
|
pAttributes,
|
|
pElementDefinition,
|
|
FoundAttributes);
|
|
|
|
//
|
|
// Log a parse error here
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
Rtl_InspectManifest_File(
|
|
PXML_LOGICAL_STATE pLogicalState,
|
|
PRTL_MANIFEST_CONTENT_RAW pManifestContent,
|
|
PXMLDOC_THING pDocumentThing,
|
|
PRTL_GROWING_LIST pAttributes,
|
|
MANIFEST_ELEMENT_CALLBACK_REASON Reason,
|
|
const struct _XML_ELEMENT_DEFINITION *pElementDefinition
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ulLeftovers;
|
|
|
|
union {
|
|
PXMLDOC_ATTRIBUTE File[eAttribs_assembly_file_Count];
|
|
} Attributes;
|
|
|
|
if (Reason != eElementNotify_Open)
|
|
return STATUS_SUCCESS;
|
|
|
|
ASSERT(pDocumentThing->ulThingType == XMLDOC_THING_ELEMENT);
|
|
if (pDocumentThing->ulThingType != XMLDOC_THING_ELEMENT)
|
|
return STATUS_INTERNAL_ERROR;
|
|
|
|
if (pElementDefinition == ELEMENT_NAMED(assembly_file)) {
|
|
|
|
ULONG ulIndex = pManifestContent->ulFileMembers;
|
|
PASSEMBLY_MEMBER_FILE_RAW pNewFile = NULL;
|
|
|
|
status = RtlValidateAttributesAndOrganize(
|
|
&pLogicalState->ParseState,
|
|
&pDocumentThing->Element,
|
|
pAttributes,
|
|
pElementDefinition,
|
|
Attributes.File);
|
|
|
|
// Log a parse error here
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
// Log a parse error here as well
|
|
if (Attributes.File[eAttribs_assembly_file_name] == NULL) {
|
|
status = STATUS_MANIFEST_FILE_TAG_MISSING_NAME;
|
|
goto Exit;
|
|
}
|
|
|
|
status = RtlIndexIntoGrowingList(&pManifestContent->FileMembers, ulIndex, (PVOID*)&pNewFile, TRUE);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
RtlZeroMemory(pNewFile, sizeof(*pNewFile));
|
|
|
|
if (Attributes.File[eAttribs_assembly_file_name])
|
|
pNewFile->FileName = Attributes.File[eAttribs_assembly_file_name]->Value;
|
|
|
|
if (Attributes.File[eAttribs_assembly_file_hashalg])
|
|
pNewFile->HashAlg = Attributes.File[eAttribs_assembly_file_hashalg]->Value;
|
|
|
|
if (Attributes.File[eAttribs_assembly_file_size])
|
|
pNewFile->Size = Attributes.File[eAttribs_assembly_file_size]->Value;
|
|
|
|
if (Attributes.File[eAttribs_assembly_file_hash])
|
|
pNewFile->HashValue = Attributes.File[eAttribs_assembly_file_hash]->Value;
|
|
|
|
if (Attributes.File[eAttribs_assembly_file_loadFrom])
|
|
pNewFile->LoadFrom = Attributes.File[eAttribs_assembly_file_loadFrom]->Value;
|
|
|
|
if (Attributes.File[eAttribs_assembly_file_digestMethod])
|
|
pNewFile->DigestMethod = Attributes.File[eAttribs_assembly_file_digestMethod]->Value;
|
|
|
|
pManifestContent->ulFileMembers++;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
Rtl_InspectManifest_AssemblyIdentity(
|
|
PXML_LOGICAL_STATE pLogicalState,
|
|
PRTL_MANIFEST_CONTENT_RAW pManifestContent,
|
|
PXMLDOC_THING pDocumentThing,
|
|
PRTL_GROWING_LIST pAttributes,
|
|
MANIFEST_ELEMENT_CALLBACK_REASON Reason,
|
|
const struct _XML_ELEMENT_DEFINITION *pElementDefinition
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PXMLDOC_ATTRIBUTE AsmIdentAttribs[eAttribs_assembly_assemblyIdentity_Count];
|
|
ULONG ulThisIdentity, ulThisAttribute, i;
|
|
|
|
if (Reason != eElementNotify_Open)
|
|
return STATUS_SUCCESS;
|
|
|
|
ASSERT(pDocumentThing && (pDocumentThing->ulThingType == XMLDOC_THING_ELEMENT));
|
|
if (!pDocumentThing || (pDocumentThing->ulThingType != XMLDOC_THING_ELEMENT))
|
|
return STATUS_INTERNAL_ERROR;
|
|
|
|
if (pElementDefinition == ELEMENT_NAMED(assembly_assemblyIdentity)) {
|
|
if (pManifestContent->ulRootIdentityIndex != INVALID_ASSEMBLY_IDENTITY_INDEX) {
|
|
// TODO: Log a parse error
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use local copies - we'll update the values in the raw content when we've
|
|
// added them all.
|
|
//
|
|
ulThisIdentity = pManifestContent->ulAssemblyIdentitiesFound;
|
|
ulThisAttribute = pManifestContent->ulAssemblyIdentityAttributes;
|
|
|
|
//
|
|
// For each, create slots to hold the assembly identities
|
|
//
|
|
for (i = 0; i < pDocumentThing->Element.ulAttributeCount; i++) {
|
|
|
|
PXMLDOC_ATTRIBUTE pThisAttribute = NULL;
|
|
PASSEMBLY_IDENTITY_ATTRIBUTE_RAW pRawIdent = NULL;
|
|
|
|
status = RtlIndexIntoGrowingList(pAttributes, i, (PVOID*)&pThisAttribute, FALSE);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlIndexIntoGrowingList(
|
|
&pManifestContent->AssemblyIdentityAttributes,
|
|
ulThisAttribute++,
|
|
(PVOID*)&pRawIdent,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
pRawIdent->Namespace = pThisAttribute->NsPrefix;
|
|
pRawIdent->Attribute = pThisAttribute->Name;
|
|
pRawIdent->Value = pThisAttribute->Value;
|
|
pRawIdent->ulIdentityIndex = ulThisIdentity;
|
|
}
|
|
|
|
//
|
|
// Whee, we got to the end and added them all - update stuff in the raw content
|
|
// so that it knows all about this new identity, and mark it as root if it is.
|
|
//
|
|
if (pElementDefinition == ELEMENT_NAMED(assembly_assemblyIdentity)) {
|
|
pManifestContent->ulRootIdentityIndex = ulThisIdentity;
|
|
}
|
|
|
|
pManifestContent->ulAssemblyIdentitiesFound++;
|
|
pManifestContent->ulAssemblyIdentityAttributes = ulThisAttribute;
|
|
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlSxsInitializeManifestRawContent(
|
|
ULONG ulRequestedContent,
|
|
PRTL_MANIFEST_CONTENT_RAW *pRawContentOut,
|
|
PVOID pvOriginalBuffer,
|
|
SIZE_T cbOriginalBuffer
|
|
)
|
|
{
|
|
PRTL_MANIFEST_CONTENT_RAW pContent = NULL;
|
|
PRTL_MINI_HEAP pExtraContent = NULL;
|
|
|
|
PVOID pvBufferUsed = NULL;
|
|
SIZE_T cbBufferUsed = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
RTL_ALLOCATOR MiniAllocator = { RtlMiniHeapAlloc, RtlMiniHeapFree };
|
|
|
|
if (pRawContentOut)
|
|
*pRawContentOut = NULL;
|
|
|
|
if (!pRawContentOut || (!pvOriginalBuffer && cbOriginalBuffer))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (pvOriginalBuffer == NULL) {
|
|
|
|
//
|
|
// If you get a compile error on this line, you'll need to increase the size
|
|
// of the 'default' allocation size above.
|
|
//
|
|
C_ASSERT(DEFAULT_MINI_HEAP_SIZE >= (sizeof(RTL_MANIFEST_CONTENT_RAW) + sizeof(RTL_MINI_HEAP)));
|
|
|
|
cbBufferUsed = DEFAULT_MINI_HEAP_SIZE;
|
|
status = g_DefaultAllocator.pfnAlloc(DEFAULT_MINI_HEAP_SIZE, &pvBufferUsed, g_DefaultAllocator.pvContext);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
}
|
|
else {
|
|
pvBufferUsed = pvOriginalBuffer;
|
|
cbBufferUsed = cbOriginalBuffer;
|
|
}
|
|
|
|
//
|
|
// Ensure there's enough space for the raw content data, as well as the extra content
|
|
//
|
|
if (cbBufferUsed < (sizeof(RTL_MANIFEST_CONTENT_RAW) + sizeof(RTL_MINI_HEAP))) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Set up the content structure and the extra turdlet content at
|
|
// the end properly
|
|
//
|
|
pContent = (PRTL_MANIFEST_CONTENT_RAW)pvBufferUsed;
|
|
|
|
status = RtlInitializeMiniHeapInPlace(
|
|
(PRTL_MINI_HEAP)(pContent + 1),
|
|
cbBufferUsed - sizeof(*pContent),
|
|
&pExtraContent);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
//
|
|
// Now let's go initialize the content data
|
|
//
|
|
RtlZeroMemory(pContent, sizeof(*pContent));
|
|
|
|
pContent->ulFlags = MANIFEST_CONTENT_SELF_ALLOCATED;
|
|
pContent->ulRootIdentityIndex = MAX_ULONG;
|
|
|
|
MiniAllocator.pvContext = pExtraContent;
|
|
|
|
status = RtlInitializeGrowingList(
|
|
&pContent->FileMembers,
|
|
sizeof(ASSEMBLY_MEMBER_FILE_RAW),
|
|
8,
|
|
NULL,
|
|
0,
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
//
|
|
// Always also need the assembly identity at the root
|
|
//
|
|
status = RtlInitializeGrowingList(
|
|
&pContent->AssemblyIdentityAttributes,
|
|
sizeof(ASSEMBLY_IDENTITY_ATTRIBUTE_RAW),
|
|
8,
|
|
NULL,
|
|
0,
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
//
|
|
// Want the COM class data?
|
|
//
|
|
if (ulRequestedContent & RTLIMS_GATHER_COMCLASSES) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pComClasses,
|
|
sizeof(COMCLASS_REDIRECTION_RAW),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Want the window class data?
|
|
//
|
|
if (ulRequestedContent & RTLIMS_GATHER_WINDOWCLASSES) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pWindowClasses,
|
|
sizeof(WINDOWCLASS_REDIRECTION_RAW),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Want the prog ids?
|
|
//
|
|
if (ulRequestedContent & RTLIMS_GATHER_COMCLASS_PROGIDS) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pProgIds,
|
|
sizeof(COMCLASS_PROGID_RAW),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Want the dependencies?
|
|
//
|
|
if (ulRequestedContent & RTLIMS_GATHER_DEPENDENCIES) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pComClasses,
|
|
sizeof(COMCLASS_REDIRECTION_RAW),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Want the external proxy stubs?
|
|
//
|
|
if (ulRequestedContent & RTLIMS_GATHER_EXTERNALPROXIES) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pExternalInterfaceProxyStubs,
|
|
sizeof(COMINTERFACE_REDIRECTION_RAW),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
//
|
|
// Want the internal proxy stubs?
|
|
//
|
|
if (ulRequestedContent & RTLIMS_GATHER_INTERFACEPROXIES) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pInterfaceProxyStubs,
|
|
sizeof(COMINTERFACE_REDIRECTION_RAW),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
//
|
|
// Want the type libraries?
|
|
//
|
|
if (ulRequestedContent & RTLIMS_GATHER_TYPELIBRARIES) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pTypeLibraries,
|
|
sizeof(TYPELIB_REDIRECT_RAW),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
|
|
if (ulRequestedContent & RTLIMS_GATHER_SIGNATURES) {
|
|
|
|
status = RtlAllocateGrowingList(
|
|
&pContent->pManifestSignatures,
|
|
sizeof(XML_DSIG_BLOCK),
|
|
&MiniAllocator);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
|
|
*pRawContentOut = pContent;
|
|
|
|
Exit:
|
|
if (!NT_SUCCESS(status) && pvBufferUsed && (pvBufferUsed != pvOriginalBuffer)) {
|
|
g_DefaultAllocator.pfnFree(pvBufferUsed, NULL);
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
RtlSxsDestroyManifestContent(
|
|
PRTL_MANIFEST_CONTENT_RAW pRawContent
|
|
)
|
|
{
|
|
if (!pRawContent)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (pRawContent->pComClasses) {
|
|
RtlDestroyGrowingList(pRawContent->pComClasses);
|
|
pRawContent->pComClasses = NULL;
|
|
}
|
|
|
|
if (pRawContent->pExternalInterfaceProxyStubs) {
|
|
RtlDestroyGrowingList(pRawContent->pExternalInterfaceProxyStubs);
|
|
pRawContent->pExternalInterfaceProxyStubs = NULL;
|
|
}
|
|
|
|
if (pRawContent->pInterfaceProxyStubs) {
|
|
RtlDestroyGrowingList(pRawContent->pInterfaceProxyStubs);
|
|
pRawContent->pInterfaceProxyStubs = NULL;
|
|
}
|
|
|
|
if (pRawContent->pManifestSignatures) {
|
|
RtlDestroyGrowingList(pRawContent->pManifestSignatures);
|
|
pRawContent->pManifestSignatures = NULL;
|
|
}
|
|
|
|
if (pRawContent->pProgIds) {
|
|
RtlDestroyGrowingList(pRawContent->pProgIds);
|
|
pRawContent->pProgIds = NULL;
|
|
}
|
|
|
|
if (pRawContent->pTypeLibraries) {
|
|
RtlDestroyGrowingList(pRawContent->pTypeLibraries);
|
|
pRawContent->pTypeLibraries = NULL;
|
|
}
|
|
|
|
if (pRawContent->pWindowClasses) {
|
|
RtlDestroyGrowingList(pRawContent->pWindowClasses);
|
|
pRawContent->pWindowClasses = NULL;
|
|
}
|
|
|
|
RtlDestroyGrowingList(&pRawContent->FileMembers);
|
|
RtlDestroyGrowingList(&pRawContent->AssemblyIdentityAttributes);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlpAllocateAndExtractString(
|
|
PXML_EXTENT pXmlExtent,
|
|
PUNICODE_STRING pusTargetString,
|
|
PXML_RAWTOKENIZATION_STATE pState,
|
|
PMINI_BUFFER pTargetBuffer
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
MINI_BUFFER mb;
|
|
|
|
if (!ARGUMENT_PRESENT(pXmlExtent) || !ARGUMENT_PRESENT(pusTargetString) ||
|
|
!ARGUMENT_PRESENT(pState) || !ARGUMENT_PRESENT(pTargetBuffer))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
RtlZeroMemory(pusTargetString, sizeof(*pusTargetString));
|
|
mb = *pTargetBuffer;
|
|
|
|
//
|
|
// ISSUE:jonwis-2002-04-19: We need to clamp this max length elsewhere - we should not
|
|
// be allowing arbitrarily-large attributes and whatnot. Unfortunately, this exposes
|
|
// "implementation details", so this clamp should be on our side of the wall, /not/
|
|
// in the XML parser itself.
|
|
//
|
|
pusTargetString->Length = 0;
|
|
pusTargetString->MaximumLength = (USHORT)pXmlExtent->ulCharacters * sizeof(WCHAR);
|
|
|
|
status = RtlMiniBufferAllocateBytes(&mb, pusTargetString->MaximumLength, &pusTargetString->Buffer);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = RtlXmlExtentToString(pState, pXmlExtent, pusTargetString, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
*pTargetBuffer = mb;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlpAllocateAndExtractString2(
|
|
PXML_EXTENT pXmlExtent,
|
|
PUNICODE_STRING *ppusTargetString,
|
|
PXML_RAWTOKENIZATION_STATE pState,
|
|
PMINI_BUFFER pTargetBuffer
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
MINI_BUFFER mb;
|
|
|
|
if (!ARGUMENT_PRESENT(pXmlExtent) || !ARGUMENT_PRESENT(ppusTargetString) ||
|
|
!ARGUMENT_PRESENT(pState) || !ARGUMENT_PRESENT(pTargetBuffer))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppusTargetString = NULL;
|
|
mb = *pTargetBuffer;
|
|
|
|
status = RtlMiniBufferAllocate(&mb, UNICODE_STRING, ppusTargetString);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = RtlpAllocateAndExtractString(
|
|
pXmlExtent,
|
|
*ppusTargetString,
|
|
pState,
|
|
pTargetBuffer);
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// These help keep things aligned
|
|
//
|
|
#define ALIGN_SIZE(type) ROUND_UP_COUNT(sizeof(type))
|
|
|
|
NTSTATUS
|
|
RtlpCalculateCookedManifestContentSize(
|
|
PRTL_MANIFEST_CONTENT_RAW pRawContent,
|
|
PXML_RAWTOKENIZATION_STATE pState,
|
|
PSIZE_T pcbRequired
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SIZE_T cbRequired;
|
|
ULONG ul = 0;
|
|
ULONG ulNamespacesFound = 0;
|
|
|
|
if (ARGUMENT_PRESENT(pcbRequired)) {
|
|
*pcbRequired = 0;
|
|
}
|
|
|
|
|
|
if (!ARGUMENT_PRESENT(pRawContent) || !ARGUMENT_PRESENT(pState) || !ARGUMENT_PRESENT(pcbRequired)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
cbRequired = ROUND_UP_COUNT(sizeof(MANIFEST_COOKED_DATA), ALIGNMENT_VALUE);
|
|
|
|
//
|
|
// For each file, gather up the data in the raw object.
|
|
//
|
|
cbRequired += ROUND_UP_COUNT(sizeof(MANIFEST_COOKED_FILE) * pRawContent->ulFileMembers, ALIGNMENT_VALUE);
|
|
|
|
for (ul = 0; ul < pRawContent->ulFileMembers; ul++) {
|
|
PASSEMBLY_MEMBER_FILE_RAW pRawFile = NULL;
|
|
|
|
status = RtlIndexIntoGrowingList(&pRawContent->FileMembers, ul, (PVOID*)&pRawFile, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (pRawFile->FileName.pvData != NULL) {
|
|
cbRequired += ROUND_UP_COUNT(pRawFile->FileName.ulCharacters * sizeof(WCHAR), ALIGNMENT_VALUE);
|
|
}
|
|
|
|
if (pRawFile->LoadFrom.pvData != NULL) {
|
|
cbRequired += ROUND_UP_COUNT(pRawFile->LoadFrom.ulCharacters * sizeof(WCHAR), ALIGNMENT_VALUE);
|
|
}
|
|
|
|
//
|
|
// Each two characters in the hash value string represents one byte.
|
|
//
|
|
if (pRawFile->HashValue.pvData != NULL) {
|
|
cbRequired += ROUND_UP_COUNT(pRawFile->HashValue.ulCharacters / 2, ALIGNMENT_VALUE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// For now, we're none too bright about pooling namespaces on identity values. Luckily,
|
|
// values in different namespaces are now not the norm, so life gets easier.
|
|
//
|
|
cbRequired += ROUND_UP_COUNT(sizeof(MANIFEST_IDENTITY_TABLE), ALIGNMENT_VALUE);
|
|
cbRequired += ROUND_UP_COUNT(sizeof(MANIFEST_COOKED_IDENTITY) * pRawContent->ulAssemblyIdentitiesFound, ALIGNMENT_VALUE);
|
|
cbRequired += ROUND_UP_COUNT(sizeof(MANIFEST_COOKED_IDENTITY_PAIR) * pRawContent->ulAssemblyIdentityAttributes, ALIGNMENT_VALUE);
|
|
|
|
for (ul = 0; ul < pRawContent->ulAssemblyIdentityAttributes; ul++) {
|
|
PASSEMBLY_IDENTITY_ATTRIBUTE_RAW pRawAttribute = NULL;
|
|
ULONG ul2 = 0;
|
|
|
|
status = RtlIndexIntoGrowingList(&pRawContent->AssemblyIdentityAttributes, ul, (PVOID*)&pRawAttribute, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// We need this much extra space to to store the data
|
|
//
|
|
cbRequired += ROUND_UP_COUNT(pRawAttribute->Attribute.ulCharacters * sizeof(WCHAR), ALIGNMENT_VALUE);
|
|
cbRequired += ROUND_UP_COUNT(pRawAttribute->Value.ulCharacters * sizeof(WCHAR), ALIGNMENT_VALUE);
|
|
cbRequired += ROUND_UP_COUNT(pRawAttribute->Namespace.ulCharacters * sizeof(WCHAR), ALIGNMENT_VALUE);
|
|
}
|
|
|
|
*pcbRequired = cbRequired;
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS FORCEINLINE
|
|
pExpandBuffer(
|
|
PUNICODE_STRING strTarget,
|
|
PVOID pvBaseBuffer,
|
|
SIZE_T cchCount
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
const USHORT usRequiredCb = (USHORT)(cchCount * sizeof(WCHAR));
|
|
if (strTarget->MaximumLength >= usRequiredCb) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
if ((strTarget->Buffer != pvBaseBuffer) && (strTarget->Buffer != NULL)) {
|
|
if (!NT_SUCCESS(status = g_DefaultAllocator.pfnFree(strTarget->Buffer, NULL)))
|
|
return status;
|
|
}
|
|
if (!NT_SUCCESS(status = g_DefaultAllocator.pfnAlloc(usRequiredCb, (PVOID*)&strTarget->Buffer, NULL))) {
|
|
strTarget->Buffer = NULL;
|
|
strTarget->MaximumLength = strTarget->Length = 0;
|
|
return status;
|
|
}
|
|
strTarget->MaximumLength = usRequiredCb;
|
|
strTarget->Length = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
#define pFreeBuffer(buff, pvBase) do { \
|
|
if (((buff)->Buffer != pvBase) && ((buff)->Buffer != NULL)) { \
|
|
RtlDefaultFreer((buff)->Buffer, NULL); \
|
|
(buff)->Buffer = NULL; (buff)->MaximumLength = 0; } \
|
|
} while (0)
|
|
|
|
|
|
|
|
struct {
|
|
const UNICODE_STRING Text;
|
|
DigestType DigestValue;
|
|
} g_rgsHashDigests[] = {
|
|
{ RTL_CONSTANT_STRING(L"fullfile"), DigestType_FullFile }
|
|
};
|
|
|
|
struct {
|
|
const UNICODE_STRING Text;
|
|
HashType HashAlgValue;
|
|
} g_rgsHashAlgs[] = {
|
|
{ RTL_CONSTANT_STRING(L"sha1"), HashType_Sha1 },
|
|
{ RTL_CONSTANT_STRING(L"sha"), HashType_Sha1 },
|
|
{ RTL_CONSTANT_STRING(L"sha-256"), HashType_Sha256 },
|
|
{ RTL_CONSTANT_STRING(L"sha-384"), HashType_Sha384 },
|
|
{ RTL_CONSTANT_STRING(L"sha-512"), HashType_Sha512 },
|
|
{ RTL_CONSTANT_STRING(L"md5"), HashType_MD5 },
|
|
{ RTL_CONSTANT_STRING(L"md4"), HashType_MD4 },
|
|
{ RTL_CONSTANT_STRING(L"md2"), HashType_MD4 },
|
|
};
|
|
|
|
NTSTATUS
|
|
RtlpParseDigestMethod(
|
|
PUNICODE_STRING pText,
|
|
DigestType *pDigestType
|
|
)
|
|
{
|
|
ULONG ul;
|
|
|
|
if (pDigestType != NULL)
|
|
*pDigestType = 0;
|
|
|
|
if (!ARGUMENT_PRESENT(pDigestType) || !ARGUMENT_PRESENT(pText)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
for (ul = 0; ul < NUMBER_OF(g_rgsHashDigests); ul++) {
|
|
if (RtlCompareUnicodeString(pText, &g_rgsHashDigests[ul].Text, TRUE) == 0) {
|
|
*pDigestType = g_rgsHashDigests[ul].DigestValue;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlpParseHashAlg(
|
|
PUNICODE_STRING pText,
|
|
HashType *pHashType
|
|
)
|
|
{
|
|
ULONG ul;
|
|
|
|
if (pHashType != NULL)
|
|
*pHashType = 0;
|
|
|
|
if (!ARGUMENT_PRESENT(pHashType) || !ARGUMENT_PRESENT(pText)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
for (ul = 0; ul < NUMBER_OF(g_rgsHashAlgs); ul++) {
|
|
if (RtlCompareUnicodeString(pText, &g_rgsHashAlgs[ul].Text, TRUE) == 0) {
|
|
*pHashType = g_rgsHashAlgs[ul].HashAlgValue;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlpAddRawIdentitiesToCookedContent(
|
|
PRTL_MANIFEST_CONTENT_RAW pRawContent,
|
|
PMANIFEST_COOKED_DATA pCookedContent,
|
|
PXML_RAWTOKENIZATION_STATE pState,
|
|
PMINI_BUFFER TargetBuffer
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ul = 0;
|
|
PMANIFEST_IDENTITY_TABLE IdentityTable = NULL;
|
|
PMANIFEST_COOKED_IDENTITY IdentityList = NULL;
|
|
PMANIFEST_COOKED_IDENTITY_PAIR NameValueList = NULL;
|
|
|
|
//
|
|
// Start off by allocating space for the table of identities, and the
|
|
// table of individual identities
|
|
//
|
|
status = RtlMiniBufferAllocate(TargetBuffer, MANIFEST_IDENTITY_TABLE, &IdentityTable);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
status = RtlMiniBufferAllocateCount(TargetBuffer, MANIFEST_COOKED_IDENTITY, pRawContent->ulAssemblyIdentitiesFound, &IdentityList);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
RtlZeroMemory(IdentityList, sizeof(*IdentityList) * pRawContent->ulAssemblyIdentitiesFound);
|
|
|
|
IdentityTable->ulIdentityCount = pRawContent->ulAssemblyIdentitiesFound;
|
|
IdentityTable->ulRootIdentityIndex = ULONG_MAX;
|
|
IdentityTable->CookedIdentities = IdentityList;
|
|
|
|
|
|
//
|
|
// Now allocate the right number of identity components
|
|
//
|
|
status = RtlMiniBufferAllocateCount(TargetBuffer, MANIFEST_COOKED_IDENTITY_PAIR, pRawContent->ulAssemblyIdentityAttributes, &NameValueList);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
RtlZeroMemory(NameValueList, sizeof(*NameValueList) * pRawContent->ulAssemblyIdentityAttributes);
|
|
|
|
//
|
|
// Spiffy - now, we start adding identity components into the list. We'll assert that the
|
|
// array of components' indexes increases monotomically.
|
|
//
|
|
for (ul = 0; ul < pRawContent->ulAssemblyIdentityAttributes; ul++) {
|
|
PASSEMBLY_IDENTITY_ATTRIBUTE_RAW RawValue = NULL;
|
|
PMANIFEST_COOKED_IDENTITY pThisIdentity = IdentityList + ul;
|
|
|
|
status = RtlIndexIntoGrowingList(
|
|
&pRawContent->AssemblyIdentityAttributes,
|
|
ul,
|
|
(PVOID*)&RawValue,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
ASSERT(RawValue->ulIdentityIndex < pRawContent->ulAssemblyIdentitiesFound);
|
|
pThisIdentity = IdentityList + RawValue->ulIdentityIndex;
|
|
|
|
//
|
|
// If this is unset to start, then set it
|
|
//
|
|
if (pThisIdentity->pIdentityPairs == NULL) {
|
|
pThisIdentity->pIdentityPairs = NameValueList + ul;
|
|
}
|
|
|
|
//
|
|
// Allocate enough space to hold the namespace, name, etc.
|
|
//
|
|
if (RawValue->Namespace.pvData) {
|
|
status = RtlpAllocateAndExtractString(
|
|
&RawValue->Namespace,
|
|
&NameValueList[ul].Namespace,
|
|
pState,
|
|
TargetBuffer);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
status = RtlpAllocateAndExtractString(
|
|
&RawValue->Attribute,
|
|
&NameValueList[ul].Name,
|
|
pState,
|
|
TargetBuffer);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlpAllocateAndExtractString(
|
|
&RawValue->Value,
|
|
&NameValueList[ul].Value,
|
|
pState,
|
|
TargetBuffer);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
|
|
pThisIdentity->ulIdentityComponents++;
|
|
}
|
|
|
|
pCookedContent->pManifestIdentity = IdentityTable;
|
|
pCookedContent->ulFlags |= COOKEDMANIFEST_HAS_IDENTITIES;
|
|
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlpAddRawFilesToCookedContent(
|
|
PRTL_MANIFEST_CONTENT_RAW pRawContent,
|
|
PMANIFEST_COOKED_DATA pCookedContent,
|
|
PXML_RAWTOKENIZATION_STATE pState,
|
|
PMINI_BUFFER TargetBuffer
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ul;
|
|
MINI_BUFFER OurMiniBuffer;
|
|
RTL_UNICODE_STRING_BUFFER TempStringBuffer;
|
|
UCHAR TempStringBufferStatic[64];
|
|
|
|
if (!ARGUMENT_PRESENT(pRawContent) || !ARGUMENT_PRESENT(pState) || !ARGUMENT_PRESENT(TargetBuffer)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
RtlInitUnicodeStringBuffer(&TempStringBuffer, TempStringBufferStatic, sizeof(TempStringBufferStatic));
|
|
|
|
//
|
|
// Copy buffer state - if we succeed, we'll write the updated buffer back
|
|
// into the one that's tracking stuff in the caller.
|
|
//
|
|
OurMiniBuffer = *TargetBuffer;
|
|
pCookedContent->ulFileCount = pRawContent->ulFileMembers;
|
|
|
|
if (pRawContent->ulFileMembers == 0) {
|
|
|
|
pCookedContent->pCookedFiles = NULL;
|
|
|
|
}
|
|
else {
|
|
|
|
PASSEMBLY_MEMBER_FILE_RAW pRawFile = NULL;
|
|
|
|
|
|
status = RtlMiniBufferAllocateCount(
|
|
&OurMiniBuffer,
|
|
MANIFEST_COOKED_FILE,
|
|
pCookedContent->ulFileCount,
|
|
&pCookedContent->pCookedFiles);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Now for each one, allocate the necessary UNICODE_STRINGs
|
|
//
|
|
for (ul = 0; ul < pRawContent->ulFileMembers; ul++) {
|
|
|
|
PMANIFEST_COOKED_FILE pFile = pCookedContent->pCookedFiles + ul;
|
|
|
|
pFile->ulFlags = 0;
|
|
|
|
status = RtlIndexIntoGrowingList(&pRawContent->FileMembers, ul, (PVOID*)&pRawFile, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If this fails, stop trying
|
|
//
|
|
if (pRawFile->FileName.pvData != NULL) {
|
|
|
|
status = RtlpAllocateAndExtractString(
|
|
&pRawFile->FileName,
|
|
&pFile->FileName,
|
|
pState,
|
|
&OurMiniBuffer);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
pFile->ulFlags |= COOKEDFILE_NAME_VALID;
|
|
|
|
}
|
|
|
|
|
|
if (pRawFile->LoadFrom.pvData != NULL) {
|
|
|
|
status = RtlpAllocateAndExtractString(
|
|
&pRawFile->LoadFrom,
|
|
&pFile->LoadFrom,
|
|
pState,
|
|
&OurMiniBuffer);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
pFile->ulFlags |= COOKEDFILE_LOADFROM_VALID;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the digest method. We don't store this anywhere, but we need to get it out
|
|
// into a UNICODE_STRING for our parsing purposes
|
|
//
|
|
if (pRawFile->DigestMethod.pvData != NULL) {
|
|
|
|
status = RtlEnsureUnicodeStringBufferSizeBytes(
|
|
&TempStringBuffer,
|
|
pRawFile->DigestMethod.ulCharacters * sizeof(WCHAR)
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlXmlExtentToString(pState, &pRawFile->DigestMethod, &TempStringBuffer.String, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
status = RtlpParseDigestMethod(&TempStringBuffer.String, &pFile->usDigestAlgorithm);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
pFile->ulFlags |= COOKEDFILE_DIGEST_ALG_VALID;
|
|
}
|
|
|
|
|
|
if (pRawFile->HashAlg.pvData != NULL) {
|
|
|
|
status = RtlEnsureUnicodeStringBufferSizeChars(
|
|
&TempStringBuffer,
|
|
pRawFile->HashAlg.ulCharacters
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlXmlExtentToString(pState, &pRawFile->HashAlg, &TempStringBuffer.String, NULL);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
status = RtlpParseHashAlg(&TempStringBuffer.String, &pFile->usHashAlgorithm);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
pFile->ulFlags |= COOKEDFILE_HASH_ALG_VALID;
|
|
}
|
|
|
|
|
|
//
|
|
// Special case here - we should extract the hash string, and then turn it into
|
|
// bytes.
|
|
//
|
|
if (pRawFile->HashValue.pvData != NULL) {
|
|
|
|
status = RtlEnsureUnicodeStringBufferSizeChars(
|
|
&TempStringBuffer,
|
|
pRawFile->HashValue.ulCharacters);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlXmlExtentToString(pState, &pRawFile->HashValue, &TempStringBuffer.String, NULL);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
if ((pRawFile->HashValue.ulCharacters % sizeof(WCHAR)) != 0) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto Exit;
|
|
}
|
|
else {
|
|
// Two characters per byte, high/low nibble
|
|
pFile->ulHashByteCount = pRawFile->HashValue.ulCharacters / 2;
|
|
}
|
|
|
|
status = RtlMiniBufferAllocateBytes(
|
|
&OurMiniBuffer,
|
|
pFile->ulHashByteCount,
|
|
&pFile->bHashData);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
status = RtlpConvertHexStringToBytes(
|
|
&TempStringBuffer.String,
|
|
pFile->bHashData,
|
|
pFile->ulHashByteCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
pFile->ulFlags |= COOKEDFILE_HASHDATA_VALID;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pCookedContent->ulFlags |= COOKEDMANIFEST_HAS_FILES;
|
|
*TargetBuffer = OurMiniBuffer;
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
RtlFreeUnicodeStringBuffer(&TempStringBuffer);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlConvertRawToCookedContent(
|
|
PRTL_MANIFEST_CONTENT_RAW pRawContent,
|
|
PXML_RAWTOKENIZATION_STATE pState,
|
|
PVOID pvOriginalRegion,
|
|
SIZE_T cbRegionSize,
|
|
PSIZE_T pcbRequired
|
|
)
|
|
{
|
|
PVOID pvCursor;
|
|
ULONG ul;
|
|
SIZE_T cbRemains = 0;
|
|
SIZE_T cbRequired = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
MINI_BUFFER OutputBuffer;
|
|
PMANIFEST_COOKED_DATA pCookedContent = NULL;
|
|
|
|
if (pcbRequired)
|
|
*pcbRequired = 0;
|
|
|
|
//
|
|
// Giving a NULL output buffer means you have zero bytes. Don't claim otherwise.
|
|
//
|
|
if (!pvOriginalRegion && (cbRegionSize != 0)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
//
|
|
// No output buffer, you have to let us tell you how much space you need.
|
|
//
|
|
else if ((pvOriginalRegion == NULL) && (pcbRequired == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// See how much we really need. I'm thinking we could do this in a single pass,
|
|
// and we'll probably want to for perf reasons, but for now we calculate, and then
|
|
// copy data around.
|
|
//
|
|
status = RtlpCalculateCookedManifestContentSize(
|
|
pRawContent,
|
|
pState,
|
|
&cbRequired);
|
|
|
|
//
|
|
// Too big - write the output size into the required space and return.
|
|
//
|
|
if (cbRequired > cbRegionSize) {
|
|
if (pcbRequired) *pcbRequired = cbRequired;
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
|
|
//
|
|
// Now, let's start writing data into the blob!
|
|
//
|
|
RtlMiniBufferInit(&OutputBuffer, pvOriginalRegion, cbRegionSize);
|
|
status = RtlMiniBufferAllocate(&OutputBuffer, MANIFEST_COOKED_DATA, &pCookedContent);
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
pCookedContent->cbTotalSize = cbRequired;
|
|
pCookedContent->ulFlags = 0;
|
|
|
|
status = RtlpAddRawFilesToCookedContent(
|
|
pRawContent,
|
|
pCookedContent,
|
|
pState,
|
|
&OutputBuffer);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = RtlpAddRawIdentitiesToCookedContent(
|
|
pRawContent,
|
|
pCookedContent,
|
|
pState,
|
|
&OutputBuffer);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|