UserPermissionCache, WindowsIdentity, Serialization

Sep 17, 2009 at 1:24 AM
Edited Sep 17, 2009 at 1:31 AM

Hi,

I am getting an exception when passing a UserPermissionCache instance accross AppDomain boundaries. I have a service that creates the UserPermissionCache and attempts to pass it back to a client process.

I think this is related to the following problem:

UserPermissionCache requires a WindowsIdentity instance for construction, so the service attempts to create a WindowsIdentity for the user from an ItnPtr token passed to the service. However, the line

WindowsIdentity id = new WindowsIdentity(token);  

thows this exception:

Invalid token for impersonation - it cannot be duplicated.

I'm guessing that WindowsIdentity is not serializable because of the IntPtr Token ...

If this is correct then I was wondering: how do we construct a UserPermissionCache such that the UserPermissionCache instance can be passed accross AppDomain boundaries/processes?

Thanks in advance...

Coordinator
Sep 17, 2009 at 6:52 AM

Hi,

have you tried to use Kerberos Protocol Transition instead of IntPtr ?

WindowsIdentity win = new WindowsIdentity(otherusername@myupn.ext);

Regards,

Andrea.

__________________________________
Andrea Ferendeles
NetSqlAzMan Project Coordinator  
E-mail aferende@hotmail.com Web http://netsqlazman.codeplex.com

Sep 23, 2009 at 4:01 AM
Edited Sep 23, 2009 at 4:46 AM

Hi Aundrea, 

Thanks for helping. 

I've been chasing this up for the past couple of days but it turns out our system is not configured for Kerberos Protocol Transition. 

So I'm setting the windowsIdentity field to null by reflection like this : 

        private static void NullWindowsIdentity(UserPermissionCache permissionCache)

        {

            Type t = permissionCache.GetType();

            FieldInfo f = t.GetField("windowsIdentity", BindingFlags.NonPublic | BindingFlags.Instance);

            f.SetValue(permissionCache, null);

        } 

which fixes the problem. 

Once constructed the UserPermissionCache doesn’t depend on the windowsIdentity field so I set that field to null and it stays null, and there's no problem. 

I will continue pursuing the correct solution (the Kerberos Protocol Transition solution)...but for the meantime I have to stick to this improvised solution.  

Thanks again Andrea,

Neo 

(Oh, and by the way, NetSqlAzMan is a Kick-Ass framework !!)

 

Sep 25, 2009 at 12:36 AM

Hello Andrea

1st. Your recommendation to create a WindowsIdentity I believe does not work while developing on my XP machine? am I correct? if so, what is the alternative during development?

2nd. Also could you point me to the code example where I can retrieve all the operations for a given user. I saw that somewhere but now I could not find it.

3rd. I am utilizing n-tier so the credentials have to cross several boundaries before querying the authorization store; this is why I need to create the WindowsIdentity right before using the API. Would NetSqlAzman will be a good alternative for this? and how would you recommend the implementation under an n-tire architrecture? I am planning on putting the authorization store behind our WCF Security service that is IIS hosted.

Thanks in advance

Cordially,

Luis

 

 

 

Coordinator
Sep 25, 2009 at 8:31 AM

Hi,

(inline answers).

1st. Your recommendation to create a WindowsIdentity I believe does not work while developing on my XP machine? am I correct? if so, what is the alternative during development?

YES. Kerberos Protocol Transition works only on 2003 or later.
You can use:
1) Your identity (WIndowsIdentity.GetCurrent())
2) A simple non-anonymous Web Services that returns you WindowsIdentity.GetUserSid() and WindowsIdentity.GetUserGroupsSid() (do not forget using NetSqlAzMan.Cache directive)

2nd. Also could you point me to the code example where I can retrieve all the operations for a given user. I saw that somewhere but now I could not find it.
Just invoke the WCF Cache Service Method called
GetAuthorizedItems():
[OperationContract(Name="GetAuthorizedItemsForDatabaseUsers")]

      AuthorizedItem[] GetAuthorizedItems(string storeName, string applicationName, string DBuserSSid, DateTime validFor, params KeyValuePair<string, object>[] contextParameters);

      [OperationContract(Name = "GetAuthorizedItemsForWindowsUsers")]
      AuthorizedItem[] GetAuthorizedItems(string storeName, string applicationName, string userSSid, string[] groupsSSid, DateTime validFor, params KeyValuePair<string, object>[] contextParameters);

3rd. I am utilizing n-tier so the credentials have to cross several boundaries before querying the authorization store; this is why I need to create the WindowsIdentity right before using the API. Would NetSqlAzman will be a good alternative for this? and how would you recommend the implementation under an n-tire architrecture? I am planning on putting the authorization store behind our WCF Security service that is IIS hosted.
Use the NetSqqlAzMan WCF Cache Service (also under IIS) but with Anonymous Authentication Disabled (you have so the WindowsIdentity of the caller user).

__________________________________
Andrea Ferendeles
NetSqlAzMan Project Coordinator  
E-mail aferende@hotmail.com Web http://netsqlazman.codeplex.com

Oct 1, 2009 at 6:09 PM

Hello Andrea,

Thanks for all the information, I have been able to move forward quite a bit. The nex issue has to do with DBusers and DBUserSID. I have issuess getting the operations from the store for a database user, this is probably because of my little knowledge about SIDs, but I imagine it will lhelp everybody if you provide us a little primer on how to go about it. Here is what I have done.

1st. Lets say that I create some ids on your dabase as follows.

 mClientSecurityDset.UsersDemo.AcceptChanges();
mClientSecurityDset.UsersDemo.AddUsersDemoRow("mediabuyuser", encoding.GetBytes("mediabuyuser"), "mediabuyuser", string.Empty, null);
mClientSecurityDset.UsersDemo.AddUsersDemoRow("mediatrafficuser", encoding.GetBytes("mediatrafficuser"), "mediatrafficuser", string.Empty, null);
mClientSecurityDset.UsersDemo.AddUsersDemoRow("customeruser1", encoding.GetBytes("customeruser1"), "customeruser1", string.Empty, null);
mClientSecurityDset.UsersDemo.AddUsersDemoRow("customeruser2", encoding.GetBytes("customeruser2"), "customeruser2", string.Empty, null);
dbUsersAdapter.Update(mClientSecurityDset.UsersDemo);

2nd. IDENTIFYING: The last column is a read onlycolumn that does nto exist in the databse and the selets reads it as  CONVERT(VARBINARY(85), UserID) AS DBUserSid, for example:

SELECT     UserID, UserName, Password, FullName, OtherFields, CONVERT(VARBINARY(85), UserID) AS DBUserSid
FROM         UsersDemo
WHERE     (UserName = @UserName)

I have an IdentityRiow with some information that I keep for later user like:

mUserIdentityRow.IsDbUser = true

3rd. AUTHENTICATING: I authenticate the user similar to step 2. but also providing the password. Here I preserve the DBUSerID as follows:

// - Authenticated as DbUser
userData = dbUsersAdapter.GetDataByCredentials(userLifeCredentials.UserName, pswByteArray);
if (userData != null && userData.Count > 0)
{
       mUserIdentityRow.IsAuthenticated = true;
       mUserIdentityRow.UseAnonymous = false;
       mUserIdentityRow.UserSid = userData[0].DBUserSid;
(**)   SecurityIdentifier SID2 = new SecurityIdentifier(userData[0].DBUserSid,0);
       mUserIdentityRow.UserSidValue = SID2.Value;
}

=======================================================================================
The important falues are the mUserIdentityRow.UserSid and mUserIdentityRow.UserSidValue.
----------------------------------------------------------------------------------------
 *** I am wondering if I am storing the information correctly? ***
------------------------------------------------------------------
The exeptuion is happening in the line marked as (**):
"Destination array is not long enough to copy all the required data. Check array length and offset.\r\nParameter name: binaryForm"
------------------------------------------------------------------
UserData[0].DBUserSid is a {byte(4)} with the following values
[0] = 0
[1] = 0
[2] = 0
[3] = 11
=======================================================================================

4th. AUTHORIZATION: Getting the authorization operations (I imagine this will work since it is almost identical to then non db user case and that one is working)

<font size="2">

 

</font>

if (mUserIdentityRow.IsDbUser)
    authorizedItems =
        storageCache.GetAuthorizedItems("RRCAzmanStore", mUserLifeCredentials.ApplicationName, mUserIdentityRow.UserSidValue, DateTime.Now, null);
 

Thanks again

Cordially,

Luis

 

 

 

 

 

 

Oct 2, 2009 at 1:12 AM

Hello Andrea

I got it working wilth a workaround utilizing information you provided in a different discussion., but I sure hope not to have to do this extra step.

So I am skiping the error line during Authentication (step 3 above)  and during Authorization (step 4 above) I am doing the following

 if (mUserIdentityRow.IsDbUser)
{
    mUserIdentityRow.UserSidValue = storage.GetDBUser(mUserLifeCredentials.UserName).CustomSid.StringValue; // this is the extra step ***
     authorizedItems =
         storageCache.GetAuthorizedItems("RRCAzmanStore", mUserLifeCredentials.ApplicationName, mUserIdentityRow.UserSidValue, DateTime.Now, null);

}

But I sure hope to find out what is wrong with the the code I provided before.

Cordially,

Luis

 

 

Coordinator
Oct 2, 2009 at 7:30 AM

Hi Luis,

DB User do not have SecurityIdentifier as custom SIDs but GUIDs (System.Guid).

So just replace you line:

SecurityIdentifier SID2 = new SecurityIdentifier(userData[0].DBUserSid,0);

with this one:

Guid SID2 = new Guid(userData[0].DBUserSid);

Regards,

Andrea.

__________________________________
Andrea Ferendeles
NetSqlAzMan Project Coordinator  
E-mail aferende@hotmail.com Web http://netsqlazman.codeplex.com

Coordinator
Oct 2, 2009 at 7:32 AM

This line works …

mUserIdentityRow.UserSidValue = storage.GetDBUser(mUserLifeCredentials.UserName).CustomSid.StringValue; // this is the extra step ***

Becase GetDBUser function returns a Guid.ToString();

__________________________________
Andrea Ferendeles
NetSqlAzMan Project Coordinator  
E-mail aferende@hotmail.com Web http://netsqlazman.codeplex.com

Oct 4, 2009 at 8:28 PM

Thanks Andrea

I will try that.

Cordially,

Luis