michael@676: diff -urN README_FILES/SRS_README.orig README_FILES/SRS_README michael@676: --- README_FILES/SRS_README.orig 1970-01-01 01:00:00.000000000 +0100 michael@676: +++ README_FILES/SRS_README 2012-02-21 14:22:05.000000000 +0100 michael@676: @@ -0,0 +1,56 @@ michael@676: + michael@676: +CREDITS: michael@676: + michael@676: +- originally written by Shevek michael@676: +- fixes and porting to postfix 2.1 by Branko F. Gracnar michael@676: +- redesigned and ported to debian postfix 2.7.0 by Chris michael@676: + michael@676: +SYNOPSIS: michael@676: +Works with all address classes (not limited to local domain). michael@676: +Doesn't depend on .forward-files or alias_maps. michael@676: +SMTPD accepts valid srs addresses without further configuration. michael@676: +Sender gets rewritten only when necessary: michael@676: +- rewrite after queuing (after separation to on- and off-site destinations) michael@676: +- rewrite only if sender and recipient are off-site and host is not a relay for recipient michael@676: +Alias domain can be chosen automatically from original on-site-recipient. michael@676: + michael@676: + michael@676: +USAGE: michael@676: + michael@676: +1) Apply the patch. michael@676: + michael@676: +2) Create makefiles with michael@676: + michael@676: +make makefiles CCARGS="-DHAS_SRS -I" AUXLIBS="-L -lsrs2" michael@676: + michael@676: + michael@676: +3) Build. michael@676: + michael@676: +4) Add the following global configuration variables to main.cf: michael@676: +All variables are optional, except where stated: michael@676: + michael@676: + srs_domain (string) michael@676: + A domain to use in rewritten addresses. This must point only michael@676: + to machines which know the encoding secret used by this michael@676: + system. It defaults to $myorigin. If set to the emtpy michael@676: + string, use domain from original recipient if host is final michael@676: + destination, otherwise use $myorigin. michael@676: + srs_hashlength (int) michael@676: + The hash length to generate in a rewritten address. michael@676: + srs_hashmin (int) michael@676: + The hash length to require when checking an address. michael@676: + srs_maxage (int) michael@676: + The maximum permitted age of a rewritten address. michael@676: + srs_secrets (string) michael@676: + [required] A list of secrets with which to generate michael@676: + and check addresses. michael@676: + srs_separator (string) michael@676: + The separator to appear immediately after SRS[01] in michael@676: + rewritten addresses. michael@676: + michael@676: + michael@676: +See http://www.libsrs2.org/ for more information about the meaning michael@676: +of these variables. They are standard across MTAs. michael@676: + michael@676: +5) Fire up and enjoy. michael@676: + michael@676: diff -urN src/cleanup/cleanup_addr.c.orig src/cleanup/cleanup_addr.c michael@676: --- src/cleanup/cleanup_addr.c.orig 2012-02-21 14:21:34.000000000 +0100 michael@676: +++ src/cleanup/cleanup_addr.c 2012-02-21 14:22:05.000000000 +0100 michael@676: @@ -163,6 +163,10 @@ michael@676: { michael@676: VSTRING *clean_addr = vstring_alloc(100); michael@676: const char *bcc; michael@676: +#ifdef HAS_SRS michael@676: + int srs_state; michael@676: + VSTRING *srs_rcpt; michael@676: +#endif michael@676: michael@676: /* michael@676: * Note: an unqualified envelope address is for all practical purposes michael@676: @@ -174,6 +178,14 @@ michael@676: cleanup_rewrite_internal(MAIL_ATTR_RWR_LOCAL, michael@676: clean_addr, *buf ? buf : var_empty_addr); michael@676: if (state->flags & CLEANUP_FLAG_MAP_OK) { michael@676: +#ifdef HAS_SRS michael@676: + if ((srs_state = postfix_srs_reverse(STR(clean_addr), &srs_rcpt)) < 0) { michael@676: + /* XXX errors */ michael@676: + } else if (srs_state == 1) { michael@676: + vstring_free(clean_addr); michael@676: + clean_addr = srs_rcpt; michael@676: + } michael@676: +#endif michael@676: if (cleanup_rcpt_canon_maps michael@676: && (cleanup_rcpt_canon_flags & CLEANUP_CANON_FLAG_ENV_RCPT)) michael@676: cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps, michael@676: diff -urN src/global/mail_params.c.orig src/global/mail_params.c michael@676: --- src/global/mail_params.c.orig 2012-02-21 14:21:34.000000000 +0100 michael@676: +++ src/global/mail_params.c 2012-02-21 14:22:05.000000000 +0100 michael@676: @@ -308,6 +308,15 @@ michael@676: bool var_long_queue_ids; michael@676: bool var_daemon_open_fatal; michael@676: michael@676: +#ifdef HAS_SRS michael@676: +int var_srs_hashlength; michael@676: +int var_srs_hashmin; michael@676: +int var_srs_maxage; michael@676: +char *var_srs_secrets; michael@676: +char *var_srs_separator; michael@676: +char *var_srs_domain; michael@676: +#endif michael@676: + michael@676: const char null_format_string[1] = ""; michael@676: michael@676: /* check_myhostname - lookup hostname and validate */ michael@676: @@ -579,6 +588,11 @@ michael@676: VAR_INT_FILT_CLASSES, DEF_INT_FILT_CLASSES, &var_int_filt_classes, 0, 0, michael@676: /* multi_instance_wrapper may have dependencies but not dependents. */ michael@676: VAR_MULTI_WRAPPER, DEF_MULTI_WRAPPER, &var_multi_wrapper, 0, 0, michael@676: +#ifdef HAS_SRS michael@676: + VAR_SRS_SECRETS, DEF_SRS_SECRETS, &var_srs_secrets, 0, 0, michael@676: + VAR_SRS_SEPARATOR, DEF_SRS_SEPARATOR, &var_srs_separator, 1, 1, michael@676: + VAR_SRS_DOMAIN, DEF_SRS_DOMAIN, &var_srs_domain, 0, 0, michael@676: +#endif michael@676: 0, michael@676: }; michael@676: static const CONFIG_STR_FN_TABLE function_str_defaults_2[] = { michael@676: @@ -602,6 +616,11 @@ michael@676: VAR_MIME_BOUND_LEN, DEF_MIME_BOUND_LEN, &var_mime_bound_len, 1, 0, michael@676: VAR_DELAY_MAX_RES, DEF_DELAY_MAX_RES, &var_delay_max_res, MIN_DELAY_MAX_RES, MAX_DELAY_MAX_RES, michael@676: VAR_INET_WINDOW, DEF_INET_WINDOW, &var_inet_windowsize, 0, 0, michael@676: +#ifdef HAS_SRS michael@676: + VAR_SRS_HASHLENGTH, DEF_SRS_HASHLENGTH, &var_srs_hashlength, 1, 20, michael@676: + VAR_SRS_HASHMIN, DEF_SRS_HASHMIN, &var_srs_hashmin, 1, 20, michael@676: + VAR_SRS_MAXAGE, DEF_SRS_MAXAGE, &var_srs_maxage, 1, 0, michael@676: +#endif michael@676: 0, michael@676: }; michael@676: static const CONFIG_LONG_TABLE long_defaults[] = { michael@676: diff -urN src/global/mail_params.h.orig src/global/mail_params.h michael@676: --- src/global/mail_params.h.orig 2012-02-21 14:21:34.000000000 +0100 michael@676: +++ src/global/mail_params.h 2012-02-21 14:22:41.000000000 +0100 michael@676: @@ -1981,6 +1981,35 @@ michael@676: #define DEF_ALLOW_UNTRUST_ROUTE 0 michael@676: extern bool var_allow_untrust_route; michael@676: michael@676: +/** michael@676: + * SRS. Shevek , updated by Chris michael@676: + */ michael@676: +#ifdef HAS_SRS michael@676: +#define VAR_SRS_HASHLENGTH "srs_hashlength" michael@676: +#define DEF_SRS_HASHLENGTH 4 michael@676: +extern int var_srs_hashlength; michael@676: + michael@676: +#define VAR_SRS_HASHMIN "srs_hashmin" michael@676: +#define DEF_SRS_HASHMIN 4 michael@676: +extern int var_srs_hashmin; michael@676: + michael@676: +#define VAR_SRS_MAXAGE "srs_maxage" michael@676: +#define DEF_SRS_MAXAGE 21 michael@676: +extern int var_srs_maxage; michael@676: + michael@676: +#define VAR_SRS_SECRETS "srs_secrets" michael@676: +#define DEF_SRS_SECRETS "" michael@676: +extern char *var_srs_secrets; michael@676: + michael@676: +#define VAR_SRS_DOMAIN "srs_domain" michael@676: +#define DEF_SRS_DOMAIN "$myorigin" michael@676: +extern char *var_srs_domain; michael@676: + michael@676: +#define VAR_SRS_SEPARATOR "srs_separator" michael@676: +#define DEF_SRS_SEPARATOR "=" michael@676: +extern char *var_srs_separator; michael@676: +#endif michael@676: + michael@676: /* michael@676: * Names of specific restrictions, and the corresponding configuration michael@676: * parameters that control the status codes sent in response to rejected michael@676: diff -urN src/global/Makefile.in.orig src/global/Makefile.in michael@676: --- src/global/Makefile.in.orig 2012-02-21 14:21:34.000000000 +0100 michael@676: +++ src/global/Makefile.in 2012-02-21 14:22:05.000000000 +0100 michael@676: @@ -29,7 +29,7 @@ michael@676: user_acl.c valid_mailhost_addr.c verify.c verify_clnt.c \ michael@676: verp_sender.c wildcard_inet_addr.c xtext.c delivered_hdr.c \ michael@676: fold_addr.c header_body_checks.c mkmap_proxy.c data_redirect.c \ michael@676: - match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \ michael@676: + match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c srs.c \ michael@676: smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \ michael@676: dict_memcache.c mail_version.c memcache_proto.c server_acl.c \ michael@676: mkmap_fail.c michael@676: @@ -63,7 +63,7 @@ michael@676: user_acl.o valid_mailhost_addr.o verify.o verify_clnt.o \ michael@676: verp_sender.o wildcard_inet_addr.o xtext.o delivered_hdr.o \ michael@676: fold_addr.o header_body_checks.o mkmap_proxy.o data_redirect.o \ michael@676: - match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \ michael@676: + match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o srs.o \ michael@676: smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \ michael@676: dict_memcache.o mail_version.o memcache_proto.o server_acl.o \ michael@676: mkmap_fail.o michael@676: diff -urN src/global/srs.c.orig src/global/srs.c michael@676: --- src/global/srs.c.orig 1970-01-01 01:00:00.000000000 +0100 michael@676: +++ src/global/srs.c 2012-02-21 14:22:27.000000000 +0100 michael@676: @@ -0,0 +1,144 @@ michael@676: +/*++ michael@676: +/* NAME michael@676: +/* srs 3 michael@676: +/* SUMMARY michael@676: +/* Sender Rewriting Scheme michael@676: +/* SYNOPSIS michael@676: +/* void postfix_srs_reverse(rcpt, decoded) michael@676: +/* const char *rcpt michael@676: +/* VSTRING **decoded michael@676: +/* michael@676: +/* int postfix_srs_forward(sender, alias, encoded) michael@676: +/* const char *sender michael@676: +/* const char *alias michael@676: +/* VSTRING **decoded michael@676: +/* DESCRIPTION michael@676: +/* This module rewrites addresses according to the Sender Rewriting michael@676: +/* Scheme, documented at http://www.libsrs2.org/. michael@676: +/* michael@676: +/* srs_reverse() reverse-rewrites the rewritten form of an address michael@676: +/* michael@676: +/* srs_forward() rewrites the envelope sender using alias domain michael@676: +/* michael@676: +/* DIAGNOSTICS michael@676: +/* LICENSE michael@676: +/* .ad michael@676: +/* .fi michael@676: +/* The Secure Mailer license must be distributed with this software. michael@676: +/* AUTHOR(S) michael@676: +/* Shevek michael@676: +/* Chris michael@676: +/*--*/ michael@676: + michael@676: +#ifdef HAS_SRS michael@676: + michael@676: +/* System library. */ michael@676: + michael@676: +#include michael@676: +#include michael@676: +#include michael@676: + michael@676: +#include michael@676: + michael@676: +/* Utility library. */ michael@676: + michael@676: +#include michael@676: +#include michael@676: +#include michael@676: +#include michael@676: + michael@676: +/* Global library. */ michael@676: + michael@676: +#include michael@676: + michael@676: +#define SRS_FAIL_UNLESS(x) do { int __ret = (x); if (__ret != SRS_SUCCESS) msg_fatal("SRS configuration error: %s", srs_strerror(__ret)); } while(0) michael@676: + michael@676: +/* my function declarations */ michael@676: +int postfix_srs_forward (const char *sender, const char *alias, VSTRING **encoded); michael@676: +int postfix_srs_reverse (const char *rcpt, VSTRING **decoded); michael@676: + michael@676: +static srs_t *srs; michael@676: + michael@676: +static void postfix_srs_init (void) { michael@676: + char *saved_secrets; michael@676: + char *secret; michael@676: + char *ptr; michael@676: + michael@676: + if (srs == NULL) { michael@676: + /* XXX Can't use memory functions from postfix because of inconsistensies in libsrs2 michael@676: + srs_set_malloc((srs_malloc_t)mymalloc, (srs_realloc_t)myrealloc, (srs_free_t)myfree); michael@676: + */ michael@676: + michael@676: + srs = srs_new(); michael@676: + /* avoid sender-domain-check, since we have already done this ourselves */ michael@676: + SRS_FAIL_UNLESS(srs_set_alwaysrewrite(srs, 1)); michael@676: + SRS_FAIL_UNLESS(srs_set_hashlength(srs, var_srs_hashlength)); michael@676: + SRS_FAIL_UNLESS(srs_set_hashmin(srs, var_srs_hashmin)); michael@676: + SRS_FAIL_UNLESS(srs_set_maxage(srs, var_srs_maxage)); michael@676: + SRS_FAIL_UNLESS(srs_set_separator(srs, var_srs_separator[0])); michael@676: + michael@676: + ptr = saved_secrets = mystrdup(var_srs_secrets); michael@676: + while ((secret = mystrtok(&ptr, ", \t\r\n")) != 0) { michael@676: + SRS_FAIL_UNLESS(srs_add_secret(srs, secret)); michael@676: + } michael@676: + myfree(saved_secrets); michael@676: + } michael@676: +} michael@676: + michael@676: +int postfix_srs_reverse(const char *rcpt, VSTRING **decoded) { michael@676: + char *out; michael@676: + int ret; michael@676: + michael@676: + if (!SRS_IS_SRS_ADDRESS(rcpt)) michael@676: + return (0); michael@676: + michael@676: + postfix_srs_init(); michael@676: + michael@676: + ret = srs_reverse_alloc(srs, &out, rcpt); michael@676: + michael@676: + if (ret != SRS_SUCCESS) { michael@676: + switch (SRS_ERROR_TYPE(ret)) { michael@676: + case SRS_ERRTYPE_CONFIG: michael@676: + msg_fatal("SRS configuration error: %s", srs_strerror(ret)); michael@676: + return (-1); michael@676: + case SRS_ERRTYPE_INPUT: michael@676: + msg_fatal("SRS reverse failed for <%s>: %s", rcpt, srs_strerror(ret)); michael@676: + return (-1); michael@676: + case SRS_ERRTYPE_SYNTAX: michael@676: + case SRS_ERRTYPE_SRS: michael@676: + msg_warn("SRS reverse failed for <%s>: %s", rcpt, srs_strerror(ret)); michael@676: + break; michael@676: + } michael@676: + return (0); michael@676: + } michael@676: + michael@676: + if (decoded) { michael@676: + *decoded = vstring_alloc(100); michael@676: + vstring_strcpy(*decoded, out); michael@676: + } michael@676: + free(out); michael@676: + michael@676: + return (1); michael@676: +} michael@676: + michael@676: +int postfix_srs_forward(const char *sender, const char *alias, VSTRING **encoded) { michael@676: + char *out; michael@676: + int ret; michael@676: + michael@676: + postfix_srs_init(); michael@676: + michael@676: + ret = srs_forward_alloc(srs, &out, sender, alias); michael@676: + michael@676: + if (ret != SRS_SUCCESS) { michael@676: + msg_warn("SRS forward failed for <%s>: %s", sender, srs_strerror(ret)); michael@676: + return (0); michael@676: + } michael@676: + michael@676: + *encoded = vstring_alloc(100); michael@676: + vstring_strcpy(*encoded, out); michael@676: + free(out); michael@676: + michael@676: + return (1); michael@676: +} michael@676: + michael@676: +#endif michael@676: diff -urN src/qmgr/qmgr_deliver.c.orig src/qmgr/qmgr_deliver.c michael@676: --- src/qmgr/qmgr_deliver.c.orig 2012-02-21 14:21:34.000000000 +0100 michael@676: +++ src/qmgr/qmgr_deliver.c 2012-02-21 14:22:05.000000000 +0100 michael@676: @@ -76,6 +76,7 @@ michael@676: #include michael@676: #include michael@676: #include michael@676: +#include michael@676: michael@676: /* Application-specific. */ michael@676: michael@676: @@ -129,6 +130,73 @@ michael@676: } michael@676: } michael@676: michael@676: +#ifdef HAS_SRS michael@676: +/* qmgr_deliver_check_srs - check whether sender should be rewritten */ michael@676: + michael@676: +static int qmgr_deliver_check_srs(QMGR_MESSAGE *message, RECIPIENT_LIST list, char **alias, RESOLVE_REPLY *reply) michael@676: +{ michael@676: + char *pos; michael@676: + michael@676: + /* michael@676: + * Rewrite if we are not final destination nor relay host for recipient and sender address michael@676: + * it should be enough to look at the first recipient, since transport/nexthop are the same for all recipients michael@676: + */ michael@676: + resolve_clnt_query(list.info->address, reply); michael@676: + if (reply->flags & RESOLVE_FLAG_FAIL) michael@676: + return (-1); michael@676: + else if (!(reply->flags & RESOLVE_CLASS_DEFAULT)) michael@676: + return (0); michael@676: + michael@676: + resolve_clnt_query(message->sender, reply); michael@676: + if (reply->flags & RESOLVE_FLAG_FAIL) michael@676: + return (-1); michael@676: + else if (!(reply->flags & RESOLVE_CLASS_DEFAULT)) michael@676: + return (0); michael@676: + michael@676: + if (var_srs_domain[0]) { michael@676: + *alias = var_srs_domain; michael@676: + } else { michael@676: + /* retrieve alias domain from original recipient if we are final destination ... */ michael@676: + resolve_clnt_query(list.info->orig_addr, reply); michael@676: + if (reply->flags & RESOLVE_FLAG_FAIL) michael@676: + return (-1); michael@676: + if ((reply->flags & RESOLVE_CLASS_FINAL) && (pos = strrchr(list.info->orig_addr, '@'))) { michael@676: + *alias = pos + 1; michael@676: + } else { michael@676: + /* ... or use myorigin as fallback */ michael@676: + *alias = var_myorigin; michael@676: + } michael@676: + } michael@676: + michael@676: + return (1); michael@676: +} michael@676: + michael@676: +/* qmgr_deliver_do_srs - compute srs address if necessary */ michael@676: + michael@676: +static int qmgr_deliver_do_srs(QMGR_MESSAGE *message, RECIPIENT_LIST list, VSTRING **encoded) michael@676: +{ michael@676: + RESOLVE_REPLY reply; michael@676: + char *alias; michael@676: + int state; michael@676: + michael@676: + resolve_clnt_init(&reply); michael@676: + state = qmgr_deliver_check_srs(message, list, &alias, &reply); michael@676: + resolve_clnt_free(&reply); michael@676: + if (state < 0) { michael@676: + msg_warn("srs lookup problem"); michael@676: + return (-1); michael@676: + } else if (state == 0) { michael@676: + return (0); michael@676: + } michael@676: + michael@676: + state = postfix_srs_forward(message->sender, alias, encoded); michael@676: + if (state < 0) michael@676: + return (-1); michael@676: + michael@676: + return (1); michael@676: +} michael@676: +#endif michael@676: + michael@676: /* qmgr_deliver_send_request - send delivery request to delivery process */ michael@676: michael@676: static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream) michael@676: @@ -140,6 +208,9 @@ michael@676: MSG_STATS stats; michael@676: char *sender; michael@676: int flags; michael@676: +#ifdef HAS_SRS michael@676: + int srs_state; michael@676: +#endif michael@676: michael@676: /* michael@676: * If variable envelope return path is requested, change prefix+@origin michael@676: @@ -147,7 +218,19 @@ michael@676: * recipient per delivery. michael@676: */ michael@676: if (message->verp_delims == 0) { michael@676: - sender = message->sender; michael@676: +#ifdef HAS_SRS michael@676: + srs_state = qmgr_deliver_do_srs(message, list, &sender_buf); michael@676: + if (srs_state < 0) { michael@676: + return (-1); michael@676: + } else if (srs_state == 1) { michael@676: + sender = vstring_str(sender_buf); michael@676: + msg_info("%s: srs_from=<%s>", message->queue_id, sender); michael@676: + } else { michael@676: + sender = message->sender; michael@676: + } michael@676: +#else michael@676: + sender = message->sender; michael@676: +#endif michael@676: } else { michael@676: sender_buf = vstring_alloc(100); michael@676: verp_sender(sender_buf, message->verp_delims, michael@676: diff -urN src/smtpd/smtpd_check.c.orig src/smtpd/smtpd_check.c michael@676: --- src/smtpd/smtpd_check.c.orig 2012-02-21 14:21:34.000000000 +0100 michael@676: +++ src/smtpd/smtpd_check.c 2012-02-21 14:22:05.000000000 +0100 michael@676: @@ -929,6 +929,19 @@ michael@676: reject_server_error(state); michael@676: } michael@676: michael@676: +#ifdef HAS_SRS michael@676: +/* check_mail_addr_srs - check if address is a valid srs address */ michael@676: + michael@676: +static int check_mail_addr_srs(SMTPD_STATE *state, const char *reply_name, const char *addr) michael@676: +{ michael@676: + int result; michael@676: + michael@676: + if ((result = postfix_srs_reverse(addr, 0)) < 0) michael@676: + reject_dict_retry(state, reply_name); michael@676: + return (result); michael@676: +} michael@676: +#endif michael@676: + michael@676: /* reject_unknown_reverse_name - fail if reverse client hostname is unknown */ michael@676: michael@676: static int reject_unknown_reverse_name(SMTPD_STATE *state) michael@676: @@ -4552,6 +4565,13 @@ michael@676: || MATCH(canonical_maps, CONST_STR(reply->recipient)) michael@676: || MATCH(virt_alias_maps, CONST_STR(reply->recipient))) michael@676: return (0); michael@676: + michael@676: +#ifdef HAS_SRS michael@676: + /* allow valid srs address */ michael@676: + if ((reply->flags & RESOLVE_CLASS_FINAL) michael@676: + && check_mail_addr_srs(state, recipient, CONST_STR(reply->recipient)) == 1) michael@676: + return (0); michael@676: +#endif michael@676: michael@676: /* michael@676: * At this point, anything that resolves to the error mailer is known to