none
Custom code access permission throws SecurityException with message “Failure decoding embedded permission set object.” RRS feed

  • Question

  • Hello,

    We have a pretty elaborate authorization system which requires the use of a custom permission somewhat similar to PrincipalPermission. I followed all suggestions on MSDN and looked at the code of PrincipalPermission and yet I have a pretty stubborn problem: if I declare the permission on the class level with one parameter and then I declare it on one of the methods with different (more specific) parameter the code access security builds a union of the two and calls Demand on it. My implementation of Demand returns with no exception and yet the system throws SecurityException with the cryptic message "Failure decoding embedded permission set object." What does it mean? I see all sorts of calls to my permission, includin ToXml and FromXml and I think that they all behave properly (or at least do not fail). Can someone out there think of what might be missing or done wrong here. Below is the code of the permission class (thanks in advance to anyone who will give it a thought):

    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.Security;
    using System.Security.Permissions;
    using System.Security.Policy;
    using System.ServiceModel;
    using System.Threading;
    using Authorization.DataContracts;
    namespace Authorization
    {
        [Serializable]
        public sealed class AuthorizationPermission : IPermission, IUnrestrictedPermission, ISerializable, IEquatable<AuthorizationPermission>
        {
            List<string> _activityIdentifiers = new List<string>();
            public AuthorizationPermission()
            {
            }
            public AuthorizationPermission(PermissionState state)
            {
                if (state == PermissionState.Unrestricted)
                    _activityIdentifiers.Add(string.Empty);
            }
            public AuthorizationPermission(string activityIdentifier, bool unrestricted = false)
            {
                if (!unrestricted && string.IsNullOrWhiteSpace(activityIdentifier))
                    throw new ArgumentNullException("activityIdentifier");
                if (unrestricted)
                    _activityIdentifiers.Add(string.Empty);
                else
                    _activityIdentifiers.Add(activityIdentifier);
            }
            protected AuthorizationPermission(SerializationInfo info, StreamingContext context)
            {
                var count = info.GetInt32("activityIdentifiersCount");
                for (var i=0; i<count; i++)
                    _activityIdentifiers.Add(info.GetString("activityIdentifier_"+i));
            }
            public static bool HasPermission(string protectedIdentifier)
            {
                try
                {
                    var auth = AuthorizationCache.GetAuthorization(Thread.CurrentPrincipal.Identity.Name, protectedIdentifier);
                    return auth.Granted;
                }
                catch (CommunicationException)
                {
                    return false;
                }
            }
            public bool Equals(AuthorizationPermission other)
            {
                if (ReferenceEquals(other, null))
                    return false;
                if (ReferenceEquals(this, other))
                    return true;
                return _activityIdentifiers.SequenceEqual(other._activityIdentifiers, StringComparer.OrdinalIgnoreCase);
            }
            public override bool Equals(object obj)
            {
                return Equals(obj as AuthorizationPermission);
            }
            public override int GetHashCode()
            {
                var hashCode = 53;
                foreach (var id in _activityIdentifiers)
                    hashCode = 17 * hashCode + id.ToUpperInvariant().GetHashCode();
                return hashCode;
            }
            public static bool operator==(AuthorizationPermission left, AuthorizationPermission right)
            {
                return ReferenceEquals(left, null) 
                            ? ReferenceEquals(right, null) 
                            : left.Equals(right);
            }
            public static bool operator!=(AuthorizationPermission left, AuthorizationPermission right)
            {
                return !(left==right);
            }
            public override string ToString()
            {
                return ToXml().ToString();
            }
            public bool IsUnrestricted()
            {
                return _activityIdentifiers.All(id => string.IsNullOrWhiteSpace(id));
            }
            public IPermission Copy()
            {
                return new AuthorizationPermission
                {
                    _activityIdentifiers = this._activityIdentifiers.ToList(),
                };
            }
            public void Demand()
            {
                if (IsUnrestricted())
                    return;
                if (!Thread.CurrentPrincipal.Identity.IsAuthenticated)
                    throw new SecurityException("The principal's identity is not authenticated.", GetType());
                if (!_activityIdentifiers.Any())
                    throw new SecurityException("Access is denied.", GetType());
                AuthorizationDto auth = null;
                try
                {
                    foreach (var id in _activityIdentifiers)
                    {
                        if (string.IsNullOrWhiteSpace(id))
                            continue;
                        auth = AuthorizationCache.GetAuthorization(Thread.CurrentPrincipal.Identity.Name, id);
                        if (auth==null)
                            ThrowSecurityException("Access is denied.");
                        if (!auth.Granted)
                            ThrowSecurityException("Access is denied. " + auth.Message);
                    }
                }
                catch (CommunicationException x)
                {
                    throw new SecurityException("Access is denied.", x);
                }
            }
            void ThrowSecurityException(string message)
            {
                AssemblyName assemblyName = null;
                Evidence evidence = null;
                try
                {
                    var callingAssembly = Assembly.GetCallingAssembly();
                    assemblyName = callingAssembly.GetName();
                    if (callingAssembly != Assembly.GetExecutingAssembly())
                        evidence = callingAssembly.Evidence;
                }
                catch (SecurityException)
                {
                }
                throw new SecurityException(
                            message,
                            assemblyName,
                            null,
                            null,
                            null,
                            SecurityAction.Demand,
                            this,
                            this,
                            evidence);
            }
            public IPermission Intersect(IPermission target)
            {
                var targetPermission = target as AuthorizationPermission;
                if (targetPermission == null)
                    return null;
                if (IsUnrestricted())
                    return targetPermission.Copy();
                if (targetPermission.IsUnrestricted())
                    return Copy();
                var intersection = _activityIdentifiers.Intersect(targetPermission._activityIdentifiers, StringComparer.OrdinalIgnoreCase).ToList();
                if (!intersection.Any())
                    return null;
                var result = new AuthorizationPermission
                {
                    _activityIdentifiers = intersection,
                };
                return result;
            }
            public IPermission Union(IPermission target)
            {
                var targetPermission = target as AuthorizationPermission;
                if (targetPermission == null || IsUnrestricted())
                    return Copy();
                if (targetPermission.IsUnrestricted())
                    return targetPermission.Copy();
                var result = new AuthorizationPermission
                {
                    _activityIdentifiers = _activityIdentifiers.Union(targetPermission._activityIdentifiers, StringComparer.OrdinalIgnoreCase).ToList(),
                };
                return result;
            }
            public bool IsSubsetOf(IPermission target)
            {
                var targetPermission = target as AuthorizationPermission;
                if (targetPermission == null)
                    return false;
                if (targetPermission.IsUnrestricted())
                    return true;
                if (IsUnrestricted())
                    return false;
                var intersection = _activityIdentifiers.Intersect(targetPermission._activityIdentifiers, StringComparer.OrdinalIgnoreCase);
                return _activityIdentifiers.Count() == intersection.Count();
            }
            public void FromXml(SecurityElement e)
            {
                if (e == null)
                    throw new ArgumentNullException("e");
                _activityIdentifiers = new List<string>();
                foreach (var ids in e.Children)
                {
                    var identifiers = ids as SecurityElement;
                    if (identifiers != null &&
                        identifiers.Tag == "activityIdentifiers")
                    {
                        foreach (var id in identifiers.Children)
                        {
                            var identifier = id as SecurityElement;
                            if (identifier != null &&
                                identifier.Tag == "add")
                                _activityIdentifiers.Add(identifier.Text);
                        }
                        break;
                    }
                }
            }
            public SecurityElement ToXml()
            {
                var element = new SecurityElement("IPermission");
                var nameComponents = GetType().AssemblyQualifiedName.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                var className = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", nameComponents[0], nameComponents[1]);
                element.AddAttribute("class", className);
                element.AddAttribute("version", "1");
                var activityIdentifiersElement = new SecurityElement("activityIdentifiers");
                foreach (var id in _activityIdentifiers)
                    activityIdentifiersElement.AddChild(
                        new SecurityElement("add", SecurityElement.Escape(id)));
                element.AddChild(activityIdentifiersElement);
                return element;
            }
            [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                if (info == null)
                    throw new ArgumentNullException("info");
                info.AddValue("activityIdentifiersCount", _activityIdentifiers.Count());
                for (var i=0; i<_activityIdentifiers.Count(); i++)
                    info.AddValue("activityIdentifier_"+i, _activityIdentifiers.ElementAt(i));
            }
        }
    }


    Thanks Val

    Tuesday, May 21, 2013 5:04 PM

Answers

  • The problem seems to lie with the way you build the class attribute value in your ToXml override.  The framework expects the assembly-qualified type name to be provided in a specific format, which your code is not currently doing.  To fix this, change your className variable assignment from

    var nameComponents = GetType().AssemblyQualifiedName.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
    var className = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", nameComponents[0], nameComponents[1]);

    to

    var type = this.GetType();
    var className = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", type.FullName, type.Assembly.FullName);
    

    HTH,

    Nicole

    • Marked as answer by Valo Tuesday, May 28, 2013 7:26 PM
    Friday, May 24, 2013 8:05 PM

All replies

  • Hi Val,

    >>My implementation of Demand returns with no exception and yet the system throws SecurityException with the cryptic message "Failure decoding embedded permission set object."

    What do you mean the Demand work well but the system throws exception?

    Which line fails?

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, May 22, 2013 1:25 PM
    Moderator
  • Mike Feng,

    Thanks for your reply. This is the weird part: no line in my permission class fails byt the .net code access security subsystem doesn't like something in my class and even though Demand and all other called methods in my class return normally (without exception) the runtime throws SecurityException.

    Thanks
    Val


    Thanks Val

    Wednesday, May 22, 2013 7:26 PM
  • Hi Val,

    To reproduce this scenario well, would you like to provide the namespace 

    Authorization.DataContracts;  

    Thanks.


    Mike Feng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, May 23, 2013 12:17 PM
    Moderator
  • Mike,

    Thanks for looking into this!

    As you might expect the AuthorizationPermission above is just the tip of the eisberg. However if you just comment all references to the rest of the system you'll be able to reproduce the problem - I just did it:

    using System;
    ...
    //using CIT.Authorization.DataContracts;
    ...
            public void Demand()
            {
                if (IsUnrestricted())
                    return;
                if (!Thread.CurrentPrincipal.Identity.IsAuthenticated)
                    throw new SecurityException("The principal's identity is not authenticated.", GetType());
                if (!_activityIdentifiers.Any())
                    throw new SecurityException("Access is denied.", GetType());
                //AuthorizationDto auth = null;
                try
                {
                    foreach (var id in _activityIdentifiers)
                    {
                        if (string.IsNullOrWhiteSpace(id))
                            continue;
                        //auth = AuthorizationCache.GetAuthorization(Thread.CurrentPrincipal.Identity.Name, id);
                        //if (auth==null)
                        //    ThrowSecurityException("Access is denied.");
                        //if (!auth.Granted)
                        //    ThrowSecurityException("Access is denied. " + auth.Message);
                    }
                }
                catch (CommunicationException x)
                {
                    throw new SecurityException("Access is denied.", x);
                }
            }

    Note that all calls to the Authorization namespace are commented-out.

    Then I have the following AuthorizationPermissionAttribute:

    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Security;
    using System.Security.Permissions;
    namespace Authorization
    {
        [AttributeUsage(
            AttributeTargets.Class |
            AttributeTargets.Struct |
            AttributeTargets.Interface |
            AttributeTargets.Constructor |
            AttributeTargets.Method |
            AttributeTargets.Property |
            AttributeTargets.Event,
            AllowMultiple=false,
            Inherited=false)]
        [Serializable]
        public sealed class AuthorizationPermissionAttribute : CodeAccessSecurityAttribute
        {
            public AuthorizationPermissionAttribute(SecurityAction action = SecurityAction.Demand)
                : base(action)
            {
            }
            public string Identifier { get; set; }
            public override IPermission CreatePermission()
            {
                if (string.IsNullOrWhiteSpace(Identifier))
                    throw new InvalidOperationException("The property ActivityIdentifier cannot be null, empty or all whitespace characters.");
                return new AuthorizationPermission(Identifier, Unrestricted);
            }
        }
    }

    And in the end I use the following test class:

    using System;
    using Authorization;
    namespace AuthorizationTest
    {
        [AuthorizationPermission(Identifier=
                "urn:AuthorizationDemo:AuthorizationProtectedDemo")]
        class AuthorizationProtectedDemo
        {
            public void GeneralAccessMethod()
            {
                Console.WriteLine("Executed AuthorizationProtectedDemo.GeneralAccessMethod");
            }
            [AuthorizationPermission(Identifier=
                "urn:AuthorizationDemo:AuthorizationProtectedDemo.RestrictedAccessMethod")]
            public void RestrictedAccessMethod()
            {
                Console.WriteLine("Executed AuthorizationProtectedDemo.RestrictedAccessMethod.");
            }
        }
    }

    Note that in the test class call to the method GeneralAccessMethod runs without problems - .NET creates a permission with the help of the attribute and does call the authorization permission for identifier "urn:AuthorizationDemo:AuthorizationProtectedDemo" - no exceptions. For a call to the second method RestrictedAccessMethod the CLR builds a permission which is a union of the two permissions - the one on the class level and the one on the method level. The call to Demand on the union passes fine, without exceptions but after that the runtime throws the SecurityException with the infamous message.

    Thanks Val


    • Edited by Valo Thursday, May 23, 2013 4:12 PM
    Thursday, May 23, 2013 4:10 PM
  • Hi Val,

    Thank you for this valuable update.

    I am involve some other one into this case, it will take some times.

    Thank you for your patient.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, May 24, 2013 3:02 AM
    Moderator
  • Awesome! Thanks a lot! Looking forward to it.

    Thanks Val

    Friday, May 24, 2013 4:39 PM
  • The problem seems to lie with the way you build the class attribute value in your ToXml override.  The framework expects the assembly-qualified type name to be provided in a specific format, which your code is not currently doing.  To fix this, change your className variable assignment from

    var nameComponents = GetType().AssemblyQualifiedName.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
    var className = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", nameComponents[0], nameComponents[1]);

    to

    var type = this.GetType();
    var className = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", type.FullName, type.Assembly.FullName);
    

    HTH,

    Nicole

    • Marked as answer by Valo Tuesday, May 28, 2013 7:26 PM
    Friday, May 24, 2013 8:05 PM
  • Great! This solved it. Actually it seems that the requirement there is to put the full assembly qualified name of the type and I was trying to pass assembly name without the version, culture and strong name signature. The code can be as simple as:
            public SecurityElement ToXml()
            {
                var element = new SecurityElement("IPermission");
                element.AddAttribute("class", GetType().AssemblyQualifiedName);
                element.AddAttribute("version", "1");
    ...

    Thank you very much, Nicole! (marking yor reply as an answer)

    Thanks Val

    Tuesday, May 28, 2013 7:26 PM