/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2008 OMC Denmark ApS.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "winsec.h"
#include <winuser.h>

#define DELETE       (0x00010000L)
#define READ_CONTROL (0x00020000L)
#define WRITE_DAC    (0x00040000L)
#define WRITE_OWNER  (0x00080000L)


#ifdef WINSTA_ALL
#undef WINSTA_ALL
#endif
#define WINSTA_ALL (WINSTA_ACCESSCLIPBOARD  | WINSTA_ACCESSGLOBALATOMS | \
		    WINSTA_CREATEDESKTOP    | WINSTA_ENUMDESKTOPS      | \
		    WINSTA_ENUMERATE        | WINSTA_EXITWINDOWS       | \
		    WINSTA_READATTRIBUTES   | WINSTA_READSCREEN        | \
		    WINSTA_WRITEATTRIBUTES  | DELETE                   | \
		    READ_CONTROL            | WRITE_DAC                | \
		    WRITE_OWNER)

#ifdef DESKTOP_ALL
#undef DESKTOP_ALL
#endif
#define DESKTOP_ALL (DESKTOP_CREATEMENU      | DESKTOP_CREATEWINDOW  |  \
		     DESKTOP_ENUMERATE       | DESKTOP_HOOKCONTROL   |  \
		     DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD |  \
		     DESKTOP_READOBJECTS     | DESKTOP_SWITCHDESKTOP |  \
		     DESKTOP_WRITEOBJECTS    | DELETE                |  \
		     READ_CONTROL            | WRITE_DAC             |  \
		     WRITE_OWNER)

#ifdef GENERIC_ACCESS
#undef GENERIC_ACCESS
#endif
#define GENERIC_ACCESS (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)


/*
 * The following code (which is clearly marked as such) and only that code,
 * has been copied from the Wine project, <http://www.winehq.org/>.
 * It is copyrighted and licensed by the Wine project as follows.
 */

/**********************************************************************
 *
 *     #### BEGINNING OF CODE FROM THE WINE PROJECT ####
 */

/*
 * Copyright 1999, 2000 Juergen Schmied <juergen.schmied@debitel.net>
 * Copyright 2003 CodeWeavers Inc. (Ulrich Czekalla)
 * Copyright 2006 Robert Reif
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 */

typedef struct _MAX_SID
{
	/* same fields as struct _SID */
	BYTE Revision;
	BYTE SubAuthorityCount;
	SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
	DWORD SubAuthority[SID_MAX_SUB_AUTHORITIES];
} MAX_SID;

typedef struct WELLKNOWNSID
{
	WCHAR wstr[2];
	WELL_KNOWN_SID_TYPE Type;
	MAX_SID Sid;
} WELLKNOWNSID;

static const WELLKNOWNSID WellKnownSids[] =
{
	{ {0,0}, WinNullSid, { SID_REVISION, 1, { SECURITY_NULL_SID_AUTHORITY }, { SECURITY_NULL_RID } } },
	{ {'W','D'}, WinWorldSid, { SID_REVISION, 1, { SECURITY_WORLD_SID_AUTHORITY }, { SECURITY_WORLD_RID } } },
	{ {0,0}, WinLocalSid, { SID_REVISION, 1, { SECURITY_LOCAL_SID_AUTHORITY }, { SECURITY_LOCAL_RID } } },
	{ {'C','O'}, WinCreatorOwnerSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_OWNER_RID } } },
	{ {'C','G'}, WinCreatorGroupSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_GROUP_RID } } },
	{ {0,0}, WinCreatorOwnerServerSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_OWNER_SERVER_RID } } },
	{ {0,0}, WinCreatorGroupServerSid, { SID_REVISION, 1, { SECURITY_CREATOR_SID_AUTHORITY }, { SECURITY_CREATOR_GROUP_SERVER_RID } } },
	{ {0,0}, WinNtAuthoritySid, { SID_REVISION, 0, { SECURITY_NT_AUTHORITY }, { SECURITY_NULL_RID } } },
	{ {0,0}, WinDialupSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_DIALUP_RID } } },
	{ {'N','U'}, WinNetworkSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_NETWORK_RID } } },
	{ {0,0}, WinBatchSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_BATCH_RID } } },
	{ {'I','U'}, WinInteractiveSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_INTERACTIVE_RID } } },
	{ {'S','U'}, WinServiceSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_SERVICE_RID } } },
	{ {'A','N'}, WinAnonymousSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_ANONYMOUS_LOGON_RID } } },
	{ {0,0}, WinProxySid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_PROXY_RID } } },
	{ {'E','D'}, WinEnterpriseControllersSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_ENTERPRISE_CONTROLLERS_RID } } },
	{ {'P','S'}, WinSelfSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_PRINCIPAL_SELF_RID } } },
	{ {'A','U'}, WinAuthenticatedUserSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_AUTHENTICATED_USER_RID } } },
	{ {'R','C'}, WinRestrictedCodeSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_RESTRICTED_CODE_RID } } },
	{ {0,0}, WinTerminalServerSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_TERMINAL_SERVER_RID } } },
	{ {0,0}, WinRemoteLogonIdSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_REMOTE_LOGON_RID } } },
	{ {'S','Y'}, WinLocalSystemSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SYSTEM_RID } } },
	{ {'L','S'}, WinLocalServiceSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_LOCAL_SERVICE_RID } } },
	{ {'N','S'}, WinNetworkServiceSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_NETWORK_SERVICE_RID } } },
	{ {0,0}, WinBuiltinDomainSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID } } },
	{ {'B','A'}, WinBuiltinAdministratorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS } } },
	{ {'B','U'}, WinBuiltinUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS } } },
	{ {'B','G'}, WinBuiltinGuestsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS } } },
	{ {'P','U'}, WinBuiltinPowerUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS } } },
	{ {'A','O'}, WinBuiltinAccountOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ACCOUNT_OPS } } },
	{ {'S','O'}, WinBuiltinSystemOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_SYSTEM_OPS } } },
	{ {'P','O'}, WinBuiltinPrintOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PRINT_OPS } } },
	{ {'B','O'}, WinBuiltinBackupOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_BACKUP_OPS } } },
	{ {'R','E'}, WinBuiltinReplicatorSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REPLICATOR } } },
	{ {'R','U'}, WinBuiltinPreWindows2000CompatibleAccessSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_PREW2KCOMPACCESS } } },
	{ {'R','D'}, WinBuiltinRemoteDesktopUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS } } },
	{ {'N','O'}, WinBuiltinNetworkConfigurationOperatorsSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS } } },
	{ {0,0}, WinNTLMAuthenticationSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_NTLM_RID } } },
	{ {0,0}, WinDigestAuthenticationSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_DIGEST_RID } } },
	{ {0,0}, WinSChannelAuthenticationSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_PACKAGE_BASE_RID, SECURITY_PACKAGE_SCHANNEL_RID } } },
	{ {0,0}, WinThisOrganizationSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_THIS_ORGANIZATION_RID } } },
	{ {0,0}, WinOtherOrganizationSid, { SID_REVISION, 1, { SECURITY_NT_AUTHORITY }, { SECURITY_OTHER_ORGANIZATION_RID } } },
	{ {0,0}, WinBuiltinIncomingForestTrustBuildersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_INCOMING_FOREST_TRUST_BUILDERS  } } },
	{ {0,0}, WinBuiltinPerfMonitoringUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_MONITORING_USERS } } },
	{ {0,0}, WinBuiltinPerfLoggingUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_LOGGING_USERS } } },
	{ {0,0}, WinBuiltinAuthorizationAccessSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_AUTHORIZATIONACCESS } } },
	{ {0,0}, WinBuiltinTerminalServerLicenseServersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_TS_LICENSE_SERVERS } } },
	{ {0,0}, WinBuiltinDCOMUsersSid, { SID_REVISION, 2, { SECURITY_NT_AUTHORITY }, { SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_DCOM_USERS } } },
};

/* these SIDs must be constructed as relative to some domain - only the RID is well-known */
typedef struct WELLKNOWNRID
{
	WELL_KNOWN_SID_TYPE Type;
	DWORD Rid;
} WELLKNOWNRID;

static const WELLKNOWNRID WellKnownRids[] = {
	{ WinAccountAdministratorSid,    DOMAIN_USER_RID_ADMIN },
	{ WinAccountGuestSid,            DOMAIN_USER_RID_GUEST },
	{ WinAccountKrbtgtSid,           DOMAIN_USER_RID_KRBTGT },
	{ WinAccountDomainAdminsSid,     DOMAIN_GROUP_RID_ADMINS },
	{ WinAccountDomainUsersSid,      DOMAIN_GROUP_RID_USERS },
	{ WinAccountDomainGuestsSid,     DOMAIN_GROUP_RID_GUESTS },
	{ WinAccountComputersSid,        DOMAIN_GROUP_RID_COMPUTERS },
	{ WinAccountControllersSid,      DOMAIN_GROUP_RID_CONTROLLERS },
	{ WinAccountCertAdminsSid,       DOMAIN_GROUP_RID_CERT_ADMINS },
	{ WinAccountSchemaAdminsSid,     DOMAIN_GROUP_RID_SCHEMA_ADMINS },
	{ WinAccountEnterpriseAdminsSid, DOMAIN_GROUP_RID_ENTERPRISE_ADMINS },
	{ WinAccountPolicyAdminsSid,     DOMAIN_GROUP_RID_POLICY_ADMINS },
	{ WinAccountRasAndIasServersSid, DOMAIN_ALIAS_RID_RAS_SERVERS },
};

BOOL WINAPI
MyCreateWellKnownSid(WELL_KNOWN_SID_TYPE WellKnownSidType,
		     PSID DomainSid,
		     PSID pSid,
		     DWORD* cbSid)
{
	unsigned int i;

	if (cbSid == NULL || pSid == NULL || (DomainSid && !IsValidSid(DomainSid))) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}

	for (i = 0; i < sizeof(WellKnownSids)/sizeof(WellKnownSids[0]); i++) {
		if (WellKnownSids[i].Type == WellKnownSidType) {
			DWORD length = GetSidLengthRequired(WellKnownSids[i].Sid.SubAuthorityCount);

			if (*cbSid < length) {
				SetLastError(ERROR_INSUFFICIENT_BUFFER);
				return FALSE;
			}

			CopyMemory(pSid, &WellKnownSids[i].Sid.Revision, length);
			*cbSid = length;
			return TRUE;
		}
	}

	if (!DomainSid || (SID_MAX_SUB_AUTHORITIES == *GetSidSubAuthorityCount(DomainSid))) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}

	for (i = 0; i < sizeof(WellKnownRids)/sizeof(WellKnownRids[0]); i++) {
		if (WellKnownRids[i].Type == WellKnownSidType) {
			UCHAR domain_subauth = *GetSidSubAuthorityCount(DomainSid);
			DWORD domain_sid_length = GetSidLengthRequired(domain_subauth);
			DWORD output_sid_length = GetSidLengthRequired(domain_subauth + 1);

			if (*cbSid < output_sid_length) {
				SetLastError(ERROR_INSUFFICIENT_BUFFER);
				return FALSE;
			}

			CopyMemory((void*)pSid, (const void*)DomainSid, domain_sid_length);
			(*GetSidSubAuthorityCount(pSid))++;
			(*GetSidSubAuthority(pSid, domain_subauth)) = WellKnownRids[i].Rid;
			*cbSid = output_sid_length;
			return TRUE;
		}
	}

	SetLastError(ERROR_INVALID_PARAMETER);
	return FALSE;
}

/*
 *           #### END OF CODE FROM THE WINE PROJECT ####
 *
 **********************************************************************/

// DomainSid must be freed with FreeSid()
bool
create_domain_sid(PSID Sid,
		  PSID &DomainSid)
{
	DomainSid = NULL;

	//S-1-5
	SID_IDENTIFIER_AUTHORITY DomainIdent = {0,0,0,0,0,5};
	DWORD SubAuth[4] = {0,0,0,0};

	if (!IsValidSid(Sid))
		return false;

	//we need the identifier to compare
	PSID_IDENTIFIER_AUTHORITY SidIdent = GetSidIdentifierAuthority(Sid);


	//make sure it is a valid identifier authority
	if (0 != memcmp(&SidIdent->Value, &DomainIdent.Value, sizeof(DomainIdent.Value)))
		return false;

	//we need at least 4 sub authorities (including 21 as the first)
	DWORD SubAuthCount = (DWORD)*GetSidSubAuthorityCount(Sid);
	if (SubAuthCount < 4)
		return false;

	//make sure first sub authority is 21 (stands for domain RID)
	DWORD value = (DWORD)*GetSidSubAuthority(Sid, 0);
	if (value != 21)
		return false;


	//copy domain sub authorities
	for (int i = 0; i < 4; i++)
		SubAuth[i] = (DWORD)*GetSidSubAuthority(Sid, i);

	//create sid from it
	if (!AllocateAndInitializeSid(&DomainIdent, 4,
				      SubAuth[0], SubAuth[1], SubAuth[2], SubAuth[3],
				      0,0,0,0, &DomainSid))
		return false;

	return true;
}

bool
ObtainUserSid(HANDLE hToken,
	      PSID *psid)
{
	bool retv = false; // assume function will fail
	DWORD dwLength = 0;
	TOKEN_INFORMATION_CLASS tic = TokenUser;
	TOKEN_USER *token_user = NULL;

	//
	// determine the size of the buffer
	//
	if (!GetTokenInformation(hToken,
				 tic,
				 (LPVOID)token_user,
				 0,
				 &dwLength)) {
		if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
			goto finally;
		token_user = (TOKEN_USER*)HeapAlloc(GetProcessHeap(),
						    HEAP_ZERO_MEMORY,
						    dwLength);
		if (!token_user)
			goto finally;
	}

	//
	// obtain the groups the access token belongs to
	//
	if (!GetTokenInformation(hToken,
				 tic,
				 (LPVOID)token_user,
				 dwLength,
				 &dwLength))
		goto finally;


	//
	// determine the length of the sid
	//
	dwLength = GetLengthSid(token_user->User.Sid);

	//
	// allocate a buffer for the logon sid
	//
	*psid = (PSID)HeapAlloc(GetProcessHeap(),
				HEAP_ZERO_MEMORY,
				dwLength);
	if (!(*psid))
		goto finally;

	//
	// obtain a copy of the logon sid
	//
	if (!CopySid(dwLength, *psid, token_user->User.Sid))
		goto finally;


	//
	// indicate success
	//
	retv = true;

finally:
	//
	// free the buffer for the token group
	//
	if (token_user)
		HeapFree(GetProcessHeap(), 0, (LPVOID)token_user);

	return retv;
}

bool
AddTheAceWindowStation(HWINSTA hwinsta,
		       PSID psid)
{
	bool retv = false; // assume function will fail
	ACCESS_ALLOWED_ACE *pace_generic = NULL;
	ACCESS_ALLOWED_ACE *pace_winsta = NULL;
	ACL_SIZE_INFORMATION aclSizeInfo;
	BOOL bDaclExist;
	BOOL bDaclPresent;
	DWORD dwNewAclSize;
	DWORD dwSidSize = 0;
	DWORD dwSdSizeNeeded;
	PACL pacl = NULL;
	PACL pNewAcl = NULL;
	PSECURITY_DESCRIPTOR psd = NULL;
	PSECURITY_DESCRIPTOR psdNew = NULL;
	PVOID pTempAce = NULL;
	SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
	unsigned int i = 0;
	size_t ace_num = 0;


	//
	// allocate generic ACE
	//
	pace_generic = (ACCESS_ALLOWED_ACE *)HeapAlloc(GetProcessHeap(),
						       HEAP_ZERO_MEMORY,
						       sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) -
						       sizeof(DWORD));
	if (!pace_generic)
		goto finally;

	// initialize generic ACE
	pace_generic->Header.AceType  = ACCESS_ALLOWED_ACE_TYPE;
	pace_generic->Header.AceFlags = CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
	pace_generic->Header.AceSize  = (WORD) (sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD));
	pace_generic->Mask            = GENERIC_ACCESS;
	if (!CopySid(GetLengthSid(psid), &pace_generic->SidStart, psid))
		goto finally;

	//
	// allocate winsta ACE
	//
	pace_winsta = (ACCESS_ALLOWED_ACE *)HeapAlloc(GetProcessHeap(),
						      HEAP_ZERO_MEMORY,
						      sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) -
						      sizeof(DWORD));
	if (!pace_winsta)
		goto finally;

	// initialize winsta ACE
	pace_winsta->Header.AceType  = ACCESS_ALLOWED_ACE_TYPE;
	pace_winsta->Header.AceFlags = NO_PROPAGATE_INHERIT_ACE;
	pace_winsta->Header.AceSize  = (WORD) (sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD));
	pace_winsta->Mask            = WINSTA_ALL;
	if (!CopySid(GetLengthSid(psid), &pace_winsta->SidStart, psid))
		goto finally;

	//
	// check for presence
	//
	if (user_object_has_ace(hwinsta, pace_generic)) {
		HeapFree(GetProcessHeap(), 0, (LPVOID)pace_generic);
		pace_generic = NULL;
	}
	if (user_object_has_ace(hwinsta, pace_winsta)) {
		HeapFree(GetProcessHeap(), 0, (LPVOID)pace_winsta);
		pace_winsta = NULL;
	}
	ace_num = !!pace_winsta + !!pace_generic;
	if (!ace_num) {
		retv = true;
		goto finally;
	}

	//
	// obtain the dacl for the windowstation
	//
	if (!GetUserObjectSecurity(hwinsta,
				   &si,
				   psd,
				   dwSidSize,
				   &dwSdSizeNeeded)) {
		if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
			goto finally;

		psd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
						      HEAP_ZERO_MEMORY,
						      dwSdSizeNeeded);
		if (!psd)
			goto finally;

		psdNew = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
							 HEAP_ZERO_MEMORY,
							 dwSdSizeNeeded);
		if (!psdNew)
			goto finally;

		dwSidSize = dwSdSizeNeeded;

		if (!GetUserObjectSecurity(hwinsta,
					   &si,
					   psd,
					   dwSidSize,
					   &dwSdSizeNeeded))
			goto finally;
	}

	//
	// create a new dacl
	//
	if (!InitializeSecurityDescriptor(psdNew,
					  SECURITY_DESCRIPTOR_REVISION))
		goto finally;

	//
	// get dacl from the security descriptor
	//
	if (!GetSecurityDescriptorDacl(psd,
				       &bDaclPresent,
				       &pacl,
				       &bDaclExist))
		goto finally;

	//
	// initialize
	//
	ZeroMemory(&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION));
	aclSizeInfo.AclBytesInUse = sizeof(ACL);

	//
	// call only if the dacl is not NULL
	//
	if (pacl) {
		//
		// get the ACL size info
		//
		if (!GetAclInformation(pacl,
				       (LPVOID)&aclSizeInfo,
				       sizeof(ACL_SIZE_INFORMATION),
				       AclSizeInformation))
			goto finally;
	}

	//
	// compute the size of the new acl
	//
	dwNewAclSize = aclSizeInfo.AclBytesInUse
		+ (ace_num * sizeof(ACCESS_ALLOWED_ACE))
		+ (ace_num * GetLengthSid(psid))
		- (ace_num * sizeof(DWORD));

	//
	// allocate memory for the new acl
	//
	pNewAcl = (PACL)HeapAlloc(GetProcessHeap(),
				  HEAP_ZERO_MEMORY,
				  dwNewAclSize);
	if (!pNewAcl)
		goto finally;

	//
	// initialize the new dacl
	//
	if (!InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION))
		goto finally;

	//
	// if DACL is present, copy it to a new DACL
	//
	if (bDaclPresent) { // only copy if DACL was present
		// copy the ACEs to our new ACL
		if (aclSizeInfo.AceCount) {
			for (i=0; i < aclSizeInfo.AceCount; i++) {
				// get an ACE
				if (!GetAce(pacl, i, &pTempAce))
					goto finally;

				// add the ACE to the new ACL
				if (!AddAce(pNewAcl,
					    ACL_REVISION,
					    MAXDWORD,
					    pTempAce,
					    ((PACE_HEADER)pTempAce)->AceSize))
					goto finally;
			}
		}
	}

	//
	// add the first ACE to the windowstation
	//
	if (pace_generic) {
		if (!AddAce(pNewAcl,
			    ACL_REVISION,
			    MAXDWORD,
			    (LPVOID)pace_generic,
			    pace_generic->Header.AceSize))
			goto finally;
	}

	//
	// add the second ACE to the windowstation
	//
	if (pace_winsta) {
		if (!AddAce(pNewAcl,
			    ACL_REVISION,
			    MAXDWORD,
			    (LPVOID)pace_winsta,
			    pace_winsta->Header.AceSize))
			goto finally;
	}

	//
	// set new dacl for the security descriptor
	//
	if (!SetSecurityDescriptorDacl(psdNew,
				       TRUE,
				       pNewAcl,
				       FALSE))
		goto finally;

	//
	// set the new security descriptor for the windowstation
	//
	if (!SetUserObjectSecurity(hwinsta, &si, psdNew))
		goto finally;

	//
	// indicate success
	//
	retv = true;

finally:

	//
	// free the allocated buffers
	//
	if (pace_generic)
		HeapFree(GetProcessHeap(), 0, (LPVOID)pace_generic);

	if (pace_winsta)
		HeapFree(GetProcessHeap(), 0, (LPVOID)pace_winsta);

	if (pNewAcl)
		HeapFree(GetProcessHeap(), 0, (LPVOID)pNewAcl);

	if (psd)
		HeapFree(GetProcessHeap(), 0, (LPVOID)psd);

	if (psdNew)
		HeapFree(GetProcessHeap(), 0, (LPVOID)psdNew);

	return retv;
}

bool
AddTheAceDesktop(HDESK hdesk,
		 PSID psid)
{
	bool retv = false; // assume function will fail
	ACCESS_ALLOWED_ACE *pace = NULL;
	ACL_SIZE_INFORMATION aclSizeInfo;
	BOOL bDaclExist;
	BOOL bDaclPresent;
	DWORD dwNewAclSize;
	DWORD dwSidSize = 0;
	DWORD dwSdSizeNeeded;
	PACL pacl = NULL;
	PACL pNewAcl = NULL;
	PSECURITY_DESCRIPTOR psd = NULL;
	PSECURITY_DESCRIPTOR psdNew = NULL;
	PVOID pTempAce = NULL;
	SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
	unsigned int i;

	size_t ace_num = 0;


	//
	// allocate desktop ACE
	//
	pace = (ACCESS_ALLOWED_ACE *)HeapAlloc(GetProcessHeap(),
					       HEAP_ZERO_MEMORY,
					       sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) -
					       sizeof(DWORD));
	if (!pace)
		goto finally;

	// initialize desktop ACE
	pace->Header.AceType  = ACCESS_ALLOWED_ACE_TYPE;
	pace->Header.AceFlags = 0;
	pace->Header.AceSize  = (WORD) (sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD));
	pace->Mask            = DESKTOP_ALL;
	if (!CopySid(GetLengthSid(psid), &pace->SidStart, psid))
		goto finally;

	//
	// check for presence
	//
	if (user_object_has_ace(hdesk, pace)) {
		retv = true;
		goto finally;
	}

	//
	// obtain the security descriptor for the desktop object
	//
	if (!GetUserObjectSecurity(hdesk,
				   &si,
				   psd,
				   dwSidSize,
				   &dwSdSizeNeeded)) {
		if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
			goto finally;

		psd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
						      HEAP_ZERO_MEMORY,
						      dwSdSizeNeeded);
		if (!psd)
			goto finally;

		psdNew = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
							 HEAP_ZERO_MEMORY,
							 dwSdSizeNeeded);
		if (!psdNew)
			goto finally;

		dwSidSize = dwSdSizeNeeded;

		if (!GetUserObjectSecurity(hdesk,
					   &si,
					   psd,
					   dwSidSize,
					   &dwSdSizeNeeded))
			goto finally;
	}

	//
	// create a new security descriptor
	//
	if (!InitializeSecurityDescriptor(psdNew,
					  SECURITY_DESCRIPTOR_REVISION))
		goto finally;

	//
	// obtain the dacl from the security descriptor
	//
	if (!GetSecurityDescriptorDacl(psd,
				       &bDaclPresent,
				       &pacl,
				       &bDaclExist))
		goto finally;

	//
	// initialize
	//
	ZeroMemory(&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION));
	aclSizeInfo.AclBytesInUse = sizeof(ACL);

	//
	// call only if dacl is not NULL
	//
	if (pacl) {
		//
		// determine the size of the ACL info
		//
		if (!GetAclInformation(pacl,
				       (LPVOID)&aclSizeInfo,
				       sizeof(ACL_SIZE_INFORMATION),
				       AclSizeInformation))
			goto finally;
	}

	//
	// compute the size of the new acl
	//
	dwNewAclSize = aclSizeInfo.AclBytesInUse
		+ sizeof(ACCESS_ALLOWED_ACE)
		+ GetLengthSid(psid)
		- sizeof(DWORD);

	//
	// allocate buffer for the new acl
	//
	pNewAcl = (PACL)HeapAlloc(GetProcessHeap(),
				  HEAP_ZERO_MEMORY,
				  dwNewAclSize);
	if (!pNewAcl)
		goto finally;

	//
	// initialize the new acl
	//
	if (!InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION))
		goto finally;

	//
	// if DACL is present, copy it to a new DACL
	//
	if (bDaclPresent) { // only copy if DACL was present
		// copy the ACEs to our new ACL
		if (aclSizeInfo.AceCount) {
			for (i = 0; i < aclSizeInfo.AceCount; i++) {
				// get an ACE
				if (!GetAce(pacl, i, &pTempAce))
					goto finally;

				// add the ACE to the new ACL
				if (!AddAce(pNewAcl,
					    ACL_REVISION,
					    MAXDWORD,
					    pTempAce,
					    ((PACE_HEADER)pTempAce)->AceSize))
					goto finally;
			}
		}
	}

	//
	// add ace to the dacl
	//
	if (!AddAccessAllowedAce(pNewAcl,
				 ACL_REVISION,
				 DESKTOP_ALL,
				 psid))
		goto finally;

	//
	// set new dacl to the new security descriptor
	//
	if (!SetSecurityDescriptorDacl(psdNew,
				       TRUE,
				       pNewAcl,
				       FALSE))
		goto finally;

	//
	// set the new security descriptor for the desktop object
	//
	if (!SetUserObjectSecurity(hdesk, &si, psdNew))
		goto finally;

	//
	// indicate success
	//
	retv = true;

finally:
	//
	// free buffers
	//
	if (pace)
		HeapFree(GetProcessHeap(), 0, (LPVOID)pace);

	if (pNewAcl)
		HeapFree(GetProcessHeap(), 0, (LPVOID)pNewAcl);

	if (psd)
		HeapFree(GetProcessHeap(), 0, (LPVOID)psd);

	if (psdNew)
		HeapFree(GetProcessHeap(), 0, (LPVOID)psdNew);

	return retv;
}

bool
user_object_has_ace(HANDLE object,
		    const ACCESS_ALLOWED_ACE * const ace)
{
	bool retv = false;
	BOOL bDaclExist = false;
	BOOL bDaclPresent = false;
	DWORD dwSidSize = 0;
	DWORD dwSdSizeNeeded;
	ACL_SIZE_INFORMATION aclSizeInfo;
	PACL pacl = NULL;
	PACL pNewAcl = NULL;
	PSECURITY_DESCRIPTOR psd = NULL;
	ACCESS_ALLOWED_ACE *pTempAce = NULL;
	SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
	unsigned int i;

	//
	// obtain the dacl for the windowstation
	//
	if (!GetUserObjectSecurity(object,
				   &si,
				   psd,
				   dwSidSize,
				   &dwSdSizeNeeded)) {
		if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
			goto finally;

		psd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
						      HEAP_ZERO_MEMORY,
						      dwSdSizeNeeded);
		if (!psd)
			goto finally;

		dwSidSize = dwSdSizeNeeded;

		if (!GetUserObjectSecurity(object,
					   &si,
					   psd,
					   dwSidSize,
					   &dwSdSizeNeeded))
			goto finally;
	}

	//
	// get dacl from the security descriptor
	//
	if (!GetSecurityDescriptorDacl(psd,
				       &bDaclPresent,
				       &pacl,
				       &bDaclExist))
		goto finally;

	//
	// initialize
	//
	ZeroMemory(&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION));
	aclSizeInfo.AclBytesInUse = sizeof(ACL);

	//
	// call only if the dacl is not NULL
	//
	if (!pacl)
		goto finally;

	//
	// get the ACL size info
	//
	if (!GetAclInformation(pacl,
			       (LPVOID)&aclSizeInfo,
			       sizeof(ACL_SIZE_INFORMATION),
			       AclSizeInformation))
		goto finally;

	// only check if DACL was present
	if (!bDaclPresent)
		goto finally;


	// check for the ACE
	if (!aclSizeInfo.AceCount)
		goto finally;

	for (i = 0; i < aclSizeInfo.AceCount; i++) {
		if (!GetAce(pacl, i, (PVOID*) (&pTempAce)))
			goto finally;

		if ((pTempAce->Header.AceType     == ace->Header.AceType)
		    && (pTempAce->Header.AceFlags == ace->Header.AceFlags)
		    && (pTempAce->Header.AceSize  == ace->Header.AceSize)
		    && (pTempAce->Mask            == ace->Mask)
		    && EqualSid(&pTempAce->SidStart, (PSID) &ace->SidStart)) {
			retv = true;
			break;
		}
	}

finally:
	if (psd)
		HeapFree(GetProcessHeap(), 0, (LPVOID)psd);

	return retv;
}

bool
adjust_dacls(PSID domain_sid)
{
	bool retv = false;
	HDESK hdesk = NULL;
	HWINSTA hwinsta = NULL;
	HWINSTA hwinstaold = NULL;
	DWORD SidSize = SECURITY_MAX_SID_SIZE;
	PSID psid = NULL;

	// obtain a handle to the interactive windowstation
	hwinsta = OpenWindowStation("winsta0",
				    FALSE,
				    READ_CONTROL | WRITE_DAC);
	if (!hwinsta)
		return false;

	hwinstaold = GetProcessWindowStation();
	if (!hwinstaold)
		return false;

	// set the windowstation to winsta0 so that you obtain
	// the correct default desktop
	if (!SetProcessWindowStation(hwinsta)) {
		CloseWindowStation(hwinsta);
		return false;
	}

	// obtain a handle to the "default" desktop
	hdesk = OpenDesktop("default",
			    0,
			    FALSE,
			    READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS);
	if (!hdesk) {
		goto error;
	}

	// Allocate enough memory for the largest possible SID.
	psid = LocalAlloc(LMEM_FIXED, SidSize);
	if (!psid)
		goto error;

	// Create a SID for the Domain Users group on the local computer.
	if (!MyCreateWellKnownSid(WinAccountDomainUsersSid, domain_sid, psid, &SidSize))
		goto error;

	// add the SID to interactive windowstation
	if (!AddTheAceWindowStation(hwinsta, psid))
		goto error;

	// add SID to "default" desktop
	if (!AddTheAceDesktop(hdesk, psid))
		goto error;

	retv = true;

error:
	// close handles to station and desktop
	CloseWindowStation(hwinsta);
	if (hdesk)
		CloseDesktop(hdesk);

	// set it back
	SetProcessWindowStation(hwinstaold);

	// free the SID
	if (psid)
		LocalFree(psid);

	return retv;
}
