Skip to main content

Reverse Permissions Audit Scripts Part 2

*****If your looking for a Exchange 2007/2010 solution have a look at this post *****

In April I posted this script that did a reverse lookup of mailbox permissions that had been assigned via Active Directory Users and Computers. This was to give a view by user of what mailbox’s a particular user had access to (instead of the normal view which is who has access to this mailbox). The second part of the permission story with exchange is the actually MAPI permissions that are set on each of the folders in a mailbox. When you delegate folders in outlook this will modify the rights of each of the folders that you delegate (eg calendar,contact,tasks). To view the Mapi permissions on a folder programmatically from a script there are a few methods that can be used the easiest way to do it is to use the Acl.dll from the platform SDK see KB240911 for details for more details. The basis of this article can be used to create a conventional script that will log the rights on each folder and save this to a CSV file. You can then open the CSV in Excel do a auto filter on the folder you want to audit and you very quick and useful report. I’ve included a conventional version of a script that does just this in the download for this post this script takes the name of the server you want to run it against as a command line parameter. Both these scripts require the acl.dll to be registered on the machine you are running the script on. Acl.dll can be found in the Platform SDK or downloaded as part of this CDOlive sample .

With the reverse MAPI permission script what I’ve done is to take the data shaping logic I used previously and applied it to all the root mailbox folders eg the root of the mailbox tree and the calendar,inbox,contacts,tasks,notes and sent item folders. So what the script will output is a per user view of what other Users’s mailbox folders each user has access to. So this script could be used to answer the question what calendars is fred-blogs able to see. To do this the script first looks up Active Directory for all users that have a mailbox in a domain and creates a parent record-set of these values. Then for every user it tries to establish a MAPI connection to there mailbox via CDO 1.2 and enumerates permissions on the root folders in the mailbox and writes these permissions to the child record-set. The recordset’s are then related on the Exchange LegacyDN and the address retrieved from CDO addressEntry object.

The script tries to connect to every mailbox in a domain and expects that the user running it will have full rights to every mailbox in the domain equiv to . I’ve put a download copy of both scripts that I’ve talked about here. The code for the reverse permissions script looks like

on error resume next
set conn1 = createobject("ADODB.Connection")
strConnString = "Data Provider=NONE; Provider=MSDataShape"
conn1.Open strConnString
set conn = createobject("ADODB.Connection")
set com = createobject("ADODB.Command")
Set iAdRootDSE = GetObject("LDAP://RootDSE")
strNameingContext = iAdRootDSE.Get("defaultNamingContext")
Conn.Provider = "ADsDSOObject"
Conn.Open "ADs Provider"
Query = "<LDAP://" & strNameingContext & ">;(&(&(&(& (mailnickname=*)" & _
"(!msExchHideFromAddressLists=TRUE) (| (&(objectCategory=person)(objectClass=user)"
& _
"(|(homeMDB=*)(msExchHomeServerName=*))) )))))" & _
";samaccountname,legacyExchangeDN,msExchHomeServerName," &_
Com.ActiveConnection = Conn
Com.CommandText = Query
Com.Properties("Page Size") = 1000
set objParentRS = createobject("adodb.recordset")
set objChildRS = createobject("adodb.recordset")
" NEW adVarChar(255) AS UOADDisplayName, " & _
" NEW adVarChar(255) AS UOADTrusteeName, " & _
" NEW adVarChar(255) AS UOADLegacyDN, " & _
" ((SHAPE APPEND " & _
" NEW adVarChar(255) AS MRmbox, " & _
" NEW adVarChar(255) AS MRFolder, " & _
" NEW adVarChar(255) AS MRTrusteeName, " & _
" NEW adVarChar(255) AS MRRights) " & _
" RELATE UOADLegacyDN TO MRTrusteeName) AS rsUOMR"
objParentRS.LockType = 3
objParentRS.Open strSQL, conn1

Set Rs = Com.Execute
While Not Rs.EOF
objParentRS("UOADDisplayName") = rs.fields("displayname")
objParentRS("UOADTrusteeName") = rs.fields("samaccountname")
objParentRS("UOADLegacyDN") = rs.fields("legacyExchangeDN")
Set objChildRS = objParentRS("rsUOMR").Value
inplinearray = Split(rs.fields("msExchHomeServerName").value, "=", -1, 1)
CDOAddr =
wscript.echo "Number of Mailboxes Checked " & objParentRS.recordcount

Do While Not objParentRS.EOF
Set objChildRS = objParentRS("rsUOMR").Value
if objChildRS.recordcount <> 0 then wscript.echo objParentRS("UOADDisplayName")
Do While Not objChildRS.EOF
wscript.echo " " & objChildRS.fields("MRmbox") & _
" " & objChildRS.fields("MRFolder") & " " & objChildRS.fields("MRRights")

function getpermissions(servername,mailboxname,displayname)
wscript.echo "Processing " & mailboxname
Set objSession = CreateObject("MAPI.Session")
objSession.Logon "","",false,true,true,true,servername & vbLF & mailboxname
if err.number <> 0 then wscript.echo err.description
set objCuser = objSession.CurrentUser
Set CdoInfoStore = objSession.GetInfoStore
Set CdoFolderRoot = CdoInfoStore.RootFolder
Set ACLObj = CreateObject("MSExchange.aclobject")
ACLObj.CDOItem = CdoFolderRoot
Set FolderACEs = ACLObj.ACEs
For each fldace in FolderACEs
if cstr(objCuser.address) <> cstr(GetACLEntryName(fldace.ID, objSession)) then
objChildRS("MRmbox") = mailboxname
objChildRS("MRFolder") = "Root"
objChildRS("MRTrusteeName") = GetACLEntryName(fldace.ID, objSession)
objChildRS("MRRights") = DispACERules(fldace)
end if
Set CdoFolders = CdoFolderRoot.Folders
Set CdoFolder = CdoFolders.GetFirst
do while Not (CdoFolder Is Nothing)
ACLObj.CDOItem = CdoFolder
Set FolderACEs = ACLObj.ACEs
For each fldace in FolderACEs
if cstr(objCuser.address) <> cstr(GetACLEntryName(fldace.ID, objSession)) then

objChildRS("MRmbox") = mailboxname
objChildRS("MRFolder") = CdoFolder.Name
objChildRS("MRTrusteeName") = GetACLEntryName(fldace.ID, objSession)
objChildRS("MRRights") = DispACERules(fldace)
end if
Set CdoFolder = CdoFolders.GetNext
if Not objSession Is Nothing Then objSession.Logoff
set objSession = nothing
End function

Function GetACLEntryName(ACLEntryID,SubSession)

select case ACLEntryID
GetACLEntryName = "Default"
GetACLEntryName = "Anonymous"
case else
Set tmpEntry = SubSession.GetAddressEntry(ACLEntryID)
tmpName = tmpEntry.address
GetACLEntryName = tmpName
end select
Set objSession = nothing
Set CdoFolderRoot = nothing
Set ACLObj = nothing
Set FolderACEs = nothing
Set objSession = nothing

End Function

Function DispACERules(DisptmpACE)

Select Case DisptmpACE.Rights

Case ROLE_NONE, 0 ' Checking in case the role has not been set on that entry.
DispACERules = "None"
Case 1024 ' Check value since ROLE_NONE is incorrect
DispACERules = "None"
DispACERules = "Author"
Case 1051 ' Check value since ROLE_AUTHOR is incorrect
DispACERules = "Author"
DispACERules = "Contributor"
Case 1026 ' Check value since ROLE_CONTRIBUTOR is incorrect
DispACERules = "Contributor"
Case 1147 ' Check value since ROLE_EDITOR is incorrect
DispACERules = "Editor"
DispACERules = "Nonediting Author"
Case 1043 ' Check value since ROLE_NONEDITING AUTHOR is incorrect
DispACERules = "Nonediting Author"
Case 2043 ' Check value since ROLE_OWNER is incorrect
DispACERules = "Owner"
DispACERules = "Publishing Author"
Case 1179 ' Check value since ROLE_PUBLISHING_AUTHOR is incorrect
DispACERules = "Publishing Author"
Case 1275 ' Check value since ROLE_PUBLISH_EDITOR is incorrect
DispACERules = "Publishing Editor"
DispACERules = "Reviewer"
Case 1025 ' Check value since ROLE_REVIEWER is incorrect
DispACERules = "Reviewer"
Case Else
DispACERules = "Custom"
End Select

End Function

Popular posts from this blog

Downloading a shared file from Onedrive for business using Powershell

I thought I'd quickly share this script I came up with to download a file that was shared using One Drive for Business (which is SharePoint under the covers) with Powershell. The following script takes a OneDrive for business URL which would look like This script is pretty simple it uses the SharePoint CSOM (Client side object Model) which it loads in the first line. It uses the URI object to separate the host and relative URL which the CSOM requires and also the SharePointOnlineCredentials object to handle the Office365 SharePoint online authentication. The following script is a function that take the OneDrive URL, Credentials for Office365 and path you want to download the file to and downloads the file. eg to run the script you would use something like ./spdownload.ps1 '

A walk-though using the Graph API Mailbox reports in Powershell

Quite recently the Reporting side of the Graph API has moved in GA from beta, there are quite a number of reports that can be run across various Office365 surfaces but in this post I'm going to focus on the Mailbox related ones. Accessing Office365 Reports using Powershell is nothing new and has been available in the previous reporting endpoint however from the end of January many of these cmdlets are now being depreciated in favour of the Graph API . Prerequisites  In comparison to using the Remote PowerShell cmdlets where only the correct Office365 Admin permissions where needed, to use the new Graph API reports endpoint you need to use OAuth for authentication so this requires an Application Registration  that is then given the correct oAuth Grants to use the Reports EndPoin

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.