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
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, ®istryKey);
|
|
|
|
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,
|
|
®istryKey,
|
|
&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;
|
|
}
|