Friday, December 16, 2011

Administrator Locked Out of FIM Portal

 

I’ve been coming up to speed on MIIS/ILM/FIM lately reading the documentation and walking through the evaluation guides in a small lab forest.  I was walking through the FIM 2010 procedure Introduction to Publishing To Active Directory from Two Authoritative Data Sources using FIM 2010 R2.  I had completed the main sync of HR into FIM and back to the Metaverse.  When I switched back to the portal to check on the results I was greeted with an error screen.

Unable to process your request. The requester of this operation is invalid. The requestor's identity was not found.

I’m not sure what caused the problem.  I didn’t have a backup to roll back to, and I didn’t want to give up and just reinstall.  From experience I know that you seldom learn more about a program than when it’s broken.  So I dove in to the problem.

I know FIM is mostly a large SQL application.  I understand the sync database pretty well.  It has two main tables.  The mms_metaverse table stores each object in a row and each attribute in a column.  This allows for indexing the attributes for fast joins and searching the metaverse.  The mms_connectorspace is more opaque.  The data from the connector space is stored as XML blobs in the hologram.

My first glance at the FIMService database showed me it didn’t look anything like the sync database.

To get started I profiled my attempt to open the portal.  You see a call retrieve the default page from SharePoint, then you see the call to figure out who I am.

FIMSQLProfiler

This is a stored procedure call to GetUserFromSecurityIdentifier where the parameter appears to be my SID in binary form.

Since SQL Server Profiler doesn’t show the return value of the query, I ran that query myself.

exec [FIMService].[fim].GetUserFromSecurityIdentifier @SecurityID=0x0010500000000000515000000467CC6805E2AB86508CBF18EF4010000,@UserID=@p2 output

Sure enough, no rows returned.  Looking at the stored procedure, it references the tables UserSecurityIdentifiers and Objects.  UserSecurityIdentifiers was completely empty.  UserSecurityIdentifiers is a simple table with only two columns: UserObjectKey and SecurityIdentifier.

There’s also a stored procedure called GetUserFromName.  When I ran that, it returned my name, so I was pretty sure I was just missing the row that tied my AD SID to the admin user in FIM.

To find my admin user in FIM I ran a query against the Objects table to find the FIM builtin administrator account '7FB2B853-24F0-4498-9534-4E10589723C4'

select * from [FIMService].[fim].[Objects] where objectid='7FB2B853-24F0-4498-9534-4E10589723C4'

For my instance it returned a value of 2340.

I issued one more query to insert my SID into the UserSecurityIdentifiers table.

insert into [FIMService].[fim].UserSecurityIdentifiers values (2340, 0x010500000000000515000000467CC6805E2AB86508CBF18EF4010000)

The value successfully added.  I opened the portal and was treated to the normal administrative view!

I still don’t know what I changed to cause the SID to be deleted, but now at least I know how to get back in without a backup.  Perhaps some more testing in the future will reveal a repeatable pattern that causes the lockout.

Monday, July 18, 2011

Access Denied when backing up WINS on Windows Server 2008

 

As part of switching our services over to Windows Server 2008, we began migrating WINS and our management scripts for WINS.  Our existing Windows Server 2003 based backup script did not work.  It was returning an access denied error.

Firing up Process Monitor produced this report.

WINS_ProcMon

I couldn’t image how a windows service didn’t have permission to write to the filesystem.

Looking at the properties of the Process Monitor event shows this detail.

WINS_ProcMonDetails

The user wasn’t NT Authority\System like I had expected.  Instead it was NT Authority\Local Service.  A search for NT Authority\Local Service and WINS produced KB Article 943514.  The article only references moving the database from its default location, but it also applies to backing up the database to another folder.  The access denied error is resolved by issuing a command like

icacls d:\backupWINS /grant "NT SERVICE\WINS:(OI)(CI)F"

Obviously this grants the WINS service full control of the d:\backupWINS folder so that it now has permissions to create its backup files.  With that in place, no more errors were encountered.

Monday, March 21, 2011

PowerShell example for LdapSessionOptions.VerifyServerCertificate

 

I’ve started switching most of my management scripts over to PowerShell.  I previously had written a small C# command-line tool that would display the certificate expiration date of a Domain Controller’s LDAPS certificate.  This utility was based on Joe Kaplan’s sample.  As this utility was called as part of a much larger and more complex VBScript it only made sense to incorporate this functionality directly into PowerShell as well.  However, figuring out how to get PowerShell to deal with the VerifyServerCertificateCallback object was a more complex an undertaking than I had anticipated, and there were several times I almost gave up and kept the certificate date check as an external utility.  However, I did eventually figure it out and thought I’d share since there are no specific examples anywhere and few examples about PowerShell and callbacks in general.

Within the .NET Framework, System.DirectoryServices.Protocols provides comparatively raw access to the LDAP APIs.  In Joe’s example, the VerifyServerCertificate property is assigned to a new VerifyServerCertificateCallback object, which itself is a function that returns either True or False based on whatever logic one wants to employ.

Most of the examples I found centered on web-server SSL certificates and ServerCertificateValidationCallback and were based on either C# which has no issues with callbacks, like Joe’s, or PowerShell v1 which had to do lots of unnatural things to use the callback.

It turns out there are two important items to know about callbacks and PowerShell v2.  First, callbacks are implemented as a scriptblock.  Second, access to the callback parameters are provided via the args array.

Add-Type -AssemblyName System.DirectoryServices.Protocols
$LDAPId = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier(($DC + ":636"), $true, $false)
$LDAPConnection = New-Object System.DirectoryServices.Protocols.LdapConnection($LDAPId)
$LDAPConnection.Credential = New-Object System.Net.NetworkCredential("", "")
$LDAPConnection.AuthType = [system.directoryservices.protocols.authtype]::anonymous
$LDAPConnection.SessionOptions.SecureSocketLayer = $true
$LDAPConnection.SessionOptions.VerifyServerCertificate = {
    $MyCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $args[1]
    $DCCertDate = $MyCert.NotAfter
    $true
}
Try {
    $LDAPConnection.Bind()
} Catch {
    $DCCertDate = "No Certificate Found"
}

During execution, when the Bind method is called, the scriptblock attached to the VerifyServerCertificate property is called.  The LdapConnection parameter for the VerifyServerCertificateCallback delegate is contained in args[0] and the X509Certificate in args[1].  In this particular example I use the callback certificate to create a new certificate object, store the expiration date and return true, which allows the connection to succeed.