1.   INSTALLATION

This module depends on OpenLDAP v2.x SDK libraries.  For details on obtaining
source of OpenLDAP look at <http://www.openldap.org>.  OpenLDAP SDK in turn
depends on OpenSSL crypto libraries and (optionaly) on Cyrus-SASL libraries.

See also: http://www.tldp.org/HOWTO/LDAP-Implementation-HOWTO/radius.html

It's not up to date, though.  For example, you do NOT have to edit
the "dictionary" file.

2. LDAP ATTRIBUTES

The mapping between radius and ldap attributes is in raddb/ldap.attrmap. You
should edit the file and add any new mapping which you need. The schema files
is located in doc/RADIUS-LDAPv3.schema. Before adding any radius attributes
the ldap server schema should be updated.  All ldap entries containing radius
attributes should contain at least "objectclass: radiusprofile"

radiusCheckItem and radiusReplyItem are special. They allow the administrator
to add any check or reply item respectively without adding it in the ldap
schema. The format should be <ldap-attribute>: <radius-attribute> <operator>
<value>

ie radiusReplyItem: Cisco-AVPair := "ip:addr-pool=dialin_pool"

3. CONFIGURATION

Add following subsection to the modules{} section of radiusd.conf to control
the rlm_ldap module:

  modules { ...
	
	ldap {

#	server: LDAP server hostname/ip address
#
#	Optionaly could contain space separated list of host[:port], but
#	redundancy/resiliency is better acheived configuring multiple rlm_ldap
#	module instances and invocing them in	redundand/failover
#	configuration in authorize/authenticate sections
#
#	You can also pass an ldap url like ldap://localhost
#	That way you can also specify alternative ldap schemas like
#	ldaps:// or ldapi://
#	The port directive will be ignored in that case
#		
#	default: settings for your system, as set in etc/openldap/ldap.conf
#
		server   = localhost

#	port: LDAP server port
#		
#	If LDAP server port is set to 636 (ldaps), SSL connection is enforced.
#	This feature is useful for LDAP servers which support SSL, but don't
#	do TLS negotiation (like Novell eDirectory).
#	
#	default: 389 (ldap)
#
		port = 636

#	net_timeout: # of seconds to wait for response of the server (network
#	failures) default: 10
#
		net_timeout = 1

#	timeout: # seconds to wait for LDAP query to finish default: 20
#
		timeout = 2

#	timelimit: # of seconds server has to process the query (server-side
#	time limit) default: 20
#
		timelimit = 5

#	ldap_debug: debug flag for LDAP SDK (see OpenLDAP documentation)
#	default: 0x0000 (no debugging messages)
#	Example:(LDAP_DEBUG_FILTER+LDAP_DEBUG_CONNS)
		ldap_debug = 0x0028 

#	identity: DN under which LDAP searches are done password: pasword
#	which authenticate this DN default: anonymous bind, no password
#	required NOTE: searches are done now over unencrypted connection!
#
#	identity = "cn=admin,o=My Org,c=UA" password = mypass


#	ldap_connections_number: The number of ldap connections that the
#	module will keep open to use in requests. Usually it will not need to
#	be larger than 5-10 connections default: 5

	ldap_connections_number = 5

#	basedn = <Base of LDAP searches>
#
		basedn   = "o=My Org,c=UA"

#	filter: LDAP search filter, to locate user object using name supplied
#	by client during Radius authentication
#	
#	default: filter   = "(uid=%u)"

#	base_filter: The LDAP search filter used for base scope searches, like
#	when searching for the default or regular profiles
#
#	deafault: base_filter = "(objectclass=radiusprofile)"

		filter   = "(uid=%u)"

#	start_tls: When set to "yes" the StartTLS extended operation is used to
#	start TLS transport encryption.
		start_tls = no

#	tls_mode: When set to "yes" OR the server port is 636 we try to connect with TLS
#	Start TLS should be prefered, tls_mode is provided only for ldap servers
#	like Active Directory which do not support it.
#	default: no

		tls_mode = no

#	tls_cacertfile: A PEM-encoded file that contains the CA Certificates that
#	you trust
		tls_cacertfile = /path/to/cacert.pem

#	tls_cacertdir: Path the a directory of CA Certificates that you trust, the
#	directory must be in "hash format" (see openssl verify)
		tls_cacertdir = /path/to/ca/dir/

#	tls_certfile: The PEM Encoded certificate file that we should present to
#	clients connecting
		tls_certfile = /path/to/radius.crt

#	tls_keyfile: The PEM Encoded private key that we should use to encrypt the
#	session
		tls_keyfile = /path/to/radius.key

#	tls_randfile: A file containing random data to seed the OpenSSL PRNG.  Not
#	needed if your OpenSSL is already properly random.
		tls_randfile = /path/to/rnd

#	tls_require_cert: Certificate Verification requirements.  Can be "never"
#	(don't even bother trying), "allow" (try, but don't fail if the cerificate
#	can't be verified), or "demand" (fail if the certificate doesn't verify.)
#	The default is "allow"
		tls_require_cert = "allow"

#	default_profile: DN of a LDAP object, which contains default RADIUS
#	attributes.  default: NULL - use only user specific attributes or
#	attributes, supplied by other modules.
#		
	default_profile = "cn=RadProfile,o=My Org,c=UA"

#	profile_attribute: user object attribute, which contains DN of
#	radiusProfile object for this user.  default: NULL - use only user
#	specific attributes or attributes, supplied by other modules.
#
#	profile_attribute = "radiusProfileDn"

#	access_attr_used_for_allow: Define if the access attribute (described
#	below) will be used to allow access (meaning if it exists then user
#	remote access will be allowed) or to deny access.  default: yes - used
#	to allow access

#	access_attr: if attribute is specified, module checks for its
#	existance in user object.  If access_attr_used_for_allow is set to
#	yes: If it exists the user is allowed to get remote access.  If it
#	exists and is set to FALSE the user is denied remote access.  If it
#	does not exist user is denied remote access by default if
#	access_attr_used_for_allow is set to no: If it exists the user is
#	denied remote access.  If it does not exist user is allowed remote
#	access.
#
#	default: NULL - don't check for the attribute

	access_attr = "dialupAccess"

#	password_header: If the user password is available we add it to the
#	check items (to assist in CHAP ie) striping any headers first.
#	NOTE: The password_header directive is NOT case insensitive
#	default: NULL
#
#	password_header = "{clear}"

#	password_attribute: Define the attribute which contains the user
#	password.
#	While integrating FreeRADIUS with Novell eDirectory, set
#	'password_attribute = nspmpassword' in order to use the universal
#	password of the eDirectory users for RADIUS authentication. This will
#	work only if FreeRADIUS is configured to build with --with-edir option.
#
#	default: NULL - don't add password
#
#	password_attribute = "userPassword"

#	edir_account_policy_check: Specifies if the module has to enforce
#	Novell eDirectory account policy check and intruder detection for
#	RADIUS users.  This will work only if FreeRADIUS is configured to build
#	with --with-edir option.
#
#	default: yes - eDirectory account policy check enabled
#
#	edir_account_policy_check = no

#	groupname_attribute: The attribute containing group name in the LDAP
#	server. It is used to search group by name.
#
#	default: cn - Search filter is "(cn=%GroupName)"
#
#	groupname_attribute = "cn"

#	compare_check_items: Specifies if the module will do a comparison on
#	the check items extracted from the ldap with the corresponding items
#	present in the incoming request.
#
#	default: no - don't do any comparisons
#
#	compare_check_items = yes

#	do_xlat: Specifies if the module will do an xlat on the radius attributes
#	extracted from the ldap database. Also the attribute operators will be
#	honored. If the directive is set to 'no' then we will fall back to the
#	pairadd() function which will just add the attributes at the end of the
#	corresponding attribute list (check or reply items). This can be used to
#	fall back to 0.8.1 behaviour without changing the ldap data or to gain a
#	little performance if the ldap data is rather simple (no special operators)
#
#	default: yes - do an xlat and honor operators
#
#	do_xlat = yes
	

#	groupmembership_filter: The filter to search for group membership of a
#	particular user after we have found the DN for the group.
#
#	default: (|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))
#
#	groupmembership_filter = "(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))"	


#	groupmembership_attribute: The attribute in the user entry that states
#	the group the user belongs to.  The attribute can either contain the
#	group name or the group DN. If it contains the group DN
#	groupmembership_attribute will also be used to find the group's name.
#	The attribute will be used after a search based on the
#	groupname_attribute and groupmembership_filter has failed.  default:
#	NULL - don't search for a group based on attributes in the user entry.
#
#	groupmembership_attribute = "radiusGroupName"

	} }

NOTE: As LDAP is case insensitive, you should probably also set "lower_user =
yes" and "lower_time = before" in main section of radiusd.conf, to get limits
on simultaneous logins working correctly. Otherwise, users will be able get
large number of sessions, capitalizing parts of their login names.

MODULE MESSAGES: On user rejection rlm_ldap will return the following module
messages:

"rlm_ldap: User not found" "rlm_ldap: Access Attribute denies access"
"rlm_ldap: Bind as user failed"

These messages will be visible in radius.log as aditional information in
"Login incorrect" and "Invalid user" log messages.

LDAP XLAT: The ldap module now supports LDAP URLs in xlat strings. That is you
can now add LDAP URLs in the configuration options and hopefully shortly also
in the users file. The strings will be of the following form:

%{ldap:ldap:///dc=company,dc=com?uid?sub?uid=%u}

The requested attributes list MUST contain only ONE attribute. In case this
attribute is multi valued which value is returned is considered UNDEFINED.
Also, adding the host:port information SHOULD be avoided unless there are more
than one ldap module instances in which case the host,port information can be
used to distinguish which module will actually return the information (the
xlat function will return NULL if the host,port information does not
correspond to the configured attributes).  If there are more than one
instances the module instance name can be used instead of the string 'ldap'
before the ldap url to decide which instance will return the information.
That is the xlat string will be of the form:

%{$instance_name:ldap:///dc=comapny,dc=com?uid?sub?uid=%u}

i.e.: ${ldap_company1:ldap:///dc=company1,dc=com?uid?sub?uid=%u}


USER PROFILE ATTRIBUTE: The module can use the User-Profile attribute. If it
is set, it will assume that it contains the DN of a profile entry containing
radius attributes. This entry will _replace_ the default profile directive.
That way we can use different profiles based on checks on the radius
attributes contained in the Access-Request packets. For example (users file):

DEFAULT	Service-Type == Outbound-User, User-Profile := "uid=outbound-dialup,dc=company,dc=com"
 

GROUP SUPPORT: The module supports searching for ldap groups by use of the
Ldap-Group attribute. As long as the module has been instanciated it can be
used to do group membership checks through other modules. For example in the
users file:

DEFAULT	Ldap-Group == "disabled", Auth-Type := Reject 
	Reply-Message = "Sorry, you are not allowed to have dialup access"

DNs are also accepted as Ldap-Group values, i.e.:

DEFAULT	Ldap-Group == "cn=disabled,dc=company,dc=com", Auth-Type := Reject
	Reply-Message = "Sorry, you are not allowed to have dialup access"



Also if you are using multiple ldap module instances a per instance
Ldap-Group attribute is registered and can be used. It is of the form
<instance_name>-Ldap-Group. In other words if in radiusd.conf we
configure an ldap module instance like:

ldap myname { [...] }

we can then use the myname-Ldap-Group attribute to match user
groups. Make sure though that the ldap module is instantiated *before*
the files module so that it will have time to register the
corresponding attribute. One solution would be to add the ldap module
in the instantiate{} block in radiusd.conf


USERDN Attribute:
When rlm_ldap has found the DN corresponding to the username provided
in the access-request (all this happens in the authorize section) it
will add an Ldap-UserDN attribute in the request items list containing
that DN. The attribute will be searched for in the authenticate
section and if present will be used for authentication (ldap bind with
the user DN/password). Otherwise a search will be performed to find
the user dn. If the administrator wishes to use rlm_ldap only for
authentication or does not wish to populate the identity,password
configuration attributes he can set this attribute by other means and
avoid the ldap search completely. For instance it can be set through
the hints file in the authorize section:

DEFAULT	Ldap-UserDN := `uid=%{User-Name},ou=people,dc=company,dc=com`

The "users" file won't work, because it can't add items to the request.


DIRECTORY COMPATIBILITY NOTE: If you use LDAP only for authorization and
authentication (e.g. you can not afford schema extention), I propose to set
all necessary attributes in raddb/users file with following authorize section
of radiusd.conf :

authorize { ldap { notfound = return } files  }

LDAP and Active Directory
-------------------------

Active directory does not return anything in the userPassword
attribute, unlike other LDAP servers.  As a result, you cannot use
Active Directory to perform CHAP, MS-CHAP, or EAP-MD5 authentication.
You can only use PAP, and then only if you list "ldap" in the
"authenticate" section.

To do MS-CHAP against an Active Directory domain, see the comments in
radiusd.conf, about "ntlm_auth".  You will need to install Samba.


If you see "Operations error" returned from an LDAp query, you may
need to set dsHeuristics to 0000002 in Active Directory.  This allows
searches to function similar to how they did in Active Directory
2k2. You can update dsHeuristics by launching ldp.exe, going to
'connection' and create a new connection. Then goto bind and bind to
your ldap server. Next select the 'Browse' menu and choose
'modify'. The DN *might* look like this:

CN=Directory Service,CN=Windows
NT,CN=Services,CN=Configuration,DC=mycompany,DC=com

Attribute is: dsHeuristics
Value is: 0000002

Set the operation to replace and you should be set.  This should solve
the 'Operations error' error that happens when attempting to search
without specifying an OU.
