[EXT] Fetching memberOf attribute

Matvey Teplov matvey.teplov at nomios.nl
Thu May 22 14:36:44 UTC 2025


Hi Brian,

Thank you for the pushing to the right direction - I reshaped the ldap group section including filters and it worked like a charm. For the record in mods-available/ldap:

ldap {
    server = 'abc.abc'
    identity = 'CN=ServiceAccount,DC=Users,DC=abc,DC=abc'
    password = '123456789'
    base_dn = 'DC=abc,DC=abc'

    user {
        base_dn = "${..base_dn}"
        filter = "(&(objectClass=user)(sAMAccountName=%{&User-Name}))"
        scope = 'sub'
        uid_attribute = "sAMAccountName"
        attribute {
                      memberOf := "session-state:LDAP-Group"
                }
        }

  group {
    base_dn = "${..base_dn}"
    filter = '(objectClass=group)'
    name_attribute = cn
    # use magic AD search to get all groups
    membership_filter = "(member:1.2.840.113556.1.4.1941:=%{control:Ldap-UserDN})"
    membership_attribute = 'memberOf'
    # enable cache module
    cacheable_dn = 'yes'
    cache_attribute = "${..:instance}-LDAP-Group"
  }

    options {
        chase_referrals = no
        rebind = yes
        ldap_debug = 2147483647
        timeout = 10
        timelimit = 3
        net_timeout = 1
        idle = 60
    }

    tls {
        start_tls = no
    }
}

The LDAP queries are happening in the authorize section after the proxy call (sites-available/default) and will yield a list of groups in the control:ldap-LDAP-Group attribute:

authorize {
    preprocess
    auth_log
    chap
#   suffix

if (&request:User-Name) {
    update session-state {
        Persisted-User-Name := "%{request:User-Name}"
    }
}

    update control {
        Proxy-To-Realm := "radius_server"
    }

        ldap
}

So, when you will be getting it in the post-auth section, you can go over it with the foreach loop:

        foreach &control:ldap-LDAP-Group {
                if ("%{Foreach-Variable-0}" == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") {
                        update reply {
                                Reply-Message := "Authorized as RO user"
                                }
                        update control {
                                Auth-Type := Accept
                                }
                        }

                if ("%{Foreach-Variable-0}" == "CN=Radius_ReadWrite_Group,DC=Groups,DC=abc,DC=abc") {
                        update reply {
                                Reply-Message := "Authorized as RW user"
                                }
                        update control {
                                Auth-Type := Accept
                                }
                        }

                if ("%{Foreach-Variable-0}" == "CN=Radius_Admin_Group,DC=Groups,DC=abc,DC=abc") {
                        update reply {
                                Reply-Message := "Authorized as Admin"
                                }
                        update control {
                                Auth-Type := Accept
                                }
                        }
                }


The problem I still have is how to send the Reject back if the loop conditions are not met, because before I go into it, I set Reject explicitly:

        update {
                reply:Reply-Message := "Unauthorized - No group found"
                control:Auth-Type := "Reject"
                }

but it is still returning accept:

(0) # Executing section post-auth from file /etc/freeradius/3.0/sites-enabled/default
(0)   post-auth {
(0)     update {
(0)       reply:Reply-Message := "Unauthorized - No group found"
(0)       control:Auth-Type := Reject
(0)     } # update = noop
(0)     foreach &control:ldap-LDAP-Group {
(0)     } # foreach &control:ldap-LDAP-Group = noop
(0)   } # post-auth = noop
(0) Login OK: [generic-ms-user] (from client localhost port 1)
(0) Sent Access-Accept Id 116 from 127.0.0.1:1825 to 127.0.0.1:50310 length 0
(0)   Reply-Message := "Unauthorized - No group found"
(0) Finished request

The strange thing is that the message is being set, but the update still says "noop". Does anyone have a clue?

Best regards Matvey Teplov
+31 62 705 12 73

________________________________
From: Freeradius-Users <freeradius-users-bounces+matvey.teplov=nomios.nl at lists.freeradius.org> on behalf of Matvey Teplov via Freeradius-Users <freeradius-users at lists.freeradius.org>
Sent: 15 May 2025 15:23
To: Brian Julin <BJulin at clarku.edu>; freeradius-users at lists.freeradius.org <freeradius-users at lists.freeradius.org>
Cc: Matvey Teplov <matvey.teplov at nomios.nl>
Subject: Re: [EXT] Fetching memberOf attribute

Hi Brian,

How do you define ldap_ad-LDAP-Group?

Best regards Matvey Teplov
+31 62 705 12 73
________________________________
From: Brian Julin <BJulin at clarku.edu>
Sent: 15 May 2025 15:02
To: freeradius-users at lists.freeradius.org <freeradius-users at lists.freeradius.org>
Cc: Matvey Teplov <matvey.teplov at nomios.nl>
Subject: Re: [EXT] Fetching memberOf attribute

[You don't often get email from bjulin at clarku.edu. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]

Matvey Teplov via Freeradius-Users <freeradius-users at lists.freeradius.org> wrote:

> Hi Guys,

> I cannot get the ldap module to fetch a memberOf attribute from AD - it is not putting it
> as a filter parameter in the LDAP search packet. I need this list later to search through for
> port-authentication phase through the groups in session-state:LDAP-Group[*]'s to identify
> which ones are present and return a proper VSA. The LDAP configuration is as follows:

We use this, because it searches the group membership hierarchy so nested groups work:

    user {
     ...
      filter = "(&(objectClass=user)(sAMAccountName=%{Stripped-User-Name}))"
      scope = 'sub'
    }
    group {
     ...
      scope = 'sub'
      filter = '(objectCategory=group)'
      membership_filter = "(member:1.2.840.113556.1.4.1941:=%{&control:Ldap-UserDN})"
     ...
   }

You can thank Microsoft for the ugly OID, as far as I know it has no textual alias.

then:

if (&ldap_ad-LDAP-Group[*] ==  "whatever")

...that statement will be true if any one of the users groups, including groups inherited from other groups, is "whatever", no need for a for loop unless you need to do something fancy.

Do note that if you have a lot of groups in the OU the search will not be especially efficient, it will even rummage through your printers and whatnot.  We made a new OU for the top level RADIUS groups and then could put groups from our other OUs inside those groups to reduce the scope of the search to just users.


-
List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html


More information about the Freeradius-Users mailing list