Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange intermittent error handling bug when calling UserPrinciapl.GetGroups in System.DirectoryServices.AccountManagement

Background

We have an asp.net 4.0 web application written in C# that calls a .net 3.5 web service written in C#. The web service is passed a user id and returns a list of data depending on the active directory groups the user belongs to.

The web service uses .net 3.5 version of System.DirectoryServices.AccountManagement to get the Sids of the groups the user belongs to.

The call to UserPrincipal.GetGroups fails intermittently with the error below. There were very long periods of time between occurrences but when it did occur it occurred repeatedly for several minutes. The issue occurred for different AD users.

The stack trace of this exception made no sense to us. We spent a lot of time looking at the Microsoft AD code in Reflector/ILSpy but couldn't get beyond the call to IADsPathName.Retrieve.

Exception

System.NotSupportedException: Specified method is not supported.
at System.Web.HttpResponseStream.get_Position()
at System.Drawing.UnsafeNativeMethods.ComStreamFromDataStream.Seek(Int64 offset, Int32 origin)
at System.DirectoryServices.AccountManagement.UnsafeNativeMethods.IADsPathname.Retrieve(Int32 lnFormatType)
at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()
at System.DirectoryServices.AccountManagement.ADStoreCtx.get_DnsForestName()
at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOf(Principal p)
at System.DirectoryServices.AccountManagement.Principal.GetGroupsHelper()
at System.DirectoryServices.AccountManagement.Principal.GetGroups()
at Data.SoftwarePublishingItemData.GetSids(String requestedForUserId)
at Data.SoftwarePublishingItemData.GetSoftwarePublishingItems(IDatabaseContext dbContext, GetSoftwarePublishingItemsSettings settings, XBXmlDocument parameters)
at Web.GetSoftwarePublishingItems.GetFlexiFieldData(String xml)

Code to reproduce

Please note, the CauseNotSupportedException method is mimicking code that is not running in our application but in code somewhere else in the environment that we are not on control of.

class Program
{
    static void Main(string[] args)
    {
        CauseNotSupportedException();

        string samAccountName = "domain.user";

        using (var principalContext = new PrincipalContext(ContextType.Domain))
        {
            using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountName))
            {
                if (userPrincipal == null)
                    throw new ActiveDirectoryObjectNotFoundException();

                using (var groups = userPrincipal.GetGroups())
                {
                    foreach (GroupPrincipal group in groups)
                    {
                        Console.WriteLine(group.Sid);
                    }
                }
            }
        }
    }

    public static void CauseNotSupportedException()
    {
        using (var b = new Bitmap(500, 500, PixelFormat.Format32bppArgb))
        {
            b.Save(new FakeStream(), ImageFormat.Png);
        }
    }
}

Implementation of Stream to mimic HttpResponseStream behaviour

public class FakeStream : Stream
{
    public override bool CanRead { get { return false; } }
    public override bool CanSeek { get { return false; } }
    public override bool CanWrite { get { return true; } }

    public override void Flush() { }

    public override long Length { get { throw new NotSupportedException("No Seek"); } }

    public override long Position
    {
        get { throw new NotSupportedException("No Seek"); }
        set { throw new NotSupportedException("No Seek"); }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        throw new InvalidOperationException("Write only stream");
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException("net_noseek");
    }

    public override void SetLength(long value) { }

    public override void Write(byte[] buffer, int offset, int count) { }
}

Questions

  1. If you run the example above the error that occurs in the CauseNotSupportedException method is thrown in the call to GetGroups. How can that be? Any theories or further insight would be appreciated.
  2. Any suggestions as to how to investigate further?
  3. Any better suggestions than catching the exception and retrying? That is our current work around.

Thanks.

Clarification

I'm not sure how clear I've been in my explanation so here's some clarification. Firstly, I am happy with the active directory code that gets the Sids. That does what I want it to do and I don't think the problem is with that as such. The real issue is that when an error occurs in other unrelated code (it's not in our app) the error manifests in the GetGroups call, hence the strange stack trace with the error originally occurring at System.Web.HttpResponseStream.get_Position(). In the sample app NotSupportedException occurs in CauseNotSupportedException but the code does not break there, it breaks on the call to GetGroups. If you comment out CauseNotSupportedException() in the sample app the error never occurs.

It is unclear to me how this can possibly happen.

like image 747
Simon Vane Avatar asked Jan 04 '12 14:01

Simon Vane


1 Answers

After raising a support call Microsoft have issued a hot fix for this issue. See link below.

The stated cause is: "This issue occurs because the System.DirectoryServices.AccountManagement namespace is a thin wrapper for the native API Active Directory Service Interfaces (ADSI). The IErrorInfo interface that is implemented by the IADsPathName interface responds to exceptions that ADSI does not throw. When there is no ADSI exception on the stack, the IErrorInfo interface throws the exception that is at the top of the stack, even if the exception is handled by another handler in the application."

http://support.microsoft.com/kb/2683913

Thank you to those who offered suggestions.

like image 54
Simon Vane Avatar answered Oct 03 '22 16:10

Simon Vane