/*++ Copyright (C) Microsoft Corporation, 1991 - 1999 Module Name: binding.cxx Abstract: The implementation of the DCE binding class is contained in this file. Author: Michael Montague (mikemon) 04-Nov-1991 Revision History: Kamen Moutafov (KamenM) Dec 99 - Feb 2000 - Support for cell debugging stuff --*/ #include #include #include #include #include #include #include #include #include #include #include #include UUID MgmtIf = { 0xafa8bd80,0x7d8a,0x11c9, {0xbe,0xf4,0x08,0x00,0x2b,0x10,0x29,0x89} }; UUID NullUuid = { 0L, 0, 0, {0,0,0,0,0,0,0,0} }; int IsMgmtIfUuid( UUID PAPI * IfId ) { if (RpcpMemoryCompare(IfId, &MgmtIf, sizeof(UUID)) == 0) { return 1; } return 0; } RPC_CHAR * DuplicateString ( IN const RPC_CHAR PAPI * String ) /*++ Routine Description: When this routine is called, it will duplicate the string into a fresh string and return it. Arguments, either: String - Supplies the string to be duplicated. Return Value: The duplicated string is returned. If insufficient memory is available to allocate a fresh string, zero will be returned. --*/ { RPC_CHAR * FreshString, * FreshStringScan; const RPC_CHAR PAPI * StringScan; unsigned int Length; ASSERT(String); Length = 1; StringScan = String; while (*StringScan++ != 0) Length += 1; FreshString = new RPC_CHAR[Length]; if (FreshString == 0) return(0); for (FreshStringScan = FreshString, StringScan = String; *StringScan != 0; FreshStringScan++, StringScan++) { *FreshStringScan = *StringScan; } *FreshStringScan = *StringScan; return(FreshString); } PSID DuplicateSID ( IN const PSID Sid ) /*++ Routine Description: When this routine is called, it will duplicate the sid into a fresh sid and return it. Arguments, either: Sid - Supplies the sid to be duplicated. Return Value: The duplicated sid is returned. If insufficient memory is available to allocate a fresh sid, zero will be returned. --*/ { PSID NewSid; ULONG SidLength; BOOL Result; ASSERT(IsValidSid(Sid)); SidLength = GetLengthSid (Sid); NewSid = (PSID) new unsigned char [SidLength]; if (NewSid == NULL) return(NULL); Result = CopySid (SidLength, NewSid, Sid); // CopySid cannot fail unless we gave it invalid parameters ASSERT(Result); return NewSid; } RPC_STATUS RpcpLookupAccountNameDirect ( IN RPC_CHAR *ServerPrincipalName, OUT PSID *Sid ) /*++ Routine Description: Lookups a server principal name and translates it to a SID. Basically an RPC wrapper for LookupAccountName (with some memory management stuff thrown in). Arguments, either: ServerPrincipalName - the server principal name to be translated to a SID Sid - On output contains a pointer to the allocated SID. Undefined on failure. Pointer must be freed with delete. Return Value: RPC_S_OK or RPC_S_* error --*/ { int i; DWORD SizeofSID, DomainNameLen; SID_NAME_USE eUse; RPC_CHAR *pDomainName; PSID pSID; DWORD LastError; RPC_STATUS Status; SizeofSID = sizeof(SID)+10*sizeof(ULONG); DomainNameLen = 256; for (i = 0; i < 2; i++) { pSID = (PSID) new char[SizeofSID]; pDomainName = new RPC_CHAR[DomainNameLen]; if (pSID == 0 || pDomainName == 0) { delete [] pSID; delete [] pDomainName; return RPC_S_OUT_OF_MEMORY; } if (LookupAccountNameW ( NULL, ServerPrincipalName, pSID, &SizeofSID, pDomainName, &DomainNameLen, &eUse)) { break; } delete [] pSID; delete [] pDomainName; LastError = GetLastError(); if (LastError != ERROR_INSUFFICIENT_BUFFER) { switch (LastError) { case ERROR_NONE_MAPPED: Status = RPC_S_UNKNOWN_PRINCIPAL; break; case ERROR_OUTOFMEMORY: Status = RPC_S_OUT_OF_MEMORY; break; case ERROR_TRUSTED_RELATIONSHIP_FAILURE: Status = ERROR_TRUSTED_RELATIONSHIP_FAILURE; break; default: Status = RPC_S_ACCESS_DENIED; } RpcpErrorAddRecord(EEInfoGCRuntime, Status, EEInfoDLRpcpLookupAccountName10, LastError); return Status; } } delete [] pDomainName; ASSERT(i < 2); *Sid = pSID; return RPC_S_OK; } RPC_STATUS RpcpLookupAccountName ( IN RPC_CHAR *ServerPrincipalName, IN OUT BOOL *fCache, OUT PSID *Sid ) /*++ Routine Description: Lookups a server principal name and translates it to a SID. For performance reasons, we first look the account name up in our per process SIDCache, if its not present there, then we look it up and add it. Note: This function maps ERROR_TRUSTED_RELATIONSHIP_FAILURE to RPC_S_ACCESS_DENIED. If you need to receive ERROR_TRUSTED_RELATIONSHIP_FAILURE, then call RpcpLookupAccountNameDirect. Arguments, either: ServerPrincipalName - the server principal name to be translated to a SID fCache - On input: If true, then we will first try the cache, if false then we will look the name up directly and bypass the cache. On output: If true, then the SID was retrieved from the cache, if false, it was retrieved from a lookup. Undefined on failure. Sid - On output contains a pointer to the allocated SID. Undefined on failure. Pointer must be freed with delete. Return Value: RPC_S_OK or RPC_S_* error --*/ { RPC_STATUS Status; if (*fCache) { // Query the cache to see if we have looked up this account name already Status = QuerySIDCache(ServerPrincipalName, Sid); if (Status != RPC_S_OK) { return Status; } if (*Sid != NULL) { return RPC_S_OK; } } // The account name is not in our cache, we need to look it up Status = RpcpLookupAccountNameDirect(ServerPrincipalName, Sid); if (Status == ERROR_TRUSTED_RELATIONSHIP_FAILURE) { Status = RPC_S_ACCESS_DENIED; } if (Status == RPC_S_OK) { *fCache = FALSE; // Add this mapping to our cache (void) AddToSIDCache(ServerPrincipalName, *Sid); } return Status; } RPC_STATUS RpcpLookupAccountSid ( IN PSID Sid, OUT RPC_CHAR **ServerPrincipalName ) /*++ Routine Description: Lookups a SID and translates it to a server principal name. Basically an RPC wrapper for LookupAccountSid (with some memory management stuff thrown in). Arguments, either: Sid - the SID to be translated into a server principal name. ServerPrincipalName - on output, a pointer to the allocated server principal name. Undefined on failure. Return Value: RPC_S_OK or RPC_S_* error --*/ { int i; DWORD SPNLength, DomainNameLen; SID_NAME_USE eUse; RPC_CHAR *pDomainName, *pServerPrincipalName; DWORD LastError; RPC_STATUS Status; SPNLength = 256; DomainNameLen = 256; for (i = 0; i < 2; i++) { pServerPrincipalName = new RPC_CHAR[SPNLength]; pDomainName = new RPC_CHAR[DomainNameLen]; if (pServerPrincipalName == 0 || pDomainName == 0) { delete [] pServerPrincipalName; delete [] pDomainName; return RPC_S_OUT_OF_MEMORY; } if (LookupAccountSidW ( NULL, Sid, pServerPrincipalName, &SPNLength, pDomainName, &DomainNameLen, &eUse)) { break; } delete [] pServerPrincipalName; delete [] pDomainName; LastError = GetLastError(); if (LastError != ERROR_INSUFFICIENT_BUFFER) { switch (LastError) { case ERROR_NONE_MAPPED: Status = RPC_S_UNKNOWN_PRINCIPAL; break; case ERROR_OUTOFMEMORY: Status = RPC_S_OUT_OF_MEMORY; break; default: Status = RPC_S_ACCESS_DENIED; } RpcpErrorAddRecord(EEInfoGCRuntime, Status, EEInfoDLRpcpLookupAccountName10, LastError); return Status; } } delete [] pDomainName; ASSERT(i < 2); *ServerPrincipalName = pServerPrincipalName; return RPC_S_OK; } DCE_BINDING::DCE_BINDING ( IN RPC_CHAR PAPI * ObjectUuid OPTIONAL, IN RPC_CHAR PAPI * RpcProtocolSequence OPTIONAL, IN RPC_CHAR PAPI * NetworkAddress OPTIONAL, IN RPC_CHAR PAPI * Endpoint OPTIONAL, IN RPC_CHAR PAPI * Options OPTIONAL, OUT RPC_STATUS PAPI * Status ) /*++ Routine Description: The constructor creates a DCE_BINDING object based on the pieces of the string binding specified. Arguments: ObjectUuid - Optionally supplies the object uuid component of the binding. RpcProtocolSequence - Optionally supplies the rpc protocol sequence component of the binding. NetworkAddress - Optionally supplies the network address component of the binding. Endpoint - Optionally supplies the endpoint component of the binding. Options - Optionally supplies the network options component of the binding. Status - Returns the status of the operation. This argument will be set to one of the following values. RPC_S_OK - The operation completed successfully. RPC_S_INVALID_STRING_UUID - The specified object uuid does not contain the valid string representation of a uuid. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to complete the operation. --*/ { ALLOCATE_THIS(DCE_BINDING); *Status = RPC_S_OK; if ( ARGUMENT_PRESENT(ObjectUuid) && (ObjectUuid[0] != 0)) { if (this->ObjectUuid.ConvertFromString(ObjectUuid)) { *Status = RPC_S_INVALID_STRING_UUID; this->ObjectUuid.SetToNullUuid(); } } else this->ObjectUuid.SetToNullUuid(); if (ARGUMENT_PRESENT(RpcProtocolSequence)) { this->RpcProtocolSequence = DuplicateString(RpcProtocolSequence); if (this->RpcProtocolSequence == 0) *Status = RPC_S_OUT_OF_MEMORY; } else this->RpcProtocolSequence = 0; if (ARGUMENT_PRESENT(NetworkAddress)) { this->NetworkAddress = DuplicateString(NetworkAddress); if (this->NetworkAddress == 0) *Status = RPC_S_OUT_OF_MEMORY; } else this->NetworkAddress = 0; if (ARGUMENT_PRESENT(Endpoint)) { this->Endpoint = DuplicateString(Endpoint); if (this->Endpoint == 0) *Status = RPC_S_OUT_OF_MEMORY; } else this->Endpoint = 0; if (ARGUMENT_PRESENT(Options)) { this->Options = DuplicateString(Options); if (this->Options == 0) *Status = RPC_S_OUT_OF_MEMORY; } else { this->Options = 0; } } /*static*/ RPC_CHAR PAPI * StringCharSearchWithEscape ( IN RPC_CHAR PAPI * String, IN unsigned int Character ) /*++ Routine Description: This routine is the same as the library routine, strchr, except that the backslash character ('\') is treated as an escape character. Arguments: String - Supplies the string in which to search for the character. Character - Supplies the character to search for in the string. Return Value: A pointer to the first occurance of Character in String is returned. If Character does not exist in String, then 0 is returned. --*/ { #ifdef DBCS_ENABLED ASSERT(IsDBCSLeadByte((RPC_CHAR)Character) == FALSE); ASSERT(IsDBCSLeadByte(RPC_CONST_CHAR('\\')) == FALSE); while(*String != (RPC_CHAR)Character) { if (*String == 0) return(0); if (*String == RPC_CONST_CHAR('\\')) { String = (RPC_CHAR *)CharNext((LPCSTR)String); } String = (RPC_CHAR *)CharNext((LPCSTR)String); } return(String); #else while (*String != (RPC_CHAR) Character) { if (*String == RPC_CONST_CHAR('\\')) String++; if (*String == 0) return(0); String++; } return(String); #endif } /*static*/ void StringCopyWithEscape ( OUT RPC_CHAR PAPI * Destination, IN RPC_CHAR PAPI * Source ) /*++ Routine Description: This routine is the same as the library routine, strcpy, except that the backslash character ('\') is treated as an escape character. When a character is escaped, the backslash character is not copied to the Destination. Arguments: Destination - Returns a duplicate of the string specified in Source, but with out escaped characters escaped. Source - Specifies the string to be copied. Return Value: None. --*/ { BOOL fLastQuote = FALSE; #ifdef DBCS_ENABLED ASSERT(IsDBCSLeadByte('\\') == FALSE); #endif while ((*Destination = *Source) != 0) { #ifdef DBCS_ENABLED if (IsDBCSLeadByte(*Source)) { // Copy the whole DBCS character; don't look for // escapes within the character. Destination++; Source++; *Destination = *Source; if (*Source == 0) { ASSERT(0); // Bad string, NULL following a lead byte. return; } Destination++; Source++; } else #endif { if ( *Source != RPC_CONST_CHAR('\\') || fLastQuote == TRUE) { Destination++; fLastQuote = FALSE; } else { fLastQuote = TRUE; } Source++; } } } /*static*/ RPC_STATUS ParseAndCopyEndpointField ( OUT RPC_CHAR ** Endpoint, IN RPC_CHAR PAPI * String ) /*++ Routine Description: This routine parses and then copies the endpoint field in String. A copy of the field is made into a newly allocated string and returned in Endpoint. String is assumed to contain only the endpoint field; the terminating ',' or ']' are not included. Arguments: Endpoint - Returns a copy of the endpoint field in a newly allocated string. String - Supplies the endpoint field to be parsed and copied. Return Value: RPC_S_OK - The operation completed successfully. RPC_S_OUT_OF_MEMORY - There is no memory available to make a copy of the string. RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint field is syntactically incorrect. This error code will be returned if the endpoint field does not match the following pattern. [ | "endpoint=" ] --*/ { // Search will be used to scan along the string to find the end of // the endpoint field and the '='. RPC_CHAR PAPI * Search; Search = StringCharSearchWithEscape(String,RPC_CONST_CHAR('=')); if (Search == 0) { // This means that we have the pattern, so we just // copy the endpoint field. Search = StringCharSearchWithEscape(String,0); *Endpoint = new RPC_CHAR[size_t(Search - String + 1)]; if (*Endpoint == 0) return(RPC_S_OUT_OF_MEMORY); StringCopyWithEscape(*Endpoint,String); return(RPC_S_OK); } // Otherwise, we have the "endpoint=" pattern. First we need to check // that the string before the '=' is in fact "endpoint". *Search = 0; if ( RpcpStringCompare(String, RPC_CONST_STRING("endpoint")) != 0 ) { *Search = RPC_CONST_CHAR('='); return(RPC_S_INVALID_ENDPOINT_FORMAT); } *Search = RPC_CONST_CHAR('='); String = Search + 1; // Now we just need to allocate a new string and copy the endpoint into // it. Search = StringCharSearchWithEscape(String,0); *Endpoint = new RPC_CHAR[size_t(Search - String + 1)]; if (*Endpoint == 0) return(RPC_S_OUT_OF_MEMORY); StringCopyWithEscape(*Endpoint,String); return(RPC_S_OK); } RPC_CHAR * AllocateEmptyString ( void ) /*++ Routine Description: This routine allocates and returns an empty string (""). Return Value: A newly allocated empty string will be returned. --*/ { RPC_CHAR * String; String = new RPC_CHAR[1]; if (String != 0) *String = 0; return(String); } DCE_BINDING::DCE_BINDING ( IN RPC_CHAR PAPI * StringBinding, OUT RPC_STATUS PAPI * Status ) /*++ Routine Description: This constructor creates a DCE_BINDING object from a string binding, which requires that the string binding be parsed into seperate strings and validated. Arguments: StringBinding - Supplies the string being to be parsed. Status - Returns the status of the operation. This parameter will take on the following values: RPC_S_OK - The operation completed successfully. RPC_S_OUT_OF_MEMORY - Insufficient memory is available to allocate space for the fields of the string binding. RPC_S_INVALID_STRING_BINDING - The string binding is syntactically invalid. RPC_S_INVALID_ENDPOINT_FORMAT - The endpoint specified in the string binding is syntactically incorrect. RPC_S_INVALID_STRING_UUID - The specified object uuid does not contain the valid string representation of a uuid. --*/ { // String will point to the beginning of the field we are trying to // parse. RPC_CHAR PAPI * String; // Search will be used to scan along the string to find the end of // the field we are trying to parse. RPC_CHAR PAPI * Search; // This will contain the string representation of the object uuid. RPC_CHAR PAPI * ObjectUuidString; ALLOCATE_THIS(DCE_BINDING); // A string binding consists of an optional object uuid, an RPC protocol // sequence, a network address, an optional endpoint, and zero or more // option fields. // // [ "@" ] ":" // [ "[" ( | "endpoint=" | ) [","] // [ ","