From: Wietse Venema Date: Mon, 13 May 2002 05:00:00 +0000 (-0500) Subject: postfix-1.1.9-20020513 X-Git-Tag: v2.0.0~66 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5a28d361739a7decafbdfc48f765c9fec3e46128;p=thirdparty%2Fpostfix.git postfix-1.1.9-20020513 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index f34023646..6d68a08e6 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -6417,6 +6417,24 @@ Apologies for any names omitted. recipient address, resulting in too much quoting in bounce reports. Files: src/smtp_proto.c, lmtp/lmtp_proto.c. +20020513 + + + Bugfix: the LDAP client used the "wrong" @ character in + addresses with multiple @. LaMont Jones, HP. File: + util/dict_ldap.c. + + Feature: lots of new LDAP stuff: result_filter (filter to + expand results from queries), chase_referrals, + LaMont Jones, HP. The LDAP bind timeout now works thanks + to Victor Duchovni, Morgan Stanley. File: util/dict_ldap.c. + + Cleanup: specify "resolve_dequoted_address = no" to prevent + Postfix from looking inside quotes for extra @ etc. characters + when resolving an address. This behavior is technically + more correct, but it opens a mail relay loophole with "user + @domain"@domain when relaying mail to a Sendmail system. + Open problems: Low: all table lookups should consistently use internalized diff --git a/postfix/README_FILES/LDAP_README b/postfix/README_FILES/LDAP_README index a69ebde3b..0b4dd869b 100644 --- a/postfix/README_FILES/LDAP_README +++ b/postfix/README_FILES/LDAP_README @@ -94,6 +94,11 @@ parameter below, "server_host", would be defined in main.cf as substitute for the address Postfix is trying to resolve, e.g. ldapsource_query_filter = (&(mail=%s)(paid_up=true)) + result_filter (%s) + Filter applied to result attributes. Supports the same expansions + as the query_filter, and can be easily used to append (or prepend) + text. + domain (Default is to ignore this.) This is a list of domain names, paths to files, or dictionaries. If specified, only lookups for the domains on this list will be @@ -166,6 +171,13 @@ parameter below, "server_host", would be defined in main.cf as other possible values, please bring it to the attention of the postfix-users@postfix.org mailing list. + chase_referrals (0) + Sets (or clears) LDAP_OPT_REFERRALS (requires LDAP version 3 + support. + + version (2) + Specifies the LDAP protocol version to use. + debuglevel (0) What level to set for debugging in the OpenLDAP libraries. @@ -327,5 +339,6 @@ Keith Stevenson: RFC 2254 escaping in queries. Samuel Tardieu: Noticed that searches could include wildcards, prompting the work on RFC 2254 escaping in queries. Spotted a bug in binding. +Sami Haahtinen: Referral chasing and v3 support. And of course Wietse. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 7fbd39fa1..b962ce2ae 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -12,6 +12,20 @@ snapshot release). Patches change the patchlevel and the release date. Snapshots change only the release date, unless they include the same bugfixes as a patch release. +Major changes with Postfix snapshot 1.1.9-20020513 +================================================== + +Updated LDAP client module with better handling of dead LDAP servers, +and with configurable filtering of query results. + +In order to allow user@domain@domain addresses from untrusted +systems, specify "resolve_dequoted_address = no" in main.cf (when +resolving mail, quote the address localpart as per RFC 822, so that +@ or % or ! operators in the address localpart remain invisible). +Although this behavior is technically more correct, it also opens +opportunities for mail relay attacks when Postfix provides backup +MX service for Sendmail systems. + Incompatible changes with Postfix snapshot 1.1.9-20020512 ========================================================= diff --git a/postfix/conf/sample-ldap.cf b/postfix/conf/sample-ldap.cf index 58a752a33..e8bb8bccc 100644 --- a/postfix/conf/sample-ldap.cf +++ b/postfix/conf/sample-ldap.cf @@ -33,6 +33,11 @@ # #ldap_query_filter = (mailacceptinggeneralid=%s) +# The ldap_result_filter parameter specifies the filter to be applied +# to the result attribute(s). See ldap_query_filter for valid expansions. +# +#ldap_result_filter = %s + # The ldap_result_attribute parameter specifies the attribute returned by # the search. # diff --git a/postfix/conf/sample-misc.cf b/postfix/conf/sample-misc.cf index d9f7b005d..4f790f0ca 100644 --- a/postfix/conf/sample-misc.cf +++ b/postfix/conf/sample-misc.cf @@ -389,6 +389,22 @@ relayhost = #relocated_maps = hash:/etc/postfix/relocated relocated_maps = +# The resolve_dequoted_address parameter controls how Postfix +# resolves addresses. +# +# By default, the Postfix address resolver does not quote the address +# localpart as per RFC 822, so that additional @ or % or ! operators +# remain visible. This behavior is safe but it is also technically +# incorrect. +# +# If you specify "resolve_dequoted_address = no", then the Postfix +# resolver will not know about additional @ etc. operators in the +# address localpart. This opens opportunities for obscure mail relay +# attacks with user@domain@domain addresses when Postfix provides +# backup MX service for Sendmail systems. +# +resolve_dequoted_address = no + # The syslog_facility parameter controls where Postfix logging is # sent by the syslog daemon. Specify a logging facility as defined # in syslog.conf(5). The default logging facility is "mail". diff --git a/postfix/html/trivial-rewrite.8.html b/postfix/html/trivial-rewrite.8.html index b5293eebf..3f81b47a5 100644 --- a/postfix/html/trivial-rewrite.8.html +++ b/postfix/html/trivial-rewrite.8.html @@ -79,6 +79,14 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) The domain that locally-posted mail appears to come from. + resolve_unquoted_address + When resolving an address, do not quote the address + localpart as per RFC 822, so that additional @, % + or ! characters remain visible. This is techni- + cally incorrect, but allows us to stop relay + attacks when forwarding mail to a Sendmail primary + MX host. + Rewriting allow_percent_hack Rewrite user%domain to user@domain. @@ -94,11 +102,11 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) Routing local_transport - Where to deliver mail for destinations that match - $mydestination or $inet_interfaces. The default + Where to deliver mail for destinations that match + $mydestination or $inet_interfaces. The default transport is local. - Syntax is transport:nexthop; see transport(5) for + Syntax is transport:nexthop; see transport(5) for details. The :nexthop part is optional. default_transport @@ -106,23 +114,23 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) is explicitly given in the transport(5) table. The default transport is smtp. - Syntax is transport:nexthop; see transport(5) for + Syntax is transport:nexthop; see transport(5) for details. The :nexthop part is optional. parent_domain_matches_subdomains - List of Postfix features that use domain.name pat- + List of Postfix features that use domain.name pat- terns to match sub.domain.name (as opposed to requiring .domain.name patterns). relayhost - The default host to send non-local mail to when no + The default host to send non-local mail to when no entry is matched in the transport(5) table. - When no relayhost is specified, mail is routed + When no relayhost is specified, mail is routed directly to the destination's mail exchanger. transport_maps - List of tables with domain to (transport, nexthop) + List of tables with domain to (transport, nexthop) mappings. SEE ALSO @@ -131,7 +139,7 @@ TRIVIAL-REWRITE(8) TRIVIAL-REWRITE(8) transport(5) transport table format LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/man/man8/trivial-rewrite.8 b/postfix/man/man8/trivial-rewrite.8 index 3aa705f57..2fcdf307d 100644 --- a/postfix/man/man8/trivial-rewrite.8 +++ b/postfix/man/man8/trivial-rewrite.8 @@ -80,6 +80,12 @@ This information is used to determine if List of domains that this machine considers local. .IP \fBmyorigin\fR The domain that locally-posted mail appears to come from. +.IP \fBresolve_unquoted_address\fR +When resolving an address, do not quote the address localpart as +per RFC 822, so that additional \fB@\fR, \fB%\fR or \fB!\fR +characters remain visible. This is technically incorrect, but +allows us to stop relay attacks when forwarding mail to a Sendmail +primary MX host. .SH Rewriting .ad .fi diff --git a/postfix/src/bounce/bounce_append_service.c b/postfix/src/bounce/bounce_append_service.c index 65697bb99..82620f02b 100644 --- a/postfix/src/bounce/bounce_append_service.c +++ b/postfix/src/bounce/bounce_append_service.c @@ -103,8 +103,7 @@ int bounce_append_service(char *service, char *queue_id, if (*recipient) vstream_fprintf(log, "<%s>: ", - printable(vstring_str(quote_822_local(in_buf, recipient, - QUOTE_FLAG_8BITCLEAN)), '?')); + printable(vstring_str(quote_822_local(in_buf, recipient)), '?')); else vstream_fprintf(log, "<>: "); vstream_fputs(printable(why, '?'), log); diff --git a/postfix/src/bounce/bounce_notify_util.c b/postfix/src/bounce/bounce_notify_util.c index b6a919efd..d4264d945 100644 --- a/postfix/src/bounce/bounce_notify_util.c +++ b/postfix/src/bounce/bounce_notify_util.c @@ -302,8 +302,7 @@ int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, "Subject: Delayed Mail (still being retried)"); } post_mail_fprintf(bounce, "To: %s", - STR(quote_822_local(bounce_info->buf, dest, - QUOTE_FLAG_8BITCLEAN))); + STR(quote_822_local(bounce_info->buf, dest))); /* * MIME header. diff --git a/postfix/src/cleanup/cleanup_map11.c b/postfix/src/cleanup/cleanup_map11.c index 350799861..6d699d348 100644 --- a/postfix/src/cleanup/cleanup_map11.c +++ b/postfix/src/cleanup/cleanup_map11.c @@ -162,7 +162,7 @@ void cleanup_map11_internal(CLEANUP_STATE *state, VSTRING *addr, * checking in one place, instead of having error handling code all over * the place. */ - quote_822_local(temp, STR(addr), QUOTE_FLAG_8BITCLEAN); + quote_822_local(temp, STR(addr)); cleanup_map11_external(state, temp, maps, propagate); unquote_822_local(addr, STR(temp)); vstring_free(temp); diff --git a/postfix/src/cleanup/cleanup_map1n.c b/postfix/src/cleanup/cleanup_map1n.c index 0a181e463..4e6e6f290 100644 --- a/postfix/src/cleanup/cleanup_map1n.c +++ b/postfix/src/cleanup/cleanup_map1n.c @@ -113,8 +113,7 @@ ARGV *cleanup_map1n_internal(CLEANUP_STATE *state, char *addr, state->queue_id, maps->title, addr); break; } - quote_822_local(state->temp1, argv->argv[arg], - QUOTE_FLAG_8BITCLEAN); + quote_822_local(state->temp1, argv->argv[arg]); if ((lookup = mail_addr_map(maps, STR(state->temp1), propagate)) != 0) { saved_lhs = mystrdup(argv->argv[arg]); for (i = 0; i < lookup->argc; i++) { diff --git a/postfix/src/cleanup/cleanup_masquerade.c b/postfix/src/cleanup/cleanup_masquerade.c index eeb38ee3c..b742e25ca 100644 --- a/postfix/src/cleanup/cleanup_masquerade.c +++ b/postfix/src/cleanup/cleanup_masquerade.c @@ -165,7 +165,7 @@ void cleanup_masquerade_internal(VSTRING *addr, ARGV *masq_domains) { VSTRING *temp = vstring_alloc(100); - quote_822_local(temp, STR(addr), QUOTE_FLAG_8BITCLEAN); + quote_822_local(temp, STR(addr)); cleanup_masquerade_external(temp, masq_domains); unquote_822_local(addr, STR(temp)); diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c index 335715220..86785783e 100644 --- a/postfix/src/cleanup/cleanup_message.c +++ b/postfix/src/cleanup/cleanup_message.c @@ -197,7 +197,6 @@ static void cleanup_rewrite_sender(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts) cleanup_extract_internal(state->header_buf, *tpp); } vstring_sprintf(state->header_buf, "%s: ", hdr_opts->name); - /* XXX should quote_822_local the address local parts. */ tok822_externalize(state->header_buf, tree, TOK822_STR_HEAD); myfree((char *) addr_list); tok822_free_tree(tree); @@ -251,7 +250,6 @@ static void cleanup_rewrite_recip(CLEANUP_STATE *state, HEADER_OPTS *hdr_opts) cleanup_masquerade_tree(*tpp, cleanup_masq_domains); } vstring_sprintf(state->header_buf, "%s: ", hdr_opts->name); - /* XXX should quote_822_local the address local parts. */ tok822_externalize(state->header_buf, tree, TOK822_STR_HEAD); myfree((char *) addr_list); tok822_free_tree(tree); @@ -465,8 +463,7 @@ static void cleanup_missing_headers(CLEANUP_STATE *state) if ((state->headers_seen & (1 << (state->resent[0] ? HDR_RESENT_FROM : HDR_FROM))) == 0) { quote_822_local(state->temp1, *state->sender ? - state->sender : MAIL_ADDR_MAIL_DAEMON, - QUOTE_FLAG_8BITCLEAN); + state->sender : MAIL_ADDR_MAIL_DAEMON); vstring_sprintf(state->temp2, "%sFrom: %s", state->resent, vstring_str(state->temp1)); if (*state->sender && state->fullname && *state->fullname) { diff --git a/postfix/src/cleanup/cleanup_rewrite.c b/postfix/src/cleanup/cleanup_rewrite.c index ab714c638..1ae6871d8 100644 --- a/postfix/src/cleanup/cleanup_rewrite.c +++ b/postfix/src/cleanup/cleanup_rewrite.c @@ -95,7 +95,7 @@ void cleanup_rewrite_internal(VSTRING *result, const char *addr) VSTRING *dst = vstring_alloc(100); VSTRING *src = vstring_alloc(100); - quote_822_local(src, addr, QUOTE_FLAG_8BITCLEAN); + quote_822_local(src, addr); cleanup_rewrite_external(dst, STR(src)); unquote_822_local(result, STR(dst)); vstring_free(dst); diff --git a/postfix/src/global/mail_copy.c b/postfix/src/global/mail_copy.c index 6b8122b70..c2d328981 100644 --- a/postfix/src/global/mail_copy.c +++ b/postfix/src/global/mail_copy.c @@ -144,7 +144,7 @@ int mail_copy(const char *sender, const char *delivered, if (flags & (MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH)) { if (sender == 0) msg_panic("%s: null sender", myname); - quote_822_local(buf, sender, QUOTE_FLAG_8BITCLEAN); + quote_822_local(buf, sender); if (flags & MAIL_COPY_FROM) { time(&now); vstream_fprintf(dst, "From %s %.24s%s", *sender == 0 ? @@ -159,7 +159,7 @@ int mail_copy(const char *sender, const char *delivered, if (flags & MAIL_COPY_DELIVERED) { if (delivered == 0) msg_panic("%s: null delivered", myname); - quote_822_local(buf, delivered, QUOTE_FLAG_8BITCLEAN); + quote_822_local(buf, delivered); vstream_fprintf(dst, "Delivered-To: %s%s", lowercase(vstring_str(buf)), eol); } diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 7a1d62dd2..0385b336f 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -741,7 +741,7 @@ extern char *var_smtp_helo_name; #define VAR_SMTP_RAND_ADDR "smtp_randomize_addresses" #define DEF_SMTP_RAND_ADDR 1 extern bool var_smtp_rand_addr; - + #define VAR_SMTP_LINE_LIMIT "smtp_line_length_limit" #define DEF_SMTP_LINE_LIMIT 990 extern int var_smtp_line_limit; @@ -1419,6 +1419,17 @@ extern int var_fault_inj_code; #define DEF_README_DIR "no" #endif + /* + * Safety: resolve the address with unquoted localpart (default, but + * technically incorrect), instead of resolving the address with quoted + * localpart (technically correct, but unsafe). The default prevents mail + * relay loopholes with "user@domain"@domain when relaying mail to a + * Sendmail system. + */ +#define VAR_RESOLVE_DEQUOTED "resolve_dequoted_address" +#define DEF_RESOLVE_DEQUOTED 1 +extern bool var_resolve_dequoted; + /* * Service names. The transport (TCP, FIFO or UNIX-domain) type is frozen * because you cannot simply mix them, and accessibility (private/public) is diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 8370958da..0f5af1c0b 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change the patchlevel and the release date. Snapshots change the * release date only, unless they include the same bugfix as a patch release. */ -#define MAIL_RELEASE_DATE "20020512" +#define MAIL_RELEASE_DATE "20020513" #define VAR_MAIL_VERSION "mail_version" #define DEF_MAIL_VERSION "1.1.9-" MAIL_RELEASE_DATE diff --git a/postfix/src/global/quote_821_local.c b/postfix/src/global/quote_821_local.c index 2fa6bd526..b87cf7eb1 100644 --- a/postfix/src/global/quote_821_local.c +++ b/postfix/src/global/quote_821_local.c @@ -6,14 +6,20 @@ /* SYNOPSIS /* #include "quote_821_local.h" /* -/* VSTRING *quote_821_local(dst, src, flags) +/* VSTRING *quote_821_local(dst, src) +/* VSTRING *dst; +/* char *src; +/* +/* VSTRING *quote_821_local_flags(dst, src, flags) /* VSTRING *dst; /* char *src; /* int flags; /* DESCRIPTION /* quote_821_local() quotes the local part of a mailbox address and /* returns a result that can be used in SMTP commands as specified -/* by RFC 821. +/* by RFC 821. It implements an 8-bit clean version of RFC 821. +/* +/* quote_821_local_flags() provides finer control. /* /* Arguments: /* .IP dst @@ -124,9 +130,9 @@ static VSTRING *make_821_quoted_string(VSTRING *dst, char *local_part, return (dst); } -/* quote_821_local - quote local part of address according to rfc 821 */ +/* quote_821_local_flags - quote local part of address according to rfc 821 */ -VSTRING *quote_821_local(VSTRING *dst, char *addr, int flags) +VSTRING *quote_821_local_flags(VSTRING *dst, char *addr, int flags) { char *at; @@ -164,8 +170,7 @@ int main(void) while (vstring_fgets_nonl(src, VSTREAM_IN)) { vstream_fprintf(VSTREAM_OUT, "%s\n", - vstring_str(quote_821_local(dst, vstring_str(src), - QUOTE_FLAG_8BITCLEAN))); + vstring_str(quote_821_local(dst, vstring_str(src)))); vstream_fflush(VSTREAM_OUT); } exit(0); diff --git a/postfix/src/global/quote_821_local.h b/postfix/src/global/quote_821_local.h index c2a8564fb..d1e91e562 100644 --- a/postfix/src/global/quote_821_local.h +++ b/postfix/src/global/quote_821_local.h @@ -21,7 +21,9 @@ /* * External interface. */ -extern VSTRING *quote_821_local(VSTRING *, char *, int); +extern VSTRING *quote_821_local_flags(VSTRING *, char *, int); +#define quote_821_local(dst, src) \ + quote_821_local_flags((dst), (src), QUOTE_FLAG_8BITCLEAN) /* LICENSE /* .ad diff --git a/postfix/src/global/quote_822_local.c b/postfix/src/global/quote_822_local.c index 54392805d..5f4672f74 100644 --- a/postfix/src/global/quote_822_local.c +++ b/postfix/src/global/quote_822_local.c @@ -6,7 +6,11 @@ /* SYNOPSIS /* #include /* -/* VSTRING *quote_822_local(dst, src, flags) +/* VSTRING *quote_822_local(dst, src) +/* VSTRING *dst; +/* const char *src; +/* +/* VSTRING *quote_822_local_flags(dst, src, flags) /* VSTRING *dst; /* const char *src; /* int flags; @@ -18,7 +22,9 @@ /* quote_822_local() quotes the local part of a mailbox and /* returns a result that can be used in message headers as /* specified by RFC 822 (actually, an 8-bit clean version of -/* RFC 822). +/* RFC 822). It implements an 8-bit clean version of RFC 822. +/* +/* quote_822_local_flags() provides finer control. /* /* unquote_822_local() transforms the local part of a mailbox /* address to unquoted (internal) form. @@ -136,9 +142,9 @@ static VSTRING *make_822_quoted_string(VSTRING *dst, const char *local_part, return (dst); } -/* quote_822_local - quote local part of mailbox according to rfc 822 */ +/* quote_822_local_flags - quote local part of mailbox according to rfc 822 */ -VSTRING *quote_822_local(VSTRING *dst, const char *mbox, int flags) +VSTRING *quote_822_local_flags(VSTRING *dst, const char *mbox, int flags) { const char *start; /* first byte of localpart */ const char *end; /* first byte after localpart */ @@ -220,7 +226,7 @@ int main(int unused_argc, char **unused_argv) VSTRING *unquoted = vstring_alloc(100); while (vstring_fgets_nonl(raw, VSTREAM_IN)) { - quote_822_local(quoted, STR(raw), QUOTE_FLAG_8BITCLEAN); + quote_822_local(quoted, STR(raw)); vstream_printf("quoted: %s\n", STR(quoted)); unquote_822_local(unquoted, STR(quoted)); vstream_printf("unquoted: %s\n", STR(unquoted)); diff --git a/postfix/src/global/quote_822_local.h b/postfix/src/global/quote_822_local.h index a24f34b10..cb5454360 100644 --- a/postfix/src/global/quote_822_local.h +++ b/postfix/src/global/quote_822_local.h @@ -24,8 +24,10 @@ /* * External interface. */ -extern VSTRING *quote_822_local(VSTRING *, const char *, int); +extern VSTRING *quote_822_local_flags(VSTRING *, const char *, int); extern VSTRING *unquote_822_local(VSTRING *, const char *); +#define quote_822_local(dst, src) \ + quote_822_local_flags((dst), (src), QUOTE_FLAG_8BITCLEAN) /* LICENSE /* .ad diff --git a/postfix/src/global/rewrite_clnt.c b/postfix/src/global/rewrite_clnt.c index 5cff7e0ba..c02375b44 100644 --- a/postfix/src/global/rewrite_clnt.c +++ b/postfix/src/global/rewrite_clnt.c @@ -166,7 +166,7 @@ VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *r * Convert the address from internal address form to external RFC822 * form, then rewrite it. After rewriting, convert to internal form. */ - quote_822_local(src, addr, QUOTE_FLAG_8BITCLEAN); + quote_822_local(src, addr); rewrite_clnt(ruleset, STR(src), dst); unquote_822_local(result, STR(dst)); vstring_free(src); diff --git a/postfix/src/global/tok822_parse.c b/postfix/src/global/tok822_parse.c index 097b4c351..194b973a0 100644 --- a/postfix/src/global/tok822_parse.c +++ b/postfix/src/global/tok822_parse.c @@ -248,8 +248,8 @@ VSTRING *tok822_externalize(VSTRING *vp, TOK822 *tree, int flags) case TOK822_ADDR: tmp = vstring_alloc(100); tok822_internalize(tmp, tp->head, TOK822_STR_NONE); - quote_822_local(vp, vstring_str(tmp), - QUOTE_FLAG_8BITCLEAN | QUOTE_FLAG_APPEND); + quote_822_local_flags(vp, vstring_str(tmp), + QUOTE_FLAG_8BITCLEAN | QUOTE_FLAG_APPEND); vstring_free(tmp); break; case TOK822_ATOM: diff --git a/postfix/src/lmtp/lmtp_proto.c b/postfix/src/lmtp/lmtp_proto.c index 6dffbc729..2fb41f1cf 100644 --- a/postfix/src/lmtp/lmtp_proto.c +++ b/postfix/src/lmtp/lmtp_proto.c @@ -297,7 +297,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state) */ #define REWRITE_ADDRESS(dst, src) do { \ if (*(src)) { \ - quote_821_local(dst, src, QUOTE_FLAG_8BITCLEAN); \ + quote_821_local(dst, src); \ lowercase(vstring_str(dst)); \ } else { \ vstring_strcpy(dst, src); \ diff --git a/postfix/src/local/delivered.c b/postfix/src/local/delivered.c index 6ab3f5696..50faeae64 100644 --- a/postfix/src/local/delivered.c +++ b/postfix/src/local/delivered.c @@ -137,7 +137,7 @@ int delivered_find(HTABLE *table, char *address) * header. We must therefore apply the same transformation when looking * up the recipient. Lowercase the delivered-to address for consistency. */ - quote_822_local(buf, address, QUOTE_FLAG_8BITCLEAN); + quote_822_local(buf, address); lowercase(STR(buf)); ht = htable_locate(table, STR(buf)); return (ht != 0); diff --git a/postfix/src/pipe/pipe.c b/postfix/src/pipe/pipe.c index e5d0805b6..59741bda0 100644 --- a/postfix/src/pipe/pipe.c +++ b/postfix/src/pipe/pipe.c @@ -382,7 +382,7 @@ static void morph_recipient(VSTRING *buf, const char *address, int flags) * Quote the recipient address as appropriate. */ if (flags & PIPE_OPT_QUOTE_LOCAL) - quote_822_local(buf, address, QUOTE_FLAG_8BITCLEAN); + quote_822_local(buf, address); else vstring_strcpy(buf, address); @@ -826,7 +826,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv) buf = vstring_alloc(10); if (attr.flags & PIPE_OPT_QUOTE_LOCAL) { - quote_822_local(buf, request->sender, QUOTE_FLAG_8BITCLEAN); + quote_822_local(buf, request->sender); dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf)); } else dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender); diff --git a/postfix/src/qmqpd/qmqpd.c b/postfix/src/qmqpd/qmqpd.c index 47106e990..263ac17da 100644 --- a/postfix/src/qmqpd/qmqpd.c +++ b/postfix/src/qmqpd/qmqpd.c @@ -340,7 +340,7 @@ static void qmqpd_write_content(QMQPD_STATE *state) "\tby %s (%s) with %s id %s", var_myhostname, var_mail_name, state->protocol, state->queue_id); - quote_822_local(state->buf, state->recipient, QUOTE_FLAG_8BITCLEAN); + quote_822_local(state->buf, state->recipient); rec_fprintf(state->cleanup, REC_TYPE_NORM, "\tfor <%s>; %s", STR(state->buf), mail_date(state->time)); } else { diff --git a/postfix/src/showq/showq.c b/postfix/src/showq/showq.c index 475d31eac..8a59cbec6 100644 --- a/postfix/src/showq/showq.c +++ b/postfix/src/showq/showq.c @@ -137,7 +137,7 @@ static void showq_report(VSTREAM *client, char *queue, char *id, case REC_TYPE_FROM: if (*start == 0) start = var_empty_addr; - quote_822_local(printable_quoted_addr, start, QUOTE_FLAG_8BITCLEAN); + quote_822_local(printable_quoted_addr, start); printable(STR(printable_quoted_addr), '?'); vstream_fprintf(client, DATA_FORMAT, id, status, msg_size > 0 ? msg_size : size, arrival_time > 0 ? @@ -147,7 +147,7 @@ static void showq_report(VSTREAM *client, char *queue, char *id, case REC_TYPE_RCPT: if (*start == 0) /* can't happen? */ start = var_empty_addr; - quote_822_local(printable_quoted_addr, start, QUOTE_FLAG_8BITCLEAN); + quote_822_local(printable_quoted_addr, start); printable(STR(printable_quoted_addr), '?'); if (dup_filter == 0 || htable_locate(dup_filter, STR(printable_quoted_addr)) == 0) diff --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index fe9d2a177..e52e3c37d 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -306,7 +306,7 @@ int smtp_xfer(SMTP_STATE *state) */ #define REWRITE_ADDRESS(dst, mid, src) do { \ if (*(src)) { \ - quote_821_local(mid, src, QUOTE_FLAG_8BITCLEAN); \ + quote_821_local(mid, src); \ smtp_unalias_addr(dst, vstring_str(mid)); \ } else { \ vstring_strcpy(dst, src); \ @@ -315,7 +315,7 @@ int smtp_xfer(SMTP_STATE *state) #define QUOTE_ADDRESS(dst, src) do { \ if (*(src)) { \ - quote_821_local(dst, src, QUOTE_FLAG_8BITCLEAN); \ + quote_821_local(dst, src); \ } else { \ vstring_strcpy(dst, src); \ } \ diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index ddc016bc9..bf7d7748a 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -954,7 +954,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) "\tby %s (%s) with %s id %s", var_myhostname, var_mail_name, state->protocol, state->queue_id); - quote_822_local(state->buffer, state->recipient, QUOTE_FLAG_8BITCLEAN); + quote_822_local(state->buffer, state->recipient); rec_fprintf(state->cleanup, REC_TYPE_NORM, "\tfor <%s>; %s", STR(state->buffer), mail_date(state->time)); } else { @@ -965,7 +965,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) "\tid %s; %s", state->queue_id, mail_date(state->time)); } #ifdef RECEIVED_ENVELOPE_FROM - quote_822_local(state->buffer, state->sender, QUOTE_FLAG_8BITCLEAN); + quote_822_local(state->buffer, state->sender); rec_fprintf(state->cleanup, REC_TYPE_NORM, "\t(envelope-from %s)", STR(state->buffer)); #endif diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 19bcf4ade..d6f7bc331 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -739,8 +739,8 @@ static const char *check_mail_addr_find(SMTPD_STATE *state, /* resolve_final - do we do final delivery for the domain? */ -static int resolve_final(SMTPD_STATE *state, const char *reply_name, -const char *domain) +static int resolve_final(SMTPD_STATE *state, const char *reply_name, + const char *domain) { /* If matches $mydestination or $inet_interfaces. */ @@ -1027,7 +1027,7 @@ static int permit_auth_destination(SMTPD_STATE *state, char *recipient) return (SMTPD_CHECK_DUNNO); /* - * Permit final delivery: the destination matches mydestination, + * Permit final delivery: the destination matches mydestination, * virtual_maps, or virtual_mailbox_maps. */ if (resolve_final(state, recipient, domain)) @@ -1729,8 +1729,8 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, * from resolve_clnt_query() must be fully qualified. */ if ((domain = strrchr(CONST_STR(reply->recipient), '@')) == 0) { - msg_warn("%s: no @domain in address: %s", myname, - CONST_STR(reply->recipient)); + msg_warn("%s: no @domain in address: %s", myname, + CONST_STR(reply->recipient)); return (0); } domain += 1; @@ -1765,7 +1765,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, * below was brought to you by the keyboard of Victor Duchovni, Morgan * Stanley and hacked up a bit by Wietse. */ -#define SUSPICIOUS(domain, reply, state, reply_name, reply_class) \ +#define SUSPICIOUS(reply, reply_class) \ (var_allow_untrust_route == 0 \ && (reply->flags & RESOLVE_FLAG_ROUTED) \ && strcmp(reply_class, SMTPD_NAME_RECIPIENT) == 0) @@ -1778,7 +1778,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, found, reply_name, reply_class, def_acl)) != 0 || *found) CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK - && SUSPICIOUS(domain, reply, state, reply_name, reply_class) ? + && SUSPICIOUS(reply, reply_class) ? SMTPD_CHECK_DUNNO : status); /* @@ -1789,7 +1789,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, found, reply_name, reply_class, def_acl)) != 0 || *found) CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK - && SUSPICIOUS(domain, reply, state, reply_name, reply_class) ? + && SUSPICIOUS(reply, reply_class) ? SMTPD_CHECK_DUNNO : status); /* @@ -1799,7 +1799,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, found, reply_name, reply_class, def_acl)) != 0 || *found) CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK - && SUSPICIOUS(domain, reply, state, reply_name, reply_class) ? + && SUSPICIOUS(reply, reply_class) ? SMTPD_CHECK_DUNNO : status); /* @@ -1813,7 +1813,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, myfree(local_at); if (status != 0 || *found) CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK - && SUSPICIOUS(domain, reply, state, reply_name, reply_class) ? + && SUSPICIOUS(reply, reply_class) ? SMTPD_CHECK_DUNNO : status); /* @@ -1827,7 +1827,7 @@ static int check_mail_access(SMTPD_STATE *state, const char *table, myfree(local_at); if (status != 0 || *found) CHECK_MAIL_ACCESS_RETURN(status == SMTPD_CHECK_OK - && SUSPICIOUS(domain, reply, state, reply_name, reply_class) ? + && SUSPICIOUS(reply, reply_class) ? SMTPD_CHECK_DUNNO : status); } diff --git a/postfix/src/trivial-rewrite/resolve.c b/postfix/src/trivial-rewrite/resolve.c index ad23735ff..7a7db6fa3 100644 --- a/postfix/src/trivial-rewrite/resolve.c +++ b/postfix/src/trivial-rewrite/resolve.c @@ -101,10 +101,17 @@ void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop, * While quoting the address local part, do not treat @ as a special * character. This allows us to detect extra @ characters and block * source routed relay attempts. + * + * But practically, we have to look at the unquoted form so that routing + * characters like @ remain visible, in order to stop user@domain@domain + * relay attempts when forwarding mail to a primary Sendmail MX host. */ - quote_822_local(addr_buf, addr, - QUOTE_FLAG_8BITCLEAN | QUOTE_FLAG_EXPOSE_AT); - tree = tok822_scan_addr(vstring_str(addr_buf)); + if (var_resolve_dequoted) { + tree = tok822_scan_addr(addr); + } else { + quote_822_local(addr_buf, addr); + tree = tok822_scan_addr(vstring_str(addr_buf)); + } /* * Preliminary resolver: strip off all instances of the local domain. diff --git a/postfix/src/trivial-rewrite/trivial-rewrite.c b/postfix/src/trivial-rewrite/trivial-rewrite.c index 1411d7b0e..7cc9a44d0 100644 --- a/postfix/src/trivial-rewrite/trivial-rewrite.c +++ b/postfix/src/trivial-rewrite/trivial-rewrite.c @@ -64,6 +64,12 @@ /* List of domains that this machine considers local. /* .IP \fBmyorigin\fR /* The domain that locally-posted mail appears to come from. +/* .IP \fBresolve_unquoted_address\fR +/* When resolving an address, do not quote the address localpart as +/* per RFC 822, so that additional \fB@\fR, \fB%\fR or \fB!\fR +/* characters remain visible. This is technically incorrect, but +/* allows us to stop relay attacks when forwarding mail to a Sendmail +/* primary MX host. /* .SH Rewriting /* .ad /* .fi @@ -167,6 +173,7 @@ bool var_append_dot_mydomain; bool var_append_at_myorigin; bool var_percent_hack; char *var_local_transport; +int var_resolve_dequoted; /* rewrite_service - read request and send reply */ @@ -239,6 +246,7 @@ int main(int argc, char **argv) VAR_APP_DOT_MYDOMAIN, DEF_APP_DOT_MYDOMAIN, &var_append_dot_mydomain, VAR_APP_AT_MYORIGIN, DEF_APP_AT_MYORIGIN, &var_append_at_myorigin, VAR_PERCENT_HACK, DEF_PERCENT_HACK, &var_percent_hack, + VAR_RESOLVE_DEQUOTED, DEF_RESOLVE_DEQUOTED, &var_resolve_dequoted, 0, }; diff --git a/postfix/src/util/attr_scan0.c b/postfix/src/util/attr_scan0.c index 0ccfc040f..528c6147a 100644 --- a/postfix/src/util/attr_scan0.c +++ b/postfix/src/util/attr_scan0.c @@ -308,7 +308,7 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) && strcmp(wanted_name, STR(name_buf)) == 0)) break; if ((flags & ATTR_FLAG_EXTRA) != 0) { - msg_warn("spurious attribute %s in input from %s", + msg_warn("unexpected attribute %s in input from %s", STR(name_buf), VSTREAM_PATH(fp)); return (conversions); } diff --git a/postfix/src/util/attr_scan64.c b/postfix/src/util/attr_scan64.c index 0c38c5ea5..26e7d04d2 100644 --- a/postfix/src/util/attr_scan64.c +++ b/postfix/src/util/attr_scan64.c @@ -327,7 +327,7 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) && strcmp(wanted_name, STR(name_buf)) == 0)) break; if ((flags & ATTR_FLAG_EXTRA) != 0) { - msg_warn("spurious attribute %s in input from %s", + msg_warn("unexpected attribute %s in input from %s", STR(name_buf), VSTREAM_PATH(fp)); return (conversions); } diff --git a/postfix/src/util/dict_ldap.c b/postfix/src/util/dict_ldap.c index 2cf8936c4..60a07e92a 100644 --- a/postfix/src/util/dict_ldap.c +++ b/postfix/src/util/dict_ldap.c @@ -39,6 +39,9 @@ /* .IP \fIldapsource_\fRquery_filter /* The filter used to search for directory entries, for example /* \fI(mailacceptinggeneralid=%s)\fR. +/* .IP \fIldapsource_\fRresult_filter +/* The filter used to expand results from queries. Default is +/* \fI%s\fR. /* .IP \fIldapsource_\fRresult_attribute /* The attribute(s) returned by the search, in which to find /* RFC822 addresses, for example \fImaildrop\fR. @@ -135,6 +138,7 @@ typedef struct { char *search_base; MATCH_LIST *domain; char *query_filter; + char *result_filter; ARGV *result_attributes; int num_attributes; /* rest of list is DN's. */ int bind; @@ -145,15 +149,25 @@ typedef struct { long cache_expiry; long cache_size; int dereference; + int chase_referrals; int debuglevel; + int version; LDAP *ld; } DICT_LDAP; +#ifndef LDAP_OPT_NETWORK_TIMEOUT /* * LDAP connection timeout support. */ static jmp_buf env; +static void dict_ldap_timeout(int unused_sig) +{ + longjmp(env, 1); +} + +#endif + static void dict_ldap_logprint(LDAP_CONST char *data) { char *myname = "dict_ldap_debug"; @@ -161,16 +175,61 @@ static void dict_ldap_logprint(LDAP_CONST char *data) msg_info("%s: %s", myname, data); } -static void dict_ldap_timeout(int unused_sig) + +static int dict_ldap_get_errno(LDAP * ld) { - longjmp(env, 1); + int rc; + +#if (LDAP_API_VERSION >= 2000) + if (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_OPT_SUCCESS) + rc = LDAP_OTHER; +#else + rc = dict_ldap->ld->ld_errno; +#endif + return rc; +} + +static int dict_ldap_set_errno(LDAP * ld, int rc) +{ +#if (LDAP_API_VERSION >= 2000) + (void) ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, &rc); +#else + ld->ld_errno = rc; +#endif + return rc; +} + +/* + * We need a version of ldap_bind that times out, otherwise all + * of Postfix can get wedged during daemon initialization. + */ +static int dict_ldap_bind_st(DICT_LDAP *dict_ldap) +{ + int msgid; + LDAPMessage *res; + struct timeval mytimeval; + + if ((msgid = ldap_bind(dict_ldap->ld, dict_ldap->bind_dn, + dict_ldap->bind_pw, LDAP_AUTH_SIMPLE)) == -1) + return (dict_ldap_get_errno(dict_ldap->ld)); + + mytimeval.tv_sec = dict_ldap->timeout; + mytimeval.tv_usec = 0; + + if (ldap_result(dict_ldap->ld, msgid, 1, &mytimeval, &res) == -1) + return (dict_ldap_get_errno(dict_ldap->ld)); + + if (dict_ldap_get_errno(dict_ldap->ld) == LDAP_TIMEOUT) { + (void) ldap_abandon(dict_ldap->ld, msgid); + return (dict_ldap_set_errno(dict_ldap->ld, LDAP_TIMEOUT)); + } + return (ldap_result2error(dict_ldap->ld, res, 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; #ifdef LDAP_API_FEATURE_X_MEMCACHE @@ -181,6 +240,9 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) #ifdef LDAP_OPT_NETWORK_TIMEOUT struct timeval mytimeval; +#else + void (*saved_alarm) (int); + #endif dict_errno = 0; @@ -232,6 +294,21 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) } #endif + /* + * v3 support is needed for referral chasing. Thanks to Sami Haahtinen + * for the patch. + */ +#ifdef LDAP_OPT_PROTOCOL_VERSION + if (ldap_set_option(dict_ldap->ld, LDAP_OPT_PROTOCOL_VERSION, + &dict_ldap->version) != LDAP_OPT_SUCCESS) + msg_warn("%s: Unable to set LDAP protocol version", myname); + + if (msg_verbose) + msg_warn("%s: Actual Protocol version used was %d.", + myname, ldap_get_option(dict_ldap->ld, + LDAP_OPT_PROTOCOL_VERSION, (int *) dict_ldap->version)); +#endif + /* * Configure alias dereferencing for this connection. Thanks to Mike * Mattice for this, and to Hery Rakotoarisoa for the v3 update. @@ -254,6 +331,22 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) msg_warn("%s: Unable to set LDAP debug level.", myname); #endif + /* Chase referrals. */ + + /* + * I have no clue where this was originally added so i'm skipping all + * tests + */ +#ifdef LDAP_OPT_REFERRALS + if (ldap_set_option(dict_ldap->ld, LDAP_OPT_REFERRALS, + dict_ldap->chase_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF) + != LDAP_OPT_SUCCESS) + msg_warn("%s: Unable to set Referral chasing.", myname); +#else + if (dict_ldap->chase_referrals) { + msg_warn("%s: Unable to set Referral chasing.", myname); + } +#endif /* * If this server requires a bind, do so. Thanks to Sam Tardieu for @@ -264,8 +357,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) 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); + rc = dict_ldap_bind_st(dict_ldap); if (rc != LDAP_SUCCESS) { msg_warn("%s: Unable to bind to server %s as %s: %d (%s)", @@ -331,6 +423,59 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) return (0); } +/* + * expand a filter (lookup or result) + */ +static void dict_ldap_expand_filter(char *filter, char *value, VSTRING *out) +{ + char *myname = "dict_ldap_expand_filter"; + char *sub, + *end; + + /* + * Yes, replace all instances of %s with the address to look up. Replace + * %u with the user portion, and %d with the domain portion. + */ + sub = filter; + end = sub + strlen(filter); + while (sub < end) { + + /* + * Make sure it's %[sud] and not something else. For backward + * compatibilty, treat anything other than %u or %d as %s, with a + * warning. + */ + if (*(sub) == '%') { + char *u = value; + char *p = strrchr(u, '@'); + + switch (*(sub + 1)) { + case 'd': + if (p) + vstring_strcat(out, p + 1); + break; + case 'u': + if (p) + vstring_strncat(out, u, p - u); + else + vstring_strcat(out, u); + break; + default: + msg_warn + ("%s: Invalid filter substitution format '%%%c'!", + myname, *(sub + 1)); + /* fall through */ + case 's': + vstring_strcat(out, u); + break; + } + sub++; + } else + vstring_strncat(out, sub, 1); + sub++; + } +} + /* * dict_ldap_get_values: for each entry returned by a search, get the values * of all its attributes. Recurses to resolve any DN or URL values found. @@ -360,6 +505,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res, for (entry = ldap_first_entry(dict_ldap->ld, res); entry != NULL; entry = ldap_next_entry(dict_ldap->ld, entry)) { + ber = NULL; attr = ldap_first_attribute(dict_ldap->ld, entry, &ber); if (attr == NULL) { if (msg_verbose) @@ -393,7 +539,11 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res, for (i = 0; vals[i] != NULL; i++) { if (VSTRING_LEN(result) > 0) vstring_strcat(result, ","); - vstring_strcat(result, vals[i]); + if (dict_ldap->result_filter == NULL) + vstring_strcat(result, vals[i]); + else + dict_ldap_expand_filter(dict_ldap->result_filter, + vals[i], result); } } else if (dict_ldap->result_attributes->argv[i]) { for (i = 0; vals[i] != NULL; i++) { @@ -570,49 +720,8 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) 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. - * Replace %u with the user portion, and %d with the domain portion. - */ - sub = dict_ldap->query_filter; - end = sub + strlen(dict_ldap->query_filter); - while (sub < end) { - - /* - * Make sure it's %[sud] and not something else. For backward - * compatibilty, treat anything other than %u or %d as %s, with a - * warning. - */ - if (*(sub) == '%') { - char *u = vstring_str(escaped_name); - char *p = strchr(u, '@'); - - switch (*(sub + 1)) { - case 'd': - if (p) - vstring_strcat(filter_buf, p + 1); - break; - case 'u': - if (p) - vstring_strncat(filter_buf, u, p - u); - else - vstring_strcat(filter_buf, u); - break; - default: - msg_warn - ("%s: Invalid lookup substitution format '%%%c'!", - myname, *(sub + 1)); - /* fall through */ - case 's': - vstring_strcat(filter_buf, u); - break; - } - sub++; - } else - vstring_strncat(filter_buf, sub, 1); - sub++; - } + dict_ldap_expand_filter(dict_ldap->query_filter, + vstring_str(escaped_name), filter_buf); } /* @@ -663,19 +772,11 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) * LDAP_DECODING_ERROR; I'm ignoring that for now. */ -#if (LDAP_API_VERSION >= 2000) - if (ldap_get_option(dict_ldap->ld, LDAP_OPT_ERROR_NUMBER, &rc) != - LDAP_OPT_SUCCESS) - msg_warn("%s: Unable to get last error number.", myname); + rc = dict_ldap_get_errno(dict_ldap->ld); if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR) - msg_warn("%s: Had some trouble with entries returned by search: %s", myname, ldap_err2string(rc)); -#else - if (dict_ldap->ld->ld_errno != LDAP_SUCCESS && - dict_ldap->ld->ld_errno != LDAP_DECODING_ERROR) msg_warn ("%s: Had some trouble with entries returned by search: %s", - myname, ldap_err2string(dict_ldap->ld->ld_errno)); -#endif + myname, ldap_err2string(rc)); if (msg_verbose) msg_info("%s: Search returned %s", myname, @@ -735,6 +836,8 @@ static void dict_ldap_close(DICT *dict) if (dict_ldap->domain) match_list_free(dict_ldap->domain); myfree(dict_ldap->query_filter); + if (dict_ldap->result_filter) + myfree(dict_ldap->result_filter); argv_free(dict_ldap->result_attributes); myfree(dict_ldap->bind_dn); myfree(dict_ldap->bind_pw); @@ -865,6 +968,19 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) msg_info("%s: %s is %s", myname, vstring_str(config_param), dict_ldap->query_filter); + vstring_sprintf(config_param, "%s_result_filter", ldapsource); + dict_ldap->result_filter = + mystrdup((char *) get_mail_conf_str(vstring_str(config_param), + "%s", + 0, 0)); + if (msg_verbose) + msg_info("%s: %s is %s", myname, vstring_str(config_param), + dict_ldap->result_filter); + + if (strcmp(dict_ldap->result_filter, "%s") == 0) { + myfree(dict_ldap->result_filter); + dict_ldap->result_filter = NULL; + } vstring_sprintf(config_param, "%s_result_attribute", ldapsource); attr = mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "maildrop", 0, 0)); @@ -953,6 +1069,24 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) dict_ldap->dereference = get_mail_conf_int(vstring_str(config_param), 0, 0, 0); + /* + * Define LDAP Version. + */ + vstring_sprintf(config_param, "%s_version", ldapsource); + dict_ldap->version = get_mail_conf_int(vstring_str(config_param), 2, 0, + 0); + switch (dict_ldap->version) { + case 2: + dict_ldap->version = LDAP_VERSION2; + break; + case 3: + dict_ldap->version = LDAP_VERSION3; + break; + default: + msg_warn("%s: Unknown version %d.", myname, dict_ldap->version); + dict_ldap->version = LDAP_VERSION2; + } + /* * Make sure only valid options for alias dereferencing are used. */ @@ -965,6 +1099,13 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->dereference); + /* Referral chasing */ + vstring_sprintf(config_param, "%s_chase_referrals", ldapsource); + dict_ldap->chase_referrals = 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->chase_referrals); + /* * Debug level. */