3.0.x: check regex against multiple instances of attribute
Hello, currently setting up 3.0 with one of our eduroam participants here in Luxembourg. They have an AD backend, and auth via ntlm_auth works fine. Now, they also want to extract group membership attributes and assign users to VLANs as appropriate. The users can be members of multiple groups and there is an LDAP attribute "memberOf" which spits out all applicable groups in sequence. I've used the ldap modules in authorize to do that, and add the groups with +=. This seems to work (debug snippet below). Now I'd like to use unlang to check "if one of the values of those attributes matches regex foo, add the user to VLANx. I used to think there was a special comparison operator for exactly that purpose, but I can't for the life of me find it in documentation. What I've tried is if ( control:Member-Check =~ /regex/ ) but that apparently checks only the first occurence of the control item; I.e. it returns true if the regex is contained in the first instance, but not if it is contained only in a non-first instance. I guess I'm missing something fairly obvious? A typical debug is this: Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : Waiting for search result... Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : User object found at DN "CN=My User,OU=Test,OU=Users,DC=mydomain,DC=com" Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : Processing user attributes Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group1,OU=SecurityGroupsManagedCentrally,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group2,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group3,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group4,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group5,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=VLAN-Students,OU=Network,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group6,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group7,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: rlm_ldap (ldap): Released connection (4) Mon Jul 7 11:21:14 2014 : Debug: (7) modsingle[authorize]: returned from ldap (rlm_ldap) for request 7 Mon Jul 7 11:21:14 2014 : Debug: (7) [ldap] = ok Mon Jul 7 11:21:14 2014 : Debug: (7) if ( control:Member-Check =~ /Student/ ) Mon Jul 7 11:21:14 2014 : Debug: (7) if ( control:Member-Check =~ /Student/ ) -> FALSE Mon Jul 7 11:21:14 2014 : Debug: (7) modsingle[authorize]: calling pap (rlm_pap) for request 7 Mon Jul 7 11:21:14 2014 : Debug: (7) modsingle[authorize]: returned from pap (rlm_pap) for request 7 Mon Jul 7 11:21:14 2014 : Debug: (7) [pap] = noop Mon Jul 7 11:21:14 2014 : Debug: (7) } # authorize = updated The regex "Student" should match the sixth instance of the control items, but doesn't. If someone could lend me a hint ... Stefan -- Stefan WINTER Ingenieur de Recherche Fondation RESTENA - Réseau Téléinformatique de l'Education Nationale et de la Recherche 6, rue Richard Coudenhove-Kalergi L-1359 Luxembourg Tel: +352 424409 1 Fax: +352 422473 PGP key updated to 4096 Bit RSA - I will encrypt all mails if the recipient's key is known to me http://pgp.mit.edu:11371/pks/lookup?op=get&search=0xC0DE6A358A39DC66
On 07-07-14 13:42, Stefan Winter wrote:
Hello,
currently setting up 3.0 with one of our eduroam participants here in Luxembourg.
They have an AD backend, and auth via ntlm_auth works fine.
Now, they also want to extract group membership attributes and assign users to VLANs as appropriate.
The users can be members of multiple groups and there is an LDAP attribute "memberOf" which spits out all applicable groups in sequence. I've used the ldap modules in authorize to do that, and add the groups with +=. This seems to work (debug snippet below).
Now I'd like to use unlang to check "if one of the values of those attributes matches regex foo, add the user to VLANx.
I used to think there was a special comparison operator for exactly that purpose, but I can't for the life of me find it in documentation.
What I've tried is
if ( control:Member-Check =~ /regex/ ) but that apparently checks only the first occurence of the control item; I.e. it returns true if the regex is contained in the first instance, but not if it is contained only in a non-first instance.
I guess I'm missing something fairly obvious?
A typical debug is this:
Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : Waiting for search result... Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : User object found at DN "CN=My User,OU=Test,OU=Users,DC=mydomain,DC=com" Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : Processing user attributes Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group1,OU=SecurityGroupsManagedCentrally,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group2,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group3,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group4,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group5,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=VLAN-Students,OU=Network,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group6,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: (7) ldap : control:Member-Check += ''CN=Group7,OU=SecurityGroups,OU=Groups,DC=mydomain,DC=com'' Mon Jul 7 11:21:14 2014 : Debug: rlm_ldap (ldap): Released connection (4) Mon Jul 7 11:21:14 2014 : Debug: (7) modsingle[authorize]: returned from ldap (rlm_ldap) for request 7 Mon Jul 7 11:21:14 2014 : Debug: (7) [ldap] = ok Mon Jul 7 11:21:14 2014 : Debug: (7) if ( control:Member-Check =~ /Student/ ) Mon Jul 7 11:21:14 2014 : Debug: (7) if ( control:Member-Check =~ /Student/ ) -> FALSE Mon Jul 7 11:21:14 2014 : Debug: (7) modsingle[authorize]: calling pap (rlm_pap) for request 7 Mon Jul 7 11:21:14 2014 : Debug: (7) modsingle[authorize]: returned from pap (rlm_pap) for request 7 Mon Jul 7 11:21:14 2014 : Debug: (7) [pap] = noop Mon Jul 7 11:21:14 2014 : Debug: (7) } # authorize = updated
The regex "Student" should match the sixth instance of the control items, but doesn't.
If someone could lend me a hint ...
You can use the foreach-keyword. It is documented in man unlang(5), but maybe a short snippet (straight from the unit tests) is clearer: # Expanded regex foreach Cisco-AVPair { if ("%{Foreach-Variable-0}" =~ /^%{Tmp-String-0}=(.*)$/i) { update reply { Called-Station-Id += "%{1}" } } } # Compiled regex foreach Cisco-AVPair { if ("%{Foreach-Variable-0}" =~ /^stupid=(.*)$/i) { update reply { Called-Station-Id += "%{1}" } } } -- Herwin Weststrate
On 07/07/14 12:42, Stefan Winter wrote:
I used to think there was a special comparison operator for exactly that purpose, but I can't for the life of me find it in documentation.
=~ used to do this i.e. loop over the attributes, but no longer does; I think it got lost in the great unlang rewrite. You'll have to foreach over the attribute then regexp-compare against "%{Foreach-Variable-X}" - you do need to string-ify it, as it's not a real attribute.
Hi,
=~ used to do this i.e. loop over the attributes, but no longer does; I think it got lost in the great unlang rewrite.
You'll have to foreach over the attribute then regexp-compare against "%{Foreach-Variable-X}" - you do need to string-ify it, as it's not a real attribute.
cant you also do that 'pulling magical blob that lists all groups in one call' method too? (cant recall the thread on this list that has the example but i'm sure there was plenty of interest/excitement about it!) alan
Hi,
=~ used to do this i.e. loop over the attributes, but no longer does; I think it got lost in the great unlang rewrite.
You'll have to foreach over the attribute then regexp-compare against "%{Foreach-Variable-X}" - you do need to string-ify it, as it's not a real attribute.
cant you also do that 'pulling magical blob that lists all groups in one call' method too? (cant recall the thread on this list that has the example but i'm sure there was plenty of interest/excitement about it!)
That's the one which concat's all the values into one "huge" attribute, and you can regex the part you want out of it? Well the people there can be members of up to some 50 groups, all having a 100+ character DN. I know that this busted FreeRADIUS 2.x max attribute length limit, and I guess it might also be too much for the (2000 char'ish) limit in 3.x. The foreach however does its job nicely :-) Thanks for the quick suggestions! Greetings, Stefan Winter -- Stefan WINTER Ingenieur de Recherche Fondation RESTENA - Réseau Téléinformatique de l'Education Nationale et de la Recherche 6, rue Richard Coudenhove-Kalergi L-1359 Luxembourg Tel: +352 424409 1 Fax: +352 422473 PGP key updated to 4096 Bit RSA - I will encrypt all mails if the recipient's key is known to me http://pgp.mit.edu:11371/pks/lookup?op=get&search=0xC0DE6A358A39DC66
That's the one which concat's all the values into one "huge" attribute, and you can regex the part you want out of it?
I vaguely recall something like this... but for the life of me can't remember what it is. Was it perchance "%{Attribute-Name[*]}"? Stefan Janet(UK) is a trading name of Jisc Collections and Janet Limited, a not-for-profit company which is registered in England under No. 2881024 and whose Registered Office is at Lumen House, Library Avenue, Harwell Oxford, Didcot, Oxfordshire. OX11 0SG. VAT No. 614944238
On 8 Jul 2014, at 04:25, Stefan Paetow <Stefan.Paetow@ja.net> wrote:
That's the one which concat's all the values into one "huge" attribute, and you can regex the part you want out of it?
I vaguely recall something like this... but for the life of me can't remember what it is. Was it perchance "%{Attribute-Name[*]}"?
Yes Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team FD31 3077 42EC 7FCD 32FE 5EE2 56CF 27F9 30A8 CAA2
Phil Mayers wrote:
=~ used to do this i.e. loop over the attributes, but no longer does; I think it got lost in the great unlang rewrite.
It should behave like the other comparison attributes. So that's either consistent and correct, or inconsistent and wrong.
You'll have to foreach over the attribute then regexp-compare against "%{Foreach-Variable-X}" - you do need to string-ify it, as it's not a real attribute.
I've tried to figure out how to fix that... it's not obvious. Maybe some bizarre internal hacks would do it. Alan DeKok.
On 7 Jul 2014, at 08:42, Alan DeKok <aland@deployingradius.com> wrote:
Phil Mayers wrote:
=~ used to do this i.e. loop over the attributes, but no longer does; I think it got lost in the great unlang rewrite.
It should behave like the other comparison attributes. So that's either consistent and correct, or inconsistent and wrong.
That was bizarre behaviour and very wrong.
You'll have to foreach over the attribute then regexp-compare against "%{Foreach-Variable-X}" - you do need to string-ify it, as it's not a real attribute.
I've tried to figure out how to fix that... it's not obvious. Maybe some bizarre internal hacks would do it.
Could probably use the same mechanism as the ldap groupcmp stuff. Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team FD31 3077 42EC 7FCD 32FE 5EE2 56CF 27F9 30A8 CAA2
On 7 Jul 2014, at 10:09, Arran Cudbard-Bell <a.cudbardb@freeradius.org> wrote:
On 7 Jul 2014, at 08:42, Alan DeKok <aland@deployingradius.com> wrote:
Phil Mayers wrote:
=~ used to do this i.e. loop over the attributes, but no longer does; I think it got lost in the great unlang rewrite.
It should behave like the other comparison attributes. So that's either consistent and correct, or inconsistent and wrong.
That was bizarre behaviour and very wrong.
You'll have to foreach over the attribute then regexp-compare against "%{Foreach-Variable-X}" - you do need to string-ify it, as it's not a real attribute.
I've tried to figure out how to fix that... it's not obvious. Maybe some bizarre internal hacks would do it.
Could probably use the same mechanism as the ldap groupcmp stuff.
OK just checked the code to make sure. If you turn on group name caching, regular expressions will work fine for group checks. Just looking through the dynamic comparison code (the stuff that gets run if caching hasn't been enabled), and it should be fairly trivial to add regex comparisons there too. There should probably be some warnings sprinkled around to say what operators are supported for group comparisons. Arran Cudbard-Bell <a.cudbardb@freeradius.org> FreeRADIUS Development Team FD31 3077 42EC 7FCD 32FE 5EE2 56CF 27F9 30A8 CAA2
On 07/07/2014 15:09, Arran Cudbard-Bell wrote:
On 7 Jul 2014, at 08:42, Alan DeKok <aland@deployingradius.com> wrote:
Phil Mayers wrote:
=~ used to do this i.e. loop over the attributes, but no longer does; I think it got lost in the great unlang rewrite.
It should behave like the other comparison attributes. So that's either consistent and correct, or inconsistent and wrong.
That was bizarre behaviour and very wrong.
Depends what you mean by "wrong". It worked that way for a *long* time in "users" files comparisons (may still for all I know) and was useful for the rare case of things like Cisco-AVPair or other multi-valued attributes, or simulating a loop before foreach existed (clever ordering of the users file and use of Fall-Through). I submitted a patch to make unlang work the same way as "users", which later got superceded by the unlang changes. So it wasn't "wrong" as in "accidental" - it was quite intentional, at least on my part ;o) (In fact the only reason it didn't work in unlang at the time was a clearly wrong use of the internal APIs that string-ified the attribute by name rather than reference IIRC) In retrospect losing this doesn't trouble me; foreach works fine and is explcit rather than magic/implicit, which is almost always better. But it does seem to be a common starting assumption that: if (Attr=="val") ...would match: Attr="foo" Attr="val" ...and if "==" does that, they all should. Having said that, the semantics for the other operators, in particular ">" are troublesome. So on balance I agree it was a weird behaviour we're better off without on all operators in all contexts. Maybe a warning in "debug" mode if someone executes a comparison against a multi-valued attribute? But TBH it's not a common confusion AFACIT.
participants (7)
-
A.L.M.Buxey@lboro.ac.uk -
Alan DeKok -
Arran Cudbard-Bell -
Herwin Weststrate -
Phil Mayers -
Stefan Paetow -
Stefan Winter