#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 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 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; }