Sunday, April 06, 2008

Showing Mailbox Rights and Send-as/Receive As rights in Powershell Exchange 2000/3/7

I’ve been working on some permissions scripts in the past couple of weeks and thought I’d post a powershell port of some VBS code from this http://msdn2.microsoft.com/en-us/library/ms992469(EXCHG.65).aspx msdn article. I’ve use this code before in a variety of scripts to read permissions from the Exchange Mailbox DACL via the msexchmailboxsecuritydescriptor AD property. Of course one should never try to set mailbox rights using this property as per http://support.microsoft.com/kb/310866/ so make sure you always treat it as read only. So what I’ve done is put together a quick sample that uses the new ActiveDirectorySecurity class in .NET 2.0 to basically load the DACL from the bytearray representation of the DACL that’s stored in this property. Also I’ve included some code to retrieve the Send-AS and Receive-AS rights from the AD object's DACL.

The code only looks at the Implicitly set ACE's and not the Inherited ACE’s (this could be easily changed) it queries ever mailbox in the domain it is executed in and outputs any explicitly set Mailbox ACE’s and Send-as/Receive-as ACE's out to the cmdline. On Exchange 2007 you could do the same thing in the Exchange Management Shell a lot easier using the get-mailboxpermission and get-adpermission.

I’ve put a download of the code here the script itself looks like.

[warning this script could cause global warming if drive when you should walk somewhere]

$root = [ADSI]'LDAP://RootDSE'
$dfDefaultRootPath = "LDAP://" + $root.DefaultNamingContext.tostring()
$dfRoot = [ADSI]$dfDefaultRootPath
$gfGALQueryFilter = "(&(&(&(mailnickname=*)(objectCategory=person)(objectClass=user))))"
$dfsearcher = new-object System.DirectoryServices.DirectorySearcher($dfRoot)
$dfsearcher.Filter = $gfGALQueryFilter
$dfsearcher.PropertiesToLoad.Add("msExchMailboxSecurityDescriptor")
$srSearchResult = $dfsearcher.FindAll()
foreach ($emResult in $srSearchResult) {
$uoUserobject = New-Object System.DirectoryServices.directoryentry
$uoUserobject = $emResult.GetDirectoryEntry()
$emProps = $emResult.Properties
[byte[]]$DaclByte = $emProps["msexchmailboxsecuritydescriptor"][0]
$adDACL = new-object System.DirectoryServices.ActiveDirectorySecurity
$adDACL.SetSecurityDescriptorBinaryForm($DaclByte)
$mbRightsacls =$adDACL.GetAccessRules($true, $false, [System.Security.Principal.SecurityIdentifier])
"Mailbox - " + $uoUserobject.DisplayName
foreach ($ace in $mbRightsacls){
if($ace.IdentityReference.Value -ne "S-1-5-10" -band $ace.IdentityReference.Value
-ne "S-1-5-18" -band $ace.IsInherited -ne $true){

$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname
}
" ACE UserName : " + $AceName
""
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::CreateChild){

" Full Mailbox Access"}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::WriteOwner
-ne 0){
" Take Ownership"}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::WriteDacl){

" Modify User Attributes"}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ListChildren){

" Is mailbox primary owner of this object"}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::Delete){

" Delete mailbox storage"}
If ($ace.ActiveDirectoryRights -band [System.DirectoryServices.ActiveDirectoryRights]::ReadControl){

" Read permissions"}

}
}
$Sendasacls = $uoUserobject.psbase.get_objectSecurity().getAccessRules($true,
$false, [System.Security.Principal.SecurityIdentifier])|? {$_.ObjectType -eq
'ab721a54-1e2f-11d0-9819-00aa0040529b'}
$Recieveasacls = $uoUserobject.psbase.get_objectSecurity().getAccessRules($true,
$false, [System.Security.Principal.SecurityIdentifier])|? {$_.ObjectType -eq
'ab721a56-1e2f-11d0-9819-00aa0040529b'}
if ($Sendasacls -ne $null){
foreach ($ace in $Sendasacls)
{
if($ace.IdentityReference.Value -ne "S-1-5-10" -band $ace.IdentityReference.Value
-ne "S-1-5-18" -band $ace.IsInherited -ne $true){
$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname
}
""
" ACE UserName : " + $AceName
" Send As Rights"
}

}
}
if ($Recieveasacls -ne $null){
foreach ($ace in $Recieveasacls)
{
if($ace.IdentityReference.Value -ne "S-1-5-10" -band
$ace.IdentityReference.Value -ne "S-1-5-18" -band $ace.IsInherited -ne $true){
$sidbind = "LDAP://<SID=" + $ace.IdentityReference.Value + ">"
$AceName = $ace.IdentityReference.Value
$aceuser = [ADSI]$sidbind
if ($aceuser.name -ne $null){
$AceName = $aceuser.samaccountname
}
""
" ACE UserName : " + $AceName
" Recieve As Rights"
}
}
}
}



17 comments:

Anonymous said...

Glen

I really appreciate the code you post. It is great to go through it and find new ways of doing things. Keep up the great work.

I want to start using the powershell, like this example script, in my environment. However we are still running Windows 2003 and Exchange 2003.

Does your script work in that environment? What do I need to do to get powershell running? And lastly, do you see this script running on workstation connected to a domain or on the Exchange server/domain controller?

Thanks
Kevin

Glen said...

This script will run fine in Windows 2003/Exchange 2003 actually thats what environment i wrote it for. Its designed to run from a workstation that is a domain member you don't need to run it on a Exchange Server. All the script uses is LDAP and obviously powershell.

Cheers
Glen

Anonymous said...

Hello Glen,

Thanks for this script. What if I want to grand a user send-as & receive-as rights for the mail store? I have a OnSyncSave eventsink and move message to a spam folder so I need those rights to move te mail. How can I do that in the PowerShell I now do it like this: http://support.microsoft.com/kb/310866/

Thanks,
Jort

Anonymous said...

Glen
Great post. This has come in very handy in documenting our Exchange 2007 environment
Will this script also work for discovering mailboxes with the "send as" right assigned?
I have tried simple substitution in the script, but it does not find the send as assignments. It would be really handy to get this to work also.

Thanks
Brent

Anonymous said...

Sorry for the above confusion, another blog, another Ps script.
I am having difficulty getting your PS script to display over 1000 mailboxes. I believe I need to insert the "-ResultSize unlimited" string into the mix, but can't figure out where.
Any suggestions?

Thanks
Brent

Glen said...

To return more the 1000 object you just need to set the PageSize eg

$dfsearcher.PageSize = 1000

You might also want to have a look at http://gsexdev.blogspot.com/2008/04/exchange-permission-and-reverse.html

Cheers
Glen

Anonymous said...

Thanks, that works.
the Pagesize setting allows the script to return 4500 mailboxes.

This is a very useful script, my complements to the Chef..

Brent

Sean McGilvray said...

I get the following error when I run the script

Cannot index into a null array.
At :line:13 char:65
+ [byte[]]$DaclByte = $emProps["msexchmailboxsecuritydescriptor"][ <<<< 0]

Can anyone help?

Sean McGilvray
smcgilvray@gmail.com

Sean McGilvray said...

Never mind guys I figured it out. The user that it is trying to access does not have a Mailbox Rights button. It looks like an old account that was from 5.5 days. I will look further into it.

Thanks,

Sean McGilvray

Anonymous said...

Hey,

Many thanks, this is a really helpful script. I wonder if you could help me customise it to my needs?

We block email to particular accounts by manually changing the self permissions on the mailbox to deny. I've modified the script to display users which have these deny permissions to their mailbox. I'd like a button to enable these accounts but can't figure out the right syntax to enable permissions on the self object for each selected account. My research shows this is simple in exch 2k7 but I'm working with 2k3.

I've added a script node to powershell to display my results and a button to enable, everything is in place except actually modifying (or remove/add) the permission. Any help would be greatly appreciated!

Glen said...

To add the Self ACE on 2003 you need something like

const FULL_MAILBOX_ACCESS = &h1
const SEND_AS = &h2
const CONTAINER_INHERIT_ACE = &h2
const ASSOCIATED_EXTERNAL = &h4
const READ_PERMISSIONS = &h20000
Dim oUser, sUserADsPath
sUserADsPath = "LDAP://dn..."
Set oUser = GetObject(sUserADsPath)
Dim oAce
Set oAce = CreateObject("AccessControlEntry")
oAce.Trustee = "NT AUTHORITY\SELF"
oAce.AccessMask = (FULL_MAILBOX_ACCESS + SEND_AS + ASSOCIATED_EXTERNAL + READ_PERMISSIONS)
oAce.AceFlags = CONTAINER_INHERIT_ACE
oAce.AceType = ACCESS_ALLOWED
Dim mailboxSD
Set mailboxSD = oUser.MailboxRights
Dim oDACL
Set oDACL = mailboxSD.DiscretionaryAcl
Dim bFoundMASInSD
bFoundMASInSD = false
for each ace in oDACL
if ( ace.AccessMask And ASSOCIATED_EXTERNAL ) then
WScript.Echo "Found existing Assoc-External for user: " & ace.Trustee & " in the security descriptor. Only one user can have this setting at a time! No changes will be made."
bFoundMASInSD = true
end if
next
if (bFoundMASInSD = false) then
oDACL.AddAce(oACE)
end if
mailboxSD.DiscretionaryAcl = oDACL
oUser.MailboxRights = Array(mailboxSD)
oUser.SetInfo

Cheers
Glen

Roland said...

This is working great!

One question: This scripts only displays the righst that er explicit put on the mailbox.
Is it also posible to includes the display for delegations by te user? So i would like to know if user A has delegated rights to user B.

Emmanuel said...

I 'm trying to write a powershell script to give full mailbox access to a specific user on a specific user mailbox (Exchange 2003)
Thanks

Glen said...

see http://support.microsoft.com/kb/310866/en-us

Cheers
Glen

Anonymous said...

Great script, Glen. I modified a little... to only display name and name of person with full access in column format.
And "explicit access" if user has permissions to own mailbox vs SELF.

I also discovered one instance where it will error some. If you have a user that is mail-enabled but no mailbox. Like someone outside of your org.
Easy to fix, just add a mailbox specific ldap attribute to the filter like this:
$gfGALQueryFilter = "(&(&(&(mailnickname=*)(homeMDB=*)(objectCategory=person)(objectClass=user))))"

Good stuff, man. Google has pointed me to your blog a number of times.

Sanjay Narsey said...

Great script. Worked perfect. Have another request how can I find if the user has “Send on Behalf” permission in the same script?

EQIS said...

Great script!
Is there extra code you can add, that would enumerate the groups (universal and security) found in some of the mailbox rights entries?