Quantcast
Channel: AD – Yet another identity management blog
Viewing all 11 articles
Browse latest View live

Resetting an AD DS password and honouring password history and age using the LDAP_SERVER_POLICY_HINTS control

$
0
0

I recently had to do some frantic experimenting around the area of password reset. I was working with a customer on a convoluted solution that necessitated a password synchronisation operation from the DMZ into a production network without a trust. We had to rule out the use of Password Change Notification Service (PCNS) as there was no way we were going to place the FIM Synchronization Service in the DMZ and we weren’t allowed to use a trust. However, I digress. Why I’m telling you this is to introduce the fact that I wrote a proof-of-concept (PoC) web site and web service that resets passwords across the no-trust-void (in a reasonably secure manner J) and utilises the new LDAP_SERVER_POLICY_HINTS_OID control to allow the password set operation to fully honour password policy. If you didn’t know an [administrative] password set operation bypasses password history and age. Well, to cut a long story short the FIM PG raised a DCR with the AD DS PG to allow this and the result is kb2386717 (a hotfix, which is part of Windows Server 2008 R2 Service Pack 1).

Why I’m waffling on about all this is because a colleague asked me to post the C# sample code that makes use of the new control and it’s indirectly related to the subjects discussed in a previous post.

So, let’s take some code written by far superior developers than I – Joe Kaplan and Ryan Dunn: http://directoryprogramming.net/files/3/csharp/entry24.aspx.

Download Joe and Ryan’s code samples and look at “Listing 10.16 Modified”. This is a class that sets or changes AD user passwords using the System.DirectoryServices.Protocols (S.DS.P) namespace.

OK, bear with me. I learned to program using Java 2 (1.1, 1.2 and 1.3) in 1999 and 2000 and haven’t done much since –I’m a scripter not a programmer. I’ve “overloaded” Joe and Ryan’s code with a new method to allow the original use of the method and permit the new use.

The following code modifies the great work published by Joe and Ryan and illustrates how to utilise the new LDAP extended control:

        /// <summary>
        /// Set password securely resets an LDAP object password using the Unicode Password "operation".
        /// </summary>
        /// <param name="userDN">The distinguished name of the target object, usually a user or inetOrgPerson object.</param>
        /// <param name="password">The string representation of the new password.</param>
        public void SetPassword(String userDN, String password)
        {
            this.SetPassword(userDN, password, false);
        }

        /// <summary>
        /// Set password securely resets an LDAP object password using the Unicode Password "operation".
        /// </summary>
        /// <param name="userDN">The distinguished name of the target object, usually a user or inetOrgPerson object.</param>
        /// <param name="password">The string representation of the new password.</param>
        /// <param name="enforcePasswordHistory">Whether or not to use the Windows Server 2008 R2 SP1 POLICY_HINTS LDAP
        /// control and allow the reset operation to honour password age and history.</param>
        /// <returns>[ResultCode] operation status/indicator.</returns>
        public ResultCode SetPassword(String userDN, String password, Boolean enforcePasswordHistory)
        //public void SetPassword(String userDN, String password, Boolean enforcePasswordHistory)
        {
            DirectoryAttributeModification pwdMod = new DirectoryAttributeModification();
            pwdMod.Name = "unicodePwd";
            pwdMod.Add(GetPasswordData(password));
            pwdMod.Operation = DirectoryAttributeOperation.Replace;

            ModifyRequest request = new ModifyRequest(userDN, pwdMod);

            if (enforcePasswordHistory)
            {
                if (ValidateCapabilities.LdapControlSupported(_connection, AddsSupportedControls.LDAP_SERVER_POLICY_HINTS_OID))
                {
                    byte[] ctrlData = BerConverter.Encode("{i}", new Object[] { 1 });
                    DirectoryControl LDAP_SERVER_POLICY_HINTS_OID = new DirectoryControl(
                            AddsSupportedControls.LDAP_SERVER_POLICY_HINTS_OID, //"1.2.840.113556.1.4.2066"
                            ctrlData,
                            true,
                            true
                        );

                    request.Controls.Add(LDAP_SERVER_POLICY_HINTS_OID);
                }
                else
                {
                    throw new LdapException("The connected directory server does not support the LDAP_SERVER_POLICY_HINTS extended control.  Unable to reset the object's password using this extension.");
                }
            }

            DirectoryResponse response = _connection.SendRequest(request);
            return response.ResultCode;
        }

And that’s it basically. My snippet above has a couple of changes –I return the ResultCode (this was for my web service) and I’ve made use of some simple utility methods I wrote to validate whether or not the capability is supported (1) and a pseudo-enumeration of valid controls (2):

(1) LdapControlSupported

        /// <summary>
        /// Ascertain whether or not the connected directory server supports a given LDAP control.
        /// The supportedControl attribute of the RootDSE object is compared against the input OID.
        /// </summary>
        /// <param name="connection">An LDAP connection.</param>
        /// <param name="control">String representation of the LDAP control OID.</param>
        /// <returns>True if the DS advertises the control.  False if not.</returns>
        public static Boolean LdapControlSupported(LdapConnection connection, String control)
        {
            String supportedControl = "supportedControl";
            SearchRequest dseSupportedControl = LdapSearchHelper.RootDSE(new String[] { supportedControl });

            return LdapSearchHelper.CompareAttributeValue(
                    connection,
                    dseSupportedControl,
                    supportedControl,
                    control
                );
        }

(2) AddsSupportedControls

    /// <summary>
    /// An "enumeration" of LDAP Supported Controls that can be held by an Active Direcory Domain
    /// Services (AD DS) domain controller (DC) or an Active Directory Lightweight Directory Services
    /// (AD LDS) directory server (DS).
    /// </summary>
    public class AddsSupportedControls
    {
        public const String LDAP_PAGED_RESULT_OID_STRING = "1.2.840.113556.1.4.319";
        public const String LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID = "1.2.840.113556.1.4.521";
        public const String LDAP_SERVER_DIRSYNC_OID = "1.2.840.113556.1.4.841";
        public const String LDAP_SERVER_DOMAIN_SCOPE_OID = "1.2.840.113556.1.4.1339";
        public const String LDAP_SERVER_EXTENDED_DN_OID = "1.2.840.113556.1.4.529";
        public const String LDAP_SERVER_GET_STATS_OID = "1.2.840.113556.1.4.970";
        public const String LDAP_SERVER_LAZY_COMMIT_OID = "1.2.840.113556.1.4.619";
        public const String LDAP_SERVER_PERMISSIVE_MODIFY_OID = "1.2.840.113556.1.4.1413";
        public const String LDAP_SERVER_NOTIFICATION_OID = "1.2.840.113556.1.4.528";
        public const String LDAP_SERVER_RESP_SORT_OID = "1.2.840.113556.1.4.474";
        public const String LDAP_SERVER_SD_FLAGS_OID = "1.2.840.113556.1.4.801";
        public const String LDAP_SERVER_SEARCH_OPTIONS_OID = "1.2.840.113556.1.4.1340";
        public const String LDAP_SERVER_SORT_OID = "1.2.840.113556.1.4.473";
        public const String LDAP_SERVER_SHOW_DELETED_OID = "1.2.840.113556.1.4.417";
        public const String LDAP_SERVER_TREE_DELETE_OID = "1.2.840.113556.1.4.805";
        public const String LDAP_SERVER_VERIFY_NAME_OID = "1.2.840.113556.1.4.1338";
        public const String LDAP_CONTROL_VLVREQUEST = "2.16.840.1.113730.3.4.9";
        public const String LDAP_CONTROL_VLVRESPONSE = "2.16.840.1.113730.3.4.10";
        public const String LDAP_SERVER_ASQ_OID = "1.2.840.113556.1.4.1504";
        public const String LDAP_SERVER_QUOTA_CONTROL_OID = "1.2.840.113556.1.4.1852";
        public const String LDAP_SERVER_RANGE_OPTION_OID = "1.2.840.113556.1.4.802";
        public const String LDAP_SERVER_SHUTDOWN_NOTIFY_OID = "1.2.840.113556.1.4.1907";
        public const String LDAP_SERVER_FORCE_UPDATE_OID = "1.2.840.113556.1.4.1974";
        public const String LDAP_SERVER_RANGE_RETRIEVAL_NOERR_OID = "1.2.840.113556.1.4.1948";
        public const String LDAP_SERVER_RODC_DCPROMO_OID = "1.2.840.113556.1.4.1341";
        public const String LDAP_SERVER_INPUT_DN_OID = "1.2.840.113556.1.4.2026";
        public const String LDAP_SERVER_SHOW_DEACTIVATED_LINK_OID = "1.2.840.113556.1.4.2065";
        public const String LDAP_SERVER_SHOW_RECYCLED_OID = "1.2.840.113556.1.4.2064";
        public const String LDAP_SERVER_POLICY_HINTS_OID = "1.2.840.113556.1.4.2066";
    }

(3) CompareAttributeValue (used in (1))

        public static Boolean CompareAttributeValue(LdapConnection connection,
            SearchRequest request, String attributeName, String attributeValue)
        {
            String[] result = LdapSearchHelper.GetSingleAttributeValue(connection, request) as String[];

            if (result != null)
            {
                foreach (String attrVal in result)
                {
                    if (attrVal == attributeValue)
                    {
                        return true;
                    }
                }
            }

            return false;
        }
    }

(4) GetSingleAttributeValue (used in (3))

        public static Object GetSingleAttributeValue(LdapConnection connection, SearchRequest request)
        {
            Object returnValue = null;
            SearchResponse response = LdapSearchHelper.LdapQuery(connection, request);
            if (response.Entries != null)
            {
                String attrName = request.Attributes[0];
                if (response.Entries[0] != null)
                {
                    SearchResultEntry res = response.Entries[0];
                    if (res.Attributes[attrName] != null) //cater for invalid attribute
                    {
                        String[] attrValues = res.Attributes[attrName].GetValues(typeof(String)) as String[];
                        if (attrValues.Length == 1)
                        {
                            returnValue = attrValues[0];
                        }
                        else
                        {
                            returnValue = attrValues;
                        }
                    }
                    else
                    {
                        throw new LdapException("Invalid attribute name specified (or insufficient permissions).");
                    }
                }
            }

            return returnValue;
        }

Wrap-up

The crux of this post, I hope, is this: you can now, via the LDAP_POLICY_HINTS_OID control reset a user password and honour all aspects of password history.  Programatically, the extended control is implemented as follows.  Using this control requires either Windows Server 2008 R2 Service Pack 1 or the kb2386717 hotfix for either Windows Server 2008 or Windows Server 2008 R2.

byte[] ctrlData = BerConverter.Encode("{i}", new Object[] { 1 });
DirectoryControl LDAP_SERVER_POLICY_HINTS_OID = new DirectoryControl(
 AddsSupportedControls.LDAP_SERVER_POLICY_HINTS_OID, //"1.2.840.113556.1.4.2066"
 ctrlData,
 true,
 true
);

request.Controls.Add(LDAP_SERVER_POLICY_HINTS_OID);

Hopefully this helps someone out there. Be warned the error message when you don’t meet complexity isn’t immediately apparent!



exported-change-not-reimported error when provisioning a linked mailbox

$
0
0

When provisioning a linked-mailbox using Forefront Identity Manager (FIM) 2010, Identity Lifecycle Manager (ILM) 2007 or Identity Integration Server (MIIS) 2003 the Active Directory Management Agent (ADMA) throws an exported-change-not-reimported error for each new mailbox-enabled user.

Upon closer inspection you will find that the cause of the error is the nTSecurityDescriptor attribute.  As discussed in my previous post the ExchangeUtils.CreateMailbox methods that are used for creating linked mailboxes (the two overrides that take a byte array) update the nTSecurityDescriptor attribute.  Upon subsequent import the security descriptor has changed (because we’re reading the raw, i.e. un-normalised format by default) which triggers the error.

The resolution to this issue is the FIM Synchronization Service ADMA registry value ADMADoNormalization –documented here.  It would appear that this value was introduced in MIIS 3.1.1057 (kb929622).  The ADMADoNormalization DWORD requires a value of 1.  No service restart is required.  It comes into effect at the next export.  The path to the registry key is:

  • HKLM\SYSTEM\CurrentControlSet\Services\FIMSynchronizationService\Parameters -for the FIM Synchronization Service; and
  • HKLM\SYSTEM\CurrentControlSet\Services\miiserver\Parameters -for ILM and MIIS.

What does normalisation do?  How does it work?

Setting this value to “1” will cause the AD MA to export an object to AD, and then read back the AD normalized ‘nTSecurityDescriptor’ attribute and write it back onto the export image to avoid ‘exported-change-not-reimported’ errors.

The above is a quote from the registry keys and values documentation.  I’ve not found enough extra detail to enable me to add to that description.  Smile

unexpected-error

If, after turning on ADMADoNormalization, you no longer get the exported-change-not-reimported error and instead get an unexpected-error which generates the following event log entry (event ID 6401, source FIMSynchronizationService):

The management agent controller encountered an unexpected error.

"ERR: MMS(2360): session.cpp(5622): ldap_get_values_len (attr=nTSecurityDescriptor) failed
BAIL: MMS(2360): session.cpp(5624): 0x80070057 (The parameter is incorrect.)
BAIL: MMS(2360): LDAPUtils.h(1581): 0x80070057 (The parameter is incorrect.)
BAIL: MMS(2360): admaexport.cpp(1821): 0x80070057 (The parameter is incorrect.)
BAIL: MMS(2360): ldapmaexportcore.cpp(897): 0x80070057 (The parameter is incorrect.)
ERR: MMS(2360): ldapmaexportcore.cpp(1251): Unexpected export failure: local result = 0x80070057
ERR: MMS(2360): cntrler.cpp(7290): Invalid export error code received: 0x  80230808
ERR: MMS(2360): cntrler.cpp(8335): Invalid error code from MA 'CloudDS' while running run profile 'Export'.
Forefront Identity Manager 4.0.3594.2"

The issue is likely that you are not running as an administrative user!  Sad smile

I’m working on a deployment whereby I’m doing my utmost to adhere to least privilege and this one has hit me hard.

By default you can’t read nTSecurityDescriptor.  However you can grant permissions to read the attribute quite easily.  Unfortunately this isn’t enough –nTSecurityDescriptor is a BLOB made up of three discreet elements: owner, DACL and SACL.  If you want to return this attribute in the results of an LDAP query, and you’re not an administrator, you need to specify the LDAP_SERVER_SD_FLAGS_OID control with one or more of the security information flags (OR’d) to describe which aspects of the SD you want, i.e. if you want the Owner and the DACL (probably all we really want from the perspective of the Synchronization Service) then you add OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION as the [BER-encoded] value of the control.

Unfortunately FIM Synchronization Service isn’t utilising this control in any of the builds that I’ve tried: 3558 (when the registry value was introduced), 3576 and 3594.  I’ve bugged this (17/11/2011) and am waiting for more information on when, if at all, a hotfix will be available.

In the meantime I have to turn off ADMADoNormalization and ignore the exported-change-not-reimported errors.

Summary

If you’re provisioning linked mailboxes you need to enable the ADMADoNormalization registry value to avoid the exported-change-not-reimported error on each exported object.

If you’re following recommended practices and the AD MA account is not an administrative user (this is the correct and only approach you should be taking but I digress) then you currently cannot make use of ADMADoNormalization and have to ignore the exported-change-not-reimported errors as the AD MA doesn’t make use of the LDAP_SERVER_SD_FLAGS_OID extended control and is therefore unable to read the security descriptor which will break the confirmation work done by the AD MA and result in you failing to properly create your linked mailbox.


Delegating the minimum set of permissions for user provisioning

$
0
0

The purpose of this post is to provide information on the permissions required by the user account that the Active Directory Domain Services (AD DS) Management Agent (MA) or ADMA uses when it interfaces with an AD domain.  I’ve seen far too many implementations of FIM –both in the various types of lab and in production that grant the ADMA accounts more permissions and privileges than they require.  In this post I’d like to discuss the minimum permissions required to perform certain operations as well as a general approach on how to actually deploy the permissions to an AD domain.

Note. I’m using the DSACLS nomenclature including the inheritance options. From the DSACLS help:

  • T. The object and its child objects.
  • S. The child objects only.
  • P. The object and child objects down to one level only (propagate inheritable permissions to one level only)

We’ll start off by discussing the permissions required to create, delete and move (between containers) users.

Creating, deleting and moving a user

To create a user you require create child permissions on the parent container.  No other permissions are required.  The created user is disabled as it has no password.  To create an enabled user you require the following permissions.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
CC user
WP userAccountControl user S
Reset Password user S

In addition to create child you also need the ability to set the password (the Reset Password control access right is required for this operation) and then change the userAccountControl bitmask such that the user is enabled, e.g. you might set the password and set userAccountControl to 512.

To delete a user you obviously require delete permissions too.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
DC user

If the permissions need to be set such that users can be created in child OUs the following are also required.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
CC user organizationalUnit S
DC user organizationalUnit S

If you want to move a user then you need to add the following permissions in addition to those defined for create and delete.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP cn user S
WP distinguishedName user S
WP name user S

Moving a user is a rename. We write the distinguishedName but the LDAP operation also requires that we change the CN and the name (the RDN, i.e. CN={CN}). The operation also requires delete child on the source and create child on the target.

Note.

The Windows Server 2008 Active Directory Users and Computers (ADUC) snap-in option “Protect object from accidental deletion” writes a deny { delete, delete tree } ACE for the EVERYONE principal that will interfere with the above permissions.  The ACE is explicit, i.e. not inherited, and therefore takes precedence over other permissions.

Additional properties

The preceding example provided the minimum permissions.  Creating a user with nothing more than a DN isn’t really all that helpful.  Additional attributes are generally required.  The sAMAccountName is pretty much mandatory from a provisioning perspective.  It is a mandatory attribute from the perspective of the security accounts manager (SAM) however the directory server will automatically generate one if you don’t provide it (Windows Server 2003 and later).  The user principal name is also a common expectation although it isn’t strictly necessary as users have an implicit UPN (sAMAccountName @ domain DNS name).  The following tables define some additional permissions required for typical account provisioning scenarios.

GAL/ADUC properties

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP c user S
WP co user S
WP company user S
WP countryCode user S
WP department user S
WP displayName user S
WP facsimileTelephoneNumber user S
WP givenName user S
WP initials user S
WP l user S
WP manager user S
WP mobile user S
WP pager user S
WP physicalDeliverOfficeName user S
WP postalCode user S
WP sn user S
WP st user S
WP streetAddress user S
WP telephoneNumber user S
WP title user S

Other attributes

Some additional attributes that are often flowed.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP employeeID user S
WP employeeType user S

Deploying permissions

The permissions need to be deployed on a per-container basis.  For example, assuming the following OU structure (over and above the default containers which are out of scope):

  • OU=employees, OU=people, DC=corp, DC=contoso, DC=com
  • OU=contractors, OU=people, DC=corp, DC=contoso, DC=com
  • OU=employees, OU=people, DC=corp, DC=contoso, DC=com
  • OU=vip,DC=corp, DC=contoso, DC=com
  • OU=deleted-users,DC=corp, DC=contoso, DC=com

The aforementioned permissions need to be applied to each OU.  It doesn’t make sense to apply the permissions at the root of the naming context (DC=corp, DC=contoso, DC=com) as then they’ll apply to all the other containers not listed above.  We don’t want to delegate permissions throughout the domain.  What if there’s also an OU=privileged-accounts,DC=corp, DC=contoso, DC=com and an OU=service-admins,DC=corp, DC=contoso, DC=com?

Applying the consistent set of permissions means that FIM can create and delete users as well as move users between containers and write the necessary properties.  The only way to set granular permissions in a consistent way is via script.  DSACLS should be the tool of choice.

An example script

Up until recently I’ve just used a CMD script –an extension of the script here.  However recently I’ve been trying to write all scripts and commands in PowerShell to make my deployment documents more consistent so here’s an example PS script that can enable the permissions described above for the containers also listed above.

# UserProvisioningPermissions.ps1 v1.0 
#   Paul Williams (pawill@microsoft.com) Microsoft Services Feb. 2010

PARAM
(
    [Parameter(Mandatory = $true)]
    [String]$Target,

    [Parameter(Mandatory = $true)]
    [String]$Trustee
);

Write-Host "`nTarget:  $Target`nTrustee: $Trustee`n";

[String[]]$attributes = @(
    "c",
    "cn",
    "co",
    "company",
    "countryCode",
    "department",
    "displayName",
    "distinguishedName",
    "facsimileTelephoneNumber",
    "givenName",
    "initials",
    "l",
    "manager",
    "mobile",
    "name",
    "pager",
    "physicalDeliverOfficeName",
    "postalCode",
    "sn",
    "st",
    "streetAddress",
    "telephoneNumber",
    "title"
);

Write-Host "Granting the following permissions...";
Write-Host "Create/Delete user objects (this object)";
[String]$cmd = "dsacls '$Target' /G '`"$Trustee`":CCDC;user;'";
Invoke-Expression $cmd |Out-Null;

Write-Host "Create/Delete user objects (child OUs)";
[String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":CCDC;user;organizationalUnit'";
Invoke-Expression $cmd |Out-Null;

Write-Host '"Reset Password" Control Access Right (CAS)';
[String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":CA;`"Reset Password`";user'";
Invoke-Expression $cmd |Out-Null;

foreach($attr in $attributes)
{
    Write-Host "Write Property (WP) $attr on descendent user objects";
    [String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":WP;$attr;user'";
    Invoke-Expression $cmd |Out-Null;
}

Write-Host "`nScript complete.";

Note

The script defines both “this object only” and the “all descendent organizationalUnit objects” inherited permissions for CC and DC, thus when applying the script to OU=People the resultant permissions are such that users can be created in OU=People or any OU underneath OU=People.

And here’s the command(s) that invoke the script.  In this example the script is called UserProvisioningPermissions.ps1 and I’m invoking it from the current directory.

.\UserProvisioningPermissions.ps1 `
    -Target "OU=people, DC=corp, DC=contoso, DC=com" `
    -Trustee "CORP\ADMAUserProvisioningPermissions";

Wrap up

The intention of this post is to provide you with enough information to define granular, precise permissions for the ADMA and not use some all encompassing group.  In addition to defining the minimum permissions for create, delete and move I also provided some additional attributes often used when provisioning and synchronising with an AD DS domain.  I provided an example script to automate the deployment of the permissions and an example of how to use it.

Given the script in the preceding section and the OUs listed in the section before that we would successfully deploy the permissions in this article by running the script three times –once against each of the following OUs.

  • OU=people, DC=corp, DC=contoso, DC=com
  • OU=vip,DC=corp, DC=contoso, DC=com
  • OU=deleted-users,DC=corp, DC=contoso, DC=com

The following is an example of again using PowerShell to make life easier.

[String[]]$containers = @(
    "OU=people, DC=corp, DC=contoso, DC=com",  
    "OU=vip,DC=corp, DC=contoso, DC=com", 
    "OU=deleted-users,DC=corp, DC=contoso, DC=com"
);

foreach($target in $containers)
{
    .\UserProvisioningPermissions.ps1 `
        -Target $target `
        -Trustee "CORP\ADMAUserProvisioningPermissions";
}

Delegating the minimum set of permissions for mailbox-enabled user and linked mailbox provisioning

$
0
0

In my previous post I described the minimum set of permissions required by the ADMA account to provision an AD DS user object.  In this post I’d like to expand on that and provide the minimum set of permissions required to provision a mailbox-enabled user and a linked-mailbox.  As with the previous post I will also cover additional non-essential but expected and required (from an implementation perspective) attributes.

Creating a user

In the previous post I stated that the minimum set of permissions to create an enabled user and delete and move a user are as follows.

Note.  I’m using the DSACLS nomenclature including the inheritance options.  From the DSACLS help:

  • T.  The object and its child objects.
  • S.  The child objects only.
  • P.  The object and child objects down to one level only (propagate inheritable permissions to one level only)
Permission/Access right Object type/ Attribute Inherited object type Inheritance
CC user    
DC user    
CC user organizationalUnit S
DC user organizationalUnit S
Reset Password   user S
WP cn user S
WP distinguishedName user S
WP name user S
WP userAccountControl user S

Those are the core permissions.  You’ll obviously want more permissions than that.  Read the rest of the post for more info.  The purpose of this post is mailbox-enabled users and linked mailboxes.  The next two sections describe the minimum permissions required for creating a mailbox-enabled user and linked mailbox respectively.  Each section is written in isolation, i.e. the linked mailbox section is not dependent on the mailbox-enabled user section, however both sections are dependent on the information written above.

Creating a mailbox-enabled user

To create a mailbox-enabled user we need to write the following additional attributes to the user before we run Update-Recipient and complete the process.

  • homeMDB
  • mailNickname
  • msExchHomeServerName

An optional (from the perspective that the mailbox will created without it) but essential (from the perspective of recommended settings) addition to the list is:

  • mDBUseDefaults

Therefore to grant the permissions to write these attributes we need to add the following ACEs.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP homeMDB user S
WP mailNickname user S
WP mDBUseDefaults user S
WP msExchHomeServerName user S

Next you need permissions to run the Update-Recipient cmdlet.  The current guidance states that you should add the ADMA account to the Recipient Administrators group however that grants too much access therefore you should create a custom role group as I describe in this post.

Note.

The FIM PG will publish revised documentation on minimum permissions and firewall ports and the advice in this post will align with the information in that documentation refresh.

It is highly likely that you will want to synchronise additional attributes. The previous post defines a bunch of common attributes and describes how to implement the permissions for those attributes.

Creating a linked mailbox

A linked mailbox requires provisioning code.  I don’t think we can create a linked mailbox via declarative provisioning but I might be wrong.  But I digress.  To create a linked mailbox you would use either ExchangeUtils::CreateMailbox(ConnectedMA, ReferenceValue, String, String, Byte[]) or ExchangeUtils::CreateMailbox(ConnectedMA, ReferenceValue, String, String, long, long, long, Byte[]) which sets the following attributes (that we have not yet factored into our permissions list):

  • homeMDB
  • mailNickname
  • mDBUseDefaults
  • msExchHomeServerName
  • msExchMailboxSecurityDescriptor
  • msExchMasterAccountSid
  • nTSecurityDescriptor

The following permissions therefore need to be granted.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP homeMDB user S
WP mailNickname user S
WP mDBUseDefaults user S
WP msExchHomeServerName user S
WP msExchMailboxSecurityDescriptor user S
WP msExchMasterAccountSid user S
WP nTSecurityDescriptor user S

Next you need permissions to run the Update-Recipient cmdlet. The current guidance states that you should add the ADMA account to the Recipient Administrators group however that grants too much access therefore you should create a custom role group as I describe in this post.

Note.

The FIM PG will publish revised documentation on minimum permissions and firewall ports and the advice in this post will be included in that documentation refresh.

In addition a type of privilege is also required – a user right: SeRestorePrivilege, a.k.a. Restore files and directories.  This user right must be granted to the ADMA account on all domain controllers.  For more information see this post.

Note.

When you provision a linked mailbox you will receive an exported-change-not-reimported error on the subsequent import.  This is because we need to normalise the SD just like AD does.  The registry value ADMADoNormalization turns this feature on however doing so when implementing least privilege will break the export.  More information in this post.  Until the bug is fixed (I have logged it) I am choosing to not enable ADMADoNormalization and instead ignore the exported-change-not-reimported error.

Wrap up

I’ve discussed the additional permissions required for a mailbox-enabled user and a linked mailbox.  I haven’t discussed GAL attributes as they’re covered in the previous post.  If there are other attributes that you need you can now see how to grant access to those attributes.

This post specifically targets Exchange Server 2010.  The permissions are pretty much the same as Exchange Server 2007.  IIRC e2k7 doesn’t require msExchHomeServerName.  Exchange Server 2003 is a little different but the premise is the same.

The import points I want to get across are this:

  1. Endevour to follow the practice of least privilege in all aspects of FIM.  And with the ADMA don’t put up with administrative access!
  2. The “protect this container from accidental deletion” option in 2008+ tools will break move and delete permissions.
  3. Linked mailboxes require SeRestorePrivilege on domain controllers.

Hopefully this helps someone.


How to use Sort Keys in LDP

$
0
0

I knocked up an example PowerShell one liner for a colleague to get the oldest item in the Deleted Objects container because of a need to identify the default tombstone lifetime (i.e. when there’s no value on the nTDSService object’s tombstoneLifetime attribute) but the environment in question didn’t have Active Directory Web Services (ADWS) thus the PowerShell one liner (I’ve listed it at the end of this post for those who care) wouldn’t work.  In these cases (when ADFIND isn’t available) I fall back to LDP –you can probably tell from this blog that I use LDP a lot.  It took me a good ten minutes to work out how to use the LDAP_SERVER_SORT_OID control (1.2.840.113556.1.4.473) so I thought I’d post how to sort results here.

For the purpose of this example I’ll describe how to list the tombstoned objects in descending order.

  1. Open LDP(Start | Run | LDP), connect and bind to the directory.
  2. Click Options | Controls(Ctrl + L).
  3. Check in the LDAP_SERVER_SHOW_DELETED_OID control by simply selecting “Return deleted objects” from the “Load Predefined” list and click OK.
  4. Click View | Tree (Ctrl + T) and press enter(leaving the Base DN empty).
  5. Expand the domain NC and right-click on CN=Deleted Objects, <your DN goes here> and click Search.
  6. Set the Scope to One Level.  Change the attributes to objectClass;name;whenChanged and click Options.
  7. Select Extended for the Search Call Type and click Sort Keys.
  8. Enter whenChanged for the Attribute Type, leave Match Rule OID empty, click Reverse Order and then Check In >>
  9. Click OK to close the Search Options dialog and click Run to execute the search.

The number of results is based on the values in Search Options.  By default it’s pretty low so you just need to look at the top most result to see when that object was deleted and then gauge the tombstone lifetime.

It’s pretty simple really.  Server-side sorting is pretty limited as a whole – you can only sort ascending or descending on one attribute and only a subset of attribute types are allowed; and also expensive.  In almost all cases you’ll want to order the data at the client side (like I do in PowerShell below) but from time to time the server-side sorting feature comes in handy.

Anyway, here’s the PowerShell that uses the ActiveDirectory module:

[String]$aDPSModuleName = "ActiveDirectory";
if(@(Get-Module -Name $aDPSModuleName).Count -eq 0)
{
    if(@(Get-Module -ListAvailable | ? { $_.Name -eq $aDPSModuleName }).Count -eq 1)
    {
        Import-Module -Name $aDPSModuleName;
    }
}

[String]$delObjFilter = 'objectClass -like "*"';
[String]$delObjearchBase = "CN=Deleted Objects,DC=corp,DC=contoso,DC=com";
[String[]]$delObjPropertiesToFetch = @( "whenChanged", "lastKnownParent" );

Get-ADObject `
    -Filter $delObjFilter `
    -IncludedelObj `
    -SearchBase $delObjearchBase `
    -SearchScope OneLevel `
    -Properties $delObjPropertiesToFetch |
        Sort-Object -Property whenChanged |
            Select-Object -Last 3 | Format-Table whenChanged;

When I said one line I slightly exaggerated.  There’s an if statement to load the module if it isn’t already and is available and I make use of some attributes to make the command easier to read.  Smile


How can I logon to my ADAM or AD LDS Management Agent (MA)?

$
0
0

What credentials can I use for the Active Directory Application Mode (ADAM) or Active Directory Lightweight Directory Services (AD LDS) Management Agent (MA) in Forefront Identity Manager (FIM) 2010 or R2?

Bit basic this post but I had to install FIM Synchronization Service and an AD LDS instance during a meeting to get this answer so figured I might as well post the information here for posterity.  Smile

Binding to ADAM/AD LDS

For the rest of this post I’m going to use the term ADAM however everything I say applies to AD LDS (in fact my testing was using AD LDS on Windows Server 2008 R2).  And when I say bind I mean logon. 

OK, with that out of the way.  You can bind to ADAM using an ADAM principal (some user-defined object type in the ADAM instance that “implements” the msDS-BindableObject class, i.e. an ADAM user); a Windows principal (a local user object on the Windows computer running ADAM, i.e. a user in the Local Security Authority or LSA, or in Active Directory Domain Services or AD DS, e.g. a user or inetOrgPerson object); a user proxy (a special type of user object in the ADAM instance that is linked, via objectSid, to an AD DS user); and lastly anonymous binds.

ADAM only authenticates “ADAM principals”.  “Windows Principals” and user proxy objects are authenticated by Windows, i.e. the local LSA or AD DS.

ADAM supports the following credential formats:

  • Windows principals:
    • User Principal Name (UPN).  Domain-based Windows Principals only, e.g. paulw@msresource.net.
    • Account Name (sAMAccountName).  The account name, e.g. computer\paulw or domain\paulw, e.g. msresource\paulw.
    • Distinguished Name (DN).  Domain-based Windows Principals only, e.g. CN=Paul Williams,OU=People,DC=msresource,DC=net.
  • ADAM principals:
    • Display Name.
    • Distinguished Name.
    • UPN.

There’s some obvious caveats for ADAM principals.  UPN or displayName must be unique across all objects (irrespective of type, so if you have OU=paulw,O=msresource,c=GB you cannot logon with a displayName or UPN of paulw).

I heard you can logon with canonical name too, but limited testing did not prove that theory.

Specifying credentials for the ADAM MA

With enough background on the subject of binding to ADAM let’s look at what we can do with the ADAM MA.

By default, the MA is configured to support a Windows Principal, i.e. a Simple Authentication and Security Layer (SASL) authentication – this means SPNEGO a.k.a. Integrated Windows Authentication (IWA).

This means, by default, you are limited to NETBIOS_DOMAIN_NAME\username (or COMPUTER_NAME\username) and username@some-domain-name.tld.  Although when I say DOMAIN\username you don’t actually write it like that – there’s an input box for username, password and domain.  When you introduce an @ into the username the domain box is no longer required although not greyed out.

Fine.  Good.

Now, stick a DN in and you’ll get an error:

image

That picture says:

The user name cannot contain any of the following characters,

‘<’, ‘>’, ‘&’, ‘”’, ‘/’, ‘\’, ‘[‘, ‘]’, ‘:’, ‘;’, ‘|’, ‘=’, ‘,’, ‘+’, ‘*’, ‘?’

Try a basic username and password (no domain, e.g. displayname and password) and you’ll get a validation error:

image

That picture says:

Please complete the credentials.

The reason for this is because we’re in SPNEGO mode.  We need to change to SIMPLE BIND to unlock the ability to specify a DN or a username and password (could be UPN or displayName) with no DOMAIN/COMPUTER requirement.  That’s done via the options button (adjacent to “Configure Connection Security”):

clip_image002

So the default is to not only bind using SASL but use SASL to sign and seal (encrypt) all communications thereafter.  When you “Enable Simple Bind” you can choose to use SSL with or without Certificate Revocation List (CRL) Checking or (dev lab only please people) no SSL, i.e. clear text credentials.

After turning on simple bind you can use a DN to bind (as many LDAP folk expect):

clip_image002[7]

Hopefully someone will find this helpful.  Smile


exported-change-not-reimported error when provisioning a linked mailbox

$
0
0

When provisioning a linked-mailbox using Forefront Identity Manager (FIM) 2010, Identity Lifecycle Manager (ILM) 2007 or Identity Integration Server (MIIS) 2003 the Active Directory Management Agent (ADMA) throws an exported-change-not-reimported error for each new mailbox-enabled user.

Upon closer inspection you will find that the cause of the error is the nTSecurityDescriptor attribute.  As discussed in my previous post the ExchangeUtils.CreateMailbox methods that are used for creating linked mailboxes (the two overrides that take a byte array) update the nTSecurityDescriptor attribute.  Upon subsequent import the security descriptor has changed (because we’re reading the raw, i.e. un-normalised format by default) which triggers the error.

The resolution to this issue is the FIM Synchronization Service ADMA registry value ADMADoNormalization –documented here.  It would appear that this value was introduced in MIIS 3.1.1057 (kb929622).  The ADMADoNormalization DWORD requires a value of 1.  No service restart is required.  It comes into effect at the next export.  The path to the registry key is:

  • HKLM\SYSTEM\CurrentControlSet\Services\FIMSynchronizationService\Parameters -for the FIM Synchronization Service; and
  • HKLM\SYSTEM\CurrentControlSet\Services\miiserver\Parameters -for ILM and MIIS.

What does normalisation do?  How does it work?

Setting this value to “1” will cause the AD MA to export an object to AD, and then read back the AD normalized ‘nTSecurityDescriptor’ attribute and write it back onto the export image to avoid ‘exported-change-not-reimported’ errors.

The above is a quote from the registry keys and values documentation.  I’ve not found enough extra detail to enable me to add to that description.  Smile

unexpected-error

If, after turning on ADMADoNormalization, you no longer get the exported-change-not-reimported error and instead get an unexpected-error which generates the following event log entry (event ID 6401, source FIMSynchronizationService):

The management agent controller encountered an unexpected error.

"ERR: MMS(2360): session.cpp(5622): ldap_get_values_len (attr=nTSecurityDescriptor) failed
BAIL: MMS(2360): session.cpp(5624): 0x80070057 (The parameter is incorrect.)
BAIL: MMS(2360): LDAPUtils.h(1581): 0x80070057 (The parameter is incorrect.)
BAIL: MMS(2360): admaexport.cpp(1821): 0x80070057 (The parameter is incorrect.)
BAIL: MMS(2360): ldapmaexportcore.cpp(897): 0x80070057 (The parameter is incorrect.)
ERR: MMS(2360): ldapmaexportcore.cpp(1251): Unexpected export failure: local result = 0x80070057
ERR: MMS(2360): cntrler.cpp(7290): Invalid export error code received: 0x  80230808
ERR: MMS(2360): cntrler.cpp(8335): Invalid error code from MA 'CloudDS' while running run profile 'Export'.
Forefront Identity Manager 4.0.3594.2"

The issue is likely that you are not running as an administrative user!  Sad smile

I’m working on a deployment whereby I’m doing my utmost to adhere to least privilege and this one has hit me hard.

By default you can’t read nTSecurityDescriptor.  However you can grant permissions to read the attribute quite easily.  Unfortunately this isn’t enough –nTSecurityDescriptor is a BLOB made up of three discreet elements: owner, DACL and SACL.  If you want to return this attribute in the results of an LDAP query, and you’re not an administrator, you need to specify the LDAP_SERVER_SD_FLAGS_OID control with one or more of the security information flags (OR’d) to describe which aspects of the SD you want, i.e. if you want the Owner and the DACL (probably all we really want from the perspective of the Synchronization Service) then you add OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION as the [BER-encoded] value of the control.

Unfortunately FIM Synchronization Service isn’t utilising this control in any of the builds that I’ve tried: 3558 (when the registry value was introduced), 3576 and 3594.  I’ve bugged this (17/11/2011) and am waiting for more information on when, if at all, a hotfix will be available.

In the meantime I have to turn off ADMADoNormalization and ignore the exported-change-not-reimported errors.

Summary

If you’re provisioning linked mailboxes you need to enable the ADMADoNormalization registry value to avoid the exported-change-not-reimported error on each exported object.

If you’re following recommended practices and the AD MA account is not an administrative user (this is the correct and only approach you should be taking but I digress) then you currently cannot make use of ADMADoNormalization and have to ignore the exported-change-not-reimported errors as the AD MA doesn’t make use of the LDAP_SERVER_SD_FLAGS_OID extended control and is therefore unable to read the security descriptor which will break the confirmation work done by the AD MA and result in you failing to properly create your linked mailbox.


Delegating the minimum set of permissions for user provisioning

$
0
0

The purpose of this post is to provide information on the permissions required by the user account that the Active Directory Domain Services (AD DS) Management Agent (MA) or ADMA uses when it interfaces with an AD domain.  I’ve seen far too many implementations of FIM –both in the various types of lab and in production that grant the ADMA accounts more permissions and privileges than they require.  In this post I’d like to discuss the minimum permissions required to perform certain operations as well as a general approach on how to actually deploy the permissions to an AD domain.

Note. I’m using the DSACLS nomenclature including the inheritance options. From the DSACLS help:

  • T. The object and its child objects.
  • S. The child objects only.
  • P. The object and child objects down to one level only (propagate inheritable permissions to one level only)

We’ll start off by discussing the permissions required to create, delete and move (between containers) users.

Creating, deleting and moving a user

To create a user you require create child permissions on the parent container.  No other permissions are required.  The created user is disabled as it has no password.  To create an enabled user you require the following permissions.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
CC user
WP userAccountControl user S
Reset Password user S

In addition to create child you also need the ability to set the password (the Reset Password control access right is required for this operation) and then change the userAccountControl bitmask such that the user is enabled, e.g. you might set the password and set userAccountControl to 512.

To delete a user you obviously require delete permissions too.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
DC user

If the permissions need to be set such that users can be created in child OUs the following are also required.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
CC user organizationalUnit S
DC user organizationalUnit S

If you want to move a user then you need to add the following permissions in addition to those defined for create and delete.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP cn user S
WP distinguishedName user S
WP name user S

Moving a user is a rename. We write the distinguishedName but the LDAP operation also requires that we change the CN and the name (the RDN, i.e. CN={CN}). The operation also requires delete child on the source and create child on the target.

Note.

The Windows Server 2008 Active Directory Users and Computers (ADUC) snap-in option “Protect object from accidental deletion” writes a deny { delete, delete tree } ACE for the EVERYONE principal that will interfere with the above permissions.  The ACE is explicit, i.e. not inherited, and therefore takes precedence over other permissions.

Additional properties

The preceding example provided the minimum permissions.  Creating a user with nothing more than a DN isn’t really all that helpful.  Additional attributes are generally required.  The sAMAccountName is pretty much mandatory from a provisioning perspective.  It is a mandatory attribute from the perspective of the security accounts manager (SAM) however the directory server will automatically generate one if you don’t provide it (Windows Server 2003 and later).  The user principal name is also a common expectation although it isn’t strictly necessary as users have an implicit UPN (sAMAccountName @ domain DNS name).  The following tables define some additional permissions required for typical account provisioning scenarios.

GAL/ADUC properties

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP c user S
WP co user S
WP company user S
WP countryCode user S
WP department user S
WP displayName user S
WP facsimileTelephoneNumber user S
WP givenName user S
WP initials user S
WP l user S
WP manager user S
WP mobile user S
WP pager user S
WP physicalDeliverOfficeName user S
WP postalCode user S
WP sn user S
WP st user S
WP streetAddress user S
WP telephoneNumber user S
WP title user S

Other attributes

Some additional attributes that are often flowed.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP employeeID user S
WP employeeType user S

Deploying permissions

The permissions need to be deployed on a per-container basis.  For example, assuming the following OU structure (over and above the default containers which are out of scope):

  • OU=employees, OU=people, DC=corp, DC=contoso, DC=com
  • OU=contractors, OU=people, DC=corp, DC=contoso, DC=com
  • OU=employees, OU=people, DC=corp, DC=contoso, DC=com
  • OU=vip,DC=corp, DC=contoso, DC=com
  • OU=deleted-users,DC=corp, DC=contoso, DC=com

The aforementioned permissions need to be applied to each OU.  It doesn’t make sense to apply the permissions at the root of the naming context (DC=corp, DC=contoso, DC=com) as then they’ll apply to all the other containers not listed above.  We don’t want to delegate permissions throughout the domain.  What if there’s also an OU=privileged-accounts,DC=corp, DC=contoso, DC=com and an OU=service-admins,DC=corp, DC=contoso, DC=com?

Applying the consistent set of permissions means that FIM can create and delete users as well as move users between containers and write the necessary properties.  The only way to set granular permissions in a consistent way is via script.  DSACLS should be the tool of choice.

An example script

Up until recently I’ve just used a CMD script –an extension of the script here.  However recently I’ve been trying to write all scripts and commands in PowerShell to make my deployment documents more consistent so here’s an example PS script that can enable the permissions described above for the containers also listed above.

# UserProvisioningPermissions.ps1 v1.0
#   Paul Williams (pawill@microsoft.com) Microsoft Services Feb. 2010

PARAM
(
    [Parameter(Mandatory = $true)]
    [String]$Target,

    [Parameter(Mandatory = $true)]
    [String]$Trustee
);

Write-Host "`nTarget:  $Target`nTrustee: $Trustee`n";

[String[]]$attributes = @(
    "c",
    "cn",
    "co",
    "company",
    "countryCode",
    "department",
    "displayName",
    "distinguishedName",
    "facsimileTelephoneNumber",
    "givenName",
    "initials",
    "l",
    "manager",
    "mobile",
    "name",
    "pager",
    "physicalDeliverOfficeName",
    "postalCode",
    "sn",
    "st",
    "streetAddress",
    "telephoneNumber",
    "title"
);

Write-Host "Granting the following permissions...";
Write-Host "Create/Delete user objects (this object)";
[String]$cmd = "dsacls '$Target' /G '`"$Trustee`":CCDC;user;'";
Invoke-Expression $cmd |Out-Null;

Write-Host "Create/Delete user objects (child OUs)";
[String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":CCDC;user;organizationalUnit'";
Invoke-Expression $cmd |Out-Null;

Write-Host '"Reset Password" Control Access Right (CAS)';
[String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":CA;`"Reset Password`";user'";
Invoke-Expression $cmd |Out-Null;

foreach($attr in $attributes)
{
    Write-Host "Write Property (WP) $attr on descendent user objects";
    [String]$cmd = "dsacls '$Target' /I:S /G '`"$Trustee`":WP;$attr;user'";
    Invoke-Expression $cmd |Out-Null;
}

Write-Host "`nScript complete.";

Note

The script defines both “this object only” and the “all descendent organizationalUnit objects” inherited permissions for CC and DC, thus when applying the script to OU=People the resultant permissions are such that users can be created in OU=People or any OU underneath OU=People.

And here’s the command(s) that invoke the script.  In this example the script is called UserProvisioningPermissions.ps1 and I’m invoking it from the current directory.

.\UserProvisioningPermissions.ps1 `
    -Target "OU=people, DC=corp, DC=contoso, DC=com" `
    -Trustee "CORP\ADMAUserProvisioningPermissions";

Wrap up

The intention of this post is to provide you with enough information to define granular, precise permissions for the ADMA and not use some all encompassing group.  In addition to defining the minimum permissions for create, delete and move I also provided some additional attributes often used when provisioning and synchronising with an AD DS domain.  I provided an example script to automate the deployment of the permissions and an example of how to use it.

Given the script in the preceding section and the OUs listed in the section before that we would successfully deploy the permissions in this article by running the script three times –once against each of the following OUs.

  • OU=people, DC=corp, DC=contoso, DC=com
  • OU=vip,DC=corp, DC=contoso, DC=com
  • OU=deleted-users,DC=corp, DC=contoso, DC=com

The following is an example of again using PowerShell to make life easier.

[String[]]$containers = @(
    "OU=people, DC=corp, DC=contoso, DC=com",
    "OU=vip,DC=corp, DC=contoso, DC=com",
    "OU=deleted-users,DC=corp, DC=contoso, DC=com"
);

foreach($target in $containers)
{
    .\UserProvisioningPermissions.ps1 `
        -Target $target `
        -Trustee "CORP\ADMAUserProvisioningPermissions";
}


Delegating the minimum set of permissions for mailbox-enabled user and linked mailbox provisioning

$
0
0

In my previous post I described the minimum set of permissions required by the ADMA account to provision an AD DS user object.  In this post I’d like to expand on that and provide the minimum set of permissions required to provision a mailbox-enabled user and a linked-mailbox.  As with the previous post I will also cover additional non-essential but expected and required (from an implementation perspective) attributes.

Creating a user

In the previous post I stated that the minimum set of permissions to create an enabled user and delete and move a user are as follows.

Note.  I’m using the DSACLS nomenclature including the inheritance options.  From the DSACLS help:

  • T.  The object and its child objects.
  • S.  The child objects only.
  • P.  The object and child objects down to one level only (propagate inheritable permissions to one level only)
Permission/Access right Object type/ Attribute Inherited object type Inheritance
CC user    
DC user    
CC user organizationalUnit S
DC user organizationalUnit S
Reset Password   user S
WP cn user S
WP distinguishedName user S
WP name user S
WP userAccountControl user S

Those are the core permissions.  You’ll obviously want more permissions than that.  Read the rest of the post for more info.  The purpose of this post is mailbox-enabled users and linked mailboxes.  The next two sections describe the minimum permissions required for creating a mailbox-enabled user and linked mailbox respectively.  Each section is written in isolation, i.e. the linked mailbox section is not dependent on the mailbox-enabled user section, however both sections are dependent on the information written above.

Creating a mailbox-enabled user

To create a mailbox-enabled user we need to write the following additional attributes to the user before we run Update-Recipient and complete the process.

  • homeMDB
  • mailNickname
  • msExchHomeServerName

An optional (from the perspective that the mailbox will created without it) but essential (from the perspective of recommended settings) addition to the list is:

  • mDBUseDefaults

Therefore to grant the permissions to write these attributes we need to add the following ACEs.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP homeMDB user S
WP mailNickname user S
WP mDBUseDefaults user S
WP msExchHomeServerName user S

Next you need permissions to run the Update-Recipient cmdlet.  The current guidance states that you should add the ADMA account to the Recipient Administrators group however that grants too much access therefore you should create a custom role group as I describe in this post.

Note.

The FIM PG will publish revised documentation on minimum permissions and firewall ports and the advice in this post will align with the information in that documentation refresh.

It is highly likely that you will want to synchronise additional attributes. The previous post defines a bunch of common attributes and describes how to implement the permissions for those attributes.

Creating a linked mailbox

A linked mailbox requires provisioning code.  I don’t think we can create a linked mailbox via declarative provisioning but I might be wrong.  But I digress.  To create a linked mailbox you would use either ExchangeUtils::CreateMailbox(ConnectedMA, ReferenceValue, String, String, Byte[]) or ExchangeUtils::CreateMailbox(ConnectedMA, ReferenceValue, String, String, long, long, long, Byte[]) which sets the following attributes (that we have not yet factored into our permissions list):

  • homeMDB
  • mailNickname
  • mDBUseDefaults
  • msExchHomeServerName
  • msExchMailboxSecurityDescriptor
  • msExchMasterAccountSid
  • nTSecurityDescriptor

The following permissions therefore need to be granted.

Permission/ Access Right Object type/ Attribute Inherited object type Inheritance
WP homeMDB user S
WP mailNickname user S
WP mDBUseDefaults user S
WP msExchHomeServerName user S
WP msExchMailboxSecurityDescriptor user S
WP msExchMasterAccountSid user S
WP nTSecurityDescriptor user S

Next you need permissions to run the Update-Recipient cmdlet. The current guidance states that you should add the ADMA account to the Recipient Administrators group however that grants too much access therefore you should create a custom role group as I describe in this post.

Note.

The FIM PG will publish revised documentation on minimum permissions and firewall ports and the advice in this post will be included in that documentation refresh.

In addition a type of privilege is also required – a user right: SeRestorePrivilege, a.k.a. Restore files and directories.  This user right must be granted to the ADMA account on all domain controllers.  For more information see this post.

Note.

When you provision a linked mailbox you will receive an exported-change-not-reimported error on the subsequent import.  This is because we need to normalise the SD just like AD does.  The registry value ADMADoNormalization turns this feature on however doing so when implementing least privilege will break the export.  More information in this post.  Until the bug is fixed (I have logged it) I am choosing to not enable ADMADoNormalization and instead ignore the exported-change-not-reimported error.

Wrap up

I’ve discussed the additional permissions required for a mailbox-enabled user and a linked mailbox.  I haven’t discussed GAL attributes as they’re covered in the previous post.  If there are other attributes that you need you can now see how to grant access to those attributes.

This post specifically targets Exchange Server 2010.  The permissions are pretty much the same as Exchange Server 2007.  IIRC e2k7 doesn’t require msExchHomeServerName.  Exchange Server 2003 is a little different but the premise is the same.

The import points I want to get across are this:

  1. Endevour to follow the practice of least privilege in all aspects of FIM.  And with the ADMA don’t put up with administrative access!
  2. The “protect this container from accidental deletion” option in 2008+ tools will break move and delete permissions.
  3. Linked mailboxes require SeRestorePrivilege on domain controllers.

Hopefully this helps someone.


How to use Sort Keys in LDP

$
0
0

I knocked up an example PowerShell one liner for a colleague to get the oldest item in the Deleted Objects container because of a need to identify the default tombstone lifetime (i.e. when there’s no value on the nTDSService object’s tombstoneLifetime attribute) but the environment in question didn’t have Active Directory Web Services (ADWS) thus the PowerShell one liner (I’ve listed it at the end of this post for those who care) wouldn’t work.  In these cases (when ADFIND isn’t available) I fall back to LDP –you can probably tell from this blog that I use LDP a lot.  It took me a good ten minutes to work out how to use the LDAP_SERVER_SORT_OID control (1.2.840.113556.1.4.473) so I thought I’d post how to sort results here.

For the purpose of this example I’ll describe how to list the tombstoned objects in descending order.

  1. Open LDP(Start | Run | LDP), connect and bind to the directory.
  2. Click Options | Controls(Ctrl + L).
  3. Check in the LDAP_SERVER_SHOW_DELETED_OID control by simply selecting “Return deleted objects” from the “Load Predefined” list and click OK.
  4. Click View | Tree (Ctrl + T) and press enter(leaving the Base DN empty).
  5. Expand the domain NC and right-click on CN=Deleted Objects, <your DN goes here> and click Search.
  6. Set the Scope to One Level.  Change the attributes to objectClass;name;whenChanged and click Options.
  7. Select Extended for the Search Call Type and click Sort Keys.
  8. Enter whenChanged for the Attribute Type, leave Match Rule OID empty, click Reverse Order and then Check In >>
  9. Click OK to close the Search Options dialog and click Run to execute the search.

The number of results is based on the values in Search Options.  By default it’s pretty low so you just need to look at the top most result to see when that object was deleted and then gauge the tombstone lifetime.

It’s pretty simple really.  Server-side sorting is pretty limited as a whole – you can only sort ascending or descending on one attribute and only a subset of attribute types are allowed; and also expensive.  In almost all cases you’ll want to order the data at the client side (like I do in PowerShell below) but from time to time the server-side sorting feature comes in handy.

Anyway, here’s the PowerShell that uses the ActiveDirectory module:

[String]$aDPSModuleName = "ActiveDirectory";
if(@(Get-Module -Name $aDPSModuleName).Count -eq 0)
{
    if(@(Get-Module -ListAvailable | ? { $_.Name -eq $aDPSModuleName }).Count -eq 1)
    {
        Import-Module -Name $aDPSModuleName;
    }
}

[String]$delObjFilter = 'objectClass -like "*"';
[String]$delObjearchBase = "CN=Deleted Objects,DC=corp,DC=contoso,DC=com";
[String[]]$delObjPropertiesToFetch = @( "whenChanged", "lastKnownParent" );

Get-ADObject `
    -Filter $delObjFilter `
    -IncludedelObj `
    -SearchBase $delObjearchBase `
    -SearchScope OneLevel `
    -Properties $delObjPropertiesToFetch |
        Sort-Object -Property whenChanged |
            Select-Object -Last 3 | Format-Table whenChanged;

When I said one line I slightly exaggerated.  There’s an if statement to load the module if it isn’t already and is available and I make use of some attributes to make the command easier to read.  Smile


How can I logon to my ADAM or AD LDS Management Agent (MA)?

$
0
0

What credentials can I use for the Active Directory Application Mode (ADAM) or Active Directory Lightweight Directory Services (AD LDS) Management Agent (MA) in Forefront Identity Manager (FIM) 2010 or R2?

Bit basic this post but I had to install FIM Synchronization Service and an AD LDS instance during a meeting to get this answer so figured I might as well post the information here for posterity.  Smile

Binding to ADAM/AD LDS

For the rest of this post I’m going to use the term ADAM however everything I say applies to AD LDS (in fact my testing was using AD LDS on Windows Server 2008 R2).  And when I say bind I mean logon. 

OK, with that out of the way.  You can bind to ADAM using an ADAM principal (some user-defined object type in the ADAM instance that “implements” the msDS-BindableObject class, i.e. an ADAM user); a Windows principal (a local user object on the Windows computer running ADAM, i.e. a user in the Local Security Authority or LSA, or in Active Directory Domain Services or AD DS, e.g. a user or inetOrgPerson object); a user proxy (a special type of user object in the ADAM instance that is linked, via objectSid, to an AD DS user); and lastly anonymous binds.

ADAM only authenticates “ADAM principals”.  “Windows Principals” and user proxy objects are authenticated by Windows, i.e. the local LSA or AD DS.

ADAM supports the following credential formats:

  • Windows principals:
    • User Principal Name (UPN).  Domain-based Windows Principals only, e.g. paulw@msresource.net.
    • Account Name (sAMAccountName).  The account name, e.g. computer\paulw or domain\paulw, e.g. msresource\paulw.
    • Distinguished Name (DN).  Domain-based Windows Principals only, e.g. CN=Paul Williams,OU=People,DC=msresource,DC=net.
  • ADAM principals:
    • Display Name.
    • Distinguished Name.
    • UPN.

There’s some obvious caveats for ADAM principals.  UPN or displayName must be unique across all objects (irrespective of type, so if you have OU=paulw,O=msresource,c=GB you cannot logon with a displayName or UPN of paulw).

I heard you can logon with canonical name too, but limited testing did not prove that theory.

Specifying credentials for the ADAM MA

With enough background on the subject of binding to ADAM let’s look at what we can do with the ADAM MA.

By default, the MA is configured to support a Windows Principal, i.e. a Simple Authentication and Security Layer (SASL) authentication – this means SPNEGO a.k.a. Integrated Windows Authentication (IWA).

This means, by default, you are limited to NETBIOS_DOMAIN_NAME\username (or COMPUTER_NAME\username) and username@some-domain-name.tld.  Although when I say DOMAIN\username you don’t actually write it like that – there’s an input box for username, password and domain.  When you introduce an @ into the username the domain box is no longer required although not greyed out.

Fine.  Good.

Now, stick a DN in and you’ll get an error:

image

That picture says:

The user name cannot contain any of the following characters,

‘<’, ‘>’, ‘&’, ‘”’, ‘/’, ‘\’, ‘[‘, ‘]’, ‘:’, ‘;’, ‘|’, ‘=’, ‘,’, ‘+’, ‘*’, ‘?’

Try a basic username and password (no domain, e.g. displayname and password) and you’ll get a validation error:

image

That picture says:

Please complete the credentials.

The reason for this is because we’re in SPNEGO mode.  We need to change to SIMPLE BIND to unlock the ability to specify a DN or a username and password (could be UPN or displayName) with no DOMAIN/COMPUTER requirement.  That’s done via the options button (adjacent to “Configure Connection Security”):

clip_image002

So the default is to not only bind using SASL but use SASL to sign and seal (encrypt) all communications thereafter.  When you “Enable Simple Bind” you can choose to use SSL with or without Certificate Revocation List (CRL) Checking or (dev lab only please people) no SSL, i.e. clear text credentials.

After turning on simple bind you can use a DN to bind (as many LDAP folk expect):

clip_image002[7]

Hopefully someone will find this helpful.  Smile


Viewing all 11 articles
Browse latest View live


Latest Images