Sanitize special characters in AD groups names

Problem this snippet solves:

With APM, when you query Active Directory to retrieve the groups membership, if an AD group contains one or several special characters, the name of the group is considered not printable by APM and therefore is transformed in hex format.

For example, if the name of an AD group is "Comptes_éditeurs" (in french), the APM session variable after AD query will be "session.ad.last.attr.memberOf = 0x436f6d707465735fc3a964697465757273". This is not convenient for usage in the APM policy.

This snippet offers an iRule to transform "not printable" group names into printable group names by replacing all not printable chars by printable ones. Indeed, the previous example "Comptes_éditeurs" will be transformed by this snippet into "Comptes_editeurs", which will be printed properly and can be used as usual in an APM policy.

How to use this snippet:

Installation

irule

To make it works, you need to install the irule on the Virtual Server that publish your application with APM authentication.

datagroup

You need to create a strings datagroup named "dg_special_chars" that contains all the not printable chars you want to replace with their replacement char. The following datagroup will replace "é, è, ê, ë" with the normal "e" :

c3a8 : 65 (è => e)

c3a9 : 65 (é => e)

c3aa : 65 (ê => e)

c3ab : 65 (ë => e)

The original special chars here (keys in the datagroup) are in hex format of UTF-8. You can have a look here http://www.utf8-chartable.de/ to find them.

The replacement chars (values in the datagroup) are in hex format of standard ASCII. You can have a look here in the "ASCII printable characters" table http://www.rapidtables.com/code/text/ascii-table.htm.

For example, if you need to replace "£" with "?", you need the following entry in your datagroup :

c2a3 : 3f

APM Policy

In your APM policy you need to add a bloc "iRule Event" right after you call AD Query and before you test groups membership. In the "iRule Event" bloc, the "Custom iRule Event Agent" needs to be "clean_group_names".

After this iRule Event, the sanitized groups names will be stored in the APM session variable "session.custom.ad.memberOf".

To test groups membership, you can use the following condition in an "Empty" bloc : expr { [mcget {session.custom.ad.memberOf}] contains "CN=MY_GROUP, CN=Users, DC=MY_DOMAIN" }

Code :

when ACCESS_POLICY_AGENT_EVENT {
    if { [ACCESS::policy agent_id] eq "clean_group_names" } {
        set newMemberOf " | "
        set memberOf [ACCESS::session data get "session.ad.last.attr.memberOf"]
        set splited [split $memberOf "|"]
        # Loop through all groups
        foreach field $splited {
            # If the group starts with 0x, it is hexa, needs to be decoded
            if { $field starts_with " 0x" } {
                # remove spaces
                set trimed [string trim $field " "] 
                # skip the 0x at the beginning
                set hex_data [string tolower [substr $trimed 2]] 
                # Loop through all items in datagroup
                foreach item [class names dg_special_chars] { 
                    set new_char [class lookup $item dg_special_chars]
                    # Replace the special char with a "normal" char
                    regsub -all $item $hex_data $new_char hex_data
                }
                # Decode the hexa without special chars to string
                set groupStr [binary format H* $hex_data]
                # Concat the sanitize group name to the list
                set newMemberOf [concat $newMemberOf $groupStr " | "]
            # The group is not hexa, just concat the value as it is
            } elseif { $field ne "" } {
                set newMemberOf [concat $newMemberOf $field " | "]
            }
        }
        # Store the sanitize memberOf into a new session var
        ACCESS::session data set "session.custom.ad.memberOf" $newMemberOf
    }
}

Tested this on version:

12.1
Published Apr 24, 2017
Version 1.0
  • Hi,

     

    you can use string map instead of foreach / regsub...

     

    when RULE_INIT {
    set static::conversion_table {c3a8 65 c3a9 65 c3aa 65 c3ab 65 c2a3 3f}
    }
    
    when ACCESS_POLICY_AGENT_EVENT {
        if { [ACCESS::policy agent_id] eq "clean_group_names" } {
            set newMemberOf " | "
            set memberOf [ACCESS::session data get "session.ad.last.attr.memberOf"]
            set splited [split $memberOf "|"]
             Loop through all groups
            foreach field $splited {
                 If the group starts with 0x, it is hexa, needs to be decoded
                if { $field starts_with " 0x" } {
                     remove spaces
                    set trimed [string trim $field " "] 
                     skip the 0x at the beginning
                    set hex_data [string tolower [substr $trimed 2]] 
                    set hex_data [string map $static::conversion_table $hex_data]
                     Decode the hexa without special chars to string
                    set groupStr [binary format H* $hex_data]
                     Concat the sanitize group name to the list
                    set newMemberOf [concat $newMemberOf $groupStr " | "]
                 The group is not hexa, just concat the value as it is
                } elseif { $field ne "" } {
                    set newMemberOf [concat $newMemberOf $field " | "]
                }
            }
             Store the sanitize memberOf into a new session var
            ACCESS::session data set "session.custom.ad.memberOf" $newMemberOf
        }
    }
    

    I think you can also do it in variable assign instead of irule event...

     

    you can try this code :

     

    session.ad.last.attr.memberOf =

     

    set conversion_table {c3a8 65 c3a9 65 c3aa 65 c3ab 65 c2a3 3f}
    if { [info exists "groups"] }{unset groups;};
    foreach field [mcget {session.ad.last.attr.memberOf}] {
        if { $field starts_with " 0x" } {
            set hex_data [string map $conversion_table [string range $field 2 end]];
            set groupStr [binary format H* $hex_data];
            lappend groups $groupStr;
        } else { lappend groups $field;};
    };
    unset -nocomplain conversion_table;
    return $groups
    
  • Hi Stanislas,

     

    Thank you for your feedback. Your proposal is a nice alternative. As far as I am concerned, I prefer keeping a datagroup to manage my replacement table.

     

  • How many entries did you configure in your Datagroup?

     

    Your irule is not optimized as you are working with loop on DG names. the string map command is the fastest solution to manage string replacement.

     

  • Yes you're right, string map is a faster way. However, this irule will be trigger only during the login phase, so not that often. Performance does not really matter for me here. It was choice I made to give priority to clarity and maintenance over performance in that case.