1
votes

My problem is as follows: I need a clr udf (in C#) to query with a given ad-usr the ad-group membership

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.DirectoryServices.AccountManagement;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlInt32 check_user_is_part_of_ad_grp(SqlString ad_usr, SqlString ad_grp)
    {
        bool bMemberOf = false;

        // set up domain context
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

        // find the group in question
        GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, ad_grp.ToString());
        UserPrincipal usr = UserPrincipal.FindByIdentity(ctx, ad_usr.ToString());

        if (group != null && usr != null)
        {
            bMemberOf = usr.IsMemberOf(group);
        }

        // Put your code here
        return new SqlInt32 (bMemberOf ? 1 : 0);
    }
}

If I publish the CLR to my SQL Server 2008 (.net 3.5), then I run the udf as follows:

select dbo.check_user_is_part_of_ad_grp('user', 'group')

And I get an error:

Msg 6522, Level 16, State 1, Line 1
A .NET Framework error occurred during execution of user-defined routine or aggregate "check_user_is_part_of_ad_grp":
System.Security.SecurityException: Request for the permission of type 'System.DirectoryServices.DirectoryServicesPermission, System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' failed.
System.Security.SecurityException:
at UserDefinedFunctions.check_user_is_part_of_ad_grp(SqlString ad_usr, SqlString ad_grp)

I set the target framework of my project to 3.5 and the permission level to EXTERNAL_ACCESS. Also the project references (System.DirectoryServices, System.DirectoryServices.AccountManamgement, System.DirectoryServices.Protocols) to EXTERNAL

I appreciate any help

1

1 Answers

0
votes

Most likely all of those Assemblies will need to be set to UNSAFE, especially the three System.DirectoryServices* .NET Framework libraries that you imported. Also, since you are importing unsupported .NET Framework libraries, you will need to set the database to TRUSTWORTHY ON in order to get them to work. Setting a Database to TRUSTWORTHY ON is typically something you want to avoid as it is a security risk, but in this case I do not believe that it can be avoided.

That said, I am not sure that you even need to create this function yourself in SQLCLR. If you are just wanting to know if a Login (Windows Logins only, obviously) belongs to a particular Active Directory group, there is a built-in function that should do that for you. The IS_MEMBER function will indicate if the current Login is a member of the specified Windows group (specified as Domain\Group). The difference in how this function works as opposed to the one that you are creating is that it only works for the current Login; you cannot pass any arbitrary Login into it. BUT, it also doesn't require any of the extra effort and security risks that are a part of this SQLCLR solution. So, something to consider :-).

Comment from O.P. on this answer:

Actually, I need to check an arbitrary Login if it's member of a particular group. I even tried to use a stored proc and `OPENQUERY' with a linked server to ADSI, but this only works as Dynamic SQL since I need to inject group and user.

In that case, just make the Dynamic SQL two layers deep instead of the usual one layer. Something along the lines of:

DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'
  SELECT *
  FROM   OPENQUERY([LinkedServer], N''
             SELECT *
             FROM   someResource
             WHERE  GroupName=N''''' + @Group + N'''''
             AND    ObjectName=N''''' + @Login + N''''';
                   '');
';

PRINT @SQL; -- DEBUG
EXEC (@SQL);

In this approach, the query executing OPENQUERY is Dynamic SQL, but the query given to OPENQUERY to execute is a string literal.