Archive for April, 2011

ADFS Error Tips: When your federation service name does not match your FQDN

With ADFS I am really starting to think it would be useful to have a way to just run a PowerShell test script to diagnose issues, much like a series of unit tests, maybe call it configuration test. Many of the configuration challenges with ADFS could be diagnosed with some scripts. So I made one here.

A few days ago I was getting the following error on ADFS (single server):

ID1038: The AudienceRestrictionCondition was not valid because the specified Audience is not present in AudienceUris.

Audience: ‘https://<fqdn>/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256&#8217;

at Microsoft.IdentityModel.Tokens.SamlSecurityTokenRequirement.ValidateAudienceRestriction(IList`1 allowedAudienceUris, IList`1 tokenAudiences)

at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateConditions(Saml2Conditions conditions, Boolean enforceAudienceRestriction)

at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.ValidateToken(SecurityToken token)

at Microsoft.IdentityServer.Service.Tokens.MSISSaml2TokenHandler.ValidateToken(SecurityToken token)

at Microsoft.IdentityModel.Tokens.WrappedSaml2SecurityTokenAuthenticator.ValidateTokenCore(SecurityToken token)

at System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(SecurityToken token)

at Microsoft.IdentityModel.Tokens.WrappedSamlSecurityTokenAuthenticator.ValidateTokenCore(SecurityToken token)

at System.IdentityModel.Selectors.SecurityTokenAuthenticator.ValidateToken(SecurityToken token)

at System.ServiceModel.Security.ReceiveSecurityHeader.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver, IList`1 allowedTokenAuthenticators, SecurityTokenAuthenticator&amp; usedTokenAuthenticator)

at System.ServiceModel.Security.ReceiveSecurityHeader.ReadToken(XmlDictionaryReader reader, Int32 position, Byte[] decryptedBuffer, SecurityToken encryptionToken, String idInEncryptedForm, TimeSpan timeout)

at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader)

at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)

at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessageCore(Message&amp; message, TimeSpan timeout)

at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(Message&amp; message, TimeSpan timeout)

at System.ServiceModel.Security.SecurityProtocol.VerifyIncomingMessage(Message&amp; message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)

at System.ServiceModel.Channels.SecurityChannelListener`1.ServerSecurityChannel`1.VerifyIncomingMessage(Message&amp; message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationState)

at System.ServiceModel.Channels.SecurityChannelListener`1.SecurityReplyChannel.ProcessReceivedRequest(RequestContext requestContext, TimeSpan timeout)

at System.ServiceModel.Channels.SecurityChannelListener`1.ReceiveRequestAndVerifySecurityAsyncResult.ProcessInnerItem(RequestContext innerItem, TimeSpan timeout)

at System.ServiceModel.Channels.SecurityChannelListener`1.ReceiveItemAndVerifySecurityAsyncResult`2.OnInnerReceiveDone()

at System.ServiceModel.Channels.SecurityChannelListener`1.ReceiveItemAndVerifySecurityAsyncResult`2.InnerTryReceiveCompletedCallback(IAsyncResult result)

at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)

at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)

at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)

at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)

at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)

at System.ServiceModel.Channels.InputQueueChannel`1.EnqueueAndDispatch(TDisposable item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)

at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)

at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, ItemDequeuedCallback dequeuedCallback)

at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)

at System.ServiceModel.Channels.SharedHttpTransportManager.OnGetContextCore(IAsyncResult result)

at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)

at System.Net.LazyAsyncResult.Complete(IntPtr userToken)

at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)

at System.Net.ListenerAsyncResult.WaitCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)

at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)


I had already enabled service and server tracing and had made sure all my certificate configuration was correct. Eventually I figured out the problem was that my federation service name was different from my server FQDN. When looking at the server trace I was seeing a different address being shown. This difference also occurs when the federation metadata exposes a different name than the current server FQDN.

Here is a script I made to check for this scenario:

Write-Host "Testing for correct federation service name"

Add-PSSnapin Microsoft.ADFS.PowerShell

$IPconfig = Get-WmiObject Win32_NetworkAdapterConfiguration | where {$_.IPaddress -like "192.*" }  # modify this as-needed

# Iterate and get IP address
$ip = $IPconfig.IPaddress
$fqdn = [System.Net.DNS]::GetHostByAddress($ip)

$fedServiceName = Get-ADFSProperties | Where { $_.Name -eq "HostName" }

if ($fedServiceName -ne $fqdn)  { Write-Host "FAIL: Server FQDN not equal to Federation Service Name." }

Remove-PSSnapin Microsoft.ADFS.PowerShell

,

3 Comments

ADFS 2 Migration Tips

Introduction

Today I was working on moving my ADFS environment to a separate VM so I could test out a deployment guide I had been working on. On my VM1 I had installed ADFS with Windows Internal Database (WID) in a standalone mode. In my target VM2 I had installed ADFS on SQL 2008 R1 as a farm. I wanted to easily move all of the ADFS configuration and settings to VM2 but found that this is actually fairly difficult.

Attempts

Database Backup / Restore

The first thing I tried to do was to create a backup of the AdfsConfiguration and AdfsArtifactStore databases from VM1 and try to restore them on VM2. Before trying this I made a backup of my existing ADFS databases. Because the SQL version used with WID is SQL 2005 it is not possible to use a .bak file exported from WID and restored on SQL 2008, you get an error. So then I had to detach the files from the WID database and try to reattach the databases on the SQL 2008 VM. I was able to reattach the databases but there were problems afterward.

In order to detach the existing SQL 2008 AdfsConfiguration database on VM2, I had to stop the ADFS 2 service. Then after reattaching the new databases from WID, the ADFS 2 service would not start. So I stopped at this point and switched over to moving the artifacts using scripts. If anyone had any other tips for making this type of migration work, I would appreciate the feedback!

Scripted Move

After not being able to move the data over through database backups, I decided to try to script everything. This approach is just as challenging because you still need to go through all of the objects in the ADFS databases and this can take some time in building out the new scripts. I realized a tool that can generate the scripts for you would be a huge time saver.

Some of the PowerShell cmdlets for ADFS use parameters typed as System.Security.Cryptography.X509Certificates.X509Certificate2. So the parameters expect that you will be passing an object rather than the common name string. When I first saw this in the API reference, I started trying to translate .NET code over for pulling a certificate from the stores like in the code on this article: http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx.

Translating the code from C# to PowerShell was very difficult and somewhat error prone. Eventually I gave up and looked for a better option. I finally found out that the certificate stores load as a PowerShell drive in PowerShell 2.0 and the certificates surface as X509Certificate2 objects. Here is an overview article on the certificate provider: http://technet.microsoft.com/en-us/library/dd347615.aspx. The certificate provider makes it very easy to reference a certificate when working with the ADFS API.

I was moving over claim issuers and relying parties that had mapped claim rules attached to them. The basic approach for handling this was to use the “Get-” cmdlet to output the claim rules, copy the rules to a text file, and then run the “Set-X-RulesFile” to reimport the rules. This is easier to understand when seen in an example.

  1. First I am going to get all of the claim issuers output to the PowerShell window:
    Get-ADFSClaimsProviderTrust
    

    This outputs something like this:

    AcceptanceTransformRules : @RuleTemplate = “PassThroughClaims”
    @RuleName = “Pass thru role”
    c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/role”%5D
    => issue(claim = c);@RuleTemplate = “PassThroughClaims”
    @RuleName = “Pass Thru Name”
    c:[Type == “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name”%5D
    => issue(claim = c);

    I just take everything after the colon on the first line and copy to a text file called acceptanceTransformRules.txt.

  2. Finally, I am going to import a claims issuer’s rules (the claim issuer MySTS needs to already exist at this point:
    # Refer to the rules file with an absolute path
    $curdir = Get-Location
    $curdirfile = Join-Path -Path $curdir -ChildPath "acceptanceTransformRules.txt"
    
    # Import the claim rules
    Set-ADFSClaimsProviderTrust -TargetName "MySTS" -AcceptanceTransformRulesFile $curdirfile
    

Moving a claims issuer from one server to another is resolved to two basic steps: 1. Installing the certificates, 2. Running a script referencing the installed certificates. Here is a PowerShell example combining the ADFS and Certificate APIs for creating a new issuer:


Write-Host "Loading ADFS Snap-In"
Add-PSSnapin Microsoft.ADFS.PowerShell

$certname = "CN=localhost"

# Getting the certificate. How simple using the certificate provider!
set-location cert:\localmachine\my
$cert = Get-ChildItem | Where { $_.Subject -eq $certname }

Set-Location c:

# certificates are correctly specified here
Add-ADFSClaimsProviderTrust -Name "MySTS" -Identifier "https://localhost/trust" -MonitoringEnabled $false -TokenSigningCertificate $cert -EncryptionCertificate $cert -AutoUpdateEnabled $false -AllowCreate $True

Write-Host "Disabling CRL checking on claims issuer trust certs because these are self-signed"
Set-ADFSClaimsProviderTrust -TargetName "MySTS" -SigningCertificateRevocationCheck "None"
Set-ADFSClaimsProviderTrust -TargetName "MySTS" -EncryptionCertificateRevocationCheck "None"

Conclusion

After going through the process of creating scripts like this I realized it is not too much work to do a scripted move but it would be much nicer if you could just export to script from the ADFS mmc or execute some other cmdlet to handle this whole process for you. Maybe in the future I might try to spend some time making something like this and putting it on CodePlex. Thanks!

, ,

Leave a comment

Getting claims to map in an R-STS scenario with ADFS 2.0

Introduction

If you have been following along, you know I have been playing with the R-STS scenario quite a bit lately. I have many, many more posts to do on this topic and I am working on these so stay tuned. Tonight I worked through a couple of hurdles in my R-STS and finally got the claims to map through ADFS 2.0. For my R-STS, I am using Dominick Baier’s StarterSTS (http://startersts.codeplex.com/) with a custom authentication provider (using a SQL user store) and then adding on some custom claims. Then I pass the resulting token into ADFS 2.0 and handle some custom claim mapping and a reissued SAML token with the final set of claims. For a long time I had been struggling to get ADFS to handle the custom claim mapping. Finally I figured out what was happening. In this post I just want to give some more information.

Details

In ADFS you can configure claim issuer trusts and relying party trusts. When you are doing an R-STS scenario, you end up with at least 2 claim issuers and at least 1 relying party. Most of the content out there is on passive federation and single STS scenarios; there is not as much information on active federation or R-STS scenarios. This makes everything more complicated. Understanding the claim procesing flow is the key. I finally started digging into some of the claim pipeline like in this article: http://technet.microsoft.com/en-us/library/ee913585(WS.10).aspx. But again, this is really more about an STS scenario rather than an R-STS scenario.

So reading this article it is easy to see that the processing order for an STS:

  1. Claims arrive at your STS party (Idp) which is ADFS
  2. Then are mapped
  3. Then sent onto the relying party in ADFS specified in your RST AppliesTo
  4. Then they are mapped
  5. Then sent back in the security token back from ADFS.

In R-STS, actually what happens is:

  1. Claims arrive at the original STS party (Idp) which in my case is the StarterSTS claim issuer
  2. Then are mapped
  3. Then sent onto the relying party in ADFS specified in your RST AppliesTo (but for the R-STS security token request)
  4. Then they are mapped
  5. Then sent back in the security token back from ADFS

I had been struggling with understanding the execution order. I had been trying to put the claim rules on the built-in “Active Directory” claim issuer because this was leg 2 of my R-STS scenario but ADFS is smart enough to pass these claims right through the built-in claim issuer. You still need to configure a pass through claim rule on both the original STS claim issuer and the relying party but everything flows through as expected.

Some hints that helped me today to accomplish this are:

– Output the claims after every token is returned. This helps you to start thinking about the pipeline.
– Start out with a claim rule in ADFS that does not evaluate incoming claims conditionally. So for example, try a hard coded claim rule like this (from http://technet.microsoft.com/en-us/library/dd807118(WS.10).aspx):

=> issue(type = “http://test/role“, value = “employee”);

The rule above just adds a hard coded claim to the resulting claims. Another helpful tip is that the rule above can issue any claim type even ones not documented in the claim descriptions of ADFS. So you can basically inject claims here without registering them with ADFS. This is both a nice flexibility and a challenge because this can hurt your debugging.

But if you are just trying to figure out where claims get injected into the pipeline, add the rule above to your relying party to just get a claim to show up on the returned token.

– Keep all the built-in Active Directory claim issuer rules in place because these are good examples. Also review the rule syntax for the rules to get a better understanding of what is happening.

Let me know if you have any questions and I will keep posting some lessons learned on my adventures with ADFS!

Thanks,

, ,

7 Comments

Enabling configuration for ADFS 2.0 Server Traces

Introduction
I was working on doing diagnostics for an R-STS scenario with ADFS 2.0 and I was getting lots of errors. I had enabled tracing already for the client side but I was not getting any error traces for the client. I think a large percentage of the errors occur on the ADFS server side. So I enabled tracing for the server side as well. I just tried it and did not know if this was officially supported or documented. Doing this REALLY helped me figure out the problem and deal with error messages that were confusing. In this post I am going to show what I did to enable the server side tracing.
In doing secondary research for this post I did see this post from back during ADFS Beta 2: http://imav8n.wordpress.com/2009/08/06/enabling-logging-in-adfs/. I will just update some of the details here.
Details
The config for ADFS is found in c:\Program Files\Active Directory Federation Services 2.0 called Microsoft.IdentityServer.ServiceHost.exe.config. Here is a copy of my config file with the tracing added. You should be able to copy this directly and then the tracing can be output for you. I did also disable the default trace ADFS listener because I wanted the output to go to a file so that I can correlate everthing using the service trace viewer
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <section name="microsoft.identityServer.service" type="Microsoft.IdentityServer.Service.Configuration.ServiceConfiguration, Microsoft.IdentityServer.Service, Version=6.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
    <section name="microsoft.identityServer" type="Microsoft.IdentityServer.Service.Configuration.IdentityServerConfiguration, Microsoft.IdentityServer.Service, Version=6.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/>
    <section name="microsoft.identityServer.proxy" type="Microsoft.IdentityServer.Service.Configuration.ProxyConfigurationSection, Microsoft.IdentityServer.Service, Version=6.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/>
  </configSections>

  <microsoft.identityServer serviceMode="Server"/>

  <microsoft.identityServer.proxy >
    <host name="" httpPort="80" httpsPort="443" />
    <proxyTrust proxyTrustRenewPeriod="240" />
  </microsoft.identityServer.proxy>

  <microsoft.identityServer.service>
    <policyStore connectionString="Data Source=\\.\pipe\mssql$microsoft##ssee\sql\query;Initial Catalog=AdfsConfiguration;Integrated Security=True"
      administrationUrl="net.tcp://localhost:1500/policy" />
    <trustMonitoring enabled="true" />

  </microsoft.identityServer.service>
  <system.diagnostics>
    <sources>
      <!-- To enable WIF tracing, change the switchValue below to desired trace level - Verbose, Information, Warning, Error, Critical -->
      <!-- Set TraceOutputOptions as comma separated value of the following; ProcessId ThreadId CallStack. Specify None to not include any of the optional data-->
      <!-- NOTE THAT THE CHANGES TO THIS SECTION REQUIRES SERVICE RESTART TO TAKE EFFECT -->
      <source name="Microsoft.IdentityModel" switchValue="Verbose">
        <listeners>
<!--          <add name="ADFSWifListener"  traceOutputOptions="ProcessId,ThreadId" initializeData="Wif" type="Microsoft.IdentityServer.Diagnostics.ADFSTraceListener,Microsoft.IdentityServer,Version=6.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> -->
          <add name="ADFSWifListener" />

        </listeners>
      </source>

      <!-- To enable WCF tracing, change the switchValue below to desired trace level - Verbose, Information, Warning, Error, Critical and
           uncomment the system.servicemodel section below -->
      <source name="System.ServiceModel" switchValue="Verbose" >
        <listeners>
        <!--  <add  name="ADFSWcfListener" traceOutputOptions="ProcessId,ThreadId" initializeData="Wcf" type="Microsoft.IdentityServer.Diagnostics.ADFSTraceListener,Microsoft.IdentityServer,Version=6.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> -->
          <add name="ADFSWcfListener" />
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging" switchValue="Verbose" >
        <listeners>
<!--          <add  name="ADFSWcfListener" traceOutputOptions="ProcessId,ThreadId" initializeData="Wcf" type="Microsoft.IdentityServer.Diagnostics.ADFSTraceListener,Microsoft.IdentityServer,Version=6.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> -->
          <add name="ADFSWcfListener" />
        </listeners>
      </source>
    </sources>
    <!-- Added by Ben Cline - sharedListeners -->
    <sharedListeners>
      <add name="ADFSWcfListener"
           type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
           initializeData="c:\temp\adfssvchost_servicemodel.svclog"
           traceOutputOptions="Timestamp">
        <filter type="" />
      </add>
      <add name="ADFSWifListener"
           type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
           initializeData="c:\temp\adfssvchost_wif.svclog"
           traceOutputOptions="Timestamp">
        <filter type="" />
      </add>

    </sharedListeners>
    <trace autoflush="true" ></trace>

  </system.diagnostics>

 <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true"
              logMessagesAtServiceLevel="true"
              logMessagesAtTransportLevel="true">
      </messageLogging>
    </diagnostics>
  </system.serviceModel>

  <runtime>
    <gcServer enabled="true"/>
  </runtime>
</configuration>

, ,

2 Comments