Hello Alan & FreeRadius Team, I am using FreeRadius with Google Secure LDAP & WPA2 Enterprise Auth and it's working great! I have a new requirement to integrate FreeRadius and pfSense for Admin/UI login and VPN authentication. I have successfully configured pfSense and FreeRadius for user authentication, but I am trying to return the user's LDAP group membership in a semi-colon (;) delimited reply:Class attribute as required by pfSense (see https://docs.netgate.com/pfsense/en/latest/usermanager/radius.html). I have managed to return the LDAP memberOf values to the default site post-auth section, and using unlang foreach I have successfully created a comma (,) delimited reply:Class attribute. The issue is pfSense requires the reply:Class list to be semi-colon delimited, and I don't see an obvious way of changing the delimiter used by %{Attr-Name[*]}. Is there a way to create a semi-colon (;) delimiter list from a multi valued attribute? Thank you in advance. This is my default site post-auth: foreach &reply:ldapMemberOf { if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { update reply { classMemberOf += "%{1}" } } } update reply { Class = "%{reply:classMemberOf[*]}" Please note, 'classMemberOf' and 'ldapMemberOf' are non response attributes (3000 & 3001) I added to the dictionary. The ldap module populates 'ldapMemberOf' with the value of the users 'memberOf' in the ldap.update section: reply:ldapMemberOf += 'memberOf' Here's the relevant server debug; note user has 5 assigned groups: (0) Auth-Type LDAP { rlm_ldap (ldap): Reserved connection (1) (0) ldap: Login attempt by "darnold" (0) ldap: Using user DN from request "uid=darnold,ou=District Office,ou=Users,dc=valleychristianschools,dc=org" (0) ldap: Waiting for bind result... (0) ldap: Bind successful (0) ldap: Bind as user "uid=darnold,ou=District Office,ou=Users,dc=valleychristianschools,dc=org" was successful rlm_ldap (ldap): Released connection (1) Need 4 more connections to reach 10 spares rlm_ldap (ldap): Opening additional connection (6), 1 of 94 pending slots used rlm_ldap (ldap): Connecting to ldaps://ldap.google.com:636 rlm_ldap (ldap): Waiting for bind result... ber_get_next failed. rlm_ldap (ldap): Bind successful (0) [ldap] = ok (0) } # Auth-Type LDAP = ok (0) # Executing section post-auth from file /etc/freeradius/3.0/sites-enabled/default (0) post-auth { (0) if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) { (0) if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) -> FALSE (0) update { (0) No attributes updated for RHS &session-state: (0) } # update = noop (0) [exec] = noop (0) policy remove_reply_message_if_eap { (0) if (&reply:EAP-Message && &reply:Reply-Message) { (0) if (&reply:EAP-Message && &reply:Reply-Message) -> FALSE (0) else { (0) [noop] = noop (0) } # else = noop (0) } # policy remove_reply_message_if_eap = noop (0) foreach &reply:ldapMemberOf (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) EXPAND Foreach-Variable-0 (0) --> cn=allstaff,ou=Groups,dc=valleychristianschools,dc=org (0) EXPAND %{Foreach-Variable-0} (0) --> cn=allstaff,ou=Groups,dc=valleychristianschools,dc=org (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) -> TRUE (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) update reply { (0) EXPAND %{1} (0) --> allstaff (0) classMemberOf += allstaff (0) } # update reply = noop (0) } # if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) = noop (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) EXPAND Foreach-Variable-0 (0) --> cn=firewalladmins,ou=Groups,dc=valleychristianschools,dc=org (0) EXPAND %{Foreach-Variable-0} (0) --> cn=firewalladmins,ou=Groups,dc=valleychristianschools,dc=org (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) -> TRUE (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) update reply { (0) EXPAND %{1} (0) --> firewalladmins (0) classMemberOf += firewalladmins (0) } # update reply = noop (0) } # if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) = noop (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) EXPAND Foreach-Variable-0 (0) --> cn=support1,ou=Groups,dc=valleychristianschools,dc=org (0) EXPAND %{Foreach-Variable-0} (0) --> cn=support1,ou=Groups,dc=valleychristianschools,dc=org (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) -> TRUE (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) update reply { (0) EXPAND %{1} (0) --> support1 (0) classMemberOf += support1 (0) } # update reply = noop (0) } # if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) = noop (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) EXPAND Foreach-Variable-0 (0) --> cn=support2,ou=Groups,dc=valleychristianschools,dc=org (0) EXPAND %{Foreach-Variable-0} (0) --> cn=support2,ou=Groups,dc=valleychristianschools,dc=org (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) -> TRUE (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) update reply { (0) EXPAND %{1} (0) --> support2 (0) classMemberOf += support2 (0) } # update reply = noop (0) } # if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) = noop (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) EXPAND Foreach-Variable-0 (0) --> cn=support,ou=Groups,dc=valleychristianschools,dc=org (0) EXPAND %{Foreach-Variable-0} (0) --> cn=support,ou=Groups,dc=valleychristianschools,dc=org (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) -> TRUE (0) if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) { (0) update reply { (0) EXPAND %{1} (0) --> support (0) classMemberOf += support (0) } # update reply = noop (0) } # if ("%{Foreach-Variable-0}" =~ /cn=([^,=]+)/) = noop (0) } # foreach &reply:ldapMemberOf = noop (0) update reply { (0) EXPAND %{reply:classMemberOf[*]} (0) --> allstaff,firewalladmins,support1,support2,support (0) Class = 0x616c6c73746166662c6669726577616c6c61646d696e732c737570706f7274312c737570706f7274322c737570706f7274 (0) } # update reply = noop (0) } # post-auth = noop (0) Sent Access-Accept Id 130 from 10.4.0.40:1812 to 10.4.0.1:45899 length 0 (0) Class = 0x616c6c73746166662c6669726577616c6c61646d696e732c737570706f7274312c737570706f7274322c737570706f7274