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.
6071 lines
174 KiB
6071 lines
174 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
xml.cxx
|
|
|
|
Abstract:
|
|
|
|
This file implements a xml policy store provider
|
|
|
|
Author:
|
|
|
|
Xiaoxi Tan (xtan) June-2001
|
|
|
|
--*/
|
|
|
|
#include "pch.hxx"
|
|
#define AZD_COMPONENT AZD_XML
|
|
#include <msxml2.h>
|
|
#include <aclapi.h>
|
|
#include "resource.h"
|
|
#include <shlwapi.h>
|
|
#include <lm.h>
|
|
#include <lmdfs.h>
|
|
|
|
#pragma warning ( push )
|
|
#pragma warning ( disable : 4127 ) // avoid warning in while(TRUE)
|
|
|
|
// defines
|
|
#define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0]))
|
|
// az object tag defines in xml store
|
|
// note, they are case sensitive
|
|
|
|
#define AZ_XML_TAG_AZSTORE L"AzAdminManager"
|
|
#define AZ_XML_TAG_APPLICATION L"AzApplication"
|
|
#define AZ_XML_TAG_OPERATION L"AzOperation"
|
|
#define AZ_XML_TAG_TASK L"AzTask"
|
|
#define AZ_XML_TAG_GROUP L"AzApplicationGroup"
|
|
#define AZ_XML_TAG_ROLE L"AzRole"
|
|
#define AZ_XML_TAG_SCOPE L"AzScope"
|
|
|
|
// az object data tag defines in xml store
|
|
#define AZ_XML_TAG_MEMBER L"Member"
|
|
#define AZ_XML_TAG_NONMEMBER L"NonMember"
|
|
#define AZ_XML_TAG_OPERATIONID L"OperationID"
|
|
#define AZ_XML_TAG_BIZRULE L"BizRule"
|
|
#define AZ_XML_TAG_BIZRULELANGUAGE L"BizRuleLanguage"
|
|
#define AZ_XML_TAG_LDAPQUERY L"LdapQuery"
|
|
|
|
// az link element defines
|
|
#define AZ_XML_TAG_LINK_OPERATION L"OperationLink"
|
|
#define AZ_XML_TAG_LINK_TASK L"TaskLink"
|
|
#define AZ_XML_TAG_LINK_APPMEMBER L"AppMemberLink"
|
|
#define AZ_XML_TAG_LINK_APPNONMEMBER L"AppNonMemberLink"
|
|
|
|
// az object attribute name defines in xml store
|
|
#define AZ_XML_TAG_ATTR_NAME L"Name"
|
|
#define AZ_XML_TAG_ATTR_DESCRIPTION L"Description"
|
|
#define AZ_XML_TAG_ATTR_GUID L"Guid"
|
|
#define AZ_XML_TAG_ATTR_GROUPTYPE L"GroupType"
|
|
#define AZ_XML_TAG_ATTR_TIMEOUT L"DomainTimeout"
|
|
#define AZ_XML_TAG_ATTR_APPCLSID L"ApplicationCLSID"
|
|
#define AZ_XML_TAG_ATTR_APPVERSION L"ApplicationVersion"
|
|
#define AZ_XML_TAG_ATTR_BIZRULEIP L"BizRuleImportedPath"
|
|
#define AZ_XML_TAG_ATTR_ROLEDEFINITION L"RoleDefinition"
|
|
#define AZ_XML_TAG_ATTR_MAXSCRIPTS L"MaxScripts"
|
|
#define AZ_XML_TAG_ATTR_SCRIPTTIMEOUT L"ScriptTimeout"
|
|
#define AZ_XML_TAG_ATTR_AUDITS L"Audits"
|
|
#define AZ_XML_TAG_ATTR_APPLICATIONDATA L"ApplicationData"
|
|
#define AZ_XML_TAG_ATTR_MAJOR_VERSION L"MajorVersion"
|
|
#define AZ_XML_TAG_ATTR_MINOR_VERSION L"MinorVersion"
|
|
|
|
// az group type values
|
|
#define AZ_XML_VAL_GROUPTYPE_LDAPQUERY L"LdapQuery"
|
|
#define AZ_XML_VAL_GROUPTYPE_BASIC L"Basic"
|
|
#define AZ_XML_VAL_TRUE L"True"
|
|
#define AZ_XML_VAL_FALSE L"False"
|
|
|
|
// xpath defines
|
|
#define AZ_XML_SELECTION_LANGUAGE L"SelectionLanguage"
|
|
#define AZ_XML_XPATH L"XPath"
|
|
|
|
//globals
|
|
WCHAR const * const g_pwszAzTrue=AZ_XML_VAL_TRUE;
|
|
WCHAR const * const g_pwszAzFalse=AZ_XML_VAL_FALSE;
|
|
WCHAR const * const g_pwszBasicGroup=AZ_XML_VAL_GROUPTYPE_BASIC;
|
|
WCHAR const * const g_pwszLdapGroup=AZ_XML_VAL_GROUPTYPE_LDAPQUERY;
|
|
PAZPE_AZROLES_INFO XmlAzrolesInfo;
|
|
|
|
//
|
|
// common XML parsing errors. Currently, we map all XML parsing errors
|
|
// to ERROR_BAD_FORMAT. If we decide to map different types of errors to
|
|
// different system error code, then these errors are roughly grouped together
|
|
// in that way. Please see myXMLErrorToHresult to real implemenation detail.
|
|
//
|
|
|
|
//
|
|
// The following errors are more closely tied to syntax (format) errors:
|
|
//
|
|
|
|
#define XML_E_MISSINGEQUALS 0xC00CE501 // Missing equals sign between attribute and attribute value.
|
|
#define XML_E_MISSINGQUOTE 0xC00CE502 // A string literal was expected, but no opening quote character was found.
|
|
#define XML_E_COMMENTSYNTAX 0xC00CE503 // Incorrect syntax was used in a comment.
|
|
#define XML_E_XMLDECLSYNTAX 0xC00CE507 // Invalid syntax for an XML declaration.
|
|
#define XML_E_MISSINGWHITESPACE 0xC00CE509 // Required white space was missing.
|
|
#define XML_E_EXPECTINGTAGEND 0xC00CE50A // The character '>' was expected.
|
|
#define XML_E_BADCHARINDTD 0xC00CE50B // Invalid character found in document type definition (DTD).
|
|
#define XML_E_MISSINGSEMICOLON 0xC00CE50D // A semicolon character was expected.
|
|
#define XML_E_UNBALANCEDPAREN 0xC00CE50F // Unbalanced parentheses.
|
|
#define XML_E_EXPECTINGOPENBRACKET 0xC00CE510 // An opening '[' character was expected.
|
|
#define XML_E_BADENDCONDSECT 0xC00CE511 // Invalid syntax in a conditional section.
|
|
#define XML_E_UNEXPECTED_WHITESPACE 0xC00CE513 // White space is not allowed at this location.
|
|
#define XML_E_INCOMPLETE_ENCODING 0xC00CE514 // End of file reached in invalid state for current encoding.
|
|
#define XML_E_BADCHARINMIXEDMODEL 0xC00CE515 // Mixed content model cannot contain this character.
|
|
#define XML_E_MISSING_STAR 0xC00CE516 // Mixed content model must be defined as zero or more('*').
|
|
#define XML_E_MISSING_PAREN 0xC00CE518 // Missing parenthesis.
|
|
#define XML_E_PIDECLSYNTAX 0xC00CE51A // Invalid syntax in processing instruction declaration.
|
|
#define XML_E_EXPECTINGCLOSEQUOTE 0xC00CE51B // A single or double closing quote character (\' or \") is missing.
|
|
#define XML_E_MULTIPLE_COLONS 0xC00CE51C // Multiple colons are not allowed in a name.
|
|
#define XML_E_WHITESPACEORQUESTIONMARK 0xC00CE520 // Expecting white space or '?'.
|
|
#define XML_E_UNEXPECTEDENDTAG 0xC00CE552 // End tag was not expected at this location.
|
|
#define XML_E_UNCLOSEDTAG 0xC00CE553 // The following tags were not closed: %1.
|
|
#define XML_E_DUPLICATEATTRIBUTE 0xC00CE554 // Duplicate attribute.
|
|
#define XML_E_MULTIPLEROOTS 0xC00CE555 // Only one top level element is allowed in an XML document.
|
|
#define XML_E_INVALIDATROOTLEVEL 0xC00CE556 // Invalid at the top level of the document.
|
|
#define XML_E_BADXMLDECL 0xC00CE557 // Invalid XML declaration.
|
|
#define XML_E_MISSINGROOT 0xC00CE558 // XML document must have a top level element.
|
|
#define XML_E_UNEXPECTEDEOF 0xC00CE559 // Unexpected end of file.
|
|
#define XML_E_BADPEREFINSUBSET 0xC00CE55A // Parameter entities cannot be used inside markup declarations in an internal subset.
|
|
#define XML_E_PE_NESTING 0xC00CE55B // The replacement text for a parameter entity must be properly nested with parenthesized groups.
|
|
#define XML_E_INVALID_CDATACLOSINGTAG 0xC00CE55C // The literal string ']]>' is not allowed in element content.
|
|
#define XML_E_UNCLOSEDPI 0xC00CE55D // Processing instruction was not closed.
|
|
#define XML_E_UNCLOSEDSTARTTAG 0xC00CE55E // Element was not closed.
|
|
#define XML_E_UNCLOSEDENDTAG 0xC00CE55F // End element was missing the character '>'.
|
|
#define XML_E_UNCLOSEDSTRING 0xC00CE560 // A string literal was not closed.
|
|
#define XML_E_UNCLOSEDCOMMENT 0xC00CE561 // A comment was not closed.
|
|
#define XML_E_UNCLOSEDDECL 0xC00CE562 // A declaration was not closed.
|
|
#define XML_E_UNCLOSEDMARKUPDECL 0xC00CE563 // A markup declaration was not closed.
|
|
#define XML_E_UNCLOSEDCDATA 0xC00CE564 // A CDATA section was not closed.
|
|
#define XML_E_BADDECLNAME 0xC00CE565 // Declaration has an invalid name.
|
|
#define XML_E_BADELEMENTINDTD 0xC00CE567 // An XML element is not allowed inside a DTD.
|
|
#define XML_E_RESERVEDNAMESPACE 0xC00CE568 // The namespace prefix is not allowed to start with the reserved string "xml".
|
|
#define XML_E_EXPECTING_VERSION 0xC00CE569 // The 'version' attribute is required at this location.
|
|
#define XML_E_EXPECTING_ENCODING 0xC00CE56A // The 'encoding' attribute is required at this location.
|
|
#define XML_E_EXPECTING_NAME 0xC00CE56B // At least one name is required at this location.
|
|
#define XML_E_UNEXPECTED_ATTRIBUTE 0xC00CE56C // The specified attribute was not expected at this location. The attribute may be case sensitive.
|
|
#define XML_E_ENDTAGMISMATCH 0xC00CE56D // End tag '%2' does not match the start tag '%1'.
|
|
#define XML_E_EXPECTING_NDATA 0xC00CE570 // NDATA keyword is missing.
|
|
#define XML_E_INVALID_TYPE 0xC00CE572 // Invalid type defined in ATTLIST.
|
|
#define XML_E_INVALIDXMLSPACE 0xC00CE573 // XML space attribute has invalid value. Must specify 'default' or 'preserve'.
|
|
#define XML_E_MULTI_ATTR_VALUE 0xC00CE574 // Multiple names found in attribute value when only one was expected.
|
|
#define XML_E_INVALID_PRESENCE 0xC00CE575 // Invalid ATTDEF declaration. Expected #REQUIRED, #IMPLIED or #FIXED.
|
|
#define XML_E_BADXMLCASE 0xC00CE576 // The name 'xml' is reserved and must be lowercase.
|
|
#define XML_E_CONDSECTINSUBSET 0xC00CE577 // Conditional sections are not allowed in an internal subset.
|
|
#define XML_E_INVALID_STANDALONE 0xC00CE579 // The standalone attribute must have the value 'yes' or 'no'.
|
|
#define XML_E_UNEXPECTED_STANDALONE 0xC00CE57A // The standalone attribute cannot be used in external entities.
|
|
#define XML_E_DTDELEMENT_OUTSIDE_DTD 0xC00CE580 // Cannot have a DTD declaration outside of a DTD.
|
|
#define XML_E_DUPLICATEDOCTYPE 0xC00CE581 // Cannot have multiple DOCTYPE declarations.
|
|
#define XML_E_CDATAINVALID 0xC00CE578 // CDATA is not allowed in a DTD.
|
|
#define XML_E_DOCTYPE_IN_DTD 0xC00CE57B // Cannot have a DOCTYPE declaration in a DTD.
|
|
#define XML_E_DOCTYPE_OUTSIDE_PROLOG 0xC00CE57E // Cannot have a DOCTYPE declaration outside of a prolog.
|
|
|
|
//
|
|
// The following errors are more closely tied to invalid data errors
|
|
// (could consider using ERROR_INVALID_DATA)
|
|
//
|
|
|
|
#define XML_E_BADCHARDATA 0xC00CE508 // An invalid character was found in text content.
|
|
#define XML_E_BADCHARINENTREF 0xC00CE50E // An invalid character was found inside an entity reference.
|
|
#define XML_E_BADCHARINDECL 0xC00CE50C // An invalid character was found inside a DTD declaration.
|
|
#define XML_E_BADCHARINMODEL 0xC00CE517 // Invalid character in content model.
|
|
#define XML_E_BADCHARINENUMERATION 0xC00CE519 // Invalid character found in ATTLIST enumeration.
|
|
#define XML_E_INVALID_DECIMAL 0xC00CE51D // Invalid character for decimal digit.
|
|
#define XML_E_INVALID_HEXIDECIMAL 0xC00CE51E // Invalid character for hexadecimal digit.
|
|
#define XML_E_BADSTARTNAMECHAR 0xC00CE504 // A name was started with an invalid character.
|
|
#define XML_E_BADNAMECHAR 0xC00CE505 // A name contained an invalid character.
|
|
#define XML_E_BADCHARINSTRING 0xC00CE506 // A string literal contained an invalid character.
|
|
#define XML_E_INVALID_UNICODE 0xC00CE51F // Invalid Unicode character value for this platform.
|
|
#define XML_E_BADEXTERNALID 0xC00CE566 // External ID is invalid.
|
|
#define XML_E_INVALID_MODEL 0xC00CE571 // Content model is invalid.
|
|
#define XML_E_MISSING_ENTITY 0xC00CE57C // Reference to undefined entity.
|
|
#define XML_E_ENTITYREF_INNAME 0xC00CE57D // Entity reference is resolved to an invalid name character.
|
|
#define XML_E_INVALID_VERSION 0xC00CE57F // Invalid version number.
|
|
|
|
//
|
|
// The following errors are more or less tied to not-supported type of errors
|
|
// (could consider using ERROR_NOT_SUPPORTED)
|
|
//
|
|
|
|
#define XML_E_INVALIDSWITCH 0xC00CE56F // Switch from current encoding to specified encoding not supported.
|
|
#define XML_E_INVALIDENCODING 0xC00CE56E // System does not support the specified encoding.
|
|
|
|
//
|
|
// The following errors are not mapped to any system error codes at this time.
|
|
//
|
|
|
|
#define XML_E_FORMATINDEX_BADINDEX 0xC00CE306 // The value passed in to formatIndex must be greater than 0.
|
|
#define XML_E_FORMATINDEX_BADFORMAT 0xC00CE307 // Invalid format string.
|
|
#define XML_E_EXPECTED_TOKEN 0xC00CE380 // Expected token %1 found %2.
|
|
#define XML_E_UNEXPECTED_TOKEN 0xC00CE381 // Unexpected token %1.
|
|
#define XML_E_INTERNALERROR 0xC00CE512 // Internal error.
|
|
#define XML_E_SUSPENDED 0xC00CE550 // The parser is suspended.
|
|
#define XML_E_STOPPED 0xC00CE551 // The parser is stopped.
|
|
#define XML_E_RESOURCE 0xC00CE582 // Error processing resource '%1'.
|
|
|
|
|
|
// macro defines
|
|
|
|
//
|
|
// if object is created or dirty bit
|
|
//
|
|
#define ObjectIsDirty(_go, _dirtyBit) \
|
|
(0x0 != (XmlAzrolesInfo->AzpeDirtyBits(_go) & (_dirtyBit)))
|
|
|
|
//
|
|
// _JumpIfErrorOrPressOn should be used in any routines that are called from
|
|
// application (through COM API) or from az core for update cache
|
|
// if it is from application, it should return error immediately.
|
|
// if it is from az core, it should press on the do the next process.
|
|
// The routine uses _JumpIfErrorOrPressOn should use _HandlePressOnError too
|
|
// Arguments:
|
|
// _hr - the current error code we check
|
|
// _hr2 - current press on error code. It should be init to S_OK
|
|
// _label - error jump label
|
|
// _lFlag - persist flag to indicate where it comes from
|
|
// _fPressOn - flag to indicate should presson on or not
|
|
// pszMsg - back trace message
|
|
//
|
|
#define _JumpIfErrorOrPressOn(_hr, _hr2, _label, _lFlag, _fPressOn, pszMsg) \
|
|
{ \
|
|
if (AZPE_FLAGS_PERSIST_UPDATE_CACHE & (_lFlag)) \
|
|
{ \
|
|
if (S_OK != _hr) \
|
|
{ \
|
|
if (_fPressOn) \
|
|
{ \
|
|
if (S_OK == _hr2) \
|
|
{ \
|
|
_hr2 = _hr; \
|
|
} \
|
|
} \
|
|
else \
|
|
{ \
|
|
_JumpError(_hr, _label, pszMsg); \
|
|
} \
|
|
_PrintError(_hr, pszMsg); \
|
|
} \
|
|
} \
|
|
else \
|
|
{ \
|
|
_JumpIfError(_hr, _label, pszMsg); \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// _HandlePressOnError should be used in any routines call
|
|
// _JumpIfErrorOrPressOn. It is the replacement of regular
|
|
// hr = S_OK at the end of each sub-routine
|
|
//
|
|
#define _HandlePressOnError(_hr, _hr2) \
|
|
{ \
|
|
if (S_OK != _hr2) \
|
|
{ \
|
|
_hr = _hr2; \
|
|
} \
|
|
else \
|
|
{ \
|
|
_hr = S_OK; \
|
|
} \
|
|
}
|
|
|
|
// xml storage
|
|
|
|
// Each provider returns a single PVOID from *PersistOpen.
|
|
// That PVOID is a pointer to whatever context the provider needs to maintain a
|
|
// description of the local storage.
|
|
// The structure below is that context for the xml store provider.
|
|
|
|
typedef struct _AZP_XML_CONTEXT
|
|
{
|
|
// interface pointer to xml document object
|
|
IXMLDOMDocument2 *pDoc;
|
|
|
|
// Policy URL
|
|
LPCWSTR pwszPolicyUrl;
|
|
|
|
// Handle to AzAuthorizationStore
|
|
AZPE_OBJECT_HANDLE hAzAuthorizationStore;
|
|
|
|
//
|
|
// TRUE if the file is writable
|
|
LONG IsWritable;
|
|
|
|
// TRUE if the current user has SE_SECURITY_PRIVILEGE on the server containing the store.
|
|
BOOLEAN HasSecurityPrivilege;
|
|
|
|
FILETIME FTLastWrite;
|
|
|
|
} AZP_XML_CONTEXT, *PAZP_XML_CONTEXT;
|
|
|
|
//
|
|
// data structure for object property/attribute
|
|
//
|
|
typedef struct _AZ_PROP_ENTRY {
|
|
ULONG lPropId; //attribute/property property ID
|
|
ENUM_AZ_DATATYPE lDataType; //data type
|
|
BOOL fSingle; //single occurence, flag to determine call on AddPropertyItem
|
|
ULONG lDirtyBit; //property dirty bit
|
|
WCHAR const *pwszTag; //tag in xml
|
|
} AZ_PROP_ENTRY;
|
|
|
|
typedef struct _AZ_CHILD_ENTRY {
|
|
WCHAR const *pwszChildTag; //child object tag
|
|
ULONG ChildType; //child object type
|
|
} AZ_CHILD_ENTRY;
|
|
|
|
typedef struct _AZ_SUBMIT_LOAD_ENTRY {
|
|
WCHAR const *pwszTag; // object node tag
|
|
WCHAR const * const *rgpwszLkTag; // for linked item deletion
|
|
AZ_PROP_ENTRY *rgpAttrs; // array of attr entry data
|
|
AZ_PROP_ENTRY *rgpEles; // array of element entries
|
|
AZ_CHILD_ENTRY *rgpChildren; // array of children
|
|
} AZ_SUBMIT_LOAD_ENTRY;
|
|
|
|
//
|
|
// Procedures implemented by the xml provider
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
XMLPersistOpen(
|
|
IN LPCWSTR PolicyUrl,
|
|
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
|
|
IN ULONG Flags,
|
|
IN BOOL CreatePolicy,
|
|
OUT PAZPE_PERSIST_CONTEXT PersistContext,
|
|
OUT LPWSTR *pwszTargetMachine
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
XMLPersistUpdateCache(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext,
|
|
IN ULONG lPersistFlags,
|
|
OUT ULONG* pulUpdateFlag
|
|
);
|
|
|
|
VOID
|
|
WINAPI
|
|
XMLPersistClose(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
XMLPersistSubmit(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext,
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG Flags,
|
|
IN BOOLEAN DeleteMe
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
XMLPersistRefresh(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext,
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags
|
|
);
|
|
|
|
|
|
//
|
|
// Define a provider info telling the core our interface
|
|
//
|
|
AZPE_PROVIDER_INFO XmlProviderInfo = {
|
|
AZPE_PROVIDER_INFO_VERSION_1,
|
|
AZ_XML_PROVIDER_NAME,
|
|
|
|
XMLPersistOpen,
|
|
XMLPersistUpdateCache,
|
|
XMLPersistClose,
|
|
XMLPersistSubmit,
|
|
XMLPersistRefresh
|
|
};
|
|
|
|
|
|
//
|
|
// following data entries or tables handling all object submit
|
|
//
|
|
|
|
//
|
|
// authorization store entry
|
|
//
|
|
AZ_PROP_ENTRY g_AdAttrs[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_AZSTORE_DOMAIN_TIMEOUT, ENUM_AZ_LONG, TRUE, AZ_DIRTY_AZSTORE_DOMAIN_TIMEOUT, AZ_XML_TAG_ATTR_TIMEOUT},
|
|
{AZ_PROP_AZSTORE_SCRIPT_ENGINE_TIMEOUT, ENUM_AZ_LONG, TRUE, AZ_DIRTY_AZSTORE_SCRIPT_ENGINE_TIMEOUT, AZ_XML_TAG_ATTR_SCRIPTTIMEOUT},
|
|
{AZ_PROP_AZSTORE_MAX_SCRIPT_ENGINES, ENUM_AZ_LONG, TRUE, AZ_DIRTY_AZSTORE_MAX_SCRIPT_ENGINES, AZ_XML_TAG_ATTR_MAXSCRIPTS},
|
|
{AZ_PROP_GENERATE_AUDITS, ENUM_AZ_BOOL, TRUE, AZ_DIRTY_GENERATE_AUDITS, AZ_XML_TAG_ATTR_AUDITS},
|
|
{AZ_PROP_APPLICATION_DATA, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_DATA, AZ_XML_TAG_ATTR_APPLICATIONDATA},
|
|
{AZ_PROP_AZSTORE_MAJOR_VERSION, ENUM_AZ_LONG, TRUE, AZ_DIRTY_AZSTORE_MAJOR_VERSION, AZ_XML_TAG_ATTR_MAJOR_VERSION},
|
|
{AZ_PROP_AZSTORE_MINOR_VERSION, ENUM_AZ_LONG, TRUE, AZ_DIRTY_AZSTORE_MINOR_VERSION, AZ_XML_TAG_ATTR_MINOR_VERSION},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
AZ_CHILD_ENTRY g_AdChild[] = {
|
|
/*
|
|
{pwszChildTag, ChildType},
|
|
*/
|
|
{AZ_XML_TAG_APPLICATION, OBJECT_TYPE_APPLICATION},
|
|
{AZ_XML_TAG_GROUP, OBJECT_TYPE_GROUP},
|
|
{NULL, 0}, //terminator entry
|
|
};
|
|
|
|
//
|
|
// application entry
|
|
//
|
|
AZ_PROP_ENTRY g_ApAttrs[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_APPLICATION_AUTHZ_INTERFACE_CLSID, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_AUTHZ_INTERFACE_CLSID, AZ_XML_TAG_ATTR_APPCLSID},
|
|
{AZ_PROP_APPLICATION_VERSION, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_VERSION, AZ_XML_TAG_ATTR_APPVERSION},
|
|
{AZ_PROP_GENERATE_AUDITS, ENUM_AZ_BOOL, TRUE, AZ_DIRTY_GENERATE_AUDITS, AZ_XML_TAG_ATTR_AUDITS},
|
|
{AZ_PROP_APPLICATION_DATA, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_DATA, AZ_XML_TAG_ATTR_APPLICATIONDATA},
|
|
{0, ENUM_AZ_BOOL, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
AZ_CHILD_ENTRY g_ApChild[] = {
|
|
/*
|
|
{pwszChildTag, ChildType},
|
|
*/
|
|
{AZ_XML_TAG_OPERATION, OBJECT_TYPE_OPERATION},
|
|
{AZ_XML_TAG_TASK, OBJECT_TYPE_TASK},
|
|
{AZ_XML_TAG_GROUP, OBJECT_TYPE_GROUP},
|
|
{AZ_XML_TAG_SCOPE, OBJECT_TYPE_SCOPE},
|
|
{AZ_XML_TAG_ROLE, OBJECT_TYPE_ROLE},
|
|
{NULL, 0}, //terminator entry
|
|
};
|
|
|
|
//
|
|
// operation entry
|
|
//
|
|
WCHAR const * const g_OpLkTags[] = {
|
|
AZ_XML_TAG_LINK_OPERATION,
|
|
NULL,
|
|
};
|
|
|
|
AZ_PROP_ENTRY g_OpAttrs[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_APPLICATION_DATA, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_DATA, AZ_XML_TAG_ATTR_APPLICATIONDATA},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
AZ_PROP_ENTRY g_OpEles[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_OPERATION_ID, ENUM_AZ_LONG, TRUE, AZ_DIRTY_OPERATION_ID, AZ_XML_TAG_OPERATIONID},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
|
|
//
|
|
// task entry
|
|
//
|
|
WCHAR const * const g_TkLkTags[] = {
|
|
AZ_XML_TAG_LINK_TASK,
|
|
NULL,
|
|
};
|
|
AZ_PROP_ENTRY g_TkAttrs[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_TASK_BIZRULE_IMPORTED_PATH, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_TASK_BIZRULE_IMPORTED_PATH, AZ_XML_TAG_ATTR_BIZRULEIP},
|
|
{AZ_PROP_TASK_IS_ROLE_DEFINITION, ENUM_AZ_BOOL, TRUE, AZ_DIRTY_TASK_IS_ROLE_DEFINITION, AZ_XML_TAG_ATTR_ROLEDEFINITION},
|
|
{AZ_PROP_APPLICATION_DATA, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_DATA, AZ_XML_TAG_ATTR_APPLICATIONDATA},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
AZ_PROP_ENTRY g_TkEles[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_TASK_BIZRULE_LANGUAGE, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_TASK_BIZRULE_LANGUAGE, AZ_XML_TAG_BIZRULELANGUAGE},
|
|
{AZ_PROP_TASK_BIZRULE, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_TASK_BIZRULE, AZ_XML_TAG_BIZRULE},
|
|
{AZ_PROP_TASK_OPERATIONS, ENUM_AZ_GUID_ARRAY, FALSE, AZ_DIRTY_TASK_OPERATIONS, AZ_XML_TAG_LINK_OPERATION},
|
|
{AZ_PROP_TASK_TASKS, ENUM_AZ_GUID_ARRAY, FALSE, AZ_DIRTY_TASK_TASKS, AZ_XML_TAG_LINK_TASK},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
|
|
//
|
|
// group entry
|
|
//
|
|
WCHAR const * const g_GpLkTags[] = {
|
|
AZ_XML_TAG_LINK_APPMEMBER,
|
|
AZ_XML_TAG_LINK_APPNONMEMBER,
|
|
NULL,
|
|
};
|
|
AZ_PROP_ENTRY g_GpAttrs[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_GROUP_TYPE, ENUM_AZ_GROUP_TYPE, TRUE, AZ_DIRTY_GROUP_TYPE, AZ_XML_TAG_ATTR_GROUPTYPE},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
AZ_PROP_ENTRY g_GpEles[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_GROUP_LDAP_QUERY, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_GROUP_LDAP_QUERY, AZ_XML_TAG_LDAPQUERY},
|
|
{AZ_PROP_GROUP_MEMBERS, ENUM_AZ_SID_ARRAY, FALSE, AZ_DIRTY_GROUP_MEMBERS, AZ_XML_TAG_MEMBER},
|
|
{AZ_PROP_GROUP_NON_MEMBERS, ENUM_AZ_SID_ARRAY, FALSE, AZ_DIRTY_GROUP_NON_MEMBERS, AZ_XML_TAG_NONMEMBER},
|
|
{AZ_PROP_GROUP_APP_MEMBERS, ENUM_AZ_GUID_ARRAY, FALSE, AZ_DIRTY_GROUP_APP_MEMBERS, AZ_XML_TAG_LINK_APPMEMBER},
|
|
{AZ_PROP_GROUP_APP_NON_MEMBERS, ENUM_AZ_GUID_ARRAY, FALSE, AZ_DIRTY_GROUP_APP_NON_MEMBERS, AZ_XML_TAG_LINK_APPNONMEMBER},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
|
|
//
|
|
// scope entry
|
|
//
|
|
AZ_PROP_ENTRY g_SpAttrs[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_APPLICATION_DATA, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_DATA, AZ_XML_TAG_ATTR_APPLICATIONDATA},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
|
|
AZ_CHILD_ENTRY g_SpChild[] = {
|
|
/*
|
|
{pwszChildTag, ChildType},
|
|
*/
|
|
{AZ_XML_TAG_TASK, OBJECT_TYPE_TASK},
|
|
{AZ_XML_TAG_GROUP, OBJECT_TYPE_GROUP},
|
|
{AZ_XML_TAG_ROLE, OBJECT_TYPE_ROLE},
|
|
{NULL, 0}, //terminator entry
|
|
};
|
|
|
|
//
|
|
// role entry
|
|
//
|
|
AZ_PROP_ENTRY g_RlAttrs[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_APPLICATION_DATA, ENUM_AZ_BSTR, TRUE, AZ_DIRTY_APPLICATION_DATA, AZ_XML_TAG_ATTR_APPLICATIONDATA},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
AZ_PROP_ENTRY g_RlEles[] = {
|
|
/*
|
|
{lPropId, lDataType, fSingle, lDirtyBit, pwszTag},
|
|
*/
|
|
{AZ_PROP_ROLE_APP_MEMBERS, ENUM_AZ_GUID_ARRAY, FALSE, AZ_DIRTY_ROLE_APP_MEMBERS, AZ_XML_TAG_LINK_APPMEMBER},
|
|
{AZ_PROP_ROLE_MEMBERS, ENUM_AZ_SID_ARRAY, FALSE, AZ_DIRTY_ROLE_MEMBERS, AZ_XML_TAG_MEMBER},
|
|
{AZ_PROP_ROLE_OPERATIONS, ENUM_AZ_GUID_ARRAY, FALSE, AZ_DIRTY_ROLE_OPERATIONS, AZ_XML_TAG_LINK_OPERATION},
|
|
{AZ_PROP_ROLE_TASKS, ENUM_AZ_GUID_ARRAY, FALSE, AZ_DIRTY_ROLE_TASKS, AZ_XML_TAG_LINK_TASK},
|
|
{0, ENUM_AZ_LONG, FALSE, 0, NULL}, //terminator entry
|
|
};
|
|
|
|
//
|
|
// ***IMPORTANT***, keep the order of element the same as defined
|
|
// in OBJECT_TYPE_* (see genobj.h)
|
|
//
|
|
AZ_SUBMIT_LOAD_ENTRY g_SubmitLoadTable[OBJECT_TYPE_COUNT] = {
|
|
/*
|
|
{pwszTag, rgpwszLkTag,rgpAttrs, rgpEles, rgpChild},
|
|
*/
|
|
|
|
{AZ_XML_TAG_AZSTORE, NULL, g_AdAttrs, NULL, g_AdChild}, //OBJECT_TYPE_AZAUTHSTORE
|
|
{AZ_XML_TAG_APPLICATION, NULL, g_ApAttrs, NULL, g_ApChild}, //OBJECT_TYPE_APPLICATION
|
|
{AZ_XML_TAG_OPERATION, g_OpLkTags, g_OpAttrs, g_OpEles, NULL}, //OBJECT_TYPE_OPERATION
|
|
{AZ_XML_TAG_TASK, g_TkLkTags, g_TkAttrs, g_TkEles, NULL}, //OBJECT_TYPE_TASK
|
|
{AZ_XML_TAG_SCOPE, NULL, g_SpAttrs, NULL, g_SpChild}, //OBJECT_TYPE_SCOPE
|
|
{AZ_XML_TAG_GROUP, g_GpLkTags, g_GpAttrs, g_GpEles, NULL}, //OBJECT_TYPE_GROUP
|
|
{AZ_XML_TAG_ROLE, NULL, g_RlAttrs, g_RlEles, NULL}, //OBJECT_TYPE_ROLE
|
|
};
|
|
|
|
//
|
|
// rights for users
|
|
//
|
|
|
|
#define AZ_POLICY_AZSTORE_MASK_XML FILE_ALL_ACCESS
|
|
#define AZ_POLICY_READER_MASK_XML FILE_GENERIC_READ
|
|
#define AZ_POLICY_ACE_FLAGS_XML 0x0
|
|
|
|
//
|
|
// User rights for XML policy admins
|
|
//
|
|
|
|
AZP_POLICY_USER_RIGHTS PolicyAdminsRights = {
|
|
|
|
AZ_POLICY_AZSTORE_MASK_XML,
|
|
AZ_POLICY_ACE_FLAGS_XML
|
|
};
|
|
|
|
PAZP_POLICY_USER_RIGHTS XMLPolicyAdminsRights[] = {
|
|
|
|
&PolicyAdminsRights,
|
|
NULL
|
|
};
|
|
|
|
//
|
|
// User rights for XML policy readers
|
|
//
|
|
|
|
AZP_POLICY_USER_RIGHTS PolicyReadersRights = {
|
|
|
|
AZ_POLICY_READER_MASK_XML,
|
|
AZ_POLICY_ACE_FLAGS_XML
|
|
};
|
|
|
|
PAZP_POLICY_USER_RIGHTS XMLPolicyReadersRights[] = {
|
|
|
|
&PolicyReadersRights,
|
|
NULL
|
|
};
|
|
|
|
//
|
|
// Rights for the SACL.
|
|
//
|
|
AZP_POLICY_USER_RIGHTS XMLSaclRights = {
|
|
|
|
DELETE|WRITE_DAC|WRITE_OWNER|FILE_GENERIC_WRITE,
|
|
0 // No Flags
|
|
};
|
|
|
|
//
|
|
// the following functions are copied from UI (shell) team
|
|
// with minor changes
|
|
//
|
|
BOOL
|
|
IsDfsPath(
|
|
IN PWSTR pszPath,
|
|
OUT PWSTR pszServer,
|
|
IN UINT cchServer
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function detects if a UNC file path is a DFS path and if so,
|
|
the real server name for the DFS path is returned in the pszServer buffer.
|
|
|
|
Arguments:
|
|
|
|
pszPath - a UNC file path to detect
|
|
|
|
pszServer - buffer to receive the real server name
|
|
|
|
cchServer - length (tchars) of the pszServer buffer
|
|
|
|
--*/
|
|
{
|
|
BOOL bIsDfs = FALSE;
|
|
PWSTR pszTemp=NULL;
|
|
PDFS_INFO_3 pDI3 = NULL;
|
|
WCHAR szServer[MAX_PATH];
|
|
|
|
if (pszPath == NULL || !PathIsUNC(pszPath))
|
|
return FALSE; // local machine
|
|
|
|
//
|
|
// allocate a temp buffer
|
|
//
|
|
pszTemp = (PWSTR)LocalAlloc(LPTR, (wcslen(pszPath)+1)*sizeof(TCHAR));
|
|
|
|
if ( pszTemp == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy(pszTemp, pszPath);
|
|
|
|
//
|
|
// Check for DFS
|
|
//
|
|
for (;;)
|
|
{
|
|
DWORD dwErr;
|
|
|
|
__try
|
|
{
|
|
// This is delay-loaded by the linker, so
|
|
// must wrap with an exception handler.
|
|
dwErr = NetDfsGetClientInfo(pszTemp,
|
|
NULL,
|
|
NULL,
|
|
3,
|
|
(LPBYTE*)&pDI3);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
LocalFree(pszTemp);
|
|
return FALSE;
|
|
}
|
|
|
|
if (NERR_Success == dwErr)
|
|
{
|
|
for (ULONG i = 0; i < pDI3->NumberOfStorages; i++)
|
|
{
|
|
if (DFS_STORAGE_STATE_ONLINE & pDI3->Storage[i].State)
|
|
{
|
|
bIsDfs = TRUE;
|
|
|
|
szServer[0] = L'\\';
|
|
szServer[1] = L'\\';
|
|
wcsncpy(&szServer[2], pDI3->Storage[i].ServerName, ARRAYLEN(szServer)-2);
|
|
|
|
//
|
|
// If this server is active, quit looking
|
|
//
|
|
if (DFS_STORAGE_STATE_ACTIVE & pDI3->Storage[i].State)
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (NERR_DfsNoSuchVolume == dwErr)
|
|
{
|
|
//
|
|
// If we're at the root, then we can't go any farther.
|
|
//
|
|
if (PathIsRoot(pszTemp))
|
|
break;
|
|
|
|
//
|
|
// Remove the last path element and try again, if nothing is
|
|
// removed, break, don't go in infinite loop
|
|
//
|
|
if (!PathRemoveFileSpec(pszTemp))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Some other error, bail
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bIsDfs)
|
|
{
|
|
//
|
|
// copy the server name to the output buffer
|
|
//
|
|
wcsncpy(pszServer, szServer, cchServer);
|
|
|
|
}
|
|
|
|
//
|
|
// free the alloated buffer
|
|
//
|
|
if (NULL != pDI3)
|
|
NetApiBufferFree(pDI3);
|
|
|
|
LocalFree(pszTemp);
|
|
|
|
return bIsDfs;
|
|
}
|
|
|
|
|
|
DWORD
|
|
_WNetGetConnection(
|
|
IN PWSTR pszLocal,
|
|
OUT PWSTR pszRemote,
|
|
IN OUT LPDWORD pdwLen
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function get an UNC path for a mapped netowrk path.
|
|
It fails if the path is not a valid network path.
|
|
|
|
Arguments:
|
|
|
|
pszLocal - the local mapped drive letter
|
|
|
|
pszRemote - the mapped remote share name
|
|
|
|
pdwLen - the length of the buffer required
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = ERROR_PROC_NOT_FOUND;
|
|
|
|
// This is the only function we call in mpr.dll, and it's delay-loaded
|
|
// so wrap it with SEH.
|
|
__try
|
|
{
|
|
dwErr = WNetGetConnection(pszLocal, pszRemote, pdwLen);
|
|
}
|
|
__finally
|
|
{
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
myGetRemotePath(
|
|
IN PCWSTR pszInName,
|
|
OUT PWSTR *ppszOutName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Return UNC version of a path that is mapped to a remote machine
|
|
|
|
|
|
Arguments:
|
|
pszInName - initial path
|
|
|
|
ppszOutName - UNC path returned, Must be freed by AzpFreeHeap
|
|
|
|
Return value:
|
|
|
|
--*/
|
|
{
|
|
|
|
if(!pszInName || !ppszOutName)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppszOutName = NULL;
|
|
|
|
DWORD dwErr;
|
|
WCHAR szLocalName[3];
|
|
|
|
WCHAR szRemoteName[MAX_PATH];
|
|
DWORD dwLen = ARRAYLEN(szRemoteName);
|
|
szRemoteName[0] = L'\0';
|
|
|
|
//
|
|
// buffer to the drive letter only
|
|
//
|
|
szLocalName[0] = pszInName[0];
|
|
szLocalName[1] = pszInName[1];
|
|
szLocalName[2] = TEXT('\0');
|
|
|
|
//
|
|
// try to make the connection alive by accessing the remote share
|
|
// but do not care error now (if it cannot connect, the next call will fail)
|
|
//
|
|
GetFileAttributes(pszInName);
|
|
|
|
//
|
|
// get connection for the drive letter
|
|
//
|
|
dwErr = _WNetGetConnection(szLocalName, szRemoteName, &dwLen);
|
|
|
|
if (NO_ERROR == dwErr)
|
|
{
|
|
//
|
|
// success, get the mapped path (local or remote)
|
|
//
|
|
dwLen = (DWORD)wcslen(szRemoteName);
|
|
|
|
} else if ( ERROR_MORE_DATA != dwErr ) {
|
|
//
|
|
// other errors including ERROR_NOT_CONNECTED
|
|
// return the error
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// if dwErr == ERROR_MORE_DATA, dwLen already has the correct value
|
|
// Skip the drive letter and add the length of the rest of the path
|
|
// (including NULL)
|
|
//
|
|
|
|
pszInName += 2;
|
|
dwLen += (DWORD)wcslen(pszInName) + 1;
|
|
|
|
//
|
|
// We should never get incomplete paths, so we should always
|
|
// see a backslash after the "X:". If this isn't true, then
|
|
// we should call GetFullPathName.
|
|
//
|
|
ASSERT(TEXT('\\') == *pszInName);
|
|
|
|
//
|
|
// Allocate the return buffer
|
|
//
|
|
*ppszOutName = (PWSTR)AzpAllocateHeap(dwLen * sizeof(WCHAR), "XMNAME" );
|
|
if (!*ppszOutName)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (ERROR_MORE_DATA == dwErr)
|
|
{
|
|
//
|
|
// Try again with the bigger buffer
|
|
//
|
|
dwErr = _WNetGetConnection(szLocalName, *ppszOutName, &dwLen);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// WNetGetConnection succeeded from the first call. Copy the result
|
|
//
|
|
wcscpy(*ppszOutName, szRemoteName);
|
|
}
|
|
|
|
//
|
|
// Copy the rest of the path
|
|
//
|
|
wcscat(*ppszOutName, pszInName);
|
|
|
|
|
|
CleanUp:
|
|
|
|
if (NO_ERROR != dwErr && *ppszOutName)
|
|
{
|
|
AzpFreeHeap(*ppszOutName);
|
|
*ppszOutName = NULL;
|
|
}
|
|
|
|
|
|
return (dwErr);
|
|
}
|
|
|
|
|
|
void
|
|
myGetVolumeInfo(
|
|
IN PCWSTR pszPath,
|
|
OUT PWSTR pszVolume,
|
|
IN ULONG cchVolume
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This function queries the volume information for the given path
|
|
|
|
Arguments:
|
|
|
|
pszPath - a file path to detect
|
|
|
|
pszVolume - buffer to receive the volume name
|
|
|
|
cchVolume - length (tchars) of the output buffer
|
|
|
|
--*/
|
|
{
|
|
if ( pszPath == NULL || pszVolume == NULL ) {
|
|
return;
|
|
}
|
|
|
|
WCHAR szVolume[MAX_PATH+1];
|
|
|
|
//
|
|
// The path can be DFS or contain volume mount points, so start
|
|
// with the full path and try GetVolumeInformation on successively
|
|
// shorter paths until it succeeds or we run out of path.
|
|
//
|
|
// However, if it's a volume mount point, we're interested in the
|
|
// the host folder's volume so back up one level to start. The
|
|
// child volume is handled separately (see AddMountedVolumePage).
|
|
//
|
|
|
|
wcsncpy(szVolume, pszPath, ARRAYLEN(szVolume));
|
|
szVolume[MAX_PATH] = '\0';
|
|
|
|
//
|
|
// input volume name is always a XML file name
|
|
//
|
|
PathRemoveFileSpec(szVolume);
|
|
|
|
for (;;)
|
|
{
|
|
PathAddBackslash(szVolume); // GetVolumeInformation likes a trailing '\'
|
|
|
|
DWORD dwFlags = 0;
|
|
if (GetVolumeInformation(szVolume,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dwFlags,
|
|
NULL,
|
|
0))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Access denied implies that we've reached the deepest volume
|
|
// in the path; we just can't get the flags. It also implies
|
|
// security, so assume persistent acls.
|
|
if (ERROR_ACCESS_DENIED == GetLastError())
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If we're at the root, then we can't go any farther.
|
|
if (PathIsRoot(szVolume))
|
|
break;
|
|
|
|
// Remove the last path element and try again
|
|
PathRemoveBackslash(szVolume);
|
|
//if nothing is removed break instead of going in infinite loop
|
|
if (!PathRemoveFileSpec(szVolume))
|
|
break;
|
|
}
|
|
|
|
PathRemoveBackslash(szVolume);
|
|
wcsncpy(pszVolume, szVolume, cchVolume);
|
|
|
|
}
|
|
|
|
DWORD
|
|
myXMLErrorToWin32 (
|
|
IN LONG lErrCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine maps XML parsing error codes to system error codes.
|
|
|
|
Arguments:
|
|
|
|
lErrCode - the XML parsing error to be translated.
|
|
|
|
Return Value:
|
|
|
|
various win32 system error codes.
|
|
|
|
Note:
|
|
|
|
(1) Currently, we map all errors related to parsing to ERROR_BAD_FORMAT.
|
|
Other errors are not translated at all. However, this is a very coarse
|
|
mapping and we might want to do a more fine-grained mapping at a
|
|
later time.
|
|
|
|
(2) All these XML_E_xxx are locally defined according to MSDN.
|
|
Please see the definition of these constants for detail. Unfortunately I
|
|
have to do that because these error codes are not available anyway I can find.
|
|
|
|
--*/
|
|
{
|
|
switch (lErrCode)
|
|
{
|
|
|
|
//
|
|
// the following errors match pretty well with ERROR_BAD_FORMAT
|
|
//
|
|
|
|
case XML_E_MISSINGEQUALS:
|
|
case XML_E_MISSINGQUOTE:
|
|
case XML_E_COMMENTSYNTAX:
|
|
case XML_E_XMLDECLSYNTAX:
|
|
case XML_E_MISSINGWHITESPACE:
|
|
case XML_E_EXPECTINGTAGEND:
|
|
case XML_E_BADCHARINDTD:
|
|
case XML_E_MISSINGSEMICOLON:
|
|
case XML_E_UNBALANCEDPAREN:
|
|
case XML_E_EXPECTINGOPENBRACKET:
|
|
case XML_E_BADENDCONDSECT:
|
|
case XML_E_UNEXPECTED_WHITESPACE:
|
|
case XML_E_INCOMPLETE_ENCODING:
|
|
case XML_E_BADCHARINMIXEDMODEL:
|
|
case XML_E_MISSING_STAR:
|
|
case XML_E_MISSING_PAREN:
|
|
case XML_E_PIDECLSYNTAX:
|
|
case XML_E_EXPECTINGCLOSEQUOTE:
|
|
case XML_E_MULTIPLE_COLONS:
|
|
case XML_E_WHITESPACEORQUESTIONMARK:
|
|
case XML_E_UNEXPECTEDENDTAG:
|
|
case XML_E_UNCLOSEDTAG:
|
|
case XML_E_DUPLICATEATTRIBUTE:
|
|
case XML_E_MULTIPLEROOTS:
|
|
case XML_E_INVALIDATROOTLEVEL:
|
|
case XML_E_BADXMLDECL:
|
|
case XML_E_MISSINGROOT:
|
|
case XML_E_UNEXPECTEDEOF:
|
|
case XML_E_BADPEREFINSUBSET:
|
|
case XML_E_PE_NESTING:
|
|
case XML_E_INVALID_CDATACLOSINGTAG:
|
|
case XML_E_UNCLOSEDPI:
|
|
case XML_E_UNCLOSEDSTARTTAG:
|
|
case XML_E_UNCLOSEDENDTAG:
|
|
case XML_E_UNCLOSEDSTRING:
|
|
case XML_E_UNCLOSEDCOMMENT:
|
|
case XML_E_UNCLOSEDDECL:
|
|
case XML_E_UNCLOSEDMARKUPDECL:
|
|
case XML_E_UNCLOSEDCDATA:
|
|
case XML_E_BADDECLNAME:
|
|
case XML_E_BADELEMENTINDTD:
|
|
case XML_E_RESERVEDNAMESPACE:
|
|
case XML_E_EXPECTING_VERSION:
|
|
case XML_E_EXPECTING_ENCODING:
|
|
case XML_E_EXPECTING_NAME:
|
|
case XML_E_UNEXPECTED_ATTRIBUTE:
|
|
case XML_E_ENDTAGMISMATCH:
|
|
case XML_E_EXPECTING_NDATA:
|
|
case XML_E_INVALID_TYPE:
|
|
case XML_E_INVALIDXMLSPACE:
|
|
case XML_E_MULTI_ATTR_VALUE:
|
|
case XML_E_INVALID_PRESENCE:
|
|
case XML_E_BADXMLCASE:
|
|
case XML_E_CONDSECTINSUBSET:
|
|
case XML_E_INVALID_STANDALONE:
|
|
case XML_E_UNEXPECTED_STANDALONE:
|
|
case XML_E_DTDELEMENT_OUTSIDE_DTD:
|
|
case XML_E_DUPLICATEDOCTYPE:
|
|
case XML_E_CDATAINVALID:
|
|
case XML_E_DOCTYPE_IN_DTD:
|
|
case XML_E_DOCTYPE_OUTSIDE_PROLOG:
|
|
|
|
//
|
|
// the following errors are more closely tied to ERROR_INVALID_DATA;
|
|
//
|
|
|
|
case XML_E_BADCHARDATA:
|
|
case XML_E_BADCHARINENTREF:
|
|
case XML_E_BADCHARINDECL:
|
|
case XML_E_BADCHARINMODEL:
|
|
case XML_E_BADCHARINENUMERATION:
|
|
case XML_E_INVALID_DECIMAL:
|
|
case XML_E_INVALID_HEXIDECIMAL:
|
|
case XML_E_BADSTARTNAMECHAR:
|
|
case XML_E_BADNAMECHAR:
|
|
case XML_E_BADCHARINSTRING:
|
|
case XML_E_INVALID_UNICODE:
|
|
case XML_E_BADEXTERNALID:
|
|
case XML_E_INVALID_MODEL:
|
|
case XML_E_MISSING_ENTITY:
|
|
case XML_E_ENTITYREF_INNAME:
|
|
case XML_E_INVALID_VERSION:
|
|
|
|
//
|
|
// the following errors are more closely tied to ERROR_NOT_SUPPORTED;
|
|
//
|
|
|
|
case XML_E_INVALIDENCODING:
|
|
case XML_E_INVALIDSWITCH:
|
|
return ERROR_BAD_FORMAT;
|
|
|
|
//
|
|
// we don't translate the other errors:
|
|
//
|
|
|
|
case XML_E_FORMATINDEX_BADINDEX:
|
|
case XML_E_FORMATINDEX_BADFORMAT:
|
|
case XML_E_EXPECTED_TOKEN:
|
|
case XML_E_UNEXPECTED_TOKEN:
|
|
case XML_E_INTERNALERROR:
|
|
case XML_E_SUSPENDED:
|
|
case XML_E_STOPPED:
|
|
case XML_E_RESOURCE:
|
|
default:
|
|
return lErrCode;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
myGetXmlError(
|
|
IN HRESULT hrIn,
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
OUT OPTIONAL BSTR *pbstrReason)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hrRet = hrIn;
|
|
LONG errorCode = 0;
|
|
IXMLDOMParseError *pErr = NULL;
|
|
|
|
hr = pDoc->get_parseError(&pErr);
|
|
_JumpIfError(hr, error, "pDoc->get_parseError");
|
|
AZASSERT(NULL != pErr);
|
|
|
|
hr = pErr->get_errorCode(&errorCode);
|
|
_JumpIfError(hr, error, "pErr->get_errorCode");
|
|
|
|
if (NULL != pbstrReason && S_OK != errorCode)
|
|
{
|
|
hr = pErr->get_reason(pbstrReason);
|
|
_JumpIfError(hr, error, "pErr->get_reason");
|
|
}
|
|
|
|
{
|
|
#ifdef DBG
|
|
CComBSTR bstrText;
|
|
pErr->get_reason(&bstrText);
|
|
AzPrint((AZD_XML, "Loading XML file failed. Reason: %s", bstrText));
|
|
#endif
|
|
}
|
|
|
|
// take xml error code
|
|
hrRet = AZ_HRESULT(myXMLErrorToWin32(errorCode));
|
|
error:
|
|
if (NULL != pErr)
|
|
{
|
|
pErr->Release();
|
|
}
|
|
return hrRet;
|
|
}
|
|
|
|
#define _JumpIfXmlError(hr, phr, label, pDoc, pszMessage) \
|
|
{ \
|
|
*(phr) = (hr); \
|
|
if (S_OK != hr) \
|
|
{ \
|
|
HRESULT hrXml; \
|
|
BSTR bstrReason = NULL; \
|
|
hrXml = myGetXmlError(hr, pDoc, &bstrReason); \
|
|
if (NULL != bstrReason) \
|
|
{ \
|
|
AzPrint((AZD_XML, "%s error occured: 0x%lx(%ws)\n", (pszMessage), (hrXml), (bstrReason))); \
|
|
} \
|
|
else \
|
|
{ \
|
|
AzPrint((AZD_XML, "%s error occured: 0x%lx\n", (pszMessage), (hrXml))); \
|
|
} \
|
|
if (NULL != bstrReason) \
|
|
{ \
|
|
SysFreeString(bstrReason); \
|
|
} \
|
|
*(phr) = hrXml; \
|
|
goto label; \
|
|
} \
|
|
}
|
|
|
|
HRESULT
|
|
myWszToBstr(
|
|
IN WCHAR const *pwsz,
|
|
OUT BSTR *pbstr)
|
|
/*
|
|
Description:
|
|
convert wsz string to a BSTR
|
|
Arguments:
|
|
IN: pwsz, a zero terminated wchar string
|
|
OUT pbstr, a pointer to a BSTR, use SysFreeString to free the resource
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == pwsz)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "invalid pwsz");
|
|
}
|
|
if (NULL == pbstr)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpIfError(hr, error, "null pbstr");
|
|
}
|
|
|
|
*pbstr = SysAllocString(pwsz);
|
|
_JumpIfOutOfMemory(&hr, error, *pbstr, "SysAllocString");
|
|
|
|
hr = S_OK;
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myWszToBstrVariant(
|
|
IN WCHAR const *pwsz,
|
|
OUT VARIANT *pvar)
|
|
/*
|
|
Description:
|
|
convert wsz string to a variant with BSTR vt
|
|
Arguments:
|
|
pwsz - a zero terminated wchar string
|
|
pvar - a pointer to a variant in which a BSTR type is returned
|
|
use VariantClear to free the resource
|
|
Return:
|
|
use VariantClear to free resource in pvar
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstr = NULL;
|
|
|
|
if (NULL == pvar)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpIfError(hr, error, "null pvar");
|
|
}
|
|
|
|
//init
|
|
VariantInit(pvar);
|
|
|
|
hr = myWszToBstr(pwsz, &bstr);
|
|
_JumpIfError(hr, error, "myWszToBstr");
|
|
|
|
pvar->vt = VT_BSTR;
|
|
pvar->bstrVal = bstr;
|
|
bstr = NULL; //release by caller
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != bstr)
|
|
{
|
|
SysFreeString(bstr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myFileExist(
|
|
IN WCHAR const *pwszFile,
|
|
OUT BOOL *pfExist)
|
|
/*
|
|
Description:
|
|
check file existence
|
|
Arguments:
|
|
pwszFile - file path
|
|
pfExist - return of file-existing flag, TRUE if file exists
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
WIN32_FILE_ATTRIBUTE_DATA wfad;
|
|
|
|
//init
|
|
*pfExist = FALSE;
|
|
|
|
if (GetFileAttributesEx(
|
|
pwszFile, //xml file path
|
|
GetFileExInfoStandard,
|
|
&wfad))
|
|
{
|
|
*pfExist = TRUE;
|
|
AzPrint((AZD_XML, "xml file %ws exists.\n", pwszFile));
|
|
}
|
|
else
|
|
{
|
|
AZ_HRESULT_LASTERROR(&hr);
|
|
if (AZ_HRESULT(ERROR_FILE_NOT_FOUND) != hr)
|
|
{
|
|
_JumpError(hr, error, "GetFileAttributesEx");
|
|
}
|
|
AzPrint((AZD_XML, "xml file %ws doesn't exist.\n", pwszFile));
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
DWORD
|
|
myXmlStoreHasUpdate (
|
|
IN AZP_XML_CONTEXT * pContext,
|
|
OUT BOOL * pbNeedUpdate
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Determine whether XML file has been modified since our store loaded.
|
|
For XML store, we just rely on timestamp to test.
|
|
|
|
Arguments:
|
|
|
|
pContext - The persist context.
|
|
|
|
pbNeedUpdate - Receives the test result.
|
|
|
|
Return Value:
|
|
NO_ERROR - If test is successful
|
|
|
|
Various error code if error is encountered.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// For XML store, we just rely on timestamp to tell if
|
|
// the store has been modified
|
|
//
|
|
|
|
AZASSERT(pbNeedUpdate != NULL);
|
|
|
|
*pbNeedUpdate = FALSE;
|
|
|
|
if (pContext->FTLastWrite.dwHighDateTime == 0 &&
|
|
pContext->FTLastWrite.dwLowDateTime == 0)
|
|
{
|
|
//
|
|
// this means that it is a new store, no need to update it
|
|
//
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA fad;
|
|
|
|
DWORD dwStatus = NO_ERROR;
|
|
|
|
if (GetFileAttributesEx(pContext->pwszPolicyUrl, GetFileExInfoStandard, &fad))
|
|
{
|
|
*pbNeedUpdate = CompareFileTime(&(fad.ftLastWriteTime), &(pContext->FTLastWrite)) != 0;
|
|
}
|
|
else
|
|
{
|
|
dwStatus = GetLastError();
|
|
}
|
|
|
|
return dwStatus;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlLoad (
|
|
IN VARIANT varUrl,
|
|
OUT IXMLDOMDocument2 ** ppFreshDom
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Load a fresh XML file into XML DOM object
|
|
|
|
Arguments:
|
|
|
|
varUrl - The XML store URL.
|
|
|
|
ppFreshDom - Freshly loaded DOM
|
|
|
|
Return Value:
|
|
|
|
S_OK - If the operation completes successfully
|
|
|
|
Various error code if errors are encountered.
|
|
|
|
--*/
|
|
{
|
|
CComPtr<IXMLDOMDocument2> srpNewDom;
|
|
HRESULT Hr = CoCreateInstance(
|
|
CLSID_DOMDocument,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IXMLDOMDocument2,
|
|
(void**)&srpNewDom);
|
|
|
|
if (SUCCEEDED(Hr))
|
|
{
|
|
//
|
|
// This really should never fail
|
|
//
|
|
|
|
Hr = srpNewDom->put_async(FALSE);
|
|
|
|
AZASSERT(SUCCEEDED(Hr));
|
|
|
|
//
|
|
// Load the XML
|
|
//
|
|
|
|
VARIANT_BOOL varbLoad;
|
|
Hr = srpNewDom->load(varUrl, &varbLoad);
|
|
|
|
if (FAILED(Hr))
|
|
{
|
|
if (0x800c0006 == Hr)
|
|
{
|
|
AzPrint((AZD_XML, "IXMLDOMDocument2::load failed. HRESULT = %d. Intepreted as 'file not found'.", Hr));
|
|
Hr = AZ_HR_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
AzPrint((AZD_XML, "IXMLDOMDocument2::load failed. HRESULT = %d", Hr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AzPrint((AZD_XML, "CoCreating IXMLDOMDocument2 failed. HRESULT = %d", Hr));
|
|
}
|
|
|
|
//
|
|
// If everything has worked, so we passback the fresh new DOM object.
|
|
// Otherwise, the smart pointer will release itself.
|
|
//
|
|
|
|
if (SUCCEEDED(Hr))
|
|
{
|
|
*ppFreshDom = srpNewDom.Detach();
|
|
}
|
|
|
|
return Hr;
|
|
}
|
|
|
|
DWORD
|
|
XmlCheckSecurityPrivilege(
|
|
IN LPCWSTR PolicyUrl,
|
|
IN BOOL fCreatePolicy
|
|
)
|
|
/*
|
|
Description:
|
|
|
|
Determine whether the caller has SE_SECURITY_PRIVILEGE on the machine containing
|
|
the xml store.
|
|
|
|
Arguments:
|
|
|
|
PolicyUrl - Full path name of the file to check privilege on.
|
|
If fCreatePolicy is TRUE, the file doesn't exist so the privilege is checked on
|
|
the underlying folder
|
|
|
|
fCreatePolicy - TRUE if the policy database is to be created.
|
|
FALSE if the policy database already exists
|
|
|
|
Return Value:
|
|
NO_ERROR - The caller has privilege
|
|
ERROR_PRIVILEGE_NOT_HELD - The caller has no privilege
|
|
Other errors
|
|
|
|
|
|
*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
HANDLE hToken = NULL;
|
|
LPCWSTR PathToCheck = PolicyUrl;
|
|
WCHAR PathBuffer[MAX_PATH+1];
|
|
|
|
TOKEN_PRIVILEGES NewPrivilegeState = {0};
|
|
TOKEN_PRIVILEGES OldPrivilegeState = {0};
|
|
BOOL bPrivilegeAdjusted = FALSE;
|
|
|
|
NTSTATUS Status;
|
|
UNICODE_STRING NtFileName;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
LPWSTR BufferToFree = NULL;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
AZASSERT( PolicyUrl != NULL );
|
|
RtlInitUnicodeString(&NtFileName, NULL);
|
|
|
|
//
|
|
// If we're creating the file,
|
|
// build the name of the underlying folder.
|
|
//
|
|
|
|
if ( fCreatePolicy ) {
|
|
LPWSTR SlashPointer;
|
|
DWORD PathLength;
|
|
|
|
//
|
|
// Determine the length of the path to the underlying folder
|
|
//
|
|
SlashPointer = wcsrchr( PolicyUrl, L'\\' );
|
|
|
|
if ( SlashPointer == NULL ) {
|
|
AzPrint(( AZD_INVPARM, "XmlCheckSecurityPrivilege: invalid full path '%ws'\n", PolicyUrl ));
|
|
WinStatus = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
PathLength = (ULONG)(SlashPointer - PolicyUrl) + 1;
|
|
|
|
if ( PathLength > MAX_PATH ) {
|
|
AzPrint(( AZD_INVPARM, "XmlCheckSecurityPrivilege: path is too long '%ws'\n", PolicyUrl ));
|
|
WinStatus = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Grab a copy of the path
|
|
//
|
|
|
|
wcsncpy( PathBuffer, PolicyUrl, PathLength );
|
|
PathBuffer[PathLength] = '\0';
|
|
|
|
PathToCheck = PathBuffer;
|
|
|
|
}
|
|
|
|
//
|
|
// Enable the SE_SECURITY_PRIVILEGE privilege in order to read the SACL
|
|
//
|
|
//
|
|
// Get the current token to adjust the security privilege.
|
|
//
|
|
|
|
WinStatus = AzpGetCurrentToken( &hToken );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Adjust the privilege.
|
|
// Ignore errors since the caller might be running "net only" and that
|
|
// user might have privilege on the machine containing the XML file.
|
|
//
|
|
|
|
WinStatus = AzpChangeSinglePrivilege(
|
|
SE_SECURITY_PRIVILEGE,
|
|
hToken,
|
|
&NewPrivilegeState,
|
|
&OldPrivilegeState );
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
bPrivilegeAdjusted = TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Convert the DOS pathname to an NT pathname
|
|
//
|
|
|
|
if ( !RtlDosPathNameToNtPathName_U(
|
|
PathToCheck,
|
|
&NtFileName,
|
|
NULL,
|
|
NULL
|
|
) ) {
|
|
WinStatus = ERROR_PATH_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
BufferToFree = NtFileName.Buffer;
|
|
|
|
//
|
|
// Call NtCreateFile to determine if we have the privilege
|
|
// Use NtCreateFile instead of CreateFile because:
|
|
// CreateFile always asks for SYNCHRONIZE and FILE_READ_ATTRIBUTES and we don't need them
|
|
// CreateFile cannot open directories
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&NtFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtCreateFile(
|
|
&hFile,
|
|
ACCESS_SYSTEM_SECURITY,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0, // Flags and attributes
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // Share mode
|
|
FILE_OPEN, // Create Disposition
|
|
0, // Create Flags
|
|
NULL, // EaBuffer
|
|
0 ); // EaSize
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
WinStatus = RtlNtStatusToDosError( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
WinStatus = NO_ERROR;
|
|
|
|
Cleanup:
|
|
|
|
if ( hToken != NULL ) {
|
|
if ( bPrivilegeAdjusted ) {
|
|
|
|
DWORD TempStatus;
|
|
|
|
TempStatus = AzpChangeSinglePrivilege(
|
|
0, // This is ignored since OldState is NULL.
|
|
hToken,
|
|
&OldPrivilegeState,
|
|
NULL // This should be set to NULL to specify REVERT.
|
|
);
|
|
|
|
ASSERT( TempStatus == NO_ERROR );
|
|
}
|
|
|
|
CloseHandle( hToken );
|
|
}
|
|
|
|
if ( BufferToFree != NULL ) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, BufferToFree);
|
|
}
|
|
|
|
if ( hFile != INVALID_HANDLE_VALUE ) {
|
|
NtClose(hFile);
|
|
}
|
|
|
|
return WinStatus;
|
|
}
|
|
|
|
DWORD
|
|
IsAclSupported(
|
|
IN LPCWSTR pwszFileName)
|
|
/*
|
|
Description:
|
|
check if a drive from a file path has ACL support
|
|
Arguments:
|
|
pwszFileName - file full path
|
|
Return:
|
|
error or ERROR_NOT_SUPPORTED if not ACL-able
|
|
*/
|
|
{
|
|
DWORD dwErr;
|
|
WCHAR *pwszVolumePathName = NULL;
|
|
DWORD dwFlags = 0;
|
|
DWORD len;
|
|
|
|
len = (DWORD)(wcslen(pwszFileName) + 2); //by 2 in case of appending '\\'
|
|
pwszVolumePathName = (WCHAR*)AzpAllocateHeap(len * sizeof(WCHAR), "XMPATH" );
|
|
if (NULL == pwszVolumePathName)
|
|
{
|
|
dwErr = ERROR_OUTOFMEMORY;
|
|
_JumpError(dwErr, error, "AzpAllocateHeap");
|
|
}
|
|
|
|
if (!GetVolumePathName(
|
|
pwszFileName,
|
|
pwszVolumePathName,
|
|
len))
|
|
{
|
|
dwErr = GetLastError();
|
|
_JumpError(dwErr, error, "GetVolumePathName");
|
|
}
|
|
|
|
len = (DWORD)wcslen(pwszVolumePathName);
|
|
if(pwszVolumePathName[len -1] != L'\\')
|
|
{
|
|
pwszVolumePathName[len] = L'\\';
|
|
pwszVolumePathName[len++] = L'\0';
|
|
}
|
|
if (!GetVolumeInformation(
|
|
pwszVolumePathName,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dwFlags,
|
|
NULL,
|
|
0))
|
|
{
|
|
dwErr = GetLastError();
|
|
_JumpError(dwErr, error, "GetVolumeInformation");
|
|
}
|
|
|
|
if(0x0 == (FS_PERSISTENT_ACLS & dwFlags))
|
|
{
|
|
dwErr = ERROR_NOT_SUPPORTED;
|
|
_JumpError(dwErr, error, "ACL is not supported");
|
|
}
|
|
|
|
dwErr = NO_ERROR;
|
|
error:
|
|
if (NULL != pwszVolumePathName)
|
|
{
|
|
AzpFreeHeap(pwszVolumePathName );
|
|
}
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
myXmlLoadAclsToAzStore(
|
|
IN PAZP_XML_CONTEXT PersistContext,
|
|
IN ULONG lPersistFlags,
|
|
IN BOOL OnlyAddPolicyAdmins
|
|
)
|
|
/*
|
|
Description:
|
|
loads xml store file DACL/SACL into the Azroles cache
|
|
Arguments:
|
|
PersistContext - Context describing the policy store
|
|
lPersistFlags - flags from the persist engine
|
|
OnlyAddPolicyAdmins - TRUE if only PolicyAdmins is to be loads
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
BOOLEAN DoSacl;
|
|
|
|
HANDLE hToken = NULL;
|
|
TOKEN_PRIVILEGES NewPrivilegeState = {0};
|
|
TOKEN_PRIVILEGES OldPrivilegeState = {0};
|
|
BOOL bPrivilegeAdjusted = FALSE;
|
|
|
|
SECURITY_INFORMATION si = 0;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
|
|
AZASSERT(NULL != PersistContext);
|
|
|
|
//
|
|
// Only do the SACL if requested and the caller has privilege
|
|
//
|
|
|
|
DoSacl = !OnlyAddPolicyAdmins && PersistContext->HasSecurityPrivilege;
|
|
|
|
if ( DoSacl ) {
|
|
|
|
//
|
|
// Enable the SE_SECURITY_PRIVILEGE privilege in order to read the SACL
|
|
//
|
|
//
|
|
// Get the current token to adjust the security privilege.
|
|
//
|
|
|
|
dwErr = AzpGetCurrentToken( &hToken );
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpGetCurrentToken");
|
|
|
|
//
|
|
// Adjust the privilege.
|
|
// Ignore errors since the caller might be running "net only" and that
|
|
// user might have privilege on the machine containing the XML file.
|
|
//
|
|
|
|
dwErr = AzpChangeSinglePrivilege(
|
|
SE_SECURITY_PRIVILEGE,
|
|
hToken,
|
|
&NewPrivilegeState,
|
|
&OldPrivilegeState );
|
|
|
|
if ( dwErr == NO_ERROR ) {
|
|
bPrivilegeAdjusted = TRUE;
|
|
}
|
|
|
|
//
|
|
// Ask for the SACL
|
|
//
|
|
si |= SACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
|
|
//
|
|
// Get file security descriptor
|
|
//
|
|
|
|
si |= DACL_SECURITY_INFORMATION;
|
|
dwErr = GetNamedSecurityInfo(
|
|
(LPWSTR)PersistContext->pwszPolicyUrl,
|
|
SE_FILE_OBJECT,
|
|
si,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&pSD);
|
|
_JumpIfWinError(dwErr, &hr, error, "GetNamedSecurityInfo");
|
|
|
|
|
|
//
|
|
// Set the security descriptor into the cache
|
|
//
|
|
|
|
dwErr = XmlAzrolesInfo->AzpeSetSecurityDescriptorIntoCache(
|
|
PersistContext->hAzAuthorizationStore,
|
|
pSD,
|
|
lPersistFlags,
|
|
&PolicyAdminsRights,
|
|
OnlyAddPolicyAdmins ? NULL : &PolicyReadersRights,
|
|
NULL,
|
|
DoSacl ? &XMLSaclRights : NULL );
|
|
_JumpIfWinError( dwErr, &hr, error, "AzpeSetSecurityDescriptorIntoCache" );
|
|
|
|
hr = S_OK;
|
|
error:
|
|
|
|
if ( hToken != NULL )
|
|
{
|
|
if ( bPrivilegeAdjusted )
|
|
{
|
|
dwErr = AzpChangeSinglePrivilege(
|
|
0, // This is ignored since OldState is NULL.
|
|
hToken,
|
|
&OldPrivilegeState,
|
|
NULL // This should be set to NULL to specify REVERT.
|
|
);
|
|
|
|
ASSERT( dwErr == NO_ERROR );
|
|
}
|
|
|
|
CloseHandle( hToken );
|
|
}
|
|
|
|
if (NULL != pSD)
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlSubmitAzStoreAcls(
|
|
IN PAZP_XML_CONTEXT pPersistContext,
|
|
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
|
|
IN ULONG lPersistFlags
|
|
)
|
|
/*
|
|
Description:
|
|
submit any acl changes to AzAuthorizationStore persist object (xml file)
|
|
Arguments:
|
|
pPersistContext - Context describing the store
|
|
pAzAuthorizationStore - AzAuthorizationStore object
|
|
lPersistFlags - flags from the persist engine
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
|
|
PSECURITY_DESCRIPTOR pOldSd = NULL;
|
|
|
|
PSECURITY_DESCRIPTOR pNewSd = NULL;
|
|
|
|
BOOL UpdateDacl = FALSE;
|
|
PACL pDacl = NULL;
|
|
BOOL bDaclPresent;
|
|
BOOL bDaclDefaulted;
|
|
|
|
BOOL UpdateSacl = FALSE;
|
|
PACL pSacl = NULL;
|
|
BOOL bSaclPresent;
|
|
BOOL bSaclDefaulted;
|
|
|
|
HANDLE hToken = NULL;
|
|
|
|
TOKEN_PRIVILEGES NewPrivilegeState = {0};
|
|
TOKEN_PRIVILEGES OldPrivilegeState = {0};
|
|
BOOL bPrivilegeAdjusted = FALSE;
|
|
|
|
|
|
BOOL EmptyPolicyAdmins = FALSE;
|
|
|
|
SECURITY_INFORMATION si = 0;
|
|
|
|
AZASSERT(NULL != pAzAuthorizationStore);
|
|
|
|
//
|
|
// Determine whether the DACL and/or SACL need to be updated
|
|
//
|
|
|
|
if ( ObjectIsDirty( pAzAuthorizationStore, AZ_DIRTY_CREATE) ) {
|
|
UpdateDacl = TRUE;
|
|
UpdateSacl = pPersistContext->HasSecurityPrivilege;
|
|
} else {
|
|
if ( ObjectIsDirty( pAzAuthorizationStore, AZ_DIRTY_POLICY_READERS|AZ_DIRTY_POLICY_ADMINS|AZ_DIRTY_DELEGATED_POLICY_USERS) ) {
|
|
|
|
UpdateDacl = TRUE;
|
|
}
|
|
|
|
if ( ObjectIsDirty( pAzAuthorizationStore, AZ_DIRTY_APPLY_STORE_SACL) ) {
|
|
UpdateSacl = TRUE;
|
|
}
|
|
|
|
if ( !UpdateDacl && !UpdateSacl ) {
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we need to update the SACL,
|
|
// we need privilege.
|
|
|
|
if ( UpdateSacl ) {
|
|
|
|
//
|
|
// Get the current token to adjust the security privilege.
|
|
//
|
|
|
|
dwErr = AzpGetCurrentToken( &hToken );
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpGetCurrentToken");
|
|
|
|
//
|
|
// Acquire security privilege so we can get/set the SACL
|
|
//
|
|
|
|
dwErr = AzpChangeSinglePrivilege(
|
|
SE_SECURITY_PRIVILEGE,
|
|
hToken,
|
|
&NewPrivilegeState,
|
|
&OldPrivilegeState
|
|
);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpChangeSinglePrivilege");
|
|
|
|
bPrivilegeAdjusted = TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// If we didn't just create the object,
|
|
// Get the existing file security descriptor so we can merge in the changes
|
|
//
|
|
|
|
if ( !ObjectIsDirty( pAzAuthorizationStore, AZ_DIRTY_CREATE) ) {
|
|
|
|
si = (UpdateDacl ? DACL_SECURITY_INFORMATION : 0) |
|
|
(UpdateSacl ? SACL_SECURITY_INFORMATION : 0);
|
|
|
|
dwErr = GetNamedSecurityInfo(
|
|
(LPWSTR)pPersistContext->pwszPolicyUrl,
|
|
SE_FILE_OBJECT,
|
|
si,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&pOldSd);
|
|
_JumpIfWinError(dwErr, &hr, error, "GetNamedSecurityInfo");
|
|
}
|
|
|
|
|
|
//
|
|
// Get the new security descriptor for the file
|
|
//
|
|
|
|
dwErr = XmlAzrolesInfo->AzpeGetSecurityDescriptorFromCache(
|
|
pAzAuthorizationStore,
|
|
lPersistFlags,
|
|
(UpdateDacl ? XMLPolicyAdminsRights : NULL),
|
|
(UpdateDacl ? XMLPolicyReadersRights : NULL),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
(UpdateSacl ? &XMLSaclRights : NULL),
|
|
pOldSd,
|
|
&pNewSd );
|
|
|
|
if ( dwErr == ERROR_EMPTY ) {
|
|
EmptyPolicyAdmins = TRUE;
|
|
dwErr = NO_ERROR;
|
|
}
|
|
_JumpIfWinError( dwErr, &hr, error, "AzpeGetSecurityDescriptorFromCache" );
|
|
|
|
//
|
|
// Update the SACL on the file
|
|
//
|
|
|
|
if ( UpdateSacl ) {
|
|
|
|
//
|
|
// Get the SACL from the returned security descriptor
|
|
//
|
|
|
|
if ( !GetSecurityDescriptorSacl(pNewSd,
|
|
&bSaclPresent,
|
|
&pSacl,
|
|
&bSaclDefaulted
|
|
) ) {
|
|
AZ_HRESULT_LASTERROR(&hr);
|
|
_JumpError(hr, error, "GetSecurityDescriptorSacl");
|
|
}
|
|
|
|
|
|
// Indicate the SACL should be set
|
|
// The SACL is never protected.
|
|
si |= SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION;
|
|
|
|
}
|
|
|
|
//
|
|
// Update the DACL on the file
|
|
//
|
|
|
|
if ( UpdateDacl ) {
|
|
|
|
//
|
|
// Get the DACL from the returned security descriptor
|
|
//
|
|
|
|
if ( !GetSecurityDescriptorDacl(pNewSd,
|
|
&bDaclPresent,
|
|
&pDacl,
|
|
&bDaclDefaulted
|
|
) ) {
|
|
AZ_HRESULT_LASTERROR(&hr);
|
|
_JumpError(hr, error, "GetSecurityDescriptorDacl");
|
|
}
|
|
|
|
|
|
ASSERT( bDaclPresent && pDacl != NULL );
|
|
|
|
//
|
|
// Indicate the DACL should be set
|
|
// The DACL needs to have its protected bit on.
|
|
//
|
|
si |= DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
//
|
|
// Set the DACL/SACL onto the file
|
|
//
|
|
|
|
dwErr = SetNamedSecurityInfo(
|
|
(LPWSTR)pPersistContext->pwszPolicyUrl,
|
|
SE_FILE_OBJECT,
|
|
si,
|
|
NULL, //psidOwner
|
|
NULL, //psidGroup
|
|
pDacl,
|
|
pSacl); //pSacl
|
|
_JumpIfWinError(dwErr, &hr, error, "SetNamedSecurityinfo");
|
|
|
|
|
|
//
|
|
// If SetNamedSecurityinfo changed PolicyAdmins,
|
|
// load the PolicyAdmins back into the the azroles core.
|
|
//
|
|
if ( EmptyPolicyAdmins ) {
|
|
hr = myXmlLoadAclsToAzStore(
|
|
pPersistContext,
|
|
lPersistFlags,
|
|
TRUE );
|
|
_JumpIfError(hr, error, "myXmlLoadAclsToAzStore");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if ( hToken != NULL ) {
|
|
if ( bPrivilegeAdjusted ) {
|
|
DWORD dwIgnoreErr;
|
|
dwIgnoreErr = AzpChangeSinglePrivilege(
|
|
0, // This is ignored since OldState is NULL.
|
|
hToken,
|
|
&OldPrivilegeState,
|
|
NULL ); // This should be set to NULL to specify REVERT.
|
|
|
|
if (dwIgnoreErr != NO_ERROR) {
|
|
AzPrint((AZD_XML, "AzpChangeSinglePrivilege failed to reset to original. Error code: %d", dwIgnoreErr));
|
|
}
|
|
}
|
|
|
|
CloseHandle( hToken );
|
|
}
|
|
|
|
XmlAzrolesInfo->AzpeFreeMemory( pNewSd );
|
|
|
|
if ( pOldSd != NULL ) {
|
|
LocalFree(pOldSd);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
myXmlValidateNodeType(
|
|
IXMLDOMNode *pNode,
|
|
DOMNodeType designedNodeType)
|
|
/*
|
|
Description:
|
|
make sure the node has a valid node type
|
|
Arguments:
|
|
pNode - pointer to xml node
|
|
designedNodeType - designed node type
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DOMNodeType nodeType;
|
|
|
|
AZASSERT(NULL != pNode);
|
|
|
|
hr = pNode->get_nodeType(&nodeType);
|
|
_JumpIfError(hr, error, "pNode->get_nodeType");
|
|
|
|
if (nodeType != designedNodeType)
|
|
{
|
|
// node type is not expected
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "bad node type");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlGetNodeAttribute(
|
|
IN IXMLDOMNode *pNode,
|
|
IN WCHAR const *pwszName,
|
|
OUT WCHAR **ppwszValue)
|
|
/*
|
|
Description:
|
|
query named attribute from xml element node
|
|
Arguments:
|
|
pNode - pointer to xml node element
|
|
pwszName - attribute name
|
|
ppwszValue - returns the value of the attribute
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
IXMLDOMNamedNodeMap *pNodeMap = NULL;
|
|
IXMLDOMNode *pNodeAttr = NULL;
|
|
BSTR bstrName = NULL;
|
|
VARIANT varValue;
|
|
|
|
AZASSERT(NULL != pNode &&
|
|
NULL != pwszName &&
|
|
NULL != ppwszValue);
|
|
|
|
// init
|
|
*ppwszValue = NULL;
|
|
VariantInit(&varValue);
|
|
|
|
// validate node type
|
|
hr = myXmlValidateNodeType(pNode, NODE_ELEMENT);
|
|
_JumpIfError(hr, error, "myXmlValidateNodeType");
|
|
|
|
// get attribute list
|
|
hr = pNode->get_attributes(&pNodeMap);
|
|
_JumpIfError(hr, error, "pNode->get_attributes");
|
|
|
|
bstrName = SysAllocString(pwszName);
|
|
_JumpIfOutOfMemory(&hr, error, bstrName, "SysAllocString");
|
|
|
|
// return attribute node object
|
|
hr = pNodeMap->getNamedItem(bstrName, &pNodeAttr);
|
|
//_JumpIfError(hr, error, "pNodeMap->getNamedItem");
|
|
if (S_OK != hr || NULL == pNodeAttr)
|
|
{
|
|
// seems doesn't have the attribute
|
|
hr = AZ_HRESULT(ERROR_NOT_FOUND);
|
|
_JumpErrorQuiet(hr, error, "attribute not found");
|
|
}
|
|
|
|
// now we get attribute value
|
|
hr = pNodeAttr->get_nodeValue(&varValue);
|
|
_JumpIfError(hr, error, "pNodeAttr->get_nodeValue");
|
|
|
|
//??? may not true if we have other attribute type
|
|
AZASSERT(VT_BSTR == varValue.vt);
|
|
|
|
// we want to return the value in wchar
|
|
*ppwszValue = (WCHAR*)AzpAllocateHeap(
|
|
(wcslen(varValue.bstrVal) + 1) * sizeof(WCHAR), "XMVAL" );
|
|
_JumpIfOutOfMemory(&hr, error, *ppwszValue, "AzpAllocateHeap");
|
|
|
|
// return
|
|
wcscpy(*ppwszValue, varValue.bstrVal);
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pNodeMap)
|
|
{
|
|
pNodeMap->Release();
|
|
}
|
|
if (NULL != pNodeAttr)
|
|
{
|
|
pNodeAttr->Release();
|
|
}
|
|
if (NULL != bstrName)
|
|
{
|
|
SysFreeString(bstrName);
|
|
}
|
|
VariantClear(&varValue);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlSetNodeAttribute(
|
|
IN IXMLDOMNode *pNode,
|
|
IN WCHAR const *pwszName,
|
|
IN WCHAR const *pwszValue,
|
|
IN OPTIONAL IXMLDOMDocument2 *pDoc)
|
|
/*
|
|
Description:
|
|
set named attribute to xml element node
|
|
Arguments:
|
|
pNode - pointer to xml node element
|
|
pwszName - attribute name
|
|
pwszValue - the value of the attribute
|
|
pDoc - NULL means the attribute must exist, otherwise it creates one if not
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
IXMLDOMNamedNodeMap *pNodeMap = NULL;
|
|
IXMLDOMNode *pNodeAttr = NULL;
|
|
IXMLDOMElement *pEle = NULL;
|
|
BSTR bstrName = NULL;
|
|
VARIANT varValue;
|
|
|
|
AZASSERT(NULL != pNode &&
|
|
NULL != pwszName &&
|
|
NULL != pwszValue);
|
|
|
|
AzPrint((AZD_XML, "(myXmlSetNodeAttribute)pNode = 0x%lx\n", pNode));
|
|
|
|
// init
|
|
VariantInit(&varValue);
|
|
|
|
// validate node type
|
|
hr = myXmlValidateNodeType(pNode, NODE_ELEMENT);
|
|
_JumpIfError(hr, error, "myXmlValidateNodeType");
|
|
|
|
// get attribute list
|
|
hr = pNode->get_attributes(&pNodeMap);
|
|
_JumpIfError(hr, error, "pNode->get_attributes");
|
|
|
|
bstrName = SysAllocString(pwszName);
|
|
_JumpIfOutOfMemory(&hr, error, bstrName, "SysAllocString");
|
|
|
|
// convert value to variant
|
|
hr = myWszToBstrVariant(
|
|
pwszValue,
|
|
&varValue);
|
|
_JumpIfError(hr, error, "myWszToBstrVariant");
|
|
|
|
// return attribute node object
|
|
hr = pNodeMap->getNamedItem(bstrName, &pNodeAttr);
|
|
if (S_OK == hr)
|
|
{
|
|
AZASSERT(NULL != pNodeAttr);
|
|
|
|
// now we set attribute value
|
|
hr = pNodeAttr->put_nodeValue(varValue);
|
|
_JumpIfError(hr, error, "pNodeAttr->put_nodeValue");
|
|
}
|
|
else
|
|
{
|
|
// the attribute doesn't exist
|
|
|
|
#if DBG
|
|
{
|
|
BSTR bstrXml = NULL;
|
|
|
|
AzPrint((AZD_XML, "attribute %ws doesn't exist in the following element...\n", bstrName));
|
|
hr = pNode->get_xml(&bstrXml);
|
|
_JumpIfError(hr, error, "pNode->get_xml");
|
|
AzPrint((AZD_XML, "The node xml = %ws\n", bstrXml));
|
|
if (NULL != bstrXml) SysFreeString(bstrXml);
|
|
}
|
|
#endif //DBG
|
|
|
|
if (NULL == pDoc)
|
|
{
|
|
// must not null if create one
|
|
hr = AZ_HRESULT(ERROR_NOT_FOUND);
|
|
_JumpErrorQuiet(hr, error, "attribute not found");
|
|
}
|
|
|
|
AZASSERT(NULL == pNodeAttr);
|
|
|
|
hr = pNode->QueryInterface(IID_IXMLDOMElement, (void**)&pEle);
|
|
_JumpIfError(hr, error, "pNode->QueryInterface");
|
|
|
|
AZASSERT(NULL != pEle);
|
|
|
|
//set attr
|
|
hr = pEle->setAttribute(
|
|
bstrName,
|
|
varValue);
|
|
_JumpIfError(hr, error, "pEle->setAttribute");
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
BSTR bstrXml = NULL;
|
|
|
|
AzPrint((AZD_XML, "set %ws attribute is done...\n", bstrName));
|
|
hr = pNode->get_xml(&bstrXml);
|
|
_JumpIfError(hr, error, "pNode->get_xml");
|
|
AzPrint((AZD_XML, "The node xml = %ws\n", bstrXml));
|
|
if (NULL != bstrXml) SysFreeString(bstrXml);
|
|
}
|
|
#endif //DBG
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pEle)
|
|
{
|
|
pEle->Release();
|
|
}
|
|
if (NULL != pNodeMap)
|
|
{
|
|
pNodeMap->Release();
|
|
}
|
|
if (NULL != pNodeAttr)
|
|
{
|
|
pNodeAttr->Release();
|
|
}
|
|
if (NULL != bstrName)
|
|
{
|
|
SysFreeString(bstrName);
|
|
}
|
|
VariantClear(&varValue);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlValidateNodeTag(
|
|
IXMLDOMNode *pNode,
|
|
WCHAR const *pwszTag)
|
|
/*
|
|
Description:
|
|
make sure the node has a valid az tag name
|
|
Arguments:
|
|
pNode - pointer to xml node
|
|
pwszTag - designed tag name for the node
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrTag = NULL;
|
|
|
|
AZASSERT(NULL != pNode &&
|
|
NULL != pwszTag);
|
|
|
|
hr = pNode->get_nodeName(&bstrTag);
|
|
_JumpIfError(hr, error, "pNode->get_nodeName");
|
|
|
|
if (0 != wcscmp(pwszTag, bstrTag))
|
|
{
|
|
// node tag name is bad
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "bad tag name");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != bstrTag)
|
|
{
|
|
SysFreeString(bstrTag);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlLoadObjectAttribute(
|
|
IN IXMLDOMNode *pNode,
|
|
IN ULONG lPersistFlags,
|
|
IN WCHAR const *pwszAttrTag,
|
|
IN AZPE_OBJECT_HANDLE pObject,
|
|
IN ULONG lPropId,
|
|
IN ENUM_AZ_DATATYPE DataType)
|
|
/*
|
|
Description: load a node attribute from persist store
|
|
to az core object
|
|
Arguments:
|
|
pNode - object node handle
|
|
lPersistFlags - persist flags passed back to az core
|
|
pwszAttrTag - attribute tag name
|
|
pObject - az core object handle
|
|
lPropId - property ID used to set on az core object
|
|
DataType - data type
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
PVOID pV;
|
|
WCHAR *pwszAttrValue = NULL;
|
|
ULONG lNumber;
|
|
|
|
AZASSERT(NULL != pNode &&
|
|
NULL != pwszAttrTag &&
|
|
NULL != pObject);
|
|
|
|
// get attribute from xml node
|
|
hr = myXmlGetNodeAttribute(
|
|
pNode,
|
|
pwszAttrTag,
|
|
&pwszAttrValue);
|
|
if (S_OK != hr && AZ_HRESULT(ERROR_NOT_FOUND) != hr)
|
|
{
|
|
_JumpError(hr, error, "myXmlGetNodeAttribute");
|
|
}
|
|
|
|
if (NULL != pwszAttrValue)
|
|
{
|
|
switch (DataType)
|
|
{
|
|
case ENUM_AZ_BSTR:
|
|
// attribute is a string
|
|
pV = (PVOID)pwszAttrValue;
|
|
break;
|
|
|
|
case ENUM_AZ_LONG:
|
|
// atribute is a number
|
|
// convert to number
|
|
lNumber = _wtol(pwszAttrValue);
|
|
pV = (PVOID)&lNumber;
|
|
break;
|
|
|
|
case ENUM_AZ_BOOL:
|
|
// attribute is a boolean
|
|
// map to boolean id
|
|
// default to false
|
|
lNumber = FALSE; // so any strings other than TRUE
|
|
if (0 == _wcsicmp(pwszAttrValue, AZ_XML_VAL_TRUE))
|
|
{
|
|
lNumber = TRUE;
|
|
}
|
|
pV = (PVOID)&lNumber;
|
|
break;
|
|
|
|
|
|
case ENUM_AZ_GROUP_TYPE:
|
|
// set default to basic
|
|
lNumber = AZ_GROUPTYPE_BASIC;
|
|
if (0 == wcscmp(pwszAttrValue, AZ_XML_VAL_GROUPTYPE_LDAPQUERY))
|
|
{
|
|
lNumber = AZ_GROUPTYPE_LDAP_QUERY;
|
|
}
|
|
pV = (PVOID)&lNumber;
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "invalid attribute data type");
|
|
}
|
|
|
|
// set object attribute
|
|
dwErr = XmlAzrolesInfo->AzpeSetProperty(
|
|
pObject,
|
|
lPersistFlags,
|
|
lPropId,
|
|
pV);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeSetProperty");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pwszAttrValue)
|
|
{
|
|
AzpFreeHeap(pwszAttrValue );
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlSubmitObjectAttribute(
|
|
IN AZPE_OBJECT_HANDLE hObject,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG lPropId,
|
|
IN ENUM_AZ_DATATYPE DataType,
|
|
IN IXMLDOMNode *pNode,
|
|
IN WCHAR const *pwszAttrTag,
|
|
IN IXMLDOMDocument2 *pDoc)
|
|
/*
|
|
Description:
|
|
gets an object property az core object and sets the
|
|
property as an node attribute in xml doc.
|
|
This is the routine used by all object attribute submissions
|
|
|
|
Arguments:
|
|
jObject - handle of the object
|
|
lPersistFlags - flags from the persist engine
|
|
lPropId - property ID
|
|
DataType - data type of the property, one of ENUM_AZ_*
|
|
pNode - the node handle at which the property is set as attribute
|
|
pwszAttrTag - attribute tag string
|
|
pDoc - root doc node handle
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
PVOID pvPropValue = NULL;
|
|
WCHAR const * pwszValue;
|
|
WCHAR wszNumber[20];
|
|
|
|
AZASSERT(NULL != hObject &&
|
|
NULL != pwszAttrTag &&
|
|
NULL != pNode &&
|
|
NULL != pDoc);
|
|
|
|
AzPrint((AZD_XML, "(myXmlSubmitObjectAttribute)pNode = 0x%lx\n", pNode));
|
|
|
|
// get property from az core
|
|
dwErr = XmlAzrolesInfo->AzpeGetProperty(
|
|
hObject,
|
|
lPersistFlags,
|
|
lPropId,
|
|
&pvPropValue);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeGetProperty");
|
|
|
|
AZASSERT(NULL != pvPropValue);
|
|
|
|
switch (DataType)
|
|
{
|
|
case ENUM_AZ_BSTR:
|
|
pwszValue = (WCHAR*)pvPropValue;
|
|
break;
|
|
|
|
case ENUM_AZ_BOOL:
|
|
// init
|
|
pwszValue = g_pwszAzFalse;
|
|
if (*((BOOL*)pvPropValue))
|
|
{
|
|
pwszValue = g_pwszAzTrue;
|
|
}
|
|
break;
|
|
|
|
case ENUM_AZ_LONG:
|
|
wsprintf(wszNumber, L"%d", (*(LONG*)pvPropValue));
|
|
pwszValue = wszNumber;
|
|
break;
|
|
|
|
case ENUM_AZ_GROUP_TYPE:
|
|
// init
|
|
pwszValue = g_pwszBasicGroup;
|
|
if (AZ_GROUPTYPE_LDAP_QUERY == *((LONG*)pvPropValue))
|
|
{
|
|
pwszValue = g_pwszLdapGroup;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "invalid attribute data type");
|
|
}
|
|
|
|
// persist the data
|
|
hr = myXmlSetNodeAttribute(
|
|
pNode,
|
|
pwszAttrTag,
|
|
pwszValue,
|
|
pDoc);
|
|
_JumpIfError(hr, error, "myXmlSetNodeAttribute");
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pvPropValue)
|
|
{
|
|
XmlAzrolesInfo->AzpeFreeMemory(pvPropValue);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
DWORD
|
|
XMLPersistIsAcl(
|
|
IN PAZP_XML_CONTEXT pPersistContext,
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
OUT BOOL *pfAcl
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(pPersistContext);
|
|
|
|
AZASSERT(NULL != AzpeObjectHandle);
|
|
|
|
//init
|
|
*pfAcl = FALSE;
|
|
|
|
//
|
|
// only authorization store object has acl for xml store
|
|
// Since we don't support XML stores on FAT system, we can safely
|
|
// return TRUE as for authorization store
|
|
//
|
|
|
|
if (OBJECT_TYPE_AZAUTHSTORE == XmlAzrolesInfo->AzpeObjectType( AzpeObjectHandle ))
|
|
{
|
|
*pfAcl = TRUE;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
HRESULT
|
|
XmlSetObjectOptions(
|
|
IN PAZP_XML_CONTEXT PersistContext,
|
|
IN ULONG lPersistFlags,
|
|
IN AZPE_OBJECT_HANDLE pObj
|
|
)
|
|
/*
|
|
Description:
|
|
|
|
Set the object options for the object
|
|
|
|
Arguments:
|
|
PersistContext - Context describing the policy store
|
|
lPersistFlags - flags from the persist engine
|
|
pObj - object to be set
|
|
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
ULONG ObjectOptions = 0;
|
|
BOOL DaclSupported;
|
|
ULONG lObjectType = XmlAzrolesInfo->AzpeObjectType(pObj);
|
|
|
|
//
|
|
// Tell azroles about the object options
|
|
//
|
|
|
|
if ( PersistContext->IsWritable ) {
|
|
ObjectOptions |= AZPE_OPTIONS_WRITABLE;
|
|
ObjectOptions |= AZPE_OPTIONS_CREATE_CHILDREN;
|
|
}
|
|
|
|
if ( lObjectType == OBJECT_TYPE_AZAUTHSTORE ) {
|
|
|
|
dwErr = XMLPersistIsAcl( PersistContext, pObj, &DaclSupported );
|
|
_JumpIfWinError(dwErr, &hr, error, "XMLPersistIsAcl");
|
|
|
|
if ( DaclSupported ) {
|
|
ObjectOptions |= AZPE_OPTIONS_SUPPORTS_DACL;
|
|
ObjectOptions |= AZPE_OPTIONS_SUPPORTS_SACL;
|
|
}
|
|
|
|
if ( PersistContext->HasSecurityPrivilege ) {
|
|
ObjectOptions |= AZPE_OPTIONS_HAS_SECURITY_PRIVILEGE;
|
|
}
|
|
}
|
|
|
|
dwErr = XmlAzrolesInfo->AzpeSetObjectOptions(
|
|
pObj,
|
|
lPersistFlags,
|
|
ObjectOptions );
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeSetObjectOptions");
|
|
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlLoadObjectCommonData(
|
|
IN PAZP_XML_CONTEXT PersistContext,
|
|
IN IXMLDOMNode *pNode,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG lObjectType,
|
|
IN AZPE_OBJECT_HANDLE pParent,
|
|
OUT OPTIONAL AZPE_OBJECT_HANDLE *ppObj)
|
|
/*
|
|
Description:
|
|
load common attributes such as decsription and guid
|
|
from xml node and create az object and set common properties
|
|
Arguments:
|
|
PersistContext - Context describing the policy store
|
|
pNode - xml node object
|
|
lPersistFlags - flags from the persist engine
|
|
lObjectType - object type (OBJECT_TYPE_*)
|
|
pObj - object to be set
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr2 = S_OK;
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
|
|
WCHAR *pwszName = NULL;
|
|
WCHAR *pwszGuid = NULL;
|
|
GUID GuidData;
|
|
|
|
AZPE_OBJECT_HANDLE pObj = NULL;
|
|
|
|
AZASSERT(NULL != pNode &&
|
|
NULL != pParent);
|
|
|
|
if (NULL != ppObj)
|
|
{
|
|
//init
|
|
*ppObj = NULL;
|
|
}
|
|
|
|
if (OBJECT_TYPE_AZAUTHSTORE != lObjectType)
|
|
{
|
|
// get object name from xml node to create initial az object
|
|
hr = myXmlGetNodeAttribute(
|
|
pNode,
|
|
AZ_XML_TAG_ATTR_NAME,
|
|
&pwszName);
|
|
_JumpIfError(hr, error, "myXmlGetNodeAttribute(name)");
|
|
|
|
// object guid must exist
|
|
hr = myXmlGetNodeAttribute(
|
|
pNode,
|
|
AZ_XML_TAG_ATTR_GUID,
|
|
&pwszGuid);
|
|
_JumpIfError(hr, error, "myXmlGetNodeAttribute");
|
|
|
|
hr = UuidFromString(pwszGuid, &GuidData);
|
|
_JumpIfError(hr, error, "UuidFromString");
|
|
|
|
//let's create a real az object
|
|
dwErr = XmlAzrolesInfo->AzpeCreateObject(
|
|
pParent,
|
|
lObjectType,
|
|
pwszName,
|
|
&GuidData,
|
|
lPersistFlags,
|
|
&pObj);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeCreateObject");
|
|
|
|
AZASSERT(NULL != pObj);
|
|
}
|
|
else
|
|
{
|
|
// for AzAuthorizationStore object, it is on the top
|
|
pObj = pParent;
|
|
}
|
|
|
|
// load other common attributes
|
|
|
|
//
|
|
// load description attribute
|
|
//
|
|
hr = myXmlLoadObjectAttribute(
|
|
pNode,
|
|
lPersistFlags,
|
|
AZ_XML_TAG_ATTR_DESCRIPTION,
|
|
pObj,
|
|
AZ_PROP_DESCRIPTION,
|
|
ENUM_AZ_BSTR);
|
|
_JumpIfErrorOrPressOn(hr, hr2, error, lPersistFlags, TRUE, "myXmlLoadObjectAttribute(description)");
|
|
|
|
|
|
//
|
|
// Tell azroles about the object options
|
|
//
|
|
|
|
hr = XmlSetObjectOptions( PersistContext, lPersistFlags, pObj );
|
|
_JumpIfError(hr, error, "XmlSetObjectOptions");
|
|
|
|
|
|
//
|
|
// Return the created object to the caller
|
|
//
|
|
if (OBJECT_TYPE_AZAUTHSTORE != lObjectType &&
|
|
NULL != ppObj)
|
|
{
|
|
// return
|
|
*ppObj = pObj;
|
|
pObj = NULL;
|
|
}
|
|
|
|
_HandlePressOnError(hr, hr2);
|
|
|
|
error:
|
|
if (NULL != pwszName)
|
|
{
|
|
AzpFreeHeap(pwszName );
|
|
}
|
|
if (NULL != pwszGuid)
|
|
{
|
|
AzpFreeHeap(pwszGuid );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
myObAddPropertyItem(
|
|
IN AZPE_OBJECT_HANDLE pObject,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG lPropertyId,
|
|
IN ENUM_AZ_DATATYPE DataType,
|
|
IN PVOID pValue)
|
|
/*
|
|
Description:
|
|
help routine to add property items
|
|
Arguments:
|
|
pObject - az core object pointer
|
|
lPersistFlags - persist flags
|
|
lPropertyId - property ID
|
|
DataType - data type of the property
|
|
pValue - property item value
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
|
|
if (ENUM_AZ_SID_ARRAY == DataType)
|
|
{
|
|
dwErr = XmlAzrolesInfo->AzpeAddPropertyItemSid(
|
|
pObject,
|
|
lPersistFlags,
|
|
lPropertyId,
|
|
pValue);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeAddPropertyItemSid");
|
|
}
|
|
else
|
|
{
|
|
// guid items
|
|
dwErr = XmlAzrolesInfo->AzpeAddPropertyItemGuidString(
|
|
pObject,
|
|
lPersistFlags,
|
|
lPropertyId,
|
|
(WCHAR*)pValue);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeAddPropertyItemGuidString");
|
|
}
|
|
|
|
|
|
hr = S_OK;
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlLoadProperties(
|
|
IN WCHAR const *pwszPropTag,
|
|
IN ULONG lPersistFlags,
|
|
IN ULONG lPropertyId,
|
|
IN ENUM_AZ_DATATYPE DataType,
|
|
IN BOOL fSingle,
|
|
IN IXMLDOMNode *pObjectNode,
|
|
IN AZPE_OBJECT_HANDLE pObject)
|
|
/*
|
|
Description:
|
|
load object properties into az core object
|
|
The property includes both single occurence element or multiple items
|
|
Argument:
|
|
pwszPropTag - property tag name
|
|
lPersistFlags - flags from the persist engine
|
|
lPropertyId - property ID for az group member
|
|
fSid - TRUE if element is in sid string format
|
|
fSingle - the property should be single occurence
|
|
this is used to determine call SetProperty or AddPropertyItem
|
|
pObjectNode - interface pointer to xml object node
|
|
pObject - az object handle to be set
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
BSTR bstrPropTag = NULL;
|
|
BSTR bstrPropValue = NULL;
|
|
IXMLDOMNodeList *pPropList = NULL;
|
|
IXMLDOMNode *pPropNode = NULL;
|
|
LONG lLength;
|
|
LONG i;
|
|
LONG lValue;
|
|
VOID *pValue;
|
|
PSID pSid = NULL;
|
|
|
|
AZASSERT(NULL != pObject &&
|
|
NULL != pwszPropTag &&
|
|
NULL != pObjectNode);
|
|
|
|
// convert to bstr for tag
|
|
bstrPropTag = SysAllocString(pwszPropTag);
|
|
_JumpIfOutOfMemory(&hr, error, bstrPropTag, "SysAllocString");
|
|
|
|
// get element list
|
|
hr = pObjectNode->selectNodes(
|
|
bstrPropTag,
|
|
&pPropList);
|
|
_JumpIfError(hr, error, "pObjectNode->selectNodes");
|
|
|
|
AZASSERT(NULL != pPropList);
|
|
|
|
// get item length
|
|
hr = pPropList->get_length(&lLength);
|
|
_JumpIfError(hr, error, "pPropList->get_length");
|
|
|
|
// add each elements into az object
|
|
for (i = 0; i < lLength; ++i)
|
|
{
|
|
// get element node
|
|
hr = pPropList->get_item(i, &pPropNode);
|
|
_JumpIfError(hr, error, "pPropList->get_item");
|
|
|
|
AZASSERT(NULL != pPropNode);
|
|
|
|
// get element value
|
|
hr = pPropNode->get_text(&bstrPropValue);
|
|
_JumpIfError(hr, error, "pPropNode->get_text");
|
|
|
|
AZASSERT(NULL != bstrPropValue);
|
|
|
|
// init point to as string
|
|
pValue = (VOID*)bstrPropValue;
|
|
|
|
// if in sid format, do conversion
|
|
if (ENUM_AZ_SID_ARRAY == DataType)
|
|
{
|
|
// convert string sid to sid
|
|
if (!ConvertStringSidToSid(bstrPropValue, &pSid))
|
|
{
|
|
AZ_HRESULT_LASTERROR(&hr);
|
|
_JumpError(hr, error, "ConvertStringSidToSid");
|
|
}
|
|
|
|
AZASSERT(NULL != pSid);
|
|
|
|
pValue = (VOID*)pSid;
|
|
}
|
|
else if (ENUM_AZ_LONG == DataType)
|
|
{
|
|
lValue = _wtol(bstrPropValue);
|
|
pValue = (VOID*)&lValue;
|
|
}
|
|
|
|
if (fSingle)
|
|
{
|
|
//
|
|
// if string is empty, don't set it with the exception of BizRule
|
|
// An empty BizRule is significantly different from no biz rule in
|
|
// out current cache implementation.
|
|
//
|
|
|
|
if ( ENUM_AZ_LONG == DataType ||
|
|
(ENUM_AZ_BSTR == DataType && AZ_PROP_TASK_BIZRULE == lPropertyId) ||
|
|
(ENUM_AZ_BSTR == DataType && L'\0' != ((WCHAR*)pValue)[0])
|
|
)
|
|
{
|
|
//
|
|
// if we see a empty string for biz rule, use a one space value so that
|
|
// the cache will have a one space biz rule. Otherwise, the empty string will
|
|
// be interprected as a NULL by our string capture utility AzpCaptureString.
|
|
//
|
|
|
|
PVOID pPropValue = (AZ_PROP_TASK_BIZRULE == lPropertyId && L'\0' == ((WCHAR*)pValue)[0]) ?
|
|
L" " : pValue;
|
|
|
|
dwErr = XmlAzrolesInfo->AzpeSetProperty(
|
|
pObject,
|
|
lPersistFlags,
|
|
lPropertyId,
|
|
pPropValue);
|
|
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeSetProperty");
|
|
}
|
|
|
|
// if single, we should be done
|
|
// in case persist store has more than one, ignore the rest
|
|
goto done;
|
|
}
|
|
|
|
// now ready to add element to az object
|
|
dwErr = myObAddPropertyItem(
|
|
pObject,
|
|
lPersistFlags,
|
|
lPropertyId,
|
|
DataType,
|
|
pValue);
|
|
_JumpIfWinError(dwErr, &hr, error, "myObAddPropertyItem");
|
|
|
|
// free for the next in for loop
|
|
if (NULL != pSid)
|
|
{
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
pPropNode->Release();
|
|
pPropNode = NULL;
|
|
SysFreeString(bstrPropValue);
|
|
bstrPropValue = NULL;
|
|
}
|
|
|
|
done:
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != bstrPropTag)
|
|
{
|
|
SysFreeString(bstrPropTag);
|
|
}
|
|
if (NULL != bstrPropValue)
|
|
{
|
|
SysFreeString(bstrPropValue);
|
|
}
|
|
if (NULL != pSid)
|
|
{
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
if (NULL != pPropList)
|
|
{
|
|
pPropList->Release();
|
|
}
|
|
if (NULL != pPropNode)
|
|
{
|
|
pPropNode->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlLoadObject(
|
|
IN PAZP_XML_CONTEXT PersistContext,
|
|
IN AZPE_OBJECT_HANDLE pParentObject,
|
|
IN ULONG lObjectType,
|
|
IN ULONG lPersistFlags,
|
|
IN BOOL fChildren,
|
|
IN BOOL fFinishAzStoreObject,
|
|
IN IXMLDOMNode *pObjectNode)
|
|
/*
|
|
Description:
|
|
load a persist object and optional all children into az core
|
|
if there is children, it will be recursive routine if load all children
|
|
Arguments:
|
|
PersistContext - Context describing the policy store
|
|
pParentObject - the parent object handle
|
|
lObjectType - the object type that is loaded
|
|
lPersistFlags - flags from the persist engine
|
|
if the flag is AZPE_FLAGS_PERSIST_OPEN, error handling returns immediately
|
|
if the flag is AZPE_FLAGS_PERSIST_UPDATE_CACHE, error handling press on.
|
|
fChildren - TRUE if load child objects
|
|
fFinishAzStoreObject - TRUE if want to call AzpeFinishObject on AzAuthorizationStore
|
|
pObjectNode - the node handle of the object
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr2 = S_OK;
|
|
HRESULT hr;
|
|
AZPE_OBJECT_HANDLE pObject = NULL;
|
|
BSTR bstrChildTag = NULL;
|
|
IXMLDOMNodeList *pChildList = NULL;
|
|
IXMLDOMNode *pChildNode = NULL;
|
|
LONG lLength = 0;
|
|
LONG i;
|
|
|
|
AZASSERT(NULL != pParentObject &&
|
|
NULL != pObjectNode);
|
|
|
|
// load/create object and set common attributes
|
|
hr = myXmlLoadObjectCommonData(
|
|
PersistContext,
|
|
pObjectNode,
|
|
lPersistFlags,
|
|
lObjectType,
|
|
pParentObject,
|
|
&pObject);
|
|
_JumpIfErrorOrPressOn(hr, hr2, error, lPersistFlags, NULL != pObject, "myXmlLoadObjectCommonData");
|
|
|
|
if (OBJECT_TYPE_AZAUTHSTORE == lObjectType)
|
|
{
|
|
// if AzAuthorizationStore object, it is top, object points to parent
|
|
AZASSERT(NULL == pObject);
|
|
pObject = pParentObject;
|
|
}
|
|
#if DBG
|
|
else
|
|
{
|
|
AZASSERT(NULL != pObject);
|
|
}
|
|
#endif //DBG
|
|
|
|
//load object attributes
|
|
if (NULL != g_SubmitLoadTable[lObjectType].rgpAttrs)
|
|
{
|
|
// load object attributes
|
|
AZ_PROP_ENTRY *pAttrs;
|
|
for (pAttrs = g_SubmitLoadTable[lObjectType].rgpAttrs;
|
|
NULL != pAttrs[0].pwszTag; ++pAttrs)
|
|
{
|
|
hr = myXmlLoadObjectAttribute(
|
|
pObjectNode,
|
|
lPersistFlags,
|
|
pAttrs[0].pwszTag,
|
|
pObject,
|
|
pAttrs[0].lPropId,
|
|
pAttrs[0].lDataType);
|
|
_JumpIfErrorOrPressOn(hr, hr2, error, lPersistFlags, TRUE, "myXmlLoadNodeAttribute");
|
|
}
|
|
}
|
|
|
|
//load object property elements
|
|
if (NULL != g_SubmitLoadTable[lObjectType].rgpEles)
|
|
{
|
|
// load object elements
|
|
AZ_PROP_ENTRY *pEles;
|
|
for (pEles = g_SubmitLoadTable[lObjectType].rgpEles;
|
|
NULL != pEles[0].pwszTag; ++pEles)
|
|
{
|
|
hr = myXmlLoadProperties(
|
|
pEles[0].pwszTag,
|
|
lPersistFlags,
|
|
pEles[0].lPropId,
|
|
pEles[0].lDataType,
|
|
pEles[0].fSingle, //single occurence
|
|
pObjectNode,
|
|
pObject);
|
|
_JumpIfErrorOrPressOn(hr, hr2, error, lPersistFlags, TRUE, "myXmlLoadProperties");
|
|
}
|
|
}
|
|
|
|
//
|
|
// load the object children
|
|
//
|
|
if (fChildren && NULL != g_SubmitLoadTable[lObjectType].rgpChildren)
|
|
{
|
|
AZ_CHILD_ENTRY *pChild;
|
|
for (pChild = g_SubmitLoadTable[lObjectType].rgpChildren;
|
|
NULL != pChild[0].pwszChildTag; ++pChild)
|
|
{
|
|
bstrChildTag = SysAllocString(pChild->pwszChildTag);
|
|
_JumpIfOutOfMemory(&hr, error, bstrChildTag, "SysAllocString");
|
|
|
|
hr = pObjectNode->selectNodes(
|
|
bstrChildTag,
|
|
&pChildList);
|
|
_JumpIfError(hr, error, "pObjectNode->selectNodes");
|
|
|
|
hr = pChildList->get_length(&lLength);
|
|
_JumpIfError(hr, error, "pTaskList->get_length");
|
|
|
|
// add each child to the current object
|
|
for (i = 0; i < lLength; ++i)
|
|
{
|
|
// get each child node
|
|
hr = pChildList->get_item(i, &pChildNode);
|
|
_JumpIfError(hr, error, "pChildList->get_item");
|
|
|
|
AZASSERT(NULL != pChildNode);
|
|
|
|
// make sure is a valid child, at least an element
|
|
hr = myXmlValidateNodeType(
|
|
pChildNode,
|
|
NODE_ELEMENT);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// ok, load child into the current object
|
|
hr = myXmlLoadObject(
|
|
PersistContext,
|
|
pObject, // parent, which is the current object
|
|
pChild->ChildType,
|
|
lPersistFlags,
|
|
fChildren,
|
|
fFinishAzStoreObject,
|
|
pChildNode);
|
|
_JumpIfErrorOrPressOn(hr, hr2, error, lPersistFlags, TRUE, "myXmlLoadObject");
|
|
}
|
|
|
|
// free node for the next child
|
|
pChildNode->Release();
|
|
pChildNode = NULL;
|
|
}
|
|
|
|
// free for the next
|
|
SysFreeString(bstrChildTag);
|
|
bstrChildTag = NULL;
|
|
pChildList->Release();
|
|
pChildList = NULL;
|
|
}
|
|
}
|
|
|
|
_HandlePressOnError(hr, hr2);
|
|
error:
|
|
if (NULL != bstrChildTag)
|
|
{
|
|
SysFreeString(bstrChildTag);
|
|
}
|
|
if (NULL != pChildList)
|
|
{
|
|
pChildList->Release();
|
|
}
|
|
if (NULL != pChildNode)
|
|
{
|
|
pChildNode->Release();
|
|
}
|
|
|
|
if ((NULL != pObject) &&
|
|
((OBJECT_TYPE_AZAUTHSTORE != lObjectType) ||
|
|
(OBJECT_TYPE_AZAUTHSTORE == lObjectType && fFinishAzStoreObject)
|
|
)
|
|
)
|
|
{
|
|
XmlAzrolesInfo->AzpeObjectFinished( pObject, AzpHresultToWinStatus(hr) );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlGetAzStoreNode(
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
OUT IXMLDOMElement **ppAzStoreNode)
|
|
/*
|
|
get the az top node, authorization store
|
|
IN: pDoc, the root node, document node
|
|
IN: ppAzStoreNode, AzAuthStore node for return
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
AZASSERT(NULL != pDoc &&
|
|
NULL != ppAzStoreNode);
|
|
|
|
// get the root node, ie. the node of authorization store policy
|
|
hr = pDoc->get_documentElement(ppAzStoreNode);
|
|
_JumpIfError(hr, error, "pDoc->get_documentElement");
|
|
|
|
AZASSERT(NULL != *ppAzStoreNode);
|
|
|
|
// make sure it has the correct tag
|
|
hr = myXmlValidateNodeTag(
|
|
*ppAzStoreNode,
|
|
AZ_XML_TAG_AZSTORE);
|
|
_JumpIfError(hr, error, "myXmlValidateNodeTag");
|
|
|
|
hr = S_OK;
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlLoadPolicyToAzAuthorizationStore(
|
|
IN PAZP_XML_CONTEXT PersistContext,
|
|
IN BOOL fCreatePolicy,
|
|
IN ULONG lPersistFlags,
|
|
IN IXMLDOMDocument2 *pDoc)
|
|
/*
|
|
load xml policy data into az authorization store object
|
|
PersistContext - Context describing the policy store
|
|
IN: lPersistFlags, flags from the persist engine
|
|
IN: pDoc, interface pointer to xml root node, document
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
IXMLDOMElement *pAzStoreNode = NULL;
|
|
|
|
AZASSERT(NULL != PersistContext &&
|
|
NULL != pDoc);
|
|
|
|
//init
|
|
|
|
// get the root node, ie. the node of ploicy
|
|
hr = myXmlGetAzStoreNode(pDoc, &pAzStoreNode);
|
|
_JumpIfError(hr, error, "myXmlGetAzStoreNode");
|
|
|
|
AZASSERT(NULL != pAzStoreNode);
|
|
|
|
// start load AzAuthorizationStore object, this call invokes
|
|
// recursively to load all AzAuthStore object children
|
|
hr = myXmlLoadObject(
|
|
PersistContext,
|
|
PersistContext->hAzAuthorizationStore,
|
|
OBJECT_TYPE_AZAUTHSTORE,
|
|
lPersistFlags,
|
|
TRUE, //load all child objects
|
|
FALSE, // we have to AzpeFinishObject later on AzAuthStore
|
|
pAzStoreNode);
|
|
_JumpIfError(hr, error, "myXmlLoadObject(AzAuthorizationStore)");
|
|
|
|
//
|
|
// On create, don't do this until AzPersistSubmit
|
|
//
|
|
if ( !fCreatePolicy ) {
|
|
|
|
// now load AzAuthStore object policy acl
|
|
hr = myXmlLoadAclsToAzStore(
|
|
PersistContext,
|
|
lPersistFlags,
|
|
FALSE );
|
|
_JumpIfError(hr, error, "myXmlLoadAclsToAzStore");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pAzStoreNode)
|
|
{
|
|
pAzStoreNode->Release();
|
|
}
|
|
XmlAzrolesInfo->AzpeObjectFinished( PersistContext->hAzAuthorizationStore, AzpHresultToWinStatus(hr) );
|
|
return hr;
|
|
}
|
|
|
|
DWORD
|
|
XMLPersistWritable(
|
|
IN PAZP_XML_CONTEXT XmlPersistContext
|
|
)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
// init
|
|
XmlPersistContext->IsWritable = FALSE;
|
|
|
|
// xml is easy, all object writable depends on policy file permission
|
|
|
|
hFile = CreateFile(
|
|
XmlPersistContext->pwszPolicyUrl,
|
|
GENERIC_WRITE,
|
|
0, // not shared
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
XmlPersistContext->IsWritable = TRUE;
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
XMLPersistTargetMachine(
|
|
IN PCWSTR PolicyUrl,
|
|
OUT PWSTR *ppwszTargetMachine OPTIONAL
|
|
)
|
|
/*++
|
|
Routine Description
|
|
Routine to determine the target machine name for account resolution
|
|
based on the policy URL.
|
|
|
|
For XML store, the target machine is the server (machine) name that
|
|
the XML store is physically stored. If fail to get the machine name,
|
|
the local machine (NULL) will be returned.
|
|
|
|
Arguments
|
|
|
|
PolicyUrl - the policy URL passed in
|
|
|
|
ppwszTargetMachine - the output target machine name
|
|
|
|
Return Value
|
|
|
|
NO_ERROR
|
|
|
|
--*/
|
|
{
|
|
|
|
LPWSTR pszUNC = NULL;
|
|
LPWSTR pszPath = (LPWSTR)PolicyUrl;
|
|
BOOL bIsUNC = FALSE;
|
|
DWORD WinStatus = NO_ERROR;
|
|
PWSTR szServer=NULL;
|
|
|
|
|
|
if ( PolicyUrl == NULL || ppwszTargetMachine == NULL ) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppwszTargetMachine = NULL;
|
|
|
|
if ( wcslen(PolicyUrl) <= 2 ) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// determine if the URL is a UNC path first
|
|
//
|
|
if ( PolicyUrl[0] == L'\\' && PolicyUrl[1] == L'\\' ) {
|
|
bIsUNC = TRUE;
|
|
}
|
|
|
|
if (!bIsUNC && PolicyUrl[1] != L':') {
|
|
//
|
|
// this is a relative path, which must be on the local machine
|
|
// in which case *ppwszTargetMachine is set to NULL
|
|
//
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if ( !bIsUNC ) {
|
|
|
|
//
|
|
// if it's a drive letter mapping
|
|
// check to see if this is a remote path
|
|
//
|
|
WinStatus = myGetRemotePath(PolicyUrl, &pszUNC);
|
|
|
|
if ( WinStatus == NO_ERROR ) {
|
|
//
|
|
// this is a valid network path
|
|
//
|
|
pszPath = pszUNC;
|
|
|
|
} else {
|
|
|
|
//
|
|
// either this is not a network drive
|
|
// or fail to resolve the connection
|
|
// in either case, use the local machine as default
|
|
// don't care the mount point much in this case since
|
|
// the mount point and a local drive use the same server name
|
|
//
|
|
WinStatus = NO_ERROR;
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// the path must be a UNC path when it gets here
|
|
// get volume information related to the path (could be a mount point or DFS)
|
|
//
|
|
|
|
WCHAR szVolume[MAX_PATH];
|
|
szVolume[0] = L'\0';
|
|
|
|
myGetVolumeInfo(pszPath,
|
|
szVolume,
|
|
ARRAYLEN(szVolume));
|
|
|
|
SafeAllocaAllocate( szServer, MAX_PATH*sizeof(WCHAR) );
|
|
|
|
if ( szServer == NULL ) {
|
|
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// get server name related to the volume
|
|
//
|
|
szServer[0] = L'\0';
|
|
BOOL bIsDfs = IsDfsPath(szVolume, szServer, MAX_PATH);
|
|
DWORD dwLen = 0;
|
|
|
|
if ( bIsDfs ) {
|
|
//
|
|
// this is a DFS volume, server name is already stored in szServer
|
|
//
|
|
dwLen = (DWORD)wcslen(szServer);
|
|
|
|
} else {
|
|
//
|
|
// not a DFS share, just use the server name in the UNC path
|
|
//
|
|
PWSTR pSlash = wcschr(&szVolume[2], L'\\');
|
|
if (pSlash)
|
|
dwLen = (ULONG)(pSlash - szVolume) - 2;
|
|
else
|
|
dwLen = (DWORD)wcslen(szVolume) - 2;
|
|
|
|
}
|
|
|
|
//
|
|
// copy the target machine name
|
|
//
|
|
*ppwszTargetMachine = (PWSTR)XmlAzrolesInfo->AzpeAllocateMemory((dwLen+1)*sizeof(WCHAR));
|
|
|
|
if ( *ppwszTargetMachine ) {
|
|
|
|
wcsncpy(*ppwszTargetMachine,
|
|
bIsDfs? szServer : szVolume + 2, // skip two backslashes
|
|
dwLen);
|
|
|
|
(*ppwszTargetMachine)[dwLen] = L'\0';
|
|
|
|
} else {
|
|
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
if ( szServer ) {
|
|
|
|
SafeAllocaFree( szServer );
|
|
|
|
}
|
|
|
|
AzpFreeHeap(pszUNC);
|
|
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
XmlProviderInitialize(
|
|
IN PAZPE_AZROLES_INFO AzrolesInfo,
|
|
OUT PAZPE_PROVIDER_INFO *ProviderInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Routine to initialize the XML provider
|
|
|
|
Arguments
|
|
|
|
AzrolesInfo - Specifies the interface to routines in azroles.dll
|
|
|
|
ProviderInfo - Returns a pointer to the provider info for the provider
|
|
|
|
Return Value
|
|
|
|
NO_ERROR - Provider was properly initialized
|
|
ERROR_UNKNOWN_REVISION - AzrolesInfo is a version the provider doesn't support
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Ensure the azroles info is a version we understand
|
|
//
|
|
|
|
if ( AzrolesInfo->AzrolesInfoVersion < AZPE_AZROLES_INFO_VERSION_1 ) {
|
|
return ERROR_UNKNOWN_REVISION;
|
|
}
|
|
|
|
XmlAzrolesInfo = AzrolesInfo;
|
|
*ProviderInfo = &XmlProviderInfo;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXMLTestVersion (
|
|
IXMLDOMDocument2 * pDoc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine if the major and minor version attributes
|
|
persisted in the document is allows us to continue reading
|
|
|
|
Arguments:
|
|
|
|
pDoc - COM interface to the opened XML document
|
|
|
|
Return Value:
|
|
|
|
S_OK - we can continue reading.
|
|
|
|
AZ_HRESULT(ERROR_REVISION_MISMATCH) if we can't continue reading due to version mismatch
|
|
|
|
Other HRESULT code
|
|
|
|
Note:
|
|
|
|
Current implementation is that if the store is written by a azroles.dll of a
|
|
newer major version than that of the current reading azroles.dll,
|
|
then reading is not supported.
|
|
|
|
--*/
|
|
{
|
|
CComPtr<IXMLDOMElement> srpDocElement;
|
|
HRESULT hr = pDoc->get_documentElement(&srpDocElement);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CComVariant varMajVer;
|
|
CComBSTR bstrMajVer(AZ_XML_TAG_ATTR_MAJOR_VERSION);
|
|
|
|
if (bstrMajVer.m_str == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
hr = srpDocElement->getAttribute(bstrMajVer, &varMajVer);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && varMajVer.vt == VT_BSTR)
|
|
{
|
|
VARIANT varMajVerInt;
|
|
VariantInit(&varMajVerInt);
|
|
|
|
hr = VariantChangeType(&varMajVerInt, &varMajVer, VARIANT_NOVALUEPROP, VT_UI4);
|
|
|
|
//
|
|
// If this variant is can't even convert to an integer, we will also consider
|
|
// it a mismatch because other errors will be more misleading.
|
|
//
|
|
|
|
if ( FAILED(hr) || (varMajVerInt.ulVal > AzGlCurrAzRolesMajorVersion) )
|
|
{
|
|
hr = AZ_HRESULT(ERROR_REVISION_MISMATCH);
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else if ( E_OUTOFMEMORY != hr )
|
|
{
|
|
//
|
|
// Reading a BSTR out from the DOM node may fail. But we will
|
|
// consider all failures except out-of-memory as a failure
|
|
// of not having version numbers and thus a version mismatch error.
|
|
//
|
|
|
|
hr = AZ_HRESULT(ERROR_REVISION_MISMATCH);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
DWORD
|
|
XMLPersistOpenEx(
|
|
IN LPCWSTR PolicyUrl,
|
|
IN PAZP_XML_CONTEXT OldXmlContext OPTIONAL,
|
|
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
|
|
IN ULONG lPersistFlags,
|
|
IN BOOL fCreatePolicy,
|
|
OUT PAZPE_PERSIST_CONTEXT PersistContext OPTIONAL,
|
|
OUT LPWSTR *pwszTargetMachine OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is shared betwen XMLPersistOpen and XMLPersistUpdateCache.
|
|
|
|
This routine submits reads the authz policy database from storage.
|
|
This routine also reads the policy database into cache.
|
|
|
|
On Success, the caller should call XMLPersistClose to free any resources
|
|
consumed by the open.
|
|
|
|
On entry, AzAuthorizationStore->PersistCritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
PolicyUrl - Specifies the location of the policy store
|
|
|
|
OldXmlContext - On an AzUpdateCache, specifies the existing context
|
|
|
|
pAzAuthorizationStore - Specifies the policy database that is to be read.
|
|
|
|
lPersistFlags - lPersistFlags from the persist engine describing the operation
|
|
AZPE_FLAGS_PERSIST_OPEN - Call is the original AzInitialize
|
|
AZPE_FLAGS_PERSIST_UPDATE_CACHE - Call is an AzUpdateCache
|
|
|
|
fCreatePolicy - TRUE if the policy database is to be created.
|
|
FALSE if the policy database already exists
|
|
|
|
PersistContext - On Success, returns a context that should be passed on all
|
|
subsequent calls.
|
|
The caller should call XMLPersistClose to free this context.
|
|
This parameter is only returned for AzInitialize calls
|
|
|
|
pwszTargetMachine - pointer to the target machine name where account resolution
|
|
should occur. This should be the machine where the ldap data
|
|
is stored.
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
AZP_XML_CONTEXT *pXMLPersistContext = NULL;
|
|
ULONG PolicyUrlSize;
|
|
WCHAR FullPathBuffer[MAX_PATH+1];
|
|
ULONG FullPathLen;
|
|
|
|
//
|
|
// using CComVariant will allow us not to worry about
|
|
// clearing the variant.
|
|
//
|
|
|
|
CComVariant varParam;
|
|
|
|
VARIANT_BOOL varbLoad;
|
|
BSTR bstrEmptyPolicy = NULL;
|
|
BOOL fPolicyFileExists;
|
|
|
|
DWORD dwErr;
|
|
|
|
//
|
|
// Get the full path name so that underlying components don't have to deal with
|
|
// relative path names
|
|
//
|
|
|
|
FullPathLen = GetFullPathName( PolicyUrl,
|
|
MAX_PATH+1,
|
|
FullPathBuffer,
|
|
NULL );
|
|
|
|
if ( FullPathLen == 0 ) {
|
|
AZ_HRESULT_LASTERROR(&hr);
|
|
_JumpError(hr, error, "GetFullPathName");
|
|
} else if ( FullPathLen > MAX_PATH ) {
|
|
dwErr = ERROR_INVALID_NAME;
|
|
_JumpIfWinError(dwErr, &hr, error, "GetFullPathName");
|
|
}
|
|
|
|
//
|
|
// If this file is on a FAT file system, then we won't support it.
|
|
// And we won't to fail it early at the open stage.
|
|
//
|
|
|
|
dwErr = IsAclSupported( FullPathBuffer );
|
|
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
_JumpIfWinError(dwErr, &hr, error, "XML stores on FAT file system are not supported");
|
|
}
|
|
|
|
PolicyUrl = FullPathBuffer;
|
|
PolicyUrlSize = (FullPathLen+1) * sizeof(WCHAR);
|
|
|
|
//
|
|
// allocate a context describing this provider
|
|
//
|
|
|
|
pXMLPersistContext = (PAZP_XML_CONTEXT) AzpAllocateHeap(
|
|
sizeof( *pXMLPersistContext ) + PolicyUrlSize,
|
|
"XMCTXT" );
|
|
|
|
_JumpIfOutOfMemory(&hr, error, pXMLPersistContext, "AzpAllocateHeap");
|
|
|
|
ZeroMemory(pXMLPersistContext, sizeof(*pXMLPersistContext));
|
|
|
|
pXMLPersistContext->hAzAuthorizationStore = pAzAuthorizationStore;
|
|
|
|
//
|
|
// Cache the PolicyUrl
|
|
//
|
|
|
|
pXMLPersistContext->pwszPolicyUrl = (LPWSTR)(pXMLPersistContext+1);
|
|
|
|
RtlCopyMemory( (PVOID)pXMLPersistContext->pwszPolicyUrl,
|
|
PolicyUrl,
|
|
PolicyUrlSize );
|
|
|
|
// create xml doc interface
|
|
hr = CoCreateInstance(
|
|
CLSID_DOMDocument,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IXMLDOMDocument2,
|
|
(void**)&(pXMLPersistContext->pDoc));
|
|
|
|
_JumpIfError(hr, error, "CoCreateInstance");
|
|
|
|
// load xml store
|
|
hr = pXMLPersistContext->pDoc->put_async(FALSE);
|
|
_JumpIfError(hr, error, "pDoc->put_async");
|
|
|
|
if (fCreatePolicy)
|
|
{
|
|
// only real open mode allows creating new store
|
|
AZASSERT(AZPE_FLAGS_PERSIST_OPEN & lPersistFlags);
|
|
|
|
// create a new policy
|
|
|
|
// prevent creating new store if the file exists
|
|
hr = myFileExist(pXMLPersistContext->pwszPolicyUrl, &fPolicyFileExists);
|
|
_JumpIfError(hr, error, "myFileExist");
|
|
|
|
if (fPolicyFileExists)
|
|
{
|
|
hr = AZ_HRESULT(ERROR_ALREADY_EXISTS);
|
|
_JumpError(hr, error, "can't create new policy store if it exists");
|
|
}
|
|
|
|
bstrEmptyPolicy = SysAllocString(
|
|
L"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<AzAdminManager MajorVersion=\"1\" MinorVersion=\"0\">\n</AzAdminManager>");
|
|
_JumpIfOutOfMemory(&hr, error, bstrEmptyPolicy, "SysAllocString");
|
|
|
|
// load empty policy
|
|
hr = pXMLPersistContext->pDoc->loadXML(
|
|
bstrEmptyPolicy,
|
|
&varbLoad);
|
|
_JumpIfXmlError(hr, &hr, error, pXMLPersistContext->pDoc, "pDoc->loadXML")
|
|
|
|
// Assume it is writable
|
|
pXMLPersistContext->IsWritable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
|
|
hr = myWszToBstrVariant(
|
|
pXMLPersistContext->pwszPolicyUrl,
|
|
&varParam);
|
|
_JumpIfError(hr, error, "myWszToBstrVariant");
|
|
|
|
// load doc object
|
|
hr = pXMLPersistContext->pDoc->load(varParam, &varbLoad);
|
|
// we want to get xml error code
|
|
hr = myGetXmlError(hr, pXMLPersistContext->pDoc, NULL);
|
|
if (0x800c0006 == hr)
|
|
{
|
|
// 0x800c0006, xml error,
|
|
// The system cannot locate the object specified.
|
|
// we want to map it file not found error
|
|
hr = AZ_HRESULT(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
_JumpIfError(hr, error, "pDoc->load")
|
|
|
|
//
|
|
// now we have loaded the XML document, we need to see
|
|
// if the version of the document is what we can understand
|
|
//
|
|
|
|
hr = myXMLTestVersion(pXMLPersistContext->pDoc);
|
|
|
|
_JumpIfError(hr, error, "myXMLReadTestVersion failed.");
|
|
|
|
//
|
|
// Determine if the file is writable.
|
|
// We purposely ignore the return code in case the caller
|
|
// doesn't have write access.
|
|
//
|
|
|
|
dwErr = XMLPersistWritable( pXMLPersistContext );
|
|
if (NO_ERROR != dwErr)
|
|
{
|
|
AzPrint((AZD_XML, "XMLPersistWritable returns error code: %d", dwErr));
|
|
}
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA fad;
|
|
|
|
//
|
|
// We need the lastWrite filetime
|
|
//
|
|
|
|
if (GetFileAttributesEx(pXMLPersistContext->pwszPolicyUrl,
|
|
GetFileExInfoStandard,
|
|
&fad))
|
|
{
|
|
pXMLPersistContext->FTLastWrite = fad.ftLastWriteTime;
|
|
}
|
|
else
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine if the caller has security privilege
|
|
//
|
|
// For UpdateCache, the privilege is already known
|
|
//
|
|
if ( lPersistFlags & AZPE_FLAGS_PERSIST_UPDATE_CACHE ) {
|
|
|
|
pXMLPersistContext->HasSecurityPrivilege = OldXmlContext->HasSecurityPrivilege;
|
|
|
|
//
|
|
// For Initialize, determine privilege by accessing the server
|
|
//
|
|
|
|
} else {
|
|
|
|
dwErr = XmlCheckSecurityPrivilege( pXMLPersistContext->pwszPolicyUrl,
|
|
fCreatePolicy );
|
|
|
|
if ( dwErr == NO_ERROR ) {
|
|
pXMLPersistContext->HasSecurityPrivilege = TRUE;
|
|
} else if ( dwErr == ERROR_PRIVILEGE_NOT_HELD ) {
|
|
pXMLPersistContext->HasSecurityPrivilege = FALSE;
|
|
} else {
|
|
_JumpIfWinError(dwErr, &hr, error, "XmlCheckSecurityPrivilege");
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Load the policy database
|
|
//
|
|
|
|
hr = myXmlLoadPolicyToAzAuthorizationStore(
|
|
pXMLPersistContext,
|
|
fCreatePolicy,
|
|
lPersistFlags,
|
|
pXMLPersistContext->pDoc); //the current node in xml tree
|
|
_JumpIfError(hr, error, "myXmlLoadPolicyToAzAuthorizationStore");
|
|
|
|
//
|
|
// On an UpdateCache,
|
|
// update the old context to contain the new doc instance
|
|
//
|
|
if (AZPE_FLAGS_PERSIST_UPDATE_CACHE & lPersistFlags)
|
|
{
|
|
IXMLDOMDocument2 *pTempDoc;
|
|
|
|
pTempDoc = pXMLPersistContext->pDoc;
|
|
pXMLPersistContext->pDoc = OldXmlContext->pDoc;
|
|
OldXmlContext->pDoc = pTempDoc;
|
|
|
|
//
|
|
// On an Initialize,
|
|
// return the context to the caller.
|
|
|
|
} else {
|
|
|
|
// return the persist context to the caller
|
|
*PersistContext = (AZPE_PERSIST_CONTEXT)pXMLPersistContext;
|
|
pXMLPersistContext = NULL;
|
|
|
|
}
|
|
|
|
if ( pwszTargetMachine ) {
|
|
//
|
|
// detect the target machine name for this URL
|
|
// if couldn't determine, default to local (NULL)
|
|
// other errors such as out of memory will fail the function
|
|
//
|
|
|
|
dwErr = XMLPersistTargetMachine( PolicyUrl, pwszTargetMachine );
|
|
|
|
_JumpIfWinError(dwErr, &hr, error, "XMLPersistTargetMachine");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
//free
|
|
if (NULL != pXMLPersistContext)
|
|
{
|
|
if (NULL != pXMLPersistContext->pDoc)
|
|
{
|
|
pXMLPersistContext->pDoc->Release();
|
|
}
|
|
AzpFreeHeap(pXMLPersistContext);
|
|
}
|
|
if (NULL != bstrEmptyPolicy)
|
|
{
|
|
SysFreeString(bstrEmptyPolicy);
|
|
}
|
|
|
|
return AzpHresultToWinStatus( hr );
|
|
}
|
|
|
|
DWORD
|
|
XMLPersistOpen(
|
|
IN LPCWSTR PolicyUrl,
|
|
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
|
|
IN ULONG lPersistFlags,
|
|
IN BOOL fCreatePolicy,
|
|
OUT PAZPE_PERSIST_CONTEXT PersistContext,
|
|
OUT LPWSTR *pwszTargetMachine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine submits reads the authz policy database from storage.
|
|
This routine also reads the policy database into cache.
|
|
|
|
On Success, the caller should call XMLPersistClose to free any resources
|
|
consumed by the open.
|
|
|
|
On entry, AzAuthorizationStore->PersistCritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
PolicyUrl - Specifies the location of the policy store
|
|
|
|
pAzAuthorizationStore - Specifies the policy database that is to be read.
|
|
|
|
lPersistFlags - lPersistFlags from the persist engine describing the operation
|
|
AZPE_FLAGS_PERSIST_OPEN - Call is the original AzInitialize
|
|
AZPE_FLAGS_PERSIST_UPDATE_CACHE - Call is an AzUpdateCache
|
|
|
|
fCreatePolicy - TRUE if the policy database is to be created.
|
|
FALSE if the policy database already exists
|
|
|
|
PersistContext - On Success, returns a context that should be passed on all
|
|
subsequent calls.
|
|
The caller should call XMLPersistClose to free this context.
|
|
|
|
pwszTargetMachine - pointer to the target machine name where account resolution
|
|
should occur. This should be the machine where the ldap data
|
|
is stored.
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
AZASSERT(AZPE_FLAGS_PERSIST_OPEN & lPersistFlags);
|
|
|
|
//
|
|
// must be freed
|
|
//
|
|
|
|
LPWSTR pwszFilePolUrl = NULL;
|
|
DWORD dwErr;
|
|
|
|
// our persist layer has determined that this is an XML policy. That means the
|
|
// URL must have the MSXML prefix and ':' following it.
|
|
|
|
if (_wcsnicmp( AZ_XML_PROVIDER_NAME, PolicyUrl, AZ_XML_PROVIDER_NAME_LENGTH) == 0) {
|
|
if (PolicyUrl[AZ_XML_PROVIDER_NAME_LENGTH] == L':')
|
|
{
|
|
PolicyUrl += AZ_XML_PROVIDER_NAME_LENGTH;
|
|
}
|
|
else
|
|
{
|
|
// this shouldn't have happened
|
|
|
|
AZASSERT(FALSE);
|
|
dwErr = ERROR_INVALID_NAME;
|
|
_JumpError(dwErr, error, "Improper MSXML URL");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// this shouldn't have happened
|
|
AZASSERT(FALSE);
|
|
dwErr = ERROR_INVALID_NAME;
|
|
_JumpError(dwErr, error, "XML provider is invoked without proper MSXML prefix");
|
|
}
|
|
|
|
// we will prefix the policy url using file: so that we can rely on file: protocol
|
|
// to handle the validity or the url.
|
|
|
|
LPCWSTR wchFileUrl = L"file";
|
|
|
|
SafeAllocaAllocate(pwszFilePolUrl, ( wcslen(wchFileUrl) + wcslen(PolicyUrl) + 1) * sizeof(WCHAR) );
|
|
|
|
if (pwszFilePolUrl == NULL)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
_JumpError(dwErr, error, "SafeAllocaAllocate");
|
|
}
|
|
|
|
memcpy(pwszFilePolUrl, wchFileUrl, wcslen(wchFileUrl) * sizeof(WCHAR));
|
|
memcpy(pwszFilePolUrl + wcslen(wchFileUrl), PolicyUrl, (wcslen(PolicyUrl) + 1) * sizeof(WCHAR));
|
|
|
|
WCHAR wszDosPath[MAX_PATH + 1];
|
|
DWORD dwPathLen = MAX_PATH + 1;
|
|
|
|
//
|
|
// PathCreateFromUrl will convert a file protocol URL (file:///<path>) to a dos path
|
|
// There are several gotchas:
|
|
// (1) A wellformed file URL should use 3 slashes, not 2.
|
|
// (2) However, as far as the path portion has no encodings, two slashes will work.
|
|
// (3) If you want to use encoding, then you have to use "msxml:///<path>" with 3 slashes!
|
|
//
|
|
|
|
dwErr = AzpHresultToWinStatus(PathCreateFromUrl(pwszFilePolUrl, wszDosPath, &dwPathLen, 0));
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
if (dwPathLen > MAX_PATH + 1)
|
|
{
|
|
dwErr = ERROR_INVALID_NAME;
|
|
}
|
|
_JumpError(dwErr, error, "PathCreateFromUrl");
|
|
}
|
|
else if (dwPathLen == 0)
|
|
{
|
|
dwErr = ERROR_INVALID_NAME;
|
|
_JumpError(dwErr, error, "PathCreateFromUrl");
|
|
}
|
|
|
|
//
|
|
// Call the worker routine
|
|
//
|
|
|
|
dwErr = XMLPersistOpenEx(wszDosPath,
|
|
NULL, // No old context
|
|
pAzAuthorizationStore,
|
|
lPersistFlags,
|
|
fCreatePolicy,
|
|
PersistContext,
|
|
pwszTargetMachine );
|
|
|
|
error:
|
|
|
|
if (pwszFilePolUrl != NULL)
|
|
{
|
|
SafeAllocaFree(pwszFilePolUrl);
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
DWORD
|
|
XMLPersistUpdateCache(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext,
|
|
IN ULONG lPersistFlags,
|
|
OUT ULONG * pulUpdatedFlag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the cache to reflect the current contents of the
|
|
policy database.
|
|
|
|
On entry, AzAuthorizationStore->PersistCritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
PersistContext - Specifies the policy database that is to be closed
|
|
|
|
lPersistFlags - lPersistFlags from the persist engine describing the operation
|
|
AZPE_FLAGS_PERSIST_UPDATE_CACHE - Call is an AzUpdateCache
|
|
|
|
pulUpdatedFlag - Passing back information whether the function call has truly
|
|
caused an update. Due to possible efficient updateCache
|
|
implementation by providers, it may decide to do nothing.
|
|
Currently, we only one bit (AZPE_FLAG_CACHE_UPDATE_STORE_LEVEL) to
|
|
indicate that the update is actually carried out.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
AZP_XML_CONTEXT *pXMLPersistContext = (AZP_XML_CONTEXT *)PersistContext;
|
|
|
|
//
|
|
// Call the worker routine
|
|
//
|
|
|
|
AZASSERT(AZPE_FLAGS_PERSIST_UPDATE_CACHE & lPersistFlags);
|
|
AZASSERT( pXMLPersistContext != NULL );
|
|
AZASSERT( pulUpdatedFlag != NULL );
|
|
|
|
//
|
|
// assume no update at all
|
|
//
|
|
|
|
*pulUpdatedFlag = 0;
|
|
|
|
BOOL bNeedUpdate;
|
|
|
|
DWORD dwStatus = myXmlStoreHasUpdate(pXMLPersistContext, &bNeedUpdate);
|
|
|
|
if (dwStatus != NO_ERROR)
|
|
{
|
|
_JumpError(dwStatus, error, "myXmlStoreHasUpdate");
|
|
}
|
|
else if (bNeedUpdate)
|
|
{
|
|
dwStatus = XMLPersistOpenEx( pXMLPersistContext->pwszPolicyUrl,
|
|
pXMLPersistContext,
|
|
pXMLPersistContext->hAzAuthorizationStore,
|
|
lPersistFlags,
|
|
FALSE, // Don't create the store
|
|
NULL,
|
|
NULL ); // Don't return a new persist context
|
|
|
|
if (NO_ERROR == dwStatus)
|
|
{
|
|
*pulUpdatedFlag = AZPE_FLAG_CACHE_UPDATE_STORE_LEVEL;
|
|
}
|
|
}
|
|
|
|
error:
|
|
|
|
return dwStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
XMLPersistClose(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine submits close the authz policy database storage handles.
|
|
|
|
On entry, AzAuthorizationStore->PersistCritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
PersistContext - Specifies the policy database that is to be closed
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
AZP_XML_CONTEXT *pPersistContext = (AZP_XML_CONTEXT *)PersistContext;
|
|
AZASSERT(pPersistContext != NULL);
|
|
|
|
// release xml doc
|
|
|
|
pPersistContext->pDoc->Release();
|
|
|
|
// free the context itself
|
|
|
|
AzpFreeHeap(pPersistContext);
|
|
}
|
|
|
|
|
|
DWORD
|
|
XMLPersistDelete(
|
|
IN LPCWSTR PolicyUrl)
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
|
|
AZASSERT(NULL != PolicyUrl);
|
|
|
|
// delete xml store is simple
|
|
if (!DeleteFile( PolicyUrl))
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
//
|
|
// implementation of updating xml store from az objects
|
|
//
|
|
|
|
|
|
HRESULT
|
|
myXmlGetNodeByAttribute(
|
|
IN LPCWSTR pwszAtt,
|
|
IN LPCWSTR pwszValue,
|
|
IN IXMLDOMNodeList *pNodeList,
|
|
OUT IXMLDOMNode **ppNode)
|
|
/*
|
|
find a node from a node list matching by given attribute's value.
|
|
IN: pwszAtt, the name of the attribute on which the lookup will be based on.
|
|
IN: pwszValue, the value of the attribute which we are looking for.
|
|
IN: pNodeList, interface pointer to a list of nodes
|
|
OUT: ppNode, the matched node
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
LONG lLength;
|
|
LONG i;
|
|
IXMLDOMNode *pNode = NULL;
|
|
WCHAR *pwszXmlAttrValue = NULL;
|
|
|
|
AZASSERT(NULL != pwszAtt &&
|
|
NULL != pwszValue &&
|
|
NULL != pNodeList &&
|
|
NULL != ppNode);
|
|
|
|
// init
|
|
*ppNode = NULL;
|
|
|
|
// get length of the list
|
|
hr = pNodeList->get_length(&lLength);
|
|
_JumpIfError(hr, error, "pNodeList->get_length");
|
|
|
|
if (0 == lLength)
|
|
{
|
|
// not found anything
|
|
hr = AZ_HRESULT(ERROR_NOT_FOUND);
|
|
_JumpError(hr, error, "not found any node children");
|
|
}
|
|
|
|
// go through the list and retrieve guid attribute
|
|
for (i = 0; i < lLength; ++i)
|
|
{
|
|
hr = pNodeList->get_item(i, &pNode);
|
|
_JumpIfError(hr, error, "pNodeList->get_item");
|
|
|
|
AZASSERT(NULL != pNode);
|
|
|
|
// get guid attribute value
|
|
hr = myXmlGetNodeAttribute(
|
|
pNode,
|
|
pwszAtt,
|
|
&pwszXmlAttrValue);
|
|
_JumpIfError(hr, error, "myXmlGetNodeAttribute(guid)");
|
|
|
|
// see if match, use case sensitive comp
|
|
if (0 == _wcsicmp(pwszValue, pwszXmlAttrValue))
|
|
{
|
|
// ok, this is the node, return
|
|
*ppNode = pNode;
|
|
pNode = NULL;
|
|
break;
|
|
}
|
|
|
|
// free for next in loop
|
|
pNode->Release();
|
|
pNode = NULL;
|
|
AzpFreeHeap(pwszXmlAttrValue);
|
|
pwszXmlAttrValue = NULL;
|
|
}
|
|
|
|
if (NULL == *ppNode)
|
|
{
|
|
hr = AZ_HRESULT(ERROR_NOT_FOUND);
|
|
_JumpError(hr, error, "not found matched node");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
if (NULL != pwszXmlAttrValue)
|
|
{
|
|
AzpFreeHeap(pwszXmlAttrValue);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlGetNodeListByXPath(
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
IN WCHAR const *pwszQuery,
|
|
OUT IXMLDOMNodeList **ppList)
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrQuery = NULL;
|
|
BSTR bstrSelectionLanguage = NULL;
|
|
BOOL fChangedSL = FALSE;
|
|
IXMLDOMNodeList *pList = NULL;
|
|
|
|
VARIANT varDefSelectionLanguage;
|
|
VARIANT varSelectionLanguage;
|
|
VARIANT varXPath;
|
|
|
|
AZASSERT(NULL != pDoc &&
|
|
NULL != pwszQuery &&
|
|
0x0 != pwszQuery[0] &&
|
|
NULL != ppList);
|
|
|
|
//init
|
|
VariantInit(&varDefSelectionLanguage);
|
|
VariantInit(&varSelectionLanguage);
|
|
VariantInit(&varXPath);
|
|
|
|
// convert query to bstr
|
|
bstrQuery = SysAllocString(pwszQuery);
|
|
_JumpIfOutOfMemory(&hr, error, bstrQuery, "SysAllocString");
|
|
|
|
bstrSelectionLanguage = SysAllocString(AZ_XML_SELECTION_LANGUAGE);
|
|
_JumpIfOutOfMemory(&hr, error, bstrSelectionLanguage, "SysAllocString");
|
|
|
|
// get default selection language
|
|
hr = pDoc->getProperty(
|
|
bstrSelectionLanguage,
|
|
&varDefSelectionLanguage);
|
|
_JumpIfError(hr, error, "pDoc->getProperty(SelectionLanguage)");
|
|
AzPrint((AZD_XML, "default SelectionLanguage=%ws\n", V_BSTR(&varDefSelectionLanguage)));
|
|
|
|
hr = myWszToBstrVariant(
|
|
AZ_XML_XPATH,
|
|
&varXPath);
|
|
_JumpIfError(hr, error, "myWszToBstrVariant");
|
|
|
|
// change selection language to xpath
|
|
hr = pDoc->setProperty(
|
|
bstrSelectionLanguage,
|
|
varXPath);
|
|
_JumpIfError(hr, error, "pDoc->setProperty(SelectionLanguage)");
|
|
fChangedSL = TRUE;
|
|
|
|
// now looking for all nodes that match the xpath pattern
|
|
hr = pDoc->selectNodes(
|
|
bstrQuery,
|
|
&pList);
|
|
_JumpIfError(hr, error, "pDoc->selectNodes");
|
|
|
|
AZASSERT(NULL != pList);
|
|
|
|
//return
|
|
*ppList = pList;
|
|
pList = NULL;
|
|
|
|
hr = S_OK;
|
|
error:
|
|
// keep the following as the 1st
|
|
if (fChangedSL)
|
|
{
|
|
HRESULT hr2;
|
|
// restore the original Selection Language
|
|
hr2 = pDoc->setProperty(
|
|
bstrSelectionLanguage,
|
|
varDefSelectionLanguage);
|
|
_PrintIfError(hr2, "pDoc->getProperty(SelectionLanguage)");
|
|
}
|
|
|
|
if (NULL != bstrQuery)
|
|
{
|
|
SysFreeString(bstrQuery);
|
|
}
|
|
if (NULL != bstrSelectionLanguage)
|
|
{
|
|
SysFreeString(bstrSelectionLanguage);
|
|
}
|
|
if (NULL != pList)
|
|
{
|
|
pList->Release();
|
|
}
|
|
|
|
VariantClear(&varDefSelectionLanguage);
|
|
VariantClear(&varSelectionLanguage);
|
|
VariantClear(&varXPath);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlRemoveAllLinks(
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
IN WCHAR const *pwszLinkTag,
|
|
IN GUID *pGuid)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszGuid = NULL;
|
|
WCHAR *pwszQuery = NULL;
|
|
IXMLDOMNodeList *pList = NULL;
|
|
IXMLDOMNode *pNode = NULL;
|
|
IXMLDOMNode *pParentNode = NULL;
|
|
LONG lLength;
|
|
LONG i;
|
|
|
|
AZASSERT(NULL != pGuid &&
|
|
NULL != pDoc &&
|
|
NULL != pwszLinkTag);
|
|
|
|
//init
|
|
|
|
// convert guid to string guid to form xpath query
|
|
hr = UuidToString(pGuid, &pwszGuid);
|
|
_JumpIfError(hr, error, "UuidToString");
|
|
|
|
// query is in a pattern of //LinkTag[.="guid"]
|
|
// for example, query OperationLink,
|
|
// //OperationLink[.="25a63179-3663-4aa7-b0b8-48ca084d0747"]
|
|
// in which "//[.=""]" has 8 chars
|
|
pwszQuery = (WCHAR*)AzpAllocateHeap(
|
|
(wcslen(pwszLinkTag) + wcslen(pwszGuid) + 9) * sizeof(WCHAR), "XMQRY" );
|
|
|
|
_JumpIfOutOfMemory(&hr, error, pwszQuery, "AzpAllocateHeap");
|
|
|
|
wsprintf(pwszQuery, L"//%s[.=\"%s\"]", pwszLinkTag, pwszGuid);
|
|
AzPrint((AZD_XML, "XPath query(link delete)=%ws\n", pwszQuery));
|
|
|
|
hr = myXmlGetNodeListByXPath(
|
|
pDoc,
|
|
pwszQuery,
|
|
&pList);
|
|
_JumpIfError(hr, error, "myXmlGetNodeListByXPath");
|
|
|
|
// get length of the list
|
|
hr = pList->get_length(&lLength);
|
|
_JumpIfError(hr, error, "pList->get_length");
|
|
|
|
if (0 < lLength)
|
|
{
|
|
// delete all of them because linked node is gone
|
|
for (i = 0; i < lLength; ++i)
|
|
{
|
|
hr = pList->get_item(i, &pNode);
|
|
_JumpIfError(hr, error, "pList->get_item");
|
|
|
|
AZASSERT(NULL != pNode);
|
|
|
|
// get parent
|
|
hr = pNode->get_parentNode(&pParentNode);
|
|
_JumpIfError(hr, error, "pNode->get_ParentNode");
|
|
|
|
AZASSERT(NULL != pParentNode);
|
|
|
|
// now delete the node
|
|
hr = pParentNode->removeChild(
|
|
pNode,
|
|
NULL); //no return needed
|
|
_JumpIfError(hr, error, "pParentNode->removeChild");
|
|
|
|
// free for the next
|
|
pNode->Release();
|
|
pNode = NULL;
|
|
pParentNode->Release();
|
|
pParentNode = NULL;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pwszGuid)
|
|
{
|
|
RpcStringFree(&pwszGuid);
|
|
}
|
|
if (NULL != pwszQuery)
|
|
{
|
|
AzpFreeHeap(pwszQuery);
|
|
}
|
|
if (NULL != pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
if (NULL != pParentNode)
|
|
{
|
|
pParentNode->Release();
|
|
}
|
|
if (NULL != pList)
|
|
{
|
|
pList->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlDeleteElement(
|
|
IN IXMLDOMNode *pParentNode,
|
|
IN WCHAR const *pwszElementTag,
|
|
IN WCHAR const *pwszElementData)
|
|
/*
|
|
Description:
|
|
delete a element from the node
|
|
Argument:
|
|
pDoc - doc root handle
|
|
pParentNode - node handle of the parent of the element
|
|
pwszElementTag - element tag
|
|
pwszElementData - element data
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrElementTag = NULL;
|
|
BSTR bstrElementData = NULL;
|
|
IXMLDOMNodeList *pNodeList = NULL;
|
|
IXMLDOMNode *pNode = NULL;
|
|
LONG lLength;
|
|
LONG i;
|
|
|
|
|
|
// let's find the element first
|
|
|
|
// convert to bstr
|
|
bstrElementTag= SysAllocString(pwszElementTag);
|
|
_JumpIfOutOfMemory(&hr, error, bstrElementTag, "SysAllocString");
|
|
|
|
// get all matched nodes, should be just one though
|
|
hr = pParentNode->selectNodes(
|
|
bstrElementTag,
|
|
&pNodeList);
|
|
_JumpIfError(hr, error, "pParentNode->selectNodes");
|
|
|
|
// get length
|
|
hr = pNodeList->get_length(&lLength);
|
|
_JumpIfError(hr, error, "pNodeList->get_length");
|
|
|
|
for (i = 0; i < lLength; ++i)
|
|
{
|
|
// get the node handle
|
|
hr = pNodeList->get_item(i, &pNode);
|
|
_JumpIfError(hr, error, "pNodeList->get_item");
|
|
|
|
// get element text
|
|
hr = pNode->get_text(&bstrElementData);
|
|
_JumpIfError(hr, error, "pNode->get_text");
|
|
|
|
// use case insensitive compare because
|
|
// all links are guid or sid, hex number is case insesitive
|
|
if (0 == _wcsicmp(pwszElementData, bstrElementData))
|
|
{
|
|
// found it
|
|
hr = pParentNode->removeChild(pNode, NULL);
|
|
_JumpIfError(hr, error, "pParentNode->removeChild");
|
|
//done, out for loop
|
|
goto done;
|
|
}
|
|
// free for next
|
|
pNode->Release();
|
|
pNode = NULL;
|
|
SysFreeString(bstrElementData);
|
|
bstrElementData = NULL;
|
|
}
|
|
|
|
// if not found, error?
|
|
AzPrint((AZD_XML, "Not found %s under tag %ws\n", pwszElementData));
|
|
|
|
done:
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pNodeList)
|
|
{
|
|
pNodeList->Release();
|
|
}
|
|
if (NULL != pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
if (NULL != bstrElementTag)
|
|
{
|
|
SysFreeString(bstrElementTag);
|
|
}
|
|
if (NULL != bstrElementData)
|
|
{
|
|
SysFreeString(bstrElementData);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlAddElement(
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
IN IXMLDOMNode *pParentNode,
|
|
IN WCHAR const *pwszElementTag,
|
|
IN WCHAR const *pwszElementData)
|
|
/*
|
|
Description:
|
|
add a new element and set data
|
|
Argument:
|
|
pDoc - doc root handle
|
|
pParentNode - node handle of the parent of the element
|
|
pwszElementTag - element tag
|
|
pwszElementData - element data
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
IXMLDOMNode *pDocElementNode = NULL;
|
|
BSTR bstrElementTag = NULL;
|
|
BSTR bstrElementData = NULL;
|
|
|
|
// convert tag to bstr
|
|
bstrElementTag = SysAllocString(pwszElementTag);
|
|
_JumpIfOutOfMemory(&hr, error, bstrElementTag, "SysAllocString");
|
|
|
|
// create new empty element node
|
|
hr = pDoc->createElement(
|
|
bstrElementTag,
|
|
(IXMLDOMElement**)&pDocElementNode);
|
|
_JumpIfError(hr, error, "pDoc->createElement");
|
|
|
|
AZASSERT(NULL != pDocElementNode);
|
|
|
|
// convert element value to bstr
|
|
bstrElementData = SysAllocString(pwszElementData);
|
|
_JumpIfOutOfMemory(&hr, error, bstrElementData, "SysAllocString");
|
|
|
|
// now let's set member value
|
|
hr = pDocElementNode->put_text(bstrElementData);
|
|
_JumpIfError(hr, error, "pDocElementNode->put_text");
|
|
|
|
// attach to the right parent
|
|
hr = pParentNode->appendChild(
|
|
pDocElementNode,
|
|
NULL); //don't need return
|
|
_JumpIfError(hr, error, "pParentNode->appendChild");
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pDocElementNode)
|
|
{
|
|
pDocElementNode->Release();
|
|
}
|
|
if (NULL != bstrElementTag)
|
|
{
|
|
SysFreeString(bstrElementTag);
|
|
}
|
|
if (NULL != bstrElementData)
|
|
{
|
|
SysFreeString(bstrElementData);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlUpdateSingleElement(
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
IN IXMLDOMNode *pParentNode,
|
|
IN WCHAR const *pwszElementTag,
|
|
IN WCHAR const *pwszElementData)
|
|
/*
|
|
Description:
|
|
if named element tag exists, update with new data
|
|
otherwise create a new element with the data
|
|
Arguments:
|
|
pDoc - doc root handle
|
|
pParentNode - node handle of the parent of the element
|
|
pwszElementTag - element tag
|
|
pwszElementData - element data
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrElementTag = NULL;
|
|
BSTR bstrElementData = NULL;
|
|
IXMLDOMNodeList *pNodeList = NULL;
|
|
IXMLDOMNode *pNode = NULL;
|
|
LONG lLength;
|
|
|
|
AZASSERT(NULL != pDoc &&
|
|
NULL != pParentNode &&
|
|
NULL != pwszElementTag &&
|
|
NULL != pwszElementData);
|
|
|
|
// let's find the element first
|
|
|
|
// convert to bstr
|
|
bstrElementTag= SysAllocString(pwszElementTag);
|
|
_JumpIfOutOfMemory(&hr, error, bstrElementTag, "SysAllocString");
|
|
|
|
// get all matched nodes, should be just one though
|
|
hr = pParentNode->selectNodes(
|
|
bstrElementTag,
|
|
&pNodeList);
|
|
_JumpIfError(hr, error, "pParentNode->selectNodes");
|
|
|
|
AZASSERT(NULL != pNodeList);
|
|
|
|
// get length
|
|
hr = pNodeList->get_length(&lLength);
|
|
_JumpIfError(hr, error, "pNodeList->get_length");
|
|
|
|
if (1 < lLength)
|
|
{
|
|
// should be single occurence
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "invalid mutliple occurence");
|
|
}
|
|
|
|
if (1 == lLength)
|
|
{
|
|
// get node to do update
|
|
hr = pNodeList->get_item(0, &pNode);
|
|
_JumpIfError(hr, error, "pNodeList->get_item");
|
|
|
|
if (_wcsicmp(pwszElementTag, AZ_XML_TAG_BIZRULE) == 0 &&
|
|
(pwszElementData == NULL || wcslen(pwszElementData) == 0) )
|
|
{
|
|
//
|
|
// For BizRule, an empty string means that we will remove
|
|
// this element. Leaving this element will mean that this is a
|
|
// blank BizRule, which is quite different as no BizRule
|
|
//
|
|
|
|
hr = pParentNode->removeChild(pNode, NULL);
|
|
}
|
|
else
|
|
{
|
|
// convert to bstr
|
|
bstrElementData= SysAllocString(pwszElementData);
|
|
_JumpIfOutOfMemory(&hr, error, bstrElementData, "SysAllocString");
|
|
|
|
hr = pNode->put_text(bstrElementData);
|
|
_JumpIfError(hr, error, "pNode->put_text");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//must not existing
|
|
AZASSERT(0 == lLength);
|
|
|
|
hr = myXmlAddElement(
|
|
pDoc,
|
|
pParentNode,
|
|
pwszElementTag,
|
|
pwszElementData);
|
|
_JumpIfError(hr, error, "myXmlAddElement");
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pNodeList)
|
|
{
|
|
pNodeList->Release();
|
|
}
|
|
if (NULL != pNode)
|
|
{
|
|
pNode->Release();
|
|
}
|
|
if (NULL != bstrElementTag)
|
|
{
|
|
SysFreeString(bstrElementTag);
|
|
}
|
|
if (NULL != bstrElementData)
|
|
{
|
|
SysFreeString(bstrElementData);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#if DBG
|
|
void myDbgPrintDeltaEntry (PAZP_DELTA_ENTRY DeltaEntry)
|
|
{
|
|
AzPrint((AZD_XML, "DeltaFlags=0x%x\n", DeltaEntry->DeltaFlags));
|
|
if ((DeltaEntry->DeltaFlags & AZP_DELTA_SID) == 0)
|
|
{
|
|
AzpDumpGuid(AZD_XML, &DeltaEntry->Guid );
|
|
AzPrint(( AZD_XML, "\n" ));
|
|
}
|
|
else
|
|
{
|
|
char *pszSid = NULL;
|
|
if (ConvertSidToStringSidA(DeltaEntry->Sid, &pszSid))
|
|
{
|
|
AzPrint((AZD_XML, "Sid=%s\n", pszSid));
|
|
LocalFree(pszSid);
|
|
}
|
|
}
|
|
}
|
|
#endif //DBG
|
|
|
|
|
|
HRESULT
|
|
myXmlSubmitObjectElements(
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
IN ULONG lPersistFlags,
|
|
IN IXMLDOMNode *pParentNode,
|
|
IN WCHAR const *pwszElementTag,
|
|
IN ENUM_AZ_DATATYPE DataType,
|
|
IN AZPE_OBJECT_HANDLE hObject,
|
|
IN ULONG lPropertyId)
|
|
/*
|
|
Description:
|
|
update all element data under a node
|
|
all element data are retrieved from az object GetProperty method
|
|
Arguments:
|
|
pDoc - root node for creation
|
|
lPersistFlags - flags from the persist engine
|
|
pParentNode - az object node under which the elements are updated
|
|
DataType - ENUM_AZ_ data type, it handles long, str, str/sid array
|
|
hObject - az object handle
|
|
lPropertyId - property id for GetProperty
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwErr;
|
|
|
|
PVOID pElements = NULL;
|
|
WCHAR wszId[20];
|
|
BOOL fSid = FALSE;
|
|
BOOL fGuid = FALSE;
|
|
BOOL fList = FALSE;
|
|
ULONG i;
|
|
PAZP_DELTA_ENTRY DeltaEntry;
|
|
WCHAR *pwszElementData;
|
|
WCHAR *pwszSid = NULL;
|
|
WCHAR *pwszGuid = NULL;
|
|
|
|
ULONG DeltaArrayCount;
|
|
PAZP_DELTA_ENTRY *DeltaArray;
|
|
|
|
|
|
AZASSERT(NULL != pDoc &&
|
|
NULL != pParentNode &&
|
|
NULL != pwszElementTag &&
|
|
NULL != hObject);
|
|
|
|
//init
|
|
|
|
|
|
// handle different type of elements
|
|
|
|
switch (DataType)
|
|
{
|
|
case ENUM_AZ_BSTR:
|
|
dwErr = XmlAzrolesInfo->AzpeGetProperty(
|
|
hObject,
|
|
lPersistFlags,
|
|
lPropertyId,
|
|
&pElements);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeGetProperty");
|
|
|
|
AZASSERT(NULL != pElements);
|
|
|
|
hr = myXmlUpdateSingleElement(
|
|
pDoc,
|
|
pParentNode,
|
|
pwszElementTag,
|
|
(WCHAR*)pElements);
|
|
_JumpIfError(hr, error, "myXmlUpdateSingleElement(ENUM_AZ_BSTR)");
|
|
break;
|
|
|
|
case ENUM_AZ_LONG:
|
|
dwErr = XmlAzrolesInfo->AzpeGetProperty(
|
|
hObject,
|
|
lPersistFlags,
|
|
lPropertyId,
|
|
&pElements);
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeGetProperty");
|
|
|
|
AZASSERT(NULL != pElements);
|
|
|
|
wsprintf(wszId, L"%d", *((LONG*)pElements));
|
|
hr = myXmlUpdateSingleElement(
|
|
pDoc,
|
|
pParentNode,
|
|
pwszElementTag,
|
|
wszId);
|
|
_JumpIfError(hr, error, "myXmlUpdateSingleElement(ENUM_AZ_LONG)");
|
|
break;
|
|
|
|
case ENUM_AZ_SID_ARRAY:
|
|
fSid = TRUE;
|
|
fList = TRUE;
|
|
break;
|
|
|
|
case ENUM_AZ_GUID_ARRAY:
|
|
fGuid = TRUE;
|
|
fList = TRUE;
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "internal error: invalid data type");
|
|
}
|
|
|
|
if (fList)
|
|
{
|
|
|
|
dwErr = XmlAzrolesInfo->AzpeGetDeltaArray(
|
|
hObject,
|
|
lPropertyId,
|
|
&DeltaArrayCount,
|
|
&DeltaArray );
|
|
_JumpIfWinError(dwErr, &hr, error, "AzpeGetDeltaArray");
|
|
|
|
for (i = 0; i < DeltaArrayCount; ++i)
|
|
{
|
|
// get delta entry
|
|
DeltaEntry = DeltaArray[i];
|
|
|
|
#if DBG
|
|
myDbgPrintDeltaEntry(DeltaEntry);
|
|
#endif //DBG
|
|
|
|
// convert linked object id to string
|
|
if (fSid)
|
|
{
|
|
AZASSERT(0x0 != (AZP_DELTA_SID & DeltaEntry->DeltaFlags));
|
|
|
|
// convert sid to string sid
|
|
if (!ConvertSidToStringSid(
|
|
DeltaEntry->Sid,
|
|
&pwszSid))
|
|
{
|
|
AZ_HRESULT_LASTERROR(&hr);
|
|
_JumpError(hr, error, "ConvertSidToStringSid");
|
|
}
|
|
|
|
AZASSERT(NULL != pwszSid);
|
|
|
|
pwszElementData = pwszSid;
|
|
}
|
|
else
|
|
{
|
|
//must be guid
|
|
AZASSERT(fGuid);
|
|
|
|
hr = UuidToString(
|
|
&DeltaEntry->Guid,
|
|
&pwszGuid);
|
|
_JumpIfError(hr, error, "UuidToString");
|
|
|
|
AZASSERT(NULL != pwszGuid);
|
|
|
|
pwszElementData = pwszGuid;
|
|
}
|
|
|
|
if (0x0 != (AZP_DELTA_ADD & DeltaEntry->DeltaFlags))
|
|
{
|
|
// this is add case
|
|
|
|
hr = myXmlAddElement(
|
|
pDoc,
|
|
pParentNode,
|
|
pwszElementTag,
|
|
pwszElementData);
|
|
_JumpIfError(hr, error, "myXmlAddElement");
|
|
}
|
|
else
|
|
{
|
|
// this is delete case
|
|
|
|
hr = myXmlDeleteElement(
|
|
pParentNode,
|
|
pwszElementTag,
|
|
pwszElementData);
|
|
_JumpIfError(hr, error, "myXmlDeleteElement");
|
|
|
|
}
|
|
// free for next in loop
|
|
if (fSid)
|
|
{
|
|
AZASSERT(NULL != pwszSid);
|
|
LocalFree(pwszSid);
|
|
pwszSid = NULL;
|
|
}
|
|
else if (fGuid)
|
|
{
|
|
AZASSERT(NULL != pwszGuid);
|
|
RpcStringFree(&pwszGuid);
|
|
pwszGuid = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pElements)
|
|
{
|
|
XmlAzrolesInfo->AzpeFreeMemory(pElements);
|
|
}
|
|
if (NULL != pwszSid)
|
|
{
|
|
LocalFree(pwszSid);
|
|
}
|
|
if (NULL != pwszGuid)
|
|
{
|
|
RpcStringFree(&pwszGuid);
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlFindChildNodeByGuid(
|
|
IN IXMLDOMNode *pParentNode,
|
|
IN WCHAR const *pwszChildTag,
|
|
IN GUID *pGuid,
|
|
OUT IXMLDOMNode **ppChildNode)
|
|
/*
|
|
find a child node by matched guid attribute
|
|
IN: pParentNode, parent node from which to do searching
|
|
IN: pwszChildTag, node tag for searching
|
|
IN: pGuid, guid for attribute matching
|
|
OUT: ppChildNode, return node
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrChildTag = NULL;
|
|
IXMLDOMNode *pChildNode = NULL;
|
|
IXMLDOMNodeList *pChildList = NULL;
|
|
|
|
PWSTR pwszGuid = NULL;
|
|
|
|
AZASSERT(NULL != pParentNode &&
|
|
NULL != pwszChildTag &&
|
|
NULL != pGuid &&
|
|
NULL != ppChildNode);
|
|
|
|
// create child tag in bstr any way
|
|
bstrChildTag = SysAllocString(pwszChildTag);
|
|
_JumpIfOutOfMemory(&hr, error, bstrChildTag, "SysAllocString");
|
|
|
|
// get list of all children
|
|
hr = pParentNode->selectNodes(
|
|
bstrChildTag,
|
|
&pChildList);
|
|
_JumpIfError(hr, error, "pParentNode->selectNodes");
|
|
|
|
AZASSERT(NULL != pChildList);
|
|
|
|
// ok, find the child node by guid
|
|
|
|
hr = UuidToString(pGuid, &pwszGuid);
|
|
_JumpIfError(hr, error, "UuidToString");
|
|
|
|
hr = myXmlGetNodeByAttribute(
|
|
AZ_XML_TAG_ATTR_GUID,
|
|
pwszGuid,
|
|
pChildList,
|
|
&pChildNode);
|
|
_JumpIfError(hr, error, "myXmlGetNamedGuidNode");
|
|
|
|
*ppChildNode = pChildNode;
|
|
pChildNode = NULL;
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != bstrChildTag)
|
|
{
|
|
SysFreeString(bstrChildTag);
|
|
}
|
|
if (NULL != pChildList)
|
|
{
|
|
pChildList->Release();
|
|
}
|
|
if (NULL != pChildNode)
|
|
{
|
|
pChildNode->Release();
|
|
}
|
|
|
|
if (NULL != pwszGuid)
|
|
{
|
|
RpcStringFree(&pwszGuid);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myXmlDetermineParentNode(
|
|
IN IXMLDOMNode *pAzStoreNode,
|
|
IN ULONG lObjectType,
|
|
IN OPTIONAL GUID *pApplicationGuid,
|
|
IN OPTIONAL GUID *pScopeGuid,
|
|
OUT IXMLDOMNode **ppParentNode)
|
|
/*
|
|
Description:
|
|
determines the parent node of the current object.
|
|
if both pApplicationGuid and pScopeGuid are NULL,
|
|
the parent node is pAzStoreNode.
|
|
if pApplicationGuid != NULL and pScopeGuid == NULL,
|
|
the parent node is application queried by the guid.
|
|
if pApplicationGuid != NULL and pScopeGuid != NULL,
|
|
the parent node is scope queried by the both guids.
|
|
Arguments:
|
|
pAzStoreNode - top parent node
|
|
lObjectType - object type flag
|
|
pApplicationGuid - the application guid under which the object lives
|
|
pScopeGuid - the scope guid under which the object lives
|
|
Return:
|
|
ppParentNode - returned parent node, freed by Release().
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
IXMLDOMNode *pApplicationNode = NULL;
|
|
IXMLDOMNode *pScopeNode = NULL;
|
|
|
|
AZASSERT(NULL != pAzStoreNode &&
|
|
NULL != ppParentNode);
|
|
|
|
// init, it can be NULL if parent is AzAuthStore
|
|
*ppParentNode = NULL;
|
|
|
|
// determine parent
|
|
|
|
if (NULL != pApplicationGuid &&
|
|
OBJECT_TYPE_APPLICATION != lObjectType)
|
|
{
|
|
hr = myXmlFindChildNodeByGuid(
|
|
pAzStoreNode,
|
|
AZ_XML_TAG_APPLICATION,
|
|
pApplicationGuid,
|
|
&pApplicationNode);
|
|
_JumpIfError(hr ,error, "myXmlFindChildNodeByGuid");
|
|
|
|
AZASSERT(NULL != pApplicationNode);
|
|
|
|
if (NULL == pScopeGuid || OBJECT_TYPE_SCOPE == lObjectType)
|
|
{
|
|
// parent must be application
|
|
*ppParentNode = pApplicationNode;
|
|
// for return
|
|
pApplicationNode = NULL;
|
|
}
|
|
else
|
|
{
|
|
// should be scope
|
|
hr = myXmlFindChildNodeByGuid(
|
|
pApplicationNode,
|
|
AZ_XML_TAG_SCOPE,
|
|
pScopeGuid,
|
|
&pScopeNode);
|
|
_JumpIfError(hr ,error, "myXmlFindChildNodeByGuid");
|
|
|
|
// oh, change parent to scope
|
|
*ppParentNode = pScopeNode;
|
|
// for return
|
|
pScopeNode = NULL;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pApplicationNode)
|
|
{
|
|
pApplicationNode->Release();
|
|
}
|
|
if (NULL != pScopeNode)
|
|
{
|
|
pScopeNode->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
myXmlSubmitObject(
|
|
IN IXMLDOMDocument2 *pDoc,
|
|
IN ULONG lPersistFlags,
|
|
IN IXMLDOMNode *pAzStoreNode,
|
|
IN GUID *pApplicationGuid,
|
|
IN GUID *pScopeGuid,
|
|
IN AZPE_OBJECT_HANDLE hObject,
|
|
IN ULONG lObjectType,
|
|
IN BOOL fDelete)
|
|
/*
|
|
Description:
|
|
submit a group object into persist store
|
|
Arguments:
|
|
pDoc - root of the doc for any creation
|
|
lPersistFlags - flags from the persist engine
|
|
pAzStoreNode - AzAuthStore node handle
|
|
pApplicationGuid - application guid attribute,
|
|
if NULL, the object is under AzAuthStore. if !NULL and pScopeGuid is NULL
|
|
the object is under application
|
|
pScopeGuid - scope guid attribute, if !NULL and pApplicationGuid !NULL,
|
|
the object is under scope
|
|
hObject - az object handle
|
|
lObjectType - object index in the submit table
|
|
fDelete - TRUE will delete the object
|
|
Return:
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
IXMLDOMNode *pParentTemp = NULL;
|
|
IXMLDOMNode *pParentNode;
|
|
BSTR bstrObjectTag = NULL;
|
|
IXMLDOMNode *pObjectNode = NULL;
|
|
IXMLDOMNodeList *pObjectList = NULL;
|
|
IXMLDOMNode *pDocObjectNode = NULL;
|
|
WCHAR *pwszGuid = NULL;
|
|
BOOL fNewObject = FALSE;
|
|
|
|
PWSTR pwszName = NULL;
|
|
|
|
AZASSERT(NULL != pDoc &&
|
|
NULL != pAzStoreNode &&
|
|
NULL != hObject);
|
|
|
|
//init to AzAuthStore node, it may be changed from myXmlDetermineParentNode
|
|
pParentNode = pAzStoreNode;
|
|
|
|
if (OBJECT_TYPE_AZAUTHSTORE == lObjectType)
|
|
{
|
|
// AzAuthStore object
|
|
pObjectNode = pAzStoreNode;
|
|
}
|
|
else
|
|
{
|
|
// non-AzAuthStore object
|
|
// determine parent
|
|
hr = myXmlDetermineParentNode(
|
|
pAzStoreNode,
|
|
lObjectType,
|
|
pApplicationGuid,
|
|
pScopeGuid,
|
|
&pParentTemp);
|
|
_JumpIfError(hr, error, "myXmlDetermineParentNode");
|
|
|
|
// if pParentTemp == NULL, the parent is AzAuthStore
|
|
if (NULL != pParentTemp)
|
|
{
|
|
pParentNode = pParentTemp;
|
|
}
|
|
|
|
//
|
|
// handle delate or create
|
|
//
|
|
|
|
// object tag name must be in bstr
|
|
bstrObjectTag = SysAllocString(g_SubmitLoadTable[lObjectType].pwszTag);
|
|
_JumpIfOutOfMemory(&hr, error, bstrObjectTag, "SysAllocString");
|
|
|
|
// get the list of objects under parent
|
|
|
|
hr = pParentNode->selectNodes(
|
|
bstrObjectTag,
|
|
&pObjectList);
|
|
_JumpIfError(hr, error, "pParentNode->selectNodes");
|
|
|
|
// at this point we should have a list of objects under parent
|
|
AZASSERT(NULL != pObjectList);
|
|
|
|
//
|
|
// Get the GUID string
|
|
//
|
|
|
|
hr = UuidToString(XmlAzrolesInfo->AzpePersistenceGuid(hObject), &pwszGuid);
|
|
_JumpIfError(hr, error, "UuidToString");
|
|
|
|
// search the object by guid
|
|
hr = myXmlGetNodeByAttribute(
|
|
AZ_XML_TAG_ATTR_GUID,
|
|
pwszGuid,
|
|
pObjectList,
|
|
&pObjectNode);
|
|
if (S_OK != hr && AZ_HRESULT(ERROR_NOT_FOUND) != hr)
|
|
{
|
|
_JumpIfError(hr, error, "myXmlGetNamedGuidNode");
|
|
}
|
|
|
|
//
|
|
// if we can't find the node by GUID, we need to test if we can
|
|
// find the node by name. If we do, then that is a problem because
|
|
// to our clients, name is the only identity they know even though
|
|
// internally, we recognize objects by their guids.
|
|
//
|
|
|
|
if (AZ_HRESULT(ERROR_NOT_FOUND) == hr)
|
|
{
|
|
DWORD dwErr = XmlAzrolesInfo->AzpeGetProperty(hObject, lPersistFlags, AZ_PROP_NAME, (PVOID*)&pwszName);
|
|
if (dwErr != NO_ERROR)
|
|
{
|
|
_JumpIfWinError(dwErr, &hr, error, "myXmlGetNamedGuidNode");
|
|
}
|
|
|
|
//
|
|
// We must not be able to find this node by the name if
|
|
// there is no node matching by GUID
|
|
//
|
|
|
|
hr = myXmlGetNodeByAttribute(
|
|
AZ_XML_TAG_ATTR_NAME,
|
|
pwszName,
|
|
pObjectList,
|
|
&pObjectNode);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = AZ_HRESULT(ERROR_ALREADY_EXISTS);
|
|
_JumpIfError(hr, error, "myXmlGetNamedGuidNode");
|
|
}
|
|
}
|
|
|
|
|
|
AzPrint((AZD_XML, "hr = 0x%lx, pObjectNode(from myXmlGetNamedGuidNode) = 0x%lx\n", hr, pObjectNode));
|
|
AZASSERT((S_OK == hr && NULL != pObjectNode) ||
|
|
(AZ_HRESULT(ERROR_NOT_FOUND) == hr && NULL == pObjectNode));
|
|
|
|
if (fDelete)
|
|
{
|
|
if (NULL != pObjectNode)
|
|
{
|
|
hr = pParentNode->removeChild(
|
|
pObjectNode,
|
|
NULL); //remove
|
|
_JumpIfError(hr, error, "pParentNode->removeChild");
|
|
}
|
|
//else
|
|
//{
|
|
// try to delete an object not existing?
|
|
|
|
// this can be from
|
|
// this object was never submitted OR
|
|
// the store has not been refreshed and
|
|
// the object is deleted by another application
|
|
//}
|
|
|
|
if (NULL != g_SubmitLoadTable[lObjectType].rgpwszLkTag)
|
|
{
|
|
// this object can be referenced by other objects
|
|
// by removing this object, we should remove all references or links
|
|
|
|
WCHAR const * const *ppwszLinkTag;
|
|
|
|
for (ppwszLinkTag = g_SubmitLoadTable[lObjectType].rgpwszLkTag;
|
|
NULL != *ppwszLinkTag; ++ppwszLinkTag)
|
|
{
|
|
// remove link
|
|
hr = myXmlRemoveAllLinks(
|
|
pDoc,
|
|
*ppwszLinkTag,
|
|
XmlAzrolesInfo->AzpePersistenceGuid(hObject));
|
|
_JumpIfError(hr, error, "myXmlRemoveAllLinks");
|
|
}
|
|
|
|
}
|
|
// deletion, it is done
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
if (NULL == pObjectNode)
|
|
{
|
|
//
|
|
// looks the node doesn't exist, must be a new object
|
|
//
|
|
|
|
if ( !ObjectIsDirty( hObject, AZ_DIRTY_CREATE) )
|
|
{
|
|
hr = AZ_HR_FROM_WIN32(ERROR_NOT_FOUND);
|
|
_JumpError(hr, error, "Submitting changes to a non-existent object");
|
|
}
|
|
|
|
fNewObject = TRUE;
|
|
|
|
// let's create it
|
|
hr = pDoc->createElement(
|
|
bstrObjectTag,
|
|
(IXMLDOMElement**)&pDocObjectNode);
|
|
_JumpIfError(hr, error, "pDoc->createElement");
|
|
|
|
AZASSERT(NULL != pDocObjectNode);
|
|
|
|
// attach the new node to the right parent
|
|
hr = pParentNode->appendChild(
|
|
pDocObjectNode,
|
|
&pObjectNode);
|
|
_JumpIfError(hr, error, "pParentNode->appendChild");
|
|
|
|
AzPrint((AZD_XML, "A new element node, 0x%lx, is added for %s\n", pObjectNode, bstrObjectTag));
|
|
|
|
}
|
|
|
|
// at this point, we should have object node
|
|
AZASSERT(NULL != pObjectNode);
|
|
|
|
// let's update object common data
|
|
|
|
if (fNewObject)
|
|
{
|
|
// let's set object guid
|
|
|
|
// convert guid to string guid
|
|
hr = UuidToString(
|
|
XmlAzrolesInfo->AzpePersistenceGuid(hObject),
|
|
&pwszGuid);
|
|
_JumpIfError(hr, error, "UuidToString");
|
|
|
|
AZASSERT(NULL != pwszGuid);
|
|
|
|
hr = myXmlSetNodeAttribute(
|
|
pObjectNode,
|
|
AZ_XML_TAG_ATTR_GUID,
|
|
pwszGuid,
|
|
pDoc);
|
|
_JumpIfError(hr, error, "myXmlSetNodeAttribute");
|
|
}
|
|
|
|
if (ObjectIsDirty(hObject, AZ_DIRTY_NAME))
|
|
{
|
|
// submit name
|
|
hr = myXmlSubmitObjectAttribute(
|
|
hObject,
|
|
lPersistFlags,
|
|
AZ_PROP_NAME,
|
|
ENUM_AZ_BSTR,
|
|
pObjectNode,
|
|
AZ_XML_TAG_ATTR_NAME,
|
|
pDoc);
|
|
_JumpIfError(hr, error, "myXmlSubmitObjectAttribute(name)");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ObjectIsDirty( hObject, AZ_DIRTY_DESCRIPTION))
|
|
{
|
|
// every object has description
|
|
hr = myXmlSubmitObjectAttribute(
|
|
hObject,
|
|
lPersistFlags,
|
|
AZ_PROP_DESCRIPTION,
|
|
ENUM_AZ_BSTR,
|
|
pObjectNode,
|
|
AZ_XML_TAG_ATTR_DESCRIPTION,
|
|
pDoc);
|
|
_JumpIfError(hr, error, "myXmlSubmitObjectAttribute(description)");
|
|
}
|
|
|
|
//
|
|
// submit object attributes if has
|
|
//
|
|
if (NULL != g_SubmitLoadTable[lObjectType].rgpAttrs)
|
|
{
|
|
// submit object attributes
|
|
AZ_PROP_ENTRY *pAttrs;
|
|
for (pAttrs = g_SubmitLoadTable[lObjectType].rgpAttrs;
|
|
NULL != pAttrs[0].pwszTag; ++pAttrs)
|
|
{
|
|
if (ObjectIsDirty(hObject, pAttrs[0].lDirtyBit))
|
|
{
|
|
hr = myXmlSubmitObjectAttribute(
|
|
hObject,
|
|
lPersistFlags,
|
|
pAttrs[0].lPropId,
|
|
pAttrs[0].lDataType,
|
|
pObjectNode,
|
|
pAttrs[0].pwszTag,
|
|
pDoc);
|
|
_JumpIfError(hr, error, "myXmlSubmitObjectAttribute");
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// submit object elements if has
|
|
//
|
|
if (NULL != g_SubmitLoadTable[lObjectType].rgpEles)
|
|
{
|
|
// submit object elements
|
|
AZ_PROP_ENTRY *pEles;
|
|
for (pEles = g_SubmitLoadTable[lObjectType].rgpEles;
|
|
NULL != pEles[0].pwszTag; ++pEles)
|
|
{
|
|
if (ObjectIsDirty(hObject, pEles[0].lDirtyBit))
|
|
{
|
|
hr = myXmlSubmitObjectElements(
|
|
pDoc,
|
|
lPersistFlags,
|
|
pObjectNode,
|
|
pEles[0].pwszTag,
|
|
pEles[0].lDataType,
|
|
hObject,
|
|
pEles[0].lPropId);
|
|
_JumpIfError(hr, error, "myXmlSubmitObjectElements");
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
hr = S_OK;
|
|
error:
|
|
if (NULL != pParentTemp)
|
|
{
|
|
pParentTemp->Release();
|
|
}
|
|
if (NULL != bstrObjectTag)
|
|
{
|
|
SysFreeString(bstrObjectTag);
|
|
}
|
|
if (OBJECT_TYPE_AZAUTHSTORE != lObjectType &&
|
|
NULL != pObjectNode &&
|
|
pObjectNode != pAzStoreNode)
|
|
{
|
|
pObjectNode->Release();
|
|
|
|
AzPrint((AZD_XML, "pObjectNode, 0x%lx, is released\n", pObjectNode));
|
|
|
|
}
|
|
if (NULL != pObjectList)
|
|
{
|
|
pObjectList->Release();
|
|
}
|
|
if (NULL != pDocObjectNode)
|
|
{
|
|
pDocObjectNode->Release();
|
|
}
|
|
if (NULL != pwszGuid)
|
|
{
|
|
RpcStringFree(&pwszGuid);
|
|
}
|
|
if (NULL != pwszName)
|
|
{
|
|
AzFreeMemory(pwszName);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
DWORD
|
|
XMLPersistSubmit(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext,
|
|
IN AZPE_OBJECT_HANDLE pObj,
|
|
IN ULONG lPersistFlags,
|
|
IN BOOLEAN DeleteMe
|
|
)
|
|
/*
|
|
Description:
|
|
This routine submits changes made to the authz policy database.
|
|
|
|
If the object is being created, the GenericObject->PersistenceGuid field will be
|
|
zero on input. Upon successful creation, this routine will set PersistenceGuid to
|
|
non-zero. Upon failed creation, this routine will leave PersistenceGuid as zero.
|
|
|
|
On entry, AzAuthorizationStore->PersistCritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
PersistContext - Specifies the policy database that is to be manipulated
|
|
|
|
GenericObject - Specifies the object in the database that is to be updated
|
|
in the underlying store.
|
|
|
|
lPersistFlags - lPersistFlags from the persist engine describing the operation
|
|
AZPE_FLAGS_PERSIST_OPEN - Call is the original AzInitialize
|
|
AZPE_FLAGS_PERSIST_UPDATE_CACHE - Call is an AzUpdateCache
|
|
|
|
DeleteMe - TRUE if the object and all of its children are to be deleted.
|
|
FALSE if the object is to be updated.
|
|
Return Value:
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
*/
|
|
{
|
|
HRESULT hr;
|
|
AZPE_OBJECT_HANDLE pTempObj;
|
|
GUID *pAppGuid;
|
|
GUID *pScopeGuid;
|
|
IXMLDOMDocument2 *pDoc;
|
|
IXMLDOMElement *pAzStoreNode = NULL;
|
|
AZP_XML_CONTEXT *pPersistContext;
|
|
VARIANT varPolicyUrl;
|
|
ULONG lObjectType = XmlAzrolesInfo->AzpeObjectType(pObj);
|
|
|
|
::VariantInit(&varPolicyUrl);
|
|
|
|
AZASSERT(NULL != pObj );
|
|
|
|
// init
|
|
pPersistContext = (AZP_XML_CONTEXT*)PersistContext;
|
|
AZASSERT(NULL != pPersistContext);
|
|
pDoc = pPersistContext->pDoc;
|
|
|
|
//
|
|
// Handle deletion of the authorization store object
|
|
//
|
|
|
|
if ( lObjectType == OBJECT_TYPE_AZAUTHSTORE && DeleteMe ) {
|
|
|
|
hr = XMLPersistDelete( pPersistContext->pwszPolicyUrl );
|
|
_PrintIfError(hr, "XMLPersistDelete");
|
|
goto error;
|
|
}
|
|
|
|
// determine at what level the object is located
|
|
// by knowing pAppGuid and pScopeGuid, it can tell
|
|
// for example,
|
|
// if pAppGuid=NULL && pScopeGuid=NULL && ObjectType=Group
|
|
// it is the object under AzAuthorizationStore
|
|
// if pAppGuid!=NULL && pScopeGuid!=NULL && ObjectType=Group
|
|
// it is the object under Scope
|
|
|
|
// init
|
|
pAppGuid = NULL;
|
|
pScopeGuid = NULL;
|
|
|
|
// go from the object up to AzAuthStore level
|
|
pTempObj = pObj;
|
|
while (NULL != pTempObj)
|
|
{
|
|
ULONG TempObjectType = XmlAzrolesInfo->AzpeObjectType(pTempObj);
|
|
|
|
if (OBJECT_TYPE_AZAUTHSTORE == TempObjectType)
|
|
{
|
|
break;
|
|
}
|
|
else if (OBJECT_TYPE_APPLICATION == TempObjectType)
|
|
{
|
|
// point to application name
|
|
pAppGuid = XmlAzrolesInfo->AzpePersistenceGuid( pTempObj );
|
|
}
|
|
else if (OBJECT_TYPE_SCOPE == TempObjectType)
|
|
{
|
|
// point to scope name
|
|
pScopeGuid = XmlAzrolesInfo->AzpePersistenceGuid( pTempObj );
|
|
}
|
|
|
|
// point to the parent
|
|
|
|
pTempObj = XmlAzrolesInfo->AzpeParentOfChild( pTempObj );
|
|
}
|
|
|
|
// we will get root node any way
|
|
// get the root node, ie. the node of ploicy
|
|
hr = myXmlGetAzStoreNode(
|
|
pDoc,
|
|
&pAzStoreNode);
|
|
_JumpIfError(hr, error, "myXmlGetAzStoreNode");
|
|
|
|
AZASSERT(NULL != pAzStoreNode);
|
|
|
|
// let's check if the object has a guid
|
|
if (OBJECT_TYPE_AZAUTHSTORE != lObjectType)
|
|
{
|
|
if (IsEqualGUID( *XmlAzrolesInfo->AzpePersistenceGuid(pObj), AzGlZeroGuid))
|
|
{
|
|
hr = UuidCreate( XmlAzrolesInfo->AzpePersistenceGuid(pObj) );
|
|
_JumpIfError(hr, error, "UuidCreate");
|
|
}
|
|
}
|
|
|
|
// submit object changes to persist store
|
|
hr = myXmlSubmitObject(
|
|
pDoc, // doc node
|
|
lPersistFlags,
|
|
pAzStoreNode, //AzAuthStore node
|
|
pAppGuid,
|
|
pScopeGuid,
|
|
pObj, //object handle
|
|
lObjectType,
|
|
DeleteMe);
|
|
_JumpIfError(hr, error, "myXmlSubmitObject");
|
|
|
|
|
|
// if submit called from persist engine, it must be dirty
|
|
// persist whole xml doc
|
|
|
|
AZASSERT(NULL != pPersistContext);
|
|
|
|
hr = myWszToBstrVariant(
|
|
pPersistContext->pwszPolicyUrl,
|
|
&varPolicyUrl);
|
|
_JumpIfError(hr, error, "myWszToBstrVariant");
|
|
|
|
//
|
|
// We should not blindly use the current DOM to save.
|
|
// If there has been changes to the store, then we should bring in
|
|
// those changes to this DOM and merge them and then do the save
|
|
//
|
|
|
|
BOOL bNeedUpdate;
|
|
DWORD dwStatus = myXmlStoreHasUpdate(pPersistContext, &bNeedUpdate);
|
|
if (NO_ERROR == dwStatus && bNeedUpdate)
|
|
{
|
|
//
|
|
// load a fresh copy of dom, and then update this object's changes
|
|
//
|
|
|
|
CComPtr<IXMLDOMDocument2> srpDocNew;
|
|
hr = myXmlLoad(varPolicyUrl, &srpDocNew);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// put the same changes to the freshly loaded dom as well
|
|
//
|
|
|
|
CComPtr<IXMLDOMElement> srpStoreNode;
|
|
hr = srpDocNew->get_documentElement(&srpStoreNode);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = myXmlSubmitObject(
|
|
srpDocNew, // doc node
|
|
lPersistFlags,
|
|
srpStoreNode, //admin node
|
|
pAppGuid,
|
|
pScopeGuid,
|
|
pObj, //object handle
|
|
lObjectType,
|
|
DeleteMe
|
|
);
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = srpDocNew->save(varPolicyUrl);
|
|
}
|
|
}
|
|
else if (NO_ERROR == dwStatus)
|
|
{
|
|
hr = pDoc->save(varPolicyUrl);
|
|
}
|
|
else
|
|
{
|
|
hr = AZ_HR_FROM_WIN32(dwStatus);
|
|
}
|
|
|
|
// release resource 1st
|
|
VariantClear(&varPolicyUrl);
|
|
_JumpIfError(hr, error, "pDoc->save");
|
|
|
|
// if AzAuthStore, apply acl
|
|
// we do it here because the file might not exist
|
|
if ( lObjectType == OBJECT_TYPE_AZAUTHSTORE )
|
|
{
|
|
// submit any acl changes
|
|
hr = myXmlSubmitAzStoreAcls(
|
|
pPersistContext,
|
|
pObj,
|
|
lPersistFlags );
|
|
_JumpIfError(hr, error, "myXmlSubmitAzStoreAcls");
|
|
}
|
|
|
|
//
|
|
// We will poll the filetime here only if no outside changes
|
|
// have happened
|
|
//
|
|
|
|
if (!bNeedUpdate)
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA fad;
|
|
|
|
if (GetFileAttributesEx(pPersistContext->pwszPolicyUrl,
|
|
GetFileExInfoStandard,
|
|
&fad))
|
|
{
|
|
pPersistContext->FTLastWrite = fad.ftLastWriteTime;
|
|
}
|
|
else
|
|
{
|
|
hr = AZ_HR_FROM_WIN32(GetLastError());
|
|
_JumpIfError(hr, error, "GetFileAttributesEx");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tell azroles about the object options
|
|
//
|
|
|
|
hr = XmlSetObjectOptions( pPersistContext, lPersistFlags, pObj );
|
|
_JumpIfError(hr, error, "XmlSetObjectOptions");
|
|
|
|
hr = S_OK;
|
|
error:
|
|
VariantClear(&varPolicyUrl);
|
|
|
|
if (NULL != pAzStoreNode)
|
|
{
|
|
pAzStoreNode->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
XMLPersistRefresh(
|
|
IN AZPE_PERSIST_CONTEXT PersistContext,
|
|
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
|
|
IN ULONG lPersistFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the attributes of the object from the policy database.
|
|
|
|
On entry, AzGlResource must be locked exclusive.
|
|
|
|
Arguments:
|
|
|
|
PersistContext - Specifies the policy database that is to be manipulated
|
|
|
|
AzpeObjectHandle - Specifies the object in the database whose cache entry is to be
|
|
updated
|
|
The GenericObject->PersistenceGuid field should be non-zero on input.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
Other status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = NO_ERROR;
|
|
PAZP_XML_CONTEXT XmlPersistContext = (PAZP_XML_CONTEXT)PersistContext;
|
|
AZPE_OBJECT_HANDLE pParentObject;
|
|
ULONG lObjectType;
|
|
IXMLDOMNodeList *pList = NULL;
|
|
IXMLDOMNode *pObjectNode = NULL;
|
|
IXMLDOMDocument2 *pDoc;
|
|
WCHAR *pwszGuid = NULL;
|
|
WCHAR *pwszQuery = NULL;
|
|
|
|
ASSERT(NULL != AzpeObjectHandle);
|
|
|
|
//init
|
|
|
|
// get object type
|
|
lObjectType = XmlAzrolesInfo->AzpeObjectType( AzpeObjectHandle );
|
|
|
|
// get doc handle
|
|
pDoc = XmlPersistContext->pDoc;
|
|
|
|
// find the object node in the doc
|
|
|
|
if (OBJECT_TYPE_AZAUTHSTORE == lObjectType)
|
|
{
|
|
// AzAuthStore is always special, itself, no parent
|
|
// get object parent
|
|
pParentObject = AzpeObjectHandle;
|
|
|
|
// get the root node, ie. the node of ploicy
|
|
dwErr = myXmlGetAzStoreNode(pDoc, (IXMLDOMElement**)&pObjectNode);
|
|
_JumpIfError(dwErr, error, "myXmlGetAzStoreNode");
|
|
|
|
// if AzAuthStore object, refresh policy acl
|
|
dwErr = myXmlLoadAclsToAzStore(
|
|
XmlPersistContext,
|
|
lPersistFlags,
|
|
FALSE );
|
|
_JumpIfError(dwErr, error, "myXmlLoadAclsToAzStore");
|
|
|
|
}
|
|
else
|
|
{
|
|
if (IsEqualGUID( *XmlAzrolesInfo->AzpePersistenceGuid( AzpeObjectHandle ), AzGlZeroGuid))
|
|
{
|
|
// this is a new object, no need to refresh
|
|
goto done;
|
|
}
|
|
|
|
// get object parent
|
|
pParentObject = XmlAzrolesInfo->AzpeParentOfChild( AzpeObjectHandle );
|
|
ASSERT(NULL != pParentObject);
|
|
|
|
// convert object guid to string guid to form xpath query
|
|
dwErr = UuidToString( XmlAzrolesInfo->AzpePersistenceGuid( AzpeObjectHandle ), &pwszGuid);
|
|
_JumpIfError(dwErr, error, "UuidToString");
|
|
|
|
// get object node by using xpath query
|
|
// query is in a pattern of //*/$ObjectTag$[@Guid="???"]
|
|
// for example, query Operation,
|
|
// //*/AzOperation[@Guid="25a63179-3663-4aa7-b0b8-48ca084d0747"]
|
|
// in which "//*/[@=""] has 10 chars
|
|
|
|
ASSERT(NULL != g_SubmitLoadTable[lObjectType].pwszTag);
|
|
|
|
pwszQuery = (WCHAR*)AzpAllocateHeap(
|
|
(wcslen(g_SubmitLoadTable[lObjectType].pwszTag) +
|
|
wcslen(AZ_XML_TAG_ATTR_GUID) +
|
|
wcslen(pwszGuid) + 11) * sizeof(WCHAR), "XMQRY2" );
|
|
if (NULL == pwszQuery)
|
|
{
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
_JumpError(dwErr, error, "AzpAllocateHeap");
|
|
}
|
|
|
|
wsprintf(pwszQuery, L"//*/%s[@%s=\"%s\"]",
|
|
g_SubmitLoadTable[lObjectType].pwszTag,
|
|
AZ_XML_TAG_ATTR_GUID,
|
|
pwszGuid);
|
|
AzPrint((AZD_XML, "XPath query(refresh)=%ws\n", pwszQuery));
|
|
|
|
dwErr = myXmlGetNodeListByXPath(
|
|
pDoc,
|
|
pwszQuery,
|
|
&pList);
|
|
_JumpIfError(dwErr, error, "myXmlGetNodeListByXPath");
|
|
|
|
#if DBG
|
|
{
|
|
LONG lLength;
|
|
// get length of the list
|
|
dwErr = pList->get_length(&lLength);
|
|
_JumpIfError(dwErr, error, "pList->get_length");
|
|
|
|
// object guid can't be shared
|
|
AZASSERT(1 == lLength);
|
|
}
|
|
#endif //DBG
|
|
|
|
dwErr = pList->get_item(0, &pObjectNode);
|
|
_JumpIfError(dwErr, error, "pList->get_item");
|
|
}
|
|
|
|
ASSERT(NULL != pObjectNode);
|
|
|
|
// now do the refresh
|
|
dwErr = myXmlLoadObject(
|
|
XmlPersistContext,
|
|
pParentObject,
|
|
lObjectType,
|
|
lPersistFlags,
|
|
FALSE, //don't load child objects
|
|
TRUE, // AzpeFinishObject right after
|
|
pObjectNode);
|
|
_JumpIfError(dwErr, error, "myXmlLoadObject(refresh)");
|
|
|
|
done:
|
|
dwErr = NO_ERROR;
|
|
error:
|
|
if (NULL != pwszGuid)
|
|
{
|
|
RpcStringFree(&pwszGuid);
|
|
}
|
|
if (NULL != pwszQuery)
|
|
{
|
|
AzpFreeHeap(pwszQuery);
|
|
}
|
|
if (NULL != pList)
|
|
{
|
|
pList->Release();
|
|
}
|
|
if (NULL != pObjectNode)
|
|
{
|
|
pObjectNode->Release();
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
#pragma warning ( pop )
|