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.
5621 lines
163 KiB
5621 lines
163 KiB
/*++
|
|
|
|
Copyright (c) 1997-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sddl.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Security Descriptor Definition Language support functions
|
|
|
|
Author:
|
|
|
|
Mac McLain (MacM) Nov 07, 1997
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
Revision History:
|
|
|
|
Jin Huang (JinHuang) 3/4/98 Fix validity flags (GetAceFlagsInTable)
|
|
Jin Huang (JinHuang) 3/10/98 Add SD controls (GetSDControlForString)
|
|
Set SidsInitialized flag
|
|
Skip any possible spaces in string
|
|
Jin Huang (JinHuang) 5/1/98 Fix memory leek, error checking
|
|
improve performance
|
|
Alaa Abdelhalim (Alaa) 7/20/99 Initialize sbz2 field to 0 in LocalGetAclForString
|
|
function.
|
|
Vishnu Patankar (VishnuP) 7/5/00 Added new API ConvertStringSDToSDDomain(A/W)
|
|
|
|
Shawn Wu (ShawnWu) 4/27/02 Added encrypted ldap support.
|
|
|
|
--*/
|
|
#include "advapi.h"
|
|
#include <windef.h>
|
|
#include <stdio.h>
|
|
#include <wchar.h>
|
|
#include <sddl.h>
|
|
#include <ntseapi.h>
|
|
#include <seopaque.h>
|
|
#include <accctrl.h>
|
|
#include <rpcdce.h>
|
|
#include <ntlsa.h>
|
|
#include "sddlp.h"
|
|
|
|
|
|
//
|
|
// include and defines for ldap calls
|
|
//
|
|
#include <winldap.h>
|
|
#include <ntldap.h>
|
|
|
|
typedef PLDAP (LDAPAPI *PFN_LDAP_INIT)( PCHAR HostName, ULONG PortNumber );
|
|
typedef ULONG (LDAPAPI *PFN_LDAP_SET_OPTION) ( LDAP *, int , const void * );
|
|
typedef ULONG (LDAPAPI *PFN_LDAP_BIND)( LDAP *, PCHAR, PCHAR, ULONG);
|
|
typedef ULONG (LDAPAPI *PFN_LDAP_UNBIND)( LDAP * );
|
|
typedef ULONG (LDAPAPI *PFN_LDAP_SEARCH)(LDAP *, PCHAR, ULONG, PCHAR, PCHAR *, ULONG,PLDAPControlA *, PLDAPControlA *, struct l_timeval *, ULONG, LDAPMessage **);
|
|
typedef LDAPMessage * (LDAPAPI *PFN_LDAP_FIRST_ENTRY)( LDAP *, LDAPMessage * );
|
|
typedef PCHAR * (LDAPAPI *PFN_LDAP_GET_VALUE)(LDAP *, LDAPMessage *, PCHAR );
|
|
typedef ULONG (LDAPAPI *PFN_LDAP_MSGFREE)( LDAPMessage * );
|
|
typedef ULONG (LDAPAPI *PFN_LDAP_VALUE_FREE)( PCHAR * );
|
|
typedef ULONG (LDAPAPI *PFN_LDAP_MAP_ERROR)( ULONG );
|
|
|
|
// 64K-1
|
|
#define SDDL_MAX_ACL_SIZE 0xFFFF
|
|
|
|
#define SDDL_SID_STRING_SIZE 256
|
|
|
|
|
|
//
|
|
// To allow the defines to be used as Wide strings, redefine the TEXT macro
|
|
//
|
|
#ifdef TEXT
|
|
#undef TEXT
|
|
#endif
|
|
#define TEXT(quote) L##quote
|
|
|
|
//
|
|
// Local macros
|
|
//
|
|
#define STRING_GUID_LEN 36
|
|
#define STRING_GUID_SIZE ( STRING_GUID_LEN * sizeof( WCHAR ) )
|
|
#define SDDL_LEN_TAG( tagdef ) ( sizeof( tagdef ) / sizeof( WCHAR ) - 1 )
|
|
#define SDDL_SIZE_TAG( tagdef ) ( wcslen( tagdef ) * sizeof( WCHAR ) )
|
|
#define SDDL_SIZE_SEP( sep ) (sizeof( WCHAR ) )
|
|
|
|
#define SDDL_VALID_DACL 0x00000001
|
|
#define SDDL_VALID_SACL 0x00000002
|
|
|
|
//
|
|
// This structure is used to do some lookups for mapping ACES
|
|
//
|
|
typedef struct _STRSD_KEY_LOOKUP {
|
|
|
|
PWSTR Key;
|
|
ULONG KeyLen;
|
|
ULONG Value;
|
|
ULONG ValidityFlags;
|
|
|
|
} STRSD_KEY_LOOKUP, *PSTRSD_KEY_LOOKUP;
|
|
|
|
typedef enum _STRSD_SID_TYPE {
|
|
ST_DOMAIN_RELATIVE = 0,
|
|
ST_WORLD,
|
|
ST_LOCALSY,
|
|
ST_LOCAL,
|
|
ST_CREATOR,
|
|
ST_NTAUTH,
|
|
ST_BUILTIN,
|
|
ST_ROOT_DOMAIN_RELATIVE
|
|
} STRSD_SID_TYPE;
|
|
|
|
//
|
|
// This structure is used to map account monikers to sids
|
|
//
|
|
typedef struct _STRSD_SID_LOOKUP {
|
|
|
|
BOOLEAN Valid;
|
|
WCHAR Key[SDDL_ALIAS_SIZE+2];
|
|
ULONG KeyLen;
|
|
PSID Sid;
|
|
ULONG Rid;
|
|
STRSD_SID_TYPE SidType;
|
|
DWORD SidBuff[ sizeof( SID ) / sizeof( DWORD ) + 5];
|
|
} STRSD_SID_LOOKUP, *PSTRSD_SID_LOOKUP;
|
|
|
|
//
|
|
// Globally defined sids
|
|
//
|
|
/* JINHUANG: not used anywhere
|
|
DWORD PersonalSelfBuiltSid[sizeof(SID)/sizeof(DWORD) + 2];
|
|
DWORD AuthUserBuiltSid[sizeof(SID)/sizeof(DWORD) + 2];
|
|
DWORD CreatorOwnerBuiltSid[sizeof(SID)/sizeof(DWORD) + 2];
|
|
DWORD CreatorGroupBuiltSid[sizeof(SID)/sizeof(DWORD) + 2];
|
|
PSID PersonalSelfSid = (PSID)PersonalSelfBuiltSid;
|
|
PSID AuthUserSid = (PSID)AuthUserBuiltSid;
|
|
PSID CreatorOwnerSid = (PSID)CreatorOwnerBuiltSid;
|
|
PSID CreatorGroupSid = (PSID)CreatorGroupBuiltSid;
|
|
*/
|
|
|
|
CRITICAL_SECTION SddlSidLookupCritical;
|
|
static DWORD SidTableReinitializeInstance=0;
|
|
|
|
// JINHUANG 3/26 BVT break for dcpromo
|
|
//
|
|
// Some of the Valid fields were preset to TRUE with NULL Sid field. The SidLookup
|
|
// table initialization is stopped if Status is not SUCCESS. So if error occurs,
|
|
// for example, no domain info as in dcpromo, other SIDs will not be initialized
|
|
// but the Valid fields are set to TRUE (with NULL SIDs).
|
|
//
|
|
// changes: 1) preset Valid field to FALSE all all lookups and set the Valid to TRUE if
|
|
// the SID is really initialized
|
|
// 2) do not stop the initialization process if an error occurs
|
|
// if the Valid field is already TRUE (already initialized), skip the row
|
|
//
|
|
static STRSD_SID_LOOKUP SidLookup[] = {
|
|
{ FALSE, SDDL_DOMAIN_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_DOMAIN_ADMINISTRATORS ),
|
|
NULL, DOMAIN_GROUP_RID_ADMINS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_GUESTS, SDDL_LEN_TAG( SDDL_DOMAIN_GUESTS ),
|
|
NULL, DOMAIN_GROUP_RID_GUESTS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_USERS, SDDL_LEN_TAG( SDDL_DOMAIN_USERS ),
|
|
NULL, DOMAIN_GROUP_RID_USERS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_DOMAIN_CONTROLLERS, SDDL_LEN_TAG( SDDL_DOMAIN_DOMAIN_CONTROLLERS ),
|
|
NULL, DOMAIN_GROUP_RID_CONTROLLERS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_COMPUTERS, SDDL_LEN_TAG( SDDL_DOMAIN_COMPUTERS ),
|
|
NULL, DOMAIN_GROUP_RID_COMPUTERS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_SCHEMA_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_SCHEMA_ADMINISTRATORS ),
|
|
NULL, DOMAIN_GROUP_RID_SCHEMA_ADMINS, ST_ROOT_DOMAIN_RELATIVE, 0 }, // should be root domain only ST_DOMAIN_RELATIVE,
|
|
{ FALSE, SDDL_ENTERPRISE_ADMINS, SDDL_LEN_TAG( SDDL_ENTERPRISE_ADMINS ),
|
|
NULL, DOMAIN_GROUP_RID_ENTERPRISE_ADMINS, ST_ROOT_DOMAIN_RELATIVE, 0 }, // root domain only
|
|
{ FALSE, SDDL_CERT_SERV_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_CERT_SERV_ADMINISTRATORS ),
|
|
NULL, DOMAIN_GROUP_RID_CERT_ADMINS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_ACCOUNT_OPERATORS, SDDL_LEN_TAG( SDDL_ACCOUNT_OPERATORS ),
|
|
NULL, DOMAIN_ALIAS_RID_ACCOUNT_OPS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_BACKUP_OPERATORS, SDDL_LEN_TAG( SDDL_BACKUP_OPERATORS ),
|
|
NULL, DOMAIN_ALIAS_RID_BACKUP_OPS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_PRINTER_OPERATORS, SDDL_LEN_TAG( SDDL_PRINTER_OPERATORS ),
|
|
NULL, DOMAIN_ALIAS_RID_PRINT_OPS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_SERVER_OPERATORS, SDDL_LEN_TAG( SDDL_SERVER_OPERATORS ),
|
|
NULL, DOMAIN_ALIAS_RID_SYSTEM_OPS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_REPLICATOR, SDDL_LEN_TAG( SDDL_REPLICATOR ),
|
|
NULL, DOMAIN_ALIAS_RID_REPLICATOR, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_RAS_SERVERS, SDDL_LEN_TAG( SDDL_RAS_SERVERS ),
|
|
NULL, DOMAIN_ALIAS_RID_RAS_SERVERS, ST_DOMAIN_RELATIVE, 0 }, // ST_LOCAL
|
|
{ FALSE, SDDL_AUTHENTICATED_USERS, SDDL_LEN_TAG( SDDL_AUTHENTICATED_USERS ),
|
|
NULL, SECURITY_AUTHENTICATED_USER_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_PERSONAL_SELF, SDDL_LEN_TAG( SDDL_PERSONAL_SELF ),
|
|
NULL, SECURITY_PRINCIPAL_SELF_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_CREATOR_OWNER, SDDL_LEN_TAG( SDDL_CREATOR_OWNER ),
|
|
NULL, SECURITY_CREATOR_OWNER_RID, ST_CREATOR, 0 },
|
|
{ FALSE, SDDL_CREATOR_GROUP, SDDL_LEN_TAG( SDDL_CREATOR_GROUP ),
|
|
NULL, SECURITY_CREATOR_GROUP_RID, ST_CREATOR, 0 },
|
|
{ FALSE, SDDL_LOCAL_SYSTEM, SDDL_LEN_TAG( SDDL_LOCAL_SYSTEM ),
|
|
NULL, SECURITY_LOCAL_SYSTEM_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_INTERACTIVE, SDDL_LEN_TAG( SDDL_INTERACTIVE ),
|
|
NULL, SECURITY_INTERACTIVE_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_NETWORK, SDDL_LEN_TAG( SDDL_NETWORK ),
|
|
NULL, SECURITY_NETWORK_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_SERVICE, SDDL_LEN_TAG( SDDL_SERVICE ),
|
|
NULL, SECURITY_SERVICE_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_ENTERPRISE_DOMAIN_CONTROLLERS, SDDL_LEN_TAG( SDDL_ENTERPRISE_DOMAIN_CONTROLLERS ),
|
|
NULL, SECURITY_SERVER_LOGON_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_RESTRICTED_CODE, SDDL_LEN_TAG( SDDL_RESTRICTED_CODE ),
|
|
NULL, SECURITY_RESTRICTED_CODE_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_ANONYMOUS, SDDL_LEN_TAG( SDDL_ANONYMOUS ),
|
|
NULL, SECURITY_ANONYMOUS_LOGON_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_LOCAL_ADMIN, SDDL_LEN_TAG( SDDL_LOCAL_ADMIN ),
|
|
NULL, DOMAIN_USER_RID_ADMIN, ST_LOCAL, 0 },
|
|
{ FALSE, SDDL_LOCAL_GUEST, SDDL_LEN_TAG( SDDL_LOCAL_GUEST ),
|
|
NULL, DOMAIN_USER_RID_GUEST, ST_LOCAL, 0 },
|
|
{ FALSE, SDDL_BUILTIN_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_BUILTIN_ADMINISTRATORS ),
|
|
NULL, DOMAIN_ALIAS_RID_ADMINS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_BUILTIN_GUESTS, SDDL_LEN_TAG( SDDL_BUILTIN_GUESTS ),
|
|
NULL, DOMAIN_ALIAS_RID_GUESTS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_BUILTIN_USERS, SDDL_LEN_TAG( SDDL_BUILTIN_USERS ),
|
|
NULL, DOMAIN_ALIAS_RID_USERS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_POWER_USERS, SDDL_LEN_TAG( SDDL_POWER_USERS ),
|
|
NULL, DOMAIN_ALIAS_RID_POWER_USERS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_EVERYONE, SDDL_LEN_TAG( SDDL_EVERYONE ),
|
|
NULL, SECURITY_WORLD_RID, ST_WORLD, 0 },
|
|
{ FALSE, SDDL_GROUP_POLICY_ADMINS, SDDL_LEN_TAG( SDDL_GROUP_POLICY_ADMINS ),
|
|
NULL, DOMAIN_GROUP_RID_POLICY_ADMINS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_ALIAS_PREW2KCOMPACC, SDDL_LEN_TAG( SDDL_ALIAS_PREW2KCOMPACC ),
|
|
NULL, DOMAIN_ALIAS_RID_PREW2KCOMPACCESS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_LOCAL_SERVICE, SDDL_LEN_TAG( SDDL_LOCAL_SERVICE ),
|
|
NULL, SECURITY_LOCAL_SERVICE_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_NETWORK_SERVICE, SDDL_LEN_TAG( SDDL_NETWORK_SERVICE ),
|
|
NULL, SECURITY_NETWORK_SERVICE_RID, ST_NTAUTH, 0 },
|
|
{ FALSE, SDDL_REMOTE_DESKTOP, SDDL_LEN_TAG( SDDL_REMOTE_DESKTOP ),
|
|
NULL, DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_NETWORK_CONFIGURATION_OPS, SDDL_LEN_TAG( SDDL_NETWORK_CONFIGURATION_OPS ),
|
|
NULL, DOMAIN_ALIAS_RID_NETWORK_CONFIGURATION_OPS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_PERFMON_USERS, SDDL_LEN_TAG( SDDL_PERFMON_USERS ),
|
|
NULL, DOMAIN_ALIAS_RID_MONITORING_USERS, ST_BUILTIN, 0 },
|
|
{ FALSE, SDDL_PERFLOG_USERS, SDDL_LEN_TAG( SDDL_PERFLOG_USERS ),
|
|
NULL, DOMAIN_ALIAS_RID_LOGGING_USERS, ST_BUILTIN, 0 }
|
|
};
|
|
|
|
|
|
|
|
STRSD_SID_LOOKUP SidLookupDomOrRootDomRelative[] = {
|
|
{ FALSE, SDDL_DOMAIN_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_DOMAIN_ADMINISTRATORS ),
|
|
NULL, DOMAIN_GROUP_RID_ADMINS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_GUESTS, SDDL_LEN_TAG( SDDL_DOMAIN_GUESTS ),
|
|
NULL, DOMAIN_GROUP_RID_GUESTS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_USERS, SDDL_LEN_TAG( SDDL_DOMAIN_USERS ),
|
|
NULL, DOMAIN_GROUP_RID_USERS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_DOMAIN_CONTROLLERS, SDDL_LEN_TAG( SDDL_DOMAIN_DOMAIN_CONTROLLERS ),
|
|
NULL, DOMAIN_GROUP_RID_CONTROLLERS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_DOMAIN_COMPUTERS, SDDL_LEN_TAG( SDDL_DOMAIN_COMPUTERS ),
|
|
NULL, DOMAIN_GROUP_RID_COMPUTERS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_CERT_SERV_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_CERT_SERV_ADMINISTRATORS ),
|
|
NULL, DOMAIN_GROUP_RID_CERT_ADMINS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_RAS_SERVERS, SDDL_LEN_TAG( SDDL_RAS_SERVERS ),
|
|
NULL, DOMAIN_ALIAS_RID_RAS_SERVERS, ST_DOMAIN_RELATIVE, 0 }, // ST_LOCAL
|
|
{ FALSE, SDDL_GROUP_POLICY_ADMINS, SDDL_LEN_TAG( SDDL_GROUP_POLICY_ADMINS ),
|
|
NULL, DOMAIN_GROUP_RID_POLICY_ADMINS, ST_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_SCHEMA_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_SCHEMA_ADMINISTRATORS ),
|
|
NULL, DOMAIN_GROUP_RID_SCHEMA_ADMINS, ST_ROOT_DOMAIN_RELATIVE, 0 },
|
|
{ FALSE, SDDL_ENTERPRISE_ADMINS, SDDL_LEN_TAG( SDDL_ENTERPRISE_ADMINS ),
|
|
NULL, DOMAIN_GROUP_RID_ENTERPRISE_ADMINS, ST_ROOT_DOMAIN_RELATIVE, 0 }
|
|
};
|
|
|
|
|
|
|
|
|
|
static DWORD RootDomSidBuf[sizeof(SID)/sizeof(DWORD)+5];
|
|
static BOOL RootDomInited=FALSE;
|
|
|
|
#define STRSD_REINITIALIZE_ENTER 1
|
|
#define STRSD_REINITIALIZE_LEAVE 2
|
|
|
|
BOOLEAN
|
|
InitializeSidLookupTable(
|
|
IN BYTE InitFlag
|
|
);
|
|
|
|
//
|
|
// Control Lookup table
|
|
//
|
|
static STRSD_KEY_LOOKUP ControlLookup[] = {
|
|
{ SDDL_PROTECTED, SDDL_LEN_TAG( SDDL_PROTECTED ), SE_DACL_PROTECTED, SDDL_VALID_DACL },
|
|
{ SDDL_AUTO_INHERIT_REQ, SDDL_LEN_TAG( SDDL_AUTO_INHERIT_REQ ), SE_DACL_AUTO_INHERIT_REQ, SDDL_VALID_DACL },
|
|
{ SDDL_AUTO_INHERITED, SDDL_LEN_TAG( SDDL_AUTO_INHERITED ), SE_DACL_AUTO_INHERITED, SDDL_VALID_DACL },
|
|
{ SDDL_PROTECTED, SDDL_LEN_TAG( SDDL_PROTECTED ), SE_SACL_PROTECTED, SDDL_VALID_SACL },
|
|
{ SDDL_AUTO_INHERIT_REQ, SDDL_LEN_TAG( SDDL_AUTO_INHERIT_REQ ), SE_SACL_AUTO_INHERIT_REQ, SDDL_VALID_SACL },
|
|
{ SDDL_AUTO_INHERITED, SDDL_LEN_TAG( SDDL_AUTO_INHERITED ), SE_SACL_AUTO_INHERITED, SDDL_VALID_SACL }
|
|
};
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
BOOL
|
|
LocalConvertStringSidToSid(
|
|
IN PWSTR String,
|
|
OUT PSID *SID,
|
|
OUT PWSTR *End
|
|
);
|
|
|
|
PSTRSD_SID_LOOKUP
|
|
LookupSidInTable(
|
|
IN PWSTR String, OPTIONAL
|
|
IN PSID Sid OPTIONAL,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN PSTRSD_SID_LOOKUP tSidLookupDomOrRootDomRelativeTable OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain,
|
|
OUT PVOID *pSASid
|
|
);
|
|
|
|
DWORD
|
|
LocalGetSidForString(
|
|
IN PWSTR String,
|
|
OUT PSID *SID,
|
|
OUT PWSTR *End,
|
|
OUT PBOOLEAN FreeSid,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN PSTRSD_SID_LOOKUP tSidLookupDomOrRootDomRelativeTable OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain
|
|
);
|
|
|
|
PSTRSD_KEY_LOOKUP
|
|
LookupAccessMaskInTable(
|
|
IN PWSTR String, OPTIONAL
|
|
IN ULONG AccessMask, OPTIONAL
|
|
IN ULONG LookupFlags
|
|
);
|
|
|
|
|
|
PSTRSD_KEY_LOOKUP
|
|
LookupAceTypeInTable(
|
|
IN PWSTR String, OPTIONAL
|
|
IN ULONG AceType, OPTIONAL
|
|
IN ULONG LookupFlags
|
|
);
|
|
|
|
PSTRSD_KEY_LOOKUP
|
|
LookupAceFlagsInTable(
|
|
IN PWSTR String, OPTIONAL
|
|
IN ULONG AceFlags OPTIONAL,
|
|
IN ULONG LookupFlags
|
|
);
|
|
|
|
DWORD
|
|
LocalGetStringForSid(
|
|
IN PSID Sid,
|
|
OUT PWSTR *String,
|
|
IN PSID RootDomainSid OPTIONAL
|
|
);
|
|
|
|
DWORD
|
|
LocalGetStringForControl(
|
|
IN SECURITY_DESCRIPTOR_CONTROL ControlCode,
|
|
IN ULONG LookupFlags,
|
|
OUT PWSTR *ControlString
|
|
);
|
|
|
|
DWORD
|
|
LocalGetSDControlForString (
|
|
IN PWSTR AclString,
|
|
IN ULONG LookupFlags,
|
|
OUT SECURITY_DESCRIPTOR_CONTROL *pControl,
|
|
OUT PWSTR *End
|
|
);
|
|
|
|
DWORD
|
|
LocalGetAclForString (
|
|
IN PWSTR AclString,
|
|
IN BOOLEAN ConvertAsDacl,
|
|
OUT PACL *Acl,
|
|
OUT PWSTR *End,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN PSTRSD_SID_LOOKUP tSidLookupDomOrRootDomRelativeTable OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain
|
|
);
|
|
|
|
DWORD
|
|
LocalConvertAclToString(
|
|
IN PACL Acl,
|
|
IN BOOLEAN AclPresent,
|
|
IN BOOLEAN ConvertAsDacl,
|
|
OUT PWSTR *AclString,
|
|
OUT PDWORD AclStringSize,
|
|
IN PSID RootDomainSid OPTIONAL
|
|
);
|
|
|
|
DWORD
|
|
LocalConvertSDToStringSD_Rev1(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT LPWSTR *StringSecurityDescriptor,
|
|
OUT PULONG StringSecurityDescriptorLen OPTIONAL
|
|
);
|
|
|
|
DWORD
|
|
LocalConvertStringSDToSD_Rev1(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain,
|
|
IN LPCWSTR StringSecurityDescriptor,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
);
|
|
|
|
BOOL
|
|
SddlpGetRootDomainSid(void);
|
|
|
|
//
|
|
// Exported functions
|
|
//
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertSidToStringSidA(
|
|
IN PSID Sid,
|
|
OUT LPSTR *StringSid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to ConvertSidToStringSidW
|
|
|
|
--*/
|
|
{
|
|
LPWSTR StringSidW = NULL;
|
|
ULONG AnsiLen, WideLen;
|
|
BOOL ReturnValue;
|
|
|
|
if ( NULL == StringSid ) {
|
|
//
|
|
// invalid parameter
|
|
//
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return(FALSE);
|
|
}
|
|
|
|
ReturnValue = ConvertSidToStringSidW( Sid, &StringSidW );
|
|
|
|
if ( ReturnValue ) {
|
|
|
|
WideLen = wcslen( StringSidW ) + 1;
|
|
|
|
AnsiLen = WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
StringSidW,
|
|
WideLen,
|
|
*StringSid,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( AnsiLen != 0 ) {
|
|
|
|
*StringSid = LocalAlloc( LMEM_FIXED, AnsiLen );
|
|
|
|
if ( *StringSid == NULL ) {
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
ReturnValue = FALSE;
|
|
|
|
} else {
|
|
|
|
AnsiLen = WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
StringSidW,
|
|
WideLen,
|
|
*StringSid,
|
|
AnsiLen,
|
|
NULL,
|
|
NULL );
|
|
ASSERT( AnsiLen != 0 );
|
|
|
|
if ( AnsiLen == 0 ) {
|
|
|
|
ReturnValue = FALSE;
|
|
//
|
|
// jinhuang: failed, free the buffer
|
|
//
|
|
LocalFree(*StringSid);
|
|
*StringSid = NULL;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// jinhuang: free the wide buffer
|
|
//
|
|
if ( StringSidW ) {
|
|
LocalFree(StringSidW);
|
|
}
|
|
|
|
if ( ReturnValue ) {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
return( ReturnValue );
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertSidToStringSidW(
|
|
IN PSID Sid,
|
|
OUT LPWSTR *StringSid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a SID into a string representation of a SID, suitable for framing or
|
|
display
|
|
|
|
Arguments:
|
|
|
|
Sid - SID to be converted.
|
|
|
|
StringSid - Where the converted SID is returned. Allocated via LocalAlloc and needs to
|
|
be freed via LocalFree.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
Extended error status is available using GetLastError.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeStringSid;
|
|
|
|
if ( NULL == Sid || NULL == StringSid ) {
|
|
//
|
|
// invalid parameter
|
|
//
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Convert using the Rtl functions
|
|
//
|
|
Status = RtlConvertSidToUnicodeString( &UnicodeStringSid, Sid, TRUE );
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
BaseSetLastNTError( Status );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Convert it to the proper allocator
|
|
//
|
|
*StringSid = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
|
UnicodeStringSid.Length + sizeof( WCHAR ) );
|
|
|
|
if ( *StringSid == NULL ) {
|
|
|
|
RtlFreeUnicodeString( &UnicodeStringSid );
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
RtlCopyMemory( *StringSid, UnicodeStringSid.Buffer, UnicodeStringSid.Length );
|
|
RtlFreeUnicodeString( &UnicodeStringSid );
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSidToSidA(
|
|
IN LPCSTR StringSid,
|
|
OUT PSID *Sid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to ConvertStringSidToSidW
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING Unicode;
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
BOOL Result;
|
|
|
|
if ( NULL == StringSid || NULL == Sid ) {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return(FALSE);
|
|
}
|
|
|
|
RtlInitAnsiString( &AnsiString, StringSid );
|
|
|
|
Status = SddlpAnsiStringToUnicodeString(&Unicode,
|
|
&AnsiString);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
BaseSetLastNTError( Status );
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
Result = ConvertStringSidToSidW( ( LPCWSTR )Unicode.Buffer, Sid );
|
|
|
|
LocalFree( Unicode.Buffer );
|
|
|
|
if ( Result ) {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
return( Result );
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSidToSidW(
|
|
IN LPCWSTR StringSid,
|
|
OUT PSID *Sid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a stringized SID into a valid, functional SID
|
|
|
|
Arguments:
|
|
|
|
StringSid - SID to be converted.
|
|
|
|
Sid - Where the converted SID is returned. Buffer is allocated via LocalAlloc and should
|
|
be free via LocalFree.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
Extended error status is available using GetLastError.
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL name was given
|
|
|
|
ERROR_INVALID_SID - The format of the given sid was incorrect
|
|
|
|
--*/
|
|
{
|
|
PWSTR End = NULL;
|
|
BOOL ReturnValue = FALSE;
|
|
PSTRSD_SID_LOOKUP MatchedEntry=NULL;
|
|
PSID pSASid=NULL;
|
|
ULONG Len=0;
|
|
DWORD SaveCode=0;
|
|
DWORD Err=0;
|
|
|
|
if ( StringSid == NULL || Sid == NULL ) {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
|
|
} else {
|
|
|
|
ReturnValue = LocalConvertStringSidToSid( ( PWSTR )StringSid, Sid, &End );
|
|
|
|
|
|
if ( ReturnValue == TRUE ) {
|
|
|
|
if ( ( ULONG )( End - StringSid ) != wcslen( StringSid ) ) {
|
|
|
|
SetLastError( ERROR_INVALID_SID );
|
|
LocalFree( *Sid );
|
|
*Sid = FALSE;
|
|
ReturnValue = FALSE;
|
|
|
|
} else {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
} else {
|
|
|
|
SaveCode = GetLastError();
|
|
|
|
//
|
|
// lookup in the SidLookup table to see if it's pre-defined
|
|
//
|
|
|
|
MatchedEntry = LookupSidInTable( (PWSTR)StringSid,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
(PVOID *)&pSASid);
|
|
|
|
if ( MatchedEntry && MatchedEntry->Sid ) {
|
|
|
|
//
|
|
// find it in the table, check if the input string is valid
|
|
//
|
|
if ( wcslen( (PWSTR)StringSid ) != MatchedEntry->KeyLen ) {
|
|
|
|
//
|
|
// the total string length doesn't match the table define
|
|
//
|
|
SetLastError(ERROR_INVALID_SID);
|
|
|
|
} else {
|
|
|
|
//
|
|
// matched! now copy it to the output buffer
|
|
//
|
|
Len = RtlLengthSid ( MatchedEntry->Sid );
|
|
|
|
*Sid = ( PSID )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Len );
|
|
|
|
if ( *Sid == NULL ) {
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
|
|
} else {
|
|
|
|
Err = RtlNtStatusToDosError(RtlCopySid ( Len, *Sid, MatchedEntry->Sid ) );
|
|
|
|
if ( ERROR_SUCCESS == Err ) {
|
|
|
|
ReturnValue = TRUE;
|
|
|
|
} else {
|
|
|
|
LocalFree(*Sid);
|
|
*Sid = NULL;
|
|
}
|
|
|
|
SetLastError(Err);
|
|
|
|
}
|
|
}
|
|
|
|
} else if ( pSASid && wcslen((PWSTR)StringSid) == SDDL_LEN_TAG( SDDL_SCHEMA_ADMINISTRATORS ) ) {
|
|
//
|
|
// this is schema admin SID
|
|
//
|
|
*Sid = pSASid;
|
|
pSASid = NULL;
|
|
|
|
ReturnValue = TRUE;
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
} else {
|
|
//
|
|
// reset last error
|
|
//
|
|
SetLastError(SaveCode);
|
|
}
|
|
}
|
|
|
|
if ( pSASid ) {
|
|
LocalFree(pSASid);
|
|
}
|
|
}
|
|
|
|
return( ReturnValue );
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSecurityDescriptorToSecurityDescriptorA(
|
|
IN LPCSTR StringSecurityDescriptor,
|
|
IN DWORD StringSDRevision,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to ConvertStringSecurityDescriptorToSecurityDescriptorW
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING Unicode;
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
BOOL Result;
|
|
|
|
if ( NULL == StringSecurityDescriptor ||
|
|
NULL == SecurityDescriptor ) {
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return(FALSE);
|
|
}
|
|
|
|
RtlInitAnsiString( &AnsiString, StringSecurityDescriptor );
|
|
|
|
Status = SddlpAnsiStringToUnicodeString(&Unicode,
|
|
&AnsiString);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
BaseSetLastNTError( Status );
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
Result = ConvertStringSecurityDescriptorToSecurityDescriptorW( ( LPCWSTR )Unicode.Buffer,
|
|
StringSDRevision,
|
|
SecurityDescriptor,
|
|
SecurityDescriptorSize);
|
|
|
|
LocalFree( Unicode.Buffer );
|
|
|
|
if ( Result ) {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
return( Result );
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
|
IN LPCWSTR StringSecurityDescriptor,
|
|
IN DWORD StringSDRevision,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a stringized Security descriptor into a valid, functional security
|
|
descriptor
|
|
|
|
Ex:
|
|
SD:[O:xyz][G:xyz][D: (Ace1)(Ace2)][S: (Ace3)(Ace4)]
|
|
where some Ace is (OA;CIIO; DS_READ; OT: abc; IOT: abc; SID: xyz)
|
|
|
|
So a possible Security descriptor may be (as all one long string):
|
|
|
|
L"O:AOG:DAD:(A;IO;RPRWXRCWDWO;;;S-1-0-0)(OA;CI;RWX;af110080-1b13-11d0-af10-0020afd3606c;"
|
|
L"a153d9e0-1b13-11d0-af10-0020afd3606c;AUS)(A;SAFA;0x7800003F;;;DA)(OA;FA;X;"
|
|
L"954378e0-1b13-11d0-af10-0020afd3606c;880b12a0-1b13-11d0-af10-0020afd3606c;PO)"
|
|
|
|
would build a security descriptor:
|
|
|
|
Revision: 0x1
|
|
Sbz1: 0x0
|
|
Control: 0x8014
|
|
Owner: S-1-5-32-548
|
|
|
|
Group:S-1-5-32-544
|
|
|
|
Dacl: Revision: 4
|
|
AceCount: 2
|
|
InUse: 84
|
|
Free: 52
|
|
Flags: 0
|
|
Ace 0:
|
|
Type: 0
|
|
Flags: 0x1
|
|
Size: 0x14
|
|
Mask: 0xe00e0010
|
|
S-1-0-0
|
|
|
|
Ace 1:
|
|
Type: 5
|
|
Flags: 0x2
|
|
Size: 0x38
|
|
Mask: 0xe0000000
|
|
af110080-1b13-11d0-af100020afd3606c
|
|
a153d9e0-1b13-11d0-af100020afd3606c
|
|
S-1-5-11
|
|
|
|
|
|
sacl: Revision: 4
|
|
AceCount: 2
|
|
InUse: 92
|
|
Free: 44
|
|
Flags: 0
|
|
Ace 0:
|
|
Type: 2
|
|
Flags: 0xc0
|
|
Size: 0x18
|
|
Mask: 0xe0000000
|
|
S-1-5-32-544
|
|
|
|
Ace 1:
|
|
Type: 7
|
|
Flags: 0x80
|
|
Size: 0x3c
|
|
Mask: 0x20000000
|
|
954378e0-1b13-11d0-af100020afd3606c
|
|
880b12a0-1b13-11d0-af100020afd3606c
|
|
S-1-5-32-550
|
|
Arguments:
|
|
|
|
StringSecurityDescriptor - Stringized security descriptor to be converted.
|
|
|
|
StringSDRevision - String revision of the input string SD
|
|
|
|
SecurityDescriptor - Where the converted SD is returned. Buffer is allocated via
|
|
LocalAlloc and should be free via LocalFree. The returned security descriptor
|
|
is always self relative
|
|
|
|
SecurityDescriptorSize - OPTIONAL. If non-NULL, the size of the converted security
|
|
descriptor is returned here.
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
Extended error status is available using GetLastError.
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL input or output parameter was given
|
|
|
|
ERROR_UNKNOWN_REVISION - An unsupported revision was given
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Little elementary parameter checking...
|
|
//
|
|
if ( StringSecurityDescriptor == NULL || SecurityDescriptor == NULL ) {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
switch ( StringSDRevision ) {
|
|
case SDDL_REVISION_1:
|
|
|
|
Err = LocalConvertStringSDToSD_Rev1( NULL, // no root domain sid is provided
|
|
NULL, // no domain sid is provided for this API
|
|
FALSE, //TRUE, do not default to domain for EA/SA
|
|
StringSecurityDescriptor,
|
|
SecurityDescriptor,
|
|
SecurityDescriptorSize);
|
|
break;
|
|
|
|
default:
|
|
|
|
Err = ERROR_UNKNOWN_REVISION;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
SetLastError( Err );
|
|
|
|
return( Err == ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertSecurityDescriptorToStringSecurityDescriptorA(
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN DWORD RequestedStringSDRevision,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT LPSTR *StringSecurityDescriptor,
|
|
OUT PULONG StringSecurityDescriptorLen OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ANSI thunk to ConvertSecurityDescriptorToStringSecurityDescriptorW
|
|
|
|
--*/
|
|
{
|
|
LPWSTR StringSecurityDescriptorW = NULL;
|
|
ULONG AnsiLen, WideLen = 0;
|
|
BOOL ReturnValue ;
|
|
|
|
if ( StringSecurityDescriptor == NULL ) {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return( FALSE );
|
|
}
|
|
|
|
ReturnValue = ConvertSecurityDescriptorToStringSecurityDescriptorW(
|
|
SecurityDescriptor,
|
|
RequestedStringSDRevision,
|
|
SecurityInformation,
|
|
&StringSecurityDescriptorW,
|
|
&WideLen );
|
|
|
|
if ( ReturnValue ) {
|
|
|
|
|
|
// jinhuang: WindeLen is returned from previous call
|
|
// WideLen = wcslen( StringSecurityDescriptorW ) + 1;
|
|
|
|
|
|
AnsiLen = WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
StringSecurityDescriptorW,
|
|
WideLen + 1,
|
|
*StringSecurityDescriptor,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( AnsiLen != 0 ) {
|
|
|
|
*StringSecurityDescriptor = LocalAlloc( LMEM_FIXED, AnsiLen );
|
|
|
|
if ( *StringSecurityDescriptor == NULL ) {
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
ReturnValue = FALSE;
|
|
|
|
} else {
|
|
|
|
AnsiLen = WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
StringSecurityDescriptorW,
|
|
WideLen + 1,
|
|
*StringSecurityDescriptor,
|
|
AnsiLen,
|
|
NULL,
|
|
NULL );
|
|
ASSERT( AnsiLen != 0 );
|
|
|
|
if ( AnsiLen == 0 ) {
|
|
|
|
LocalFree(*StringSecurityDescriptor);
|
|
*StringSecurityDescriptor = NULL;
|
|
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
//
|
|
// jinhuang
|
|
// output the length (optional)
|
|
//
|
|
if ( StringSecurityDescriptorLen ) {
|
|
*StringSecurityDescriptorLen = AnsiLen;
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
//
|
|
// jinhuang
|
|
// StringSecurityDescriptorW should be freed
|
|
//
|
|
|
|
LocalFree(StringSecurityDescriptorW);
|
|
|
|
}
|
|
|
|
if ( ReturnValue ) {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
return( ReturnValue );
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertSecurityDescriptorToStringSecurityDescriptorW(
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN DWORD RequestedStringSDRevision,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT LPWSTR *StringSecurityDescriptor,
|
|
OUT PULONG StringSecurityDescriptorLen OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a security descriptor into a string version persuant to SDDL definition
|
|
|
|
Arguments:
|
|
|
|
SecurityDescriptor - Security Descriptor to be converted.
|
|
|
|
RequestedStringSDRevision - Requested revision of the output string security descriptor
|
|
|
|
SecurityInformation - security information of which to be converted
|
|
|
|
StringSecurityDescriptor - Where the converted SD is returned. Buffer is allocated via
|
|
LocalAlloc and should be free via LocalFree.
|
|
|
|
StringSecurityDescriptorLen - the optional length of the converted SD
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
Extended error status is available using GetLastError.
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL input or output parameter was given
|
|
|
|
ERROR_UNKNOWN_REVISION - An unsupported revision was given
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
|
|
//
|
|
// A little parameter checking...
|
|
//
|
|
|
|
if ( (SecurityDescriptor == NULL || SecurityInformation == 0) &&
|
|
StringSecurityDescriptor ) {
|
|
|
|
*StringSecurityDescriptor = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof( WCHAR ) );
|
|
|
|
if (*StringSecurityDescriptor) {
|
|
|
|
(*StringSecurityDescriptor)[0] = L'\0';
|
|
|
|
if (StringSecurityDescriptorLen) {
|
|
|
|
*StringSecurityDescriptorLen = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
}
|
|
|
|
SetLastError( Err );
|
|
|
|
return( Err == ERROR_SUCCESS );
|
|
|
|
}
|
|
|
|
if ( SecurityDescriptor == NULL || StringSecurityDescriptor == NULL ||
|
|
SecurityInformation == 0 ) {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
switch ( RequestedStringSDRevision ) {
|
|
case SDDL_REVISION_1:
|
|
|
|
Err = LocalConvertSDToStringSD_Rev1( NULL, // root domain sid is not privided
|
|
SecurityDescriptor,
|
|
SecurityInformation,
|
|
StringSecurityDescriptor,
|
|
StringSecurityDescriptorLen );
|
|
break;
|
|
|
|
default:
|
|
Err = ERROR_UNKNOWN_REVISION;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
SetLastError( Err );
|
|
|
|
return( Err == ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Private functions
|
|
//
|
|
BOOL
|
|
LocalConvertStringSidToSid (
|
|
IN PWSTR StringSid,
|
|
OUT PSID *Sid,
|
|
OUT PWSTR *End
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will convert a string representation of a SID back into
|
|
a sid. The expected format of the string is:
|
|
"S-1-5-32-549"
|
|
If a string in a different format or an incorrect or incomplete string
|
|
is given, the operation is failed.
|
|
|
|
The returned sid must be free via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
StringSid - The string to be converted
|
|
|
|
Sid - Where the created SID is to be returned
|
|
|
|
End - Where in the string we stopped processing
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success.
|
|
|
|
FALSE - Failure. Additional information returned from GetLastError(). Errors set are:
|
|
|
|
ERROR_SUCCESS indicates success
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY indicates a memory allocation for the ouput sid
|
|
failed
|
|
ERROR_INVALID_SID indicates that the given string did not represent a sid
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
UCHAR Revision, Subs;
|
|
SID_IDENTIFIER_AUTHORITY IDAuth;
|
|
PULONG SubAuth = NULL;
|
|
PWSTR CurrEnd, Curr, Next;
|
|
|
|
ULONG Index;
|
|
INT gBase=10;
|
|
INT lBase=10;
|
|
ULONGLONG Auto;
|
|
ULONG ulRev;
|
|
|
|
//
|
|
// ID authority is defined to be a 48 bit value
|
|
//
|
|
|
|
const ULONGLONG ullMaxIdAuthority = 0x0000FFFFFFFFFFFF;
|
|
|
|
WCHAR wchSidString[SDDL_SID_STRING_SIZE];
|
|
PWSTR pwszHeapSidStr = NULL;
|
|
|
|
if ( NULL == StringSid || NULL == Sid || NULL == End ) {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
// if ( wcslen( StringSid ) < 2 || ( *StringSid != L'S' && *( StringSid + 1 ) != L'-' ) ) {
|
|
|
|
//
|
|
// no need to check length because StringSid is NULL
|
|
// and if the first char is NULL, it won't access the second char
|
|
//
|
|
if ( (*StringSid != L'S' && *StringSid != L's') ||
|
|
*( StringSid + 1 ) != L'-' ) {
|
|
//
|
|
// string sid should always start with S-
|
|
//
|
|
SetLastError( ERROR_INVALID_SID );
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
Curr = StringSid + 2;
|
|
|
|
if ( (*Curr == L'0') &&
|
|
( *(Curr+1) == L'x' ||
|
|
*(Curr+1) == L'X' ) ) {
|
|
|
|
gBase = 16;
|
|
}
|
|
|
|
ulRev = wcstoul( Curr, &CurrEnd, gBase );
|
|
|
|
if (ulRev > 0xFF || CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) {
|
|
//
|
|
// no revision is provided, or invalid delimeter
|
|
//
|
|
SetLastError( ERROR_INVALID_SID );
|
|
return( FALSE );
|
|
}
|
|
|
|
Revision = (UCHAR)ulRev;
|
|
|
|
Curr = CurrEnd + 1;
|
|
|
|
//
|
|
// Count the number of characters in the indentifer authority...
|
|
//
|
|
Next = wcschr( Curr, L'-' );
|
|
if (Next == NULL || Next == Curr)
|
|
{
|
|
//
|
|
// no identifier authority
|
|
//
|
|
|
|
SetLastError( ERROR_INVALID_SID );
|
|
return( FALSE );
|
|
}
|
|
|
|
/*
|
|
Length = 6 doesn't mean each digit is a id authority value, could be 0x...
|
|
|
|
if ( Next != NULL && (Next - Curr == 6) ) {
|
|
|
|
for ( Index = 0; Index < 6; Index++ ) {
|
|
|
|
// IDAuth.Value[Index] = (UCHAR)Next[Index]; what is this ???
|
|
IDAuth.Value[Index] = (BYTE) (Curr[Index]-L'0');
|
|
}
|
|
|
|
Curr +=6;
|
|
|
|
} else {
|
|
*/
|
|
if ( (*Curr == L'0') &&
|
|
( *(Curr+1) == L'x' ||
|
|
*(Curr+1) == L'X' ) ) {
|
|
|
|
lBase = 16;
|
|
} else {
|
|
lBase = gBase;
|
|
}
|
|
|
|
Auto = _wcstoui64( Curr, &CurrEnd, lBase );
|
|
|
|
if (Auto > ullMaxIdAuthority)
|
|
{
|
|
//
|
|
// Must be overflow
|
|
//
|
|
|
|
SetLastError( ERROR_INVALID_SID );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if ( CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) {
|
|
//
|
|
// no revision is provided, or invalid delimeter
|
|
//
|
|
SetLastError( ERROR_INVALID_SID );
|
|
return( FALSE );
|
|
}
|
|
|
|
IDAuth.Value[5] = ( UCHAR )Auto & 0xFF;
|
|
IDAuth.Value[4] = ( UCHAR )(( Auto >> 8 ) & 0xFF );
|
|
IDAuth.Value[3] = ( UCHAR )(( Auto >> 16 ) & 0xFF );
|
|
IDAuth.Value[2] = ( UCHAR )(( Auto >> 24 ) & 0xFF );
|
|
IDAuth.Value[1] = ( UCHAR )(( Auto >> 32 ) & 0xFF );
|
|
IDAuth.Value[0] = ( UCHAR )(( Auto >> 40 ) & 0xFF );
|
|
Curr = CurrEnd;
|
|
// }
|
|
|
|
//
|
|
// Now, count the number of sub auths, at least one sub auth is required
|
|
//
|
|
Subs = 0;
|
|
Next = Curr;
|
|
|
|
//
|
|
// We'll have to count our sub authoritys one character at a time,
|
|
// since there are several deliminators that we can have...
|
|
//
|
|
|
|
while ( Next ) {
|
|
|
|
if ( *Next == L'-' && *(Next-1) != L'-') {
|
|
|
|
//
|
|
// do not allow two continuous '-'s
|
|
// We've found one!
|
|
//
|
|
Subs++;
|
|
|
|
if ( (*(Next+1) == L'0') &&
|
|
( *(Next+2) == L'x' ||
|
|
*(Next+2) == L'X' ) ) {
|
|
//
|
|
// this is hex indicator
|
|
//
|
|
Next += 2;
|
|
|
|
}
|
|
|
|
} else if ( *Next == SDDL_SEPERATORC || *Next == L'\0' ||
|
|
*Next == SDDL_ACE_ENDC || *Next == L' ' ||
|
|
( *(Next+1) == SDDL_DELIMINATORC &&
|
|
(*Next == L'G' || *Next == L'O' || *Next == L'S')) ) {
|
|
//
|
|
// space is a terminator too
|
|
//
|
|
if ( *( Next - 1 ) == L'-' ) {
|
|
//
|
|
// shouldn't allow a SID terminated with '-'
|
|
//
|
|
Err = ERROR_INVALID_SID;
|
|
Next--;
|
|
|
|
} else {
|
|
Subs++;
|
|
}
|
|
|
|
*End = Next;
|
|
break;
|
|
|
|
} else if ( !iswxdigit( *Next ) ) {
|
|
|
|
Err = ERROR_INVALID_SID;
|
|
*End = Next;
|
|
// Subs++;
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Note: SID is also used as a owner or group
|
|
//
|
|
// Some of the tags (namely 'D' for Dacl) fall under the category of iswxdigit, so
|
|
// if the current character is a character we care about and the next one is a
|
|
// delminiator, we'll quit
|
|
//
|
|
if ( *Next == L'D' && *( Next + 1 ) == SDDL_DELIMINATORC ) {
|
|
|
|
//
|
|
// We need to make a copy this of SID string from Curr to Next
|
|
//
|
|
|
|
LPCWSTR pwszStart = Curr;
|
|
|
|
//
|
|
// For majority, the our stack variable's size is large enough.
|
|
// This will perform much better.
|
|
//
|
|
|
|
if ( (Next - pwszStart) < SDDL_SID_STRING_SIZE )
|
|
{
|
|
Curr = wchSidString;
|
|
}
|
|
else
|
|
{
|
|
pwszHeapSidStr = (PWSTR)LocalAlloc(LPTR, ((Next - pwszStart) + 1) * sizeof(WCHAR));
|
|
if ( NULL == pwszHeapSidStr )
|
|
{
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Curr = pwszHeapSidStr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the sub-string of the sid and then NULL terminate it
|
|
//
|
|
|
|
memcpy(Curr, pwszStart, (Next - pwszStart) * sizeof(WCHAR));
|
|
Curr[(Next - pwszStart)] = L'\0';
|
|
|
|
*End = Next;
|
|
Subs++;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
Next++;
|
|
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
if ( Subs != 0 ) Subs--;
|
|
|
|
if ( Subs != 0 ) {
|
|
|
|
Curr++;
|
|
|
|
SubAuth = ( PULONG )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Subs * sizeof( ULONG ) );
|
|
|
|
if ( SubAuth == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
for ( Index = 0; Index < Subs; Index++ ) {
|
|
|
|
if ( (*Curr == L'0') &&
|
|
( *(Curr+1) == L'x' ||
|
|
*(Curr+1) == L'X' ) ) {
|
|
|
|
lBase = 16;
|
|
} else {
|
|
lBase = gBase;
|
|
}
|
|
|
|
SubAuth[Index] = wcstoul( Curr, &CurrEnd, lBase );
|
|
|
|
//
|
|
// Valid sub-authority ID must end with delimiter L'-', right parentheless, or one
|
|
// of the following:
|
|
// D:
|
|
// S:
|
|
// O:
|
|
// G:
|
|
//
|
|
|
|
if (CurrEnd != NULL &&
|
|
*CurrEnd != L'\0' &&
|
|
*CurrEnd != SDDL_ACE_ENDC &&
|
|
*CurrEnd != L'-' &&
|
|
( *(CurrEnd + 1) != SDDL_DELIMINATORC || // because of *CurrEnd != L'\0', we are safe
|
|
*CurrEnd != *SDDL_OWNER &&
|
|
*CurrEnd != *SDDL_GROUP &&
|
|
*CurrEnd != *SDDL_DACL &&
|
|
*CurrEnd != *SDDL_SACL
|
|
)
|
|
)
|
|
{
|
|
|
|
Err = ERROR_INVALID_SID;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Curr = CurrEnd + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Err = ERROR_INVALID_SID;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, create the SID
|
|
//
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
*Sid = ( PSID )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
|
sizeof( SID ) + Subs * sizeof( ULONG ) );
|
|
|
|
if ( *Sid == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
PISID ISid = ( PISID )*Sid;
|
|
ISid->Revision = Revision;
|
|
ISid->SubAuthorityCount = Subs;
|
|
RtlCopyMemory( &( ISid->IdentifierAuthority ), &IDAuth,
|
|
sizeof( SID_IDENTIFIER_AUTHORITY ) );
|
|
RtlCopyMemory( ISid->SubAuthority, SubAuth, Subs * sizeof( ULONG ) );
|
|
}
|
|
}
|
|
|
|
LocalFree( SubAuth );
|
|
|
|
if ( NULL != pwszHeapSidStr ) {
|
|
|
|
LocalFree(pwszHeapSidStr);
|
|
}
|
|
|
|
SetLastError( Err );
|
|
|
|
return( Err == ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
PSTRSD_SID_LOOKUP
|
|
LookupSidInTable(
|
|
IN PWSTR String OPTIONAL,
|
|
IN PSID Sid OPTIONAL,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN PSTRSD_SID_LOOKUP tSidLookupDomOrRootDomRelativeTable OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain,
|
|
IN PVOID *pSASid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine if the given sid or string sid exists in the lookup table.
|
|
|
|
A pointer to the matching static lookup entry is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
String - The string to be looked up
|
|
|
|
Sid - The sid to be looked up.
|
|
|
|
Return Value:
|
|
|
|
Lookup table entry if found
|
|
|
|
NULL if not found
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN LookupSid = FALSE;
|
|
DWORD i, SidCount = sizeof( SidLookup ) / sizeof( STRSD_SID_LOOKUP );
|
|
PSTRSD_SID_LOOKUP MatchedEntry = NULL;
|
|
DWORD DomainAdminIndex;
|
|
|
|
BOOL InitRootDomain;
|
|
ULONG Rid;
|
|
BOOL bIsSA = FALSE;
|
|
|
|
|
|
if ( String == NULL && Sid == NULL ) {
|
|
//
|
|
// JINHUANG: if both string and sid are NULL
|
|
// just return NULL
|
|
//
|
|
return((PSTRSD_SID_LOOKUP)NULL);
|
|
}
|
|
|
|
*pSASid = NULL;
|
|
|
|
InitRootDomain = FALSE;
|
|
DomainAdminIndex = SidCount;
|
|
|
|
if ( String == NULL ) {
|
|
//
|
|
// lookup on the Sid
|
|
//
|
|
LookupSid = TRUE;
|
|
|
|
//
|
|
// check if the RID is for Enterprise Admins
|
|
//
|
|
Rid = *( RtlSubAuthoritySid( Sid,
|
|
*( RtlSubAuthorityCountSid(Sid) ) - 1 ) );
|
|
|
|
if ( DOMAIN_GROUP_RID_ENTERPRISE_ADMINS == Rid ||
|
|
DOMAIN_GROUP_RID_SCHEMA_ADMINS == Rid ) {
|
|
|
|
InitRootDomain = TRUE;
|
|
if ( DOMAIN_GROUP_RID_SCHEMA_ADMINS == Rid ) {
|
|
bIsSA = TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( _wcsnicmp( String, SDDL_ENTERPRISE_ADMINS, SDDL_LEN_TAG( SDDL_ENTERPRISE_ADMINS ) ) == 0 ) {
|
|
//
|
|
// Enterprise admins string is requested
|
|
//
|
|
InitRootDomain = TRUE;
|
|
} else if ( _wcsnicmp( String, SDDL_SCHEMA_ADMINISTRATORS, SDDL_LEN_TAG( SDDL_SCHEMA_ADMINISTRATORS ) ) == 0 ) {
|
|
//
|
|
// schema admin is requested
|
|
//
|
|
InitRootDomain = TRUE;
|
|
bIsSA = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// the new API is the caller iff DomainSid != NULL
|
|
// on demand, initialize domain relative dynamic table for new API ()
|
|
//
|
|
|
|
if ( DomainSid ) {
|
|
|
|
//
|
|
// in this search, we will deal with the per thread table and not the global table
|
|
// in actuality, this per thread table is a proper subset of the global table
|
|
//
|
|
|
|
for ( i = 0; i < sizeof(SidLookupDomOrRootDomRelative)/sizeof(STRSD_SID_LOOKUP); i++ ) {
|
|
|
|
//
|
|
// for performance, only compute SID etc. if names match.
|
|
// in case the table is sparse, this heuristic comes in handy
|
|
// this doesn't preclude future lookup calls from leveraging useful work done here
|
|
//
|
|
|
|
if ( _wcsnicmp( String,
|
|
tSidLookupDomOrRootDomRelativeTable[ i ].Key,
|
|
tSidLookupDomOrRootDomRelativeTable[ i ].KeyLen ) == 0 ) {
|
|
|
|
|
|
if (tSidLookupDomOrRootDomRelativeTable[ i ].Valid == FALSE ||
|
|
tSidLookupDomOrRootDomRelativeTable[ i ].Sid == NULL) {
|
|
|
|
PSID RootDomainSidOrDomainSid = NULL;
|
|
|
|
if ( tSidLookupDomOrRootDomRelativeTable[ i ].SidType == ST_DOMAIN_RELATIVE )
|
|
RootDomainSidOrDomainSid = DomainSid;
|
|
else if ( tSidLookupDomOrRootDomRelativeTable[ i ].SidType == ST_ROOT_DOMAIN_RELATIVE &&
|
|
RootDomainSid )
|
|
RootDomainSidOrDomainSid = RootDomainSid;
|
|
else {
|
|
|
|
//
|
|
// this will happen when the RootDomainSid is not provided when using the
|
|
// new API and so ST_ROOT_DOMAIN_RELATIVE type SIDs will get resolved wrt
|
|
// the local m/c's root domain - so we will allow the lookup to continue
|
|
// in the normal way
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// do this for legacy-code reasons
|
|
//
|
|
|
|
tSidLookupDomOrRootDomRelativeTable[ i ].Sid =
|
|
( PSID )tSidLookupDomOrRootDomRelativeTable[ i ].SidBuff;
|
|
|
|
|
|
RtlCopyMemory( tSidLookupDomOrRootDomRelativeTable[ i ].Sid, RootDomainSidOrDomainSid,
|
|
RtlLengthSid( RootDomainSidOrDomainSid ) );
|
|
( ( PISID )( tSidLookupDomOrRootDomRelativeTable[ i ].Sid ) )->SubAuthorityCount++;
|
|
*( RtlSubAuthoritySid( tSidLookupDomOrRootDomRelativeTable[ i ].Sid,
|
|
*( RtlSubAuthorityCountSid( RootDomainSidOrDomainSid ) ) ) ) =
|
|
tSidLookupDomOrRootDomRelativeTable[ i ].Rid;
|
|
|
|
tSidLookupDomOrRootDomRelativeTable[ i ].Valid = TRUE;
|
|
|
|
}
|
|
|
|
if (tSidLookupDomOrRootDomRelativeTable[ i ].Valid == TRUE)
|
|
MatchedEntry = &tSidLookupDomOrRootDomRelativeTable[ i ];
|
|
|
|
//
|
|
// if we get here, we have to return MatchedEntry since we know that:
|
|
//
|
|
// (a) the new API is the caller (DomainSid != NULL) and
|
|
// (b) we are dealing with ST_DOMAIN_RELATIVE or
|
|
// ST_ROOT_DOMAIN_RELATIVE type trustees (with RootDomainSid provided)
|
|
// (c) there is a match with the trustee name such as "DA" or "EA"
|
|
//
|
|
|
|
return (MatchedEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
InitializeSidLookupTable(STRSD_REINITIALIZE_ENTER);
|
|
|
|
if ( InitRootDomain &&
|
|
RootDomainSid == NULL &&
|
|
DefaultToDomain == FALSE &&
|
|
( RootDomInited == FALSE ||
|
|
!RtlValidSid( (PSID)RootDomSidBuf ) ) ) {
|
|
|
|
//
|
|
// get the root domain sid (using ldap calls)
|
|
//
|
|
|
|
SddlpGetRootDomainSid();
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i = 0; i < SidCount; i++ ) {
|
|
|
|
//
|
|
// if new API and domain relative trustee, skip the entry
|
|
// since the global table won't be used in this scenario.
|
|
// matches for such entries are taken care of, in the per-thread table above
|
|
//
|
|
// cannot skip if InitRootDomain == TRUE since SDDL_DOMAIN_ADMINISTRATORS in
|
|
// SidLookup[] is potentially matched against, shortly
|
|
//
|
|
|
|
if ( InitRootDomain == FALSE &&
|
|
DomainSid &&
|
|
SidLookup[ i ].SidType == ST_DOMAIN_RELATIVE )
|
|
continue;
|
|
|
|
//
|
|
// if this is an entry that has been initialized, skip it
|
|
//
|
|
|
|
if ( SidLookup[ i ].Valid == FALSE ||
|
|
SidLookup[ i ].Sid == NULL ) {
|
|
|
|
|
|
if ( SidLookup[ i ].SidType == ST_ROOT_DOMAIN_RELATIVE &&
|
|
InitRootDomain ) {
|
|
|
|
if ( RootDomainSid != NULL ) {
|
|
|
|
EnterCriticalSection(&SddlSidLookupCritical);
|
|
|
|
RtlCopyMemory( SidLookup[ i ].Sid, RootDomainSid,
|
|
RtlLengthSid( RootDomainSid ) );
|
|
( ( PISID )( SidLookup[ i ].Sid ) )->SubAuthorityCount++;
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid,
|
|
*( RtlSubAuthorityCountSid( RootDomainSid ) ) ) ) =
|
|
SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
|
|
LeaveCriticalSection(&SddlSidLookupCritical);
|
|
|
|
} else if ( DefaultToDomain ) {
|
|
//
|
|
// should default EA to DA and SA to domain relative
|
|
//
|
|
} else {
|
|
|
|
if ( RootDomInited && RtlValidSid( (PSID)RootDomSidBuf ) &&
|
|
( ( SidLookup[ i ].Valid == FALSE ) ||
|
|
( SidLookup[ i ].Sid == NULL ) ) ) {
|
|
|
|
EnterCriticalSection(&SddlSidLookupCritical);
|
|
|
|
RtlCopyMemory( SidLookup[ i ].Sid, (PSID)RootDomSidBuf,
|
|
RtlLengthSid( (PSID)RootDomSidBuf ) );
|
|
( ( PISID )( SidLookup[ i ].Sid ) )->SubAuthorityCount++;
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid,
|
|
*( RtlSubAuthorityCountSid( (PSID)RootDomSidBuf ) ) ) ) =
|
|
SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
|
|
LeaveCriticalSection(&SddlSidLookupCritical);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if ( SidLookup[ i ].Valid == FALSE ||
|
|
SidLookup[ i ].Sid == NULL ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( LookupSid ) {
|
|
|
|
if ( RtlEqualSid( Sid, SidLookup[ i ].Sid ) ) {
|
|
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// check for the current key first
|
|
//
|
|
if ( _wcsnicmp( String, SidLookup[i].Key, SidLookup[i].KeyLen ) == 0 ) {
|
|
|
|
break;
|
|
|
|
} else if ( InitRootDomain && DefaultToDomain &&
|
|
(RootDomainSid == NULL) ) {
|
|
|
|
//
|
|
// looking for EA/SA, not found them,
|
|
// EA needs to default to DA, SA needs to default to domain relative
|
|
//
|
|
if ( _wcsnicmp( SDDL_DOMAIN_ADMINISTRATORS, SidLookup[i].Key, SidLookup[i].KeyLen ) == 0 ) {
|
|
DomainAdminIndex = i;
|
|
// break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ( i < SidCount ) {
|
|
|
|
MatchedEntry = &SidLookup[ i ];
|
|
|
|
} else if ( InitRootDomain && DefaultToDomain &&
|
|
(RootDomainSid == NULL) &&
|
|
( DomainAdminIndex < SidCount ) ) {
|
|
|
|
if ( bIsSA ) {
|
|
//
|
|
// default to domain relative sid
|
|
//
|
|
|
|
if ( LookupSid ) {
|
|
*pSASid = (PVOID)Sid;
|
|
|
|
} else if ( SidLookup[ DomainAdminIndex ].Sid ) {
|
|
//
|
|
// allocate buffer for domain relative SA sid
|
|
// which means it's only valid on the root domain
|
|
//
|
|
|
|
i = RtlLengthSid( SidLookup[ DomainAdminIndex ].Sid );
|
|
|
|
*pSASid = (PVOID)LocalAlloc( LMEM_FIXED, i+1 );
|
|
|
|
if ( *pSASid != NULL ) {
|
|
|
|
RtlCopyMemory( (PSID)(*pSASid), SidLookup[ DomainAdminIndex ].Sid, i );
|
|
|
|
// replace the DA rid with SA rid
|
|
*( RtlSubAuthoritySid( (PSID)(*pSASid),
|
|
*( RtlSubAuthorityCountSid( SidLookup[ DomainAdminIndex ].Sid )) - 1) ) =
|
|
DOMAIN_GROUP_RID_SCHEMA_ADMINS;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// default to the domain admin account
|
|
//
|
|
|
|
MatchedEntry = &SidLookup[ DomainAdminIndex ];
|
|
}
|
|
}
|
|
|
|
InitializeSidLookupTable(STRSD_REINITIALIZE_LEAVE);
|
|
|
|
return( MatchedEntry );
|
|
}
|
|
|
|
|
|
DWORD
|
|
LocalGetSidForString(
|
|
IN PWSTR String,
|
|
OUT PSID *SID,
|
|
OUT PWSTR *End,
|
|
OUT PBOOLEAN FreeSid,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN PSTRSD_SID_LOOKUP tSidLookupDomOrRootDomRelativeTable OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine which sid is an appropriate match for the
|
|
given string, either as a sid moniker or as a string representation of a
|
|
sid (ie: "DA or "S-1-0-0" )
|
|
|
|
The returned sid must be free via a call to LocalFree if the *pFreeSid
|
|
output parameter is TRUE. If it's FALSE, no additional action needs to
|
|
be taken
|
|
|
|
|
|
Arguments:
|
|
|
|
String - The string to be converted
|
|
|
|
Sid - Where the created SID is to be returned
|
|
|
|
End - Where in the string we stopped processing
|
|
|
|
FreeSid - Determines whether the returned SID needs to be freed via a
|
|
call to LocalFree or not
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - success
|
|
|
|
ERROR_NON_MAPPED - An invalid format of the SID was given
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
PSTRSD_SID_LOOKUP MatchedEntry;
|
|
PSID pSidSA=NULL;
|
|
|
|
if ( String == NULL || SID == NULL || End == NULL || FreeSid == NULL ) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Assume we'll return a well known sid
|
|
//
|
|
*FreeSid = FALSE;
|
|
|
|
// if ( wcslen( String ) < 2 ) {
|
|
// no need to do wcslen (expensive) because we know that String is not NULL
|
|
// so just check for the first and second char
|
|
if ( *String == L'\0' || *( String +1 ) == L'\0' ) {
|
|
|
|
return( ERROR_NONE_MAPPED );
|
|
}
|
|
|
|
//
|
|
// Set our end of string pointer
|
|
//
|
|
*End = String + 2;
|
|
|
|
MatchedEntry = LookupSidInTable( String,
|
|
NULL,
|
|
RootDomainSid,
|
|
DomainSid,
|
|
tSidLookupDomOrRootDomRelativeTable,
|
|
DefaultToDomain,
|
|
(PVOID *)&pSidSA);
|
|
|
|
//
|
|
// If we didn't find a match, try it as a sid string
|
|
//
|
|
if ( MatchedEntry == NULL ) {
|
|
|
|
if ( pSidSA ) {
|
|
//
|
|
// this is schema admin lookup
|
|
//
|
|
*SID = pSidSA;
|
|
*FreeSid = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We assumed a known moniker, so we'll have to unset our end of string pointer.
|
|
// Also, if it's a not a SID, the Convert routine will return the appropriate error.
|
|
//
|
|
*End -= 2;
|
|
if ( LocalConvertStringSidToSid( String, SID, End) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS && *SID != NULL ) {
|
|
|
|
*FreeSid = TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the entry that's been selected hasn't been initialized yet, do it now
|
|
//
|
|
*SID = MatchedEntry->Sid;
|
|
}
|
|
|
|
|
|
return(Err);
|
|
}
|
|
|
|
|
|
DWORD
|
|
LocalGetStringForSid(
|
|
IN PSID Sid,
|
|
OUT PWSTR *String,
|
|
IN PSID RootDomainSid OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine which string represents a sid, either as a sid moniker or
|
|
as a string representation of a sid (ie: "DA or "S-1-0-0" )
|
|
|
|
The returned string must be free via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
Sid - Sid to be converted
|
|
|
|
String - Where the mapped Sid is to be returned
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - success
|
|
|
|
ERROR_NON_MAPPED - An invalid format of the SID was given
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
PSTRSD_SID_LOOKUP MatchedEntry;
|
|
PSID pSidSA=NULL;
|
|
DWORD Len;
|
|
|
|
if ( Sid == NULL || String == NULL ) {
|
|
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Try to find a match in the lookup table
|
|
//
|
|
MatchedEntry = LookupSidInTable( NULL,
|
|
Sid,
|
|
RootDomainSid,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
(PVOID *)&pSidSA );
|
|
|
|
//
|
|
// If a match was found, return it
|
|
//
|
|
if ( MatchedEntry || pSidSA ) {
|
|
|
|
if ( MatchedEntry ) {
|
|
Len = MatchedEntry->KeyLen;
|
|
} else {
|
|
Len = wcslen(SDDL_SCHEMA_ADMINISTRATORS);
|
|
}
|
|
|
|
*String = LocalAlloc( LMEM_FIXED, ( Len * sizeof( WCHAR ) ) + sizeof( WCHAR ) );
|
|
if ( *String == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
if ( MatchedEntry ) {
|
|
wcscpy( *String, MatchedEntry->Key );
|
|
} else {
|
|
wcscpy( *String, SDDL_SCHEMA_ADMINISTRATORS);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( ConvertSidToStringSidW( Sid, String ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
}
|
|
|
|
}
|
|
|
|
return(Err);
|
|
}
|
|
|
|
DWORD
|
|
LocalGetStringForControl(
|
|
IN SECURITY_DESCRIPTOR_CONTROL ControlCode,
|
|
IN ULONG LookupFlags,
|
|
OUT PWSTR *ControlString
|
|
)
|
|
{
|
|
DWORD i, ControlCount = sizeof( ControlLookup ) / sizeof( STRSD_KEY_LOOKUP );
|
|
WCHAR Buffer[256];
|
|
DWORD nOffset=0;
|
|
|
|
|
|
if ( !ControlString ) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
*ControlString = NULL;
|
|
|
|
for ( i = 0; i < ControlCount; i++ ) {
|
|
|
|
//
|
|
// If it doesn't match our lookup type, skip it.
|
|
//
|
|
if ( ( LookupFlags & ControlLookup[ i ].ValidityFlags ) != LookupFlags ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( ControlCode & ControlLookup[ i ].Value ) {
|
|
|
|
wcsncpy(Buffer+nOffset,
|
|
ControlLookup[ i ].Key,
|
|
ControlLookup[ i ].KeyLen );
|
|
|
|
nOffset += ControlLookup[ i ].KeyLen;
|
|
|
|
}
|
|
}
|
|
|
|
Buffer[nOffset] = L'\0';
|
|
|
|
if ( nOffset ) {
|
|
*ControlString = (PWSTR)LocalAlloc(0, (nOffset+1)*sizeof(WCHAR));
|
|
|
|
if ( *ControlString ) {
|
|
|
|
wcscpy(*ControlString, Buffer);
|
|
|
|
} else {
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
PSTRSD_KEY_LOOKUP
|
|
LookupAccessMaskInTable(
|
|
IN PWSTR String, OPTIONAL
|
|
IN ULONG AccessMask, OPTIONAL
|
|
IN ULONG LookupFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine if the given access mask or string right exists in the lookup
|
|
table.
|
|
|
|
A pointer to the matching static lookup entry is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
String - The string to be looked up
|
|
|
|
AccessMask - The accessMask to be looked up.
|
|
|
|
LookupFlags - Flags to use for lookup (Dacl or Sacl)
|
|
|
|
Return Value:
|
|
|
|
Lookup table entry if found
|
|
|
|
NULL if not found
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// This is how the access mask is looked up. Always have the multi-char
|
|
// rights before the single char ones
|
|
//
|
|
static STRSD_KEY_LOOKUP RightsLookup[] = {
|
|
{ SDDL_READ_PROPERTY, SDDL_LEN_TAG( SDDL_READ_PROPERTY ), ACTRL_DS_READ_PROP, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_WRITE_PROPERTY, SDDL_LEN_TAG( SDDL_WRITE_PROPERTY ), ACTRL_DS_WRITE_PROP, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_CREATE_CHILD, SDDL_LEN_TAG( SDDL_CREATE_CHILD ), ACTRL_DS_CREATE_CHILD, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_DELETE_CHILD, SDDL_LEN_TAG( SDDL_DELETE_CHILD ), ACTRL_DS_DELETE_CHILD, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_LIST_CHILDREN, SDDL_LEN_TAG( SDDL_LIST_CHILDREN ), ACTRL_DS_LIST, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_SELF_WRITE, SDDL_LEN_TAG( SDDL_SELF_WRITE ), ACTRL_DS_SELF, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_LIST_OBJECT, SDDL_LEN_TAG( SDDL_LIST_OBJECT ), ACTRL_DS_LIST_OBJECT, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_DELETE_TREE, SDDL_LEN_TAG( SDDL_DELETE_TREE ), ACTRL_DS_DELETE_TREE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_CONTROL_ACCESS, SDDL_LEN_TAG( SDDL_CONTROL_ACCESS ), ACTRL_DS_CONTROL_ACCESS, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
|
|
{ SDDL_READ_CONTROL, SDDL_LEN_TAG( SDDL_READ_CONTROL ), READ_CONTROL, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_WRITE_DAC, SDDL_LEN_TAG( SDDL_WRITE_DAC ), WRITE_DAC, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_WRITE_OWNER, SDDL_LEN_TAG( SDDL_WRITE_OWNER ), WRITE_OWNER, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_STANDARD_DELETE, SDDL_LEN_TAG( SDDL_STANDARD_DELETE ), DELETE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_GENERIC_ALL, SDDL_LEN_TAG( SDDL_GENERIC_ALL ), GENERIC_ALL, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_GENERIC_READ, SDDL_LEN_TAG( SDDL_GENERIC_READ ), GENERIC_READ, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_GENERIC_WRITE, SDDL_LEN_TAG( SDDL_GENERIC_WRITE ), GENERIC_WRITE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_GENERIC_EXECUTE, SDDL_LEN_TAG( SDDL_GENERIC_EXECUTE ), GENERIC_EXECUTE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_FILE_ALL, SDDL_LEN_TAG( SDDL_FILE_ALL ), FILE_ALL_ACCESS, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_FILE_READ, SDDL_LEN_TAG( SDDL_FILE_READ ), FILE_GENERIC_READ, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_FILE_WRITE, SDDL_LEN_TAG( SDDL_FILE_WRITE ), FILE_GENERIC_WRITE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_FILE_EXECUTE, SDDL_LEN_TAG( SDDL_FILE_EXECUTE ), FILE_GENERIC_EXECUTE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_KEY_ALL, SDDL_LEN_TAG( SDDL_KEY_ALL ), KEY_ALL_ACCESS, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_KEY_READ, SDDL_LEN_TAG( SDDL_KEY_READ ), KEY_READ, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_KEY_WRITE, SDDL_LEN_TAG( SDDL_KEY_WRITE ), KEY_WRITE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_KEY_EXECUTE, SDDL_LEN_TAG( SDDL_KEY_EXECUTE ), KEY_EXECUTE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
};
|
|
DWORD i, RightsCount = sizeof(RightsLookup) / sizeof(STRSD_KEY_LOOKUP);
|
|
PSTRSD_KEY_LOOKUP MatchedEntry = NULL;
|
|
BOOLEAN LookupString = FALSE;
|
|
|
|
if ( String ) {
|
|
|
|
LookupString = TRUE;
|
|
}
|
|
|
|
for ( i = 0; i < RightsCount; i++ ) {
|
|
|
|
//
|
|
// If it doesn't match our lookup type, skip it.
|
|
//
|
|
if ( ( LookupFlags & RightsLookup[ i ].ValidityFlags ) != LookupFlags ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( LookupString ) {
|
|
|
|
if ( _wcsnicmp( String, RightsLookup[ i ].Key, RightsLookup[ i ].KeyLen ) == 0 ) {
|
|
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( AccessMask == RightsLookup[ i ].Value ) {
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If a match was found, return it
|
|
//
|
|
if ( i < RightsCount ) {
|
|
|
|
MatchedEntry = &RightsLookup[ i ];
|
|
}
|
|
|
|
|
|
return( MatchedEntry );
|
|
|
|
}
|
|
|
|
|
|
PSTRSD_KEY_LOOKUP
|
|
LookupAceTypeInTable(
|
|
IN PWSTR String, OPTIONAL
|
|
IN ULONG AceType, OPTIONAL
|
|
IN ULONG LookupFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine if the given ace type or string type exists in the lookup
|
|
table.
|
|
|
|
A pointer to the matching static lookup entry is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
String - The string to be looked up
|
|
|
|
AceType - The ace type to be looked up.
|
|
|
|
LookupFlags - Flags to use for lookup (Dacl or Sacl)
|
|
|
|
Return Value:
|
|
|
|
Lookup table entry if found
|
|
|
|
NULL if not found
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Lookup table
|
|
//
|
|
static STRSD_KEY_LOOKUP TypeLookup[] = {
|
|
{ SDDL_ACCESS_ALLOWED, SDDL_LEN_TAG( SDDL_ACCESS_ALLOWED ), ACCESS_ALLOWED_ACE_TYPE, SDDL_VALID_DACL },
|
|
{ SDDL_ACCESS_DENIED, SDDL_LEN_TAG( SDDL_ACCESS_DENIED ), ACCESS_DENIED_ACE_TYPE, SDDL_VALID_DACL },
|
|
{ SDDL_OBJECT_ACCESS_ALLOWED, SDDL_LEN_TAG( SDDL_OBJECT_ACCESS_ALLOWED ),
|
|
ACCESS_ALLOWED_OBJECT_ACE_TYPE, SDDL_VALID_DACL },
|
|
{ SDDL_OBJECT_ACCESS_DENIED, SDDL_LEN_TAG( SDDL_OBJECT_ACCESS_DENIED ),
|
|
ACCESS_DENIED_OBJECT_ACE_TYPE, SDDL_VALID_DACL },
|
|
{ SDDL_AUDIT, SDDL_LEN_TAG( SDDL_AUDIT ), SYSTEM_AUDIT_ACE_TYPE, SDDL_VALID_SACL },
|
|
{ SDDL_ALARM, SDDL_LEN_TAG( SDDL_ALARM ), SYSTEM_ALARM_ACE_TYPE, SDDL_VALID_SACL },
|
|
{ SDDL_OBJECT_AUDIT, SDDL_LEN_TAG( SDDL_OBJECT_AUDIT ), SYSTEM_AUDIT_OBJECT_ACE_TYPE, SDDL_VALID_SACL },
|
|
{ SDDL_OBJECT_ALARM, SDDL_LEN_TAG( SDDL_OBJECT_ALARM ), SYSTEM_ALARM_OBJECT_ACE_TYPE, SDDL_VALID_SACL }
|
|
};
|
|
DWORD i, TypeCount = sizeof( TypeLookup ) / sizeof( STRSD_KEY_LOOKUP );
|
|
PSTRSD_KEY_LOOKUP MatchedEntry = NULL;
|
|
BOOLEAN LookupString = FALSE;
|
|
|
|
if ( String ) {
|
|
|
|
LookupString = TRUE;
|
|
}
|
|
|
|
for ( i = 0; i < TypeCount; i++ ) {
|
|
|
|
//
|
|
// If it doesn't match our lookup type, skip it.
|
|
//
|
|
if ( ( LookupFlags & TypeLookup[ i ].ValidityFlags ) != LookupFlags ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( LookupString ) {
|
|
|
|
if ( _wcsnicmp( String, TypeLookup[ i ].Key, TypeLookup[ i ].KeyLen ) == 0 ) {
|
|
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( AceType == TypeLookup[ i ].Value ) {
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If a match was found, return it
|
|
//
|
|
if ( i < TypeCount ) {
|
|
|
|
MatchedEntry = &TypeLookup[ i ];
|
|
}
|
|
|
|
|
|
return( MatchedEntry );
|
|
}
|
|
|
|
|
|
|
|
PSTRSD_KEY_LOOKUP
|
|
LookupAceFlagsInTable(
|
|
IN PWSTR String, OPTIONAL
|
|
IN ULONG AceFlags, OPTIONAL
|
|
IN ULONG LookupFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will determine if the given ace flags or string flags exists in the lookup
|
|
table.
|
|
|
|
A pointer to the matching static lookup entry is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
String - The string to be looked up
|
|
|
|
AceFlags - The ace flags to be looked up.
|
|
|
|
LookupFlags - Flags to use for lookup (Dacl or Sacl)
|
|
|
|
Return Value:
|
|
|
|
Lookup table entry if found
|
|
|
|
NULL if not found
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Lookup tables
|
|
//
|
|
static STRSD_KEY_LOOKUP FlagLookup[] = {
|
|
{ SDDL_CONTAINER_INHERIT, SDDL_LEN_TAG( SDDL_CONTAINER_INHERIT ), CONTAINER_INHERIT_ACE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_OBJECT_INHERIT, SDDL_LEN_TAG( SDDL_OBJECT_INHERIT ), OBJECT_INHERIT_ACE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_NO_PROPAGATE, SDDL_LEN_TAG( SDDL_NO_PROPAGATE ), NO_PROPAGATE_INHERIT_ACE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_INHERIT_ONLY, SDDL_LEN_TAG( SDDL_INHERIT_ONLY ), INHERIT_ONLY_ACE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_INHERITED, SDDL_LEN_TAG( SDDL_INHERITED ), INHERITED_ACE, SDDL_VALID_DACL | SDDL_VALID_SACL },
|
|
{ SDDL_AUDIT_SUCCESS, SDDL_LEN_TAG( SDDL_AUDIT_SUCCESS ), SUCCESSFUL_ACCESS_ACE_FLAG, SDDL_VALID_SACL },
|
|
{ SDDL_AUDIT_FAILURE, SDDL_LEN_TAG( SDDL_AUDIT_FAILURE ), FAILED_ACCESS_ACE_FLAG, SDDL_VALID_SACL }
|
|
};
|
|
DWORD i, FlagCount = sizeof( FlagLookup ) / sizeof( STRSD_KEY_LOOKUP );
|
|
PSTRSD_KEY_LOOKUP MatchedEntry = NULL;
|
|
BOOLEAN LookupString = FALSE;
|
|
|
|
if ( String ) {
|
|
|
|
LookupString = TRUE;
|
|
}
|
|
|
|
|
|
for ( i = 0; i < FlagCount; i++ ) {
|
|
|
|
//
|
|
// If it doesn't match our lookup type, skip it.
|
|
//
|
|
if ( ( LookupFlags & FlagLookup[ i ].ValidityFlags ) != LookupFlags ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( LookupString ) {
|
|
|
|
if ( _wcsnicmp( String, FlagLookup[ i ].Key, FlagLookup[ i ].KeyLen ) == 0 ) {
|
|
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( AceFlags == FlagLookup[ i ].Value ) {
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If a match was found, return it
|
|
//
|
|
if ( i < FlagCount ) {
|
|
|
|
MatchedEntry = &FlagLookup[ i ];
|
|
}
|
|
|
|
|
|
return( MatchedEntry );
|
|
}
|
|
|
|
|
|
DWORD
|
|
LocalGetSDControlForString (
|
|
IN PWSTR ControlString,
|
|
IN ULONG LookupFlags,
|
|
OUT SECURITY_DESCRIPTOR_CONTROL *pControl,
|
|
OUT PWSTR *End
|
|
)
|
|
{
|
|
|
|
DWORD i, ControlCount = sizeof( ControlLookup ) / sizeof( STRSD_KEY_LOOKUP );
|
|
PWSTR pCursor=ControlString;
|
|
BOOL bFound;
|
|
|
|
if ( !ControlString || !pControl || !End ) {
|
|
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
*pControl = 0;
|
|
|
|
while ( pCursor && *pCursor == L' ' ) {
|
|
//
|
|
// skip any blanks
|
|
//
|
|
pCursor++;
|
|
}
|
|
|
|
|
|
do {
|
|
|
|
bFound = FALSE;
|
|
|
|
for ( i = 0; i < ControlCount; i++ ) {
|
|
|
|
//
|
|
// If it doesn't match our lookup type, skip it.
|
|
//
|
|
if ( ( LookupFlags & ControlLookup[ i ].ValidityFlags ) != LookupFlags ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( _wcsnicmp( pCursor,
|
|
ControlLookup[ i ].Key,
|
|
ControlLookup[ i ].KeyLen ) == 0 ) {
|
|
|
|
*pControl |= ControlLookup[ i ].Value;
|
|
|
|
pCursor += ControlLookup[ i ].KeyLen;
|
|
|
|
while ( pCursor && *pCursor == L' ' ) {
|
|
//
|
|
// skip any blanks
|
|
//
|
|
pCursor++;
|
|
}
|
|
|
|
bFound = TRUE;
|
|
|
|
break; // break the for loop
|
|
}
|
|
}
|
|
|
|
} while ( bFound );
|
|
|
|
|
|
*End = pCursor;
|
|
|
|
|
|
return( ERROR_SUCCESS );
|
|
|
|
}
|
|
|
|
DWORD
|
|
LocalGetAclForString(
|
|
IN PWSTR AclString,
|
|
IN BOOLEAN ConvertAsDacl,
|
|
OUT PACL *Acl,
|
|
OUT PWSTR *End,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN PSTRSD_SID_LOOKUP tSidLookupDomOrRootDomRelativeTable OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine convert a string into an ACL. The format of the aces is:
|
|
|
|
Ace := ( Type; Flags; Rights; ObjGuid; IObjGuid; Sid;
|
|
Type : = A | D | OA | OD {Access, Deny, ObjectAccess, ObjectDeny}
|
|
Flags := Flags Flag
|
|
Flag : = CI | IO | NP | SA | FA {Container Inherit,Inherit Only, NoProp,
|
|
SuccessAudit, FailAdit }
|
|
Rights := Rights Right
|
|
Right := DS_READ_PROPERTY | blah blah
|
|
Guid := String representation of a GUID (via RPC UuidToString)
|
|
Sid := DA | PS | AO | PO | AU | S-* (Domain Admins, PersonalSelf, Acct Ops,
|
|
PrinterOps, AuthenticatedUsers, or
|
|
the string representation of a sid)
|
|
The seperator is a ';'.
|
|
|
|
The returned ACL must be free via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
AclString - The string to be converted
|
|
|
|
ConvertAsDacl - Treat the input string as a dacl string
|
|
|
|
ppSid - Where the created SID is to be returned
|
|
|
|
End - Where in the string we stopped processing
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS indicates success
|
|
ERROR_NOT_ENOUGH_MEMORY indicates a memory allocation for the ouput acl
|
|
failed
|
|
ERROR_INVALID_PARAMETER The string does not represent an ACL
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
DWORD AclSize = 0, AclUsed = 0;
|
|
ULONG Acls = 0, i, j;
|
|
PWSTR Curr, MaskEnd;
|
|
WCHAR ConvertGuidString[ STRING_GUID_LEN + 1];
|
|
BOOLEAN FreeSid = FALSE;
|
|
BOOL OpRes;
|
|
PSTRSD_KEY_LOOKUP MatchedEntry;
|
|
ULONG LookupFlags;
|
|
PSID SidPtr = NULL;
|
|
|
|
|
|
if ( NULL == AclString || NULL == Acl || NULL == End ) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if ( ConvertAsDacl ) {
|
|
|
|
LookupFlags = SDDL_VALID_DACL;
|
|
|
|
} else {
|
|
|
|
LookupFlags = SDDL_VALID_SACL;
|
|
}
|
|
|
|
//
|
|
// First, we'll have to go through and count the number of entries that
|
|
// we have. We'll do the by computing the length of this ACL (which is
|
|
// delimited by either the end of the list or a ':' that seperates a key
|
|
// from a value
|
|
//
|
|
*End = wcschr( AclString, SDDL_DELIMINATORC );
|
|
|
|
if ( *End == AclString ) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if ( *End == NULL ) {
|
|
|
|
*End = AclString + wcslen( AclString );
|
|
|
|
} else {
|
|
|
|
( *End )--;
|
|
}
|
|
|
|
//
|
|
// Now, do the count
|
|
//
|
|
Curr = AclString;
|
|
|
|
OpRes = 0;
|
|
// while ( Curr != *End ) {
|
|
while ( Curr < *End ) {
|
|
|
|
if ( *Curr == SDDL_SEPERATORC ) {
|
|
|
|
Acls++;
|
|
|
|
} else if ( *Curr != L' ' ) {
|
|
OpRes = 1;
|
|
}
|
|
|
|
Curr++;
|
|
}
|
|
|
|
//
|
|
// Now, we've counted the total number of seperators. Make sure we
|
|
// have the right number. (There is 5 seperators per ace)
|
|
//
|
|
if ( Acls % 5 == 0 ) {
|
|
|
|
if ( Acls == 0 && OpRes ) {
|
|
//
|
|
// gabbage chars in between
|
|
//
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
Acls = Acls / 5;
|
|
}
|
|
|
|
} else {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS && Acls == 0 ) {
|
|
|
|
*Acl = LocalAlloc( LMEM_FIXED, sizeof( ACL ) );
|
|
|
|
if ( *Acl == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
( *Acl )->AclRevision = ACL_REVISION;
|
|
( *Acl )->Sbz1 = ( BYTE )0;
|
|
( *Acl )->AclSize = ( USHORT )sizeof( ACL ) ;
|
|
( *Acl )->AceCount = 0;
|
|
( *Acl )->Sbz2 = ( USHORT )0;
|
|
|
|
}
|
|
|
|
return( Err );
|
|
}
|
|
|
|
//
|
|
// Ok now do the allocation. We'll do a sort of worst case initial
|
|
// allocation. This saves us from having to process everything twice
|
|
// (once to size, once to build). If we determine later that we have
|
|
// an acl that is not big enough, we allocate additional space. The only
|
|
// time that this reallocation should happen is if the input string
|
|
// contains a lot of explicit SIDs. Otherwise, the chosen buffer size
|
|
// should be pretty close to the proper size
|
|
//
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
AclSize = sizeof( ACL ) + ( Acls * ( sizeof( ACCESS_ALLOWED_OBJECT_ACE ) +
|
|
sizeof( SID ) + ( 6 * sizeof( ULONG ) ) ) );
|
|
if ( AclSize > SDDL_MAX_ACL_SIZE ) {
|
|
AclSize = SDDL_MAX_ACL_SIZE;
|
|
}
|
|
|
|
*Acl = ( PACL )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, AclSize );
|
|
|
|
if ( *Acl == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
AclUsed = sizeof( ACL );
|
|
|
|
//
|
|
// We'll start initializing it...
|
|
//
|
|
( *Acl )->AclRevision = ACL_REVISION;
|
|
( *Acl )->Sbz1 = ( BYTE )0;
|
|
( *Acl )->AclSize = ( USHORT )AclSize;
|
|
( *Acl )->AceCount = 0;
|
|
( *Acl )->Sbz2 = ( USHORT )0;
|
|
|
|
//
|
|
// Ok, now we'll go through and start building them all
|
|
//
|
|
Curr = AclString;
|
|
|
|
for( i = 0; i < Acls; i++ ) {
|
|
|
|
//
|
|
// First, get the type..
|
|
//
|
|
UCHAR Type;
|
|
UCHAR Flags = 0;
|
|
USHORT Size;
|
|
ACCESS_MASK Mask = 0;
|
|
GUID *ObjId = NULL, ObjGuid;
|
|
GUID *IObjId = NULL, IObjGuid;
|
|
PWSTR Next;
|
|
DWORD AceSize = 0;
|
|
|
|
//
|
|
// skip any space before (
|
|
//
|
|
while(*Curr == L' ' ) {
|
|
Curr++;
|
|
}
|
|
//
|
|
// Skip any parens that may exist in the ace list
|
|
//
|
|
if ( *Curr == SDDL_ACE_BEGINC ) {
|
|
|
|
Curr++;
|
|
}
|
|
//
|
|
// skip any space after (
|
|
//
|
|
while(*Curr == L' ' ) {
|
|
Curr++;
|
|
}
|
|
|
|
MatchedEntry = LookupAceTypeInTable( Curr, 0, LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
Type = ( UCHAR )MatchedEntry->Value;
|
|
Curr += MatchedEntry->KeyLen + 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Found an invalid type
|
|
//
|
|
Err = ERROR_INVALID_DATATYPE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we have any object aces, set the acl revision to REVISION_DS
|
|
//
|
|
if ( Type >= ACCESS_MIN_MS_OBJECT_ACE_TYPE && Type <= ACCESS_MAX_MS_OBJECT_ACE_TYPE ) {
|
|
|
|
( *Acl )->AclRevision = ACL_REVISION_DS;
|
|
}
|
|
|
|
//
|
|
// skip any space before ;
|
|
//
|
|
while(*Curr == L' ' ) {
|
|
Curr++;
|
|
}
|
|
|
|
//
|
|
// Next, get the flags...
|
|
//
|
|
while ( Curr != *End ) {
|
|
|
|
if ( *Curr == SDDL_SEPERATORC ) {
|
|
|
|
Curr++;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Skip any blanks
|
|
//
|
|
while ( *Curr == L' ' ) {
|
|
|
|
Curr++;
|
|
}
|
|
|
|
MatchedEntry = LookupAceFlagsInTable( Curr, 0, LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
Flags |= ( UCHAR )MatchedEntry->Value;
|
|
Curr += MatchedEntry->KeyLen;
|
|
|
|
} else {
|
|
//
|
|
// Found an invalid flag
|
|
//
|
|
Err = ERROR_INVALID_FLAGS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// skip any space after ;
|
|
//
|
|
while(*Curr == L' ' ) {
|
|
Curr++;
|
|
}
|
|
|
|
//
|
|
// Now, get the access mask
|
|
//
|
|
while( TRUE ) {
|
|
|
|
if ( *Curr == SDDL_SEPERATORC ) {
|
|
|
|
Curr++;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Skip any blanks
|
|
//
|
|
while ( *Curr == L' ' ) {
|
|
|
|
Curr++;
|
|
}
|
|
|
|
MatchedEntry = LookupAccessMaskInTable( Curr, 0, LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
Mask |= MatchedEntry->Value;
|
|
Curr += MatchedEntry->KeyLen;
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the rights couldn't be looked up, see if it's a converted mask
|
|
//
|
|
|
|
Mask |= wcstoul( Curr, &MaskEnd, 0 );
|
|
|
|
if ( MaskEnd != Curr ) {
|
|
|
|
Curr = MaskEnd;
|
|
|
|
} else {
|
|
//
|
|
// Found an invalid right
|
|
//
|
|
Err = ERROR_INVALID_ACL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If that worked, we'll get the ids
|
|
//
|
|
for ( j = 0; j < 2; j++ ) {
|
|
|
|
//
|
|
// skip any space before ;
|
|
//
|
|
while(*Curr == L' ' ) {
|
|
Curr++;
|
|
}
|
|
|
|
if ( *Curr != SDDL_SEPERATORC ) {
|
|
|
|
wcsncpy( ConvertGuidString, Curr, STRING_GUID_LEN );
|
|
ConvertGuidString[ STRING_GUID_LEN ] = UNICODE_NULL;
|
|
|
|
if ( j == 0 ) {
|
|
|
|
|
|
if ( UuidFromStringW( ConvertGuidString, &ObjGuid ) == RPC_S_OK ) {
|
|
|
|
ObjId = &ObjGuid;
|
|
|
|
} else {
|
|
|
|
Err = RPC_S_INVALID_STRING_UUID;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( UuidFromStringW( ConvertGuidString, &IObjGuid ) == RPC_S_OK ) {
|
|
|
|
IObjId = &IObjGuid;
|
|
|
|
} else {
|
|
|
|
Err = RPC_S_INVALID_STRING_UUID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// success
|
|
Curr += STRING_GUID_LEN;
|
|
if ( *Curr != SDDL_SEPERATORC &&
|
|
*Curr != L' ' ) {
|
|
|
|
Err = RPC_S_INVALID_STRING_UUID;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
Curr++;
|
|
}
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// skip any space before ;
|
|
//
|
|
while(*Curr == L' ' ) {
|
|
Curr++;
|
|
}
|
|
|
|
//
|
|
// Finally, the SID
|
|
//
|
|
if ( ERROR_SUCCESS == Err ) {
|
|
|
|
PWSTR End;
|
|
Err = LocalGetSidForString( Curr,
|
|
&SidPtr,
|
|
&End,
|
|
&FreeSid,
|
|
RootDomainSid,
|
|
DomainSid,
|
|
tSidLookupDomOrRootDomRelativeTable,
|
|
DefaultToDomain );
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
if ( End == NULL ) {
|
|
Err = ERROR_INVALID_ACL;
|
|
} else {
|
|
|
|
while(*End == L' ' ) {
|
|
End++;
|
|
}
|
|
//
|
|
// a ace must be terminated by ')'
|
|
//
|
|
if ( *End != SDDL_ACE_ENDC ) {
|
|
Err = ERROR_INVALID_ACL;
|
|
|
|
} else {
|
|
|
|
Curr = End + 1;
|
|
|
|
if ( !SidPtr ) {
|
|
Err = ERROR_INVALID_ACL;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Quit on an error
|
|
//
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now, we'll create the ace, and add it...
|
|
//
|
|
|
|
//
|
|
// First, make sure we have the room for it
|
|
//
|
|
switch ( Type ) {
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
case SYSTEM_ALARM_ACE_TYPE:
|
|
|
|
AceSize = sizeof( ACCESS_ALLOWED_ACE );
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
|
|
case SYSTEM_ALARM_OBJECT_ACE_TYPE:
|
|
AceSize = sizeof( KNOWN_OBJECT_ACE );
|
|
|
|
if ( ObjId ) {
|
|
|
|
AceSize += sizeof ( GUID );
|
|
}
|
|
|
|
if ( IObjId ) {
|
|
|
|
AceSize += sizeof ( GUID );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Err = ERROR_INVALID_ACL;
|
|
break;
|
|
|
|
}
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
AceSize += RtlLengthSid( SidPtr ) - sizeof( ULONG );
|
|
|
|
if (AceSize + AclUsed > AclSize)
|
|
{
|
|
//
|
|
// We'll have to reallocate, since our buffer isn't
|
|
// big enough...
|
|
//
|
|
PACL NewAcl;
|
|
DWORD NewSize = AclSize + ( ( Acls - i ) * AceSize );
|
|
|
|
NewAcl = ( PACL )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
|
NewSize );
|
|
if ( NewAcl == NULL ) {
|
|
|
|
LocalFree( *Acl );
|
|
*Acl = NULL;
|
|
|
|
if ( FreeSid == TRUE ) {
|
|
|
|
LocalFree( SidPtr );
|
|
SidPtr = NULL;
|
|
|
|
FreeSid = FALSE;
|
|
}
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
|
|
} else {
|
|
|
|
memcpy( NewAcl, *Acl, AclSize );
|
|
NewAcl->AclSize = ( USHORT )NewSize;
|
|
|
|
LocalFree( *Acl );
|
|
*Acl = NewAcl;
|
|
|
|
AclSize = NewSize;
|
|
}
|
|
|
|
}
|
|
|
|
AclUsed += AceSize;
|
|
|
|
SetLastError( ERROR_SUCCESS );
|
|
|
|
switch (Type)
|
|
{
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
OpRes = AddAuditAccessAceEx( *Acl,
|
|
ACL_REVISION,
|
|
Flags &
|
|
~(SUCCESSFUL_ACCESS_ACE_FLAG |
|
|
FAILED_ACCESS_ACE_FLAG),
|
|
Mask,
|
|
SidPtr,
|
|
Flags & SUCCESSFUL_ACCESS_ACE_FLAG,
|
|
Flags & FAILED_ACCESS_ACE_FLAG );
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
OpRes = AddAccessAllowedAceEx( *Acl,
|
|
ACL_REVISION,
|
|
Flags,
|
|
Mask,
|
|
SidPtr );
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
OpRes = AddAccessDeniedAceEx( *Acl,
|
|
ACL_REVISION,
|
|
Flags,
|
|
Mask,
|
|
SidPtr );
|
|
|
|
break;
|
|
|
|
case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
|
|
OpRes = AddAuditAccessObjectAce( *Acl,
|
|
ACL_REVISION_DS,
|
|
Flags,
|
|
Mask,
|
|
ObjId,
|
|
IObjId,
|
|
SidPtr,
|
|
Flags & SUCCESSFUL_ACCESS_ACE_FLAG,
|
|
Flags & FAILED_ACCESS_ACE_FLAG );
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
OpRes = AddAccessAllowedObjectAce( *Acl,
|
|
ACL_REVISION_DS,
|
|
Flags,
|
|
Mask,
|
|
ObjId,
|
|
IObjId,
|
|
SidPtr );
|
|
break;
|
|
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
OpRes = AddAccessDeniedObjectAce( *Acl,
|
|
ACL_REVISION_DS,
|
|
Flags,
|
|
Mask,
|
|
ObjId,
|
|
IObjId,
|
|
SidPtr );
|
|
|
|
break;
|
|
|
|
default:
|
|
SetLastError( ERROR_INVALID_DATATYPE );
|
|
OpRes = FALSE;
|
|
break;
|
|
|
|
}
|
|
|
|
if ( OpRes == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Clean up whatever memory we have to
|
|
//
|
|
if ( FreeSid == TRUE ) {
|
|
|
|
LocalFree( SidPtr );
|
|
}
|
|
|
|
SidPtr = NULL;
|
|
|
|
if ( *Curr == SDDL_ACE_BEGINC ) {
|
|
|
|
Curr++;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If something didn't work, clean up
|
|
//
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
LocalFree( *Acl );
|
|
*Acl = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Set a more realistic acl size
|
|
//
|
|
( *Acl )->AclSize = ( USHORT )AclUsed;
|
|
}
|
|
|
|
//
|
|
// free any SIDs buffer used
|
|
//
|
|
if ( FreeSid && SidPtr ) {
|
|
LocalFree(SidPtr);
|
|
SidPtr = NULL;
|
|
}
|
|
|
|
FreeSid = FALSE;
|
|
}
|
|
}
|
|
|
|
return(Err);
|
|
}
|
|
|
|
|
|
DWORD
|
|
LocalConvertAclToString(
|
|
IN PACL Acl,
|
|
IN BOOLEAN AclPresent,
|
|
IN BOOLEAN ConvertAsDacl,
|
|
OUT PWSTR *AclString,
|
|
OUT PDWORD AclStringSize,
|
|
IN PSID RootDomainSid OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine convert an acl into a string. The format of the aces is:
|
|
|
|
Ace := ( Type; Flags; Rights; ObjGuid; IObjGuid; Sid;
|
|
Type : = A | D | OA | OD {Access, Deny, ObjectAccess, ObjectDeny}
|
|
Flags := Flags Flag
|
|
Flag : = CI | IO | NP | SA | FA {Container Inherit,Inherit Only, NoProp,
|
|
SuccessAudit, FailAdit }
|
|
Rights := Rights Right
|
|
Right := DS_READ_PROPERTY | blah blah
|
|
Guid := String representation of a GUID (via RPC UuidToString)
|
|
Sid := DA | PS | AO | PO | AU | S-* (Domain Admins, PersonalSelf, Acct Ops,
|
|
PrinterOps, AuthenticatedUsers, or
|
|
the string representation of a sid)
|
|
The seperator is a ';'.
|
|
|
|
The returned string must be free via a call to LocalFree
|
|
|
|
|
|
Arguments:
|
|
|
|
Acl - The acl to be converted
|
|
|
|
AclPresent - if TRUE, the acl is present, even if NULL
|
|
|
|
AclString - Where the created acl string is to be returned
|
|
|
|
ConvertAsDacl - Convert the given acl as the DACL, not the SACL
|
|
|
|
AclStringLen - The size of the allocated string is returned here
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - success
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY indicates a memory allocation for the ouput acl
|
|
failed
|
|
|
|
ERROR_INVALID_PARAMETER The string does not represent an ACL
|
|
|
|
ERROR_INVALID_ACL - An unexpected access mask was encountered or a NULL acl was encountered
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
DWORD AclSize = 0, MaskSize;
|
|
PACE_HEADER AceHeader;
|
|
ULONG i, j;
|
|
PWSTR *SidStrings = NULL, Curr, Guid;
|
|
BOOLEAN *SidFrees = NULL;
|
|
UINT *pMaskAsString = NULL;
|
|
PSTRSD_KEY_LOOKUP MatchedEntry;
|
|
PSTRSD_SID_LOOKUP MatchedSidEntry;
|
|
PKNOWN_ACE KnownAce;
|
|
PKNOWN_OBJECT_ACE KnownObjectAce;
|
|
ACCESS_MASK AccessMask;
|
|
PSID Sid, pSidSA=NULL;
|
|
GUID *Obj, *Inherit;
|
|
ULONG LookupFlags;
|
|
ULONG AceFlags[ ] = {
|
|
OBJECT_INHERIT_ACE,
|
|
CONTAINER_INHERIT_ACE,
|
|
NO_PROPAGATE_INHERIT_ACE,
|
|
INHERIT_ONLY_ACE,
|
|
INHERITED_ACE,
|
|
SUCCESSFUL_ACCESS_ACE_FLAG,
|
|
FAILED_ACCESS_ACE_FLAG
|
|
};
|
|
|
|
|
|
if ( AclString == NULL || AclStringSize == NULL ) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// treat the case when (AclPresent == TRUE and Acl == NULL)
|
|
// same as the case when AclPresent == FALSE.
|
|
// This fix is to be compatible with IsValidSecurityDescriptor()
|
|
// when ACL_PRESENT bit is set and Acl == NULL
|
|
//
|
|
|
|
if ( !AclPresent ||
|
|
(AclPresent && (Acl == NULL) ) ) {
|
|
|
|
*AclString = NULL;
|
|
*AclStringSize = 0;
|
|
return( ERROR_SUCCESS );
|
|
|
|
}
|
|
|
|
//
|
|
// If the ace count is 0, then it's an empty acl, and we don't handle those...
|
|
//
|
|
if ( Acl->AceCount == 0 ) {
|
|
|
|
*AclString = NULL;
|
|
*AclStringSize = 0;
|
|
return( ERROR_SUCCESS );
|
|
|
|
}
|
|
|
|
if ( ConvertAsDacl ) {
|
|
|
|
LookupFlags = SDDL_VALID_DACL;
|
|
|
|
} else {
|
|
|
|
LookupFlags = SDDL_VALID_SACL;
|
|
}
|
|
|
|
//
|
|
// Allocate buffers to hold sids that are looked up
|
|
//
|
|
SidStrings = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Acl->AceCount * sizeof( PWSTR ) );
|
|
|
|
if ( SidStrings == NULL ) {
|
|
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
SidFrees = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Acl->AceCount * sizeof( BOOLEAN ) );
|
|
|
|
if ( SidFrees == NULL ) {
|
|
|
|
LocalFree( SidStrings );
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
pMaskAsString = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Acl->AceCount * sizeof( UINT ) );
|
|
|
|
if ( pMaskAsString == NULL ) {
|
|
|
|
LocalFree( SidStrings );
|
|
LocalFree( SidFrees );
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
AceHeader = ( PACE_HEADER )FirstAce( Acl );
|
|
|
|
//
|
|
// Size the acl, so we know how big a buffer to allocate
|
|
//
|
|
for(i = 0; i < Acl->AceCount && Err == ERROR_SUCCESS;
|
|
i++, AceHeader = ( PACE_HEADER )NextAce( AceHeader ) ) {
|
|
|
|
AclSize += sizeof( WCHAR );
|
|
//
|
|
// First, the type
|
|
//
|
|
MatchedEntry = LookupAceTypeInTable( NULL, ( ULONG )AceHeader->AceType, LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
AclSize += SDDL_SIZE_TAG( MatchedEntry->Key ) + SDDL_SIZE_SEP( SDDL_SEPERATORC );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Found an invalid type
|
|
//
|
|
Err = ERROR_INVALID_ACL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Next, process the ace flags
|
|
//
|
|
for ( j = 0; j < sizeof( AceFlags ) / sizeof( ULONG ); j++ ) {
|
|
|
|
if ( ( ULONG )AceHeader->AceFlags & ( AceFlags[ j ] ) ) {
|
|
|
|
MatchedEntry = LookupAceFlagsInTable( 0, AceFlags[ j ], LookupFlags );
|
|
if ( MatchedEntry ) {
|
|
|
|
AclSize += SDDL_SIZE_TAG( MatchedEntry->Key );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
AclSize += SDDL_SIZE_SEP( SDDL_SEPERATORC );
|
|
}
|
|
|
|
//
|
|
// Next, the rights and optionally the guids. This gets done on a per ace type basis
|
|
//
|
|
switch ( AceHeader->AceType ) {
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
case SYSTEM_ALARM_ACE_TYPE:
|
|
KnownAce = ( PKNOWN_ACE )AceHeader;
|
|
AccessMask = KnownAce->Mask;
|
|
Sid = ( PSID )&KnownAce->SidStart;
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
|
|
case SYSTEM_ALARM_OBJECT_ACE_TYPE:
|
|
KnownObjectAce = ( PKNOWN_OBJECT_ACE )AceHeader;
|
|
AccessMask = KnownObjectAce->Mask;
|
|
Sid = RtlObjectAceSid( AceHeader );
|
|
|
|
//
|
|
// Deal with potential guids
|
|
//
|
|
if ( RtlObjectAceObjectType( AceHeader ) ) {
|
|
|
|
AclSize += STRING_GUID_SIZE;
|
|
}
|
|
|
|
if ( RtlObjectAceInheritedObjectType( AceHeader ) ) {
|
|
|
|
AclSize += STRING_GUID_SIZE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
Err = ERROR_INVALID_ACL;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Size the rights
|
|
//
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
MaskSize = 0;
|
|
pMaskAsString[i] = 0;
|
|
|
|
//
|
|
// lookup for the exact value first
|
|
//
|
|
MatchedEntry = LookupAccessMaskInTable( 0, AccessMask, LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
pMaskAsString[i] = 1;
|
|
MaskSize += SDDL_SIZE_TAG( MatchedEntry->Key );
|
|
|
|
} else {
|
|
//
|
|
// look for each bit
|
|
//
|
|
for ( j = 0; j < 32; j++ ) {
|
|
|
|
if ( AccessMask & ( 1 << j ) ) {
|
|
|
|
MatchedEntry = LookupAccessMaskInTable( 0, AccessMask & ( 1 << j ), LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
MaskSize += SDDL_SIZE_TAG( MatchedEntry->Key );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Found an invalid type. We'll convert the whole thing to a string
|
|
//
|
|
pMaskAsString[i] = 2;
|
|
MaskSize = 10 * sizeof( WCHAR );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
AclSize += MaskSize;
|
|
AclSize += SDDL_SIZE_SEP( SDDL_SEPERATORC );
|
|
}
|
|
}
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add in space for the guid seperators
|
|
//
|
|
AclSize += 2 * SDDL_SIZE_SEP( SDDL_SEPERATORC );
|
|
|
|
//
|
|
// Finally, lookup the sids
|
|
//
|
|
MatchedSidEntry = LookupSidInTable( NULL,
|
|
Sid,
|
|
RootDomainSid,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
&pSidSA );
|
|
|
|
//
|
|
// If we didn't find a match, try it as a sid string
|
|
//
|
|
if ( MatchedSidEntry == NULL ) {
|
|
|
|
if ( pSidSA ) {
|
|
//
|
|
// when sid lookup finds the sid of SA, pSidSA is assigned to Sid
|
|
// so it doesn't need to be freed.
|
|
//
|
|
|
|
SidStrings[ i ] = LocalAlloc( LMEM_FIXED, (wcslen(SDDL_SCHEMA_ADMINISTRATORS)+1)*sizeof(TCHAR) );
|
|
|
|
if ( SidStrings[ i ] == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
|
|
} else {
|
|
wcscpy( SidStrings[ i ], SDDL_SCHEMA_ADMINISTRATORS );
|
|
|
|
SidFrees[ i ] = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( ConvertSidToStringSidW( Sid, &SidStrings[ i ] ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
break;
|
|
|
|
} else {
|
|
|
|
SidFrees[ i ] = TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the entry that's been selected hasn't been initialized yet, do it now
|
|
//
|
|
SidStrings[ i ] = MatchedSidEntry->Key;
|
|
}
|
|
AclSize += SDDL_SIZE_TAG( SidStrings[ i ] );
|
|
|
|
|
|
AclSize += sizeof( WCHAR ); // Closing paren
|
|
AclSize += sizeof( WCHAR ); // Trailing NULL
|
|
}
|
|
|
|
//
|
|
// If all of that worked, allocate the return buffer, and build the acl string
|
|
//
|
|
if ( AclSize == 0 ) {
|
|
Err = ERROR_INVALID_ACL;
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
if ( AclSize % 2 != 0 ) {
|
|
AclSize++;
|
|
}
|
|
|
|
*AclString = LocalAlloc( LMEM_FIXED, AclSize );
|
|
|
|
if ( *AclString == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build the acl
|
|
//
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
Curr = *AclString;
|
|
|
|
AceHeader = ( PACE_HEADER )FirstAce( Acl );
|
|
|
|
//
|
|
// Size the acl, so we know how big a buffer to allocate
|
|
//
|
|
for(i = 0; i < Acl->AceCount && Err == ERROR_SUCCESS;
|
|
i++, AceHeader = ( PACE_HEADER )NextAce( AceHeader ) ) {
|
|
|
|
//
|
|
// "("
|
|
//
|
|
*Curr = SDDL_ACE_BEGINC;
|
|
Curr++;
|
|
|
|
//
|
|
// By the time we get down here, we've ensured that we can lookup all the values,
|
|
// so there is no need to check for failure
|
|
//
|
|
|
|
//
|
|
// First, the type, must find it
|
|
// "T;"
|
|
//
|
|
MatchedEntry = LookupAceTypeInTable( NULL, ( ULONG )AceHeader->AceType, LookupFlags );
|
|
if ( MatchedEntry ) {
|
|
wcscpy( Curr, MatchedEntry->Key );
|
|
Curr += MatchedEntry->KeyLen;
|
|
}
|
|
*Curr = SDDL_SEPERATORC;
|
|
Curr++;
|
|
|
|
//
|
|
// Next, process the ace flags
|
|
// "CIIO;"
|
|
//
|
|
for ( j = 0; j < sizeof( AceFlags ) / sizeof( ULONG ); j++ ) {
|
|
|
|
if ( ( ULONG )AceHeader->AceFlags & ( AceFlags[ j ] ) ) {
|
|
|
|
MatchedEntry = LookupAceFlagsInTable( 0, AceFlags[ j ], LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
wcscpy( Curr, MatchedEntry->Key );
|
|
Curr+= MatchedEntry->KeyLen;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
*Curr = SDDL_SEPERATORC;
|
|
Curr++;
|
|
|
|
//
|
|
// Next, the rights and optionally the guids. This gets done on a per ace type basis
|
|
//
|
|
Obj = NULL;
|
|
Inherit = NULL;
|
|
|
|
switch ( AceHeader->AceType ) {
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
case SYSTEM_ALARM_ACE_TYPE:
|
|
KnownAce = ( PKNOWN_ACE )AceHeader;
|
|
AccessMask = KnownAce->Mask;
|
|
Sid = ( PSID )&KnownAce->SidStart;
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
|
|
case SYSTEM_ALARM_OBJECT_ACE_TYPE:
|
|
KnownObjectAce = ( PKNOWN_OBJECT_ACE )AceHeader;
|
|
AccessMask = KnownObjectAce->Mask;
|
|
Sid = RtlObjectAceSid( AceHeader );
|
|
|
|
//
|
|
// Deal with potential guids
|
|
//
|
|
Inherit = RtlObjectAceInheritedObjectType( AceHeader );
|
|
Obj = RtlObjectAceObjectType( AceHeader );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add the rights
|
|
//
|
|
if ( pMaskAsString[i] == 2 ) {
|
|
|
|
wcscpy( Curr, L"0x");
|
|
Curr += 2;
|
|
_ultow( AccessMask, Curr, 16 );
|
|
Curr += wcslen( Curr );
|
|
|
|
} else if ( pMaskAsString[i] == 1 ) {
|
|
|
|
//
|
|
// lookup for the entire value first
|
|
//
|
|
MatchedEntry = LookupAccessMaskInTable( 0, AccessMask, LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
wcscpy( Curr, MatchedEntry->Key );
|
|
Curr += MatchedEntry->KeyLen;
|
|
}
|
|
|
|
} else { // 0
|
|
|
|
for ( j = 0; j < 32; j++ ) {
|
|
|
|
if ( AccessMask & (1 << j) ) {
|
|
|
|
MatchedEntry = LookupAccessMaskInTable( 0, AccessMask & ( 1 << j ), LookupFlags );
|
|
|
|
if ( MatchedEntry ) {
|
|
|
|
wcscpy( Curr, MatchedEntry->Key );
|
|
Curr += MatchedEntry->KeyLen;
|
|
|
|
} // else shouldn't happen but if it happens
|
|
// it is too late to convert it into 0x format
|
|
// because the buffer is already allocated with smaller size.
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
*Curr = SDDL_SEPERATORC;
|
|
Curr++;
|
|
|
|
|
|
//
|
|
// Optional object guid
|
|
//
|
|
if ( Obj ) {
|
|
|
|
Err = UuidToStringW( Obj, &Guid );
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
wcscpy( Curr, Guid );
|
|
Curr += wcslen( Guid );
|
|
RpcStringFreeW( &Guid );
|
|
|
|
}
|
|
*Curr = SDDL_SEPERATORC;
|
|
Curr++;
|
|
|
|
if ( Inherit ) {
|
|
|
|
Err = UuidToStringW( Inherit, &Guid );
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
break;
|
|
}
|
|
|
|
wcscpy( Curr, Guid );
|
|
Curr += wcslen( Guid );
|
|
RpcStringFreeW( &Guid );
|
|
|
|
}
|
|
*Curr = SDDL_SEPERATORC;
|
|
Curr++;
|
|
|
|
//
|
|
// Finally, the sids
|
|
//
|
|
wcscpy( Curr, SidStrings[ i ] );
|
|
Curr += wcslen( SidStrings[ i ] );
|
|
|
|
*Curr = SDDL_ACE_ENDC;
|
|
Curr++;
|
|
*Curr = UNICODE_NULL;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free any allocated memory
|
|
// jinhuang: should always free the allocated buffer
|
|
//
|
|
|
|
// if ( Err != ERROR_SUCCESS && SidStrings ) {
|
|
|
|
for ( j = 0; j < Acl->AceCount; j++ ) {
|
|
|
|
if ( SidFrees[ j ] ) {
|
|
|
|
LocalFree( SidStrings[ j ] );
|
|
}
|
|
}
|
|
// }
|
|
|
|
LocalFree( SidStrings );
|
|
LocalFree( SidFrees );
|
|
LocalFree( pMaskAsString );
|
|
|
|
if ( Err != ERROR_SUCCESS ) {
|
|
|
|
LocalFree(*AclString);
|
|
*AclString = NULL;
|
|
*AclStringSize = 0;
|
|
|
|
} else {
|
|
|
|
*AclStringSize = AclSize;
|
|
|
|
}
|
|
|
|
return( Err );
|
|
}
|
|
|
|
|
|
DWORD
|
|
LocalConvertSDToStringSD_Rev1(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT LPWSTR *StringSecurityDescriptor,
|
|
OUT PULONG StringSecurityDescriptorLen OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a security descriptor into a string version persuant to SDDL definition
|
|
|
|
Arguments:
|
|
|
|
SecurityDescriptor - Security Descriptor to be converted.
|
|
|
|
SecurityInformation - the security information of which component to be converted
|
|
|
|
StringSecurityDescriptor - Where the converted SD is returned. Buffer is allocated via
|
|
LocalAlloc and should be free via LocalFree.
|
|
|
|
StringSecurityDescriptorLen - optional length of the converted SD buffer.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
Extended error status is available using GetLastError.
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
DWORD ReturnBufferSize = 0, AclSize;
|
|
PSID Owner = NULL, Group = NULL;
|
|
PACL Dacl = NULL, Sacl = NULL;
|
|
BOOLEAN Defaulted, SaclPresent=FALSE, DaclPresent=FALSE;
|
|
PWSTR OwnerString = NULL, GroupString = NULL, SaclString = NULL, DaclString = NULL;
|
|
SECURITY_DESCRIPTOR_CONTROL ControlCode;
|
|
ULONG Revision;
|
|
PWSTR DaclControl=NULL, SaclControl=NULL;
|
|
|
|
if ( SecurityDescriptor == NULL || StringSecurityDescriptor == NULL ) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Get the relevant security descriptor parts
|
|
// based on the SecurityInforamtion input parameter
|
|
//
|
|
if ( SecurityInformation & OWNER_SECURITY_INFORMATION ) {
|
|
|
|
Status = RtlGetOwnerSecurityDescriptor( SecurityDescriptor, &Owner, &Defaulted );
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) &&
|
|
SecurityInformation & GROUP_SECURITY_INFORMATION ) {
|
|
|
|
Status = RtlGetGroupSecurityDescriptor( SecurityDescriptor, &Group, &Defaulted );
|
|
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) &&
|
|
SecurityInformation & DACL_SECURITY_INFORMATION ) {
|
|
|
|
Status = RtlGetDaclSecurityDescriptor( SecurityDescriptor, &DaclPresent, &Dacl, &Defaulted );
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) &&
|
|
SecurityInformation & SACL_SECURITY_INFORMATION ) {
|
|
|
|
Status = RtlGetSaclSecurityDescriptor( SecurityDescriptor, &SaclPresent, &Sacl, &Defaulted );
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
Status = RtlGetControlSecurityDescriptor ( SecurityDescriptor, &ControlCode, &Revision);
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
Err = RtlNtStatusToDosError( Status );
|
|
return( Err );
|
|
}
|
|
|
|
//
|
|
// make sure the SidLookup table is reinitialized
|
|
//
|
|
InitializeSidLookupTable(STRSD_REINITIALIZE_ENTER);
|
|
|
|
//
|
|
// Convert the owner and group, if they exist
|
|
//
|
|
if ( Owner ) {
|
|
|
|
Err = LocalGetStringForSid( Owner,
|
|
&OwnerString,
|
|
RootDomainSid
|
|
);
|
|
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS && Group ) {
|
|
|
|
Err = LocalGetStringForSid( Group,
|
|
&GroupString,
|
|
RootDomainSid );
|
|
}
|
|
|
|
//
|
|
// JINHUANG 3/10/98
|
|
// get DACL control string
|
|
//
|
|
if ( Err == ERROR_SUCCESS && ControlCode ) {
|
|
|
|
Err = LocalGetStringForControl(ControlCode, SDDL_VALID_DACL, &DaclControl);
|
|
}
|
|
|
|
//
|
|
// get SACL control string
|
|
//
|
|
if ( Err == ERROR_SUCCESS && ControlCode ) {
|
|
|
|
Err = LocalGetStringForControl(ControlCode, SDDL_VALID_SACL, &SaclControl);
|
|
}
|
|
|
|
//
|
|
// SACL first because the size of DACL is needed later
|
|
//
|
|
if ( Err == ERROR_SUCCESS && SaclPresent ) {
|
|
|
|
|
|
Err = LocalConvertAclToString( Sacl,
|
|
SaclPresent,
|
|
FALSE,
|
|
&SaclString,
|
|
&AclSize,
|
|
RootDomainSid );
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
ReturnBufferSize += AclSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Then, the DACL
|
|
//
|
|
if ( Err == ERROR_SUCCESS && DaclPresent ) {
|
|
|
|
Err = LocalConvertAclToString( Dacl,
|
|
DaclPresent,
|
|
TRUE,
|
|
&DaclString,
|
|
&AclSize,
|
|
RootDomainSid );
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
ReturnBufferSize += AclSize;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, if all of that worked, allocate and build the return string
|
|
//
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
if ( OwnerString ) {
|
|
|
|
ReturnBufferSize += ( wcslen( OwnerString ) * sizeof( WCHAR ) ) +
|
|
SDDL_SIZE_TAG( SDDL_OWNER ) +
|
|
SDDL_SIZE_SEP( SDDL_DELIMINATORC );
|
|
}
|
|
|
|
if ( GroupString ) {
|
|
|
|
ReturnBufferSize += ( wcslen( GroupString ) * sizeof( WCHAR ) ) +
|
|
SDDL_SIZE_TAG( SDDL_GROUP ) +
|
|
SDDL_SIZE_SEP( SDDL_DELIMINATORC );
|
|
}
|
|
|
|
if ( DaclPresent ) {
|
|
|
|
ReturnBufferSize += SDDL_SIZE_TAG( SDDL_DACL ) +
|
|
SDDL_SIZE_SEP( SDDL_DELIMINATORC );
|
|
|
|
if ( DaclControl ) {
|
|
ReturnBufferSize += (wcslen( DaclControl ) * sizeof(WCHAR)) ;
|
|
}
|
|
}
|
|
|
|
if ( SaclPresent ) {
|
|
|
|
ReturnBufferSize += SDDL_SIZE_TAG( SDDL_SACL ) +
|
|
SDDL_SIZE_SEP( SDDL_DELIMINATORC );
|
|
|
|
if ( SaclControl ) {
|
|
ReturnBufferSize += (wcslen( SaclControl ) * sizeof(WCHAR)) ;
|
|
}
|
|
}
|
|
|
|
|
|
*StringSecurityDescriptor = LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
|
ReturnBufferSize + sizeof( WCHAR ) );
|
|
|
|
if ( *StringSecurityDescriptor == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Build the string from the previously determined components. Note that
|
|
// if a component is not present, it is skipped.
|
|
//
|
|
DWORD dwOffset=0;
|
|
|
|
if ( OwnerString ) {
|
|
|
|
swprintf( *StringSecurityDescriptor, L"%ws%wc%ws",
|
|
SDDL_OWNER, SDDL_DELIMINATORC, OwnerString );
|
|
|
|
dwOffset = wcslen(*StringSecurityDescriptor);
|
|
}
|
|
|
|
if ( GroupString ) {
|
|
|
|
swprintf( *StringSecurityDescriptor + dwOffset,
|
|
L"%ws%wc%ws", SDDL_GROUP, SDDL_DELIMINATORC, GroupString );
|
|
|
|
Revision = wcslen( *StringSecurityDescriptor + dwOffset ); // temp use
|
|
dwOffset += Revision;
|
|
|
|
}
|
|
|
|
if ( DaclPresent ) {
|
|
|
|
if ( DaclControl ) {
|
|
swprintf( *StringSecurityDescriptor + dwOffset,
|
|
L"%ws%wc%ws", SDDL_DACL, SDDL_DELIMINATORC, DaclControl );
|
|
} else {
|
|
swprintf( *StringSecurityDescriptor + dwOffset,
|
|
L"%ws%wc", SDDL_DACL, SDDL_DELIMINATORC );
|
|
}
|
|
|
|
Revision = wcslen( *StringSecurityDescriptor + dwOffset );
|
|
dwOffset += Revision;
|
|
|
|
if ( DaclString ) {
|
|
|
|
wcscpy( *StringSecurityDescriptor + dwOffset, DaclString );
|
|
|
|
Revision = wcslen( *StringSecurityDescriptor + dwOffset ); // temp use
|
|
dwOffset += Revision; // (AclSize/sizeof(WCHAR));
|
|
}
|
|
|
|
}
|
|
|
|
if ( SaclPresent ) {
|
|
|
|
if ( SaclControl ) {
|
|
|
|
swprintf( *StringSecurityDescriptor + dwOffset,
|
|
L"%ws%wc%ws", SDDL_SACL, SDDL_DELIMINATORC, SaclControl );
|
|
} else {
|
|
swprintf( *StringSecurityDescriptor + dwOffset,
|
|
L"%ws%wc", SDDL_SACL, SDDL_DELIMINATORC );
|
|
}
|
|
|
|
Revision = wcslen( *StringSecurityDescriptor + dwOffset );
|
|
dwOffset += Revision;
|
|
|
|
if ( SaclString ) {
|
|
|
|
wcscpy( *StringSecurityDescriptor + dwOffset, SaclString);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// jinhuang
|
|
// output the buffer size
|
|
//
|
|
|
|
if ( StringSecurityDescriptorLen ) {
|
|
*StringSecurityDescriptorLen = ReturnBufferSize/sizeof(WCHAR);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Free any buffers that were allocated
|
|
//
|
|
LocalFree( OwnerString );
|
|
LocalFree( GroupString );
|
|
LocalFree( SaclString );
|
|
LocalFree( DaclString );
|
|
|
|
//
|
|
// JINHUANG 3/10/98
|
|
// it's ok to free them even if they are NULL
|
|
//
|
|
LocalFree( SaclControl );
|
|
LocalFree( DaclControl );
|
|
|
|
//
|
|
// decrement the SidLookup instance
|
|
//
|
|
InitializeSidLookupTable(STRSD_REINITIALIZE_LEAVE);
|
|
|
|
return( Err );
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
LocalConvertStringSDToSD_Rev1(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN BOOLEAN DefaultToDomain,
|
|
IN LPCWSTR StringSecurityDescriptor,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a revision 1 stringized Security descriptor into a valid, functional
|
|
security descriptor
|
|
|
|
Arguments:
|
|
|
|
StringSecurityDescriptor - Stringized security descriptor to be converted.
|
|
|
|
SecurityDescriptor - Where the converted SD is returned. Buffer is allocated via
|
|
LocalAlloc and should be free via LocalFree. The returned security descriptor
|
|
is always self relative
|
|
|
|
SecurityDescriptorSize - OPTIONAL. If non-NULL, the size of the converted security
|
|
descriptor is returned here.
|
|
|
|
SecurityInformation - OPTIONAL. If non-NULL, the security information of the converted
|
|
security descriptor is returned here.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
Extended error status is available using GetLastError.
|
|
|
|
ERROR_INVALID_PARAMETER - A NULL input or output parameter was given
|
|
|
|
ERROR_UNKNOWN_REVISION - An unsupported revision was given
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = ERROR_SUCCESS;
|
|
PSID Owner = NULL, Group = NULL;
|
|
PACL Dacl = NULL, Sacl = NULL;
|
|
SECURITY_INFORMATION SeInfo = 0;
|
|
SECURITY_DESCRIPTOR SD;
|
|
PWSTR Curr, End;
|
|
NTSTATUS Status;
|
|
BOOLEAN FreeOwner = FALSE, FreeGroup = FALSE;
|
|
SID_IDENTIFIER_AUTHORITY UaspBuiltinAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY UaspCreatorAuthority = SECURITY_CREATOR_SID_AUTHORITY;
|
|
ULONG SDSize = 0;
|
|
BOOLEAN DaclPresent = FALSE, SaclPresent = FALSE;
|
|
SECURITY_DESCRIPTOR_CONTROL DaclControl=0, SaclControl=0;
|
|
PSTRSD_SID_LOOKUP tSidLookupDomOrRootDomRelativeTable = NULL;
|
|
|
|
|
|
if ( StringSecurityDescriptor == NULL || SecurityDescriptor == NULL ) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if ( SecurityDescriptorSize ) {
|
|
|
|
*SecurityDescriptorSize = 0;
|
|
}
|
|
|
|
//
|
|
// make sure the SidLookup table is reinitialized
|
|
//
|
|
InitializeSidLookupTable(STRSD_REINITIALIZE_ENTER);
|
|
|
|
//
|
|
// for the API ConvertStringSDToSDDomain, if DomainSid != NULL, we need a table on
|
|
// the heap, for this thread to stash lookups for ST_DOMAIN_RELATIVE type trustees
|
|
// this table will be freed when all lookups have been done for this thread
|
|
//
|
|
|
|
|
|
|
|
if (DomainSid) {
|
|
|
|
tSidLookupDomOrRootDomRelativeTable =
|
|
(PSTRSD_SID_LOOKUP) LocalAlloc(LMEM_ZEROINIT, sizeof(SidLookupDomOrRootDomRelative));
|
|
|
|
//
|
|
// table copy/init from the template-table
|
|
//
|
|
|
|
if (tSidLookupDomOrRootDomRelativeTable)
|
|
|
|
memcpy(tSidLookupDomOrRootDomRelativeTable,
|
|
SidLookupDomOrRootDomRelative,
|
|
sizeof(SidLookupDomOrRootDomRelative));
|
|
|
|
else
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
}
|
|
|
|
//
|
|
// Now, we'll just start parsing and building
|
|
//
|
|
Curr = ( PWSTR )StringSecurityDescriptor;
|
|
|
|
while ( Err == ERROR_SUCCESS && Curr ) {
|
|
|
|
switch( *Curr ) {
|
|
|
|
//
|
|
// Get the Owner sid
|
|
//
|
|
case L'O':
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
if ( *(Curr+1) == SDDL_DELIMINATORC ) {
|
|
|
|
Curr += 2;
|
|
|
|
if ( Owner == NULL ) {
|
|
|
|
Err = LocalGetSidForString( Curr,
|
|
&Owner,
|
|
&End,
|
|
&FreeOwner,
|
|
RootDomainSid,
|
|
DomainSid,
|
|
tSidLookupDomOrRootDomRelativeTable,
|
|
DefaultToDomain );
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
Curr = End;
|
|
SeInfo |= OWNER_SECURITY_INFORMATION;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Get the Group sid
|
|
//
|
|
case L'G':
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
if ( *(Curr+1) == SDDL_DELIMINATORC ) {
|
|
|
|
Curr += 2;
|
|
|
|
if ( Group == NULL ) {
|
|
|
|
Err = LocalGetSidForString( Curr,
|
|
&Group,
|
|
&End,
|
|
&FreeGroup,
|
|
RootDomainSid,
|
|
DomainSid,
|
|
tSidLookupDomOrRootDomRelativeTable,
|
|
DefaultToDomain );
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
Curr = End;
|
|
SeInfo |= GROUP_SECURITY_INFORMATION;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Next, the DAcl
|
|
//
|
|
case L'D':
|
|
|
|
if ( *(Curr+1) == SDDL_DELIMINATORC ) {
|
|
|
|
Curr += 2;
|
|
|
|
if ( Dacl == NULL ) {
|
|
|
|
//
|
|
// JINHUANG: 3/10/98
|
|
// look for any security descriptor controls
|
|
//
|
|
if ( *Curr != SDDL_ACE_BEGINC ) {
|
|
|
|
Err = LocalGetSDControlForString( Curr,
|
|
SDDL_VALID_DACL,
|
|
&DaclControl,
|
|
&End );
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
Curr = End;
|
|
}
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
Err = LocalGetAclForString( Curr,
|
|
TRUE,
|
|
&Dacl,
|
|
&End,
|
|
RootDomainSid,
|
|
DomainSid,
|
|
tSidLookupDomOrRootDomRelativeTable,
|
|
DefaultToDomain );
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
Curr = End;
|
|
SeInfo |= DACL_SECURITY_INFORMATION;
|
|
DaclPresent = TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
} else {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Finally, the SAcl
|
|
//
|
|
case L'S':
|
|
|
|
if ( *(Curr+1) == SDDL_DELIMINATORC ) {
|
|
|
|
Curr += 2;
|
|
|
|
if ( Sacl == NULL ) {
|
|
|
|
//
|
|
// JINHUANG: 3/10/98
|
|
// look for any security descriptor controls
|
|
//
|
|
if ( *Curr != SDDL_ACE_BEGINC ) {
|
|
|
|
Err = LocalGetSDControlForString( Curr,
|
|
SDDL_VALID_SACL,
|
|
&SaclControl,
|
|
&End );
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
Curr = End;
|
|
}
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
Err = LocalGetAclForString( Curr,
|
|
FALSE,
|
|
&Sacl,
|
|
&End,
|
|
RootDomainSid,
|
|
DomainSid,
|
|
tSidLookupDomOrRootDomRelativeTable,
|
|
DefaultToDomain );
|
|
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
Curr = End;
|
|
SeInfo |= SACL_SECURITY_INFORMATION;
|
|
SaclPresent = TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
} else {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// It's a space, so ignore it
|
|
//
|
|
case L' ':
|
|
Curr++;
|
|
break;
|
|
|
|
//
|
|
// End of the string, so quit
|
|
//
|
|
case L'\0':
|
|
Curr = NULL;
|
|
break;
|
|
|
|
//
|
|
// Don't know what it is, so consider it an error
|
|
//
|
|
default:
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Ok, if we have the information we need, we'll create the security
|
|
// descriptor
|
|
//
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
if ( InitializeSecurityDescriptor(&SD, SECURITY_DESCRIPTOR_REVISION ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
}
|
|
|
|
//
|
|
// JINHUANG 3/10/98
|
|
// set the security descriptor control
|
|
//
|
|
|
|
SD.Control |= (DaclControl | SaclControl);
|
|
|
|
//
|
|
// Now, add the owner and group
|
|
//
|
|
if ( Err == ERROR_SUCCESS && Owner != NULL ) {
|
|
|
|
if ( SetSecurityDescriptorOwner(&SD, Owner, FALSE ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
}
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS && Group != NULL ) {
|
|
|
|
if ( SetSecurityDescriptorGroup( &SD, Group, FALSE ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Then the DACL and SACL
|
|
//
|
|
if ( Err == ERROR_SUCCESS && DaclPresent ) {
|
|
|
|
if ( SetSecurityDescriptorDacl( &SD, DaclPresent, Dacl, FALSE ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
}
|
|
}
|
|
|
|
if ( Err == ERROR_SUCCESS && SaclPresent ) {
|
|
|
|
if ( SetSecurityDescriptorSacl( &SD, SaclPresent, Sacl, FALSE ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Finally, we'll allocate our buffer and return
|
|
//
|
|
if ( Err == ERROR_SUCCESS ) {
|
|
|
|
MakeSelfRelativeSD( &SD, *SecurityDescriptor, &SDSize );
|
|
|
|
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
|
|
|
|
*SecurityDescriptor = (PSECURITY_DESCRIPTOR) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
|
|
SDSize );
|
|
|
|
if ( *SecurityDescriptor == NULL ) {
|
|
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
} else {
|
|
|
|
if ( MakeSelfRelativeSD( &SD, *SecurityDescriptor, &SDSize ) == FALSE ) {
|
|
|
|
Err = GetLastError();
|
|
LocalFree( *SecurityDescriptor );
|
|
*SecurityDescriptor = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This should never happen
|
|
//
|
|
if ( GetLastError() == ERROR_SUCCESS ) {
|
|
|
|
Err = ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the security descriptor size, if requested
|
|
//
|
|
if ( Err == ERROR_SUCCESS && SecurityDescriptorSize ) {
|
|
|
|
*SecurityDescriptorSize = SDSize;
|
|
}
|
|
|
|
//
|
|
// Finally, free any memory we may have allocated...
|
|
//
|
|
if ( FreeOwner == TRUE ) {
|
|
|
|
LocalFree( Owner );
|
|
|
|
}
|
|
|
|
if ( FreeGroup == TRUE ) {
|
|
|
|
LocalFree( Group );
|
|
|
|
}
|
|
|
|
LocalFree( Dacl );
|
|
LocalFree( Sacl );
|
|
|
|
if (tSidLookupDomOrRootDomRelativeTable)
|
|
|
|
LocalFree(tSidLookupDomOrRootDomRelativeTable);
|
|
|
|
//
|
|
// make sure the SidLookup table is reinitialized
|
|
//
|
|
InitializeSidLookupTable(STRSD_REINITIALIZE_LEAVE);
|
|
|
|
return( Err );
|
|
}
|
|
|
|
STRSD_SID_LOOKUP gDomainSidLookup;
|
|
STRSD_SID_LOOKUP gDnsDomainSidLookup;
|
|
|
|
BOOL gbDomainSidCached = FALSE;
|
|
BOOL gbDnsDomainSidCached = FALSE;
|
|
|
|
|
|
BOOLEAN
|
|
CacheDomainAndDnsDomainSids(
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
LSA_HANDLE LsaPolicyHandle;
|
|
PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL;
|
|
PPOLICY_DNS_DOMAIN_INFO DnsDomainInfo = NULL;
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
|
|
|
|
Status = LsaOpenPolicy( NULL, &ObjectAttributes,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
&LsaPolicyHandle );
|
|
|
|
//
|
|
// There's a high probability that both LSA queries succeed or fail together
|
|
//
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
Status = LsaQueryInformationPolicy( LsaPolicyHandle,
|
|
PolicyDnsDomainInformation,
|
|
( PVOID * )&DnsDomainInfo );
|
|
|
|
if ( NT_SUCCESS(Status) && DnsDomainInfo != NULL && DnsDomainInfo->Sid != NULL ) {
|
|
|
|
RtlCopyMemory( gDnsDomainSidLookup.SidBuff, DnsDomainInfo->Sid,
|
|
RtlLengthSid( DnsDomainInfo->Sid ) );
|
|
|
|
gDnsDomainSidLookup.Sid = (PSID) gDnsDomainSidLookup.SidBuff;
|
|
|
|
gbDnsDomainSidCached = TRUE;
|
|
}
|
|
|
|
|
|
Status = LsaQueryInformationPolicy( LsaPolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
( PVOID * )&DomainInfo );
|
|
|
|
if ( NT_SUCCESS(Status) && DomainInfo != NULL && DomainInfo->DomainSid != NULL ) {
|
|
|
|
RtlCopyMemory( gDomainSidLookup.SidBuff, DomainInfo->DomainSid,
|
|
RtlLengthSid( DomainInfo->DomainSid ) );
|
|
|
|
gDomainSidLookup.Sid = (PSID) gDomainSidLookup.SidBuff;
|
|
|
|
gbDomainSidCached = TRUE;
|
|
}
|
|
|
|
LsaClose( LsaPolicyHandle );
|
|
|
|
}
|
|
|
|
LsaFreeMemory( DomainInfo );
|
|
LsaFreeMemory( DnsDomainInfo );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL gbLookupTableInitialized = FALSE;
|
|
|
|
|
|
BOOLEAN
|
|
InitializeSidLookupTable(
|
|
IN BYTE InitFlag
|
|
)
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY UaspWorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY UaspLocalAuthority = SECURITY_LOCAL_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY UaspCreatorAuthority = SECURITY_CREATOR_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY UaspNtAuthority = SECURITY_NT_AUTHORITY;
|
|
DWORD i;
|
|
|
|
|
|
EnterCriticalSection(&SddlSidLookupCritical);
|
|
|
|
switch ( InitFlag ) {
|
|
case STRSD_REINITIALIZE_ENTER:
|
|
|
|
SidTableReinitializeInstance++;
|
|
|
|
if ( SidTableReinitializeInstance > 1 || gbLookupTableInitialized == TRUE) {
|
|
//
|
|
// there is another thread going, or no need to reinitialize the table again
|
|
//
|
|
LeaveCriticalSection(&SddlSidLookupCritical);
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case STRSD_REINITIALIZE_LEAVE:
|
|
|
|
if ( SidTableReinitializeInstance > 1 ) {
|
|
SidTableReinitializeInstance--;
|
|
} else {
|
|
SidTableReinitializeInstance = 0;
|
|
}
|
|
|
|
LeaveCriticalSection(&SddlSidLookupCritical);
|
|
|
|
return TRUE;
|
|
break;
|
|
}
|
|
|
|
|
|
CacheDomainAndDnsDomainSids();
|
|
|
|
//
|
|
// If the list of sids hasn't been built, do it now
|
|
//
|
|
// JINHUANG 3/26 BVT break,
|
|
// see comments above always try to initialization table
|
|
// but skip the ones already initialized for performance
|
|
//
|
|
for ( i = 0;
|
|
i < sizeof( SidLookup ) / sizeof( STRSD_SID_LOOKUP ); i++ ) {
|
|
|
|
if ( STRSD_REINITIALIZE_ENTER == InitFlag ) {
|
|
SidLookup[ i ].Valid = FALSE;
|
|
}
|
|
|
|
if ( SidLookup[ i ].Valid == TRUE &&
|
|
SidLookup[ i ].Sid != NULL ) {
|
|
//
|
|
// this one is already initialized
|
|
//
|
|
continue;
|
|
}
|
|
|
|
SidLookup[ i ].Sid = ( PSID )SidLookup[ i ].SidBuff;
|
|
|
|
switch ( SidLookup[ i ].SidType ) {
|
|
case ST_DOMAIN_RELATIVE:
|
|
|
|
if ( gbDnsDomainSidCached ) {
|
|
|
|
RtlCopyMemory( SidLookup[ i ].Sid, gDnsDomainSidLookup.Sid,
|
|
RtlLengthSid( gDnsDomainSidLookup.Sid ) );
|
|
( ( PISID )( SidLookup[ i ].Sid ) )->SubAuthorityCount++;
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid,
|
|
*( RtlSubAuthorityCountSid( gDnsDomainSidLookup.Sid ) ) ) ) =
|
|
SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ST_ROOT_DOMAIN_RELATIVE:
|
|
//
|
|
// will be initialized on demand
|
|
//
|
|
break;
|
|
|
|
case ST_WORLD:
|
|
RtlInitializeSid( SidLookup[ i ].Sid, &UaspWorldAuthority, 1 );
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid, 0 ) ) = SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
break;
|
|
|
|
case ST_LOCALSY:
|
|
RtlInitializeSid( SidLookup[ i ].Sid, &UaspLocalAuthority, 1 );
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid, 0 ) ) = SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
break;
|
|
|
|
case ST_LOCAL:
|
|
if ( gbDomainSidCached ) {
|
|
|
|
RtlCopyMemory( SidLookup[ i ].Sid, gDomainSidLookup.Sid,
|
|
RtlLengthSid( gDomainSidLookup.Sid ) );
|
|
|
|
( ( PISID )( SidLookup[ i ].Sid ) )->SubAuthorityCount++;
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid,
|
|
*( RtlSubAuthorityCountSid( gDomainSidLookup.Sid ) ) ) ) =
|
|
SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
}
|
|
break;
|
|
|
|
case ST_CREATOR:
|
|
RtlInitializeSid( SidLookup[ i ].Sid, &UaspCreatorAuthority, 1 );
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid, 0 ) ) = SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
break;
|
|
|
|
case ST_NTAUTH:
|
|
RtlInitializeSid( SidLookup[ i ].Sid, &UaspNtAuthority, 1 );
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid, 0 ) ) = SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
break;
|
|
|
|
case ST_BUILTIN:
|
|
RtlInitializeSid( SidLookup[ i ].Sid, &UaspNtAuthority, 2 );
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid, 0 ) ) = SECURITY_BUILTIN_DOMAIN_RID;
|
|
*( RtlSubAuthoritySid( SidLookup[ i ].Sid, 1 ) ) = SidLookup[ i ].Rid;
|
|
SidLookup[ i ].Valid = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// only update gbLookupTableInitialized if LSA Lookups passed
|
|
//
|
|
|
|
if (gbDnsDomainSidCached && gbDomainSidCached)
|
|
gbLookupTableInitialized = TRUE;
|
|
|
|
LeaveCriticalSection(&SddlSidLookupCritical);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSDToSDRootDomainA(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN LPCSTR StringSecurityDescriptor,
|
|
IN DWORD StringSDRevision,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
)
|
|
{
|
|
UNICODE_STRING Unicode;
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
BOOL Result;
|
|
|
|
if ( NULL == StringSecurityDescriptor ||
|
|
NULL == SecurityDescriptor ) {
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return(FALSE);
|
|
}
|
|
|
|
RtlInitAnsiString( &AnsiString, StringSecurityDescriptor );
|
|
|
|
Status = SddlpAnsiStringToUnicodeString(&Unicode,
|
|
&AnsiString);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
BaseSetLastNTError( Status );
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
Result = ConvertStringSDToSDRootDomainW( RootDomainSid,
|
|
( LPCWSTR )Unicode.Buffer,
|
|
StringSDRevision,
|
|
SecurityDescriptor,
|
|
SecurityDescriptorSize);
|
|
|
|
LocalFree( Unicode.Buffer );
|
|
|
|
if ( Result ) {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
return( Result );
|
|
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSDToSDRootDomainW(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN LPCWSTR StringSecurityDescriptor,
|
|
IN DWORD StringSDRevision,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
)
|
|
{
|
|
|
|
DWORD Err = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Little elementary parameter checking...
|
|
//
|
|
if ( StringSecurityDescriptor == NULL || SecurityDescriptor == NULL ) {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
switch ( StringSDRevision ) {
|
|
case SDDL_REVISION_1:
|
|
|
|
Err = LocalConvertStringSDToSD_Rev1( RootDomainSid, // root domain sid
|
|
NULL, // no domain sid is provided for this API
|
|
TRUE, // default to domain for EA/SA if root domain sid is not provided
|
|
StringSecurityDescriptor,
|
|
SecurityDescriptor,
|
|
SecurityDescriptorSize);
|
|
break;
|
|
|
|
default:
|
|
|
|
Err = ERROR_UNKNOWN_REVISION;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
SetLastError( Err );
|
|
|
|
return( Err == ERROR_SUCCESS );
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertSDToStringSDRootDomainA(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN DWORD RequestedStringSDRevision,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT LPSTR *StringSecurityDescriptor OPTIONAL,
|
|
OUT PULONG StringSecurityDescriptorLen OPTIONAL
|
|
)
|
|
{
|
|
LPWSTR StringSecurityDescriptorW = NULL;
|
|
ULONG AnsiLen, WideLen = 0;
|
|
BOOL ReturnValue ;
|
|
|
|
if ( StringSecurityDescriptor == NULL ||
|
|
0 == SecurityInformation ) {
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return( FALSE );
|
|
}
|
|
|
|
ReturnValue = ConvertSDToStringSDRootDomainW(
|
|
RootDomainSid,
|
|
SecurityDescriptor,
|
|
RequestedStringSDRevision,
|
|
SecurityInformation,
|
|
&StringSecurityDescriptorW,
|
|
&WideLen );
|
|
|
|
if ( ReturnValue ) {
|
|
|
|
|
|
AnsiLen = WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
StringSecurityDescriptorW,
|
|
WideLen + 1,
|
|
*StringSecurityDescriptor,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( AnsiLen != 0 ) {
|
|
|
|
*StringSecurityDescriptor = LocalAlloc( LMEM_FIXED, AnsiLen );
|
|
|
|
if ( *StringSecurityDescriptor == NULL ) {
|
|
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
ReturnValue = FALSE;
|
|
|
|
} else {
|
|
|
|
AnsiLen = WideCharToMultiByte( CP_ACP,
|
|
0,
|
|
StringSecurityDescriptorW,
|
|
WideLen + 1,
|
|
*StringSecurityDescriptor,
|
|
AnsiLen,
|
|
NULL,
|
|
NULL );
|
|
ASSERT( AnsiLen != 0 );
|
|
|
|
if ( AnsiLen == 0 ) {
|
|
|
|
LocalFree(*StringSecurityDescriptor);
|
|
*StringSecurityDescriptor = NULL;
|
|
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
//
|
|
// jinhuang
|
|
// output the length (optional)
|
|
//
|
|
if ( StringSecurityDescriptorLen ) {
|
|
*StringSecurityDescriptorLen = AnsiLen;
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
LocalFree(StringSecurityDescriptorW);
|
|
|
|
}
|
|
|
|
if ( ReturnValue ) {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
return( ReturnValue );
|
|
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertSDToStringSDRootDomainW(
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN DWORD RequestedStringSDRevision,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT LPWSTR *StringSecurityDescriptor OPTIONAL,
|
|
OUT PULONG StringSecurityDescriptorLen OPTIONAL
|
|
)
|
|
{
|
|
|
|
DWORD Err = ERROR_SUCCESS;
|
|
|
|
//
|
|
// A little parameter checking...
|
|
//
|
|
if ( SecurityDescriptor == NULL || StringSecurityDescriptor == NULL ||
|
|
SecurityInformation == 0 ) {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
switch ( RequestedStringSDRevision ) {
|
|
case SDDL_REVISION_1:
|
|
|
|
Err = LocalConvertSDToStringSD_Rev1( RootDomainSid, // root domain sid
|
|
SecurityDescriptor,
|
|
SecurityInformation,
|
|
StringSecurityDescriptor,
|
|
StringSecurityDescriptorLen );
|
|
break;
|
|
|
|
default:
|
|
Err = ERROR_UNKNOWN_REVISION;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
SetLastError( Err );
|
|
|
|
return( Err == ERROR_SUCCESS);
|
|
|
|
}
|
|
|
|
BOOL
|
|
SddlpGetRootDomainSid(void)
|
|
{
|
|
//
|
|
// get root domain sid, save it in RootDomSidBuf (global)
|
|
// this function is called within the critical section
|
|
//
|
|
// 1) ldap_open to the DC of interest.
|
|
// 2) you do not need to ldap_connect - the following step works anonymously
|
|
// 3) read the operational attribute rootDomainNamingContext and provide the
|
|
// operational control LDAP_SERVER_EXTENDED_DN_OID as defined in sdk\inc\ntldap.h.
|
|
|
|
|
|
DWORD Win32rc=NO_ERROR;
|
|
BOOL bRetValue=FALSE;
|
|
|
|
HINSTANCE hLdapDll=NULL;
|
|
PFN_LDAP_INIT pfnLdapInit=NULL;
|
|
PFN_LDAP_SET_OPTION pfnLdapSetOption=NULL;
|
|
PFN_LDAP_BIND pfnLdapBind=NULL;
|
|
PFN_LDAP_UNBIND pfnLdapUnbind=NULL;
|
|
PFN_LDAP_SEARCH pfnLdapSearch=NULL;
|
|
PFN_LDAP_FIRST_ENTRY pfnLdapFirstEntry=NULL;
|
|
PFN_LDAP_GET_VALUE pfnLdapGetValue=NULL;
|
|
PFN_LDAP_MSGFREE pfnLdapMsgFree=NULL;
|
|
PFN_LDAP_VALUE_FREE pfnLdapValueFree=NULL;
|
|
PFN_LDAP_MAP_ERROR pfnLdapMapError=NULL;
|
|
|
|
PLDAP phLdap=NULL;
|
|
|
|
LDAPControlA serverControls = { LDAP_SERVER_EXTENDED_DN_OID,
|
|
{ 0, (PCHAR) NULL },
|
|
TRUE
|
|
};
|
|
LPSTR Attribs[] = { LDAP_OPATT_ROOT_DOMAIN_NAMING_CONTEXT, NULL };
|
|
|
|
PLDAPControlA rServerControls[] = { &serverControls, NULL };
|
|
PLDAPMessage pMessage = NULL;
|
|
LDAPMessage *pEntry = NULL;
|
|
PCHAR *ppszValues=NULL;
|
|
|
|
LPSTR pSidStart, pSidEnd, pParse;
|
|
BYTE *pDest;
|
|
BYTE OneByte;
|
|
|
|
hLdapDll = LoadLibraryA("wldap32.dll");
|
|
|
|
if ( hLdapDll) {
|
|
pfnLdapInit = (PFN_LDAP_INIT)GetProcAddress(hLdapDll,
|
|
"ldap_initA");
|
|
pfnLdapSetOption = (PFN_LDAP_SET_OPTION)GetProcAddress(hLdapDll,
|
|
"ldap_set_option");
|
|
pfnLdapBind = (PFN_LDAP_BIND)GetProcAddress(hLdapDll,
|
|
"ldap_bind_sA");
|
|
pfnLdapUnbind = (PFN_LDAP_UNBIND)GetProcAddress(hLdapDll,
|
|
"ldap_unbind");
|
|
pfnLdapSearch = (PFN_LDAP_SEARCH)GetProcAddress(hLdapDll,
|
|
"ldap_search_ext_sA");
|
|
pfnLdapFirstEntry = (PFN_LDAP_FIRST_ENTRY)GetProcAddress(hLdapDll,
|
|
"ldap_first_entry");
|
|
pfnLdapGetValue = (PFN_LDAP_GET_VALUE)GetProcAddress(hLdapDll,
|
|
"ldap_get_valuesA");
|
|
pfnLdapMsgFree = (PFN_LDAP_MSGFREE)GetProcAddress(hLdapDll,
|
|
"ldap_msgfree");
|
|
pfnLdapValueFree = (PFN_LDAP_VALUE_FREE)GetProcAddress(hLdapDll,
|
|
"ldap_value_freeA");
|
|
pfnLdapMapError = (PFN_LDAP_MAP_ERROR)GetProcAddress(hLdapDll,
|
|
"LdapMapErrorToWin32");
|
|
}
|
|
|
|
if ( pfnLdapInit == NULL ||
|
|
pfnLdapSetOption == NULL ||
|
|
pfnLdapBind == NULL ||
|
|
pfnLdapUnbind == NULL ||
|
|
pfnLdapSearch == NULL ||
|
|
pfnLdapFirstEntry == NULL ||
|
|
pfnLdapGetValue == NULL ||
|
|
pfnLdapMsgFree == NULL ||
|
|
pfnLdapValueFree == NULL ||
|
|
pfnLdapMapError == NULL ) {
|
|
|
|
Win32rc = ERROR_PROC_NOT_FOUND;
|
|
|
|
} else {
|
|
|
|
//
|
|
// bind to ldap
|
|
//
|
|
|
|
phLdap = (*pfnLdapInit)(NULL, LDAP_PORT);
|
|
|
|
if ( phLdap == NULL ) {
|
|
Win32rc = ERROR_DS_UNAVAILABLE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Turn on the encryption option
|
|
//
|
|
|
|
LONG LdapOption = PtrToLong( LDAP_OPT_ON );
|
|
ULONG uLdapStatus = (*pfnLdapSetOption)( phLdap, LDAP_OPT_ENCRYPT, &LdapOption );
|
|
Win32rc = (*pfnLdapMapError)( uLdapStatus );
|
|
|
|
//
|
|
// If everything goes on fine, then we can finally bind
|
|
//
|
|
|
|
if (NO_ERROR == Win32rc)
|
|
{
|
|
uLdapStatus = (*pfnLdapBind)(phLdap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
|
|
Win32rc = (*pfnLdapMapError)( uLdapStatus );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( NO_ERROR == Win32rc ) {
|
|
//
|
|
// now get the ldap handle,
|
|
//
|
|
|
|
Win32rc = (*pfnLdapSearch)(
|
|
phLdap,
|
|
"",
|
|
LDAP_SCOPE_BASE,
|
|
"(objectClass=*)",
|
|
Attribs,
|
|
0,
|
|
(PLDAPControlA *)&rServerControls,
|
|
NULL,
|
|
NULL,
|
|
10000,
|
|
&pMessage);
|
|
|
|
if( Win32rc == NO_ERROR && pMessage ) {
|
|
|
|
Win32rc = ERROR_SUCCESS;
|
|
|
|
pEntry = (*pfnLdapFirstEntry)(phLdap, pMessage);
|
|
|
|
if(pEntry == NULL) {
|
|
|
|
Win32rc = (*pfnLdapMapError)( phLdap->ld_errno );
|
|
|
|
} else {
|
|
//
|
|
// Now, we'll have to get the values
|
|
//
|
|
ppszValues = (*pfnLdapGetValue)(phLdap,
|
|
pEntry,
|
|
Attribs[0]);
|
|
|
|
if( ppszValues == NULL) {
|
|
|
|
Win32rc = (*pfnLdapMapError)( phLdap->ld_errno );
|
|
|
|
} else if ( ppszValues[0] && ppszValues[0][0] != '\0' ) {
|
|
|
|
//
|
|
// ppszValues[0] is the value to parse.
|
|
// The data will be returned as something like:
|
|
|
|
// <GUID=278676f8d753d211a61ad7e2dfa25f11>;<SID=010400000000000515000000828ba6289b0bc11e67c2ef7f>;DC=colinbrdom1,DC=nttest,DC=microsoft,DC=com
|
|
|
|
// Parse through this to find the <SID=xxxxxx> part. Note that it may be missing, but the GUID= and trailer should not be.
|
|
// The xxxxx represents the hex nibbles of the SID. Translate to the binary form and case to a SID.
|
|
|
|
|
|
pSidStart = strstr(ppszValues[0], "<SID=");
|
|
|
|
if ( pSidStart ) {
|
|
//
|
|
// find the end of this SID
|
|
//
|
|
pSidEnd = strchr(pSidStart, '>');
|
|
|
|
if ( pSidEnd ) {
|
|
|
|
EnterCriticalSection(&SddlSidLookupCritical);
|
|
|
|
pParse = pSidStart + 5;
|
|
pDest = (BYTE *)RootDomSidBuf;
|
|
|
|
while ( pParse < pSidEnd-1 ) {
|
|
|
|
if ( *pParse >= '0' && *pParse <= '9' ) {
|
|
OneByte = (BYTE) ((*pParse - '0') * 16);
|
|
} else {
|
|
OneByte = (BYTE) ( (tolower(*pParse) - 'a' + 10) * 16 );
|
|
}
|
|
|
|
if ( *(pParse+1) >= '0' && *(pParse+1) <= '9' ) {
|
|
OneByte += (BYTE) ( *(pParse+1) - '0' ) ;
|
|
} else {
|
|
OneByte += (BYTE) ( tolower(*(pParse+1)) - 'a' + 10 ) ;
|
|
}
|
|
|
|
*pDest = OneByte;
|
|
pDest++;
|
|
pParse += 2;
|
|
}
|
|
|
|
RootDomInited = TRUE;
|
|
|
|
LeaveCriticalSection(&SddlSidLookupCritical);
|
|
|
|
bRetValue = TRUE;
|
|
|
|
} else {
|
|
Win32rc = ERROR_OBJECT_NOT_FOUND;
|
|
}
|
|
} else {
|
|
Win32rc = ERROR_OBJECT_NOT_FOUND;
|
|
}
|
|
|
|
(*pfnLdapValueFree)(ppszValues);
|
|
|
|
} else {
|
|
Win32rc = ERROR_OBJECT_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
(*pfnLdapMsgFree)(pMessage);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// even though it's not binded, use unbind to close
|
|
//
|
|
if ( phLdap != NULL && pfnLdapUnbind )
|
|
(*pfnLdapUnbind)(phLdap);
|
|
|
|
if ( hLdapDll ) {
|
|
FreeLibrary(hLdapDll);
|
|
}
|
|
|
|
SetLastError(Win32rc);
|
|
|
|
return bRetValue;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSDToSDDomainA(
|
|
IN PSID DomainSid,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN LPCSTR StringSecurityDescriptor,
|
|
IN DWORD StringSDRevision,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
)
|
|
/*
|
|
Routine Description:
|
|
|
|
This API is exported from advapi32.dll.
|
|
This is the ANSI version of ConvertStringSDToSDDomainW and it calls the latter API.
|
|
See description for ConvertStringSDToSDDomainW.
|
|
|
|
*/
|
|
{
|
|
UNICODE_STRING Unicode;
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
BOOL Result;
|
|
|
|
if ( NULL == StringSecurityDescriptor ||
|
|
NULL == SecurityDescriptor ||
|
|
NULL == DomainSid ||
|
|
!RtlValidSid(DomainSid) ||
|
|
(RootDomainSid && !RtlValidSid(RootDomainSid)) ) {
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return(FALSE);
|
|
}
|
|
|
|
RtlInitAnsiString( &AnsiString, StringSecurityDescriptor );
|
|
|
|
Status = SddlpAnsiStringToUnicodeString(&Unicode,
|
|
&AnsiString);
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
|
|
BaseSetLastNTError( Status );
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
Result = ConvertStringSDToSDDomainW( DomainSid,
|
|
RootDomainSid,
|
|
( LPCWSTR )Unicode.Buffer,
|
|
StringSDRevision,
|
|
SecurityDescriptor,
|
|
SecurityDescriptorSize);
|
|
|
|
LocalFree( Unicode.Buffer );
|
|
|
|
if ( Result ) {
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
return( Result );
|
|
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
ConvertStringSDToSDDomainW(
|
|
IN PSID DomainSid,
|
|
IN PSID RootDomainSid OPTIONAL,
|
|
IN LPCWSTR StringSecurityDescriptor,
|
|
IN DWORD StringSDRevision,
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor,
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL
|
|
)
|
|
/*
|
|
Routine Description:
|
|
|
|
This API is exported from advapi32.dll. It is similar to ConvertSDToStringSDRootDomainW
|
|
except that it takes in a required DomainSid parameter. Domain relative trustees will be
|
|
resolved w.r.t. DomainSid.
|
|
|
|
|
|
Arguments:
|
|
|
|
IN PSID DomainSid - pointer to the domain sid w.r.t. which
|
|
ST_DOMAIN_RELATIVE type trustees are resolved against
|
|
IN PSID RootDomainSid OPTIONAL - pointer to root domain sid w.r.t. which
|
|
ST_ROOT_DOMAIN_RELATIVE type trustees are resolved against
|
|
if this is NULL, the local m/c's root domain is used
|
|
IN LPCWSTR StringSecurityDescriptor - the input SDDL string
|
|
IN DWORD StringSDRevision - SDDL revision, only SDDL_REVISION_1 is supported
|
|
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor - pointer to the constructed security descriptor
|
|
OUT PULONG SecurityDescriptorSize OPTIONAL - size of the constructed security descriptor
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if succeeded else FALSE. Caller can use GetLastError() to retrieve the error code.
|
|
*/
|
|
{
|
|
|
|
DWORD Err = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Little elementary parameter checking...
|
|
//
|
|
if ( StringSecurityDescriptor == NULL ||
|
|
SecurityDescriptor == NULL ||
|
|
DomainSid == NULL ||
|
|
!RtlValidSid(DomainSid) ||
|
|
(RootDomainSid && !RtlValidSid(RootDomainSid)) ) {
|
|
|
|
Err = ERROR_INVALID_PARAMETER;
|
|
|
|
} else {
|
|
|
|
switch ( StringSDRevision ) {
|
|
case SDDL_REVISION_1:
|
|
|
|
Err = LocalConvertStringSDToSD_Rev1( RootDomainSid, // no root domain sid maybe provided for this API
|
|
DomainSid, // domain sid
|
|
FALSE, // domain sid is required
|
|
StringSecurityDescriptor,
|
|
SecurityDescriptor,
|
|
SecurityDescriptorSize);
|
|
break;
|
|
|
|
default:
|
|
|
|
Err = ERROR_UNKNOWN_REVISION;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
SetLastError( Err );
|
|
|
|
return( Err == ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SddlpAnsiStringToUnicodeString(
|
|
OUT PUNICODE_STRING DestinationString,
|
|
IN PANSI_STRING SourceString
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
See RtlAnsiStringToUnicodeString - only difference is that Unicode Length
|
|
is immaterial here. When using this API, never depend on a well formed
|
|
UNICODE_STRING - only rely on the Buffer field.
|
|
|
|
This functions converts the specified ansi source string into a
|
|
Unicode string. The translation is done with respect to the
|
|
current system locale information.
|
|
|
|
Arguments:
|
|
|
|
DestinationString - Returns a unicode string that is equivalent to
|
|
the ansi source string. Should be freed outside using LocalFree()
|
|
|
|
SourceString - Supplies the ansi source string that is to be
|
|
converted to unicode.
|
|
|
|
Return Value:
|
|
|
|
SUCCESS - The conversion was successful
|
|
|
|
!SUCCESS - The operation failed. No storage was allocated and no
|
|
conversion was done. None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG UnicodeLength;
|
|
ULONG Index;
|
|
NTSTATUS st;
|
|
|
|
UnicodeLength = RtlAnsiStringToUnicodeSize(SourceString);
|
|
|
|
DestinationString->Buffer = (PWSTR) LocalAlloc(LMEM_ZEROINIT, UnicodeLength);
|
|
|
|
if ( !DestinationString->Buffer ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
st = RtlMultiByteToUnicodeN(
|
|
DestinationString->Buffer,
|
|
UnicodeLength - sizeof(UNICODE_NULL),
|
|
&Index,
|
|
SourceString->Buffer,
|
|
SourceString->Length
|
|
);
|
|
|
|
if (!NT_SUCCESS(st)) {
|
|
|
|
LocalFree(DestinationString->Buffer);
|
|
|
|
DestinationString->Buffer = NULL;
|
|
|
|
return st;
|
|
}
|
|
|
|
DestinationString->Buffer[Index / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|