LDAP SUPPORT IN POSTFIX
=======================

Postfix can use an LDAP directory as a source for any of its lookups:
aliases, virtual, canonical, etc. This allows you to keep information
for your mail service in a replicated network database with fine-grained
access controls. By not storing it locally on the mail server, the
administrators can maintain it from anywhere, and the users can control
whatever bits of it you think appropriate. You can have multiple mail
servers using the same information, without the hassle and delay of
having to copy it to each.

BUILDING WITH LDAP SUPPORT
==========================

You need to have LDAP libraries and include files installed somewhere on
your system, and you need to configure the Postfix Makefiles
accordingly.

If you're using the libraries from the UM distribution
(http://www.umich.edu/~dirsvcs/ldap/ldap.html) or OpenLDAP
(http://www.openldap.org), something like this in the top level of your
Postfix source tree should work:

    % make tidy
    % make makefiles CCARGS="-I/some/where/include -DHAS_LDAP" \
	    AUXLIBS="/some/where/lib/libldap.a /some/where/lib/liblber.a"

The 'make tidy' command is needed only if you have previously built
Postfix without LDAP support.

If your LDAP libraries were built with Kerberos support, you'll also
need to include your Kerberos libraries in this line. Note that the KTH
Kerberos IV libraries might conflict with Postfix's lib/libdns.a, which
defines dns_lookup. If that happens, you'll probably want to link with
LDAP libraries that lack Kerberos support just to build Postfix, as it
doesn't support Kerberos binds to the LDAP server anyway. Sorry about
the bother.

If you're using one of the Netscape LDAP SDKs, you'll need to change the
AUXLIBS line to point to libldap10.so or libldapssl30.so or whatever you
have, and you may need to use the appropriate linker option (e.g. '-R')
so the executables can find it at runtime.

CONFIGURING LDAP LOOKUPS
========================

In order to use LDAP lookups, define at least one LDAP source as a table
lookup in main.cf, for example:

    alias_maps = hash:/etc/aliases, ldap:ldapsource

Each LDAP source can have the following parameters, which should be
prefixed in main.cf with the name you've given the source in its
definition and an underscore. To continue the example, the first
parameter below, "server_host", would be defined in main.cf as
"ldapsource_server_host". Defaults are given in parentheses:

    server_host (localhost)
    	The name of the host running the LDAP server, e.g.
		ldapsource_server_host = ldap.your.com
	It should be possible with all the libraries mentioned above to 
	specify multiple servers separated by spaces, with the libraries
	trying them in order should the first one fail. It should also
	be possible to give each server in the list a different port, by
	naming them like "ldap.your.com:1444".

    server_port (389) 
    	The port the LDAP server listens on, e.g.
		ldapsource_server_port = 778

    search_base (No default; you must configure this.)
    	The base at which to conduct the search, e.g. 
		ldapsource_search_base = dc=your, dc=com

    timeout (10 seconds)
    	The number of seconds a search can take before timing out, e.g.
		ldapsource_timeout = 5
    	
    query_filter (mailacceptinggeneralid=%s)
    	The RFC2254 filter used to search the directory, where %s is a 
	substitute for the address Postfix is trying to resolve, e.g.
		ldapsource_query_filter = (&(mail=%s)(paid_up=true))

    result_attribute (maildrop)
	The attribute Postfix will read from any directory entries
	returned by the lookup, to be resolved to an email address.
		ldapsource_result_attribute = mailbox

    scope (sub)
        The LDAP search scope: sub, base, or one. These translate into
	LDAP_SCOPE_SUBTREE, LDAP_SCOPE_BASE, and LDAP_SCOPE_ONELEVEL.

    bind (yes)
    	Whether or not to bind to the LDAP server. Newer LDAP
	implementations don't require clients to bind, which saves
	time. Example:
		ldapsource_bind = no

	If you do need to bind, you might consider configuring Postfix
	to connect to the local machine on a port that's an SSL tunnel
	to your LDAP server. If your LDAP server doesn't natively
	support SSL, put a tunnel (wrapper, proxy, whatever you want to
	call it) on that system too. This should prevent the password
	from traversing the network in the clear.

    bind_dn ("")
    	If you do have to bind, do it with this distinguished name.
	Example:
		ldapsource_bind_dn = uid=postfix, dc=your, dc=com

    bind_pw ("")
    	The password for the distinguished name above. If you have to
	use this, you probably want to make main.cf readable only by
	the Postfix user. Example:
		ldapsource_bind_pw = postfixpw

    cache (no)
        Whether to use a client-side cache for the LDAP connection. See
	ldap_enable_cache(3). It's off by default.

    cache_expiry (30 seconds)
        If the client-side cache is enabled, cached results will expire
	after this many seconds.

    cache_size (32768 bytes)
        If the client-side cache is enabled, this is its size in bytes.

    dereference (0)
    	When to dereference LDAP aliases. (Note that this has nothing
	do with Postfix aliases.) The permitted values are those 
	legal for the OpenLDAP/UM LDAP implementations:

		0	never
		1	when searching
		2	when locating the base object for the search
		3	always

	See ldap.h or the ldap_open(3) or ldapsearch(1) man pages for
	more information. And if you're using an LDAP package that has
	other possible values, please bring it to the attention of the
	postfix-users@postfix.org mailing list.

Don't use quotes in these variables; at least, not until the Postfix
configuration routines understand how to deal with quoted strings.

EXAMPLES
========

Here's a basic example for using LDAP to look up aliases. In main.cf,
you have these configuration parameters defined:

alias_maps = hash:/etc/aliases, ldap:ldapsource
ldapsource_server_host = ldap.my.com
ldapsource_search_base = dc=my, dc=com

Upon receiving mail for a local address "ldapuser" that isn't found in
the /etc/aliases database, Postfix will search the LDAP server listening
at port 389 on ldap.my.com. It will bind anonymously, search for any
directory entries whose mailacceptinggeneralid attribute is "ldapuser",
read the "maildrop" attributes of those found, and build a list of their
maildrops, which will be treated as RFC822 addresses to which the
message will be delivered.

If you want to keep information for virtual lookups in your directory,
it's only a little more complicated. You'll want to make sure all of
your virtual mailacceptinggeneralid attributes are fully qualified with
their virtual domains. If you want to designate a directory entry as the
default user for a virtual domain, just give it an additional
mailacceptinggeneralid (or the equivalent in your directory) of
"@virtual.dom". That's right, no user part.

If you want to get information for relay_domains out of your directory,
the simplest way to get it is to add the domain name (without even the
'@') as a mailacceptinggeneralid to some recipient in each domain, then
add "$virtual_maps" to your relay_domains line. Then you can use the
same map you use to find virtual recipients to determine if a domain is
a valid virtual domain and should be allowed to relay.

For example, the catchall user for a virtual domain might look like
this:

   dn: cn=defaultrecipient, dc=fake, dc=dom
   objectclass: top
   objectclass: rfc822mailgroup
   cn: defaultrecipient
   owner: uid=root, dc=someserver, dc=isp, dc=dom
   mailacceptinggeneralid: fake.dom
   mailacceptinggeneralid: @fake.dom
   maildrop: realuser@real.dom         

If you don't necessarily have a catchall user for the domain (i.e. you
want mail to unknown users in the domain to bounce), and don't want to
tag an arbitrary user in the virtual domain, you might define another
LDAP map that finds your virtual domain's domain object entry, and add
that map to relay_domains instead of "$virtual_maps". All that's
necessary is that a search for the domain name return something.

Other common uses for LDAP lookups include rewriting senders and
recipients with Postfix' canonical lookups, for example in order to make
mail leaving your site appear to be coming from "First.Last@site.dom"
instead of "userid@site.dom".

NOTES AND THINGS TO THINK ABOUT
===============================

- You probably want to make sure that mailacceptinggeneralids are
  unique, and that not just anyone can specify theirs as postmaster or
  root, say.

- An entry can have an arbitrary number of mailacceptinggeneralids or
  maildrops. Maildrops can also be comma-separated lists of addresses.
  They will all be found and returned by the lookups. For example, you
  could define an entry intended for use as a mailing list that looks
  like this (Warning! Schema made up just for this example):

  dn: cn=Accounting Staff List, dc=my, dc=com
  cn: Accounting Staff List
  o: my.com
  objectclass: maillist
  mailacceptinggeneralid: accountingstaff
  mailacceptinggeneralid: accounting-staff
  maildrop: mylist-owner
  maildrop: an-accountant
  maildrop: some-other-accountant
  maildrop: this, that, theother

- If you use an LDAP map for lookups other than aliases, you may have to
  make sure the lookup makes sense. In the case of virtual lookups,
  maildrops other than mail addresses are pretty useless, because
  Postfix can't know how to set the ownership for program or file
  delivery. Your query_filter should probably look something like this:

  virtual_query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")(maildrop="*:*")(maildrop="*/*"))))

- And for that matter, even for aliases, you may not want users able to
  specify their maildrops as programs, includes, etc. This might be
  particularly pertinent on a "sealed" server where they don't have
  local UNIX accounts, but exist only in LDAP and Cyrus. You might allow
  the fun stuff only for directory entries owned by an administrative
  account:

  local_query_filter = (&(mailacceptinggeneralid=%s)(|(!(maildrop="*|*")(maildrop="*:*")(maildrop="*/*"))(owner=cn=root, dc=your, dc=com)))

  So that if the object had a program as its maildrop and weren't owned
  by "cn=root" it wouldn't be returned as a valid local user. This will
  require some thought on your part to implement safely, considering the
  ramifications of this type of delivery. You may decide it's not worth
  the bother to allow any of that nonsense in LDAP lookups, ban it in
  the query_filter, and keep things like majordomo lists in local alias
  databases.

- LDAP lookups are slower than local DB or DBM lookups. For most sites
  they won't be a bottleneck, but it's a good idea to know how to tune
  your directory service.

FEEDBACK
========

If you have questions, send them to postfix-users@postfix.org. Please
include relevant information about your Postfix setup: LDAP-related
output from postconf, which LDAP libraries you built with, and which
directory server you're using. If your question involves your directory
contents, please include the applicable bits of some directory entries.

CREDITS
=======

Support for LDAP was initially written by Prabhat K Singh of VSNL,
Bombay, India, and then hideously bloated by John Hensley to support
multiple sources and more configurable attributes. The caching bits were
initially worked out by Prabhat, then munged to support the multiple
sources. 

Other contributors, of code or direction or dope slaps, include:

Manuel Guesdon
Carsten Hoeger
Keith Stevenson
Samuel Tardieu

And of course Wietse.
