field by the recipient localpart when a destination matches
$mydestination/$inet_interfaces. The price is the introduction
of a new parameter local_destination_recipient_limit which
- defaults to 1 i order to maintain backwards compatibility.
+ defaults to 1 in order to maintain backwards compatibility.
Files: qmgr/qmgr.c, qmgr/qmgr_message.c.
20000129
record delimiter (for example, eol=\r\n). This is necessary
for transports that require CRLF instead of UNIX-style LF.
-20000428
-
- New code: added support to the vstring module for fixed-size
- buffers. This may help to avoid clumsy code that would have
- to prevent memory leaks. The buffers cause a fatal error
- when they turn out too small. File: util/vstring.[hc].
-
20000502
In order to support timeouts more conveniently, VSTREAMs
20000505
- Bugfix: the SMTP server now flushes output in-between tarpit
- delays, to avoid protocol timeouts when a client causes lots
- of errors. Found by Lamont Jones, HP. File: smtpd/smtpd_chat.c.
+ Bugfix: the SMTP server now flushes unwritten output before
+ tarpit delays, to avoid protocol timeouts in pipelined
+ sessions when a client causes lots of errors. Found by
+ Lamont Jones, HP. File: smtpd/smtpd_chat.c.
+
+ Finished the LMTP client, which is based on a modified
+ version of the SMTP client by Philippe Prindeville, Mirapoint,
+ Inc., later modified by Amos Gouaux, UTDallas, and then
+ Wietse ripped it all up again. Currently this talks LMTP
+ over TCP only.
+
+ Feature: override main.cf parameters in master.cf. Specify
+ "-o parameter=value" after the program name. This allows
+ you to selectively override myhostname etc. See also the
+ new smtp_bind_address parameter below.
20000506
Convenience: the LMTP and SMTP clients now append the local
domain to unqualified nexthop destinations. This makes it
- more convenient to set up transport maps. Files:
+ more convenient to set up transport maps. Files:
lmtp/lmtp_addr.c, smtp/smtp_addr.c.
+
+ Sendmail compatibility: the Postfix SMTP client now skips
+ servers that greet the client with a 4xx or 5xx status
+ code. To disable, set both smtp_skip_4xx_greeting and
+ smtp_skip_5xx_greeting to "no".
+
+20000507
+
+ Portability: NetBSD has migrated to /etc/mail/aliases. We
+ can expect to see this happen more often when systems start
+ shipping Sendmail 8.10. File: util/sys_defs.h
+
+ Updated LDAP code by John Hensley, with support for
+ dereferencing of LDAP aliases, which have nothing to do
+ with Postfix aliases.
+
+ Feature: "smtp_bind_address=x.x.x.x" specifies the source
+ IP address for SMTP client connections. Specify in master.cf
+ as "smtp -o smtp_bind_address=x.x.x.x" in order to give
+ different delivery agents different source addresses.
your command search path.
If you need to build Postfix for multiple architectures, use the
-makelinks shell script to build a shadow tree with symbolic links
-to the source files.
-
- % sh makelinks `pwd` /some/where/else
+lndir command to build a shadow tree with symbolic links to the
+source files. lndir is part of X11R6.
If at any time in the build process you get messages like: "make:
don't know how to ..." you should be able to recover by running
+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
==========================
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 should work:
+(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/libldap.a /some/where/liblber.a"
+ AUXLIBS="/some/where/lib/libldap.a /some/where/lib/liblber.a"
-The `make tidy' command is needed only if you have previously built
+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
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 yet support Kerberos binds to the LDAP server anyway. Sorry
-about the bother.
+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 -R option so the executables can find
-it at runtime.
+have, and you may need to use the appropriate linker option (e.g. '-R')
+so the executables can find it at runtime.
-USING LDAP LOOKUPS
-==================
+CONFIGURING LDAP LOOKUPS
+========================
In order to use LDAP lookups, define at least one LDAP source as a table
lookup in main.cf, for example:
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. 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:
+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.
+ 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)
+ 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
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:
bind_pw ("")
The password for the distinguished name above. If you have to
- have this, you probably want to make main.cf readable only by
+ 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.
-EXAMPLE
-=======
+EXAMPLES
+========
-Here's a basic example. In main.cf, you have these configuration
-parameters defined:
+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
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
===============================
unique, and that not just anyone can specify theirs as postmaster or
root, say.
-- An entry can have an arbitrary number of maildrops. Maildrops can also
- be comma-separated lists of addresses. 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):
+- 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
- 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 like "|/some/program" are pretty useless. Your query_filter
- should probably look something like this:
+ 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="*:*"))))
+ virtual_query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")(maildrop="*:*")(maildrop="*/*"))))
-- And for that matter, you may not want users able to specify their
- maildrops as programs, particularly if they'd be executed on the
- server. A safer local query_filter could look something like:
+- 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="*|*"))(owner=cn=root, dc=your, dc=com)))
+ 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
- probably require some thought on your part to implement safely,
- considering the ramifications of includes and programs. 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.
-
-- It's not yet known how all this scales, but LDAP lookups are much more
- expensive than checking a DB file. If you anticipate a lot of lookups,
- it may pay to plan your directory to reduce the number of lookups. For
- instance, rather than having a bunch of objects that serve as aliases
- to just one object, you could simply add their mailacceptinggeneralids
- to the target object. This:
-
- dn: uid=firstlast, dc=your, dc=com
- maildrop: firstlast@mailbox.your.com
- mailacceptinggeneralid: firstlast
- mailacceptinggeneralid: First.Last
- mailacceptinggeneralid: F.Last
-
- Not this:
-
- dn: uid=firstlast, dc=your, dc=com
- maildrop: firstlast@mailbox.your.com
- mailacceptinggeneralid: firstlast
-
- dn: cn=First.Last, dc=your, dc=com
- maildrop: firstlast
- mailacceptinggeneralid: First.Last
-
- dn: cn=F.Last, dc=your, dc=com
- maildrop: firstlast
- mailacceptinggeneralid: F.Last
-
- Any performance reports will be much appreciated on the postfix-users
- list.
-
- UPDATE: At Merit, I've seen over 150000 deliveries per day with no
- noticeable delay from our OpenLDAP server. I'd now recommend not
- resorting to the above unless you anticipate much more traffic than
- that. It makes management of your directory less intuitive, which is
- probably not worth the reduction in lookups.
+ 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
=======
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 contributions have been submitted to move toward better
-support of Netscape/LDAPv3 libraries, and any other improvements are of
-course welcome.
+sources.
+
+Other contributors, of code or direction or dope slaps, include:
+
+Manuel Guesdon
+Carsten Hoeger
+Keith Stevenson
+Samuel Tardieu
+
+And of course Wietse.
Postfix LMTP support
====================
-Postfix LMTP support was initially implemented by Philip A.
-Prindeville of Mirapoint, Inc., USA, and was modified further by
-Amos Gouaux of University of Texas at Dallas, Richardson, USA.
-Wietse then beat the code into its present shape.
+Postfix LMTP support is based on a modified version of the Postfix
+SMTP client. The initial version was by Philip A. Prindeville of
+Mirapoint, Inc., USA. This code was modified further by Amos Gouaux
+of University of Texas at Dallas, Richardson, USA. Wietse Venema
+reduced the code to its present shape.
Postfix can be configured to talk to a local or remote LMTP server.
Most people will run the LMTP server on the same machine that runs
lmtp stream tcp nowait cyrus /usr/sbin/tcpd /usr/local/cyrus/bin/deliver -e -l
-/usr/sbin/tcpd is from the tcp_wrappers package. You want this
-to make sure only your mail relay(s) can talk to the LMTP server.
-Postfix by default enables connection cacheing for delivery via
-LMTP, so do not worry about the load of wrapping the LMTP port.
+/usr/sbin/tcpd is from the tcp_wrappers package. You want this to
+make sure only your mail relay(s) can talk to the LMTP server.
+Postfix by default does multiple deliveries per LMTP session
+(connection cacheing), so do not worry about the overhead of
+tcp_wrapping the LMTP port.
+
+On some systems, tcpd is built into inetd, so you do not have to
+specify tcpd in the inetd.conf file. Instead of tcpd/inetd, xinetd
+can do a similar job of logging and access control.
Configuring Postfix
===================
lmtp unix - - n - - lmtp
-NOTES: Root privs are not necessary!
+NOTE: Root privileges are not necessary!
-We put this in /etc/postfix/transport:
+Put this in /etc/postfix/transport:
inbox.domain.org lmtp:inbox.domain.org
-Naturally, this means we also have to have in
-/etc/postfix/main.cf:
+Naturally, this means we also need in /etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/transport
-Use the map type of your choice. Use "postconf -m" to find out
-what map types are supported.
+Instead of "hash", use the map type of your choice. Some systems
+use "dbm" instead. Use "postconf -m" to find out what map types
+are supported.
-Connection cacheing performance
-===============================
+Improving connection cacheing performance
+=========================================
After delivering a message via LMTP, Postfix will keep the connection
open for a while, so that it can be reused for a subsequent delivery.
This reduces overhead of LMTP servers that create one process per
connection.
-The Postfix LMTP client makes only one connection at a time. For
-connection cacheing to work well, the Postfix LMTP client has to
-avoid switching destination hosts. If you have multiple LMTP servers,
-configure separate master.cf entries for each LMTP server, and
-configure transport entries that distribute mail domains to the
-right LMTP servers.
+For LMTP connection cacheing to work, the Postfix LMTP client should
+not switch destination hosts. This is no problem when you run only
+one LMTP server. However, if you run multiple LMTP servers, this
+can be an issue.
- /etc/postfix/transport:
- foo.com lmtp1:lmtp1host
- bar.com lmtp2:lmtp2host
+You can prevent the LMTP client from switching between servers by
+configuring a separate mail delivery transport for each LMTP server:
/etc/postfix/master.cf:
lmtp1 unix - - n - - lmtp
lmtp2 unix - - n - - lmtp
+ . . . . . . . .
+
+Configure transport table entries such that the lmtp1 mail delivery
+transport is used for all deliveries to the LMTP server #1, the
+mail lmtp2 transport for the LMTP server #2, and so on.
+
+ /etc/postfix/transport:
+ foo.com lmtp1:lmtp1host
+ bar.com lmtp2:lmtp2host
DIRS = util global dns master postfix smtpstone sendmail error \
pickup cleanup smtpd local lmtp trivial-rewrite qmgr smtp bounce pipe \
showq postalias postcat postconf postdrop postkick postlock postlog \
- postmap postsuper spawn # base64 proto man html
+ postmap postsuper # spawn base64 proto man html
default: update
-Incompatible changes with snapshot-20000506
+Incompatible changes with snapshot-20000507
===========================================
-None.
+As required by RFC 822, Postfix now inserts a generic destination
+message header when no destination header is present. The text is
+specified via the undisclosed_recipients_header configuration
+parameter (default: "To: undisclosed-recipients:;").
+
+The Postfix sendmail command treats a line with only `.' as
+the end of input, for the sake of sendmail compatibility. To disable
+this feature, specify the sendmail-compatible `-i' or `-oi' flags
+on the sendmail command line.
+
+For the sake of Sendmail compatibility, the Postfix SMTP client
+skips over SMTP servers that greet with a 4XX or 5XX reply code,
+treating them as unreachable servers. To obtain prior behavior
+(4XX=retry, 5XX=bounce), specify "smtp_skip_4xx_greeting = no" and
+"smtp_skip_5xx_greeting = no".
+
+The read/write interface underneath VSTREAMs has been extended with
+parameters that specify a read/write timeout and application context.
+This should make it easier to plug in encryption modules such as TLS.
-Major changes with snapshot-20000506
+Major changes with snapshot-20000507
====================================
-Preliminary LMTP client support with connection cacheing, currently
-only over TCP sockets. Support for LMTP over UNIX-domain support
-will be added later. See the LMTP_README file for more details.
+Better documentation of Postfix lookup tables, including descriptions
+of how to use regular expressions in Postfix lookup tables.
+
+Updated mysql and LDAP client code with fixes and improvements.
+
+In master.cf you can selectively override main.cf configuration
+parameters, for example: "smtpd -o myhostname=foo.com".
+
+In main.cf, specify "smtp_bind_address=x.x.x.x" to bind SMTP
+connections to a specific local interface. Or override the default
+setting in master.cf with "smtp -o smtp_bind_address=x.x.x.x".
+For now, you must specify a numeric IP address.
+
+Preliminary LMTP client support over TCP with connection cacheing.
+Support for LMTP over UNIX-domain sockets will be added later as
+an enhancement to the transport table syntax. See the LMTP_README
+file for more details.
+
+By the way, LMTP client-side connection cacheing is a good example
+for how to do the same in the SMTP client.
Preliminary support for SASL authentication, both in the SMTP server
and in the SMTP client. See the SASL_README file for more details.
-The pipe mailer now has a configurable end-of-line attribute.
-Specify, for example, "pipe ... eol=\r\n" for delivery mechanisms
-that require CRLF record delimiters.
-
-The manual pages in sample configuration files now show plain text
-without TROFF escape sequences.
+The pipe delivery agent has a configurable end-of-line attribute.
+Specify "pipe ... eol=\r\n" for delivery mechanisms that require
+CRLF record delimiters. The eol attribute understands the following
+C-style escape sequences: \a \b \f \n \r \t \v \nnn \\.
Incompatible changes with snapshot-20000309
===========================================
-As required by RFC 822, Postfix now inserts a generic destination
-message header when no destination header is present. The text is
-specified via the undisclosed_recipients_header configuration
-parameter (default: "To: undisclosed-recipients:;").
-
-The Postfix sendmail command now treats a line with only `.' as
-the end of input, for the sake of sendmail compatibility. To disable
-this feature, specify the sendmail-compatible `-i' or `-oi' flags
-on the sendmail command line.
+This release is mainly to have a reference point after reorganizing
+the cleanup daemon, and before adding some major contributions from
+other people.
Major changes with snapshot-20000309
====================================
The manual pages in Postfix configuration files no longer contain
troff formatting codes. The text is now generated from prototype
files in a new "proto" subdirectory.
-
Incompatible changes with postfix-19991231:
===========================================
Do not use this code. The Postfix SASL support is based on the
Cyrus SASL library, which has not enough documentation about how
-the software is supposed to work, and it is not clear if the code
-is safe enough for security-critical applications.
+the software is supposed to work. It is not clear if the code is
+safe enough for security-critical applications.
Postfix+SASL 1.5.5 appears to work on RedHat 6.1 (pwcheck_method
set to shadow or sasldb), Solaris 2.7 (pwcheck_method set to shadow
On some systems this generates the necessary Makefile definitions:
- % make tidy # if you have left-over files from a previus build
+ % make tidy # if you have left-over files from a previous build
% make makefiles CCARGS=-DUSE_SASL_AUTH" -I/usr/local/include" \
AUXLIBS="-L/usr/local/lib -lsasl"
On Solaris 2.x you need to specify run-time link information,
otherwise ld.so will not find the SASL shared library:
- % make tidy # if you have left-over files from a previus build
+ % make tidy # if you have left-over files from a previous build
% make makefiles CCARGS=-DUSE_SASL_AUTH" -I/usr/local/include" \
AUXLIBS="-L/usr/local/lib -R/usr/local/lib -lsasl"
-Enabling SASL authentication in the SMTP server
-===============================================
+Enabling SASL authentication in the Postfix SMTP server
+=======================================================
If you installed the Cyrus SASL libraries as per the default, you
will have to symlink /usr/lib/sasl -> /usr/local/lib/sasl.
smtpd_recipient_restrictions =
permit_mynetworks permit_sasl_authenticated ...
-In /usr/local/lib/sasl/smtpd.conf you need to specify what authentication
-mechanism the server will support, for example:
+In /usr/local/lib/sasl/smtpd.conf you need to specify how the server
+should validate client passwords. For example:
- pwcheck_method: sasldb
+ /usr/local/lib/sasl/smtpd.conf:
+ pwcheck_method: sasldb
This will use the SASL password file (default: /etc/sasldb), which
-is maintained with the saslpasswd command. On some systems the
-saslpasswd command needs to be run multiple times before it stops
-complaining. The Postfix SMTP server needs read access to the
-sasldb file - you have to play games with group access permissions.
+is maintained with the saslpasswd command (part of the Cyrus SASL
+software). On some poorly-supported systems the saslpasswd command
+needs to be run multiple times before it stops complaining. The
+Postfix SMTP server needs read access to the sasldb file - you may
+have to play games with group access permissions. On RedHat 6.1,
+SASL 1.5.5 insists on write access to /etc/sasldb.
-To run chrooted with SASL support is an interesting exercise.
+Instead of the SASL-specific password file you can configure the
+Postfix SMTP server to validate client passwords against the UNIX
+shadow password file:
+
+ /usr/local/lib/sasl/smtpd.conf:
+ pwcheck_method: shadow
+
+However this requires that Postfix has read access to the UNIX shadow
+password file, which is normally readable only by root. Shadow
+password support has been found to work for Solaris 2.7 and RedHat
+6. 1 but not with freeBSD 3.4.
+
+To run software chrooted with SASL support is an interesting exercise.
+This is one of the many problems with the present SASL support.
To test the whole mess, connect to the SMTP server, and you should
be able to have a conversation like this:
form of username\0username\0password (the \0 is a null byte). The
example above is for a user named `test' with password `testpass'.
-Enabling SASL authentication in the SMTP client
-===============================================
+Enabling SASL authentication in the Postfix SMTP client
+=======================================================
Turn on client-side SASL authentication, and specify a table with
per-host username and password information.
foo.com username:password
bar.com username
-The SASL password file is opened before the SMTP server enters the
-optional chroot jail, so there is no need to copy the sasl_passwd
-DB or DBM file into /var/spool/postfix/etc/postfix.
+The SASL client password file is opened before the SMTP server
+enters the optional chroot jail, so you can keep the file in
+/etc/postfix.
vstring_sprintf(state->temp2, "%sFrom: %s",
state->resent, vstring_str(state->temp1));
if (state->fullname && *state->fullname) {
- vstring_strcat(state->temp2, " (");
- token = tok822_alloc(TOK822_COMMENT_TEXT, state->fullname);
+ vstring_sprintf(state->temp1, "(%s)", state->fullname);
+ token = tok822_parse(vstring_str(state->temp1));
+ vstring_strcat(state->temp2, " ");
tok822_externalize(state->temp2, token, TOK822_STR_NONE);
- tok822_free(token);
- vstring_strcat(state->temp2, ")");
+ tok822_free_tree(token);
}
CLEANUP_OUT_BUF(state, REC_TYPE_NORM, state->temp2);
} else if ((state->headers_seen & (1 << (state->resent[0] ?
showq unix n - n - - showq
error unix - - n - - error
local unix - n n - - local
-lmtp unix - - n - - lmtp server=localhost
+lmtp unix - - n - - lmtp
cyrus unix - n n - - pipe
flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
uucp unix - n n - - pipe
# HERE JUST SERVES AS AN EXAMPLE.
#
# This file contains example settings of Postfix configuration
-# parameters that control SASL authentication.
+# parameters that control SASL authentication for the Postfix
+# SMTP server and client programs.
# SMTP SERVER CONTROLS
-# The smtpd_sasl_auth_enable parameter controls whether authentication
-# is enabled in the Postfix SMTP server.
+# The smtpd_sasl_auth_enable parameter controls whether SMTP client
+# authentication is enabled in the Postfix SMTP server. By default,
+# the Postfix SMTP server does not use authentication.
#
-# If a client is authenticated, then the permit_sasl_authenticated
-# can be used to permit relay access.
+# If an SMTP client is authenticated, then the permit_sasl_authenticated
+# access restriction can be used to permit relay access, like this:
+#
+# smtpd_recipient_restrictions = permit_sasl_authenticated, ...
+#
+# To reject all SMTP connections from unauthenticated clients,
+# specify smtpd_delay_reject=yes (which is the default) and use:
+#
+# smtpd_client_restrictions = permit_sasl_authenticated
#
# In order to enable server-side authentication, build Postfix with
# SASL support, and install a configuration file /usr/lib/sasl/smtpd.conf
# pwcheck_method: sasldb
#
# or whatever method is suitable for your environment: PAM, shadow,
-# whatever. If you use sasldb, you can add users with the "saslpasswd"
-# command that is part of the SASL library. If you use PAM, The PAM
-# service name for SASL authentication is "smtp", and adding users
-# depends entirely on how PAM is set up.
+# etc. If you use sasldb, you can add users with the "saslpasswd"
+# command that comes with the SASL library. If you configure Postfix
+# to use PAM, the PAM service name for SASL authentication is "smtp",
+# and adding users depends entirely on how PAM is set up.
#
# If you run your SMTP server chrooted, then you need to copy PAM
# and/or SASL support libraries and data files into the chroot jail.
+# That's a lot of files, and it seems not very practical to do so.
#
-smtpd_sasl_auth_enable = yes
+#smtpd_sasl_auth_enable = yes
+smtpd_sasl_auth_enable = no
# The smtpd_sasl_security_options parameter controls what authentication
# mechanisms the Postfix SMTP server will offer to the client. The
# By default, the Postfix SMTP server accepts plaintext passwords but
# not anonymous logins.
#
-# Horror! It appears that clients try authentication methods in the
-# order as advertised by the server (PLAIN ANONYMOUS CRAM-MD5
-# ...) which means that if you disable plaintext passwords, clients
-# will log in anonymously even when they would be able to use CRAM-MD5.
+# HORROR! It appears that clients try authentication methods in the
+# order as advertised by the server (e.g., PLAIN ANONYMOUS CRAM-MD5)
+# which means that if you disable plaintext passwords, clients will
+# log in anonymously, even when they should be able to use CRAM-MD5.
# So, if you disable plaintext logins, disable anonymous logins too.
# Postfix treats anonymous login as no authentication.
#
# SMTP CLIENT CONTROLS
# The smtp_sasl_auth_enable parameter controls whether authentication
-# is enabled in the Postfix SMTP client.
+# is enabled in the Postfix SMTP client. By default, the Postfix SMTP
+# client uses no authentication.
#
-smtp_sasl_auth_enable = yes
+#smtp_sasl_auth_enable = yes
+smtp_sasl_auth_enable = no
# The smtp_sasl_password_maps parameter specifies the names of lookup
# tables with one username:password entry per remote hostname. If a
# client will not attempt to authenticate to the remote host.
#
# The Postfix SMTP client opens the lookup table before going to
-# chroot jail, so you can keep the password file in /etc/postfix.
+# chroot jail, so you can leave the password file in /etc/postfix.
#
smtp_auth_passwd_map = hash:/etc/postfix/saslpass
#
ignore_mx_lookup_error = no
+# The smtp_bind_address parameter specifies a numerical network
+# address that the client should bind to when making a connection.
+# This can be used in the main.cf file, or in the master.cf file,
+# for example:
+#
+# smtp ... smtp -o smtp_bind_address=111.222.333.444
+#
+#smtp_bind_address=111.222.333.444
+
# The smtp_skip_4xx_greeting parameter controls what happens when
# an SMTP server greets us with a 4XX status code. By default, Postfix
# backs off. Specify "smtp_skip_4xx_greeting = yes" to move on the
/*
/* void mail_conf_read()
/*
+/* void mail_conf_suck()
+/*
/* void mail_conf_update(name, value)
/* const char *name;
/* const char *value;
/* const char *mail_conf_lookup_eval(name)
/* const char *name;
/* DESCRIPTION
-/* mail_conf_read() reads the global Postfix configuration file, and
+/* mail_conf_suck() reads the global Postfix configuration file, and
/* stores its values into a global configuration dictionary.
/*
+/* mail_conf_read() invokes mail_conf_suck() and assigns the values
+/* to global variables by calling mail_params_init().
+/*
/* The following routines are wrappers around the generic dictionary
/* access routines.
/*
/* mail_conf_read - read global configuration file */
void mail_conf_read(void)
+{
+ mail_conf_suck();
+ mail_params_init();
+}
+
+/* mail_conf_suck - suck in the global configuration file */
+
+void mail_conf_suck(void)
{
char *config_dir;
char *path;
path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
dict_load_file(CONFIG_DICT, path);
myfree(path);
- mail_params_init();
}
/* mail_conf_eval - expand macros in string */
* Basic configuration management.
*/
extern void mail_conf_read(void);
+extern void mail_conf_suck(void);
extern void mail_conf_update(const char *, const char *);
extern const char *mail_conf_lookup(const char *);
quote_822_local(buf, sender);
if (flags & MAIL_COPY_FROM) {
time(&now);
- vstream_fprintf(dst, "From %s %s", *sender == 0 ?
- MAIL_ADDR_MAIL_DAEMON :
- vstring_str(buf),
- asctime(localtime(&now)));
+ vstream_fprintf(dst, "From %s %.24s%s", *sender == 0 ?
+ MAIL_ADDR_MAIL_DAEMON : vstring_str(buf),
+ asctime(localtime(&now)), eol);
}
if (flags & MAIL_COPY_RETURN_PATH) {
vstream_fprintf(dst, "Return-Path: <%s>%s",
#define DEF_SMTP_ALWAYS_EHLO 0
extern bool var_smtp_always_ehlo;
+#define VAR_SMTP_BIND_ADDR "smtp_bind_address"
+#define DEF_SMTP_BIND_ADDR ""
+extern char *var_smtp_bind_addr;
+
/*
* SMTP server. The soft error limit determines how many errors an SMTP
* client may make before we start to slow down; the hard error limit
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20000506"
+#define DEF_MAIL_VERSION "Snapshot-20000507"
extern char *var_mail_version;
/* LICENSE
#define SMTP_ERR_EOF 1 /* unexpected client disconnect */
#define SMTP_ERR_TIME 2 /* time out */
-extern void smtp_jump_setup(VSTREAM *, jmp_buf *);
extern void smtp_timeout_setup(VSTREAM *, int);
extern void smtp_printf(VSTREAM *, const char *,...);
extern int smtp_get(VSTRING *, VSTREAM *, int);
<ul>
-<li><a href="#delay">Postfix responds slowly to SMTP connections</a>
+<li><a href="#delay">Postfix responds slowly to incoming SMTP connections</a>
<li><a href="#numerical_log">Postfix logs SMTP clients as IP
addresses</a>
<hr>
-<a name="delay"><h3>Postfix responds slowly to SMTP connections</h3></a>
+<a name="delay"><h3>Postfix responds slowly to incoming SMTP connections</h3></a>
<dl>
<b>eol=string</b> (default: <b>\n</b>)
The output record delimiter. Typically one would
- use either <b>\r\n</b> or <b>\n</b>. You can specify the usual C-
- style backslash escape sequences.
+ use either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
+ escape sequences are recognized: <b>\a</b> <b>\b</b> <b>\f</b> <b>\n</b> <b>\r</b> <b>\t</b>
+ <b>\v</b> <b>\</b><i>octal</i> and <b>\\</b>.
<b>argv</b>=<i>command</i>... (required)
- The command to be executed. This must be specified
+ The command to be executed. This must be specified
as the last command attribute. The command is exe-
cuted directly, i.e. without interpretation of
- shell meta characters by a shell command inter-
+ shell meta characters by a shell command inter-
preter.
In the command argument vector, the following
macros are recognized and replaced with correspond-
- ing information from the Postfix queue manager
+ ing information from the Postfix queue manager
delivery request:
<b>${extension</b>}
- This macro expands to the extension part of
- a recipient address. For example, with an
+ This macro expands to the extension part of
+ a recipient address. For example, with an
address <i>user+foo@domain</i> the extension is
- <i>foo</i>. A command-line argument that contains
- <b>${extension</b>} expands into as many command-
+ <i>foo</i>. A command-line argument that contains
+ <b>${extension</b>} expands into as many command-
line arguments as there are recipients.
<b>${mailbox</b>}
- This macro expands to the complete local
- part of a recipient address. For example,
- with an address <i>user+foo@domain</i> the mailbox
- is <i>user+foo</i>. A command-line argument that
- contains <b>${mailbox</b>} expands into as many
- command-line arguments as there are recipi-
+ This macro expands to the complete local
+ part of a recipient address. For example,
+ with an address <i>user+foo@domain</i> the mailbox
+ is <i>user+foo</i>. A command-line argument that
+ contains <b>${mailbox</b>} expands into as many
+ command-line arguments as there are recipi-
ents.
<b>${nexthop</b>}
<b>${recipient</b>}
This macro expands to the complete recipient
- address. A command-line argument that con-
+ address. A command-line argument that con-
tains <b>${recipient</b>} expands into as many com-
mand-line arguments as there are recipients.
<b>${sender</b>}
- This macro expands to the envelope sender
+ This macro expands to the envelope sender
address.
<b>${user</b>}
This macro expands to the username part of a
- recipient address. For example, with an
+ recipient address. For example, with an
address <i>user+foo@domain</i> the username part is
<i>user</i>. A command-line argument that contains
- <b>${user</b>} expands into as many command-line
- arguments as there are recipients.
+ <b>${user</b>} expands into as many command-line
PIPE(8) PIPE(8)
- In addition to the form ${<i>name</i>}, the forms $<i>name</i> and
- $(<i>name</i>) are also recognized. Specify <b>$$</b> where a single <b>$</b>
+ arguments as there are recipients.
+
+ In addition to the form ${<i>name</i>}, the forms $<i>name</i> and
+ $(<i>name</i>) are also recognized. Specify <b>$$</b> where a single <b>$</b>
is wanted.
<b>DIAGNOSTICS</b>
- Command exit status codes are expected to follow the con-
+ Command exit status codes are expected to follow the con-
ventions defined in <<b>sysexits.h</b>>.
- Problems and transactions are logged to <b>syslogd</b>(8). Cor-
- rupted message files are marked so that the queue manager
+ Problems and transactions are logged to <b>syslogd</b>(8). Cor-
+ rupted message files are marked so that the queue manager
can move them to the <b>corrupt</b> queue for further inspection.
<b>SECURITY</b>
- This program needs a dual personality 1) to access the
- private Postfix queue and IPC mechanisms, and 2) to exe-
+ This program needs a dual personality 1) to access the
+ private Postfix queue and IPC mechanisms, and 2) to exe-
cute external commands as the specified user. It is there-
fore security sensitive.
<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this program. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
command after a configuration change.
<b>Miscellaneous</b>
<b>mail</b><i>_</i><b>owner</b>
- The process privileges used while not running an
+ The process privileges used while not running an
external command.
<b>Resource</b> <b>controls</b>
- In the text below, <i>transport</i> is the first field in a <b>mas-</b>
+ In the text below, <i>transport</i> is the first field in a <b>mas-</b>
<b>ter.cf</b> entry.
<i>transport_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
Limit the number of parallel deliveries to the same
- destination, for delivery via the named <i>transport</i>.
- The default limit is taken from the <b>default</b><i>_</i><b>desti-</b>
- <b>nation</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter. The limit is
+ destination, for delivery via the named <i>transport</i>.
+ The default limit is taken from the <b>default</b><i>_</i><b>desti-</b>
+ <b>nation</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter. The limit is
enforced by the Postfix queue manager.
<i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit the number of recipients per message deliv-
- ery, for delivery via the named <i>transport</i>. The
- default limit is taken from the <b>default</b><i>_</i><b>destina-</b>
- <b>tion</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter. The limit is
+ Limit the number of recipients per message deliv-
+ ery, for delivery via the named <i>transport</i>. The
+ default limit is taken from the <b>default</b><i>_</i><b>destina-</b>
+ <b>tion</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter. The limit is
enforced by the Postfix queue manager.
<i>transport_</i><b>time</b><i>_</i><b>limit</b>
- Limit the time for delivery to external command,
- for delivery via the named <b>transport</b>. The default
- limit is taken from the <b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b> parame-
- ter. The limit is enforced by the Postfix queue
- manager.
-
+ Limit the time for delivery to external command,
+ for delivery via the named <b>transport</b>. The default
+ limit is taken from the <b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b> parame-
+ ter. The limit is enforced by the Postfix queue
PIPE(8) PIPE(8)
+ manager.
+
<b>SEE</b> <b>ALSO</b>
<a href="bounce.8.html">bounce(8)</a> non-delivery status reports
<a href="master.8.html">master(8)</a> process manager
syslogd(8) system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
-
-
Do not wait for the server response after sending
QUIT.
+ <b>smtp</b><i>_</i><b>bind</b><i>_</i><b>address</b>
+ Numerical network address to bind to when making a
+ connection.
+
<b>Authentication</b> <b>controls</b>
<b>smtp</b><i>_</i><b>enable</b><i>_</i><b>sasl</b><i>_</i><b>auth</b>
- Enable per-session authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
- (SASL). By default, Postfix is built without SASL
+ Enable per-session authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
+ (SASL). By default, Postfix is built without SASL
support.
<b>smtp</b><i>_</i><b>sasl</b><i>_</i><b>password</b><i>_</i><b>maps</b>
- Lookup tables with per-host <i>name</i>:<i>password</i> entries.
- No entry for a host means no attempt to authenti-
+ Lookup tables with per-host <i>name</i>:<i>password</i> entries.
+ No entry for a host means no attempt to authenti-
cate.
<b>smtp</b><i>_</i><b>sasl</b><i>_</i><b>security</b><i>_</i><b>options</b>
<b>Resource</b> <b>controls</b>
<b>smtp</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
Limit the number of parallel deliveries to the same
- destination. The default limit is taken from the
+ destination. The default limit is taken from the
<b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
<b>smtp</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit the number of recipients per message deliv-
- ery. The default limit is taken from the
+ Limit the number of recipients per message deliv-
+ ery. The default limit is taken from the
<b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
<b>Timeout</b> <b>controls</b>
<b>smtp</b><i>_</i><b>connect</b><i>_</i><b>timeout</b>
Timeout in seconds for completing a TCP connection.
- When no connection can be made within the deadline,
- the SMTP client tries the next address on the mail
- exchanger list.
-
SMTP(8) SMTP(8)
+ When no connection can be made within the deadline,
+ the SMTP client tries the next address on the mail
+ exchanger list.
+
<b>smtp</b><i>_</i><b>helo</b><i>_</i><b>timeout</b>
- Timeout in seconds for receiving the SMTP greeting
+ Timeout in seconds for receiving the SMTP greeting
banner. When the server drops the connection with-
- out sending a greeting banner, or when it sends no
+ out sending a greeting banner, or when it sends no
greeting banner within the deadline, the SMTP
client tries the next address on the mail exchanger
list.
<b>smtp</b><i>_</i><b>helo</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>HELO</b> command,
+ Timeout in seconds for sending the <b>HELO</b> command,
and for receiving the server response.
<b>smtp</b><i>_</i><b>mail</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>MAIL</b> <b>FROM</b> com-
+ Timeout in seconds for sending the <b>MAIL</b> <b>FROM</b> com-
mand, and for receiving the server response.
<b>smtp</b><i>_</i><b>rcpt</b><i>_</i><b>timeout</b>
and for receiving the server response.
<b>smtp</b><i>_</i><b>data</b><i>_</i><b>init</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>DATA</b> command,
+ Timeout in seconds for sending the <b>DATA</b> command,
and for receiving the server response.
<b>smtp</b><i>_</i><b>data</b><i>_</i><b>xfer</b><i>_</i><b>timeout</b>
<b>smtp</b><i>_</i><b>data</b><i>_</i><b>done</b><i>_</i><b>timeout</b>
Timeout in seconds for sending the "<b>.</b>" command, and
for receiving the server response. When no response
- is received, a warning is logged that the mail may
+ is received, a warning is logged that the mail may
be delivered multiple times.
<b>smtp</b><i>_</i><b>quit</b><i>_</i><b>timeout</b>
- Timeout in seconds for sending the <b>QUIT</b> command,
+ Timeout in seconds for sending the <b>QUIT</b> command,
and for receiving the server response.
<b>SEE</b> <b>ALSO</b>
syslogd(8) system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
Wietse Venema
IBM T.J. Watson Research
+
+
+
+ 4
+
+
+
+
+
+SMTP(8) SMTP(8)
+
+
P.O. Box 704
Yorktown Heights, NY 10598, USA
- 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
</pre> </body> </html>
$(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
-e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
- @make -f Makefile.in Makefile
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
# do not edit below this line - it is generated by 'make depend'
lmtp.o: lmtp.c
+++ /dev/null
-README.local
-
-This file describes how to use the lmtp service for local delivery.
-You'll need postfix-19991231-pl04 or later for this to work because
-it relies on the "spawn" service.
-
-Configure your Postfix as follows:
-
-/etc/postfix/master.cf:
-
-#local unix - n n - - local
-local unix - - n - - lmtp
- serv=unix:private/lmtpd
-lmtpd unix - n n - - spawn
- user=cyrus:cyrus argv=/usr/local/cyrus/bin/deliver -e -l
-
-
-First, we comment out the original "local" service and define
-a new one based on the "lmtp" client. The "serv=" argument says
-what LMTP server we're to talk to, in this case the "lmtpd"
-service.
-
-The `-l' option to deliver tells it to go into LMTP mode, and the
-`-e' tells it to use the duplicate delivery database, which is
-required in order to use the vacation features of Sieve, the
-filtering language in Cyrus 1.6.X.
-
-A note about spawn, this is a new experimental service. The
-makefile-patch included with this bundle will add spawn to the
-compile targets. However, you may need to check that it actually
-gets installed.
-
-A note about local delivery and the number of recipients. Starting
-with postfix-19991231-pl04, it is now possible to specify the
-maximum number of recipients per message for local delivery. By
-default, this is set to 1 as follows:
-
- local_destination_recipient_limit = 1
-
-You can set it to zero (means no limit) or, safer, set it to some
-reasonable number so that your machine doesn't risk running out of
-resources on a message with an inordinate number of recipients.
-
-Why is this of interest? Well, if a message contains multiple
-recipients, and all these recipients happen to be on the same Cyrus
-partition, then recent (1.6.X) releases of deliver will hard link
-the message to each recipient instead of each recipient getting a
-copy. So you'll probably want to set the above mail.cf value to
-something reasonable to take advantage of this feature in Cyrus.
-
+++ /dev/null
-# script to compare common code between smtp and lmtp
-
-sed '
- s/SMTP/LMTP/g
- s/smtp/lmtp/g
-' $*
DELIVER_REQUEST *request;
int status;
+ /*
+ * Sanity check. This service takes no command-line arguments.
+ */
+ if (argv[0])
+ msg_fatal("unexpected command-line argument: %s", argv[0]);
+
/*
* This routine runs whenever a client connects to the UNIX-domain socket
* dedicated to remote LMTP delivery service. What we see below is a
\fIusername\fR.
.IP "\fBeol=string\fR (default: \fB\en\fR)"
The output record delimiter. Typically one would use either
-\fB\er\en\fR or \fB\en\fR. You can specify the usual C-style
-backslash escape sequences.
+\fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
+sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
+\e\fIoctal\fR and \fB\e\e\fR.
.IP "\fBargv\fR=\fIcommand\fR... (required)"
The command to be executed. This must be specified as the
last command attribute.
Skip servers that greet us with a 5xx status code.
.IP \fBsmtp_skip_quit_response\fR
Do not wait for the server response after sending QUIT.
+.IP \fBsmtp_bind_address\fR
+Numerical network address to bind to when making a connection.
.SH "Authentication controls"
.IP \fBsmtp_enable_sasl_auth\fR
Enable per-session authentication as per RFC 2554 (SASL).
* Initialize from the configuration file. Allow command-line options to
* override compiled-in defaults or configured parameter values.
*/
- mail_conf_read();
- va_start(ap, service);
- while ((key = va_arg(ap, int)) != 0) {
- switch (key) {
- case MAIL_SERVER_INT_TABLE:
- get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
- break;
- case MAIL_SERVER_STR_TABLE:
- get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
- break;
- case MAIL_SERVER_BOOL_TABLE:
- get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
- break;
- case MAIL_SERVER_RAW_TABLE:
- get_mail_conf_raw_table(va_arg(ap, CONFIG_STR_TABLE *));
- break;
- case MAIL_SERVER_PRE_INIT:
- pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
- break;
- case MAIL_SERVER_POST_INIT:
- post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
- break;
- case MAIL_SERVER_LOOP:
- loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
- break;
- case MAIL_SERVER_EXIT:
- multi_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
- break;
- case MAIL_SERVER_PRE_ACCEPT:
- multi_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
- break;
- default:
- msg_panic("%s: unknown argument type: %d", myname, key);
- }
- }
- va_end(ap);
+ mail_conf_suck();
/*
* Pick up policy settings from master process. Shut up error messages to
while ((c = GETOPT(argc, argv, "cDi:lm:n:o:s:St:uv")) > 0) {
switch (c) {
case 'c':
- root_dir = var_queue_dir;
+ root_dir = "setme";
break;
case 'D':
debug_me = 1;
break;
case 'i':
- if ((var_idle_limit = atoi(optarg)) <= 0)
- msg_fatal("invalid max_idle time: %s", optarg);
+ mail_conf_update(VAR_MAX_IDLE, optarg);
break;
case 'l':
alone = 1;
break;
case 'm':
- if ((var_use_limit = atoi(optarg)) <= 0)
- msg_fatal("invalid max_use: %s", optarg);
+ mail_conf_update(VAR_MAX_USE, optarg);
break;
case 'n':
service_name = optarg;
break;
case 'o':
- mail_conf_update(optarg,
- (oval = split_at(optarg, '=')) ? oval : "");
- mail_params_init(); /* XXX */
+ if ((oval = split_at(optarg, '=')) == 0)
+ oval = "";
+ mail_conf_update(optarg, oval);
break;
case 's':
if ((socket_count = atoi(optarg)) <= 0)
stream = VSTREAM_IN;
break;
case 'u':
- user_name = var_mail_owner;
+ user_name = "setme";
break;
case 't':
transport = optarg;
}
}
+ /*
+ * Initialize generic parameters.
+ */
+ mail_params_init();
+
+ /*
+ * Application-specific initialization.
+ */
+ va_start(ap, service);
+ while ((key = va_arg(ap, int)) != 0) {
+ switch (key) {
+ case MAIL_SERVER_INT_TABLE:
+ get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
+ break;
+ case MAIL_SERVER_STR_TABLE:
+ get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
+ break;
+ case MAIL_SERVER_BOOL_TABLE:
+ get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
+ break;
+ case MAIL_SERVER_RAW_TABLE:
+ get_mail_conf_raw_table(va_arg(ap, CONFIG_STR_TABLE *));
+ break;
+ case MAIL_SERVER_PRE_INIT:
+ pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
+ break;
+ case MAIL_SERVER_POST_INIT:
+ post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
+ break;
+ case MAIL_SERVER_LOOP:
+ loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
+ break;
+ case MAIL_SERVER_EXIT:
+ multi_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
+ break;
+ case MAIL_SERVER_PRE_ACCEPT:
+ multi_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
+ break;
+ default:
+ msg_panic("%s: unknown argument type: %d", myname, key);
+ }
+ }
+ va_end(ap);
+
+ if (root_dir)
+ root_dir = var_queue_dir;
+ if (user_name)
+ user_name = var_mail_owner;
+
/*
* If not connected to stdin, stdin must not be a terminal.
*/
* Initialize from the configuration file. Allow command-line options to
* override compiled-in defaults or configured parameter values.
*/
- mail_conf_read();
- va_start(ap, service);
- while ((key = va_arg(ap, int)) != 0) {
- switch (key) {
- case MAIL_SERVER_INT_TABLE:
- get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
- break;
- case MAIL_SERVER_STR_TABLE:
- get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
- break;
- case MAIL_SERVER_BOOL_TABLE:
- get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
- break;
- case MAIL_SERVER_RAW_TABLE:
- get_mail_conf_raw_table(va_arg(ap, CONFIG_STR_TABLE *));
- break;
- case MAIL_SERVER_PRE_INIT:
- pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
- break;
- case MAIL_SERVER_POST_INIT:
- post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
- break;
- case MAIL_SERVER_LOOP:
- loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
- break;
- case MAIL_SERVER_EXIT:
- single_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
- break;
- case MAIL_SERVER_PRE_ACCEPT:
- single_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
- break;
- default:
- msg_panic("%s: unknown argument type: %d", myname, key);
- }
- }
- va_end(ap);
+ mail_conf_suck();
/*
* Pick up policy settings from master process. Shut up error messages to
while ((c = GETOPT(argc, argv, "cDi:lm:n:o:s:St:uv")) > 0) {
switch (c) {
case 'c':
- root_dir = var_queue_dir;
+ root_dir = "setme";
break;
case 'D':
debug_me = 1;
break;
case 'i':
- if ((var_idle_limit = atoi(optarg)) <= 0)
- msg_fatal("invalid max_idle time: %s", optarg);
+ mail_conf_update(VAR_MAX_IDLE, optarg);
break;
case 'l':
alone = 1;
break;
case 'm':
- if ((var_use_limit = atoi(optarg)) <= 0)
- msg_fatal("invalid max_use: %s", optarg);
+ mail_conf_update(VAR_MAX_USE, optarg);
break;
case 'n':
service_name = optarg;
break;
case 'o':
- mail_conf_update(optarg,
- (oval = split_at(optarg, '=')) ? oval : "");
- mail_params_init(); /* XXX */
+ if ((oval = split_at(optarg, '=')) == 0)
+ oval = "";
+ mail_conf_update(optarg, oval);
break;
case 's':
if ((socket_count = atoi(optarg)) <= 0)
stream = VSTREAM_IN;
break;
case 'u':
- user_name = var_mail_owner;
+ user_name = "setme";
break;
case 't':
transport = optarg;
}
}
+ /*
+ * Initialize generic parameters.
+ */
+ mail_params_init();
+
+ /*
+ * Application-specific initialization.
+ */
+ va_start(ap, service);
+ while ((key = va_arg(ap, int)) != 0) {
+ switch (key) {
+ case MAIL_SERVER_INT_TABLE:
+ get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
+ break;
+ case MAIL_SERVER_STR_TABLE:
+ get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
+ break;
+ case MAIL_SERVER_BOOL_TABLE:
+ get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
+ break;
+ case MAIL_SERVER_RAW_TABLE:
+ get_mail_conf_raw_table(va_arg(ap, CONFIG_STR_TABLE *));
+ break;
+ case MAIL_SERVER_PRE_INIT:
+ pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
+ break;
+ case MAIL_SERVER_POST_INIT:
+ post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
+ break;
+ case MAIL_SERVER_LOOP:
+ loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
+ break;
+ case MAIL_SERVER_EXIT:
+ single_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
+ break;
+ case MAIL_SERVER_PRE_ACCEPT:
+ single_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
+ break;
+ default:
+ msg_panic("%s: unknown argument type: %d", myname, key);
+ }
+ }
+ va_end(ap);
+
+ if (root_dir)
+ root_dir = var_queue_dir;
+ if (user_name)
+ user_name = var_mail_owner;
+
/*
* If not connected to stdin, stdin must not be a terminal.
*/
int time_left = 0;
int fd;
-
if (msg_verbose)
msg_info("%s: trigger arrived", myname);
* Initialize from the configuration file. Allow command-line options to
* override compiled-in defaults or configured parameter values.
*/
- mail_conf_read();
- va_start(ap, service);
- while ((key = va_arg(ap, int)) != 0) {
- switch (key) {
- case MAIL_SERVER_INT_TABLE:
- get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
- break;
- case MAIL_SERVER_STR_TABLE:
- get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
- break;
- case MAIL_SERVER_BOOL_TABLE:
- get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
- break;
- case MAIL_SERVER_RAW_TABLE:
- get_mail_conf_raw_table(va_arg(ap, CONFIG_STR_TABLE *));
- break;
- case MAIL_SERVER_PRE_INIT:
- pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
- break;
- case MAIL_SERVER_POST_INIT:
- post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
- break;
- case MAIL_SERVER_LOOP:
- loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
- break;
- case MAIL_SERVER_EXIT:
- trigger_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
- break;
- case MAIL_SERVER_PRE_ACCEPT:
- trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
- break;
- default:
- msg_panic("%s: unknown argument type: %d", myname, key);
- }
- }
- va_end(ap);
+ mail_conf_suck();
/*
* Pick up policy settings from master process. Shut up error messages to
while ((c = GETOPT(argc, argv, "cDi:lm:n:o:s:St:uv")) > 0) {
switch (c) {
case 'c':
- root_dir = var_queue_dir;
+ root_dir = "setme";
break;
case 'D':
debug_me = 1;
break;
case 'i':
- if ((var_idle_limit = atoi(optarg)) <= 0)
- msg_fatal("invalid max_idle time: %s", optarg);
+ mail_conf_update(VAR_MAX_IDLE, optarg);
break;
case 'l':
alone = 1;
break;
case 'm':
- if ((var_use_limit = atoi(optarg)) <= 0)
- msg_fatal("invalid max_use: %s", optarg);
+ mail_conf_update(VAR_MAX_USE, optarg);
break;
case 'n':
service_name = optarg;
break;
case 'o':
- mail_conf_update(optarg,
- (oval = split_at(optarg, '=')) ? oval : "");
- mail_params_init(); /* XXX */
+ if ((oval = split_at(optarg, '=')) == 0)
+ oval = "";
+ mail_conf_update(optarg, oval);
break;
case 's':
if ((socket_count = atoi(optarg)) <= 0)
transport = optarg;
break;
case 'u':
- user_name = var_mail_owner;
+ user_name = "setme";
break;
case 'v':
msg_verbose++;
}
}
+ /*
+ * Initialize generic parameters.
+ */
+ mail_params_init();
+
+ /*
+ * Application-specific initialization.
+ */
+ va_start(ap, service);
+ while ((key = va_arg(ap, int)) != 0) {
+ switch (key) {
+ case MAIL_SERVER_INT_TABLE:
+ get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
+ break;
+ case MAIL_SERVER_STR_TABLE:
+ get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
+ break;
+ case MAIL_SERVER_BOOL_TABLE:
+ get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
+ break;
+ case MAIL_SERVER_RAW_TABLE:
+ get_mail_conf_raw_table(va_arg(ap, CONFIG_STR_TABLE *));
+ break;
+ case MAIL_SERVER_PRE_INIT:
+ pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
+ break;
+ case MAIL_SERVER_POST_INIT:
+ post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
+ break;
+ case MAIL_SERVER_LOOP:
+ loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
+ break;
+ case MAIL_SERVER_EXIT:
+ trigger_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
+ break;
+ case MAIL_SERVER_PRE_ACCEPT:
+ trigger_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
+ break;
+ default:
+ msg_panic("%s: unknown argument type: %d", myname, key);
+ }
+ }
+ va_end(ap);
+
+ if (root_dir)
+ root_dir = var_queue_dir;
+ if (user_name)
+ user_name = var_mail_owner;
+
/*
* If not connected to stdin, stdin must not be a terminal.
*/
/* \fIusername\fR.
/* .IP "\fBeol=string\fR (default: \fB\en\fR)"
/* The output record delimiter. Typically one would use either
-/* \fB\er\en\fR or \fB\en\fR. You can specify the usual C-style
-/* backslash escape sequences.
+/* \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
+/* sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
+/* \e\fIoctal\fR and \fB\e\e\fR.
/* .IP "\fBargv\fR=\fIcommand\fR... (required)"
/* The command to be executed. This must be specified as the
/* last command attribute.
* eol=string
*/
else if (strncasecmp("eol=", *argv, sizeof("eol=") - 1) == 0) {
- unescape(attr->eol, *argv + sizeof("eol=") -1);
+ unescape(attr->eol, *argv + sizeof("eol=") - 1);
}
/*
/* Skip servers that greet us with a 5xx status code.
/* .IP \fBsmtp_skip_quit_response\fR
/* Do not wait for the server response after sending QUIT.
+/* .IP \fBsmtp_bind_address\fR
+/* Numerical network address to bind to when making a connection.
/* .SH "Authentication controls"
/* .IP \fBsmtp_enable_sasl_auth\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
char *var_smtp_sasl_opts;
char *var_smtp_sasl_passwd;
bool var_smtp_sasl_enable;
+char *var_smtp_bind_addr;
/*
* Global variables. smtp_errno is set by the address lookup routines and by
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
VAR_SMTP_SASL_PASSWD, DEF_SMTP_SASL_PASSWD, &var_smtp_sasl_passwd, 0, 0,
VAR_SMTP_SASL_OPTS, DEF_SMTP_SASL_OPTS, &var_smtp_sasl_opts, 0, 0,
+ VAR_SMTP_BIND_ADDR, DEF_SMTP_BIND_ADDR, &var_smtp_bind_addr, 0, 0,
0,
};
static CONFIG_INT_TABLE int_table[] = {
#include <strings.h>
#endif
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffff
+#endif
+
/* Utility library. */
#include <msg.h>
if ((sock = socket(sin.sin_family, SOCK_STREAM, 0)) < 0)
msg_fatal("%s: socket: %m", myname);
+ /*
+ * Allow the sysadmin to specify the source address, for example, as "-o
+ * smtp_bind_address=x.x.x.x" in the master.cf file.
+ */
+ if (*var_smtp_bind_addr) {
+ sin.sin_addr.s_addr = inet_addr(var_smtp_bind_addr);
+ if (sin.sin_addr.s_addr == INADDR_NONE)
+ msg_fatal("%s: bad %s parameter: %s",
+ myname, VAR_SMTP_BIND_ADDR, var_smtp_bind_addr);
+ if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0)
+ msg_warn("%s: bind %s: %m", myname, inet_ntoa(sin.sin_addr));
+ if (msg_verbose)
+ msg_info("%s: bind %s", myname, inet_ntoa(sin.sin_addr));
+ }
+
/*
* When running as a virtual host, bind to the virtual interface so that
* the mail appears to come from the "right" machine address.
*/
- addr_list = own_inet_addr_list();
- if (addr_list->used == 1) {
- sin.sin_port = 0;
+ else if ((addr_list = own_inet_addr_list())->used == 1) {
memcpy((char *) &sin.sin_addr, addr_list->addrs, sizeof(sin.sin_addr));
inaddr = ntohl(sin.sin_addr.s_addr);
if (!IN_CLASSA(inaddr)
if ((*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len)) == 0)
return (SASL_NOMEM);
(*psecret)->len = len;
- strcpy((*psecret)->data, state->sasl_passwd);
+ memcpy((*psecret)->data, state->sasl_passwd, len + 1);
return (SASL_OK);
}
smtpd_chat_reply(state, "250-SIZE");
smtpd_chat_reply(state, "250-ETRN");
#ifdef USE_SASL_AUTH
- if (var_smtpd_sasl_enable)
+ if (SMTPD_STAND_ALONE(state) == 0 && var_smtpd_sasl_enable)
smtpd_chat_reply(state, "250-AUTH %s", state->sasl_mechanism_list);
#endif
smtpd_chat_reply(state, "250 8BITMIME");
}
/*
- * Report trouble. Log a warning only if we are going to sleep+reject.
+ * Report trouble. Log a warning only if we are going to sleep+reject so
+ * that attackers can't flood our logfiles.
*/
if ((naddr < 1 && !allow_empty_addr)
|| naddr > 1
if ((state->msg_size = off_cvt_string(arg + 5)) < 0)
state->msg_size = 0;
#ifdef USE_SASL_AUTH
- } else if (var_smtpd_sasl_enable && strncasecmp(arg, "AUTH=", 5) == 0) {
+ } else if (SMTPD_STAND_ALONE(state) == 0
+ && var_smtpd_sasl_enable
+ && strncasecmp(arg, "AUTH=", 5) == 0) {
if ((err = smtpd_sasl_mail_opt(state, arg + 5)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
/*
- * The table of all SMTP commands that we know.
+ * The table of all SMTP commands that we know. Set the junk limit flag on
+ * any command that can be repeated an arbitrary number of times without
+ * triggering a tarpit delay of some sort.
*/
typedef struct SMTPD_CMD {
char *name;
#define SMTPD_CMD_FLAG_LIMIT (1<<0) /* limit usage */
static SMTPD_CMD smtpd_cmd_table[] = {
- "HELO", helo_cmd, 0,
- "EHLO", ehlo_cmd, 0,
+ "HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT,
+ "EHLO", ehlo_cmd, SMTPD_CMD_FLAG_LIMIT,
#ifdef USE_SASL_AUTH
"AUTH", smtpd_sasl_auth_cmd, 0,
* that abort the connection and go into a connect-error-disconnect loop;
* sleep-on-anything slows down clients that make an excessive number of
* errors within a session.
+ *
+ * Flush unsent output before sleeping. Pipelined error responses could
+ * result in client-side timeouts.
*/
if (state->error_count > var_smtpd_soft_erlim)
- sleep(state->error_count), vstream_fflush(state->client);
+ vstream_fflush(state->client), sleep(state->error_count);
else if (STR(state->buffer)[0] == '4' || STR(state->buffer)[0] == '5')
- sleep(var_smtpd_err_sleep), vstream_fflush(state->client);
+ vstream_fflush(state->client), sleep(var_smtpd_err_sleep);
smtp_fputs(STR(state->buffer), LEN(state->buffer), state->client);
}
void smtpd_sasl_connect(SMTPD_STATE *state)
{
- int sasl_mechanism_count;
+ unsigned sasl_mechanism_count;
sasl_security_properties_t sec_props;
/*
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1);
}
- if (!var_smtpd_sasl_enable) {
+ if (SMTPD_STAND_ALONE(state) || !var_smtpd_sasl_enable) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: authentication not enabled");
return (-1);
+
/*++
/* NAME
/* dict_ldap 3
/* .IP \fIldapsource_\fRresult_attribute
/* The attribute returned by the search, in which to find
/* RFC822 addresses, for example \fImaildrop\fR.
+/* .IP \fIldapsource_\fRscope
+/* LDAP search scope: sub, base, or one.
/* .IP \fIldapsource_\fRbind
/* Whether or not to bind to the server -- LDAP v3 implementations don't
/* require it, which saves some overhead.
/* If you must bind to the server, do it with this distinguished name ...
/* .IP \fIldapsource_\fRbind_pw
/* \&... and this password.
-/* BUGS
-/* Thrice a year, needed or not.
+/* .IP \fIldapsource_\fRcache
+/* Whether or not to turn on client-side caching.
+/* .IP \fIldapsource_\fRcache_expiry
+/* If you do cache results, expire them after this many seconds.
+/* .IP \fIldapsource_\fRcache_size
+/* The cache size in bytes. Does nothing if the cache is off, of course.
+/* .IP \fIldapsource_\fRdereference
+/* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page.
/* SEE ALSO
/* dict(3) generic dictionary manager
-/* DIAGNOSTICS
-/* Warnings: unable to connect to server, unable to bind to server.
/* AUTHOR(S)
/* Prabhat K Singh
/* VSNL, Bombay, India.
/* Yorktown Heights, NY 10532, USA
/*
/* John Hensley
-/* stormroll@yahoo.com
+/* roll@ic.net
/*
/*--*/
#include <sys/time.h>
#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include "dict.h"
#include "dict_ldap.h"
-/* Global library. */
-
-#include "../global/mail_conf.h" /* XXX Fixme. */
-
/*
- * structure containing all the configuration parameters for a given
- * LDAP source, plus its connection handle
+ * Structure containing all the configuration parameters for a given
+ * LDAP source, plus its connection handle.
*/
typedef struct {
DICT dict; /* generic member */
char *ldapsource;
char *server_host;
int server_port;
+ int scope;
char *search_base;
char *query_filter;
char *result_attribute;
char *bind_dn;
char *bind_pw;
int timeout;
+ int cache;
+ long cache_expiry;
+ long cache_size;
+ int dereference;
LDAP *ld;
} DICT_LDAP;
- /*
- * LDAP connection timeout support.
- */
+/*
+ * LDAP connection timeout support.
+ */
static jmp_buf env;
static void dict_ldap_timeout(int unused_sig)
longjmp(env, 1);
}
+/* Establish a connection to the LDAP server. */
+static int dict_ldap_connect(DICT_LDAP *dict_ldap)
+{
+ char *myname = "dict_ldap_connect";
+ void (*saved_alarm) (int);
+ int rc = 0;
+
+ dict_errno = 0;
+
+ if (msg_verbose)
+ msg_info("%s: Connecting to server %s", myname,
+ dict_ldap->server_host);
+
+ if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
+ msg_warn("%s: Error setting signal handler for open timeout: %m",
+ myname);
+ dict_errno = DICT_ERR_RETRY;
+ return (-1);
+ }
+ alarm(dict_ldap->timeout);
+ if (setjmp(env) == 0)
+ dict_ldap->ld = ldap_open(dict_ldap->server_host,
+ (int) dict_ldap->server_port);
+ alarm(0);
+
+ if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
+ msg_warn("%s: Error resetting signal handler after open: %m",
+ myname);
+ dict_errno = DICT_ERR_RETRY;
+ return (-1);
+ }
+ if (dict_ldap->ld == NULL) {
+ msg_warn("%s: Unable to connect to LDAP server %s",
+ myname, dict_ldap->server_host);
+ dict_errno = DICT_ERR_RETRY;
+ return (-1);
+ }
+
+ /*
+ * Configure alias dereferencing for this connection. Thanks to Mike
+ * Mattice for this.
+ */
+ dict_ldap->ld->ld_deref = dict_ldap->dereference;
+
+ /*
+ * If this server requires a bind, do so. Thanks to Sam Tardieu for
+ * noticing that the original bind call was broken.
+ */
+ if (dict_ldap->bind) {
+ if (msg_verbose)
+ msg_info("%s: Binding to server %s as dn %s",
+ myname, dict_ldap->server_host, dict_ldap->bind_dn);
+
+ rc = ldap_bind_s(dict_ldap->ld, dict_ldap->bind_dn,
+ dict_ldap->bind_pw, LDAP_AUTH_SIMPLE);
+
+ if (rc != LDAP_SUCCESS) {
+ msg_warn("%s: Unable to bind to server %s as %s: %d (%s)",
+ myname, dict_ldap->server_host, dict_ldap->bind_dn,
+ rc, ldap_err2string(rc));
+ dict_errno = DICT_ERR_RETRY;
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("%s: Successful bind to server %s as %s ",
+ myname, dict_ldap->server_host, dict_ldap->bind_dn);
+ }
+
+ /*
+ * Set up client-side caching if it's configured.
+ */
+ if (dict_ldap->cache) {
+ if (msg_verbose)
+ msg_info
+ ("%s: Enabling %ld-byte cache for %s with %ld-second expiry",
+ myname, dict_ldap->cache_size, dict_ldap->ldapsource,
+ dict_ldap->cache_expiry);
+
+ rc = ldap_enable_cache(dict_ldap->ld, dict_ldap->cache_expiry,
+ dict_ldap->cache_size);
+ if (rc != LDAP_SUCCESS) {
+ msg_warn
+ ("%s: Unable to configure cache for %s: %d (%s) -- continuing",
+ myname, dict_ldap->ldapsource, rc, ldap_err2string(rc));
+ } else {
+ if (msg_verbose)
+ msg_info("%s: Caching enabled for %s",
+ myname, dict_ldap->ldapsource);
+ }
+ }
+ if (msg_verbose)
+ msg_info("%s: Cached connection handle for LDAP source %s",
+ myname, dict_ldap->ldapsource);
+
+ return (0);
+}
+
/* dict_ldap_lookup - find database entry */
static const char *dict_ldap_lookup(DICT *dict, const char *name)
struct timeval tv;
VSTRING *escaped_name = 0,
*filter_buf = 0;
- char **attr_values;
+ char *result_attributes[1],
+ **attr_values;
long i = 0;
int rc = 0;
- void (*saved_alarm) (int);
char *sub,
*end;
dict_errno = 0;
/*
- * Initialize.
+ * Initialize the result holder.
*/
if (result == 0)
result = vstring_alloc(2);
-
vstring_strcpy(result, "");
if (msg_verbose)
msg_info("%s: In dict_ldap_lookup", myname);
+ /*
+ * Connect to the LDAP server, if necessary.
+ */
if (dict_ldap->ld == NULL) {
if (msg_verbose)
- msg_info("%s: no existing connection for ldapsource %s, reopening",
- myname, dict_ldap->ldapsource);
+ msg_info
+ ("%s: No existing connection for ldapsource %s, reopening",
+ myname, dict_ldap->ldapsource);
- if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
- msg_warn("%s: error setting signal handler for open timeout: %m", myname);
- dict_errno = DICT_ERR_RETRY;
- return (0);
- }
- if (msg_verbose)
- msg_info("%s: connecting to server %s", myname,
- dict_ldap->server_host);
+ dict_ldap_connect(dict_ldap);
- alarm(dict_ldap->timeout);
- if (setjmp(env) == 0)
- dict_ldap->ld = ldap_open(dict_ldap->server_host,
- (int) dict_ldap->server_port);
- alarm(0);
-
- if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
- msg_warn("%s: error resetting signal handler after open: %m", myname);
- dict_errno = DICT_ERR_RETRY;
+ /*
+ * if dict_ldap_connect() set dict_errno, abort.
+ */
+ if (dict_errno)
return (0);
- }
- if (msg_verbose)
- msg_info("%s: after ldap_open", myname);
+ } else if (msg_verbose)
+ msg_info("%s: Using existing connection for ldapsource %s",
+ myname, dict_ldap->ldapsource);
- if (dict_ldap->ld == NULL) {
- msg_warn("%s: Unable to contact LDAP server %s",
- myname, dict_ldap->server_host);
- dict_errno = DICT_ERR_RETRY;
- return (0);
- } else {
-
- /*
- * If this server requires a bind, do so.
- */
- if (dict_ldap->bind) {
- if (msg_verbose)
- msg_info("%s: about to bind to server %s as dn %s", myname,
- dict_ldap->server_host, dict_ldap->bind_dn);
-
- rc = ldap_bind_s(dict_ldap->ld, dict_ldap->bind_dn,
- dict_ldap->bind_pw, LDAP_AUTH_SIMPLE);
- if (rc != LDAP_SUCCESS) {
- msg_warn("%s: Unable to bind to server %s as %s (%d -- %s): ", myname, dict_ldap->server_host, dict_ldap->bind_dn, rc, ldap_err2string(rc));
- dict_errno = DICT_ERR_RETRY;
- return (0);
- } else {
- if (msg_verbose)
- msg_info("%s: Successful bind to server %s as %s (%d -- %s): ", myname, dict_ldap->server_host, dict_ldap->bind_dn, rc, ldap_err2string(rc));
- }
- }
- if (msg_verbose)
- msg_info("%s: cached connection handle for LDAP source %s",
- myname, dict_ldap->ldapsource);
- }
- }
/*
* Prepare the query.
/*
* If any characters in the supplied address should be escaped per RFC
- * 2254, do so.
+ * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
+ * Samuel Tardieu for spotting that wildcard searches were being done in
+ * the first place, which prompted the ill-conceived lookup_wildcards
+ * parameter and then this more comprehensive mechanism.
*/
-
end = (char *) name + strlen((char *) name);
sub = (char *) strpbrk((char *) name, "*()\\\0");
if (sub && sub != end) {
if (msg_verbose)
- msg_info("%s: found character(s) in %s that must be escaped", myname, name);
+ msg_info("%s: Found character(s) in %s that must be escaped",
+ myname, name);
for (sub = (char *) name; sub != end; sub++) {
switch (*sub) {
case '*':
}
}
if (msg_verbose)
- msg_info("%s: after escaping, it's %s", myname, vstring_str(escaped_name));
+ msg_info("%s: After escaping, it's %s", myname,
+ vstring_str(escaped_name));
} else
vstring_strcpy(escaped_name, (char *) name);
- /* Does the supplied query_filter even include a substitution? */
- if (strstr(dict_ldap->query_filter, "%s") == NULL) {
- /* No, log the fact and continue. */
- msg_warn("%s: fixed query_filter %s is probably useless", myname,
+ /*
+ * Does the supplied query_filter even include a substitution?
+ */
+ if ((char *) strstr(dict_ldap->query_filter, "%s") == NULL) {
+
+ /*
+ * No, log the fact and continue.
+ */
+ msg_warn("%s: Fixed query_filter %s is probably useless", myname,
dict_ldap->query_filter);
vstring_strcpy(filter_buf, dict_ldap->query_filter);
} else {
- /* Yes, replace all instances of %s with the address to look up. */
+ /*
+ * Yes, replace all instances of %s with the address to look up.
+ */
sub = dict_ldap->query_filter;
end = sub + strlen(dict_ldap->query_filter);
while (sub < end) {
*/
if (*(sub) == '%') {
if ((sub + 1) != end && *(sub + 1) != 's')
- msg_warn("%s: invalid lookup substitution format '%%%c'!", myname, *(sub + 1));
+ msg_warn
+ ("%s: Invalid lookup substitution format '%%%c'!",
+ myname, *(sub + 1));
vstring_strcat(filter_buf, vstring_str(escaped_name));
sub++;
} else
}
}
- /* On to the search. */
+ /*
+ * On to the search.
+ */
if (msg_verbose)
- msg_info("%s: searching with filter %s", myname,
+ msg_info("%s: Searching with filter %s", myname,
vstring_str(filter_buf));
+ /*
+ * Put result_attribute in an array, so the search can return only that
+ * attribute and not the entire entry.
+ */
+ result_attributes[0] = dict_ldap->result_attribute;
+
if ((rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
- LDAP_SCOPE_SUBTREE,
+ dict_ldap->scope,
vstring_str(filter_buf),
- 0, 0, &tv, &res)) == LDAP_SUCCESS) {
+ result_attributes,
+ 0, &tv, &res)) == LDAP_SUCCESS) {
/*
* Search worked; extract the requested result_attribute.
*/
if (msg_verbose)
- msg_info("%s: search found %d matches", myname,
+ msg_info("%s: Search found %d match(es)", myname,
ldap_count_entries(dict_ldap->ld, res));
- /* There could have been lots of hits. */
- for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL; entry = ldap_next_entry(dict_ldap->ld, entry)) {
+ /*
+ * There could have been lots of hits.
+ */
+ for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL;
+ entry = ldap_next_entry(dict_ldap->ld, entry)) {
- /* And each entry could have multiple attributes. */
+ /*
+ * And each entry could have multiple attributes.
+ */
attr_values = ldap_get_values(dict_ldap->ld, entry,
dict_ldap->result_attribute);
if (attr_values == NULL) {
- msg_warn("%s: entry doesn't have any values for %s", myname, dict_ldap->result_attribute);
+ msg_warn("%s: Entry doesn't have any values for %s",
+ myname, dict_ldap->result_attribute);
continue;
}
}
ldap_value_free(attr_values);
}
+ if (dict_ldap->ld->ld_errno != LDAP_SUCCESS)
+ msg_warn
+ ("%s: Had some trouble with entries returned by search: %s",
+ myname, ldap_err2string(dict_ldap->ld->ld_errno));
if (msg_verbose)
- msg_info("%s: search returned: %s", myname, vstring_str(result));
+ msg_info("%s: Search returned %s", myname,
+ VSTRING_LEN(result) >
+ 0 ? vstring_str(result) : "nothing");
} else {
- /* Rats. That didn't work. */
- msg_warn("%s: search error %d: %s ", myname, rc,
+
+ /*
+ * Rats. The search didn't work.
+ */
+ msg_warn("%s: Search error %d: %s ", myname, rc,
ldap_err2string(rc));
/*
ldap_unbind(dict_ldap->ld);
dict_ldap->ld = NULL;
- /* And tell the caller to try again later. */
+ /*
+ * And tell the caller to try again later.
+ */
dict_errno = DICT_ERR_RETRY;
}
- /* Cleanup. */
+ /*
+ * Cleanup.
+ */
if (res != 0)
ldap_msgfree(res);
if (filter_buf != 0)
static void dict_ldap_update(DICT *dict, const char *unused_name,
const char *unused_value)
{
- msg_fatal("dict_ldap_update: operation not implemented");
+ msg_fatal("dict_ldap_update: Operation not implemented");
}
/* dict_ldap_close - disassociate from data base */
DICT_LDAP *dict_ldap;
VSTRING *config_param;
int rc = 0;
- void (*saved_alarm) (int);
+ char *scope;
dict_ldap = (DICT_LDAP *) mymalloc(sizeof(*dict_ldap));
dict_ldap->dict.lookup = dict_ldap_lookup;
dict_ldap->dict.flags = dict_flags | DICT_FLAG_FIXED;
if (msg_verbose)
- msg_info("%s: using LDAP source %s", myname, ldapsource);
+ msg_info("%s: Using LDAP source %s", myname, ldapsource);
dict_ldap->ldapsource = mystrdup(ldapsource);
msg_info("%s: %s is %d", myname, vstring_str(config_param),
dict_ldap->server_port);
+ /*
+ * Scope handling thanks to Carsten Hoeger of SuSE.
+ */
+ vstring_sprintf(config_param, "%s_scope", ldapsource);
+ scope =
+ (char *) get_mail_conf_str(vstring_str(config_param), "sub", 0, 0);
+
+ if (strcasecmp(scope, "one") == 0) {
+ dict_ldap->scope = LDAP_SCOPE_ONELEVEL;
+ if (msg_verbose)
+ msg_info("%s: %s is LDAP_SCOPE_ONELEVEL", myname,
+ vstring_str(config_param));
+
+ } else if (strcasecmp(scope, "base") == 0) {
+ dict_ldap->scope = LDAP_SCOPE_BASE;
+ if (msg_verbose)
+ msg_info("%s: %s is LDAP_SCOPE_BASE", myname,
+ vstring_str(config_param));
+
+ } else {
+ dict_ldap->scope = LDAP_SCOPE_SUBTREE;
+ if (msg_verbose)
+ msg_info("%s: %s is LDAP_SCOPE_SUBTREE", myname,
+ vstring_str(config_param));
+
+ }
+
+ myfree(scope);
+
vstring_sprintf(config_param, "%s_search_base", ldapsource);
- dict_ldap->search_base =
- mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "", 0, 0));
+ dict_ldap->search_base = mystrdup((char *)
+ get_mail_conf_str(vstring_str
+ (config_param), "",
+ 0, 0));
if (msg_verbose)
msg_info("%s: %s is %s", myname, vstring_str(config_param),
dict_ldap->search_base);
- /* get configured value of "ldapsource_timeout"; default to 10 */
+ /*
+ * get configured value of "ldapsource_timeout"; default to 10 seconds
+ *
+ * Thanks to Manuel Guesdon for spotting that this wasn't really getting
+ * set.
+ */
vstring_sprintf(config_param, "%s_timeout", ldapsource);
- dict_ldap->timeout = get_mail_conf_int(vstring_str(config_param), 10, 0, 0);
+ dict_ldap->timeout =
+ get_mail_conf_int(vstring_str(config_param), 10, 0, 0);
if (msg_verbose)
msg_info("%s: %s is %d", myname, vstring_str(config_param),
dict_ldap->timeout);
vstring_sprintf(config_param, "%s_query_filter", ldapsource);
dict_ldap->query_filter =
mystrdup((char *) get_mail_conf_str(vstring_str(config_param),
- "(mailacceptinggeneralid=%s)", 0, 0));
+ "(mailacceptinggeneralid=%s)",
+ 0, 0));
if (msg_verbose)
msg_info("%s: %s is %s", myname, vstring_str(config_param),
dict_ldap->query_filter);
msg_info("%s: %s is %s", myname, vstring_str(config_param),
dict_ldap->result_attribute);
- /* get configured value of "ldapsource_bind"; default to true */
+ /*
+ * get configured value of "ldapsource_bind"; default to true
+ */
vstring_sprintf(config_param, "%s_bind", ldapsource);
dict_ldap->bind = get_mail_conf_bool(vstring_str(config_param), 1);
if (msg_verbose)
msg_info("%s: %s is %d", myname, vstring_str(config_param),
dict_ldap->bind);
- /* get configured value of "ldapsource_bind_dn"; default to "" */
+ /*
+ * get configured value of "ldapsource_bind_dn"; default to ""
+ */
vstring_sprintf(config_param, "%s_bind_dn", ldapsource);
- dict_ldap->bind_dn =
- mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "", 0, 0));
+ dict_ldap->bind_dn = mystrdup((char *)
+ get_mail_conf_str(vstring_str
+ (config_param), "", 0,
+ 0));
if (msg_verbose)
msg_info("%s: %s is %s", myname, vstring_str(config_param),
dict_ldap->bind_dn);
- /* get configured value of "ldapsource_bind_pw"; default to "" */
+ /*
+ * get configured value of "ldapsource_bind_pw"; default to ""
+ */
vstring_sprintf(config_param, "%s_bind_pw", ldapsource);
- dict_ldap->bind_pw =
- mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "", 0, 0));
+ dict_ldap->bind_pw = mystrdup((char *)
+ get_mail_conf_str(vstring_str
+ (config_param), "", 0,
+ 0));
if (msg_verbose)
msg_info("%s: %s is %s", myname, vstring_str(config_param),
dict_ldap->bind_pw);
/*
- * establish the connection to the LDAP server
+ * get configured value of "ldapsource_cache"; default to false
*/
+ vstring_sprintf(config_param, "%s_cache", ldapsource);
+ dict_ldap->cache = get_mail_conf_bool(vstring_str(config_param), 0);
+ if (msg_verbose)
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->cache);
+ /*
+ * get configured value of "ldapsource_cache_expiry"; default to 30
+ * seconds
+ */
+ vstring_sprintf(config_param, "%s_cache_expiry", ldapsource);
+ dict_ldap->cache_expiry = get_mail_conf_int(vstring_str(config_param),
+ 30, 0, 0);
if (msg_verbose)
- msg_info("%s: connecting to server %s", myname,
- dict_ldap->server_host);
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->cache_expiry);
- if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
- msg_warn("%s: error setting signal handler for open timeout: %m", myname);
- dict_errno = DICT_ERR_RETRY;
- return (0);
- }
- alarm(dict_ldap->timeout);
- if (setjmp(env) == 0)
- dict_ldap->ld = ldap_open(dict_ldap->server_host,
- (int) dict_ldap->server_port);
- alarm(0);
+ /*
+ * get configured value of "ldapsource_cache_size"; default to 32k
+ */
+ vstring_sprintf(config_param, "%s_cache_size", ldapsource);
+ dict_ldap->cache_size = get_mail_conf_int(vstring_str(config_param),
+ 32768, 0, 0);
+ if (msg_verbose)
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->cache_size);
- if (signal(SIGALRM, saved_alarm) == SIG_ERR) {
- msg_warn("%s: error resetting signal handler after open: %m", myname);
- dict_errno = DICT_ERR_RETRY;
- return (0);
+ /*
+ * Alias dereferencing suggested by Mike Mattice.
+ */
+ vstring_sprintf(config_param, "%s_dereference", ldapsource);
+ dict_ldap->dereference = get_mail_conf_int(vstring_str(config_param), 0, 0,
+ 0);
+
+ /*
+ * Make sure only valid options for alias dereferencing are used.
+ */
+ if (dict_ldap->dereference < 0 || dict_ldap->dereference > 3) {
+ msg_warn("%s: Unrecognized value %d specified for %s; using 0",
+ myname, dict_ldap->dereference, vstring_str(config_param));
+ dict_ldap->dereference = 0;
}
- if (dict_ldap->ld == NULL) {
- msg_warn("%s: Unable to contact LDAP server %s",
- myname, dict_ldap->server_host);
- dict_errno = DICT_ERR_RETRY;
- return (0);
- } else {
+ if (msg_verbose)
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->dereference);
- if (msg_verbose)
- msg_info("%s: after ldap_open", myname);
+ dict_ldap_connect(dict_ldap);
- /*
- * If this server requires a bind, do so.
- */
- if (dict_ldap->bind) {
- if (msg_verbose)
- msg_info("%s: about to bind to server %s as dn %s", myname,
- dict_ldap->server_host, dict_ldap->bind_dn);
-
- rc = ldap_bind_s(dict_ldap->ld, dict_ldap->bind_dn,
- dict_ldap->bind_pw, LDAP_AUTH_SIMPLE);
- if (rc != LDAP_SUCCESS) {
- msg_warn("%s: Unable to bind to server %s as %s (%d -- %s): ", myname, dict_ldap->server_host, dict_ldap->bind_dn, rc, ldap_err2string(rc));
- dict_errno = DICT_ERR_RETRY;
- return (0);
- } else {
- if (msg_verbose)
- msg_info("%s: Successful bind to server %s as %s (%d -- %s): ", myname, dict_ldap->server_host, dict_ldap->bind_dn, rc, ldap_err2string(rc));
- }
- }
- if (msg_verbose)
- msg_info("%s: cached connection handle for LDAP source %s",
- myname, dict_ldap->ldapsource);
+ /*
+ * if dict_ldap_connect() set dict_errno, free dict_ldap and abort.
+ */
+ if (dict_errno) {
+ if (dict_ldap->ld)
+ ldap_unbind(dict_ldap->ld);
+
+ myfree((char *) dict_ldap);
+ return (0);
}
+ /*
+ * Otherwise, we're all set. Return the new dict_ldap structure.
+ */
return (&dict_ldap->dict);
}
* directory. Adding support for a new system type means updating the
* makedefs script, and adding a section below for the new system.
*/
+#if (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 104250000)
+#define ALIAS_DB_MAP "hash:/etc/mail/aliases" /* sendmail 8.10 */
+#endif
+
#if defined(FREEBSD2) || defined(FREEBSD3) || defined(FREEBSD4) \
|| defined(FREEBSD5) \
|| defined(BSDI2) || defined(BSDI3) || defined(BSDI4) \
#define HAS_DB
#define HAS_SA_LEN
#define DEF_DB_TYPE "hash"
+#ifndef ALIAS_DB_MAP
#define ALIAS_DB_MAP "hash:/etc/aliases"
+#endif
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
#define USE_STATFS
/* Vertical tab character.
/* .IP \e\e
/* Backslash character.
-/* .IP \enum
+/* .IP \e\fInum\fR
/* 8-bit character whose ASCII value is the 1..3 digit
/* octal number \fInum\fR.
+/* .IP \e\fIother\fR
+/* The backslash character is discarded.
/* LICENSE
/* .ad
/* .fi
/* int vstream_peek(stream)
/* VSTREAM *stream;
/*
-/* int vstream_setjmp(stream, buffer)
+/* int vstream_setjmp(stream)
/* VSTREAM *stream;
-/* jmp_buf *buffer;
/*
/* void longjmp(stream, val)
/* VSTREAM *stream;
-/*++
+/*
/* NAME
/* vstring 3
/* SUMMARY
/* VSTRING *vstring_alloc(len)
/* int len;
/*
-/* VSTRING *vstring_init(vp, buf, len)
-/* VSTRING *vp;
-/* char *buf;
-/* int len;
-/*
/* vstring_ctl(vp, type, value, ..., VSTRING_CTL_END)
/* VSTRING *vp;
/* int type;
/* of at least "len" bytes. The minimal length is 1. The result
/* is a null-terminated string of length zero.
/*
-/* vstring_init() initializes a fixed-size buffer. Attempts to
-/* allocate space beyond the buffer end cause the process to
-/* terminate with a fatal run-time error.
-/*
/* vstring_ctl() gives control over memory management policy.
/* The function takes a VSTRING pointer and a list of zero
/* or more (name,value) pairs. The expected valye type of the
#include <sys_defs.h>
#include <stddef.h>
-#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
#include <stdarg.h>
#include <string.h>
return (vp);
}
-/* vstring_fixed_get_ready - vbuf callback for read buffer empty condition */
-
-static int vstring_fixed_get_ready(VBUF *unused_buf)
-{
- msg_panic("vstring_fixed_get: write-only buffer");
-}
-
-/* vstring_fixed_put_ready - vbuf callback for write buffer full condition */
-
-static int vstring_fixed_put_ready(VBUF *unused_bp)
-{
- msg_fatal("fixed-size string buffer full");
-}
-
-/* vstring_fixed_space - vbuf callback to reserve space */
-
-static int vstring_fixed_space(VBUF *bp, int len)
-{
- if (len < 0)
- msg_panic("vstring_fixed_space: bad length %d", len);
- if (len - bp->cnt > 0)
- msg_fatal("fixed-size string buffer full");
- return (0);
-}
-
-/* vstring_init - initialize fixed-length buffer */
-
-VSTRING *vstring_init(VSTRING *vp, char *buf, int len)
-{
- if (len < 1)
- msg_panic("vstring_init: bad length %d", len);
- vp->vbuf.flags = 0;
- vp->vbuf.data = (unsigned char *) buf;
- vp->vbuf.len = len;
- VSTRING_RESET(vp);
- vp->vbuf.data[0] = 0;
- vp->vbuf.get_ready = vstring_fixed_get_ready;
- vp->vbuf.put_ready = vstring_fixed_put_ready;
- vp->vbuf.space = vstring_fixed_space;
- vp->maxlen = 0;
- return (vp);
-}
-
/* vstring_free - destroy variable-length string */
VSTRING *vstring_free(VSTRING *vp)
} VSTRING;
extern VSTRING *vstring_alloc(int);
-extern VSTRING *vstring_init(VSTRING *, char *, int);
extern void vstring_ctl(VSTRING *,...);
extern VSTRING *vstring_truncate(VSTRING *, int);
extern VSTRING *vstring_free(VSTRING *);