InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

How to grant access to all mailboxes on a mailbox store to a special account.

Description

To grant an account access to all mailboxes on a mailbox store, the easiest way it to grant the "Receive as" and "Send as" permissions on the mailbox store. To do this, navigate to the mailbox store in the Exchange Systems manager:


The mailbox store in the Exchange Systems Manager

Next, select "Properties" on the context menu of the mailbox store and select the "Security tab".

If you want to grant the access to all mailboxes to a non-administrative account, you can simply add that account to the list and grant the "Send As" and "Receive As" permission.

To grant those permissions to an administrative account, you must perform a few additional steps, since members of the Domain Administrators have a deny on these permission


The security properties of the mailbox store

This denial is placed at the organizational level. This can be examined with ADSIEdit (adsiedit.msc):


ADSI edit displaying the security settings for the Exchange Organization

You can see, that the Domain Administrators have both, an allow as well as a deny permission. This is propagated down to the mailbox store, and prevents administrative accounts from accessing other users mailboxes. Now, you can remove these deny permission, but this is not advisable - they are there for a reason. You would end up with far too many people having these rights.

A better approach is to give only one specific account these rights. If this account is also in the Domain Administrators group and already displayed in the security tab, click "Advanced" and then "Add". Select the account the grant the "Receive As" and "Send as" permissions.

The account will be granted the permissions, because an explicit allow on a lower level overrides an inherited deny.


Posted by Henning Krause on Thursday, April 13, 2006 12:00 AM, last modified on Thursday, April 13, 2006 12:00 PM
Permalink | Post RSSRSS comment feed

Find an user in a multi-domain Active Directory enironment programmatically

More Information

An often given solution to the problem outlined above is something like this (C#):

    1 public void FindUser(string name)

    2 {

    3     DirectoryEntry gc;

    4     DirectoryEntry searchRoot;

    5     DirectorySearcher searcher;

    6     SearchResultCollection result;

    7 

    8     // Get the directoryentry of the Global Catalog root

    9     gc = new DirectoryEntry("GC:", username, password, AuthenticationTypes.Secure);

   10 

   11     // This node has exactly one child, which can be used to search the entire forest

   12     foreach (DirectoryEntry child in gc.Children)

   13     {

   14         searchRoot = child;

   15     }

   16 

   17     // Search the forest for the user object

   18     searcher = new DirectorySearcher(searchRoot,

   19     string.Format("(&(objectCategory=person)​(objectClass=user)​(sAMAccountName={0}))", name),

   20     new string[] { "distinguishedName" }, SearchScope.Subtree);

   21 

   22     result = searcher.FindAll;

   23 }

Now, the result variable contains all user-accounts with the given account name. But in a multi-domain environment, several users may have the same account name.
So, one must iterate through the list to determine the domain of each object. Unfortunately, that information is not stored within the user object.
Another drawback is, that the above solution can only be used from a computer which is logged on to a domain within the forest. From outside the forest, this will not work.
The solution in the next paragraph shows how to work around these issues.

Solution

To find an user-account object based on the domain\username information, follow the following steps:

  1. Get the directory entry for RootDse (LDAP://RootDse or GC://RootDse).
  2. From that entry, retrieve distinguished name for the configuration naming context (Property configurationNamingContext).
  3. Get the directory entry CN=<domain name>, CN=Partitions, <distinguished name of configuration naming context>. Replace the <domain name> with the domain part of the username, and the <distinguished name of configuration naming context> with the distinguished name of the configuration naming context found above. If no such directory entry exists, the given domain was invalid.
  4. From that entry, get the naming context of the domain (Property nCName).
  5. Now that you have the distinguished name of the domain where the desired user account is located, you can do a search on that domain (depending on your environment and the required information from the user object, it may or may not be a good idea to use the global catalog).
    To find the user object in domain specified above, do a search with this filter: (&(objectCategory=person)(objectClass=user)(sAMAccountName=<username>)) (Replace the <username> with the name of the account you are searching for).
    This search will either return one found directory entry, or nothing if no user with the given account name exists.

C# Example

Below is a sample in C#:

Note: This code assumes you have a DirectoryEntry pointing to the Configuration-Naming Context

    1 /// <summary>

    2 /// Searches the forrest for the directory entry of the given user.

    3 /// </summary>

    4 /// <param name="principalname">Name of the user to find. Must be in the form domain</param>

    5 /// <param name="useGC">Specifies whether to use the Global Catalog to find the user. If false, a standard LDAP-Query is used.</param>

    6 /// <returns>The directoryentry of the user, if it is found. Null otherwise.</returns>

    7 public DirectoryEntry FindUser(string principalname, bool useGC)

    8 {

    9     DirectoryEntry searchRoot;

   10     DirectorySearcher searcher;

   11     string[] name;

   12     string ncName;

   13 

   14     name = principalname.Split('\\\\');

   15     if (name.Length != 2) throw new ArgumentException("principalname is not in the correct format", principalname);

   16 

   17     ncName = ResolveNetBiosNameToDN(name[0]);

   18 

   19     searchRoot = GetDirectoryEntry(ncName, useGC);

   20 

   21     searcher = new DirectorySearcher(searchRoot,

   22                                     string.Format("(&(objectCategory=person)​(objectClass=user)​(sAMAccountName={0}))", name[1]),

   23                                     new string[] {"distinguishedName"}, SearchScope.Subtree);

   24 

   25     try

   26     {

   27         return searcher.FindOne().GetDirectoryEntry();

   28     }

   29     catch (NullReferenceException ex)

   30     {

   31         throw new ArgumentException("The given username was not found", "principalname", ex);

   32     }

   33 }

   34 

   35 private string ResolveNetBiosNameToDN(string netbiosName)

   36 {

   37     try

   38     {

   39         return (string) GetDirectoryEntry(string.Format("CN={0}, CN=Partitions, {1}", netbiosName, (string) ConfigurationNamingContext.Properties["distinguishedName"].Value)).Properties["nCName"].Value;

   40     }

   41     catch (System.Runtime.InteropServices.COMException ex)

   42     {

   43         if ((uint) ex.ErrorCode == 0x80072030) throw new ArgumentException("The given netbios name was invalid", "netbiosName", ex);

   44         else throw;

   45     }

   46 }

   47 

   48 public DirectoryEntry GetDirectoryEntry(string distinguishedName)

   49 {

   50     return GetDirectoryEntry(distinguishedName, false);

   51 }

   52 

   53 public DirectoryEntry GetDirectoryEntry(string distinguishedName, bool useGC)

   54 {

   55     string path;

   56 

   57     path = (useGC) ? "GC://" : "LDAP://";

   58     if (_Server != null) path += _Server;

   59 

   60     if ((!path.EndsWith("/")) && (distinguishedName != "")) path += "/";

   61     path += distinguishedName;

   62 

   63     if (path.EndsWith("//")) path = path.Remove(path.Length-2, 2);

   64 

   65     return new DirectoryEntry(path, Username, Password, _AuthenticationType);

   66 }


Posted by Henning Krause on Friday, December 31, 2004 12:00 AM, last modified on Monday, November 29, 2010 9:25 PM
Permalink | Post RSSRSS comment feed