LDAP, *SQL, and memcache. Files: util/dict_test.c, util/dict.c,
global/mail_dict.c.
- Workaround: apparently, some distributions don't use proper
- so-number versioning, causing programs to fail erratically
- after an update replaces the Postfix library but not the
- program. Files: global/mail_version.[hc], master/*server.c,
+ Workaround: apparently, some distributions use Postfix
+ shared libraries without proper so-number versioning. This
+ causes programs to fail mysteriously, after an update
+ replaces the Postfix library but not the program (someone
+ experienced this with an extra copy of the Postfix SMTP
+ server). Files: global/mail_version.[hc], master/*server.c,
master/master.c, src/postalias/postalias.c,
src/postdrop/postdrop.c, src/postfix/postfix.c,
src/postlog/postlog.c, src/postmap/postmap.c,
dependencies. Files: global/memcache_proto.[hc],
global/dict_memcache.c.
+ Bugfix: missing lookup table entry and terminator, causing
+ proxymap(8) server segfault when postscreen(8) or verify(8)
+ attempted to access their cache via the proxymap(8) server.
+ This could never have worked anyway, because the Postfix
+ proxymap protocol did not support cache cleanup. File
+ util/dict.c.
+
Feature: support for persistent backup database in the
memcache client. The database can be shared with the proxymap
service, but it needs to be listed as "proxy:maptype:mapname"
(depending on whether the access is read-only or read-write).
Support for proxymap-over-tcp (proxy:maptype:mapname@host:port)
is under development. File: global/dict_memcache.c.
+
+20111214
+
+ Documentation: updated the submission and smtps examples
+ in the sample master.cf file, so that their logging is
+ easier to recognize. File: conf/master.cf.
+
+20111215
+
+ Documentation: use different hosts to separate MUA "port
+ 25" traffic from the "port 25" MX service. Files:
+ postscreen/postscreen.c, proto/POSTSCREEN_README.html.
+
+20111216
+
+ Cleanup: the proxymap client did not correctly propagate
+ the "open_lock" flag, causing the proxymap service to open
+ postscreen(8) and verify(8) caches twice, instead of once.
+ File: global/dict_proxy.c.
+
+ Cleanup: the verify and postscreen caches were not listed
+ as "authorized" for access via the proxywrite service. File:
+ global/mail_params.h.
+
+ Refactoring: the postscreen permanent access list code is
+ now a library module, so that it can be also used for remote
+ access to the proxymap server. Files: global/server_acl.[hc].
+
+ Hardening: read/write deadlines, to make the proxymap server
+ suitable for remote access. File: proxymap/proxymap.c.
+
+20111217
+
+ Cleanup: more orthogonal definition of when the proxymap
+ server can/cannot share a single map instance among multiple
+ requestors, and corresponding code cleanup in the proxymap
+ client and server. Files: util/dict.h, util/dict_test.c,
+ global/dict_proxy.c, proxymap/proxymap.c.
+
+ Human factors: the postscreen/verify cache manager now logs
+ the full database name including the proxy: prefix, to avoid
+ WTF surprises. File: util/dict_cache.c.
Typically, the Postfix memcache client is used to reduce query load on a
persistent database, but it may also be used to query a memory-only database
-for low-value, easy-to-create, information such as a reputation cache for
+for low-value, easy-to-recreate, information such as a reputation cache for
postscreen(8), verify(8) or greylisting.
L\bLi\bim\bmi\bit\bta\bat\bti\bio\bon\bns\bs
postscreen(8) should not be used on SMTP ports that receive mail from end-user
clients (MUAs). In a typical deployment, postscreen(8) is used on the "port 25"
service, while MUA clients submit mail via the submission service (port 587)
-which normally requires client authentication.
+which normally requires client authentication, or via a "port 25" server that
+provides no MX service (i.e. a dedicated server that provides submission
+service on port 25).
postscreen(8) is part of a multi-layer defense.
whitelist. The temporary whitelist is not used for SMTP client addresses that
appear on the permanent access list.
+ NOTE: To share a postscreen(8) cache between multiple postscreen(8)
+ instances, use "postscreen_cache_map = proxy:btree:/path/to/file". This
+ requires Postfix 2.9 or later; earlier proxymap(8) implementations don't
+ support cache cleanup. For an alternative approach see the memcache_table
+ (5) manpage.
+
When the SMTP client address appears on the temporary whitelist, postscreen(8)
logs this with the client address and port number as:
The memcache client updates the memcache whenever it looks up or
modifies information in the persistent database.
-The persistent database can be shared with the proxymap service,
-but it needs to be listed as "proxy:maptype:mapname" in the
-proxy_read_maps or proxy_write_maps parameter value (depending on
-whether the access is read-only or read-write).
-
Support for proxymap-over-tcp (proxy:maptype:mapname@host:port) is
under development.
or require that they reset dict_errno on entry, either exit
with a fatal error or set dict_errno on error.
+ multi_connect() function that takes a list of inet:host:port
+ and/or unix:pathname specs, with an explicit "inet" prefix
+ argument to handle applications that use host:port only.
+ This will simplify multi-host implementation for memcache
+ client, dovecot client, and other.
+
dict_memcache: treat "bad" key as cache miss, i.e. read/write
- the database as if the cache did not exist.
+ the database as if the cache did not exist. This does not
+ work because most Postfix maps (virtual, canonical, access,
+ transport, ...) also don't support spaces in keys.
+
+ postscreen: keep the cache open after "postfix reload" when
+ it is remote (type memcache: or proxy:). This does not work
+ because memcache can use a non-proxied file as backup).
Is it possible to replace msg_fatal calls in match_ops.c
by msg_warn and longjmp? The callers will have to specify
#dnsblog unix - - n - 0 dnsblog
#tlsproxy unix - - n - 0 tlsproxy
#submission inet n - n - - smtpd
+# -o syslog_name=postfix/submission
# -o smtpd_tls_security_level=encrypt
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
# -o milter_macro_daemon_name=ORIGINATING
#smtps inet n - n - - smtpd
+# -o syslog_name=postfix/smtps
# -o smtpd_tls_wrappermode=yes
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_client_restrictions=permit_sasl_authenticated,reject
<p> Typically, the Postfix memcache client is used to reduce query
load on a persistent database, but it may also be used to query a
-memory-only database for low-value, easy-to-create, information
+memory-only database for low-value, easy-to-recreate, information
such as a reputation cache for <a href="postscreen.8.html">postscreen(8)</a>, <a href="verify.8.html">verify(8)</a> or greylisting.
</p>
mail from end-user clients (MUAs). In a typical deployment,
<a href="postscreen.8.html">postscreen(8)</a> is used on the "port 25" service, while MUA clients
submit mail via the submission service (port 587) which normally
-requires client authentication. </p>
+requires client authentication, or via a "port 25" server that
+provides no MX service (i.e. a dedicated server that provides
+submission service on port 25). </p>
<p> <a href="postscreen.8.html">postscreen(8)</a> is part of a multi-layer defense. <p>
temporary whitelist is not used for SMTP client addresses
that appear on the <i>permanent</i> access list. </p>
+<blockquote> <p> NOTE: To share a <a href="postscreen.8.html">postscreen(8)</a> cache between
+multiple <a href="postscreen.8.html">postscreen(8)</a> instances, use "<tt><a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> =
+<a href="proxymap.8.html">proxy</a>:btree:/path/to/file</tt>". This requires Postfix 2.9 or
+later; earlier <a href="proxymap.8.html">proxymap(8)</a> implementations don't support cache
+cleanup. For an alternative approach see the <a href="memcache_table.5.html">memcache_table(5)</a>
+manpage. </p> </blockquote>
+
<p> When the SMTP client address appears on the temporary
whitelist, <a href="postscreen.8.html">postscreen(8)</a> logs this with the client address and port
number as: </p>
memcache client will update the memcache database
whenever it looks up or changes information in the
persistent database. Specify a Postfix "<a href="DATABASE_README.html">type:table</a>"
- database. Example:
+ database. Examples:
+ # Non-shared postscreen cache.
backup = btree:/var/lib/postfix/<a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a>
+ # Shared postscreen cache for processes on the same host.
+ backup = <a href="proxymap.8.html">proxy</a>:btree:/var/lib/postfix/<a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a>
+
Access to remote proxymap servers is under develop-
ment.
<p> Persistent storage for the <a href="postscreen.8.html">postscreen(8)</a> server decisions. </p>
+<p> To share a <a href="postscreen.8.html">postscreen(8)</a> cache between multiple <a href="postscreen.8.html">postscreen(8)</a>
+instances, use "<a href="postconf.5.html#postscreen_cache_map">postscreen_cache_map</a> = <a href="proxymap.8.html">proxy</a>:btree:/path/to/file".
+This requires Postfix version 2.9 or later; earlier <a href="proxymap.8.html">proxymap(8)</a>
+implementations don't support cache cleanup. For an alternative
+approach see the <a href="memcache_table.5.html">memcache_table(5)</a> manpage. </p>
+
<p> This feature is available in Postfix 2.8. </p>
This program should not be used on SMTP ports that receive
mail from end-user clients (MUAs). In a typical deploy-
ment, <a href="postscreen.8.html"><b>postscreen</b>(8)</a> is used on the "port 25" service,
- while MUA clients submit mail via the <b>submission</b> service.
+ while MUA clients submit mail via the <b>submission</b> service,
+ or via a "port 25" server that provides no MX service
+ (i.e. a dedicated server that provides <b>submission</b> service
+ on port 25).
<a href="postscreen.8.html"><b>postscreen</b>(8)</a> maintains a temporary whitelist for clients
that have passed a number of tests. When an SMTP client
for the memcache database. The Postfix memcache client will
update the memcache database whenever it looks up or changes
information in the persistent database. Specify a Postfix
-"type:table" database. Example:
+"type:table" database. Examples:
.nf
+ # Non-shared postscreen cache.
backup = btree:/var/lib/postfix/postscreen_cache_map
+
+ # Shared postscreen cache for processes on the same host.
+ backup = proxy:btree:/var/lib/postfix/postscreen_cache_map
.fi
Access to remote proxymap servers is under development.
.SH postscreen_cache_map (default: btree:$data_directory/postscreen_cache)
Persistent storage for the \fBpostscreen\fR(8) server decisions.
.PP
+To share a \fBpostscreen\fR(8) cache between multiple \fBpostscreen\fR(8)
+instances, use "postscreen_cache_map = proxy:btree:/path/to/file".
+This requires Postfix version 2.9 or later; earlier \fBproxymap\fR(8)
+implementations don't support cache cleanup. For an alternative
+approach see the \fBmemcache_table\fR(5) manpage.
+.PP
This feature is available in Postfix 2.8.
.SH postscreen_cache_retention_time (default: 7d)
The amount of time that \fBpostscreen\fR(8) will cache an expired
This program should not be used on SMTP ports that receive
mail from end-user clients (MUAs). In a typical deployment,
\fBpostscreen\fR(8) is used on the "port 25" service, while
-MUA clients submit mail via the \fBsubmission\fR service.
+MUA clients submit mail via the \fBsubmission\fR service,
+or via a "port 25" server that provides no MX service (i.e.
+a dedicated server that provides \fBsubmission\fR service
+on port 25).
\fBpostscreen\fR(8) maintains a temporary whitelist for
clients that have passed a number of tests. When an SMTP
<p> Typically, the Postfix memcache client is used to reduce query
load on a persistent database, but it may also be used to query a
-memory-only database for low-value, easy-to-create, information
+memory-only database for low-value, easy-to-recreate, information
such as a reputation cache for postscreen(8), verify(8) or greylisting.
</p>
mail from end-user clients (MUAs). In a typical deployment,
postscreen(8) is used on the "port 25" service, while MUA clients
submit mail via the submission service (port 587) which normally
-requires client authentication. </p>
+requires client authentication, or via a "port 25" server that
+provides no MX service (i.e. a dedicated server that provides
+submission service on port 25). </p>
<p> postscreen(8) is part of a multi-layer defense. <p>
temporary whitelist is not used for SMTP client addresses
that appear on the <i>permanent</i> access list. </p>
+<blockquote> <p> NOTE: To share a postscreen(8) cache between
+multiple postscreen(8) instances, use "<tt>postscreen_cache_map =
+proxy:btree:/path/to/file</tt>". This requires Postfix 2.9 or
+later; earlier proxymap(8) implementations don't support cache
+cleanup. For an alternative approach see the memcache_table(5)
+manpage. </p> </blockquote>
+
<p> When the SMTP client address appears on the temporary
whitelist, postscreen(8) logs this with the client address and port
number as: </p>
# for the memcache database. The Postfix memcache client will
# update the memcache database whenever it looks up or changes
# information in the persistent database. Specify a Postfix
-# "type:table" database. Example:
+# "type:table" database. Examples:
#
# .nf
+# # Non-shared postscreen cache.
# backup = btree:/var/lib/postfix/postscreen_cache_map
+#
+# # Shared postscreen cache for processes on the same host.
+# backup = proxy:btree:/var/lib/postfix/postscreen_cache_map
# .fi
#
# Access to remote proxymap servers is under development.
<p> Persistent storage for the postscreen(8) server decisions. </p>
+<p> To share a postscreen(8) cache between multiple postscreen(8)
+instances, use "postscreen_cache_map = proxy:btree:/path/to/file".
+This requires Postfix version 2.9 or later; earlier proxymap(8)
+implementations don't support cache cleanup. For an alternative
+approach see the memcache_table(5) manpage. </p>
+
<p> This feature is available in Postfix 2.8. </p>
%PARAM smtpd_service_name smtpd
fold_addr.c header_body_checks.c mkmap_proxy.c data_redirect.c \
match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \
smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \
- dict_memcache.c mail_version.c memcache_proto.c
+ dict_memcache.c mail_version.c memcache_proto.c server_acl.c
OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
fold_addr.o header_body_checks.o mkmap_proxy.o data_redirect.o \
match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \
smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \
- dict_memcache.o mail_version.o memcache_proto.o
+ dict_memcache.o mail_version.o memcache_proto.o server_acl.o
HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
verp_sender.h wildcard_inet_addr.h xtext.h delivered_hdr.h \
fold_addr.h header_body_checks.h data_redirect.h match_service.h \
addr_match_list.h smtp_reply_footer.h safe_ultostr.h \
- verify_sender_addr.h dict_memcache.h memcache_proto.h
+ verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
verify_clnt xtext anvil_clnt scache ehlo_mask \
valid_mailhost_addr own_inet_addr header_body_checks \
data_redirect addr_match_list safe_ultostr verify_sender_addr \
- mail_version mail_dict
+ mail_version mail_dict server_acl
LIBS = ../../lib/libutil.a
LIB_DIR = ../../lib
mail_dict: mail_dict.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+server_acl: server_acl.c $(LIB) $(LIBS)
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
xtext_test scache_multi_test ehlo_mask_test \
namadr_list_test mail_conf_time_test header_body_checks_tests \
mime_state.o: mime_state.c
mime_state.o: mime_state.h
mime_state.o: rec_type.h
+mkmap_cdb.o: ../../include/argv.h
+mkmap_cdb.o: ../../include/dict.h
+mkmap_cdb.o: ../../include/dict_cdb.h
+mkmap_cdb.o: ../../include/mymalloc.h
mkmap_cdb.o: ../../include/sys_defs.h
+mkmap_cdb.o: ../../include/vbuf.h
+mkmap_cdb.o: ../../include/vstream.h
+mkmap_cdb.o: ../../include/vstring.h
+mkmap_cdb.o: mkmap.h
mkmap_cdb.o: mkmap_cdb.c
mkmap_db.o: ../../include/argv.h
mkmap_db.o: ../../include/dict.h
sent.o: sent.h
sent.o: trace.h
sent.o: verify.h
+server_acl.o: ../../include/match_list.h
+server_acl.o: ../../include/match_ops.h
+server_acl.o: ../../include/msg.h
+server_acl.o: ../../include/mymalloc.h
+server_acl.o: ../../include/stringops.h
+server_acl.o: ../../include/sys_defs.h
+server_acl.o: ../../include/vbuf.h
+server_acl.o: ../../include/vstring.h
+server_acl.o: addr_match_list.h
+server_acl.o: mail_params.h
+server_acl.o: match_parent_style.h
+server_acl.o: server_acl.c
smtp_reply_footer.o: ../../include/mac_expand.h
smtp_reply_footer.o: ../../include/mac_parse.h
smtp_reply_footer.o: ../../include/msg.h
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
-#define msg_verbose 1
+/*#define msg_verbose 1*/
/* dict_memcache_set - set memcache key/value */
DICT dict; /* generic members */
CLNT_STREAM *clnt; /* client handle (shared) */
const char *service; /* service name */
- int in_flags; /* caller-specified flags */
+ int inst_flags; /* saved dict flags */
VSTRING *reskey; /* result key storage */
VSTRING *result; /* storage */
} DICT_PROXY;
VSTRING_TERMINATE(dict_proxy->reskey);
VSTRING_RESET(dict_proxy->result);
VSTRING_TERMINATE(dict_proxy->result);
- request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
+ request_flags = dict_proxy->inst_flags
| (dict->flags & DICT_FLAG_RQST_MASK);
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
*/
VSTRING_RESET(dict_proxy->result);
VSTRING_TERMINATE(dict_proxy->result);
- request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
+ request_flags = dict_proxy->inst_flags
| (dict->flags & DICT_FLAG_RQST_MASK);
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
* associated with a specific connection. Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open().
*/
- request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
+ request_flags = dict_proxy->inst_flags
| (dict->flags & DICT_FLAG_RQST_MASK);
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
* associated with a specific connection. Each lookup needs to specify
* the table and the flags that were specified to dict_proxy_open().
*/
- request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
+ request_flags = dict_proxy->inst_flags
| (dict->flags & DICT_FLAG_RQST_MASK);
for (;;) {
stream = clnt_stream_access(dict_proxy->clnt);
dict_proxy->dict.delete = dict_proxy_delete;
dict_proxy->dict.sequence = dict_proxy_sequence;
dict_proxy->dict.close = dict_proxy_close;
- dict_proxy->in_flags = dict_flags;
+ dict_proxy->inst_flags = (dict_flags & DICT_FLAG_INST_MASK);
dict_proxy->reskey = vstring_alloc(10);
dict_proxy->result = vstring_alloc(10);
dict_proxy->clnt = *pstream;
if (attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_OPEN,
ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict_proxy->dict.name,
- ATTR_TYPE_INT, MAIL_ATTR_FLAGS, dict_proxy->in_flags,
+ ATTR_TYPE_INT, MAIL_ATTR_FLAGS, dict_proxy->inst_flags,
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
msg_fatal("%s service is not configured for table \"%s\"",
dict_proxy->service, dict_proxy->dict.name);
case PROXY_STAT_OK:
- dict_proxy->dict.flags = dict_proxy->in_flags
+ dict_proxy->dict.flags = (dict_flags & ~DICT_FLAG_IMPL_MASK)
| (server_flags & DICT_FLAG_IMPL_MASK);
return (DICT_DEBUG (&dict_proxy->dict));
default:
" $" VAR_SEND_BCC_MAPS \
" $" VAR_RCPT_BCC_MAPS \
" $" VAR_SMTP_GENERIC_MAPS \
- " $" VAR_LMTP_GENERIC_MAPS
+ " $" VAR_LMTP_GENERIC_MAPS \
+ " $" VAR_ALIAS_MAPS
extern char *var_proxy_read_maps;
#define VAR_PROXY_WRITE_MAPS "proxy_write_maps"
#define DEF_PROXY_WRITE_MAPS "$" VAR_SMTP_SASL_AUTH_CACHE_NAME \
- " $" VAR_LMTP_SASL_AUTH_CACHE_NAME
+ " $" VAR_LMTP_SASL_AUTH_CACHE_NAME \
+ " $" VAR_VERIFY_MAP \
+ " $" VAR_PSC_CACHE_MAP
extern char *var_proxy_write_maps;
+#define VAR_PROXY_READ_ACL "proxy_read_access_list"
+#define DEF_PROXY_READ_ACL "reject"
+extern char *var_proxy_read_acl;
+
+#define VAR_PROXY_WRITE_ACL "proxy_write_access_list"
+#define DEF_PROXY_WRITE_ACL "reject"
+extern char *var_proxy_write_acl;
+
/*
* Other.
*/
#define DEF_PSC_CMD_FILTER ""
extern char *var_psc_cmd_filter;
-#define PSC_ACL_NAME_WL_MYNETWORKS "permit_mynetworks"
-#define PSC_ACL_NAME_WHITELIST "permit"
-#define PSC_ACL_NAME_BLACKLIST "reject"
-#define PSC_ACL_NAME_DUNNO "dunno"
-#define PSC_ACL_NAME_ERROR "error"
-
#define VAR_PSC_ACL "postscreen_access_list"
-#define DEF_PSC_ACL PSC_ACL_NAME_WL_MYNETWORKS
+#define DEF_PSC_ACL SERVER_ACL_NAME_WL_MYNETWORKS
extern char *var_psc_acl;
#define VAR_PSC_WLIST_IF "postscreen_whitelist_interfaces"
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20111213"
+#define MAIL_RELEASE_DATE "20111217"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT
--- /dev/null
+/*++
+/* NAME
+/* server_acl 3
+/* SUMMARY
+/* server access list
+/* SYNOPSIS
+/* #include <server_acl.h>
+/*
+/* void server_acl_pre_jail_init(mynetworks, param_name)
+/* const char *mynetworks;
+/* const char *param_name;
+/*
+/* SERVER_ACL *server_acl_parse(extern_acl, param_name)
+/* const char *extern_acl;
+/* const char *param_name;
+/*
+/* int server_acl_eval(client_addr, intern_acl, param_name)
+/* const char *client_addr;
+/* SERVER_ACL *intern_acl;
+/* const char *param_name;
+/* DESCRIPTION
+/* This module implements a permanent black/whitelist that
+/* is meant to be evaluated immediately after a client connects
+/* to a server.
+/*
+/* server_acl_pre_jail_init() does before-chroot initialization
+/* for the permit_mynetworks setting.
+/*
+/* server_acl_parse() converts an access list from raw string
+/* form to binary form. It should also be called as part of
+/* before-chroot initialization.
+/*
+/* server_acl_eval() evaluates an access list for the specified
+/* client address. The result is SERVER_ACL_ACT_PERMIT (permit),
+/* SERVER_ACL_ACT_REJECT (reject), SERVER_ACL_ACT_DUNNO (no
+/* decision), or SERVER_ACL_ACT_ERROR (error, unknown command
+/* or database access error).
+/*
+/* Arguments:
+/* .IP mynetworks
+/* Network addresses that match "permit_mynetworks".
+/* .IP param_name
+/* The configuration parameter name for the access list from
+/* main.cf. The information is used for error reporting (nested
+/* table, unknown keyword) and to select the appropriate
+/* behavior from parent_domain_matches_subdomains.
+/* .IP extern_acl
+/* External access list representation.
+/* .IP intern_acl
+/* Internal access list representation.
+/* .IP client_addr
+/* The client IP address as printable string (without []).
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <dict.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <addr_match_list.h>
+#include <match_parent_style.h>
+#include <server_acl.h>
+
+/* Application-specific. */
+
+#define SERVER_ACL_SEPARATORS ", \t\r\n"
+
+static ADDR_MATCH_LIST *server_acl_mynetworks;
+
+#define STR vstring_str
+
+/* server_acl_pre_jail_init - initialize */
+
+void server_acl_pre_jail_init(const char *mynetworks, const char *origin)
+{
+ if (server_acl_mynetworks)
+ addr_match_list_free(server_acl_mynetworks);
+ server_acl_mynetworks = addr_match_list_init(match_parent_style(origin),
+ mynetworks);
+}
+
+/* server_acl_parse - parse access list */
+
+SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
+{
+ char *saved_acl = mystrdup(extern_acl);
+ SERVER_ACL *intern_acl = argv_alloc(1);
+ char *bp = saved_acl;
+ char *acl;
+
+#define STREQ(x,y) ((*x) == (*y) && strcasecmp((x), (y)) == 0)
+#define STRNE(x,y) ((*x) != (*y) || strcasecmp((x), (y)) != 0)
+
+ /*
+ * Nested tables are not allowed. Tables are opened before entering the
+ * chroot jail, while access lists are evaluated after entering the
+ * chroot jail.
+ */
+ while ((acl = mystrtok(&bp, SERVER_ACL_SEPARATORS)) != 0) {
+ if (strchr(acl, ':') != 0) {
+ if (strchr(origin, ':') != 0) {
+ msg_warn("table %s: lookup result \"%s\" is not allowed"
+ " -- ignoring remainder of access list",
+ origin, acl);
+ argv_add(intern_acl, SERVER_ACL_NAME_DUNNO, (char *) 0);
+ break;
+ } else {
+ if (dict_handle(acl) == 0)
+ dict_register(acl, dict_open(acl, O_RDONLY, DICT_FLAG_LOCK
+ | DICT_FLAG_FOLD_FIX));
+ }
+ }
+ argv_add(intern_acl, acl, (char *) 0);
+ }
+ argv_terminate(intern_acl);
+
+ /*
+ * Cleanup.
+ */
+ myfree(saved_acl);
+ return (intern_acl);
+}
+
+/* server_acl_eval - evaluate access list */
+
+int server_acl_eval(const char *client_addr, SERVER_ACL * intern_acl,
+ const char *origin)
+{
+ const char *myname = "server_acl_eval";
+ char **cpp;
+ DICT *dict;
+ SERVER_ACL *argv;
+ const char *acl;
+ const char *dict_val;
+ int ret;
+ ARGV fake_argv;
+ const char *fake_args[2];
+
+ for (cpp = intern_acl->argv; (acl = *cpp) != 0; cpp++) {
+ if (msg_verbose)
+ msg_info("source=%s address=%s acl=%s",
+ origin, client_addr, acl);
+ if (STREQ(acl, SERVER_ACL_NAME_REJECT)) {
+ return (SERVER_ACL_ACT_REJECT);
+ } else if (STREQ(acl, SERVER_ACL_NAME_PERMIT)) {
+ return (SERVER_ACL_ACT_PERMIT);
+ } else if (STREQ(acl, SERVER_ACL_NAME_WL_MYNETWORKS)) {
+ if (addr_match_list_match(server_acl_mynetworks, client_addr))
+ return (SERVER_ACL_ACT_PERMIT);
+ } else if (strchr(acl, ':') != 0) {
+ if ((dict = dict_handle(acl)) == 0)
+ msg_panic("%s: unexpected dictionary: %s", myname, acl);
+ if ((dict_val = dict_get(dict, client_addr)) != 0) {
+ /* Fake up an ARGV to avoid lots of mallocs and frees. */
+ if (dict_val[strcspn(dict_val, ":" SERVER_ACL_SEPARATORS)] == 0) {
+ fake_args[0] = dict_val;
+ fake_args[1] = 0;
+ fake_argv.argv = (char **) fake_args;
+ fake_argv.argc = fake_argv.len = 1;
+ ret = server_acl_eval(client_addr, &fake_argv, acl);
+ } else {
+ argv = server_acl_parse(dict_val, acl);
+ ret = server_acl_eval(client_addr, argv, acl);
+ argv_free(argv);
+ }
+ if (ret != SERVER_ACL_ACT_DUNNO)
+ return (ret);
+ } else if (dict_errno != 0) {
+ msg_warn("%s: table lookup error -- ignoring the remainder "
+ "of this access list", acl);
+ return (SERVER_ACL_ACT_ERROR);
+ }
+ } else if (STREQ(acl, SERVER_ACL_NAME_DUNNO)) {
+ return (SERVER_ACL_ACT_DUNNO);
+ } else {
+ msg_warn("%s: unknown command: %s -- ignoring the remainder "
+ "of this access list", origin, acl);
+ return (SERVER_ACL_ACT_ERROR);
+ }
+ }
+ if (msg_verbose)
+ msg_info("source=%s address=%s - no match",
+ origin, client_addr);
+ return (SERVER_ACL_ACT_DUNNO);
+}
+
+ /*
+ * Access lists need testing. Not only with good inputs; error cases must
+ * also be handled appropriately.
+ */
+#ifdef TEST
+#include <unistd.h>
+#include <stdlib.h>
+#include <vstring_vstream.h>
+#include <name_code.h>
+#include <split_at.h>
+
+char *var_par_dom_match = DEF_PAR_DOM_MATCH;
+char *var_mynetworks = "";
+char *var_server_acl = "";
+
+#define UPDATE_VAR(s,v) do { if (*(s)) myfree(s); (s) = mystrdup(v); } while (0)
+
+int main(void)
+{
+ VSTRING *buf = vstring_alloc(100);
+ SERVER_ACL *argv;
+ int ret;
+ int have_tty = isatty(0);
+ char *bufp;
+ char *cmd;
+ char *value;
+ const NAME_CODE acl_map[] = {
+ SERVER_ACL_NAME_ERROR, SERVER_ACL_ACT_ERROR,
+ SERVER_ACL_NAME_PERMIT, SERVER_ACL_ACT_PERMIT,
+ SERVER_ACL_NAME_REJECT, SERVER_ACL_ACT_REJECT,
+ SERVER_ACL_NAME_DUNNO, SERVER_ACL_ACT_DUNNO,
+ 0,
+ };
+
+#define VAR_SERVER_ACL "server_acl"
+
+ while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
+ bufp = STR(buf);
+ if (have_tty == 0) {
+ vstream_printf("> %s\n", bufp);
+ vstream_fflush(VSTREAM_OUT);
+ }
+ if (*bufp == '#')
+ continue;
+ if ((cmd = mystrtok(&bufp, " =")) == 0 || STREQ(cmd, "?")) {
+ vstream_printf("usage: %s=value|%s=value|address=value\n",
+ VAR_MYNETWORKS, VAR_SERVER_ACL);
+ } else if ((value = mystrtok(&bufp, " =")) == 0) {
+ vstream_printf("missing value\n");
+ } else if (STREQ(cmd, VAR_MYNETWORKS)) {
+ UPDATE_VAR(var_mynetworks, value);
+ } else if (STREQ(cmd, VAR_SERVER_ACL)) {
+ UPDATE_VAR(var_server_acl, value);
+ } else if (STREQ(cmd, "address")) {
+ server_acl_pre_jail_init(var_mynetworks, VAR_SERVER_ACL);
+ argv = server_acl_parse(var_server_acl, VAR_SERVER_ACL);
+ ret = server_acl_eval(value, argv, VAR_SERVER_ACL);
+ argv_free(argv);
+ vstream_printf("%s: %s\n", value, str_name_code(acl_map, ret));
+ } else {
+ vstream_printf("unknown command: \"%s\"\n", cmd);
+ }
+ vstream_fflush(VSTREAM_OUT);
+ }
+ vstring_free(buf);
+ exit(0);
+}
+
+#endif
--- /dev/null
+#ifndef _SERVER_ACL_INCLUDED_
+#define _SERVER_ACL_INCLUDED_
+
+/*++
+/* NAME
+/* dict_memcache 3h
+/* SUMMARY
+/* dictionary interface to memcache databases
+/* SYNOPSIS
+/* #include <dict_memcache.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+
+ /*
+ * External interface.
+ */
+typedef ARGV SERVER_ACL;
+extern void server_acl_pre_jail_init(const char *, const char *);
+extern SERVER_ACL *server_acl_parse(const char *, const char *);
+extern int server_acl_eval(const char *, SERVER_ACL *, const char *);
+
+#define SERVER_ACL_NAME_WL_MYNETWORKS "permit_mynetworks"
+#define SERVER_ACL_NAME_PERMIT "permit"
+#define SERVER_ACL_NAME_DUNNO "dunno"
+#define SERVER_ACL_NAME_REJECT "reject"
+#define SERVER_ACL_NAME_ERROR "error"
+
+#define SERVER_ACL_ACT_PERMIT 1
+#define SERVER_ACL_ACT_DUNNO 0
+#define SERVER_ACL_ACT_REJECT (-1)
+#define SERVER_ACL_ACT_ERROR (-2)
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
postconf_builtin.o: ../../include/msg.h
postconf_builtin.o: ../../include/mymalloc.h
postconf_builtin.o: ../../include/mynetworks.h
+postconf_builtin.o: ../../include/server_acl.h
postconf_builtin.o: ../../include/stringops.h
postconf_builtin.o: ../../include/sys_defs.h
postconf_builtin.o: ../../include/vbuf.h
#include <mail_proto.h>
#include <mail_addr.h>
#include <inet_proto.h>
+#include <server_acl.h>
/* Application-specific. */
SRCS = postscreen.c postscreen_dict.c postscreen_dnsbl.c \
postscreen_early.c postscreen_smtpd.c postscreen_misc.c \
postscreen_state.c postscreen_tests.c postscreen_send.c \
- postscreen_starttls.c postscreen_expand.c postscreen_access.c
+ postscreen_starttls.c postscreen_expand.c
OBJS = postscreen.o postscreen_dict.o postscreen_dnsbl.o \
postscreen_early.o postscreen_smtpd.o postscreen_misc.o \
postscreen_state.o postscreen_tests.o postscreen_send.o \
- postscreen_starttls.o postscreen_expand.o postscreen_access.o
+ postscreen_starttls.o postscreen_expand.o
HDRS =
TESTSRC =
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
-TESTPROG= postscreen_access
+TESTPROG=
PROG = postscreen
INC_DIR = ../../include
LIBS = ../../lib/libmaster.a ../../lib/libtls.a ../../lib/libglobal.a \
../../libexec/$(PROG): $(PROG)
cp $(PROG) ../../libexec
-postscreen_access: $(PROG) $(LIBS)
- mv $@.o junk
- $(CC) -DTEST $(CFLAGS) -o $@ $@.c $(LIBS) $(SYSLIBS)
- mv junk $@.o
-
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
postscreen.o: ../../include/myaddrinfo.h
postscreen.o: ../../include/mymalloc.h
postscreen.o: ../../include/name_code.h
+postscreen.o: ../../include/server_acl.h
postscreen.o: ../../include/set_eugid.h
postscreen.o: ../../include/string_list.h
postscreen.o: ../../include/sys_defs.h
postscreen.o: ../../include/vstring.h
postscreen.o: postscreen.c
postscreen.o: postscreen.h
-postscreen_access.o: ../../include/addr_match_list.h
-postscreen_access.o: ../../include/argv.h
-postscreen_access.o: ../../include/dict.h
-postscreen_access.o: ../../include/dict_cache.h
-postscreen_access.o: ../../include/events.h
-postscreen_access.o: ../../include/htable.h
-postscreen_access.o: ../../include/mail_params.h
-postscreen_access.o: ../../include/maps.h
-postscreen_access.o: ../../include/match_list.h
-postscreen_access.o: ../../include/match_ops.h
-postscreen_access.o: ../../include/match_parent_style.h
-postscreen_access.o: ../../include/msg.h
-postscreen_access.o: ../../include/mymalloc.h
-postscreen_access.o: ../../include/string_list.h
-postscreen_access.o: ../../include/stringops.h
-postscreen_access.o: ../../include/sys_defs.h
-postscreen_access.o: ../../include/vbuf.h
-postscreen_access.o: ../../include/vstream.h
-postscreen_access.o: ../../include/vstring.h
-postscreen_access.o: postscreen.h
-postscreen_access.o: postscreen_access.c
postscreen_dict.o: ../../include/addr_match_list.h
postscreen_dict.o: ../../include/argv.h
postscreen_dict.o: ../../include/dict.h
postscreen_dict.o: ../../include/match_list.h
postscreen_dict.o: ../../include/match_ops.h
postscreen_dict.o: ../../include/msg.h
+postscreen_dict.o: ../../include/server_acl.h
postscreen_dict.o: ../../include/string_list.h
postscreen_dict.o: ../../include/sys_defs.h
postscreen_dict.o: ../../include/vbuf.h
postscreen_dnsbl.o: ../../include/msg.h
postscreen_dnsbl.o: ../../include/myaddrinfo.h
postscreen_dnsbl.o: ../../include/mymalloc.h
+postscreen_dnsbl.o: ../../include/server_acl.h
postscreen_dnsbl.o: ../../include/split_at.h
postscreen_dnsbl.o: ../../include/string_list.h
postscreen_dnsbl.o: ../../include/stringops.h
postscreen_early.o: ../../include/match_ops.h
postscreen_early.o: ../../include/msg.h
postscreen_early.o: ../../include/mymalloc.h
+postscreen_early.o: ../../include/server_acl.h
postscreen_early.o: ../../include/string_list.h
postscreen_early.o: ../../include/stringops.h
postscreen_early.o: ../../include/sys_defs.h
postscreen_expand.o: ../../include/match_list.h
postscreen_expand.o: ../../include/match_ops.h
postscreen_expand.o: ../../include/msg.h
+postscreen_expand.o: ../../include/server_acl.h
postscreen_expand.o: ../../include/string_list.h
postscreen_expand.o: ../../include/stringops.h
postscreen_expand.o: ../../include/sys_defs.h
postscreen_misc.o: ../../include/match_list.h
postscreen_misc.o: ../../include/match_ops.h
postscreen_misc.o: ../../include/msg.h
+postscreen_misc.o: ../../include/server_acl.h
postscreen_misc.o: ../../include/string_list.h
postscreen_misc.o: ../../include/sys_defs.h
postscreen_misc.o: ../../include/vbuf.h
postscreen_send.o: ../../include/match_list.h
postscreen_send.o: ../../include/match_ops.h
postscreen_send.o: ../../include/msg.h
+postscreen_send.o: ../../include/server_acl.h
postscreen_send.o: ../../include/smtp_reply_footer.h
postscreen_send.o: ../../include/string_list.h
postscreen_send.o: ../../include/sys_defs.h
postscreen_smtpd.o: ../../include/mymalloc.h
postscreen_smtpd.o: ../../include/name_code.h
postscreen_smtpd.o: ../../include/name_mask.h
+postscreen_smtpd.o: ../../include/server_acl.h
postscreen_smtpd.o: ../../include/string_list.h
postscreen_smtpd.o: ../../include/stringops.h
postscreen_smtpd.o: ../../include/sys_defs.h
postscreen_starttls.o: ../../include/mymalloc.h
postscreen_starttls.o: ../../include/name_code.h
postscreen_starttls.o: ../../include/name_mask.h
+postscreen_starttls.o: ../../include/server_acl.h
postscreen_starttls.o: ../../include/string_list.h
postscreen_starttls.o: ../../include/stringops.h
postscreen_starttls.o: ../../include/sys_defs.h
postscreen_state.o: ../../include/msg.h
postscreen_state.o: ../../include/mymalloc.h
postscreen_state.o: ../../include/name_mask.h
+postscreen_state.o: ../../include/server_acl.h
postscreen_state.o: ../../include/string_list.h
postscreen_state.o: ../../include/sys_defs.h
postscreen_state.o: ../../include/vbuf.h
postscreen_tests.o: ../../include/match_list.h
postscreen_tests.o: ../../include/match_ops.h
postscreen_tests.o: ../../include/msg.h
+postscreen_tests.o: ../../include/server_acl.h
postscreen_tests.o: ../../include/string_list.h
postscreen_tests.o: ../../include/sys_defs.h
postscreen_tests.o: ../../include/vbuf.h
/* This program should not be used on SMTP ports that receive
/* mail from end-user clients (MUAs). In a typical deployment,
/* \fBpostscreen\fR(8) is used on the "port 25" service, while
-/* MUA clients submit mail via the \fBsubmission\fR service.
+/* MUA clients submit mail via the \fBsubmission\fR service,
+/* or via a "port 25" server that provides no MX service (i.e.
+/* a dedicated server that provides \fBsubmission\fR service
+/* on port 25).
/*
/* \fBpostscreen\fR(8) maintains a temporary whitelist for
/* clients that have passed a number of tests. When an SMTP
* Open read-only maps before dropping privilege, for consistency with
* other Postfix daemons.
*/
- psc_acl_pre_jail_init();
+ psc_acl_pre_jail_init(var_mynetworks, VAR_PSC_ACL);
if (*var_psc_acl)
psc_acl = psc_acl_parse(var_psc_acl, VAR_PSC_ACL);
if (*var_psc_forbid_cmds)
#include <addr_match_list.h>
#include <string_list.h>
#include <maps.h>
+#include <server_acl.h>
/*
* Preliminary stuff, to be fixed.
extern const char *psc_expand_lookup(const char *, int, char *);
/*
- * postscreen_access.c
+ * postscreen_access emulation.
*/
-#define PSC_ACL_ACT_WHITELIST 1
-#define PSC_ACL_ACT_DUNNO 0
-#define PSC_ACL_ACT_BLACKLIST (-1)
-#define PSC_ACL_ACT_ERROR (-2)
-
-extern void psc_acl_pre_jail_init(void);
-extern ARGV *psc_acl_parse(const char *, const char *);
-extern int psc_acl_eval(PSC_STATE *, ARGV *, const char *);
+#define PSC_ACL_ACT_WHITELIST SERVER_ACL_ACT_PERMIT
+#define PSC_ACL_ACT_DUNNO SERVER_ACL_ACT_DUNNO
+#define PSC_ACL_ACT_BLACKLIST SERVER_ACL_ACT_REJECT
+#define PSC_ACL_ACT_ERROR SERVER_ACL_ACT_ERROR
+
+#define psc_acl_pre_jail_init server_acl_pre_jail_init
+#define psc_acl_parse server_acl_parse
+#define psc_acl_eval(s,a,p) server_acl_eval((s)->smtp_client_addr, (a), (p))
/* LICENSE
/* .ad
+++ /dev/null
-/*++
-/* NAME
-/* postscreen_access 3
-/* SUMMARY
-/* postscreen access list support
-/* SYNOPSIS
-/* #include <postscreen.h>
-/*
-/* void psc_acl_pre_jail_init()
-/*
-/* ARGV *psc_acl_parse(raw_acl, origin)
-/* const char *raw_acl;
-/* const char *origin;
-/*
-/* int psc_acl_eval(state, cooked_acl, origin)
-/* PSC_STATE *state;
-/* ARGV *cooked_acl;
-/* const char *origin;
-/* DESCRIPTION
-/* This module implements the permanent black/whitelist that
-/* is evaluated immediately after a client connects to postscreen.
-/*
-/* psc_acl_pre_jail_init() does before-chroot initialization.
-/*
-/* psc_acl_parse() converts an access list from raw string
-/* form to binary form.
-/*
-/* psc_acl_eval() evaluates an access list for the specified
-/* SMTP session.
-/*
-/* Arguments:
-/* .IP raw_acl
-/* String with space/comma separated commands.
-/* .IP cooked_acl
-/* The parsed access list.
-/* .IP origin
-/* This should be "postscreen_access_list" for an access list
-/* from main.cf, and the type:name of a lookup table otherwise.
-/* The information is used for error reporting (nested table,
-/* unknown keyword).
-/* .IP state
-/* Connection state.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <string.h>
-
-#ifdef STRCASECMP_IN_STRINGS_H
-#include <strings.h>
-#endif
-
-/* Utility library. */
-
-#include <msg.h>
-#include <mymalloc.h>
-#include <stringops.h>
-
-/* Global library. */
-
-#include <mail_params.h>
-#include <addr_match_list.h>
-#include <match_parent_style.h>
-
-/* Application-specific. */
-
-#include <postscreen.h>
-
-#define PSC_ACL_SEPARATORS ", \t\r"
-
-static ADDR_MATCH_LIST *psc_mynetworks;
-
-/* psc_acl_pre_jail_init - initialize */
-
-void psc_acl_pre_jail_init(void)
-{
- if (psc_mynetworks)
- addr_match_list_free(psc_mynetworks);
- psc_mynetworks = addr_match_list_init(match_parent_style(VAR_MYNETWORKS),
- var_mynetworks);
-}
-
-/* psc_acl_parse - parse access list */
-
-ARGV *psc_acl_parse(const char *acl, const char *origin)
-{
- char *saved_checks = mystrdup(acl);
- ARGV *argv = argv_alloc(1);
- char *bp = saved_checks;
- char *name;
-
-#define STREQ(x,y) ((*x) == (*y) && strcasecmp((x), (y)) == 0)
-#define STRNE(x,y) ((*x) != (*y) || strcasecmp((x), (y)) != 0)
-
- /*
- * Nested tables are not allowed. Tables are opened before entering the
- * chroot jail, while access lists are evaluated after entering the
- * chroot jail.
- */
- while ((name = mystrtok(&bp, PSC_ACL_SEPARATORS)) != 0) {
- if (strchr(name, ':') != 0) {
- if (STRNE(origin, VAR_PSC_ACL)) {
- msg_warn("table %s: lookup result \"%s\" is not allowed"
- " -- ignoring remainder of access list",
- origin, name);
- argv_add(argv, PSC_ACL_NAME_DUNNO, (char *) 0);
- break;
- } else {
- if (dict_handle(name) == 0)
- dict_register(name, dict_open(name, O_RDONLY, DICT_FLAG_LOCK
- | DICT_FLAG_FOLD_FIX));
- }
- }
- argv_add(argv, name, (char *) 0);
- }
- argv_terminate(argv);
-
- /*
- * Cleanup.
- */
- myfree(saved_checks);
- return (argv);
-}
-
-/* psc_acl_eval - evaluate access list */
-
-int psc_acl_eval(PSC_STATE *state, ARGV *acl, const char *origin)
-{
- const char *myname = "psc_acl_eval";
- char **cpp;
- DICT *dict;
- ARGV *argv;
- const char *name;
- const char *dict_val;
- int ret;
-
- for (cpp = acl->argv; (name = *cpp) != 0; cpp++) {
- if (msg_verbose)
- msg_info("source=%s address=%s acl=%s",
- origin, state->smtp_client_addr, name);
- if (STREQ(name, PSC_ACL_NAME_BLACKLIST)) {
- return (PSC_ACL_ACT_BLACKLIST);
- } else if (STREQ(name, PSC_ACL_NAME_WHITELIST)) {
- return (PSC_ACL_ACT_WHITELIST);
- } else if (STREQ(name, PSC_ACL_NAME_WL_MYNETWORKS)) {
- if (addr_match_list_match(psc_mynetworks, state->smtp_client_addr))
- return (PSC_ACL_ACT_WHITELIST);
- } else if (strchr(name, ':') != 0) {
- if ((dict = dict_handle(name)) == 0)
- msg_panic("%s: unexpected dictionary: %s", myname, name);
- if ((dict_val = dict_get(dict, state->smtp_client_addr)) != 0) {
- argv = psc_acl_parse(dict_val, name);
- ret = psc_acl_eval(state, argv, name);
- argv_free(argv);
- if (ret != PSC_ACL_ACT_DUNNO)
- return (ret);
- } else if (dict_errno != 0) {
- msg_warn("%s: table lookup error -- ignoring the remainder "
- "of this access list", name);
- return (PSC_ACL_ACT_ERROR);
- }
- } else if (STREQ(name, PSC_ACL_NAME_DUNNO)) {
- return (PSC_ACL_ACT_DUNNO);
- } else {
- msg_warn("%s: unknown command: %s -- ignoring the remainder "
- "of this access list", origin, name);
- return (PSC_ACL_ACT_ERROR);
- }
- }
- if (msg_verbose)
- msg_info("source=%s address=%s - no match",
- origin, state->smtp_client_addr);
- return (PSC_ACL_ACT_DUNNO);
-}
-
- /*
- * Access lists need testing. Not only with good inputs; error cases must
- * also be handled appropriately.
- */
-#ifdef TEST
-#include <unistd.h>
-#include <stdlib.h>
-#include <vstring_vstream.h>
-#include <name_code.h>
-#include <split_at.h>
-
-char *var_par_dom_match = DEF_PAR_DOM_MATCH;
-char *var_mynetworks = "";
-char *var_psc_acl = "";
-
-#define UPDATE_VAR(s,v) do { if (*(s)) myfree(s); (s) = mystrdup(v); } while (0)
-
-int main(void)
-{
- VSTRING *buf = vstring_alloc(100);
- PSC_STATE state;
- ARGV *argv;
- int ret;
- int have_tty = isatty(0);
- char *bufp;
- char *cmd;
- char *value;
- const NAME_CODE acl_map[] = {
- PSC_ACL_NAME_ERROR, PSC_ACL_ACT_ERROR,
- PSC_ACL_NAME_WHITELIST, PSC_ACL_ACT_WHITELIST,
- PSC_ACL_NAME_BLACKLIST, PSC_ACL_ACT_BLACKLIST,
- PSC_ACL_NAME_DUNNO, PSC_ACL_ACT_DUNNO,
- 0,
- };
-
- while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
- bufp = STR(buf);
- if (have_tty == 0) {
- vstream_printf("> %s\n", bufp);
- vstream_fflush(VSTREAM_OUT);
- }
- if (*bufp == '#')
- continue;
- if ((cmd = mystrtok(&bufp, " =")) == 0 || STREQ(cmd, "?")) {
- vstream_printf("usage: %s=value|%s=value|address=value\n",
- VAR_MYNETWORKS, VAR_PSC_ACL);
- } else if ((value = mystrtok(&bufp, " =")) == 0) {
- vstream_printf("missing value\n");
- } else if (STREQ(cmd, VAR_MYNETWORKS)) {
- UPDATE_VAR(var_mynetworks, value);
- } else if (STREQ(cmd, VAR_PSC_ACL)) {
- UPDATE_VAR(var_psc_acl, value);
- } else if (STREQ(cmd, "address")) {
- psc_acl_pre_jail_init();
- argv = psc_acl_parse(var_psc_acl, VAR_PSC_ACL);
- state.smtp_client_addr = value;
- ret = psc_acl_eval(&state, argv, VAR_PSC_ACL);
- argv_free(argv);
- vstream_printf("%s: %s\n", value, str_name_code(acl_map, ret));
- } else {
- vstream_printf("unknown command: \"%s\"\n", cmd);
- }
- vstream_fflush(VSTREAM_OUT);
- }
- vstring_free(buf);
- exit(0);
-}
-
-#endif
char *var_rcpt_canon_maps;
char *var_relocated_maps;
char *var_transport_maps;
+char *var_verify_map;
+char *var_psc_cache_map;
char *var_proxy_read_maps;
char *var_proxy_write_maps;
* of open() flags should be received directly from the client.
*/
vstring_sprintf(map_type_name_flags, "%s:%s", map_type_name,
- dict_flags_str(request_flags & DICT_FLAG_NP_INST_MASK));
+ dict_flags_str(request_flags & DICT_FLAG_INST_MASK));
+ if (msg_verbose)
+ msg_info("proxy_map_find: %s", STR(map_type_name_flags));
if ((dict = dict_handle(STR(map_type_name_flags))) == 0)
dict = dict_open(map_type_name, proxy_writer ?
WRITE_OPEN_FLAGS : READ_OPEN_FLAGS,
if (argv[0])
msg_fatal("unexpected command-line argument: %s", argv[0]);
+ /*
+ * Deadline enforcement.
+ */
+ if (vstream_fstat(client_stream, VSTREAM_FLAG_DEADLINE) == 0)
+ vstream_control(client_stream,
+ VSTREAM_CTL_TIMEOUT, 1,
+ VSTREAM_CTL_END);
+
/*
* This routine runs whenever a client connects to the socket dedicated
* to the proxymap service. All connection-management stuff is handled by
* the common code in multi_server.c.
*/
+ vstream_control(client_stream,
+ VSTREAM_CTL_START_DEADLINE,
+ VSTREAM_CTL_END);
if (attr_scan(client_stream,
ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
ATTR_TYPE_STR, MAIL_ATTR_REQ, request,
ATTR_TYPE_END);
}
}
+ vstream_control(client_stream,
+ VSTREAM_CTL_START_DEADLINE,
+ VSTREAM_CTL_END);
vstream_fflush(client_stream);
}
VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
VAR_PROXY_READ_MAPS, DEF_PROXY_READ_MAPS, &var_proxy_read_maps, 0, 0,
VAR_PROXY_WRITE_MAPS, DEF_PROXY_WRITE_MAPS, &var_proxy_write_maps, 0, 0,
+ VAR_VERIFY_MAP, DEF_VERIFY_MAP, &var_verify_map, 0, 0,
+ VAR_PSC_CACHE_MAP, DEF_PSC_CACHE_MAP, &var_psc_cache_map, 0, 0,
0,
};
* Mapping between flag names and flag values.
*/
static const NAME_MASK dict_mask[] = {
- "warn_dup", (1 << 0), /* if file, warn about dups */
- "ignore_dup", (1 << 1), /* if file, ignore dups */
- "try0null", (1 << 2), /* do not append 0 to key/value */
- "try1null", (1 << 3), /* append 0 to key/value */
- "fixed", (1 << 4), /* fixed key map */
- "pattern", (1 << 5), /* keys are patterns */
- "lock", (1 << 6), /* lock before access */
- "replace", (1 << 7), /* if file, replace dups */
- "sync_update", (1 << 8), /* if file, sync updates */
- "debug", (1 << 9), /* log access */
- "no_regsub", (1 << 11), /* disallow regexp substitution */
- "no_proxy", (1 << 12), /* disallow proxy mapping */
- "no_unauth", (1 << 13), /* disallow unauthenticated data */
- "fold_fix", (1 << 14), /* case-fold with fixed-case key map */
- "fold_mul", (1 << 15), /* case-fold with multi-case key map */
+ "warn_dup", DICT_FLAG_DUP_WARN, /* if file, warn about dups */
+ "ignore_dup", DICT_FLAG_DUP_IGNORE, /* if file, ignore dups */
+ "try0null", DICT_FLAG_TRY0NULL, /* do not append 0 to key/value */
+ "try1null", DICT_FLAG_TRY1NULL, /* append 0 to key/value */
+ "fixed", DICT_FLAG_FIXED, /* fixed key map */
+ "pattern", DICT_FLAG_PATTERN, /* keys are patterns */
+ "lock", DICT_FLAG_LOCK, /* lock before access */
+ "replace", DICT_FLAG_DUP_REPLACE, /* if file, replace dups */
+ "sync_update", DICT_FLAG_SYNC_UPDATE, /* if file, sync updates */
+ "debug", DICT_FLAG_DEBUG, /* log access */
+ "no_regsub", DICT_FLAG_NO_REGSUB, /* disallow regexp substitution */
+ "no_proxy", DICT_FLAG_NO_PROXY, /* disallow proxy mapping */
+ "no_unauth", DICT_FLAG_NO_UNAUTH, /* disallow unauthenticated data */
+ "fold_fix", DICT_FLAG_FOLD_FIX, /* case-fold with fixed-case key map */
+ "fold_mul", DICT_FLAG_FOLD_MUL, /* case-fold with multi-case key map */
+ "open_lock", DICT_FLAG_OPEN_LOCK, /* permanent lock upon open */
+ 0,
};
/* dict_flags_str - convert mask to string for debugging purposes */
buf = vstring_alloc(1);
return (str_name_mask_opt(buf, "dictionary flags", dict_mask, dict_flags,
- NAME_MASK_RETURN | NAME_MASK_PIPE));
+ NAME_MASK_NUMBER | NAME_MASK_PIPE));
}
* The subsets of flags that control how a map is used. These are relevant
* mainly for proxymap support. Note: some categories overlap.
*
- * DICT_FLAG_PARANOID - flags that forbid the use of insecure map types for
- * security-sensitive operations. These flags are specified by the caller,
- * and are checked by the map implementation itself upon open, lookup etc.
- * requests.
+ * DICT_FLAG_IMPL_MASK - flags that are set by the map implementation itself.
*
- * DICT_FLAG_IMPL_MASK - flags that specify properties of the lookup table
- * implementation. These flags are set by the map implementation itself.
+ * DICT_FLAG_PARANOID - requestor flags that forbid the use of insecure map
+ * types for security-sensitive operations. These flags are checked by the
+ * map implementation itself upon open, lookup etc. requests.
*
- * DICT_FLAG_INST_MASK - flags that control how a specific table instance is
- * opened or used. The caller specifies these flags, and the caller may not
- * change them between open, lookup, etc. requests (although the map itself
- * may make changes to some of these flags).
+ * DICT_FLAG_RQST_MASK - all requestor flags, including paranoid flags, that
+ * the requestor may change between open, lookup etc. requests.
*
- * DICT_FLAG_NP_INST_MASK - ditto, but without the paranoia flags.
- *
- * DICT_FLAG_RQST_MASK - flags that the caller specifies, and that the caller
- * may change between open, lookup etc. requests.
+ * DICT_FLAG_INST_MASK - none of the above flags. The requestor may not change
+ * these flags between open, lookup, etc. requests (although a map may make
+ * changes to its copy of some of these flags). The proxymap server opens
+ * only one map instance for all client requests with the same values of
+ * these flags, and the proxymap client uses its own saved copy of these
+ * flags.
*/
#define DICT_FLAG_PARANOID \
(DICT_FLAG_NO_REGSUB | DICT_FLAG_NO_PROXY | DICT_FLAG_NO_UNAUTH)
#define DICT_FLAG_IMPL_MASK (DICT_FLAG_FIXED | DICT_FLAG_PATTERN)
#define DICT_FLAG_RQST_MASK (DICT_FLAG_FOLD_ANY | DICT_FLAG_LOCK | \
DICT_FLAG_DUP_REPLACE | DICT_FLAG_DUP_WARN | \
- DICT_FLAG_SYNC_UPDATE)
-#define DICT_FLAG_NP_INST_MASK ~(DICT_FLAG_IMPL_MASK | DICT_FLAG_RQST_MASK)
-#define DICT_FLAG_INST_MASK (DICT_FLAG_NP_INST_MASK | DICT_FLAG_PARANOID)
+ DICT_FLAG_DUP_IGNORE | DICT_FLAG_SYNC_UPDATE | \
+ DICT_FLAG_PARANOID)
+#define DICT_FLAG_INST_MASK ~(DICT_FLAG_IMPL_MASK | DICT_FLAG_RQST_MASK)
extern int dict_unknown_allowed;
extern int dict_errno;
* underlying database.
*/
struct DICT_CACHE {
+ char *name; /* full name including proxy: */
int cache_flags; /* see below */
int user_flags; /* logging */
DICT *db; /* database handle */
myname, previous_curr_key, previous_curr_val);
if (dict_del(cp->db, previous_curr_key) != 0)
msg_warn("database %s: could not delete entry for %s",
- cp->db->name, previous_curr_key);
+ cp->name, previous_curr_key);
}
/*
{
if (cp->user_flags & DICT_CACHE_FLAG_STATISTICS)
msg_info("cache %s %s cleanup: retained=%d dropped=%d entries",
- cp->db->name, full_partial, cp->retained, cp->dropped);
+ cp->name, full_partial, cp->retained, cp->dropped);
cp->retained = cp->dropped = 0;
}
cp->retained = cp->dropped = 0;
first_next = DICT_SEQ_FUN_FIRST;
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
- msg_info("%s: start %s cache cleanup", myname, cp->db->name);
+ msg_info("%s: start %s cache cleanup", myname, cp->name);
}
/*
cp->dropped++;
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: drop %s cache entry for %s",
- myname, cp->db->name, cache_key);
+ myname, cp->name, cache_key);
} else {
cp->retained++;
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
msg_info("%s: keep %s cache entry for %s",
- myname, cp->db->name, cache_key);
+ myname, cp->name, cache_key);
}
next_interval = 0;
}
*/
else {
if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE)
- msg_info("%s: done %s cache cleanup scan", myname, cp->db->name);
+ msg_info("%s: done %s cache cleanup scan", myname, cp->name);
dict_cache_clean_stat_log_reset(cp, "full");
stamp_buf = vstring_alloc(100);
vstring_sprintf(stamp_buf, "%ld", (long) event_time());
cp->exp_interval = va_arg(ap, int);
if (cp->exp_interval < 0)
msg_panic("%s: bad %s cache cleanup interval %d",
- myname, cp->db->name, cp->exp_interval);
+ myname, cp->name, cp->exp_interval);
break;
case DICT_CACHE_CTL_VALIDATOR:
cp->exp_validator = va_arg(ap, DICT_CACHE_VALIDATOR_FN);
*/
if (cache_cleanup_is_active)
msg_panic("%s: %s cache cleanup is already scheduled",
- myname, cp->db->name);
+ myname, cp->name);
/*
* The next start time depends on the last completion time.
next_interval = cp->exp_interval;
if ((cp->user_flags & DICT_CACHE_FLAG_VERBOSE) && next_interval > 0)
msg_info("%s cache cleanup will start after %ds",
- cp->db->name, (int) next_interval);
+ cp->name, (int) next_interval);
event_request_timer(dict_cache_clean_event, (char *) cp,
(int) next_interval);
}
* Create the DICT_CACHE object.
*/
cp = (DICT_CACHE *) mymalloc(sizeof(*cp));
+ cp->name = mystrdup(dbname);
cp->cache_flags = 0;
cp->user_flags = 0;
cp->db = dict;
/*
* Destroy the DICT_CACHE object.
*/
+ myfree(cp->name);
dict_cache_control(cp, DICT_CACHE_CTL_INTERVAL, 0, DICT_CACHE_CTL_END);
dict_close(cp->db);
if (cp->saved_curr_key)
* execute still presents overhead for the processor pipeline, processor
* cache, etc).
*/
- return (cp->db->name);
+ return (cp->name);
}
if (*bufp == '#')
continue;
if ((cmd = mystrtok(&bufp, " ")) == 0) {
- vstream_printf("usage: verbose|del key|get key|put key=value|first|next\n");
+ vstream_printf("usage: verbose|del key|get key|put key=value|first|next|masks|flags\n");
vstream_fflush(VSTREAM_OUT);
continue;
}
vstream_printf("%s\n",
dict_errno == DICT_ERR_RETRY ?
"soft error" : "not found");
+ } else if (strcmp(cmd, "flags") == 0 && !key && !value) {
+ vstream_printf("dict flags %s\n",
+ dict_flags_str(dict->flags));
+ } else if (strcmp(cmd, "masks") == 0 && !key && !value) {
+ vstream_printf("DICT_FLAG_IMPL_MASK %s\n",
+ dict_flags_str(DICT_FLAG_IMPL_MASK));
+ vstream_printf("DICT_FLAG_PARANOID %s\n",
+ dict_flags_str(DICT_FLAG_PARANOID));
+ vstream_printf("DICT_FLAG_RQST_MASK %s\n",
+ dict_flags_str(DICT_FLAG_RQST_MASK));
+ vstream_printf("DICT_FLAG_INST_MASK %s\n",
+ dict_flags_str(DICT_FLAG_INST_MASK));
} else {
- vstream_printf("usage: del key|get key|put key=value|first|next\n");
+ vstream_printf("usage: del key|get key|put key=value|first|next|masks|flags\n");
}
vstream_fflush(VSTREAM_OUT);
}