Monday, September 08, 2008

Restricting Citrix users on the CSG

I have a need to restrict users who can access the Citrix Web Interface (v 4.6) from outside the company, yet allow everyone to access it from inside. Some searching turned up several references to people restricting access based on username or group membership (such as http://www.jasonconger.com/Controlling-Access-to-Web-Interface-using-Web-Interface-Access-Control-Center.aspx), but nobody looked at how the site was being accessed. My first thought was to setup 2 web interfaces (one internal and one external) and use one of these functions, but I needed other customizations and I didn't like the idea of having to keep them in sync.

A little searching in the out of the box code and I realized there is an existing function called isUsingSecureGateway() that reports if the connection is being proxied via the Citrix Secure Gateway, this is exactly what I needed. Combine this function with examples from others and I have it all.

Step 1: Setup the code

I created a new file named /App_Data/wiCustomizations/CSGRestrict.aspxf to house the interface customizations. This file will contain the functions to check group membership and to search for membership within the group. The key here is to configure the variable groupsPermitted with the names of the security groups to allow access. The groupsPermitted is comma delimited (i.e. add multiple groups with commas in between) and are compared via regular expressions; this means that wildcards can be used to permit users. This file is stored as text on the web server and can easily be edited at any time to add/remove groups.

The second item to notice is the LDAP call needs a username and password to access active directory. Unless you have configured IIS to run under a domain account (not suggested) then you will need to add in a username here. Make sure this account has limited rights since the password will be stored in clear text and anyone with access to the server will have access to the password.

public
bool CSGAllow(string username, string domain)

{


string groupsPermitted = "Domain Admins,.*csgaccess";



bool bAllowed = MemberOf(username, domain, groupsPermitted);


return bAllowed;

}

private
bool MemberOf(string userName, string domainName, string groupNames)

{


bool retVal = false;

System.DirectoryServices.DirectoryEntry dirEntry = new System.DirectoryServices.DirectoryEntry("LDAP://" + domainName, "username", "password");

System.DirectoryServices.DirectorySearcher dirSearcher = new System.DirectoryServices.DirectorySearcher(dirEntry);

dirSearcher.Filter = string.Format("(sAMAccountName={0})", userName);

dirSearcher.PropertiesToLoad.Add("memberOf");



int propCount = 0;

System.DirectoryServices.SearchResult dirSearchResult = dirSearcher.FindOne();

propCount = dirSearchResult.Properties["memberOf"].Count;


for (int i = 0; i < propCount - 1; i++)

{


string member = dirSearchResult.Properties["memberOf"][i].ToString();


foreach (string strGroup in groupNames.Split(",".ToCharArray()))

{

System.Text.RegularExpressions.Regex myPattern = new System.Text.RegularExpressions.Regex("cn=" + strGroup + ".*,", System.Text.RegularExpressions.RegexOptions.IgnoreCase);


if (myPattern.IsMatch(member))


return
true;

}

}


return
false;

}


Step 2: Link into the login process

Two files need to be edited here – the first is /auth/login.aspx to reference our customized code

<!--#include file="~/app_data/wiCustomizations/serverscripts/csgRestrict.aspxf"-->

The second file to edit is /App_Data/auth/serverscripts/login.aspxf to initiate the security group check. The inserted code is highlighted below.

// Make sure none of the credential fields contain control characters

if( Strings.hasControlChars( user )

|| Strings.hasControlChars( password )

|| Strings.hasControlChars( domain )

|| Strings.hasControlChars( context )

|| Strings.hasControlChars( passcode ) ) {

messageCenterControl.setType( MessageCenterControl.MSGTYPE_ERROR );

messageCenterControl.setKey( "InvalidCredentials" );

} else
if (expAuth is ExplicitNDSAuth) {


// Defer to special NDS processing code that handles context lookup

result = loginAuthenticateNDS((ExplicitNDSAuth) expAuth, user, password, passcode, context);

}

else
if (isUsingSecureGateway() && !CSGAllow(user, domain))

{

messageCenterControl.setType(MessageCenterControl.MSGTYPE_ERROR);

messageCenterControl.setKey("NoCSGAccess");

}

else

{


// Not NDS - parse the fields and go do any Two-factor

ExplicitUDPAuth udpAuth = (ExplicitUDPAuth)expAuth;


Step 3: Define a custom error message

The final step is to setup a custom error message alerting users they don't have access via the CSG. Using the Citrix article http://support.citrix.com/article/CTX107490 as guidance I copied the common_strings.properties file to the languages folder in the web interface. I then edited the file and added the below line, ensuring the variable NoCSGAccess matches the string that is called from login.aspxf above.

NoCSGAccess=This account has not been approved for accessing the Web Interface via the CSG


2 comments:

Anonymous said...
This comment has been removed by a blog administrator.
Anonymous said...

hi,
is this steps valid for WI 5.4 as well?
thanks.