Leaked source code of windows server 2003
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.
 
 
 
 
 
 

5269 lines
157 KiB

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
checker.cxx
Abstract:
IIS Services IISADMIN Extension
Unicode Metadata Sink.
Author:
Michael W. Thomas 11-19-98
--*/
#include <cominc.hxx>
#include <lmaccess.h>
#include <lmserver.h>
#include <lmapibuf.h>
#include <lmerr.h>
#include <ntlsa.h>
#include <time.h>
#include <ntsam.h>
#include <netlib.h>
#include <resource.h>
#define SECURITY_WIN32
#define ISSP_LEVEL 32
#define ISSP_MODE 1
#include <sspi.h>
#include <sspi.h>
#include <eventlog.hxx>
#include "extend.h"
#include <comadmin.h>
#include <sddl.h>
#include "wmrgexp.h"
typedef TCHAR USERNAME_STRING_TYPE[MAX_PATH];
typedef TCHAR PASSWORD_STRING_TYPE[LM20_PWLEN+1];
const LPCWSTR ROOTMDPath = L"/LM/W3SVC";
#define IIS_WP_GROUP L"IIS_WPG"
#ifndef ARRAYSIZE
#define ARRAYSIZE(_a) (sizeof((_a))/sizeof(*(_a)))
#endif
typedef enum
{
GUFM_SUCCESS,
GUFM_NO_PATH,
GUFM_NO_PASSWORD,
GUFM_NO_USER_ID
} GUFM_RETURN;
typedef struct _COM_APP_THREAD_STRUCT
{
USERNAME_STRING_TYPE ustWamUserName;
PASSWORD_STRING_TYPE pstWamUserPass;
} COM_APP_THREAD_STRUCT;
BOOL ValidatePassword(IN LPCTSTR UserName,IN LPCTSTR Domain,IN LPCTSTR Password);
void InitLsaString(PLSA_UNICODE_STRING LsaString,LPWSTR String);
DWORD GetPrincipalSID (LPCTSTR Principal,PSID *Sid,BOOL *pbWellKnownSID);
DWORD OpenPolicy(LPTSTR ServerName,DWORD DesiredAccess,PLSA_HANDLE PolicyHandle);
DWORD AddRightToUserAccount(LPCTSTR szAccountName, LPTSTR PrivilegeName);
DWORD DoesUserHaveThisRight(LPCTSTR szAccountName, LPTSTR PrivilegeName,BOOL *fHaveThatRight);
HRESULT UpdateComApplications(LPCTSTR szWamUserName,LPCTSTR szWamUserPass);
DWORD UpdateComApplicationsThread(PVOID pv);
int IsDomainController(void);
BOOL WaitForDCAvailability(void);
HRESULT UpdateAdminAcl(IMDCOM *pcCom, LPCWSTR szPath, LPCWSTR szAccountName);
BOOL MakeSureUserGetsCreated(LPTSTR pszAnonyName,LPTSTR pszAnonyPass,DWORD dwUserCommentResourceId,DWORD dwUserFullNameResourceId,BOOL fSpecicaliWamAccount);
// these two lines for logging event about account recreation
EVENT_LOG *g_eventLogForAccountRecreation = NULL;
BOOL CreateEventLogObject();
VOID
UpdateUserRights(
LPCTSTR account,
LPTSTR pstrRights[],
DWORD dwNofRights)
{
DWORD status;
BOOL fPresence;
for (DWORD i=0;i<dwNofRights;i++)
{
status = DoesUserHaveThisRight(account,pstrRights[i],&fPresence);
if (!NT_SUCCESS(status))
{
DBGPRINTF(( DBG_CONTEXT,"[UpdateAnonymousUser] DoesUserHaveThisRight returned err=0x%0X for account %s right %s\n",status,account,pstrRights[i]));
}
else
{
if (!fPresence)
{
status = AddRightToUserAccount(account,pstrRights[i]);
if (!NT_SUCCESS(status))
{
DBGPRINTF(( DBG_CONTEXT,"[UpdateAnonymousUser] AddRightToUserAccount returned err=0x%0X for account %s right %s\n",status,account,pstrRights[i]));
}
}
}
}
}
DWORD
AddRightToUserAccount(
LPCTSTR szAccountName,
LPTSTR PrivilegeName)
{
NTSTATUS status;
LSA_UNICODE_STRING UserRightString;
LSA_HANDLE PolicyHandle = NULL;
PSID pSID = NULL;
BOOL bWellKnownSID = FALSE;
// Create a LSA_UNICODE_STRING for the privilege name.
InitLsaString(&UserRightString, PrivilegeName);
// get the sid of szAccountName
status = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);
if (status != ERROR_SUCCESS)
{
DBGPRINTF(( DBG_CONTEXT,"[AddRightToUserAccount] GetPrincipalSID returned err=0x%0X\n",status));
return (status);
}
status = OpenPolicy(NULL, POLICY_ALL_ACCESS,&PolicyHandle);
if ( status == NERR_Success )
{
status = LsaAddAccountRights (
PolicyHandle,
pSID,
&UserRightString,
1);
}
if (PolicyHandle)
{
LsaClose(PolicyHandle);
}
if (pSID)
{
if (bWellKnownSID)
{
FreeSid (pSID);
}
else
{
free (pSID);
}
}
return status;
}
DWORD
DoesUserHaveThisRight(
LPCTSTR szAccountName,
LPTSTR PrivilegeName,
BOOL *fHaveThatRight)
{
BOOL fEnabled = FALSE;
NTSTATUS status;
LSA_UNICODE_STRING UserRightString;
LSA_HANDLE PolicyHandle = NULL;
PSID pSID = NULL;
BOOL bWellKnownSID = FALSE;
*fHaveThatRight = FALSE;
// Create a LSA_UNICODE_STRING for the privilege name.
InitLsaString(&UserRightString, PrivilegeName);
// get the sid of szAccountName
status = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);
if (status != ERROR_SUCCESS)
{
return status;
}
status = OpenPolicy(NULL, POLICY_ALL_ACCESS,&PolicyHandle);
if ( status == NERR_Success )
{
UINT i;
LSA_UNICODE_STRING *rgUserRights = NULL;
ULONG cRights;
status = LsaEnumerateAccountRights(
PolicyHandle,
pSID,
&rgUserRights,
&cRights);
if (status==STATUS_OBJECT_NAME_NOT_FOUND)
{
// no rights/privileges for this account
status = ERROR_SUCCESS;
fEnabled = FALSE;
}
else if (!NT_SUCCESS(status))
{
//iisDebugOut((LOG_TYPE_ERROR, _T("DoesUserHaveBasicRights:GetPrincipalSID:Failed to enumerate rights: status 0x%08lx\n"), status));
goto DoesUserHaveBasicRights_Exit;
}
for(i=0; i < cRights; i++)
{
if ( RtlEqualUnicodeString(&rgUserRights[i],&UserRightString,FALSE) )
{
fEnabled = TRUE;
break;
}
}
if (rgUserRights)
{
LsaFreeMemory(rgUserRights);
}
}
DoesUserHaveBasicRights_Exit:
if (PolicyHandle)
{
LsaClose(PolicyHandle);
}
if (pSID)
{
if (bWellKnownSID)
{
FreeSid (pSID);
}
else
{
free (pSID);
}
}
*fHaveThatRight = fEnabled;
return status;
}
DWORD
GetCurrentUserSID(
PSID *Sid)
{
DWORD dwReturn = ERROR_SUCCESS;
TOKEN_USER *tokenUser = NULL;
HANDLE tokenHandle = NULL;
DWORD tokenSize = 0 ;
DWORD sidLength = 0;
if ( !Sid )
{
dwReturn = ERROR_BAD_ARGUMENTS;
goto exit;
}
*Sid = NULL;
if ( !OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &tokenHandle ) )
{
dwReturn = GetLastError();
goto exit;
}
if ( !GetTokenInformation( tokenHandle, TokenUser, tokenUser, 0, &tokenSize ) )
{
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
{
dwReturn = GetLastError();
goto exit;
}
}
else
{
dwReturn = (DWORD)E_UNEXPECTED;
goto exit;
}
tokenUser = (TOKEN_USER *)malloc( tokenSize );
if ( !tokenUser )
{
dwReturn = ERROR_OUTOFMEMORY;
goto exit;
}
if ( !GetTokenInformation( tokenHandle, TokenUser, tokenUser, tokenSize, &tokenSize ) )
{
dwReturn = GetLastError();
goto exit;
}
sidLength = GetLengthSid( tokenUser->User.Sid );
*Sid = (PSID)malloc( sidLength );
if ( !*Sid )
{
dwReturn = ERROR_OUTOFMEMORY;
goto exit;
}
memcpy( *Sid, tokenUser->User.Sid, sidLength );
exit:
if (tokenHandle)
{
CloseHandle (tokenHandle);
tokenHandle = NULL;
}
if (tokenUser)
{
free(tokenUser);
tokenUser = NULL;
}
return dwReturn;
}
DWORD
CreateNewSD(
SECURITY_DESCRIPTOR **SD)
{
PACL dacl;
DWORD sidLength;
PSID sid = NULL;
PSID groupSID;
PSID ownerSID;
DWORD returnValue;
*SD = NULL;
returnValue = GetCurrentUserSID (&sid);
if (returnValue != ERROR_SUCCESS)
{
if (sid)
{
free(sid);
}
return returnValue;
}
sidLength = GetLengthSid (sid);
*SD = (SECURITY_DESCRIPTOR *) malloc (
(sizeof (ACL)+sizeof (ACCESS_ALLOWED_ACE)+sidLength) +
(2 * sidLength) +
sizeof (SECURITY_DESCRIPTOR));
if (!*SD)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return GetLastError();
}
groupSID = (SID *) (*SD + 1);
ownerSID = (SID *) (((BYTE *) groupSID) + sidLength);
dacl = (ACL *) (((BYTE *) ownerSID) + sidLength);
if (!InitializeSecurityDescriptor (*SD, SECURITY_DESCRIPTOR_REVISION))
{
free (*SD);
free (sid);
return GetLastError();
}
if (!InitializeAcl (dacl,
sizeof (ACL)+sizeof (ACCESS_ALLOWED_ACE)+sidLength,
ACL_REVISION2))
{
free (*SD);
free (sid);
return GetLastError();
}
if (!AddAccessAllowedAce (dacl,
ACL_REVISION2,
COM_RIGHTS_EXECUTE,
sid))
{
free (*SD);
free (sid);
return GetLastError();
}
if (!SetSecurityDescriptorDacl (*SD, TRUE, dacl, FALSE))
{
free (*SD);
free (sid);
return GetLastError();
}
memcpy (groupSID, sid, sidLength);
if (!SetSecurityDescriptorGroup (*SD, groupSID, FALSE))
{
free (*SD);
free (sid);
return GetLastError();
}
memcpy (ownerSID, sid, sidLength);
if (!SetSecurityDescriptorOwner (*SD, ownerSID, FALSE))
{
free (*SD);
free (sid);
return GetLastError();
}
if (sid)
free(sid);
return ERROR_SUCCESS;
}
DWORD
GetNamedValueSD (
HKEY RootKey,
LPTSTR KeyName,
LPTSTR ValueName,
SECURITY_DESCRIPTOR **SD,
BOOL *NewSD)
{
DWORD returnValue;
HKEY registryKey;
DWORD valueType;
DWORD valueSize = 0;
*NewSD = FALSE;
//
// Get the security descriptor from the named value. If it doesn't
// exist, create a fresh one.
//
returnValue = RegOpenKeyEx (RootKey, KeyName, 0, KEY_ALL_ACCESS, &registryKey);
if (returnValue != ERROR_SUCCESS)
{
if (returnValue == ERROR_FILE_NOT_FOUND)
{
*SD = NULL;
returnValue = CreateNewSD (SD);
if (returnValue != ERROR_SUCCESS)
{
if (*SD)
{
free(*SD);
}
return returnValue;
}
*NewSD = TRUE;
return ERROR_SUCCESS;
}
else
{
return returnValue;
}
}
returnValue = RegQueryValueEx (registryKey, ValueName, NULL, &valueType, NULL, &valueSize);
if (returnValue && returnValue != ERROR_INSUFFICIENT_BUFFER)
{
*SD = NULL;
returnValue = CreateNewSD (SD);
if (returnValue != ERROR_SUCCESS)
{
if (*SD)
{
free(*SD);
}
return returnValue;
}
*NewSD = TRUE;
}
else
{
*SD = (SECURITY_DESCRIPTOR *) malloc (valueSize);
if (!*SD)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return GetLastError();
}
returnValue = RegQueryValueEx (registryKey,
ValueName,
NULL,
&valueType,
(LPBYTE) *SD,
&valueSize);
if (returnValue)
{
if (*SD)
free (*SD);
*SD = NULL;
returnValue = CreateNewSD (SD);
if (returnValue != ERROR_SUCCESS)
{
if (*SD)
{
free(*SD);
}
return returnValue;
}
*NewSD = TRUE;
}
}
RegCloseKey (registryKey);
return ERROR_SUCCESS;
}
DWORD
GetPrincipalSID(
LPCTSTR Principal,
PSID *Sid,
BOOL *pbWellKnownSID)
{
HRESULT hr = S_OK;
DWORD returnValue=ERROR_SUCCESS;
SID_IDENTIFIER_AUTHORITY SidIdentifierNTAuthority = SECURITY_NT_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SidIdentifierWORLDAuthority = SECURITY_WORLD_SID_AUTHORITY;
PSID_IDENTIFIER_AUTHORITY pSidIdentifierAuthority = NULL;
BYTE Count = 0;
DWORD dwRID[8];
TCHAR pszPrincipal[MAX_PATH];
*pbWellKnownSID = TRUE;
memset(&(dwRID[0]), 0, 8 * sizeof(DWORD));
DBG_ASSERT(wcslen(Principal) < MAX_PATH);
hr = StringCchCopy(pszPrincipal, ARRAYSIZE(pszPrincipal), Principal);
if ( FAILED(hr) )
{
return HRESULTTOWIN32( hr );
}
_wcslwr(pszPrincipal);
if ( wcsstr(pszPrincipal, TEXT("administrators")) != NULL )
{
// Administrators group
pSidIdentifierAuthority = &SidIdentifierNTAuthority;
Count = 2;
dwRID[0] = SECURITY_BUILTIN_DOMAIN_RID;
dwRID[1] = DOMAIN_ALIAS_RID_ADMINS;
}
else if ( wcsstr(pszPrincipal, TEXT("system")) != NULL)
{
// SYSTEM
pSidIdentifierAuthority = &SidIdentifierNTAuthority;
Count = 1;
dwRID[0] = SECURITY_LOCAL_SYSTEM_RID;
}
else if ( wcsstr(pszPrincipal, TEXT("interactive")) != NULL)
{
// INTERACTIVE
pSidIdentifierAuthority = &SidIdentifierNTAuthority;
Count = 1;
dwRID[0] = SECURITY_INTERACTIVE_RID;
}
else if ( wcsstr(pszPrincipal, TEXT("everyone")) != NULL)
{
// Everyone
pSidIdentifierAuthority = &SidIdentifierWORLDAuthority;
Count = 1;
dwRID[0] = SECURITY_WORLD_RID;
}
else
{
*pbWellKnownSID = FALSE;
}
// IVANPASH
// Going through the above code *pbWellKnownSID and pSidIdentifierAuthority
// will always be in sync, but still there is warning on /W4
// so I added initializing pSidIdentifierAuthority to NULL and the
// additional check for pSidIdentifierAuthority in this if to make cl and prefast happy.
if ( *pbWellKnownSID && pSidIdentifierAuthority )
{
if ( !AllocateAndInitializeSid( pSidIdentifierAuthority,
(BYTE)Count,
dwRID[0],
dwRID[1],
dwRID[2],
dwRID[3],
dwRID[4],
dwRID[5],
dwRID[6],
dwRID[7],
Sid) )
{
returnValue = GetLastError();
}
}
else
{
// get regular account sid
DWORD sidSize;
TCHAR refDomain [256];
DWORD refDomainSize;
SID_NAME_USE snu;
sidSize = 0;
refDomainSize = 255;
LookupAccountName (NULL,
pszPrincipal,
*Sid,
&sidSize,
refDomain,
&refDomainSize,
&snu);
returnValue = GetLastError();
if (returnValue == ERROR_INSUFFICIENT_BUFFER)
{
*Sid = (PSID) malloc (sidSize);
if (!*Sid)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return GetLastError();
}
refDomainSize = 255;
if (!LookupAccountName (NULL,
pszPrincipal,
*Sid,
&sidSize,
refDomain,
&refDomainSize,
&snu))
{
free( *Sid );
*Sid = NULL;
returnValue = GetLastError();
}
else
{
returnValue = ERROR_SUCCESS;
}
}
}
return returnValue;
}
DWORD
CopyACL(
PACL OldACL,
PACL NewACL)
{
ACL_SIZE_INFORMATION aclSizeInfo;
LPVOID ace;
ACE_HEADER *aceHeader;
ULONG i;
GetAclInformation (OldACL,
(LPVOID) &aclSizeInfo,
(DWORD) sizeof (aclSizeInfo),
AclSizeInformation);
//
// Copy all of the ACEs to the new ACL
//
for (i = 0; i < aclSizeInfo.AceCount; i++)
{
//
// Get the ACE and header info
//
if (!GetAce (OldACL, i, &ace))
return GetLastError();
aceHeader = (ACE_HEADER *) ace;
//
// Add the ACE to the new list
//
if (!AddAce (NewACL, ACL_REVISION, 0xffffffff, ace, aceHeader->AceSize))
return GetLastError();
}
return ERROR_SUCCESS;
}
DWORD
AddAccessAllowedACEToACL(
PACL *Acl,
DWORD PermissionMask,
LPTSTR Principal)
{
ACL_SIZE_INFORMATION aclSizeInfo;
int aclSize;
DWORD returnValue = ERROR_SUCCESS;
PSID principalSID = NULL;
PACL oldACL;
PACL newACL;
BOOL bWellKnownSID = FALSE;
oldACL = *Acl;
returnValue = GetPrincipalSID (Principal, &principalSID, &bWellKnownSID);
if (returnValue != ERROR_SUCCESS)
{
return returnValue;
}
GetAclInformation (oldACL,
(LPVOID) &aclSizeInfo,
(DWORD) sizeof (ACL_SIZE_INFORMATION),
AclSizeInformation);
aclSize = aclSizeInfo.AclBytesInUse +
sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) +
GetLengthSid (principalSID) - sizeof (DWORD);
newACL = (PACL) new BYTE [aclSize];
if ( newACL == NULL )
{
returnValue = ERROR_OUTOFMEMORY;
goto cleanup;
}
if (!InitializeAcl (newACL, aclSize, ACL_REVISION))
{
returnValue = GetLastError();
goto cleanup;
}
returnValue = CopyACL (oldACL, newACL);
if (returnValue != ERROR_SUCCESS)
{
goto cleanup;
}
if (!AddAccessAllowedAce (newACL, ACL_REVISION2, PermissionMask, principalSID))
{
returnValue = GetLastError();
goto cleanup;
}
*Acl = newACL;
newACL = NULL;
cleanup:
// BugFix: 57654 Whistler
// Prefix bug leaking memory in error condition.
// By setting the newACL to NULL above if we have
// relinquished the memory to *Acl, we avoid releasing
// memory we have passed back to the caller.
// EBK 5/5/2000
if (newACL)
{
delete [] newACL;
newACL = NULL;
}
if (principalSID)
{
if (bWellKnownSID)
{
FreeSid (principalSID);
}
else
{
free (principalSID);
}
}
return returnValue;
}
DWORD
RemovePrincipalFromACL (
PACL Acl,
LPTSTR Principal)
{
ACL_SIZE_INFORMATION aclSizeInfo;
ULONG i;
LPVOID ace;
ACCESS_ALLOWED_ACE *accessAllowedAce;
ACCESS_DENIED_ACE *accessDeniedAce;
SYSTEM_AUDIT_ACE *systemAuditAce;
PSID principalSID = NULL;
DWORD returnValue = ERROR_SUCCESS;
ACE_HEADER *aceHeader;
BOOL bWellKnownSID = FALSE;
returnValue = GetPrincipalSID (Principal, &principalSID, &bWellKnownSID);
if (returnValue != ERROR_SUCCESS)
{
return returnValue;
}
GetAclInformation (Acl,
(LPVOID) &aclSizeInfo,
(DWORD) sizeof (ACL_SIZE_INFORMATION),
AclSizeInformation);
for (i = 0; i < aclSizeInfo.AceCount; i++)
{
if (!GetAce (Acl, i, &ace))
{
returnValue = GetLastError();
break;
}
aceHeader = (ACE_HEADER *) ace;
if (aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
accessAllowedAce = (ACCESS_ALLOWED_ACE *) ace;
if (EqualSid (principalSID, (PSID) &accessAllowedAce->SidStart))
{
DeleteAce (Acl, i);
break;
}
}
else if (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE)
{
accessDeniedAce = (ACCESS_DENIED_ACE *) ace;
if (EqualSid (principalSID, (PSID) &accessDeniedAce->SidStart))
{
DeleteAce (Acl, i);
break;
}
}
else if (aceHeader->AceType == SYSTEM_AUDIT_ACE_TYPE)
{
systemAuditAce = (SYSTEM_AUDIT_ACE *) ace;
if (EqualSid (principalSID, (PSID) &systemAuditAce->SidStart))
{
DeleteAce (Acl, i);
break;
}
}
}
if (principalSID)
{
if (bWellKnownSID)
{
FreeSid (principalSID);
}
else
{
free (principalSID);
}
}
return returnValue;
}
DWORD
MakeSDAbsolute(
PSECURITY_DESCRIPTOR OldSD,
PSECURITY_DESCRIPTOR *NewSD)
{
PSECURITY_DESCRIPTOR sd = NULL;
DWORD descriptorSize;
DWORD daclSize;
DWORD saclSize;
DWORD ownerSIDSize;
DWORD groupSIDSize;
PACL dacl = NULL;
PACL sacl = NULL;
PSID ownerSID = NULL;
PSID groupSID = NULL;
BOOL present;
BOOL systemDefault;
//
// Get SACL
//
if (!GetSecurityDescriptorSacl (OldSD, &present, &sacl, &systemDefault))
{
return GetLastError();
}
if (sacl && present)
{
saclSize = sacl->AclSize;
}
else
{
saclSize = 0;
}
//
// Get DACL
//
if (!GetSecurityDescriptorDacl (OldSD, &present, &dacl, &systemDefault))
return GetLastError();
if (dacl && present)
{
daclSize = dacl->AclSize;
}
else
{
daclSize = 0;
}
//
// Get Owner
//
if (!GetSecurityDescriptorOwner (OldSD, &ownerSID, &systemDefault))
{
return GetLastError();
}
ownerSIDSize = GetLengthSid (ownerSID);
//
// Get Group
//
if (!GetSecurityDescriptorGroup (OldSD, &groupSID, &systemDefault))
{
return GetLastError();
}
groupSIDSize = GetLengthSid (groupSID);
//
// Do the conversion
//
descriptorSize = 0;
MakeAbsoluteSD (OldSD, sd, &descriptorSize, dacl, &daclSize, sacl,
&saclSize, ownerSID, &ownerSIDSize, groupSID,
&groupSIDSize);
sd = (PSECURITY_DESCRIPTOR) new BYTE [SECURITY_DESCRIPTOR_MIN_LENGTH];
if (!sd)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return GetLastError();
}
if (!InitializeSecurityDescriptor (sd, SECURITY_DESCRIPTOR_REVISION))
return GetLastError();
if (!MakeAbsoluteSD (OldSD, sd, &descriptorSize, dacl, &daclSize, sacl,
&saclSize, ownerSID, &ownerSIDSize, groupSID,
&groupSIDSize))
{
return GetLastError();
}
*NewSD = sd;
return ERROR_SUCCESS;
}
DWORD
SetNamedValueSD(
HKEY RootKey,
LPTSTR KeyName,
LPTSTR ValueName,
SECURITY_DESCRIPTOR *SD)
{
DWORD returnValue;
DWORD disposition;
HKEY registryKey;
//
// Create new key or open existing key
//
returnValue = RegCreateKeyEx (RootKey,
KeyName,
0,
TEXT(""),
0,
KEY_ALL_ACCESS,
NULL,
&registryKey,
&disposition);
if (returnValue != ERROR_SUCCESS)
{
return returnValue;
}
//
// Write the security descriptor
//
returnValue = RegSetValueEx (registryKey,
ValueName,
0,
REG_BINARY,
(LPBYTE) SD,
GetSecurityDescriptorLength (SD));
if (returnValue != ERROR_SUCCESS)
{
return returnValue;
}
RegCloseKey (registryKey);
return ERROR_SUCCESS;
}
DWORD
RemovePrincipalFromNamedValueSD (
HKEY RootKey,
LPTSTR KeyName,
LPTSTR ValueName,
LPTSTR Principal)
{
DWORD returnValue = ERROR_SUCCESS;
SECURITY_DESCRIPTOR *sd = NULL;
SECURITY_DESCRIPTOR *sdSelfRelative = NULL;
SECURITY_DESCRIPTOR *sdAbsolute = NULL;
DWORD secDescSize;
BOOL present;
BOOL defaultDACL;
PACL dacl = NULL;
BOOL newSD = FALSE;
BOOL fFreeAbsolute = TRUE;
returnValue = GetNamedValueSD (RootKey, KeyName, ValueName, &sd, &newSD);
//
// Get security descriptor from registry or create a new one
//
if (returnValue != ERROR_SUCCESS)
{
return returnValue;
}
if (!GetSecurityDescriptorDacl (sd, &present, &dacl, &defaultDACL))
{
returnValue = GetLastError();
goto Cleanup;
}
//
// If the security descriptor is new, add the required Principals to it
//
if (newSD)
{
AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("SYSTEM"));
AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("INTERACTIVE"));
}
//
// Remove the Principal that the caller wants removed
//
returnValue = RemovePrincipalFromACL (dacl, Principal);
if (returnValue != ERROR_SUCCESS)
{
goto Cleanup;
}
//
// Make the security descriptor absolute if it isn't new
//
if (!newSD)
{
MakeSDAbsolute ((PSECURITY_DESCRIPTOR) sd, (PSECURITY_DESCRIPTOR *) &sdAbsolute);
fFreeAbsolute = TRUE;
}
else
{
sdAbsolute = sd;
fFreeAbsolute = FALSE;
}
//
// Set the discretionary ACL on the security descriptor
//
if (!SetSecurityDescriptorDacl (sdAbsolute, TRUE, dacl, FALSE))
{
returnValue = GetLastError();
goto Cleanup;
}
//
// Make the security descriptor self-relative so that we can
// store it in the registry
//
secDescSize = 0;
MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize);
sdSelfRelative = (SECURITY_DESCRIPTOR *) malloc (secDescSize);
if (!sdSelfRelative)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
returnValue = GetLastError();
goto Cleanup;
}
if (!MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize))
{
returnValue = GetLastError();
goto Cleanup;
}
//
// Store the security descriptor in the registry
//
SetNamedValueSD (RootKey, KeyName, ValueName, sdSelfRelative);
Cleanup:
if (sd)
{
free (sd);
}
if (sdSelfRelative)
{
free (sdSelfRelative);
}
if (fFreeAbsolute && sdAbsolute)
{
free (sdAbsolute);
}
return returnValue;
}
DWORD
AddAccessDeniedACEToACL(
PACL *Acl,
DWORD PermissionMask,
LPTSTR Principal)
{
ACL_SIZE_INFORMATION aclSizeInfo;
int aclSize;
DWORD returnValue = ERROR_SUCCESS;
PSID principalSID = NULL;
PACL oldACL;
PACL newACL;
BOOL bWellKnownSID = FALSE;
oldACL = *Acl;
returnValue = GetPrincipalSID (Principal, &principalSID, &bWellKnownSID);
if (returnValue != ERROR_SUCCESS)
{
return returnValue;
}
GetAclInformation (oldACL,
(LPVOID) &aclSizeInfo,
(DWORD) sizeof (ACL_SIZE_INFORMATION),
AclSizeInformation);
aclSize = aclSizeInfo.AclBytesInUse +
sizeof (ACL) + sizeof (ACCESS_DENIED_ACE) +
GetLengthSid (principalSID) - sizeof (DWORD);
newACL = (PACL) new BYTE [aclSize];
if ( newACL == NULL )
{
returnValue = ERROR_OUTOFMEMORY;
goto cleanup;
}
if (!InitializeAcl (newACL, aclSize, ACL_REVISION))
{
returnValue = GetLastError();
goto cleanup;
}
if (!AddAccessDeniedAce (newACL, ACL_REVISION2, PermissionMask, principalSID))
{
returnValue = GetLastError();
goto cleanup;
}
returnValue = CopyACL (oldACL, newACL);
if (returnValue != ERROR_SUCCESS)
{
goto cleanup;
}
*Acl = newACL;
newACL = NULL;
cleanup:
// BugFix: 57654 Whistler
// Prefix bug leaking memory in error condition.
// By setting the newACL to NULL above if we have
// relinquished the memory to *Acl, we avoid releasing
// memory we have passed back to the caller.
// EBK 5/5/2000
if (newACL)
{
delete[] newACL;
newACL = NULL;
}
if (principalSID)
{
if (bWellKnownSID)
{
FreeSid (principalSID);
}
else
{
free (principalSID);
}
}
return returnValue;
}
DWORD
AddPrincipalToNamedValueSD(
HKEY RootKey,
LPTSTR KeyName,
LPTSTR ValueName,
LPTSTR Principal,
BOOL Permit)
{
DWORD returnValue = ERROR_SUCCESS;
SECURITY_DESCRIPTOR *sd = NULL;
SECURITY_DESCRIPTOR *sdSelfRelative = NULL;
SECURITY_DESCRIPTOR *sdAbsolute = NULL;
DWORD secDescSize;
BOOL present;
BOOL defaultDACL;
PACL dacl;
BOOL newSD = FALSE;
BOOL fFreeAbsolute = TRUE;
returnValue = GetNamedValueSD (RootKey, KeyName, ValueName, &sd, &newSD);
//
// Get security descriptor from registry or create a new one
//
if (returnValue != ERROR_SUCCESS)
{
return returnValue;
}
if (!GetSecurityDescriptorDacl (sd, &present, &dacl, &defaultDACL))
{
returnValue = GetLastError();
goto Cleanup;
}
if (newSD)
{
AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("SYSTEM"));
AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, TEXT("INTERACTIVE"));
}
//
// Add the Principal that the caller wants added
//
if (Permit)
{
returnValue = AddAccessAllowedACEToACL (&dacl, COM_RIGHTS_EXECUTE, Principal);
}
else
{
returnValue = AddAccessDeniedACEToACL (&dacl, GENERIC_ALL, Principal);
}
if (returnValue != ERROR_SUCCESS)
{
goto Cleanup;
}
//
// Make the security descriptor absolute if it isn't new
//
if (!newSD)
{
MakeSDAbsolute ((PSECURITY_DESCRIPTOR) sd, (PSECURITY_DESCRIPTOR *) &sdAbsolute);
fFreeAbsolute = TRUE;
}
else
{
sdAbsolute = sd;
fFreeAbsolute = FALSE;
}
//
// Set the discretionary ACL on the security descriptor
//
if (!SetSecurityDescriptorDacl (sdAbsolute, TRUE, dacl, FALSE))
{
returnValue = GetLastError();
goto Cleanup;
}
//
// Make the security descriptor self-relative so that we can
// store it in the registry
//
secDescSize = 0;
MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize);
sdSelfRelative = (SECURITY_DESCRIPTOR *) malloc (secDescSize);
if (!sdSelfRelative)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
returnValue = GetLastError();
goto Cleanup;
}
if (!MakeSelfRelativeSD (sdAbsolute, sdSelfRelative, &secDescSize))
{
returnValue = GetLastError();
goto Cleanup;
}
//
// Store the security descriptor in the registry
//
SetNamedValueSD (RootKey, KeyName, ValueName, sdSelfRelative);
Cleanup:
if (sd)
{
free (sd);
}
if (sdSelfRelative)
{
free (sdSelfRelative);
}
if (fFreeAbsolute && sdAbsolute)
{
free (sdAbsolute);
}
return returnValue;
}
DWORD
ChangeDCOMAccessACL(
LPTSTR Principal,
BOOL SetPrincipal,
BOOL Permit)
{
DWORD err;
if (SetPrincipal)
{
// There is not need to check the return value of RemovePrincipalFromNamedValueSD,
// because its failure is not important (for example DefaultAccessPermission might
// not exist). The important call that must succeed is AddPrincipalToNamedValueSD
RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\OLE"),
TEXT("DefaultAccessPermission"),
Principal);
err = AddPrincipalToNamedValueSD (HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\OLE"),
TEXT("DefaultAccessPermission"),
Principal,
Permit);
}
else
{
err = RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\OLE"),
TEXT("DefaultAccessPermission"),
Principal);
}
return err;
}
DWORD
ChangeDCOMLaunchACL(
LPTSTR Principal,
BOOL SetPrincipal,
BOOL Permit)
{
TCHAR keyName [256] = TEXT("Software\\Microsoft\\OLE");
DWORD err;
if (SetPrincipal)
{
// There is not need to check the return value of RemovePrincipalFromNamedValueSD,
// because its failure is not important (for example DefaultAccessPermission might
// not exist). The important call that must succeed is AddPrincipalToNamedValueSD
RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
keyName,
TEXT("DefaultLaunchPermission"),
Principal);
err = AddPrincipalToNamedValueSD (HKEY_LOCAL_MACHINE,
keyName,
TEXT("DefaultLaunchPermission"),
Principal,
Permit);
}
else
{
err = RemovePrincipalFromNamedValueSD (HKEY_LOCAL_MACHINE,
keyName,
TEXT("DefaultLaunchPermission"),
Principal);
}
return err;
}
GUFM_RETURN
GetUserFromMetabase(
IMDCOM *pcCom,
LPWSTR pszPath,
DWORD dwUserMetaId,
DWORD dwPasswordMetaId,
USERNAME_STRING_TYPE ustUserBuf,
PASSWORD_STRING_TYPE pstPasswordBuf)
{
HRESULT hresTemp;
GUFM_RETURN gufmReturn = GUFM_SUCCESS;
METADATA_RECORD mdrData;
DWORD dwRequiredDataLen;
METADATA_HANDLE mhOpenHandle;
hresTemp = pcCom->ComMDOpenMetaObject(METADATA_MASTER_ROOT_HANDLE,
pszPath,
METADATA_PERMISSION_READ,
OPEN_TIMEOUT_VALUE,
&mhOpenHandle);
if (FAILED(hresTemp))
{
gufmReturn = GUFM_NO_PATH;
}
else
{
MD_SET_DATA_RECORD_EXT(&mdrData,
dwUserMetaId,
METADATA_NO_ATTRIBUTES,
ALL_METADATA,
STRING_METADATA,
MAX_PATH * sizeof(TCHAR),
(PBYTE)ustUserBuf)
hresTemp = pcCom->ComMDGetMetaData(mhOpenHandle,
NULL,
&mdrData,
&dwRequiredDataLen);
if (FAILED(hresTemp) || (ustUserBuf[0] == (TCHAR)'\0'))
{
gufmReturn = GUFM_NO_USER_ID;
}
else
{
MD_SET_DATA_RECORD_EXT(&mdrData,
dwPasswordMetaId,
METADATA_NO_ATTRIBUTES,
ALL_METADATA,
STRING_METADATA,
(LM20_PWLEN+1) * sizeof(TCHAR),
(PBYTE)pstPasswordBuf)
hresTemp = pcCom->ComMDGetMetaData(mhOpenHandle,
NULL,
&mdrData,
&dwRequiredDataLen);
if (FAILED(hresTemp))
{
gufmReturn = GUFM_NO_PASSWORD;
}
}
pcCom->ComMDCloseMetaObject(mhOpenHandle);
}
return gufmReturn;
}
BOOL
WritePasswordToMetabase(
IMDCOM *pcCom,
LPWSTR pszPath,
DWORD dwPasswordMetaId,
PASSWORD_STRING_TYPE pstPasswordBuf)
{
HRESULT hresReturn;
METADATA_RECORD mdrData;
METADATA_HANDLE mhOpenHandle;
hresReturn = pcCom->ComMDOpenMetaObject(METADATA_MASTER_ROOT_HANDLE,
pszPath,
METADATA_PERMISSION_WRITE,
OPEN_TIMEOUT_VALUE,
&mhOpenHandle);
if (SUCCEEDED(hresReturn))
{
MD_SET_DATA_RECORD_EXT(&mdrData,
dwPasswordMetaId,
METADATA_INHERIT | METADATA_SECURE,
IIS_MD_UT_FILE,
STRING_METADATA,
sizeof(pstPasswordBuf),
(PBYTE)pstPasswordBuf)
hresReturn = pcCom->ComMDSetMetaData(mhOpenHandle,
NULL,
&mdrData);
pcCom->ComMDCloseMetaObject(mhOpenHandle);
}
return SUCCEEDED(hresReturn);
}
BOOL
DoesUserExist(
LPWSTR strUsername,
BOOL *fDisabled)
{
BYTE *pBuffer;
INT err = NERR_Success;
BOOL fReturn = FALSE;
*fDisabled = FALSE;
err = NetUserGetInfo( NULL, strUsername, 3, &pBuffer );
if ( err == NERR_Success )
{
*fDisabled = !!(((PUSER_INFO_3)pBuffer)->usri3_flags & UF_ACCOUNTDISABLE);
NetApiBufferFree( pBuffer );
fReturn = TRUE;
}
return( fReturn );
}
NET_API_STATUS
NetpNtStatusToApiStatus (
IN NTSTATUS NtStatus)
/*++
Routine Description:
This function takes an NT status code and maps it to the appropriate
LAN Man error code.
Arguments:
NtStatus - Supplies the NT status.
Return Value:
Returns the appropriate LAN Man error code for the NT status.
--*/
{
NET_API_STATUS error;
//
// A small optimization for the most common case.
//
if ( NtStatus == STATUS_SUCCESS )
{
return NERR_Success;
}
switch ( NtStatus )
{
case STATUS_BUFFER_TOO_SMALL :
return NERR_BufTooSmall;
case STATUS_FILES_OPEN :
return NERR_OpenFiles;
case STATUS_CONNECTION_IN_USE :
return NERR_DevInUse;
case STATUS_INVALID_LOGON_HOURS :
return NERR_InvalidLogonHours;
case STATUS_INVALID_WORKSTATION :
return NERR_InvalidWorkstation;
case STATUS_PASSWORD_EXPIRED :
return NERR_PasswordExpired;
case STATUS_ACCOUNT_EXPIRED :
return NERR_AccountExpired;
case STATUS_REDIRECTOR_NOT_STARTED :
return NERR_NetNotStarted;
case STATUS_GROUP_EXISTS:
return NERR_GroupExists;
case STATUS_INTERNAL_DB_CORRUPTION:
return NERR_InvalidDatabase;
case STATUS_INVALID_ACCOUNT_NAME:
return NERR_BadUsername;
case STATUS_INVALID_DOMAIN_ROLE:
case STATUS_INVALID_SERVER_STATE:
case STATUS_BACKUP_CONTROLLER:
return NERR_NotPrimary;
case STATUS_INVALID_DOMAIN_STATE:
return NERR_ACFNotLoaded;
case STATUS_MEMBER_IN_GROUP:
return NERR_UserInGroup;
case STATUS_MEMBER_NOT_IN_GROUP:
return NERR_UserNotInGroup;
case STATUS_NONE_MAPPED:
case STATUS_NO_SUCH_GROUP:
return NERR_GroupNotFound;
case STATUS_SPECIAL_GROUP:
case STATUS_MEMBERS_PRIMARY_GROUP:
return NERR_SpeGroupOp;
case STATUS_USER_EXISTS:
return NERR_UserExists;
case STATUS_NO_SUCH_USER:
return NERR_UserNotFound;
case STATUS_PRIVILEGE_NOT_HELD:
return ERROR_ACCESS_DENIED;
case STATUS_LOGON_SERVER_CONFLICT:
return NERR_LogonServerConflict;
case STATUS_TIME_DIFFERENCE_AT_DC:
return NERR_TimeDiffAtDC;
case STATUS_SYNCHRONIZATION_REQUIRED:
return NERR_SyncRequired;
case STATUS_WRONG_PASSWORD_CORE:
return NERR_BadPasswordCore;
case STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
return NERR_DCNotFound;
case STATUS_PASSWORD_RESTRICTION:
return NERR_PasswordTooShort;
case STATUS_ALREADY_DISCONNECTED:
return NERR_Success;
default:
//
// Use the system routine to do the mapping to ERROR_ codes.
//
#ifndef WIN32_CHICAGO
error = RtlNtStatusToDosError( NtStatus );
if ( error != (NET_API_STATUS)NtStatus )
{
return error;
}
#endif // WIN32_CHICAGO
//
// Could not map the NT status to anything appropriate.
//
return NERR_InternalError;
}
} // NetpNtStatusToApiStatus
NET_API_STATUS
UaspGetDomainId(
IN LPCWSTR ServerName OPTIONAL,
OUT PSAM_HANDLE SamServerHandle OPTIONAL,
OUT PPOLICY_ACCOUNT_DOMAIN_INFO * AccountDomainInfo
)
/*++
Routine Description:
Return a domain ID of the account domain of a server.
Arguments:
ServerName - A pointer to a string containing the name of the
Domain Controller (DC) to query. A NULL pointer
or string specifies the local machine.
SamServerHandle - Returns the SAM connection handle if the caller wants it.
DomainId - Receives a pointer to the domain ID.
Caller must deallocate buffer using NetpMemoryFree.
Return Value:
Error code for the operation.
--*/
{
NET_API_STATUS NetStatus;
NTSTATUS Status;
SAM_HANDLE LocalSamHandle = NULL;
ACCESS_MASK LSADesiredAccess;
LSA_HANDLE LSAPolicyHandle = NULL;
OBJECT_ATTRIBUTES LSAObjectAttributes;
UNICODE_STRING ServerNameString;
//
// Connect to the SAM server
//
RtlInitUnicodeString( &ServerNameString, ServerName );
Status = SamConnect(
&ServerNameString,
&LocalSamHandle,
SAM_SERVER_LOOKUP_DOMAIN,
NULL);
if ( !NT_SUCCESS(Status))
{
LocalSamHandle = NULL;
NetStatus = NetpNtStatusToApiStatus( Status );
goto Cleanup;
}
//
// Open LSA to read account domain info.
//
if ( AccountDomainInfo != NULL)
{
//
// set desired access mask.
//
LSADesiredAccess = POLICY_VIEW_LOCAL_INFORMATION;
InitializeObjectAttributes( &LSAObjectAttributes,
NULL, // Name
0, // Attributes
NULL, // Root
NULL ); // Security Descriptor
Status = LsaOpenPolicy( &ServerNameString,
&LSAObjectAttributes,
LSADesiredAccess,
&LSAPolicyHandle );
if( !NT_SUCCESS(Status) )
{
NetStatus = NetpNtStatusToApiStatus( Status );
goto Cleanup;
}
//
// now read account domain info from LSA.
//
Status = LsaQueryInformationPolicy(
LSAPolicyHandle,
PolicyAccountDomainInformation,
(PVOID *) AccountDomainInfo );
if( !NT_SUCCESS(Status) )
{
NetStatus = NetpNtStatusToApiStatus( Status );
goto Cleanup;
}
}
//
// Return the SAM connection handle to the caller if he wants it.
// Otherwise, disconnect from SAM.
//
if ( ARGUMENT_PRESENT( SamServerHandle ) )
{
*SamServerHandle = LocalSamHandle;
LocalSamHandle = NULL;
}
NetStatus = NERR_Success;
//
// Cleanup locally used resources
//
Cleanup:
if ( LocalSamHandle != NULL )
{
(VOID) SamCloseHandle( LocalSamHandle );
}
if( LSAPolicyHandle != NULL )
{
LsaClose( LSAPolicyHandle );
}
return NetStatus;
} // UaspGetDomainId
NET_API_STATUS
SampCreateFullSid(
IN PSID DomainSid,
IN ULONG Rid,
OUT PSID *AccountSid)
/*++
Routine Description:
This function creates a domain account sid given a domain sid and
the relative id of the account within the domain.
The returned Sid may be freed with LocalFree.
--*/
{
NET_API_STATUS NetStatus;
NTSTATUS IgnoreStatus;
UCHAR AccountSubAuthorityCount;
ULONG AccountSidLength;
PULONG RidLocation;
//
// Calculate the size of the new sid
//
AccountSubAuthorityCount = *RtlSubAuthorityCountSid(DomainSid) + (UCHAR)1;
AccountSidLength = RtlLengthRequiredSid(AccountSubAuthorityCount);
//
// Allocate space for the account sid
//
*AccountSid = LocalAlloc(LMEM_ZEROINIT,AccountSidLength);
if (*AccountSid == NULL)
{
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
//
// Copy the domain sid into the first part of the account sid
//
IgnoreStatus = RtlCopySid(AccountSidLength, *AccountSid, DomainSid);
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Increment the account sid sub-authority count
//
*RtlSubAuthorityCountSid(*AccountSid) = AccountSubAuthorityCount;
//
// Add the rid as the final sub-authority
//
RidLocation = RtlSubAuthoritySid(*AccountSid, AccountSubAuthorityCount-1);
*RidLocation = Rid;
NetStatus = NERR_Success;
}
return(NetStatus);
}
BOOL
GetGuestUserNameForDomain_FastWay(
LPTSTR szDomainToLookUp,
LPTSTR lpGuestUsrName,
DWORD cchGuestUsrName)
{
HRESULT hr = S_OK;
BOOL fReturn = FALSE;
NET_API_STATUS NetStatus;
SAM_HANDLE SamServerHandle = NULL; // for UaspGetDomainId()
PPOLICY_ACCOUNT_DOMAIN_INFO pAccountDomainInfo = NULL;
PSID pAccountSid = NULL;
PSID pDomainSid = NULL;
SID_NAME_USE sidNameUse = SidTypeUser; // for LookupAccountSid()
TCHAR szUserName[UNLEN+1];
DWORD cbName = UNLEN+1;
TCHAR szReferencedDomainName[UNLEN+1]; // use UNLEN because DNLEN is too small
DWORD cbReferencedDomainName = sizeof(szReferencedDomainName);
// make sure not to return back gobble-d-gook
*lpGuestUsrName = TEXT('\0');
//
// Get the Sid for the specified Domain
//
// szDomainToLookUp=NULL for local machine
NetStatus = UaspGetDomainId( szDomainToLookUp,&SamServerHandle,&pAccountDomainInfo );
if ( NetStatus != NERR_Success )
{
goto GetGuestUserNameForDomain_FastWay_Exit;
}
pDomainSid = pAccountDomainInfo->DomainSid;
//
// Use the Domain Sid and the well known Guest RID to create the Real Guest Sid
//
// Well-known users ...
// DOMAIN_USER_RID_ADMIN (0x000001F4L)
// DOMAIN_USER_RID_GUEST (0x000001F5L)
NetStatus = NERR_InternalError;
NetStatus = SampCreateFullSid(pDomainSid, DOMAIN_USER_RID_GUEST, &pAccountSid);
if ( NetStatus != NERR_Success )
{
goto GetGuestUserNameForDomain_FastWay_Exit;
}
//
// Check if the SID is valid
//
if (0 == IsValidSid(pAccountSid))
{
goto GetGuestUserNameForDomain_FastWay_Exit;
}
//
// Retrieve the UserName for the specified SID
//
*szUserName = TEXT('\0');
*szReferencedDomainName = TEXT('\0');
// szDomainToLookUp=NULL for local machine
if (!LookupAccountSid(szDomainToLookUp,
pAccountSid,
szUserName,
&cbName,
szReferencedDomainName,
&cbReferencedDomainName,
&sidNameUse))
{
goto GetGuestUserNameForDomain_FastWay_Exit;
}
// Return the guest user name that we got.
hr = StringCchCopy(lpGuestUsrName, cchGuestUsrName, szUserName);
if ( FAILED(hr) )
{
*lpGuestUsrName = TEXT('\0');
goto GetGuestUserNameForDomain_FastWay_Exit;
}
// Wow, after all that, we must have succeeded
fReturn = TRUE;
GetGuestUserNameForDomain_FastWay_Exit:
// Free the Domain info if we got some
if (pAccountDomainInfo)
{
NetpMemoryFree(pAccountDomainInfo);
}
// Free the sid if we had allocated one
if (pAccountSid)
{
LocalFree(pAccountSid);
}
return fReturn;
}
BOOL
GetGuestUserName_SlowWay(
LPWSTR lpGuestUsrName,
DWORD cchGuestUsrName)
{
HRESULT hr = S_OK;
LPWSTR ServerName = NULL; // default to local machine
DWORD Level = 1; // to retrieve info of all local and global normal user accounts
DWORD Index = 0;
DWORD EntriesRequested = 5;
DWORD PreferredMaxLength = 1024;
DWORD ReturnedEntryCount = 0;
PVOID SortedBuffer = NULL;
NET_DISPLAY_USER *p = NULL;
DWORD i=0;
int err = 0;
BOOL fStatus = TRUE;
while (fStatus)
{
err = NetQueryDisplayInformation(ServerName,
Level,
Index,
EntriesRequested,
PreferredMaxLength,
&ReturnedEntryCount,
&SortedBuffer);
if (err == NERR_Success)
{
fStatus = FALSE;
}
if (err == NERR_Success || err == ERROR_MORE_DATA)
{
p = (NET_DISPLAY_USER *)SortedBuffer;
i = 0;
while (i < ReturnedEntryCount && (p[i].usri1_user_id != DOMAIN_USER_RID_GUEST))
{
i++;
}
if (i == ReturnedEntryCount)
{
if (err == ERROR_MORE_DATA)
{
// need to get more entries
Index = p[i-1].usri1_next_index;
}
}
else
{
hr = StringCchCopyW(lpGuestUsrName, cchGuestUsrName, p[i].usri1_name);
if ( FAILED(hr) )
{
NetApiBufferFree(SortedBuffer);
SortedBuffer = NULL;
*lpGuestUsrName = TEXT('\0');
return FALSE;
}
fStatus = FALSE;
}
}
else
{
if ( SortedBuffer != NULL )
{
NetApiBufferFree(SortedBuffer);
SortedBuffer = NULL;
}
return FALSE;
}
NetApiBufferFree(SortedBuffer);
SortedBuffer = NULL;
}
return TRUE;
}
BOOL
GetGuestUserName(
LPTSTR lpOutGuestUsrName,
DWORD cchOutGuestUsrName)
{
HRESULT hr = S_OK;
TCHAR szGuestUsrName[UNLEN+1];
LPTSTR pszComputerName = NULL;
// try to retrieve the guest username the fast way
// meaning = lookup the domain sid, and the well known guest rid, to get the guest sid.
// then look it up. The reason for this function is that on large domains with mega users
// the account can be quickly looked up.
if (!GetGuestUserNameForDomain_FastWay(pszComputerName, szGuestUsrName, UNLEN+1))
{
// if the fast way failed for some reason, then let's do it
// the slow way, since this way always used to work, only on large domains (1 mil users)
// it could take 24hrs (since this function actually enumerates thru the domain)
if (!GetGuestUserName_SlowWay(szGuestUsrName, UNLEN+1))
{
return FALSE;
}
}
// Return back the username
hr = StringCchCopyW(lpOutGuestUsrName, cchOutGuestUsrName, szGuestUsrName);
if ( FAILED(hr) )
{
return FALSE;
}
return TRUE;
}
int
GetGuestGrpName(
LPTSTR lpGuestGrpName)
{
LPCTSTR ServerName = NULL; // local machine
DWORD cbName = UNLEN+1; // use UNLEN because DNLEN is too small
TCHAR ReferencedDomainName[UNLEN+1];
DWORD cbReferencedDomainName = sizeof(ReferencedDomainName) / sizeof(TCHAR);
SID_NAME_USE sidNameUse = SidTypeUser;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
PSID GuestsSid = NULL;
AllocateAndInitializeSid(&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_GUESTS,
0,
0,
0,
0,
0,
0,
&GuestsSid);
LookupAccountSid(ServerName,
GuestsSid,
lpGuestGrpName,
&cbName,
ReferencedDomainName,
&cbReferencedDomainName,
&sidNameUse);
if (GuestsSid)
{
FreeSid(GuestsSid);
}
return 0;
}
INT
RegisterAccountToLocalGroup(
LPCTSTR szAccountName,
LPCTSTR szLocalGroupName,
BOOL fAction)
{
HRESULT hr = S_OK;
int err;
PSID pSID = NULL;
BOOL bWellKnownSID = FALSE;
TCHAR szLocalizedLocalGroupName[GNLEN + 1] = TEXT( "\0" ) ;
WCHAR wszLocalGroupName[_MAX_PATH];
LOCALGROUP_MEMBERS_INFO_0 buf;
// get the sid of szAccountName
err = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);
if (err != ERROR_SUCCESS)
{
return (err);
}
// Get the localized LocalGroupName
if (_wcsicmp(szLocalGroupName, TEXT("Guests")) == 0)
{
GetGuestGrpName(szLocalizedLocalGroupName);
}
else
{
hr = StringCchCopy(szLocalizedLocalGroupName, ARRAYSIZE(szLocalizedLocalGroupName), szLocalGroupName);
if ( FAILED(hr) )
{
err = (INT)HRESULTTOWIN32(hr);
goto exit;
}
}
// transfer szLocalGroupName to WCHAR
hr = StringCchCopyW(wszLocalGroupName, ARRAYSIZE(wszLocalGroupName), szLocalizedLocalGroupName);
if ( FAILED(hr) )
{
err = (INT)HRESULTTOWIN32(hr);
goto exit;
}
buf.lgrmi0_sid = pSID;
if (fAction)
{
err = NetLocalGroupAddMembers(NULL, wszLocalGroupName, 0, (LPBYTE)&buf, 1);
}
else
{
err = NetLocalGroupDelMembers(NULL, wszLocalGroupName, 0, (LPBYTE)&buf, 1);
}
exit:
if (pSID)
{
if (bWellKnownSID)
{
FreeSid (pSID);
}
else
{
free (pSID);
}
}
return (err);
}
void
InitLsaString(
PLSA_UNICODE_STRING LsaString,
LPWSTR String)
{
DWORD StringLength;
if (String == NULL)
{
LsaString->Buffer = NULL;
LsaString->Length = 0;
LsaString->MaximumLength = 0;
return;
}
StringLength = (DWORD)wcslen(String);
LsaString->Buffer = String;
LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR);
}
DWORD
OpenPolicy(
LPTSTR ServerName,
DWORD DesiredAccess,
PLSA_HANDLE PolicyHandle)
{
DWORD Error;
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
LSA_UNICODE_STRING ServerString;
PLSA_UNICODE_STRING Server = NULL;
SECURITY_QUALITY_OF_SERVICE QualityOfService;
QualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
QualityOfService.ImpersonationLevel = SecurityImpersonation;
QualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
QualityOfService.EffectiveOnly = FALSE;
//
// The two fields that must be set are length and the quality of service.
//
ObjectAttributes.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
ObjectAttributes.RootDirectory = NULL;
ObjectAttributes.ObjectName = NULL;
ObjectAttributes.Attributes = 0;
ObjectAttributes.SecurityDescriptor = NULL;
ObjectAttributes.SecurityQualityOfService = &QualityOfService;
if (ServerName != NULL)
{
//
// Make a LSA_UNICODE_STRING out of the LPWSTR passed in
//
InitLsaString(&ServerString,ServerName);
Server = &ServerString;
}
//
// Attempt to open the policy for all access
//
Error = LsaOpenPolicy(Server,&ObjectAttributes,DesiredAccess,PolicyHandle);
return(Error);
}
INT
RegisterAccountUserRights(
LPCTSTR szAccountName,
BOOL fAction,
BOOL fSpecicaliWamAccount)
{
int err;
PSID pSID = NULL;
BOOL bWellKnownSID = FALSE;
LSA_UNICODE_STRING UserRightString;
LSA_HANDLE PolicyHandle = NULL;
// get the sid of szAccountName
err = GetPrincipalSID ((LPTSTR)szAccountName, &pSID, &bWellKnownSID);
if (err != ERROR_SUCCESS)
{
return (err);
}
err = OpenPolicy(NULL, POLICY_ALL_ACCESS,&PolicyHandle);
if ( err == NERR_Success )
{
if (fAction)
{
// defined in ntsecapi.h and ntlsa.h
//#define SE_INTERACTIVE_LOGON_NAME TEXT("SeInteractiveLogonRight")
//#define SE_NETWORK_LOGON_NAME TEXT("SeNetworkLogonRight")
//#define SE_BATCH_LOGON_NAME TEXT("SeBatchLogonRight")
//#define SE_SERVICE_LOGON_NAME TEXT("SeServiceLogonRight")
// Defined in winnt.h
//#define SE_CREATE_TOKEN_NAME TEXT("SeCreateTokenPrivilege")
//#define SE_ASSIGNPRIMARYTOKEN_NAME TEXT("SeAssignPrimaryTokenPrivilege")
//#define SE_LOCK_MEMORY_NAME TEXT("SeLockMemoryPrivilege")
//#define SE_INCREASE_QUOTA_NAME TEXT("SeIncreaseQuotaPrivilege")
//#define SE_UNSOLICITED_INPUT_NAME TEXT("SeUnsolicitedInputPrivilege")
//#define SE_MACHINE_ACCOUNT_NAME TEXT("SeMachineAccountPrivilege")
//#define SE_TCB_NAME TEXT("SeTcbPrivilege")
//#define SE_SECURITY_NAME TEXT("SeSecurityPrivilege")
//#define SE_TAKE_OWNERSHIP_NAME TEXT("SeTakeOwnershipPrivilege")
//#define SE_LOAD_DRIVER_NAME TEXT("SeLoadDriverPrivilege")
//#define SE_SYSTEM_PROFILE_NAME TEXT("SeSystemProfilePrivilege")
//#define SE_SYSTEMTIME_NAME TEXT("SeSystemtimePrivilege")
//#define SE_PROF_SINGLE_PROCESS_NAME TEXT("SeProfileSingleProcessPrivilege")
//#define SE_INC_BASE_PRIORITY_NAME TEXT("SeIncreaseBasePriorityPrivilege")
//#define SE_CREATE_PAGEFILE_NAME TEXT("SeCreatePagefilePrivilege")
//#define SE_CREATE_PERMANENT_NAME TEXT("SeCreatePermanentPrivilege")
//#define SE_BACKUP_NAME TEXT("SeBackupPrivilege")
//#define SE_RESTORE_NAME TEXT("SeRestorePrivilege")
//#define SE_SHUTDOWN_NAME TEXT("SeShutdownPrivilege")
//#define SE_DEBUG_NAME TEXT("SeDebugPrivilege")
//#define SE_AUDIT_NAME TEXT("SeAuditPrivilege")
//#define SE_SYSTEM_ENVIRONMENT_NAME TEXT("SeSystemEnvironmentPrivilege")
//#define SE_CHANGE_NOTIFY_NAME TEXT("SeChangeNotifyPrivilege")
//#define SE_REMOTE_SHUTDOWN_NAME TEXT("SeRemoteShutdownPrivilege")
//#define SE_UNDOCK_NAME TEXT("SeUndockPrivilege")
//#define SE_SYNC_AGENT_NAME TEXT("SeSyncAgentPrivilege")
//#define SE_ENABLE_DELEGATION_NAME TEXT("SeEnableDelegationPrivilege")
if (fSpecicaliWamAccount)
{
// no interactive logon for iwam!
InitLsaString(&UserRightString, SE_NETWORK_LOGON_NAME);
err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
InitLsaString(&UserRightString, SE_BATCH_LOGON_NAME);
err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
}
else
{
InitLsaString(&UserRightString, SE_INTERACTIVE_LOGON_NAME);
err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
InitLsaString(&UserRightString, SE_NETWORK_LOGON_NAME);
err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
InitLsaString(&UserRightString, SE_BATCH_LOGON_NAME);
err = LsaAddAccountRights(PolicyHandle, pSID, &UserRightString, 1);
}
}
else
{
InitLsaString(&UserRightString, SE_INTERACTIVE_LOGON_NAME);
err = LsaRemoveAccountRights(PolicyHandle, pSID, FALSE, &UserRightString,1);
InitLsaString(&UserRightString, SE_NETWORK_LOGON_NAME);
err = LsaRemoveAccountRights(PolicyHandle, pSID, FALSE, &UserRightString,1);
InitLsaString(&UserRightString, SE_BATCH_LOGON_NAME);
err = LsaRemoveAccountRights(PolicyHandle, pSID, FALSE, &UserRightString,1);
}
LsaClose(PolicyHandle);
}
if (pSID)
{
if (bWellKnownSID)
{
FreeSid (pSID);
}
else
{
free (pSID);
}
}
return (err);
}
int
ChangeUserPassword(
IN LPTSTR szUserName,
IN LPTSTR szNewPassword)
{
HRESULT hr = S_OK;
int iReturn = TRUE;
USER_INFO_1003 pi1003;
NET_API_STATUS nas;
TCHAR szRawComputerName[CNLEN + 10];
DWORD dwLen = CNLEN + 10;
TCHAR szComputerName[CNLEN + 10];
TCHAR szCopyOfUserName[UNLEN+10];
TCHAR szTempFullUserName[(CNLEN + 10) + (UNLEN+1)];
LPTSTR pch = NULL;
hr = StringCchCopy(szCopyOfUserName, ARRAYSIZE(szCopyOfUserName), szUserName);
if ( FAILED(hr) )
{
iReturn = FALSE;
goto ChangeUserPassword_Exit;
}
//iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("ChangeUserPassword().Start.name=%s,pass=%s"),szCopyOfUserName,szNewPassword));
if ( !GetComputerName( szRawComputerName, &dwLen ))
{
iReturn = FALSE;
goto ChangeUserPassword_Exit;
}
// Make a copy to be sure not to move the pointer around.
hr = StringCchCopy(szTempFullUserName, ARRAYSIZE(szTempFullUserName), szCopyOfUserName);
if ( FAILED(hr) )
{
iReturn = FALSE;
goto ChangeUserPassword_Exit;
}
// Check if there is a "\" in there.
pch = wcschr(szTempFullUserName, '\\');
if (pch)
{
// szCopyOfUserName should now go from something like this:
// mycomputer\myuser
// to this myuser
hr = StringCchCopy(szCopyOfUserName, ARRAYSIZE(szCopyOfUserName), pch+1);
if ( FAILED(hr) )
{
iReturn = FALSE;
goto ChangeUserPassword_Exit;
}
// trim off the '\' character to leave just the domain\computername so we can check against it.
*pch = '\0';
// compare the szTempFullUserName with the local computername.
if (0 == _wcsicmp(szRawComputerName, szTempFullUserName))
{
// the computername\username has a hardcoded computername in it.
// lets try to get only the username
// look szCopyOfusername is already set
}
else
{
// the local computer machine name
// and the specified username are different, so get out
// and don't even try to change this user\password since
// it's probably a domain\username
// return true -- saying that we did in fact change the passoword.
// we really didn't but we can't
iReturn = TRUE;
goto ChangeUserPassword_Exit;
}
}
// Make sure the computername has a \\ in front of it
if ( szRawComputerName[0] != '\\' )
{
hr = StringCchCopyW(szComputerName, ARRAYSIZE(szComputerName), L"\\\\");
if ( FAILED(hr) )
{
iReturn = FALSE;
goto ChangeUserPassword_Exit;
}
}
hr = StringCchCatW(szComputerName, ARRAYSIZE(szComputerName), szRawComputerName);
if ( FAILED(hr) )
{
iReturn = FALSE;
goto ChangeUserPassword_Exit;
}
//
// administrative over-ride of existing password
//
// by this time szCopyOfUserName
// should not look like mycomputername\username but it should look like username.
pi1003.usri1003_password = szNewPassword;
nas = NetUserSetInfo(
szComputerName, // computer name
szCopyOfUserName, // username
1003, // info level
(LPBYTE)&pi1003, // new info
NULL
);
if(nas != NERR_Success)
{
iReturn = FALSE;
goto ChangeUserPassword_Exit;
}
ChangeUserPassword_Exit:
//iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("ChangeUserPassword().End.Ret=%d"),iReturn));
return iReturn;
}
//
// Create InternetGuest Account
//
BOOL
CreateUser(
LPCTSTR szUsername,
LPCTSTR szPassword,
LPCTSTR szComment,
LPCTSTR szFullName,
BOOL fSpecialiWamAccount)
{
INT err = NERR_Success;
BYTE *pBuffer;
WCHAR defGuest[UNLEN+1];
TCHAR defGuestGroup[GNLEN+1];
WCHAR wchGuestGroup[GNLEN+1];
WCHAR wchUsername[UNLEN+1];
WCHAR wchPassword[LM20_PWLEN+1];
BOOL fRet;
fRet = GetGuestUserName(defGuest, UNLEN+1);
if ( !fRet )
{
return FALSE;
}
GetGuestGrpName(defGuestGroup);
SecureZeroMemory((PVOID)wchUsername, sizeof(wchUsername));
SecureZeroMemory((PVOID)wchPassword, sizeof(wchPassword));
wcsncpy(wchGuestGroup, defGuestGroup, GNLEN);
wcsncpy(wchUsername, szUsername, UNLEN);
wcsncpy(wchPassword, szPassword, LM20_PWLEN);
err = NetUserGetInfo( NULL, defGuest, 3, &pBuffer );
if ( err == NERR_Success )
{
WCHAR wchComment[MAXCOMMENTSZ+1];
WCHAR wchFullName[UNLEN+1];
memset((PVOID)wchComment, 0, sizeof(wchComment));
memset((PVOID)wchFullName, 0, sizeof(wchFullName));
wcsncpy(wchComment, szComment, MAXCOMMENTSZ);
wcsncpy(wchFullName, szFullName, UNLEN);
USER_INFO_3 *lpui3 = (USER_INFO_3 *)pBuffer;
lpui3->usri3_name = wchUsername;
lpui3->usri3_password = wchPassword;
lpui3->usri3_flags &= ~ UF_ACCOUNTDISABLE;
lpui3->usri3_flags |= UF_DONT_EXPIRE_PASSWD;
lpui3->usri3_acct_expires = TIMEQ_FOREVER;
lpui3->usri3_comment = wchComment;
lpui3->usri3_usr_comment = wchComment;
lpui3->usri3_full_name = wchFullName;
lpui3->usri3_primary_group_id = DOMAIN_GROUP_RID_USERS;
DWORD parm_err;
err = NetUserAdd( NULL, 3, pBuffer, &parm_err );
if ( err != NERR_Success )
{
if ( err == NERR_UserExists )
{
// see if we can just change the password.
if (TRUE == ChangeUserPassword((LPTSTR) szUsername, (LPTSTR) szPassword))
{
err = NERR_Success;
}
}
}
NetApiBufferFree( pBuffer );
}
if ( err == NERR_Success )
{
// add it to the guests or IIS_WPG group
if (fSpecialiWamAccount)
{
RegisterAccountToLocalGroup(szUsername, IIS_WP_GROUP, TRUE);
}
else
{
RegisterAccountToLocalGroup(szUsername, TEXT("Guests"), TRUE);
}
// add certain user rights to this account
RegisterAccountUserRights(szUsername, TRUE, fSpecialiWamAccount);
}
return err == NERR_Success;
}
INT
DeleteGuestUser(
LPCTSTR szUsername)
{
INT err = NERR_Success;
BOOL fDisabled;
WCHAR wchUsername[UNLEN+1];
wcsncpy(wchUsername, szUsername, UNLEN);
if (FALSE == DoesUserExist(wchUsername,&fDisabled))
{
return err;
}
// remove it from the guests group
RegisterAccountToLocalGroup(szUsername, TEXT("Guests"), FALSE);
// remove certain user rights of this account
RegisterAccountUserRights(szUsername, FALSE, TRUE);
err = ::NetUserDel( TEXT(""), wchUsername );
return err;
}
#define MAX_REALISTIC_RESOURCE_LEN MAX_PATH
BOOL
CreateUserAccount(
LPTSTR pszAnonyName,
LPTSTR pszAnonyPass,
DWORD dwUserCommentResourceId,
DWORD dwUserFullNameResourceId,
BOOL fSpecicaliWamAccount)
{
BOOL fReturn = FALSE;
WCHAR pszComment[MAX_REALISTIC_RESOURCE_LEN];
WCHAR pszFullName[MAX_REALISTIC_RESOURCE_LEN];
HMODULE hBinary;
//
// First Load the Resources
//
hBinary = GetModuleHandle(TEXT("svcext"));
if (hBinary != NULL)
{
fReturn = LoadString(hBinary,
dwUserCommentResourceId,
pszComment,
MAX_REALISTIC_RESOURCE_LEN);
if (fReturn)
{
fReturn = LoadString(hBinary,
dwUserFullNameResourceId,
pszFullName,
MAX_REALISTIC_RESOURCE_LEN);
}
}
if (fReturn)
{
fReturn = CreateUser(pszAnonyName,
pszAnonyPass,
pszComment,
pszFullName,
fSpecicaliWamAccount
);
}
if (fReturn)
{
ChangeDCOMLaunchACL(pszAnonyName,
TRUE,
TRUE);
/* removed when fixing bug 355249
ChangeDCOMAccessACL(pszAnonyName,
TRUE,
TRUE);
*/
}
return fReturn;
}
typedef void (*P_SslGenerateRandomBits)( PUCHAR pRandomData, LONG size );
P_SslGenerateRandomBits ProcSslGenerateRandomBits = NULL;
BOOL
ValidatePassword(
IN LPCTSTR UserName,
IN LPCTSTR Domain,
IN LPCTSTR Password)
/*++
Routine Description:
Uses SSPI to validate the specified password
Arguments:
UserName - Supplies the user name
Domain - Supplies the user's domain
Password - Supplies the password
Return Value:
TRUE if the password is valid.
FALSE otherwise.
--*/
{
SECURITY_STATUS SecStatus;
SECURITY_STATUS AcceptStatus;
SECURITY_STATUS InitStatus;
CredHandle ClientCredHandle;
CredHandle ServerCredHandle;
BOOL ClientCredAllocated = FALSE;
BOOL ServerCredAllocated = FALSE;
CtxtHandle ClientContextHandle;
CtxtHandle ServerContextHandle;
TimeStamp Lifetime;
ULONG ContextAttributes;
PSecPkgInfo PackageInfo = NULL;
ULONG ClientFlags;
ULONG ServerFlags;
TCHAR TargetName[100];
SEC_WINNT_AUTH_IDENTITY_W AuthIdentity;
BOOL Validated = FALSE;
SecBufferDesc NegotiateDesc;
SecBuffer NegotiateBuffer;
SecBufferDesc ChallengeDesc;
SecBuffer ChallengeBuffer;
SecBufferDesc AuthenticateDesc;
SecBuffer AuthenticateBuffer;
AuthIdentity.User = (LPWSTR)UserName;
AuthIdentity.UserLength = lstrlenW(UserName);
AuthIdentity.Domain = (LPWSTR)Domain;
AuthIdentity.DomainLength = lstrlenW(Domain);
AuthIdentity.Password = (LPWSTR)Password;
AuthIdentity.PasswordLength = lstrlenW(Password);
AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
NegotiateBuffer.pvBuffer = NULL;
ChallengeBuffer.pvBuffer = NULL;
AuthenticateBuffer.pvBuffer = NULL;
//
// Get info about the security packages.
//
SecStatus = QuerySecurityPackageInfo( TEXT("NTLM"), &PackageInfo );
if ( SecStatus != STATUS_SUCCESS )
{
goto error_exit;
}
//
// Acquire a credential handle for the server side
//
SecStatus = AcquireCredentialsHandle(
NULL,
TEXT("NTLM"),
SECPKG_CRED_INBOUND,
NULL,
&AuthIdentity,
NULL,
NULL,
&ServerCredHandle,
&Lifetime );
if ( SecStatus != STATUS_SUCCESS )
{
goto error_exit;
}
ServerCredAllocated = TRUE;
//
// Acquire a credential handle for the client side
//
SecStatus = AcquireCredentialsHandle(
NULL, // New principal
TEXT("NTLM"),
SECPKG_CRED_OUTBOUND,
NULL,
&AuthIdentity,
NULL,
NULL,
&ClientCredHandle,
&Lifetime );
if ( SecStatus != STATUS_SUCCESS )
{
goto error_exit;
}
ClientCredAllocated = TRUE;
//
// Get the NegotiateMessage (ClientSide)
//
NegotiateDesc.ulVersion = 0;
NegotiateDesc.cBuffers = 1;
NegotiateDesc.pBuffers = &NegotiateBuffer;
NegotiateBuffer.cbBuffer = PackageInfo->cbMaxToken;
NegotiateBuffer.BufferType = SECBUFFER_TOKEN;
NegotiateBuffer.pvBuffer = LocalAlloc( 0, NegotiateBuffer.cbBuffer );
if ( NegotiateBuffer.pvBuffer == NULL )
{
goto error_exit;
}
ClientFlags = ISC_REQ_MUTUAL_AUTH | ISC_REQ_REPLAY_DETECT;
InitStatus = InitializeSecurityContext(
&ClientCredHandle,
NULL, // No Client context yet
NULL,
ClientFlags,
0, // Reserved 1
SECURITY_NATIVE_DREP,
NULL, // No initial input token
0, // Reserved 2
&ClientContextHandle,
&NegotiateDesc,
&ContextAttributes,
&Lifetime );
if ( !NT_SUCCESS(InitStatus) )
{
goto error_exit;
}
//
// Get the ChallengeMessage (ServerSide)
//
NegotiateBuffer.BufferType |= SECBUFFER_READONLY;
ChallengeDesc.ulVersion = 0;
ChallengeDesc.cBuffers = 1;
ChallengeDesc.pBuffers = &ChallengeBuffer;
ChallengeBuffer.cbBuffer = PackageInfo->cbMaxToken;
ChallengeBuffer.BufferType = SECBUFFER_TOKEN;
ChallengeBuffer.pvBuffer = LocalAlloc( 0, ChallengeBuffer.cbBuffer );
if ( ChallengeBuffer.pvBuffer == NULL )
{
goto error_exit;
}
ServerFlags = ASC_REQ_EXTENDED_ERROR;
AcceptStatus = AcceptSecurityContext(
&ServerCredHandle,
NULL, // No Server context yet
&NegotiateDesc,
ServerFlags,
SECURITY_NATIVE_DREP,
&ServerContextHandle,
&ChallengeDesc,
&ContextAttributes,
&Lifetime );
if ( !NT_SUCCESS(AcceptStatus) )
{
goto error_exit;
}
if (InitStatus != STATUS_SUCCESS)
{
//
// Get the AuthenticateMessage (ClientSide)
//
ChallengeBuffer.BufferType |= SECBUFFER_READONLY;
AuthenticateDesc.ulVersion = 0;
AuthenticateDesc.cBuffers = 1;
AuthenticateDesc.pBuffers = &AuthenticateBuffer;
AuthenticateBuffer.cbBuffer = PackageInfo->cbMaxToken;
AuthenticateBuffer.BufferType = SECBUFFER_TOKEN;
AuthenticateBuffer.pvBuffer = LocalAlloc( 0, AuthenticateBuffer.cbBuffer );
if ( AuthenticateBuffer.pvBuffer == NULL )
{
goto error_exit;
}
SecStatus = InitializeSecurityContext(
NULL,
&ClientContextHandle,
TargetName,
0,
0, // Reserved 1
SECURITY_NATIVE_DREP,
&ChallengeDesc,
0, // Reserved 2
&ClientContextHandle,
&AuthenticateDesc,
&ContextAttributes,
&Lifetime );
if ( !NT_SUCCESS(SecStatus) )
{
goto error_exit;
}
if (AcceptStatus != STATUS_SUCCESS)
{
//
// Finally authenticate the user (ServerSide)
//
AuthenticateBuffer.BufferType |= SECBUFFER_READONLY;
SecStatus = AcceptSecurityContext(
NULL,
&ServerContextHandle,
&AuthenticateDesc,
ServerFlags,
SECURITY_NATIVE_DREP,
&ServerContextHandle,
NULL,
&ContextAttributes,
&Lifetime );
if ( !NT_SUCCESS(SecStatus) )
{
goto error_exit;
}
Validated = TRUE;
}
}
error_exit:
if (ServerCredAllocated)
{
FreeCredentialsHandle( &ServerCredHandle );
}
if (ClientCredAllocated)
{
FreeCredentialsHandle( &ClientCredHandle );
}
//
// Final Cleanup
//
if ( NegotiateBuffer.pvBuffer != NULL )
{
(VOID) LocalFree( NegotiateBuffer.pvBuffer );
}
if ( ChallengeBuffer.pvBuffer != NULL )
{
(VOID) LocalFree( ChallengeBuffer.pvBuffer );
}
if ( AuthenticateBuffer.pvBuffer != NULL )
{
(VOID) LocalFree( AuthenticateBuffer.pvBuffer );
}
return(Validated);
}
DWORD
ChangeAppIDAccessACL(
LPTSTR AppID,
LPTSTR Principal,
BOOL SetPrincipal,
BOOL Permit)
{
HRESULT hr = S_OK;
TCHAR keyName [256];
DWORD err;
hr = StringCchCopy( keyName, ARRAYSIZE(keyName), TEXT("APPID\\") );
if ( FAILED(hr) )
{
return HRESULTTOWIN32( hr );
}
hr = StringCchCat( keyName, ARRAYSIZE(keyName), AppID );
if ( FAILED(hr) )
{
return HRESULTTOWIN32( hr );
}
if (SetPrincipal)
{
// There is not need to check the return value of RemovePrincipalFromNamedValueSD,
// because its failure is not important (for example DefaultAccessPermission might
// not exist). The important call that must succeed is AddPrincipalToNamedValueSD
RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
keyName,
TEXT("AccessPermission"),
Principal);
err = AddPrincipalToNamedValueSD (HKEY_CLASSES_ROOT,
keyName,
TEXT("AccessPermission"),
Principal,
Permit);
}
else
{
err = RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
keyName,
TEXT("AccessPermission"),
Principal);
}
return err;
}
DWORD
ChangeAppIDLaunchACL(
LPTSTR AppID,
LPTSTR Principal,
BOOL SetPrincipal,
BOOL Permit)
{
HRESULT hr = S_OK;
TCHAR keyName [256];
DWORD err;
hr = StringCchCopy( keyName, ARRAYSIZE(keyName), TEXT("APPID\\") );
if ( FAILED(hr) )
{
return HRESULTTOWIN32( hr );
}
hr = StringCchCat( keyName, ARRAYSIZE(keyName), AppID );
if ( FAILED(hr) )
{
return HRESULTTOWIN32( hr );
}
if (SetPrincipal)
{
// There is not need to check the return value of RemovePrincipalFromNamedValueSD,
// because its failure is not important (for example DefaultAccessPermission might
// not exist). The important call that must succeed is AddPrincipalToNamedValueSD
RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
keyName,
TEXT("LaunchPermission"),
Principal);
err = AddPrincipalToNamedValueSD (HKEY_CLASSES_ROOT,
keyName,
TEXT("LaunchPermission"),
Principal,
Permit);
}
else
{
err = RemovePrincipalFromNamedValueSD (HKEY_CLASSES_ROOT,
keyName,
TEXT("LaunchPermission"),
Principal);
}
return err;
}
//
// Function will open the metabase and check the iusr_ and iwam_ usernames.
// it will check if the names are still valid and if the passwords are still valid.
//
VOID
UpdateAnonymousUser(
IMDCOM *pcCom,
LPTSTR pszPath,
DWORD dwUserMetaId,
DWORD dwPasswordMetaId,
DWORD dwUserCommentResourceId,
DWORD dwUserFullNameResourceId,
LPTSTR pszDefaultUserNamePrefix,
USERNAME_STRING_TYPE ustSyncName,
PASSWORD_STRING_TYPE pstSyncPass,
BOOL fPerformPasswordValidate)
{
HRESULT hr = S_OK;
USERNAME_STRING_TYPE ustAnonyName = L"\0";
PASSWORD_STRING_TYPE pstAnonyPass = L"\0";
GUFM_RETURN gufmTemp;
BOOL fRet;
BOOL fExistence;
BOOL fDisabled;
BOOL fUpdateComApplications = FALSE;
LPTSTR pstrRightsFor_IUSR[] =
{
L"SeInteractiveLogonRight",
L"SeNetworkLogonRight",
L"SeBatchLogonRight"
};
LPTSTR pstrRightsFor_IWAM[] =
{
L"SeNetworkLogonRight",
L"SeBatchLogonRight",
L"SeAssignPrimaryTokenPrivilege",
L"SeIncreaseQuotaPrivilege"
};
//
// Get the WAM username and password
//
gufmTemp = GetUserFromMetabase(pcCom,
pszPath,
dwUserMetaId,
dwPasswordMetaId,
ustAnonyName,
pstAnonyPass);
//
// If the metabase path doesn't exist, then
// service doesn't exist, punt
// If ID doesn't exist in the metabase, then punt, assume they
// don't want an anonymous User. We may want to revisit this.
//
//
if ((gufmTemp != GUFM_NO_PATH) && (gufmTemp != GUFM_NO_USER_ID))
{
BOOL fCreateAccount = FALSE;
//
// See if this is our default account. Otherwise do nothing.
//
if (_wcsnicmp(pszDefaultUserNamePrefix,
ustAnonyName,
wcslen(pszDefaultUserNamePrefix)) == 0)
{
// Check if this user actually exists...
fExistence = DoesUserExist(ustAnonyName,&fDisabled);
if (fExistence)
{
if (fDisabled)
{
fCreateAccount = FALSE;
if (!g_eventLogForAccountRecreation)
{
CreateEventLogObject ();
}
if (g_eventLogForAccountRecreation)
{
CHAR szAnsiUserName[MAX_PATH];
const CHAR *pszUserNames[1];
if (! WideCharToMultiByte(CP_ACP,
0,
ustAnonyName,
-1,
szAnsiUserName,
MAX_PATH-1,
NULL,
NULL))
{
SecureZeroMemory(szAnsiUserName, sizeof(szAnsiUserName));
}
pszUserNames[0] = szAnsiUserName;
g_eventLogForAccountRecreation->LogEvent(
INET_SVC_ACCOUNT_DISABLED,
1,
pszUserNames,
0 );
}
}
else
{
if (gufmTemp != GUFM_NO_PASSWORD)
{
DBG_ASSERT(gufmTemp == GUFM_SUCCESS);
if (fPerformPasswordValidate)
{
BOOL fCheckPassword = TRUE;
//
// Make sure this is the same password as other
// instances of this account. If not, set it.
//
if ((pstSyncPass[0] != (TCHAR)'\0') &&
(_wcsicmp(ustSyncName, ustAnonyName) == 0))
{
if (wcscmp(pstSyncPass,
pstAnonyPass) != 0)
{
//
// Passwords are different.
//
if (WritePasswordToMetabase(pcCom,
pszPath,
dwPasswordMetaId,
pstSyncPass))
{
// We already checked there is enough speace in pstAnonyPass
hr = StringCchCopy( pstAnonyPass, ARRAYSIZE(pstAnonyPass), pstSyncPass );
DBG_ASSERT( SUCCEEDED(hr) );
}
else
{
fCheckPassword = FALSE;
}
}
}
if (fCheckPassword)
{
if (ValidatePassword(ustAnonyName,
TEXT(""),
pstAnonyPass))
{
// thats a good case account is ok, do nothing there
}
else
{
// we comment out DeleteGuestUser because we try to change pswd on that user
// DeleteGuestUser(ustAnonyName);
fCreateAccount = TRUE;
}
}
}
//
// Set the sync password here
//
hr = StringCbCopy( pstSyncPass, sizeof(PASSWORD_STRING_TYPE), pstAnonyPass );
DBG_ASSERT( SUCCEEDED(hr) );
hr = StringCbCopy( ustSyncName, sizeof(USERNAME_STRING_TYPE), ustAnonyName );
DBG_ASSERT( SUCCEEDED(hr) );
}
}
}
else
{
fCreateAccount = TRUE;
}
if (fCreateAccount)
{
//
// The user does not exist, so let's create it.
// Make sure there's a password first.
//
if (fCreateAccount)
{
if (MD_WAM_USER_NAME == dwUserMetaId)
{
fRet = MakeSureUserGetsCreated(ustAnonyName,pstAnonyPass,dwUserCommentResourceId,dwUserFullNameResourceId,TRUE);
if( fRet )
{
fUpdateComApplications = TRUE;
}
}
else
{
fRet = MakeSureUserGetsCreated(ustAnonyName,pstAnonyPass,dwUserCommentResourceId,dwUserFullNameResourceId,FALSE);
}
if (!g_eventLogForAccountRecreation)
{
CreateEventLogObject ();
}
if (g_eventLogForAccountRecreation)
{
CHAR szAnsiUserName[MAX_PATH];
const CHAR *pszUserNames[1];
if (! WideCharToMultiByte(CP_ACP,
0,
ustAnonyName,
-1,
szAnsiUserName,
MAX_PATH-1,
NULL,
NULL))
{
SecureZeroMemory(szAnsiUserName, sizeof(szAnsiUserName));
}
pszUserNames[0] = szAnsiUserName;
// if succeded to recreate an account then log an event
if (fRet)
{
g_eventLogForAccountRecreation->LogEvent(
INET_SVC_ACCOUNT_RECREATED,
1,
pszUserNames,
0 );
}
else
{
// if the creation of the account failed, then log that too.
g_eventLogForAccountRecreation->LogEvent(
INET_SVC_ACCOUNT_CREATE_FAILED,
1,
pszUserNames,
0 );
}
}
if (dwUserMetaId == MD_WAM_USER_NAME)
{
ChangeAppIDLaunchACL(TEXT("{9209B1A6-964A-11D0-9372-00A0C9034910}"),
ustAnonyName,
TRUE,
TRUE);
ChangeAppIDAccessACL(TEXT("{9209B1A6-964A-11D0-9372-00A0C9034910}"),
ustAnonyName,
TRUE,
TRUE);
}
} // fCreateAccount == TRUE
} // fCreateAccount == TRUE
//
// check if user has enough rights otherwise add some (bug 361833)
//
if (wcscmp(pszDefaultUserNamePrefix,TEXT("IUSR_")) == 0)
{
UpdateUserRights (ustAnonyName,pstrRightsFor_IUSR,sizeof(pstrRightsFor_IUSR)/sizeof(LPTSTR));
}
else if (wcscmp(pszDefaultUserNamePrefix,TEXT("IWAM_")) == 0)
{
UpdateUserRights (ustAnonyName,pstrRightsFor_IWAM,sizeof(pstrRightsFor_IWAM)/sizeof(LPTSTR));
}
// Update the com applications with the new wam user information
if( fUpdateComApplications )
{
DWORD dwThreadID = 0;
HANDLE hThread = NULL;
COM_APP_THREAD_STRUCT * pUpdateComAppInfo = NULL;
pUpdateComAppInfo = new COM_APP_THREAD_STRUCT;
if (pUpdateComAppInfo)
{
if ( wcslen(ustAnonyName) < (sizeof(pUpdateComAppInfo->ustWamUserName) / sizeof(WCHAR)) )
{
hr = StringCchCopy(pUpdateComAppInfo->ustWamUserName, ARRAYSIZE(pUpdateComAppInfo->ustWamUserName), ustAnonyName);
DBG_ASSERT( SUCCEEDED(hr) );
if ( wcslen(pstAnonyPass) < (sizeof(pUpdateComAppInfo->pstWamUserPass) / sizeof(WCHAR)) )
{
StringCchCopy(pUpdateComAppInfo->pstWamUserPass, ARRAYSIZE(pUpdateComAppInfo->pstWamUserPass), pstAnonyPass);
hThread = CreateThread(NULL,
0,
UpdateComApplicationsThread,
(PVOID) pUpdateComAppInfo,
0,
&dwThreadID);
if (hThread != NULL)
{
CloseHandle(hThread);
}
else
{
if (pUpdateComAppInfo)
{
delete pUpdateComAppInfo;
pUpdateComAppInfo=NULL;
}
}
}
}
}
}
}
else
{
// This is not one of our accouts.
// in other words -- it doesn't start with
// iusr_ or iwam_
//
// however there is a problem here.
//
// on machines that are made to be replica domain controllers or
// backup domain controllers, when dcpromo is run to create those types
// of machines, all the local accounts are wiped out.
//
// this is fine if the usernames are iusr_ or iwam_, since they are just
// re-created in the above code (or the user is warned that they were unable
// to be crated). however in the case where these are
// user created accounts, the user has no way of knowing that
// they're iusr/iwam accounts have been hosed.
//
// the code here is just to warn the user of that fact.
if (TRUE == IsDomainController())
{
// check if they are valid.
// Check if this user actually exists...
fExistence = DoesUserExist(ustAnonyName,&fDisabled);
if (!fExistence)
{
if (!fDisabled)
{
// the user doesn't exist
// log SOMETHING at least
if (!g_eventLogForAccountRecreation)
{
CreateEventLogObject ();
}
if (g_eventLogForAccountRecreation)
{
CHAR szAnsiUserName[MAX_PATH];
const CHAR *pszUserNames[1];
if (! WideCharToMultiByte(CP_ACP,
0,
ustAnonyName,
-1,
szAnsiUserName,
MAX_PATH-1,
NULL,
NULL))
{
SecureZeroMemory(szAnsiUserName, sizeof(szAnsiUserName));
}
pszUserNames[0] = szAnsiUserName;
g_eventLogForAccountRecreation->LogEvent(
INET_SVC_ACCOUNT_NOT_EXIST,
1,
pszUserNames,
0 );
}
}
}
}
}
}
}
HRESULT
CreateGroup(
LPWSTR szGroupName,
LPWSTR szGroupComment)
{
HRESULT hr = S_OK;
NET_API_STATUS dwRes;
LOCALGROUP_INFO_1 MyLocalGroup;
const DWORD dwMaxCount = 20;
DWORD dwCount = 0;
MyLocalGroup.lgrpi1_name = szGroupName;
MyLocalGroup.lgrpi1_comment = szGroupComment;
do
{
dwRes = NetLocalGroupAdd(NULL, 1, (LPBYTE)&MyLocalGroup, NULL);
if(dwRes != NERR_Success &&
dwRes != NERR_GroupExists &&
dwRes != ERROR_ALIAS_EXISTS)
{
hr = HRESULT_FROM_WIN32(dwRes);
//
// if this is a domain controller.
// we have to wait for the domain controller
// replication to be finished, otherwise The CreateUserAccount
// call will fail
//
// if we failed to create the user
// it could be because this is a DC and we need
// to wait for the sysvol to be ready.
Sleep(10000);
WaitForDCAvailability();
}
else
{
hr = S_OK;
}
}
while ((FAILED(hr)) && (++dwCount < dwMaxCount));
return hr;
}
VOID
UpdateUsers(
BOOL fRestore /* = FALSE */)
{
HRESULT hresTemp;
IMDCOM *pcCom;
BOOL fPerformUpdate = TRUE;
BOOL fPerformPasswordValidate = FALSE;
HKEY hkRegistryKey = NULL;
DWORD dwRegReturn;
DWORD dwBuffer;
DWORD dwSize;
DWORD dwType;
HRESULT hr;
//
// First get the metabase interface
//
dwRegReturn = RegOpenKey(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\InetStp",
&hkRegistryKey);
if (dwRegReturn == ERROR_SUCCESS)
{
dwSize = sizeof(dwBuffer);
dwRegReturn = RegQueryValueEx(hkRegistryKey,
L"DisableUserAccountRestore",
NULL,
&dwType,
(BYTE *)&dwBuffer,
&dwSize);
if ((dwRegReturn == ERROR_SUCCESS) && dwType == (REG_DWORD))
{
fPerformUpdate = FALSE;
}
if (fPerformUpdate)
{
// we are doing the check to see if the user exists...
// see if we need to verify that the password is ssynced as well...
dwRegReturn = RegQueryValueEx(hkRegistryKey,
L"EnableUserAccountRestorePassSync",
NULL,
&dwType,
(BYTE *)&dwBuffer,
&dwSize);
if ((dwRegReturn == ERROR_SUCCESS) && dwType == (REG_DWORD))
{
fPerformPasswordValidate = TRUE;
}
}
RegCloseKey( hkRegistryKey );
}
if( fRestore )
{
fPerformPasswordValidate = TRUE;
}
if (fPerformUpdate)
{
hresTemp = CoCreateInstance(CLSID_MDCOM,
NULL,
CLSCTX_SERVER,
IID_IMDCOM,
(void**) &pcCom);
if (SUCCEEDED(hresTemp))
{
//
// Make sure the IIS_WPG group exists
//
hr = CreateGroup(IIS_WP_GROUP, L"IIS Worker Process Group");
DBGPRINTF((DBG_CONTEXT, "Called into CreateGroup, hr %x\n", hr));
LPTSTR pstrRightsFor_IIS_WPG[] = { L"SeBatchLogonRight", L"SeImpersonatePrivilege" };
UpdateUserRights(IIS_WP_GROUP,pstrRightsFor_IIS_WPG,sizeof(pstrRightsFor_IIS_WPG)/sizeof(LPTSTR));
PASSWORD_STRING_TYPE pstAnonyPass;
USERNAME_STRING_TYPE ustAnonyName;
pstAnonyPass[0] = (TCHAR)'\0';
ustAnonyName[0] = (TCHAR)'\0';
UpdateAnonymousUser(pcCom,
TEXT("LM/W3SVC"),
MD_WAM_USER_NAME,
MD_WAM_PWD,
IDS_WAMUSER_COMMENT,
IDS_WAMUSER_FULLNAME,
TEXT("IWAM_"),
ustAnonyName,
pstAnonyPass,
fPerformPasswordValidate);
pstAnonyPass[0] = (TCHAR)'\0';
ustAnonyName[0] = (TCHAR)'\0';
UpdateAnonymousUser(pcCom,
TEXT("LM/W3SVC"),
MD_ANONYMOUS_USER_NAME,
MD_ANONYMOUS_PWD,
IDS_USER_COMMENT,
IDS_USER_FULLNAME,
TEXT("IUSR_"),
ustAnonyName,
pstAnonyPass,
fPerformPasswordValidate);
//
// At this point pstAnonyPass should contain the web server password.
//
UpdateAnonymousUser(pcCom,
TEXT("LM/MSFTPSVC"),
MD_ANONYMOUS_USER_NAME,
MD_ANONYMOUS_PWD,
IDS_USER_COMMENT,
IDS_USER_FULLNAME,
TEXT("IUSR_"),
ustAnonyName,
pstAnonyPass,
fPerformPasswordValidate);
hr = UpdateAdminAcl(pcCom,
L"/",
IIS_WP_GROUP);
DBGPRINTF((DBG_CONTEXT, "Called into UpdateAdminAcl, hr %x\n", hr));
hr = UpdateAdminAcl(pcCom,
L"/",
L"NT Authority\\Network Service");
DBGPRINTF((DBG_CONTEXT, "Called into UpdateAdminAcl, hr %x\n", hr));
hr = UpdateAdminAcl(pcCom,
L"/",
L"NT Authority\\Local Service");
DBGPRINTF((DBG_CONTEXT, "Called into UpdateAdminAcl, hr %x\n", hr));
pcCom->Release();
}
}
if (g_eventLogForAccountRecreation)
{
delete g_eventLogForAccountRecreation;
g_eventLogForAccountRecreation = NULL;
}
}
void
DumpAdminACL(
PSECURITY_DESCRIPTOR pSD)
{
HRESULT hr = S_OK;
BOOL b = FALSE;
BOOL bDaclPresent = FALSE;
BOOL bDaclDefaulted = FALSE;
PACL pDacl = NULL;
ACCESS_ALLOWED_ACE* pAce;
DBGPRINTF((DBG_CONTEXT, "Dumping AdminAcl %p\n", pSD));
b = GetSecurityDescriptorDacl(pSD, &bDaclPresent, &pDacl, &bDaclDefaulted);
if (b)
{
DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:ACE count: %d\n", (int)pDacl->AceCount));
// now check if SID's ACE is there
for (int i = 0; i < pDacl->AceCount; i++)
{
if (!GetAce(pDacl, i, (LPVOID *) &pAce))
{
DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:GetAce failed with 0x%x\n", GetLastError()));
continue;
}
if (IsValidSid( (PSID) &(pAce->SidStart) ) )
{
LPTSTR pszSid;
LPCTSTR ServerName = NULL; // local machine
DWORD cbName = UNLEN+1;
TCHAR ReferencedDomainName[200];
DWORD cbReferencedDomainName = sizeof(ReferencedDomainName);
SID_NAME_USE sidNameUse = SidTypeUser;
TCHAR szUserName[UNLEN + 1];
// dump out the sid in string format
if ( ConvertSidToStringSid( (PSID)&(pAce->SidStart), &pszSid ) )
{
hr = StringCchCopy(szUserName, ARRAYSIZE(szUserName), L"(unknown...)");
DBG_ASSERT( SUCCEEDED(hr) );
if (LookupAccountSid(ServerName, (PSID) &(pAce->SidStart), szUserName, &cbName, ReferencedDomainName, &cbReferencedDomainName, &sidNameUse))
{
// echo to logfile
DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:Sid[%i]=%S,%S,0x%x,0x%x,0x%x,0x%x\n",i,
pszSid,
szUserName,
pAce->Header.AceType,
pAce->Header.AceFlags,
pAce->Header.AceSize,
pAce->Mask
));
}
else
{
DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:Sid[%i]=%S='%S'\n",i,pszSid,szUserName));
}
LocalFree(LocalHandle(pszSid));
}
}
else
{
DBGPRINTF((DBG_CONTEXT, "DumpAdminACL:IsValidSid failed with 0x%x\n", GetLastError()));
}
}
}
}
BOOL
MakeAbsoluteCopyFromRelative(
PSECURITY_DESCRIPTOR psdOriginal,
PSECURITY_DESCRIPTOR* ppsdNew)
{
// we have to find out whether the original is already self-relative
SECURITY_DESCRIPTOR_CONTROL sdc = 0;
PSECURITY_DESCRIPTOR psdAbsoluteCopy = NULL;
DWORD dwRevision = 0;
DWORD cb = 0;
PACL Dacl = NULL;
PACL Sacl = NULL;
PSID Owner = NULL;
PSID Group = NULL;
DWORD dwDaclSize = 0;
DWORD dwSaclSize = 0;
DWORD dwOwnerSize = 0;
DWORD dwPrimaryGroupSize = 0;
if( !IsValidSecurityDescriptor( psdOriginal ) )
{
goto cleanup;
}
if( !GetSecurityDescriptorControl( psdOriginal, &sdc, &dwRevision ) )
{
goto cleanup;
}
if( sdc & SE_SELF_RELATIVE )
{
// the original is in self-relative format, build an absolute copy
// get required buffer size
cb = 0;
MakeAbsoluteSD(
psdOriginal, // address of self-relative SD
psdAbsoluteCopy, // address of absolute SD
&cb, // address of size of absolute SD
NULL, // address of discretionary ACL
&dwDaclSize, // address of size of discretionary ACL
NULL, // address of system ACL
&dwSaclSize, // address of size of system ACL
NULL, // address of owner SID
&dwOwnerSize, // address of size of owner SID
NULL, // address of primary-group SID
&dwPrimaryGroupSize // address of size of group SID
);
// alloc the memory
psdAbsoluteCopy = (PSECURITY_DESCRIPTOR) malloc( cb );
Dacl = (PACL) malloc( dwDaclSize );
Sacl = (PACL) malloc( dwSaclSize );
Owner = (PSID) malloc( dwOwnerSize );
Group = (PSID) malloc( dwPrimaryGroupSize );
if(NULL == psdAbsoluteCopy ||
NULL == Dacl ||
NULL == Sacl ||
NULL == Owner ||
NULL == Group )
{
goto cleanup;
}
// make the copy
if( !MakeAbsoluteSD(
psdOriginal, // address of self-relative SD
psdAbsoluteCopy, // address of absolute SD
&cb, // address of size of absolute SD
Dacl, // address of discretionary ACL
&dwDaclSize, // address of size of discretionary ACL
Sacl, // address of system ACL
&dwSaclSize, // address of size of system ACL
Owner, // address of owner SID
&dwOwnerSize, // address of size of owner SID
Group, // address of primary-group SID
&dwPrimaryGroupSize // address of size of group SID
) )
{
goto cleanup;
}
}
else
{
// the original is in absolute format, fail
goto cleanup;
}
// paranoia check
if( !IsValidSecurityDescriptor( psdAbsoluteCopy ) )
{
goto cleanup;
}
if( !IsValidSecurityDescriptor( psdOriginal ) )
{
goto cleanup;
}
*ppsdNew = psdAbsoluteCopy;
return(TRUE);
cleanup:
if( Dacl != NULL )
{
free((PVOID) Dacl );
Dacl = NULL;
}
if( Sacl != NULL )
{
free((PVOID) Sacl );
Sacl = NULL;
}
if( Owner != NULL )
{
free((PVOID) Owner );
Owner = NULL;
}
if( Group != NULL )
{
free((PVOID) Group );
Group = NULL;
}
if( psdAbsoluteCopy != NULL )
{
free((PVOID) psdAbsoluteCopy );
psdAbsoluteCopy = NULL;
}
*ppsdNew = NULL;
return (FALSE);
}
BOOL
AddUserAccessToSD(
IN PSECURITY_DESCRIPTOR pSd,
IN PSID pSid,
IN DWORD NewAccess,
IN UCHAR TheAceType,
OUT PSECURITY_DESCRIPTOR *ppSdNew)
{
ULONG i;
BOOL bReturn = FALSE;
BOOL Result;
BOOL DaclPresent;
BOOL DaclDefaulted;
DWORD Length;
DWORD NewAclLength;
ACCESS_ALLOWED_ACE* OldAce;
PACE_HEADER NewAce = NULL;
ACL_SIZE_INFORMATION AclInfo;
PACL Dacl = NULL;
PACL NewDacl = NULL;
PACL NewAceDacl = NULL;
PSECURITY_DESCRIPTOR NewSD = NULL;
PSECURITY_DESCRIPTOR OldSD = NULL;
PSECURITY_DESCRIPTOR outpSD = NULL;
DWORD cboutpSD = 0;
BOOL fAceForGroupPresent = FALSE;
DWORD dwMask;
OldSD = pSd;
// only do if the ace is allowed/denied
if (ACCESS_ALLOWED_ACE_TYPE != TheAceType && ACCESS_DENIED_ACE_TYPE != TheAceType)
{
goto Exit;
}
// Convert SecurityDescriptor to absolute format. It generates
// a new SecurityDescriptor for its output which we must free.
if ( !MakeAbsoluteCopyFromRelative(OldSD, &NewSD) )
{
goto Exit;
}
// Must get DACL pointer from new (absolute) SD
if(!GetSecurityDescriptorDacl(NewSD,&DaclPresent,&Dacl,&DaclDefaulted))
{
goto Exit;
}
// If no DACL, no need to add the user since no DACL
// means all accesss
if( !DaclPresent )
{
bReturn = TRUE;
goto Exit;
}
// Code can return DaclPresent, but a NULL which means
// a NULL Dacl is present. This allows all access to the object.
if( Dacl == NULL )
{
bReturn = TRUE;
goto Exit;
}
// Get the current ACL's size
if( !GetAclInformation(Dacl,&AclInfo,sizeof(AclInfo),AclSizeInformation) )
{
goto Exit;
}
// Check if access is already there
// --------------------------------
// Check to see if this SID already exists in there
// if it does (and it has the right access we want) then forget it, we don't have to do anything more.
for (i = 0; i < AclInfo.AceCount; i++)
{
ACE_HEADER *pAceHeader;
ACCESS_ALLOWED_ACE* pAce = NULL;
if (!GetAce(Dacl, i, (LPVOID *) &pAce))
{
goto Exit;
}
pAceHeader = (ACE_HEADER *)pAce;
// check if group sid is already there
if (EqualSid((PSID) &(pAce->SidStart), pSid))
{
if (pAceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
// If the correct access is present, return success
if ((pAce->Mask & NewAccess) == NewAccess)
{
bReturn = TRUE;
goto Exit;
}
else
{
// the ace that exist doesn't have the permissions that we want.
// If an ACE for our SID exists, we just need to bump
// up the access level instead of creating a new ACE
fAceForGroupPresent = TRUE;
}
}
break;
}
}
// If we have to create a new ACE
// (because our user isn't listed in the existing ACL)
// then let's Create a new ACL to put the new access allowed ACE on
// --------------------------------
if (!fAceForGroupPresent)
{
NewAclLength = sizeof(ACL) +
sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) +
GetLengthSid( pSid );
NewAceDacl = (PACL) LocalAlloc( LMEM_FIXED, NewAclLength );
if ( NewAceDacl == NULL )
{
goto Exit;
}
if(!InitializeAcl( NewAceDacl, NewAclLength, ACL_REVISION ))
{
goto Exit;
}
if (ACCESS_DENIED_ACE_TYPE == TheAceType)
{
Result = AddAccessDeniedAce(NewAceDacl,ACL_REVISION,NewAccess,pSid);
}
else
{
Result = AddAccessAllowedAce(NewAceDacl,ACL_REVISION,NewAccess,pSid);
}
if( !Result )
{
goto Exit;
}
// Grab the 1st ace from the Newly created Dacl
if(!GetAce( NewAceDacl, 0, (void **)&NewAce ))
{
goto Exit;
}
// add CONTAINER_INHERIT_ACE TO AceFlags
//NewAce->AceFlags |= CONTAINER_INHERIT_ACE;
Length = AclInfo.AclBytesInUse + NewAce->AceSize;
}
else
{
Length = AclInfo.AclBytesInUse;
}
// Allocate new DACL
NewDacl = (PACL) LocalAlloc( LMEM_FIXED, Length );
if(NewDacl == NULL)
{
goto Exit;
}
if(!InitializeAcl( NewDacl, Length, ACL_REVISION ))
{
goto Exit;
}
// Insert new ACE at the front of the new DACL
if (!fAceForGroupPresent)
{
if(!AddAce( NewDacl, ACL_REVISION, 0, NewAce, NewAce->AceSize ))
{
goto Exit;
}
}
// ----------------------------------------
// Read thru the old Dacl and get the ACE's
// add it to the new Dacl
// ----------------------------------------
for ( i = 0; i < AclInfo.AceCount; i++ )
{
ACE_HEADER *pAceHeader;
Result = GetAce( Dacl, i, (LPVOID*) &OldAce );
if( !Result )
{
goto Exit;
}
pAceHeader = (ACE_HEADER *)OldAce;
// If an ACE for our SID exists, we just need to bump
// up the access level instead of creating a new ACE
//
if (pAceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE)
{
dwMask = OldAce->Mask;
if (fAceForGroupPresent)
{
if (EqualSid((PSID) &(OldAce->SidStart), pSid))
{
dwMask = NewAccess | OldAce->Mask;
}
}
// now add ace to new dacl
Result = AddAccessAllowedAceEx(NewDacl, ACL_REVISION, OldAce->Header.AceFlags,dwMask,(PSID) &(OldAce->SidStart));
if( !Result )
{
goto Exit;
}
}
else
{
// copy denied or audit ace.
if (!AddAce(NewDacl, ACL_REVISION, 0xFFFFFFFF,OldAce, pAceHeader->AceSize ))
{
goto Exit;
}
}
}
// Set new DACL for Security Descriptor
if(!SetSecurityDescriptorDacl(NewSD,TRUE,NewDacl,FALSE))
{
goto Exit;
}
// The new SD is in absolute format. change it to Relative before we pass it back
cboutpSD = 0;
MakeSelfRelativeSD(NewSD, outpSD, &cboutpSD);
outpSD = (PSECURITY_DESCRIPTOR)GlobalAlloc(GPTR, cboutpSD);
if ( !outpSD )
{
goto Exit;
}
if (!MakeSelfRelativeSD(NewSD, outpSD, &cboutpSD))
{
goto Exit;
}
// The new SD is passed back in relative format,
*ppSdNew = outpSD;
bReturn = TRUE;
Exit:
if (NewSD)
{
free( NewSD );
NewSD = NULL;
}
if (NewDacl)
{
LocalFree( NewDacl );
NewDacl = NULL;
}
if (NewAceDacl)
{
LocalFree( NewAceDacl );
NewAceDacl = NULL;
}
return bReturn;
}
HRESULT
UpdateAdminAcl(
IMDCOM * pcCom,
LPCWSTR szPath,
LPCWSTR szAccountName)
/*++
Routine Description:
After DCPromo, need to update the AdminAcl in the metabase
--*/
{
METADATA_HANDLE hMetabase = NULL;
METADATA_RECORD mdrAdminAcl;
BUFFER buffSid;
BUFFER buffDomainName;
PSECURITY_DESCRIPTOR pOldSd = NULL;
PSECURITY_DESCRIPTOR pNewSd = NULL;
HRESULT hr;
DWORD dwMDGetDataLen;
DWORD cbSid;
DWORD cchDomainName;
SID_NAME_USE peUse;
DWORD AccessMask = ( MD_ACR_READ |
MD_ACR_UNSECURE_PROPS_READ |
MD_ACR_ENUM_KEYS );
hr = pcCom->ComMDOpenMetaObject( METADATA_MASTER_ROOT_HANDLE,
szPath,
METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
OPEN_TIMEOUT_VALUE,
&hMetabase
);
if( FAILED(hr) )
{
DBGPRINTF((DBG_CONTEXT, "Error opening metabase path %S, hr %x\n", szPath, hr));
goto cleanup;
}
MD_SET_DATA_RECORD_EXT( &mdrAdminAcl,
MD_ADMIN_ACL,
METADATA_INHERIT | METADATA_SECURE | METADATA_REFERENCE,
IIS_MD_UT_SERVER,
BINARY_METADATA,
0,
NULL
);
hr = pcCom->ComMDGetMetaData(hMetabase,
NULL,
&mdrAdminAcl,
&dwMDGetDataLen);
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Error retrieving data, hr %x\n", hr));
goto cleanup;
}
pOldSd = (PSECURITY_DESCRIPTOR)mdrAdminAcl.pbMDData;
//
// obtain the logon sid of the user/group
//
cbSid = buffSid.QuerySize();
cchDomainName = buffDomainName.QuerySize() / sizeof(WCHAR);
while(!LookupAccountName(NULL,
szAccountName,
buffSid.QueryPtr(),
&cbSid,
(LPWSTR)buffDomainName.QueryPtr(),
&cchDomainName,
&peUse))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
DBGPRINTF((DBG_CONTEXT, "Error retrieving account %S, hr %x\n", szAccountName, hr));
hr = HRESULT_FROM_WIN32(GetLastError());
goto cleanup;
}
if (!buffSid.Resize(cbSid) ||
!buffDomainName.Resize(cchDomainName * sizeof(WCHAR)))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto cleanup;
}
}
DumpAdminACL(pOldSd);
if (!AddUserAccessToSD(pOldSd,
buffSid.QueryPtr(),
AccessMask,
ACCESS_ALLOWED_ACE_TYPE,
&pNewSd))
{
hr = HRESULT_FROM_WIN32(GetLastError());
DBGPRINTF((DBG_CONTEXT, "Error adding user to AdminAcl, hr %x\n", hr));
goto cleanup;
}
if (pNewSd)
{
DumpAdminACL(pNewSd);
DWORD dwNewSd = GetSecurityDescriptorLength(pNewSd);
MD_SET_DATA_RECORD_EXT( &mdrAdminAcl,
MD_ADMIN_ACL,
METADATA_INHERIT | METADATA_SECURE | METADATA_REFERENCE,
IIS_MD_UT_SERVER,
BINARY_METADATA,
dwNewSd,
pNewSd
);
hr = pcCom->ComMDSetMetaData(hMetabase,
NULL,
&mdrAdminAcl);
if (FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Error setting adminacl, hr %x\n", hr));
goto cleanup;
}
}
cleanup:
if (pNewSd)
{
GlobalFree(pNewSd);
pNewSd = NULL;
}
if (hMetabase)
{
// Done with the metabase
pcCom->ComMDCloseMetaObject(hMetabase);
hMetabase = NULL;
}
return hr;
}
HRESULT
UpdateComApplications(
LPCTSTR szWamUserName,
LPCTSTR szWamUserPass)
/*++
Routine Description:
If the IWAM account has been modified, it is necessary to update the
the out of process com+ applications with the correct account
information. This routine will collect all the com+ applications and
reset the activation information.
Arguments:
szWamUserName - the new user name
szWamUserPass - the new user password
Note:
This routine is a royal pain in the butt. I take back
all the good things I may have said about com automation.
Return Value:
HRESULT - Return value from failed API call
- E_OUTOFMEMORY
- S_OK - everything worked
- S_FALSE - encountered a non-fatal error, unable
to reset at least one application.
--*/
{
HRESULT hr = S_OK;
BOOL fNoErrors = TRUE;
METADATA_HANDLE hMetabase = NULL;
WCHAR * pwszDataPaths = NULL;
DWORD cchDataPaths = 0;
BOOL fTryAgain;
DWORD cMaxApplications;
WCHAR * pwszCurrentPath;
STACK_BUFFER( bufMDPath, 64 );
WCHAR * pwszMDPath = NULL;
DWORD dwMDPathLen;
SAFEARRAY * psaApplications = NULL;
SAFEARRAYBOUND rgsaBound[1];
DWORD cApplications;
VARIANT varAppKey;
LONG rgIndices[1];
METADATA_RECORD mdrAppIsolated;
METADATA_RECORD mdrAppPackageId;
METADATA_RECORD mdrWamClsid;
DWORD dwAppIsolated;
WCHAR wszAppPackageId[ 40 ];
WCHAR wszWamClsid[ 40 ];
DWORD dwMDGetDataLen = 0;
ICOMAdminCatalog * pComCatalog = NULL;
ICatalogCollection *pComAppCollection = NULL;
ICatalogObject * pComApp = NULL;
LONG nAppsInCollection;
LONG iCurrentApp;
LONG nChanges;
BOOL fAppCreated = FALSE;
VARIANT varOldAppIdentity;
VARIANT varNewAppIdentity;
VARIANT varNewAppPassword;
IMDCOM * pcCom = NULL;
BSTR bstrApplications = NULL;
BSTR bstrIdentity = NULL;
BSTR bstrPassword = NULL;
DWORD cbMDPath;
// This is built unicode right now. Since all the com apis I need
// are unicode only I'm using wide characters here. I should get
// plenty of compiler errors if _UNICODE isn't defined, but just
// in case....
// DBG_ASSERT( sizeof(TCHAR) == sizeof(WCHAR) );
DBGPRINTF(( DBG_CONTEXT,
"Updating activation identity for out of process apps.\n"
));
// Init variants
VariantInit( &varAppKey );
VariantInit( &varOldAppIdentity );
VariantInit( &varNewAppIdentity );
VariantInit( &varNewAppPassword );
// Allocate the BSTRs
bstrApplications = SysAllocString( L"Applications" );
if ( bstrApplications == NULL )
{
hr = E_OUTOFMEMORY;
DBGERROR(( DBG_CONTEXT,
"Out of memory allocating BSTR\n"
));
goto cleanup;
}
bstrIdentity = SysAllocString( L"Identity" );
if ( bstrIdentity == NULL )
{
hr = E_OUTOFMEMORY;
DBGERROR(( DBG_CONTEXT,
"Out of memory allocating BSTR\n"
));
goto cleanup;
}
bstrPassword = SysAllocString( L"Password" );
if ( bstrPassword == NULL )
{
hr = E_OUTOFMEMORY;
DBGERROR(( DBG_CONTEXT,
"Out of memory allocating BSTR\n"
));
goto cleanup;
}
hr = CoCreateInstance(CLSID_MDCOM,
NULL,
CLSCTX_SERVER,
IID_IMDCOM,
(void**) &pcCom);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed CoCreateInstance CLSID_MDCOM (%08x)\n",
hr
));
goto cleanup;
}
//
// Get the applications to be reset by querying the metabase paths
//
hr = pcCom->ComMDOpenMetaObject( METADATA_MASTER_ROOT_HANDLE,
ROOTMDPath,
METADATA_PERMISSION_READ,
OPEN_TIMEOUT_VALUE,
&hMetabase
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to open metabase (%08x)\n",
hr
));
goto cleanup;
}
// Get the data paths string
fTryAgain = TRUE;
do
{
hr = pcCom->ComMDGetMetaDataPaths( hMetabase,
NULL,
MD_APP_PACKAGE_ID,
STRING_METADATA,
cchDataPaths,
pwszDataPaths,
&cchDataPaths
);
if( HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr )
{
delete[] pwszDataPaths;
pwszDataPaths = NULL;
pwszDataPaths = new WCHAR[cchDataPaths];
if( !pwszDataPaths )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
}
else
{
fTryAgain = FALSE;
}
}
while( fTryAgain );
//
// Done with the metabase for now
//
pcCom->ComMDCloseMetaObject(hMetabase);
hMetabase = NULL;
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to find metadata (%08x) Data(%d)\n",
hr,
MD_APP_PACKAGE_ID
));
goto cleanup;
}
else if (pwszDataPaths == NULL)
{
//
// If we found no isolated apps, make the path list an empty multisz
//
cchDataPaths = 1;
pwszDataPaths = new WCHAR[cchDataPaths];
if( !pwszDataPaths )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
pwszDataPaths[0] = L'\0';
}
// Determine the maximum number of applications
cMaxApplications = 1; // The pooled application
for( pwszCurrentPath = pwszDataPaths;
*pwszCurrentPath != L'\0';
pwszCurrentPath += wcslen(pwszCurrentPath) + 1
)
{
cMaxApplications++;
}
//
// Build a key array and load the com applications.
//
// Create an array to hold the keys
rgsaBound[0].cElements = cMaxApplications;
rgsaBound[0].lLbound = 0;
psaApplications = SafeArrayCreate( VT_VARIANT, 1, rgsaBound );
if( psaApplications == NULL )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
// Set the out of process pool application key
varAppKey.vt = VT_BSTR;
varAppKey.bstrVal = SysAllocString( W3_OOP_POOL_PACKAGE_ID );
if( !varAppKey.bstrVal )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
rgIndices[0] = 0;
hr = SafeArrayPutElement( psaApplications, rgIndices, &varAppKey );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed setting an element in a safe array (%08x)\n",
hr
));
goto cleanup;
}
// For each of the application paths determine if an out of process
// application is defined there and set the appropriate key into
// our array
MD_SET_DATA_RECORD_EXT( &mdrAppIsolated,
MD_APP_ISOLATED,
METADATA_NO_ATTRIBUTES,
ALL_METADATA,
DWORD_METADATA,
sizeof(DWORD),
(PBYTE)&dwAppIsolated
);
MD_SET_DATA_RECORD_EXT( &mdrAppPackageId,
MD_APP_PACKAGE_ID,
METADATA_NO_ATTRIBUTES,
ALL_METADATA,
STRING_METADATA,
sizeof(wszAppPackageId),
(PBYTE)wszAppPackageId
);
wszAppPackageId[0] = L'\0';
MD_SET_DATA_RECORD_EXT( &mdrWamClsid,
MD_APP_WAM_CLSID,
METADATA_NO_ATTRIBUTES,
ALL_METADATA,
STRING_METADATA,
sizeof(wszWamClsid),
(PBYTE)wszWamClsid
);
wszWamClsid[0] = L'\0';
// Go through each data path and set it into our array if
// it is an isolated application
cApplications = 1; // Actual # of applications - 1 for pool
for( pwszCurrentPath = pwszDataPaths;
*pwszCurrentPath != L'\0';
pwszCurrentPath += wcslen(pwszCurrentPath) + 1
)
{
if( hMetabase != NULL )
{
pcCom->ComMDCloseMetaObject(hMetabase);
hMetabase = NULL;
}
//
// 20 is the size of L"/LM/W3SVC"
//
cbMDPath = (DWORD)(wcslen( pwszCurrentPath ) * sizeof( WCHAR )) + 20;
if ( !bufMDPath.Resize( cbMDPath ) )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
pwszMDPath = ( WCHAR* )bufMDPath.QueryPtr();
hr = StringCbCopyW( pwszMDPath, cbMDPath, ROOTMDPath );
if ( FAILED(hr) )
{
goto cleanup;
}
hr = StringCbCopyW( pwszMDPath, cbMDPath, pwszCurrentPath );
if ( FAILED(hr) )
{
goto cleanup;
}
//
// Get rid of the trailing '/' or '\\'
//
dwMDPathLen = (DWORD)wcslen( pwszMDPath );
if( pwszMDPath[ dwMDPathLen - 1 ] == L'\\' ||
pwszMDPath[ dwMDPathLen - 1 ] == L'/' )
{
pwszMDPath[ dwMDPathLen - 1 ] = L'\0';
}
hr = pcCom->ComMDOpenMetaObject( METADATA_MASTER_ROOT_HANDLE,
pwszMDPath,
METADATA_PERMISSION_READ,
OPEN_TIMEOUT_VALUE,
&hMetabase
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to open metabase (%08x)\n",
hr
));
goto cleanup;
}
hr = pcCom->ComMDGetMetaData( hMetabase,
NULL,
&mdrAppIsolated,
&dwMDGetDataLen
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get data from the metabase (%08x)"
" Path(%S) Data(%d)\n",
hr,
pwszMDPath,
mdrAppIsolated.dwMDIdentifier
));
fNoErrors = FALSE;
continue;
}
// Is the application out of process
if( dwAppIsolated == 1 )
{
// Get the application id
hr = pcCom->ComMDGetMetaData( hMetabase,
NULL,
&mdrAppPackageId,
&dwMDGetDataLen
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get data from the metabase (%08x)"
" Path(%S) Data(%d)\n",
hr,
pwszMDPath,
mdrAppPackageId.dwMDIdentifier
));
fNoErrors = FALSE;
continue;
}
// Get the wam class id
hr = pcCom->ComMDGetMetaData( hMetabase,
NULL,
&mdrWamClsid,
&dwMDGetDataLen
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get data from the metabase (%08x)"
" Path(%S) Data(%d)\n",
hr,
pwszMDPath,
mdrWamClsid.dwMDIdentifier
));
fNoErrors = FALSE;
continue;
}
//
// Close meta object since we may need to write to it after we
// create a new COM+ application.
//
pcCom->ComMDCloseMetaObject(hMetabase);
hMetabase = NULL;
if ( g_pfnCreateCOMPlusApplication )
{
hr = g_pfnCreateCOMPlusApplication( pwszMDPath,
wszAppPackageId,
wszWamClsid,
&fAppCreated );
}
else
{
hr = E_FAIL;
}
if( FAILED( hr ) )
{
fNoErrors = FALSE;
continue;
}
if( fAppCreated )
{
//
// We don't need to fix the password for this
// COM+ application
//
continue;
}
// Add the application id to the array
VariantClear( &varAppKey );
varAppKey.vt = VT_BSTR;
varAppKey.bstrVal = SysAllocString( wszAppPackageId );
if( !varAppKey.bstrVal )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
rgIndices[0]++;
hr = SafeArrayPutElement( psaApplications,
rgIndices,
&varAppKey
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to set safe array element (%08x)\n",
hr
));
VariantClear( &varAppKey );
rgIndices[0]--;
fNoErrors = FALSE;
continue;
}
cApplications++;
}
}
//
// IVANPASH: BUG #524473
// Close meta object, because if W3SVC is starting it will keep SCM locked
// until it finishes, but it will be blocked by us from accessing /LM/W3SVC.
// and we will be blocked in the CoCreate for the COM+ Catalog waiting for
// SCM to unlock to start the service and SCM is waiting for W3SVC.
//
if( hMetabase != NULL )
{
pcCom->ComMDCloseMetaObject(hMetabase);
hMetabase = NULL;
}
// Shrink the size of the safe-array if necessary
if( cApplications < cMaxApplications )
{
rgsaBound[0].cElements = cApplications;
hr = SafeArrayRedim( psaApplications, rgsaBound );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to redim safe array (%08x)\n",
hr
));
goto cleanup;
}
}
//
// For each application reset the activation identity
//
// Use our key array to get the application collection
DBG_ASSERT( hMetabase == NULL );
hr = CoCreateInstance( CLSID_COMAdminCatalog,
NULL,
CLSCTX_SERVER,
IID_ICOMAdminCatalog,
(void**)&pComCatalog
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to create COM catalog (%08x)\n",
hr
));
goto cleanup;
}
hr = pComCatalog->GetCollection( bstrApplications,
(IDispatch **)&pComAppCollection
);
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get Applications collection (%08x)\n",
hr
));
goto cleanup;
}
hr = pComAppCollection->PopulateByKey( psaApplications );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to populate Applications collection (%08x)\n",
hr
));
goto cleanup;
}
// Iterate over the application collection and update all the
// applications that use IWAM.
hr = pComAppCollection->get_Count( &nAppsInCollection );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get Applications count (%08x)\n",
hr
));
goto cleanup;
}
// Init our new app identity and password.
varNewAppIdentity.vt = VT_BSTR;
varNewAppIdentity.bstrVal = SysAllocString( szWamUserName );
if( !varNewAppIdentity.bstrVal )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
varNewAppPassword.vt = VT_BSTR;
varNewAppPassword.bstrVal = SysAllocString( szWamUserPass );
if( !varNewAppPassword.bstrVal )
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
for( iCurrentApp = 0; iCurrentApp < nAppsInCollection; ++iCurrentApp )
{
if( pComApp )
{
pComApp->Release();
pComApp = NULL;
}
if( varOldAppIdentity.vt != VT_EMPTY )
{
VariantClear( &varOldAppIdentity );
}
hr = pComAppCollection->get_Item( iCurrentApp,
(IDispatch **)&pComApp );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get item from Applications collection (%08x)\n",
hr
));
fNoErrors = FALSE;
continue;
}
// If the user has set this to something other than the IWAM_
// user, then we will respect that and not reset the identiy.
hr = pComApp->get_Value( bstrIdentity, &varOldAppIdentity );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to get Identify from Application (%08x)\n",
hr
));
fNoErrors = FALSE;
continue;
}
DBG_ASSERT( varOldAppIdentity.vt == VT_BSTR );
if( varOldAppIdentity.vt == VT_BSTR )
{
if( memcmp( L"IWAM_", varOldAppIdentity.bstrVal, 10 ) == 0 )
{
hr = pComApp->put_Value( bstrIdentity, varNewAppIdentity );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to set new Identify (%08x)\n",
hr
));
fNoErrors = FALSE;
continue;
}
hr = pComApp->put_Value( bstrPassword, varNewAppPassword );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to set new Password (%08x)\n",
hr
));
fNoErrors = FALSE;
continue;
}
}
else
{
DBGINFO(( DBG_CONTEXT,
"Unrecognized application identity (%S)\n",
varOldAppIdentity.bstrVal
));
}
}
}
hr = pComAppCollection->SaveChanges( &nChanges );
if( FAILED(hr) )
{
DBGERROR(( DBG_CONTEXT,
"Failed to save changes (%08x)\n",
hr
));
goto cleanup;
}
cleanup:
if( hMetabase != NULL )
{
pcCom->ComMDCloseMetaObject(hMetabase);
hMetabase = NULL;
}
if( psaApplications != NULL )
{
SafeArrayDestroy( psaApplications );
}
if( pComCatalog != NULL )
{
pComCatalog->Release();
}
if( pComAppCollection != NULL )
{
pComAppCollection->Release();
}
if( pComApp != NULL )
{
pComApp->Release();
}
if( varAppKey.vt != VT_EMPTY )
{
VariantClear( &varAppKey );
}
if( varOldAppIdentity.vt != VT_EMPTY )
{
VariantClear( &varOldAppIdentity );
}
if( varNewAppIdentity.vt != VT_EMPTY )
{
VariantClear( &varNewAppIdentity );
}
if( varNewAppPassword.vt != VT_EMPTY )
{
VariantClear( &varNewAppPassword );
}
delete [] pwszDataPaths;
if ( bstrApplications != NULL)
{
SysFreeString( bstrApplications );
bstrApplications = NULL;
}
if ( bstrIdentity != NULL)
{
SysFreeString( bstrIdentity );
bstrIdentity = NULL;
}
if ( bstrPassword != NULL)
{
SysFreeString( bstrPassword );
bstrPassword = NULL;
}
if( pcCom != NULL )
{
pcCom->Release();
}
// return
if( FAILED(hr) )
{
return hr;
}
else if( fNoErrors == FALSE )
{
return S_FALSE;
}
else
{
return S_OK;
}
}
int
IsDomainController(void)
{
int iReturn = FALSE;
OSVERSIONINFOEX VerInfo;
ZeroMemory(&VerInfo, sizeof VerInfo);
VerInfo.dwOSVersionInfoSize = sizeof VerInfo;
if (GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&VerInfo)))
{
if (VER_NT_DOMAIN_CONTROLLER == VerInfo.wProductType)
{
iReturn = TRUE;
}
}
return iReturn;
}
BOOL
MakeSureUserGetsCreated(
LPTSTR pszAnonyName,
LPTSTR pszAnonyPass,
DWORD dwUserCommentResourceId,
DWORD dwUserFullNameResourceId,
BOOL fSpecicaliWamAccount)
{
BOOL bReturn = FALSE;
const DWORD dwMaxCount = 20;
DWORD dwCount = 0;
do
{
bReturn = CreateUserAccount(pszAnonyName,pszAnonyPass,dwUserCommentResourceId,dwUserFullNameResourceId,fSpecicaliWamAccount);
if (!bReturn)
{
// if this is a domain controller.
// we have to wait for the domain controller
// replication to be finished, otherwise The CreateUserAccount
// call will fail
//
// if we failed to create the user
// it could be because this is a DC and we need
// to wait for the sysvol to be ready.
Sleep(10000);
WaitForDCAvailability();
}
}
while ((!bReturn) && (++dwCount < dwMaxCount));
return bReturn;
}
BOOL
WaitForDCAvailability(void)
{
BOOL bRetVal = FALSE;
DWORD dwResult;
DWORD dwCount = 0;
DWORD dwMaxWait = 60000; // 1 minute max wait
HKEY hKeyNetLogonParams;
if (FALSE == IsDomainController())
{
// not a domain controller
// so we don't have to worry about replication delays...
return TRUE;
}
dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("SYSTEM\\CurrentControlSet\\Services\\netlogon\\parameters"),0,KEY_READ,&hKeyNetLogonParams );
if ( dwResult == ERROR_SUCCESS )
{
//
// value exists
//
DWORD dwSysVolReady = 0;
DWORD dwSize = sizeof( DWORD );
DWORD dwType = REG_DWORD;
dwResult = RegQueryValueEx( hKeyNetLogonParams,TEXT("SysVolReady"),0,&dwType,(LPBYTE) &dwSysVolReady,&dwSize );
if ( dwResult == ERROR_SUCCESS )
{
//
// SysVolReady?
//
if ( dwSysVolReady == 0 )
{
HANDLE hEvent;
//
// wait for SysVol to become ready
//
hEvent = CreateEvent( 0, TRUE, FALSE, TEXT("IISSysVolReadyEvent") );
if ( hEvent )
{
dwResult = RegNotifyChangeKeyValue( hKeyNetLogonParams, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE );
if ( dwResult == ERROR_SUCCESS )
{
const DWORD dwMaxCount = 3;
do
{
//
// wait for SysVolReady to change
// hEvent is signaled for any changes in hKeyNetLogonParams
// not just the SysVolReady value.
//
WaitForSingleObject (hEvent, dwMaxWait / dwMaxCount );
dwResult = RegQueryValueEx( hKeyNetLogonParams,TEXT("SysVolReady"),0,&dwType,(LPBYTE) &dwSysVolReady,&dwSize );
}
while ( dwSysVolReady == 0 && ++dwCount < dwMaxCount );
if ( dwSysVolReady )
{
bRetVal = TRUE;
}
}
CloseHandle( hEvent );
}
}
else
{
// sysvol is ready
bRetVal = TRUE;
}
}
else
{
//
// value is non-existent, SysVol is assumed to be ready
//
if ( dwResult == ERROR_FILE_NOT_FOUND )
{
bRetVal = TRUE;
}
}
RegCloseKey( hKeyNetLogonParams );
}
else
{
// error opening regkey, maybe it's not even there
bRetVal = TRUE;
}
return bRetVal;
}
DWORD
UpdateComApplicationsThread(
PVOID pv)
{
HRESULT hr = S_OK;
COM_APP_THREAD_STRUCT *pUpdateComAppInfo = (COM_APP_THREAD_STRUCT *) pv;
BOOL fComInitialized = FALSE;
if ( pUpdateComAppInfo == NULL )
{
goto exit;
}
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if ( FAILED( hr ) )
{
goto exit;
}
fComInitialized = TRUE;
hr = UpdateComApplications( pUpdateComAppInfo->ustWamUserName,
pUpdateComAppInfo->pstWamUserPass );
exit:
if( hr != S_OK )
{
if( !g_eventLogForAccountRecreation )
{
CreateEventLogObject();
}
if ( g_eventLogForAccountRecreation )
{
g_eventLogForAccountRecreation->LogEvent( INET_SVC_ACCOUNT_COMUPDATE_FAILED,
0,
NULL,
hr );
}
}
if ( pUpdateComAppInfo != NULL )
{
// erase the memory that we held the username/password in.
SecureZeroMemory( (PVOID)pUpdateComAppInfo->ustWamUserName,
sizeof(pUpdateComAppInfo->ustWamUserName) );
SecureZeroMemory( (PVOID)pUpdateComAppInfo->pstWamUserPass,
sizeof(pUpdateComAppInfo->pstWamUserPass) );
delete pUpdateComAppInfo;
pUpdateComAppInfo = NULL;
}
if ( fComInitialized )
{
CoUninitialize();
}
return 0;
}