>From cdb4687b74ffc1b794c5169d097d65a23a10845c Mon Sep 17 00:00:00 2001
From: John Dennis <jdennis@redhat.com>
Date: Fri, 20 Aug 2010 11:57:24 -0400
Subject: [PATCH] Load clients from LDAP
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit

---
 doc/examples/389_ds_schema.ldif |  574 +++++++++++++++++++++++++++++++++++++++
 raddb/modules/ldap              |   17 ++
 src/modules/rlm_ldap/rlm_ldap.c |  236 +++++++++++++++--
 3 files changed, 806 insertions(+), 21 deletions(-)
 create mode 100644 doc/examples/389_ds_schema.ldif

diff --git a/doc/examples/389_ds_schema.ldif b/doc/examples/389_ds_schema.ldif
new file mode 100644
index 0000000..652487c
--- /dev/null
+++ b/doc/examples/389_ds_schema.ldif
@@ -0,0 +1,574 @@
+# This is a LDAPv3 schema for RADIUS attributes.
+# Tested on OpenLDAP 2.0.7
+# Posted by Javier Fernandez-Sanguino Pena <jfernandez@sgi.es>
+# LDAP v3 version by Jochen Friedrich <jochen@scram.de>
+# Updates by Adrian Pavlykevych <pam@polynet.lviv.ua>
+# Modified by John Dennis <jdennis@redhat.com> for use with Directory Sever/IPA
+#
+# Note: These OID's do not seem to be registered, the closest I could find
+# was 1.3.6.1.4.1.3317
+# {iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) gnome(3317)}
+#
+##############
+dn: cn=schema
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.1
+        NAME 'radiusArapFeatures'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.2
+        NAME 'radiusArapSecurity'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.3
+        NAME 'radiusArapZoneAccess'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.44
+        NAME 'radiusAuthType'
+        DESC 'checkItem: Auth-Type'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.4
+        NAME 'radiusCallbackId'
+        DESC 'replyItem: Callback-Id'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.5
+        NAME 'radiusCallbackNumber'
+        DESC 'replyItem: Callback-Number'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.6
+        NAME 'radiusCalledStationId'
+        DESC 'checkItem: Called-Station-Id'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.7
+        NAME 'radiusCallingStationId'
+        DESC 'checkItem: Calling-Station-Id'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.8
+        NAME 'radiusClass'
+        DESC 'replyItem: Class'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.45
+        NAME 'radiusClientIPAddress'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.9
+        NAME 'radiusFilterId'
+        DESC 'replyItem: Filter-Id'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.10
+        NAME 'radiusFramedAppleTalkLink'
+        DESC 'replyItem: Framed-AppleTalk-Link'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.11
+        NAME 'radiusFramedAppleTalkNetwork'
+        DESC 'replyItem: Framed-AppleTalk-Network'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.12
+        NAME 'radiusFramedAppleTalkZone'
+        DESC 'replyItem: Framed-AppleTalk-Zone'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.13
+        NAME 'radiusFramedCompression'
+        DESC 'replyItem: Framed-Compression'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.14
+        NAME 'radiusFramedIPAddress'
+        DESC 'replyItem: Framed-IP-Address'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.15
+        NAME 'radiusFramedIPNetmask'
+        DESC 'replyItem: Framed-IP-Netmask'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.16
+        NAME 'radiusFramedIPXNetwork'
+        DESC 'replyItem: Framed-IPX-Network'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.17
+        NAME 'radiusFramedMTU'
+        DESC 'replyItem: Framed-MTU'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.18
+        NAME 'radiusFramedProtocol'
+        DESC 'replyItem: Framed-Protocol'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.19
+        NAME 'radiusFramedRoute'
+        DESC 'replyItem: Framed-Route'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.20
+        NAME 'radiusFramedRouting'
+        DESC 'replyItem: Framed-Routing'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.46
+        NAME 'radiusGroupName'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.47
+        NAME 'radiusHint'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.48
+        NAME 'radiusHuntgroupName'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.21
+        NAME 'radiusIdleTimeout'
+        DESC 'replyItem: Idle-Timeout'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.22
+        NAME 'radiusLoginIPHost'
+        DESC 'replyItem: Login-IP-Host'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.23
+        NAME 'radiusLoginLATGroup'
+        DESC 'replyItem: Login-LAT-Group'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.24
+        NAME 'radiusLoginLATNode'
+        DESC 'replyItem: Login-LAT-Node'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.25
+        NAME 'radiusLoginLATPort'
+        DESC 'replyItem: Login-LAT-Port'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.26
+        NAME 'radiusLoginLATService'
+        DESC 'replyItem: Login-LAT-Service'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.27
+        NAME 'radiusLoginService'
+        DESC 'replyItem: Login-Service'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.28
+        NAME 'radiusLoginTCPPort'
+        DESC 'replyItem: Login-TCP-Port'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.29
+        NAME 'radiusPasswordRetry'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.30
+        NAME 'radiusPortLimit'
+        DESC 'replyItem: Port-Limit'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.49
+        NAME 'radiusProfileDn'
+        DESC ''
+        EQUALITY distinguishedNameMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.31
+        NAME 'radiusPrompt'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.50
+        NAME 'radiusProxyToRealm'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.51
+        NAME 'radiusReplicateToRealm'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.52
+        NAME 'radiusRealm'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.32
+        NAME 'radiusServiceType'
+        DESC 'replyItem: Service-Type'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.33
+        NAME 'radiusSessionTimeout'
+        DESC 'replyItem: Session-Timeout'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.34
+        NAME 'radiusTerminationAction'
+        DESC 'replyItem: Termination-Action'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.35
+        NAME 'radiusTunnelAssignmentId'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.36
+        NAME 'radiusTunnelMediumType'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.37
+        NAME 'radiusTunnelPassword'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.38
+        NAME 'radiusTunnelPreference'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.39
+        NAME 'radiusTunnelPrivateGroupId'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.40
+        NAME 'radiusTunnelServerEndpoint'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.41
+        NAME 'radiusTunnelType'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.42
+        NAME 'radiusVSA'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.43
+        NAME 'radiusTunnelClientEndpoint'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+#need to change asn1.id
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.53
+        NAME 'radiusSimultaneousUse'
+        DESC 'checkItem: Simultaneous-Use'
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.54
+        NAME 'radiusLoginTime'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.55
+        NAME 'radiusUserCategory'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.56
+        NAME 'radiusStripUserName'
+        DESC ''
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.57
+        NAME 'dialupAccess'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.58
+        NAME 'radiusExpiration'
+        DESC 'checkItem: Expiration'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.59
+        NAME 'radiusCheckItem'
+        DESC 'checkItem: $GENERIC$'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.60
+        NAME 'radiusReplyItem'
+        DESC 'replyItem: $GENERIC$'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.61
+        NAME 'radiusNASIpAddress'
+        DESC ''
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.62
+        NAME 'radiusReplyMessage'
+        DESC 'replyItem: Reply-Message'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+objectClasses:
+    ( 1.3.6.1.4.1.3317.4.3.2.1
+        NAME 'radiusprofile'
+        SUP top AUXILIARY
+        DESC ''
+      MUST cn
+        MAY ( radiusArapFeatures $ radiusArapSecurity $ radiusArapZoneAccess $
+            radiusAuthType $ radiusCallbackId $ radiusCallbackNumber $
+            radiusCalledStationId $ radiusCallingStationId $ radiusClass $
+            radiusClientIPAddress $ radiusFilterId $ radiusFramedAppleTalkLink $
+            radiusFramedAppleTalkNetwork $ radiusFramedAppleTalkZone $
+            radiusFramedCompression $ radiusFramedIPAddress $
+            radiusFramedIPNetmask $ radiusFramedIPXNetwork $
+            radiusFramedMTU $ radiusFramedProtocol $
+            radiusCheckItem $ radiusReplyItem $
+            radiusFramedRoute $ radiusFramedRouting $ radiusIdleTimeout $
+            radiusGroupName $ radiusHint $ radiusHuntgroupName $
+            radiusLoginIPHost $ radiusLoginLATGroup $ radiusLoginLATNode $
+            radiusLoginLATPort $ radiusLoginLATService $ radiusLoginService $
+            radiusLoginTCPPort $ radiusLoginTime $ radiusPasswordRetry $
+            radiusPortLimit $ radiusPrompt $ radiusProxyToRealm $
+            radiusRealm $ radiusReplicateToRealm $ radiusServiceType $
+            radiusSessionTimeout $ radiusStripUserName $
+            radiusTerminationAction $ radiusTunnelClientEndpoint $ radiusProfileDn $
+            radiusSimultaneousUse $ radiusTunnelAssignmentId $
+            radiusTunnelMediumType $ radiusTunnelPassword $ radiusTunnelPreference $
+            radiusTunnelPrivateGroupId $ radiusTunnelServerEndpoint $
+            radiusTunnelType $ radiusUserCategory $ radiusVSA $
+            radiusExpiration $ dialupAccess $ radiusNASIpAddress $
+            radiusReplyMessage )
+    )
+objectClasses:
+    ( 1.3.6.1.4.1.3317.4.3.2.2
+        NAME 'radiusObjectProfile'
+        SUP top STRUCTURAL
+        DESC 'A Container Objectclass to be used for creating radius profile object'
+        MUST cn
+        MAY ( uid $ userPassword $ description )
+  )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.64
+        NAME 'radiusClientSecret'
+        DESC 'NAS password'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.65
+        NAME 'radiusClientNASType'
+        DESC 'NAS type'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+        SINGLE-VALUE
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.66
+        NAME 'radiusClientShortName'
+        DESC 'Descriptive name for NAS'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.67
+        NAME 'radiusClientVirtualServer'
+        DESC 'Assign NAS to this virtual server'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+attributeTypes:
+    ( 1.3.6.1.4.1.3317.4.3.1.68
+        NAME 'radiusClientCOAServer'
+        DESC 'COA server'
+        EQUALITY caseIgnoreIA5Match
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+    )
+objectClasses:
+    ( 1.3.6.1.4.1.3317.4.3.2.3
+        NAME 'radiusClientProfile'
+        SUP top STRUCTURAL
+        DESC 'A Container Objectclass to be used for describing radius clients'
+        MUST (cn $ radiusClientIPAddress $ radiusClientSecret)
+        MAY ( radiusClientNASType $ radiusClientShortName $ 
+            radiusClientVirtualServer $ radiusClientCOAServer $ description )
+  )
diff --git a/raddb/modules/ldap b/raddb/modules/ldap
index 8a2077a..1b9984f 100644
--- a/raddb/modules/ldap
+++ b/raddb/modules/ldap
@@ -158,6 +158,23 @@ ldap {
 	#  allowed values: {no, yes}
 	# set_auth_type = yes
 
+	# Clients can be read from the LDAP directory,
+	# this is only done at server start up time and will
+	# only be done if clients_basedn is defined.
+	#
+	# The LDAP attributes for a client are:
+	# cn, description,
+	# radiusClientIPAddress, radiusClientSecret
+	# radiusClientNASType, radiusClientShortName
+	# radiusClientVirtualServer, radiusClientCOAServer
+	#
+	# clients_basedn = "o=My Org,c=UA"
+	#
+	# The clients_filter is used in the LDAP search for client
+	# definitions, by default it is "(objectclass=radiusClientProfile)"
+	#
+	# clients_filter = "(objectclass=radiusClientProfile)"
+
 	#  ldap_debug: debug flag for LDAP SDK
 	#  (see OpenLDAP documentation).  Set this to enable
 	#  huge amounts of LDAP debugging on the screen.
diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c
index 3c12e9f..23a7060 100644
--- a/src/modules/rlm_ldap/rlm_ldap.c
+++ b/src/modules/rlm_ldap/rlm_ldap.c
@@ -1,3 +1,4 @@
+/* -*- indent-tabs-mode: t; c-basic-offset: 8; -*- */
 /*
  * rlm_ldap.c	LDAP authorization and authentication module.
  *
@@ -144,6 +145,8 @@ typedef struct {
 	char           *base_filter;
 	char           *basedn;
 	char           *default_profile;
+	char           *clients_basedn;
+	char           *clients_filter;
 	char           *profile_attr;
 	char           *access_attr;
 	char           *passwd_hdr;
@@ -283,6 +286,10 @@ static const CONF_PARSER module_config[] = {
 	 offsetof(ldap_instance,default_profile), NULL, NULL},
 	{"profile_attribute", PW_TYPE_STRING_PTR,
 	 offsetof(ldap_instance,profile_attr), NULL, NULL},
+	{"clients_basedn", PW_TYPE_STRING_PTR,
+         offsetof(ldap_instance,clients_basedn), NULL, NULL},
+	{"clients_filter", PW_TYPE_STRING_PTR,
+         offsetof(ldap_instance,clients_filter), NULL, "(objectclass=radiusClientProfile)"},
 
 	/*
 	 *	Getting passwords from the database
@@ -360,6 +367,7 @@ static int ldap_groupcmp(void *, REQUEST *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PA
 static size_t ldap_xlat(void *, REQUEST *, char *, char *, size_t, RADIUS_ESCAPE_STRING);
 static LDAP    *ldap_connect(void *instance, const char *, const char *, int, int *, char **);
 static int     read_mappings(ldap_instance* inst);
+static int generate_ldap_clients(ldap_instance* inst);
 
 static inline int ldap_get_conn(LDAP_CONN *conns,LDAP_CONN **ret,
 				ldap_instance *inst)
@@ -689,6 +697,10 @@ ldap_instantiate(CONF_SECTION * conf, void **instance)
 
 	DEBUG("conns: %p",inst->conns);
 
+	if (generate_ldap_clients(inst)) {
+		radlog(L_ERR, "rlm_ldap: Failed to read client list from ldap");
+	}
+
 	*instance = inst;
 
 
@@ -822,13 +834,14 @@ read_mappings(ldap_instance* inst)
 
 static int perform_search(void *instance, LDAP_CONN *conn,
 			  char *search_basedn, int scope, char *filter,
-			  char **attrs, LDAPMessage ** result)
+			  char **attrs, LDAPMessage ** result, int require_n_entries)
 {
 	int             res = RLM_MODULE_OK;
 	int		ldap_errno = 0;
 	ldap_instance  *inst = instance;
 	int		search_retry = 0;
 	struct timeval  tv;
+	int n_entries;
 
 	*result = NULL;
 
@@ -917,19 +930,196 @@ retry:
 		return (RLM_MODULE_FAIL);
 	}
 
-	ldap_errno = ldap_count_entries(conn->ld, *result);
-	if (ldap_errno != 1) {
-		if (ldap_errno == 0) {
-			DEBUG("  [%s] object not found", inst->xlat_name);
-		} else {
-			DEBUG("  [%s] got ambiguous search result (%d results)", inst->xlat_name, ldap_errno);
+	if (require_n_entries >= 0) {
+		n_entries = ldap_count_entries(conn->ld, *result);
+		if (n_entries != require_n_entries) {
+			if (n_entries == 0) {
+				DEBUG("  [%s] object not found", inst->xlat_name);
+			} else {
+				DEBUG("  [%s] got ambiguous search result, expected %d entries but got %d",
+				      inst->xlat_name, require_n_entries, n_entries);
+			}
+			res = RLM_MODULE_NOTFOUND;
+			ldap_msgfree(*result);
 		}
-		res = RLM_MODULE_NOTFOUND;
-		ldap_msgfree(*result);
 	}
 	return res;
 }
 
+/*
+ * generate_ldap_clients(). read clients from ldap, append to client list
+ */
+static int generate_ldap_clients(ldap_instance* inst)
+{
+	LDAP_CONN	*conn;
+	int		conn_id = -1;
+	int		res;
+	LDAPMessage    *result, *entry;
+	int		client_idx;
+	char           *prefix_ptr = NULL;
+
+	if (!inst->clients_basedn) {
+		DEBUG("rlm_ldap: generate_ldap_clients, client basedn not set, skipping...");
+		return 0;
+	}
+	DEBUG("rlm_ldap: generate_ldap_clients, client_basedn=%s", inst->clients_basedn);
+
+
+	if ((conn_id = ldap_get_conn(inst->conns,&conn,inst)) == -1){
+		radlog(L_ERR, "rlm_ldap: All ldap connections are in use");
+		return 1;
+	}
+	if ((res = perform_search(inst, conn, inst->clients_basedn, LDAP_SCOPE_SUBTREE,
+				  inst->clients_filter, NULL, &result, -1)) != RLM_MODULE_OK){
+		DEBUG("rlm_ldap::generate_ldap_clients: search failed");
+		ldap_release_conn(conn_id,inst);
+		return 1;
+	}
+
+	for (entry = ldap_first_entry(conn->ld, result), client_idx = 0;
+	     entry != NULL;
+	     entry = ldap_next_entry(conn->ld, entry), client_idx++) {
+		char *val, **vals;
+		RADCLIENT *client; 
+		char *description = NULL;
+
+		client = rad_malloc(sizeof(*client));
+		memset(client, 0, sizeof(*client));
+
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientIpAddress")) != NULL) {
+			val = vals[0];
+
+#ifdef WITH_DYNAMIC_CLIENTS
+			client->dynamic = 1;
+#endif
+
+			/*
+			 * Look for prefixes
+			 */
+			client->prefix = -1;
+			prefix_ptr = strchr(val, '/');
+			if (prefix_ptr) {
+				client->prefix = atoi(prefix_ptr + 1);
+				if ((client->prefix < 0) || (client->prefix > 128)) {
+					radlog(L_ERR, "rlm_ldap (%s): Invalid Prefix value '%s' for IP.",
+					       inst->xlat_name, prefix_ptr + 1);
+					free(client);
+					ldap_value_free(vals);
+					continue;
+				}
+				/* Replace '/' with '\0' */
+				*prefix_ptr = '\0';
+			}
+
+			/*
+			 * Always get the numeric representation of IP
+			 */
+			if (ip_hton(val, AF_UNSPEC, &client->ipaddr) < 0) {
+				radlog(L_CONS|L_ERR, "rlm_ldap (%s): Failed to look up hostname %s: %s",
+				       inst->xlat_name,
+				       val, fr_strerror());
+				free(client);
+				ldap_value_free(vals);
+				continue;
+			} else {
+				char buffer[256];
+				ip_ntoh(&client->ipaddr, buffer, sizeof(buffer));
+				client->longname = strdup(buffer);
+			}
+
+			if (client->prefix < 0) switch (client->ipaddr.af) {
+				case AF_INET:
+					client->prefix = 32;
+					break;
+				case AF_INET6:
+					client->prefix = 128;
+					break;
+				default:
+					break;
+				}
+
+
+			ldap_value_free(vals);
+		}
+
+
+		if ((vals = ldap_get_values(conn->ld, entry, "description")) != NULL) {
+			val = vals[0];
+			description = strdup(val);
+			ldap_value_free(vals);
+		}
+
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientSecret")) != NULL) {
+			val = vals[0];
+			client->secret = strdup(val);
+			ldap_value_free(vals);
+		}
+
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientNASType")) != NULL) {
+			val = vals[0];
+			client->nastype = strdup(val);
+			ldap_value_free(vals);
+		}
+
+
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientShortName")) != NULL) {
+			val = vals[0];
+			client->shortname = strdup(val);
+			ldap_value_free(vals);
+		}
+
+
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientVirtualServer")) != NULL) {
+			val = vals[0];
+			client->server = strdup(val);
+			ldap_value_free(vals);
+		}
+
+
+#ifdef WITH_COA
+		if ((vals = ldap_get_values(conn->ld, entry, "radiusClientCOAServer")) != NULL) {
+			val = vals[0];
+			client->coa_name = strdup(val);
+			ldap_value_free(vals);
+		}
+#endif
+
+		DEBUG("rlm_ldap (%s): Adding client %s [name=%s server=%s nastype=%s coa_server=%s description=%s]",
+		      inst->xlat_name, client->longname,
+		      client->shortname ? client->shortname : "<none>",
+		      client->server    ? client->server    : "<none>",
+		      client->nastype,
+#ifdef WITH_COA
+		      client->coa_name  ? client->coa_name  : "<none>",
+#else
+		      "<unsupported>",
+#endif
+		      description       ? description       : "<none>");
+
+		if (!client_add(NULL, client)) {
+			DEBUG("rlm_ldap (%s): Failed to add client %s (%s) to clients list.  Maybe there's a duplicate?",
+			      inst->xlat_name, client->longname, client->shortname);
+
+			if (description) free(description);
+			client_free(client);
+			ldap_msgfree(result);
+			ldap_release_conn(conn_id,inst);
+
+			return -1;
+		}
+
+		if (description) {
+			free(description);
+			description = NULL;
+		}
+	}
+
+	DEBUG("rlm_ldap (%s): %d client(s) added", inst->xlat_name, client_idx);
+
+	ldap_msgfree(result);
+	ldap_release_conn(conn_id,inst);
+	return 0;
+}
 
 /*
  *	Translate the LDAP queries.
@@ -1036,7 +1226,7 @@ static int ldap_groupcmp(void *instance, REQUEST *req,
 			return 1;
 		}
                 if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE,
-					filter, attrs, &result)) != RLM_MODULE_OK){
+					  filter, attrs, &result, 1)) != RLM_MODULE_OK){
                         DEBUG("rlm_ldap::ldap_groupcmp: search failed");
 			ldap_release_conn(conn_id,inst);
                         return 1;
@@ -1086,7 +1276,7 @@ static int ldap_groupcmp(void *instance, REQUEST *req,
 	}
 
 	if ((res = perform_search(inst, conn, basedn, LDAP_SCOPE_SUBTREE,
-				filter, attrs, &result)) == RLM_MODULE_OK) {
+				  filter, attrs, &result, 1)) == RLM_MODULE_OK) {
 		DEBUG("rlm_ldap::ldap_groupcmp: User found in group %s",
 				(char *)check->vp_strvalue);
 		ldap_msgfree(result);
@@ -1118,7 +1308,7 @@ static int ldap_groupcmp(void *instance, REQUEST *req,
 	}
 	if ((res = perform_search(inst, conn, vp_user_dn->vp_strvalue,
 				  LDAP_SCOPE_BASE, filter, group_attrs,
-				  &result)) != RLM_MODULE_OK) {
+				  &result, 1)) != RLM_MODULE_OK) {
 		DEBUG("rlm_ldap::ldap_groupcmp: Search returned error");
 		ldap_release_conn(conn_id, inst);
 		return 1;
@@ -1143,8 +1333,8 @@ static int ldap_groupcmp(void *instance, REQUEST *req,
 					inst->groupname_attr,
 					(char *)check->vp_strvalue);
 				if ((res = perform_search(inst, conn, vals[i],
-						LDAP_SCOPE_BASE, filter,
-						attrs, &gr_result)) != RLM_MODULE_OK){
+							  LDAP_SCOPE_BASE, filter,
+							  attrs, &gr_result, 1)) != RLM_MODULE_OK){
 					if (res != RLM_MODULE_NOTFOUND) {
 						DEBUG("rlm_ldap::ldap_groupcmp: Search returned error");
 						ldap_value_free(vals);
@@ -1238,7 +1428,9 @@ static size_t ldap_xlat(void *instance, REQUEST *request, char *fmt,
 		ldap_free_urldesc(ldap_url);
 		return 0;
 	}
-	if ((res = perform_search(inst, conn, ldap_url->lud_dn, ldap_url->lud_scope, ldap_url->lud_filter, ldap_url->lud_attrs, &result)) != RLM_MODULE_OK){
+	if ((res = perform_search(inst, conn, ldap_url->lud_dn, ldap_url->lud_scope,
+				  ldap_url->lud_filter, ldap_url->lud_attrs,
+				  &result, 1)) != RLM_MODULE_OK){
 		if (res == RLM_MODULE_NOTFOUND){
 			DEBUG("  [%s] Search returned not found", inst->xlat_name);
 			ldap_free_urldesc(ldap_url);
@@ -1366,7 +1558,8 @@ static int ldap_authorize(void *instance, REQUEST * request)
 		radlog(L_ERR, "  [%s] All ldap connections are in use", inst->xlat_name);
 		return RLM_MODULE_FAIL;
 	}
-	if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, inst->atts, &result)) != RLM_MODULE_OK) {
+	if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE,
+				  filter, inst->atts, &result, 1)) != RLM_MODULE_OK) {
 		RDEBUG("search failed");
 		if (res == RLM_MODULE_NOTFOUND){
 			snprintf(module_fmsg,sizeof(module_fmsg),"  [%s] User not found", inst->xlat_name);
@@ -1450,8 +1643,8 @@ static int ldap_authorize(void *instance, REQUEST * request)
 			profile = user_profile->vp_strvalue;
 		if (profile && strlen(profile)){
 			if ((res = perform_search(instance, conn,
-				profile, LDAP_SCOPE_BASE,
-				filter, inst->atts, &def_result)) == RLM_MODULE_OK){
+						  profile, LDAP_SCOPE_BASE,
+						  filter, inst->atts, &def_result, 1)) == RLM_MODULE_OK){
 				if ((def_msg = ldap_first_entry(conn->ld,def_result))){
 					if ((check_tmp = ldap_pairget(conn->ld,def_msg,inst->check_item_map,check_pairs,1, inst))) {
 						if (inst->do_xlat){
@@ -1489,8 +1682,8 @@ static int ldap_authorize(void *instance, REQUEST * request)
 			strlcpy(filter,inst->base_filter,sizeof(filter));
 			while(vals[i] != NULL && strlen(vals[i])){
 				if ((res = perform_search(instance, conn,
-					vals[i], LDAP_SCOPE_BASE,
-					filter, inst->atts, &def_attr_result)) == RLM_MODULE_OK){
+							  vals[i], LDAP_SCOPE_BASE,
+							  filter, inst->atts, &def_attr_result, 1)) == RLM_MODULE_OK){
 					if ((def_attr_msg = ldap_first_entry(conn->ld,def_attr_result))){
 						if ((check_tmp = ldap_pairget(conn->ld,def_attr_msg,inst->check_item_map,check_pairs,1, inst))) {
 							if (inst->do_xlat){
@@ -1879,7 +2072,8 @@ static int ldap_authenticate(void *instance, REQUEST * request)
 			radlog(L_ERR, "  [%s] All ldap connections are in use", inst->xlat_name);
 			return RLM_MODULE_FAIL;
 		}
-		if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE, filter, attrs, &result)) != RLM_MODULE_OK) {
+		if ((res = perform_search(instance, conn, basedn, LDAP_SCOPE_SUBTREE,
+					  filter, attrs, &result, 1)) != RLM_MODULE_OK) {
 			if (res == RLM_MODULE_NOTFOUND){
 				snprintf(module_fmsg,sizeof(module_fmsg),"  [%s] User not found", inst->xlat_name);
 				module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
-- 
1.7.2.1

