int anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr, int *count, int *rate, int *msgs, int *rcpts, int *newtls, int *auths) { char *ident = ANVIL_IDENT(service, addr); int status; if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, ATTR_FLAG_NONE, /* Query attributes. */ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_LOOKUP), SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply attributes. */ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), RECV_ATTR_INT(ANVIL_ATTR_COUNT, count), RECV_ATTR_INT(ANVIL_ATTR_RATE, rate), RECV_ATTR_INT(ANVIL_ATTR_MAIL, msgs), RECV_ATTR_INT(ANVIL_ATTR_RCPT, rcpts), RECV_ATTR_INT(ANVIL_ATTR_NTLS, newtls), RECV_ATTR_INT(ANVIL_ATTR_AUTH, auths), ATTR_TYPE_END) != 7) status = ANVIL_STAT_FAIL; else if (status != ANVIL_STAT_OK) status = ANVIL_STAT_FAIL; myfree(ident); return (status); }
int main(int unused_argc, char **argv) { HTABLE *table = htable_create(1); msg_vstream_init(argv[0], VSTREAM_ERR); msg_verbose = 1; htable_enter(table, "foo-name", mystrdup("foo-value")); htable_enter(table, "bar-name", mystrdup("bar-value")); attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(ATTR_NAME_INT, 4711), SEND_ATTR_LONG(ATTR_NAME_LONG, 1234L), SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"), SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"), SEND_ATTR_HASH(table), SEND_ATTR_LONG(ATTR_NAME_LONG, 4321L), ATTR_TYPE_END); attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(ATTR_NAME_INT, 4711), SEND_ATTR_LONG(ATTR_NAME_LONG, 1234L), SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"), SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"), ATTR_TYPE_END); if (vstream_fflush(VSTREAM_OUT) != 0) msg_fatal("write error: %m"); htable_free(table, myfree); return (0); }
int tls_mgr_delete(const char *cache_type, const char *cache_id) { int status; /* * Create the tlsmgr client handle. */ if (tls_mgr == 0) tls_mgr_open(); /* * Send the request and receive the reply. */ if (attr_clnt_request(tls_mgr, ATTR_FLAG_NONE, /* Request */ SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_DELETE), SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id), ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply */ RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), ATTR_TYPE_END) != 1) status = TLS_MGR_STAT_FAIL; return (status); }
int tls_mgr_policy(const char *cache_type, int *cachable, int *timeout) { int status; /* * Create the tlsmgr client handle. */ if (tls_mgr == 0) tls_mgr_open(); /* * Request policy. */ if (attr_clnt_request(tls_mgr, ATTR_FLAG_NONE, /* Request attributes */ SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_POLICY), SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply attributes */ RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), RECV_ATTR_INT(TLS_MGR_ATTR_CACHABLE, cachable), RECV_ATTR_INT(TLS_MGR_ATTR_SESSTOUT, timeout), ATTR_TYPE_END) != 3) status = TLS_MGR_STAT_FAIL; return (status); }
int tls_mgr_lookup(const char *cache_type, const char *cache_id, VSTRING *buf) { int status; /* * Create the tlsmgr client handle. */ if (tls_mgr == 0) tls_mgr_open(); /* * Send the request and receive the reply. */ if (attr_clnt_request(tls_mgr, ATTR_FLAG_NONE, /* Request */ SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_LOOKUP), SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id), ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply */ RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), RECV_ATTR_DATA(TLS_MGR_ATTR_SESSION, buf), ATTR_TYPE_END) != 2) status = TLS_MGR_STAT_FAIL; return (status); }
int tls_mgr_seed(VSTRING *buf, int len) { int status; /* * Create the tlsmgr client handle. */ if (tls_mgr == 0) tls_mgr_open(); /* * Request seed. */ if (attr_clnt_request(tls_mgr, ATTR_FLAG_NONE, /* Request attributes */ SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_SEED), SEND_ATTR_INT(TLS_MGR_ATTR_SIZE, len), ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply attributes */ RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), RECV_ATTR_DATA(TLS_MGR_ATTR_SEED, buf), ATTR_TYPE_END) != 2) status = TLS_MGR_STAT_FAIL; return (status); }
int anvil_clnt_disconnect(ANVIL_CLNT *anvil_clnt, const char *service, const char *addr) { char *ident = ANVIL_IDENT(service, addr); int status; if (attr_clnt_request((ATTR_CLNT *) anvil_clnt, ATTR_FLAG_NONE, /* Query attributes. */ SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_DISC), SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident), ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply attributes. */ RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status), ATTR_TYPE_END) != 1) status = ANVIL_STAT_FAIL; else if (status != ANVIL_STAT_OK) status = ANVIL_STAT_FAIL; myfree(ident); return (status); }
int rewrite_proto(VSTREAM *stream) { RWR_CONTEXT *context; TOK822 *tree; if (attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_RULE, ruleset), RECV_ATTR_STR(MAIL_ATTR_ADDR, address), ATTR_TYPE_END) != 2) return (-1); if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_LOCAL) == 0) context = &local_context; else if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_REMOTE) == 0) context = &remote_context; else { msg_warn("unknown context: %s", vstring_str(ruleset)); return (-1); } /* * Sanity check. An address is supposed to be in externalized form. */ if (*vstring_str(address) == 0) { msg_warn("rewrite_addr: null address"); vstring_strcpy(result, vstring_str(address)); } /* * Convert the address from externalized (quoted) form to token list, * rewrite it, and convert back. */ else { tree = tok822_scan_addr(vstring_str(address)); rewrite_tree(context, tree); tok822_externalize(result, tree, TOK822_STR_DEFL); tok822_free_tree(tree); } if (msg_verbose) msg_info("`%s' `%s' -> `%s'", vstring_str(ruleset), vstring_str(address), vstring_str(result)); attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_FLAGS, server_flags), SEND_ATTR_STR(MAIL_ATTR_ADDR, vstring_str(result)), ATTR_TYPE_END); if (vstream_fflush(stream) != 0) { msg_warn("write rewrite reply: %m"); return (-1); } return (0); }
static TLS_TICKET_KEY *request_scache_key(unsigned char *keyname) { TLS_TICKET_KEY tmp; static VSTRING *keybuf; char *name; size_t len; int status; /* * Create the tlsmgr client handle. */ if (tls_mgr == 0) tls_mgr_open(); if (keybuf == 0) keybuf = vstring_alloc(sizeof(tmp)); /* In tlsmgr requests we encode null key names as empty strings. */ name = keyname ? (char *) keyname : ""; len = keyname ? TLS_TICKET_NAMELEN : 0; /* * Send the request and receive the reply. */ if (attr_clnt_request(tls_mgr, ATTR_FLAG_NONE, /* Request */ SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_TKTKEY), SEND_ATTR_DATA(TLS_MGR_ATTR_KEYNAME, len, name), ATTR_TYPE_END, ATTR_FLAG_MISSING, /* Reply */ RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), RECV_ATTR_DATA(TLS_MGR_ATTR_KEYBUF, keybuf), ATTR_TYPE_END) != 2 || status != TLS_MGR_STAT_OK || LEN(keybuf) != sizeof(tmp)) return (0); memcpy((void *) &tmp, STR(keybuf), sizeof(tmp)); return (tls_scache_key_rotate(&tmp)); }
int dsn_print(ATTR_PRINT_MASTER_FN print_fn, VSTREAM *fp, int flags, void *ptr) { DSN *dsn = (DSN *) ptr; int ret; /* * The attribute order is determined by backwards compatibility. It can * be sanitized after all the ad-hoc DSN read/write code is replaced. */ ret = print_fn(fp, flags | ATTR_FLAG_MORE, SEND_ATTR_STR(MAIL_ATTR_DSN_STATUS, dsn->status), SEND_ATTR_STR(MAIL_ATTR_DSN_DTYPE, dsn->dtype), SEND_ATTR_STR(MAIL_ATTR_DSN_DTEXT, dsn->dtext), SEND_ATTR_STR(MAIL_ATTR_DSN_MTYPE, dsn->mtype), SEND_ATTR_STR(MAIL_ATTR_DSN_MNAME, dsn->mname), SEND_ATTR_STR(MAIL_ATTR_DSN_ACTION, dsn->action), SEND_ATTR_STR(MAIL_ATTR_WHY, dsn->reason), ATTR_TYPE_END); return (ret); }
static void verify_query_service(VSTREAM *client_stream) { VSTRING *addr = vstring_alloc(10); VSTRING *get_buf = 0; VSTRING *put_buf = 0; const char *raw_data; int addr_status; long probed; long updated; char *text; if (attr_scan(client_stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_ADDR, addr), ATTR_TYPE_END) == 1) { long now = (long) time((time_t *) 0); /* * Produce a default record when no usable record exists. * * If negative caching is disabled, purge an expired record from the * database. * * XXX Assume that a probe is lost if no response is received in 1000 * seconds. If this number is too small the queue will slowly fill up * with delayed probes. * * XXX Maintain a moving average for the probe turnaround time, and * allow probe "retransmission" when a probe is outstanding for, say * some minimal amount of time (1000 sec) plus several times the * observed probe turnaround time. This causes probing to back off * when the mail system becomes congested. */ #define POSITIVE_ENTRY_EXPIRED(addr_status, updated) \ (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_exp < now) #define NEGATIVE_ENTRY_EXPIRED(addr_status, updated) \ (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_exp < now) #define PROBE_TTL 1000 /* FIX 200501 IPv6 patch did not neuter ":" in address literals. */ translit(STR(addr), ":", "_"); if ((raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0 /* not found */ || ((get_buf = vstring_alloc(10)), vstring_strcpy(get_buf, raw_data), /* malformed */ verify_parse_entry(STR(get_buf), &addr_status, &probed, &updated, &text) < 0) || (now - probed > PROBE_TTL /* safe to probe */ && (POSITIVE_ENTRY_EXPIRED(addr_status, updated) || NEGATIVE_ENTRY_EXPIRED(addr_status, updated)))) { addr_status = DEL_RCPT_STAT_TODO; probed = 0; updated = 0; text = "Address verification in progress"; if (raw_data != 0 && var_verify_neg_cache == 0) dict_cache_delete(verify_map, STR(addr)); } if (msg_verbose) msg_info("GOT %s status=%d probed=%ld updated=%ld text=%s", STR(addr), addr_status, probed, updated, text); /* * Respond to the client. */ attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_OK), SEND_ATTR_INT(MAIL_ATTR_ADDR_STATUS, addr_status), SEND_ATTR_STR(MAIL_ATTR_WHY, text), ATTR_TYPE_END); /* * Send a new probe when the information needs to be refreshed. * * XXX For an initial proof of concept implementation, use synchronous * mail submission. This needs to be made async for high-volume * sites, which makes it even more interesting to eliminate duplicate * queries while a probe is being built. * * If negative caching is turned off, update the database only when * refreshing an existing entry. */ #define POSITIVE_REFRESH_NEEDED(addr_status, updated) \ (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_try < now) #define NEGATIVE_REFRESH_NEEDED(addr_status, updated) \ (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_try < now) if (now - probed > PROBE_TTL && (POSITIVE_REFRESH_NEEDED(addr_status, updated) || NEGATIVE_REFRESH_NEEDED(addr_status, updated))) { if (msg_verbose) msg_info("PROBE %s status=%d probed=%ld updated=%ld", STR(addr), addr_status, now, updated); post_mail_fopen_async(make_verify_sender_addr(), STR(addr), MAIL_SRC_MASK_VERIFY, DEL_REQ_FLAG_MTA_VRFY, SMTPUTF8_FLAG_NONE, (VSTRING *) 0, verify_post_mail_action, (void *) 0); if (updated != 0 || var_verify_neg_cache != 0) { put_buf = vstring_alloc(10); verify_make_entry(put_buf, addr_status, now, updated, text); if (msg_verbose) msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s", STR(addr), addr_status, now, updated, text); dict_cache_update(verify_map, STR(addr), STR(put_buf)); } } } vstring_free(addr); if (get_buf) vstring_free(get_buf); if (put_buf) vstring_free(put_buf); }
int psc_dnsbl_request(const char *client_addr, void (*callback) (int, void *), void *context) { const char *myname = "psc_dnsbl_request"; int fd; VSTREAM *stream; HTABLE_INFO **ht; PSC_DNSBL_SCORE *score; HTABLE_INFO *hash_node; static int request_count; /* * Some spambots make several connections at nearly the same time, * causing their pregreet delays to overlap. Such connections can share * the efforts of DNSBL lookup. * * We store a reference-counted DNSBL score under its client IP address. We * increment the reference count with each score request, and decrement * the reference count with each score retrieval. * * Do not notify the requestor NOW when the DNS replies are already in. * Reason: we must not make a backwards call while we are still in the * middle of executing the corresponding forward call. Instead we create * a zero-delay timer request and call the notification function from * there. * * psc_dnsbl_request() could instead return a result value to indicate that * the DNSBL score is already available, but that would complicate the * caller with two different notification code paths: one asynchronous * code path via the callback invocation, and one synchronous code path * via the psc_dnsbl_request() result value. That would be a source of * future bugs. */ if ((hash_node = htable_locate(dnsbl_score_cache, client_addr)) != 0) { score = (PSC_DNSBL_SCORE *) hash_node->value; score->refcount += 1; PSC_CALL_BACK_EXTEND(hash_node, score); PSC_CALL_BACK_ENTER(score, callback, context); if (msg_verbose > 1) msg_info("%s: reuse blocklist score for %s refcount=%d pending=%d", myname, client_addr, score->refcount, score->pending_lookups); if (score->pending_lookups == 0) event_request_timer(callback, context, EVENT_NULL_DELAY); return (PSC_CALL_BACK_INDEX_OF_LAST(score)); } if (msg_verbose > 1) msg_info("%s: create blocklist score for %s", myname, client_addr); score = (PSC_DNSBL_SCORE *) mymalloc(sizeof(*score)); score->request_id = request_count++; score->dnsbl_name = 0; score->dnsbl_weight = 0; /* As with dnsblog(8), a value < 0 means no reply TTL. */ score->pass_ttl = -1; score->fail_ttl = -1; score->total = 0; score->refcount = 1; score->pending_lookups = 0; PSC_CALL_BACK_INIT(score); PSC_CALL_BACK_ENTER(score, callback, context); (void) htable_enter(dnsbl_score_cache, client_addr, (void *) score); /* * Send a query to all DNSBL servers. Later, DNSBL lookup will be done * with an UDP-based DNS client that is built directly into Postfix code. * We therefore do not optimize the maximum out of this temporary * implementation. */ for (ht = dnsbl_site_list; *ht; ht++) { if ((fd = LOCAL_CONNECT(psc_dnsbl_service, NON_BLOCKING, 1)) < 0) { msg_warn("%s: connect to %s service: %m", myname, psc_dnsbl_service); continue; } stream = vstream_fdopen(fd, O_RDWR); vstream_control(stream, CA_VSTREAM_CTL_CONTEXT(ht[0]->key), CA_VSTREAM_CTL_END); attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, ht[0]->key), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, client_addr), SEND_ATTR_INT(MAIL_ATTR_LABEL, score->request_id), ATTR_TYPE_END); if (vstream_fflush(stream) != 0) { msg_warn("%s: error sending to %s service: %m", myname, psc_dnsbl_service); vstream_fclose(stream); continue; } PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, (void *) stream, var_psc_dnsbl_tmout); score->pending_lookups += 1; } return (PSC_CALL_BACK_INDEX_OF_LAST(score)); }
static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) { VSTRING *buf = vstring_alloc(100); CLEANUP_STATE *state; int flags; int type = 0; int status; /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * Open a queue file and initialize state. */ state = cleanup_open(src); /* * Send the queue id to the client. Read client processing options. If we * can't read the client processing options we can pretty much forget * about the whole operation. */ attr_print(src, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_QUEUEID, state->queue_id), ATTR_TYPE_END); if (attr_scan(src, ATTR_FLAG_STRICT, RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags), ATTR_TYPE_END) != 1) { state->errs |= CLEANUP_STAT_BAD; flags = 0; } cleanup_control(state, flags); /* * XXX Rely on the front-end programs to enforce record size limits. * * First, copy the envelope records to the queue file. Then, copy the * message content (headers and body). Finally, attach any information * extracted from message headers. */ while (CLEANUP_OUT_OK(state)) { if ((type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) < 0) { state->errs |= CLEANUP_STAT_BAD; break; } if (REC_GET_HIDDEN_TYPE(type)) { msg_warn("%s: record type %d not allowed - discarding this message", state->queue_id, type); state->errs |= CLEANUP_STAT_BAD; break; } CLEANUP_RECORD(state, type, vstring_str(buf), VSTRING_LEN(buf)); if (type == REC_TYPE_END) break; } /* * Keep reading in case of problems, until the sender is ready to receive * our status report. */ if (CLEANUP_OUT_OK(state) == 0 && type > 0) { while (type != REC_TYPE_END && (type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) > 0) { if (type == REC_TYPE_MILT_COUNT) { int milter_count = atoi(vstring_str(buf)); /* Avoid deadlock. */ if (milter_count >= 0) cleanup_milter_receive(state, milter_count); } } } /* * Log something to make timeout errors easier to debug. */ if (vstream_ftimeout(src)) msg_warn("%s: read timeout on %s", state->queue_id, VSTREAM_PATH(src)); /* * Finish this message, and report the result status to the client. */ status = cleanup_flush(state); /* in case state is modified */ attr_print(src, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, status), SEND_ATTR_STR(MAIL_ATTR_WHY, (state->flags & CLEANUP_FLAG_SMTP_REPLY) && state->smtp_reply ? state->smtp_reply : state->reason ? state->reason : ""), ATTR_TYPE_END); cleanup_free(state); /* * Cleanup. */ vstring_free(buf); }
int main(int argc, char **argv) { struct stat st; int fd; int c; VSTRING *buf; int status; MAIL_STREAM *dst; int rec_type; static char *segment_info[] = { REC_TYPE_POST_ENVELOPE, REC_TYPE_POST_CONTENT, REC_TYPE_POST_EXTRACT, "" }; char **expected; uid_t uid = getuid(); ARGV *import_env; const char *error_text; char *attr_name; char *attr_value; const char *errstr; char *junk; struct timeval start; int saved_errno; int from_count = 0; int rcpt_count = 0; int validate_input = 1; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up logging. Censor the process name: it is provided by the user. */ argv[0] = "postdrop"; msg_vstream_init(argv[0], VSTREAM_ERR); msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Parse JCL. This program is set-gid and must sanitize all command-line * arguments. The configuration directory argument is validated by the * mail configuration read routine. Don't do complex things until we have * completed initializations. */ while ((c = GETOPT(argc, argv, "c:rv")) > 0) { switch (c) { case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'r': /* forward compatibility */ break; case 'v': if (geteuid() == 0) msg_verbose++; break; default: msg_fatal("usage: %s [-c config_dir] [-v]", argv[0]); } } /* * Read the global configuration file and extract configuration * information. Some claim that the user should supply the working * directory instead. That might be OK, given that this command needs * write permission in a subdirectory called "maildrop". However we still * need to reliably detect incomplete input, and so we must perform * record-level I/O. With that, we should also take the opportunity to * perform some sanity checks on the input. */ mail_conf_read(); /* Re-evaluate mail_task() after reading main.cf. */ msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); get_mail_conf_str_table(str_table); /* * Mail submission access control. Should this be in the user-land gate, * or in the daemon process? */ mail_dict_init(); if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl, uid)) != 0) msg_fatal("User %s(%ld) is not allowed to submit mail", errstr, (long) uid); /* * Stop run-away process accidents by limiting the queue file size. This * is not a defense against DOS attack. */ if (var_message_limit > 0 && get_file_limit() > var_message_limit) set_file_limit((off_t) var_message_limit); /* * This program is installed with setgid privileges. Strip the process * environment so that we don't have to trust the C library. */ import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); clean_env(import_env->argv); argv_free(import_env); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); if (msg_verbose) msg_info("chdir %s", var_queue_dir); /* * Set up signal handlers and a runtime error handler so that we can * clean up incomplete output. * * postdrop_sig() uses the in-kernel SIGINT handler address as an atomic * variable to prevent nested postdrop_sig() calls. For this reason, the * SIGINT handler must be configured before other signal handlers are * allowed to invoke postdrop_sig(). */ signal(SIGPIPE, SIG_IGN); signal(SIGXFSZ, SIG_IGN); signal(SIGINT, postdrop_sig); signal(SIGQUIT, postdrop_sig); if (signal(SIGTERM, SIG_IGN) == SIG_DFL) signal(SIGTERM, postdrop_sig); if (signal(SIGHUP, SIG_IGN) == SIG_DFL) signal(SIGHUP, postdrop_sig); msg_cleanup(postdrop_cleanup); /* End of initializations. */ /* * Don't trust the caller's time information. */ GETTIMEOFDAY(&start); /* * Create queue file. mail_stream_file() never fails. Send the queue ID * to the caller. Stash away a copy of the queue file name so we can * clean up in case of a fatal error or an interrupt. */ dst = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC, var_pickup_service, 0444); attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_QUEUEID, dst->id), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); postdrop_path = mystrdup(VSTREAM_PATH(dst->stream)); /* * Copy stdin to file. The format is checked so that we can recognize * incomplete input and cancel the operation. With the sanity checks * applied here, the pickup daemon could skip format checks and pass a * file descriptor to the cleanup daemon. These are by no means all * sanity checks - the cleanup service and queue manager services will * reject messages that lack required information. * * If something goes wrong, slurp up the input before responding to the * client, otherwise the client will give up after detecting SIGPIPE. * * Allow attribute records if the attribute specifies the MIME body type * (sendmail -B). */ vstream_control(VSTREAM_IN, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END); buf = vstring_alloc(100); expected = segment_info; /* Override time information from the untrusted caller. */ rec_fprintf(dst->stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(start)); for (;;) { /* Don't allow PTR records. */ rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE); if (rec_type == REC_TYPE_EOF) { /* request cancelled */ mail_stream_cleanup(dst); if (remove(postdrop_path)) msg_warn("uid=%ld: remove %s: %m", (long) uid, postdrop_path); else if (msg_verbose) msg_info("remove %s", postdrop_path); myfree(postdrop_path); postdrop_path = 0; exit(0); } if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); if (strchr(*expected, rec_type) == 0) msg_fatal("uid=%ld: unexpected record type: %d", (long) uid, rec_type); if (rec_type == **expected) expected++; /* Override time information from the untrusted caller. */ if (rec_type == REC_TYPE_TIME) continue; /* Check these at submission time instead of pickup time. */ if (rec_type == REC_TYPE_FROM) from_count++; if (rec_type == REC_TYPE_RCPT) rcpt_count++; /* Limit the attribute types that users may specify. */ if (rec_type == REC_TYPE_ATTR) { if ((error_text = split_nameval(vstring_str(buf), &attr_name, &attr_value)) != 0) { msg_warn("uid=%ld: ignoring malformed record: %s: %.200s", (long) uid, error_text, vstring_str(buf)); continue; } #define STREQ(x,y) (strcmp(x,y) == 0) if ((STREQ(attr_name, MAIL_ATTR_ENCODING) && (STREQ(attr_value, MAIL_ATTR_ENC_7BIT) || STREQ(attr_value, MAIL_ATTR_ENC_8BIT) || STREQ(attr_value, MAIL_ATTR_ENC_NONE))) || STREQ(attr_name, MAIL_ATTR_DSN_ENVID) || STREQ(attr_name, MAIL_ATTR_DSN_NOTIFY) || rec_attr_map(attr_name) || (STREQ(attr_name, MAIL_ATTR_RWR_CONTEXT) && (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL) || STREQ(attr_value, MAIL_ATTR_RWR_REMOTE))) || STREQ(attr_name, MAIL_ATTR_TRACE_FLAGS)) { /* XXX */ rec_fprintf(dst->stream, REC_TYPE_ATTR, "%s=%s", attr_name, attr_value); } else { msg_warn("uid=%ld: ignoring attribute record: %.200s=%.200s", (long) uid, attr_name, attr_value); } continue; } if (REC_PUT_BUF(dst->stream, rec_type, buf) < 0) { /* rec_get() errors must not clobber errno. */ saved_errno = errno; while ((rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE)) != REC_TYPE_END && rec_type != REC_TYPE_EOF) if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); validate_input = 0; errno = saved_errno; break; } if (rec_type == REC_TYPE_END) break; } vstring_free(buf); /* * As of Postfix 2.7 the pickup daemon discards mail without recipients. * Such mail may enter the maildrop queue when "postsuper -r" is invoked * before the queue manager deletes an already delivered message. Looking * at file ownership is not a good way to make decisions on what mail to * discard. Instead, the pickup server now requires that new submissions * always have at least one recipient record. * * The Postfix sendmail command already rejects mail without recipients. * However, in the future postdrop may receive mail via other programs, * so we add a redundant recipient check here for future proofing. * * The test for the sender address is just for consistency of error * reporting (report at submission time instead of pickup time). Besides * the segment terminator records, there aren't any other mandatory * records in a Postfix submission queue file. */ if (validate_input && (from_count == 0 || rcpt_count == 0)) { status = CLEANUP_STAT_BAD; mail_stream_cleanup(dst); } /* * Finish the file. */ else if ((status = mail_stream_finish(dst, (VSTRING *) 0)) != 0) { msg_warn("uid=%ld: %m", (long) uid); postdrop_cleanup(); } /* * Disable deletion on fatal error before reporting success, so the file * will not be deleted after we have taken responsibility for delivery. */ if (postdrop_path) { junk = postdrop_path; postdrop_path = 0; myfree(junk); } /* * Send the completion status to the caller and terminate. */ attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, status), SEND_ATTR_STR(MAIL_ATTR_WHY, ""), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); exit(status); }
int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop, VSTRING *endp_prop) { SMTP_ITERATOR *iter = session->iterator; VSTREAM *mp; int fd; /* * Encode the delivery request next-hop to endpoint binding properties: * whether or not this server is best MX host for the delivery request * next-hop or fall-back logical destination (this information is needed * for loop handling in smtp_proto()). * * TODO: save SASL username and password information so that we can * correctly save a reused authenticated connection. * * These memory writes should never fail. */ if ((mp = vstream_memopen(dest_prop, O_WRONLY)) == 0 || attr_print_plain(mp, ATTR_FLAG_NONE, SEND_ATTR_STR(SESS_ATTR_DEST, STR(iter->dest)), SEND_ATTR_STR(SESS_ATTR_HOST, STR(iter->host)), SEND_ATTR_STR(SESS_ATTR_ADDR, STR(iter->addr)), SEND_ATTR_INT(SESS_ATTR_DEST_FEATURES, session->features & SMTP_FEATURE_DESTINATION_MASK), ATTR_TYPE_END) != 0 || vstream_fclose(mp) != 0) msg_fatal("smtp_session_passivate: can't save dest properties: %m"); /* * Encode the physical endpoint properties: all the session properties * except for "session from cache", "best MX", or "RSET failure". Plus * the TLS level, reuse count, and connection expiration time. * * XXX Should also record how many non-delivering mail transactions there * were during this session, and perhaps other statistics, so that we * don't reuse a session too much. * * TODO: passivate SASL username and password information so that we can * correctly save a reused authenticated connection. * * These memory writes should never fail. */ if ((mp = vstream_memopen(endp_prop, O_WRONLY)) == 0 || attr_print_plain(mp, ATTR_FLAG_NONE, #ifdef USE_TLS SEND_ATTR_INT(SESS_ATTR_TLS_LEVEL, session->state->tls->level), #endif SEND_ATTR_INT(SESS_ATTR_REUSE_COUNT, session->reuse_count), SEND_ATTR_INT(SESS_ATTR_ENDP_FEATURES, session->features & SMTP_FEATURE_ENDPOINT_MASK), SEND_ATTR_LONG(SESS_ATTR_EXPIRE_TIME, (long) session->expire_time), ATTR_TYPE_END) != 0 /* * Append the passivated TLS context. These memory writes should never * fail. */ #ifdef USE_TLS || (session->tls_context && attr_print_plain(mp, ATTR_FLAG_NONE, SEND_ATTR_FUNC(tls_proxy_context_print, (void *) session->tls_context), ATTR_TYPE_END) != 0) #endif || vstream_fclose(mp) != 0) msg_fatal("smtp_session_passivate: cannot save TLS context: %m"); /* * Salvage the underlying file descriptor, and destroy the session * object. */ fd = vstream_fileno(session->stream); vstream_fdclose(session->stream); session->stream = 0; smtp_session_free(session); return (fd); }
void psc_send_socket(PSC_STATE *state) { const char *myname = "psc_send_socket"; int server_fd; int pass_err; VSTREAM *fp; if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, vstream_fileno(state->smtp_client_stream), state->smtp_client_addr, state->smtp_client_port); /* * Connect to the real SMTP service over a local IPC channel, send the * file descriptor, and close the file descriptor to save resources. * Experience has shown that some systems will discard information when * we close a channel immediately after writing. Thus, we waste resources * waiting for the remote side to close the local IPC channel first. The * good side of waiting is that we learn when the real SMTP server is * falling behind. * * This is where we would forward the connection to an SMTP server that * provides an appropriate level of service for this client class. For * example, a server that is more forgiving, or one that is more * suspicious. Alternatively, we could send attributes along with the * socket with client reputation information, making everything even more * Postfix-specific. */ if ((server_fd = LOCAL_CONNECT(psc_smtpd_service_name, NON_BLOCKING, PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name); if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); } else { PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n"); psc_free_session_state(state); } return; } /* XXX Note: no dummy read between LOCAL_SEND_FD() and attr_print(). */ fp = vstream_fdopen(server_fd, O_RDWR); pass_err = (LOCAL_SEND_FD(server_fd, vstream_fileno(state->smtp_client_stream)) < 0 || (attr_print(fp, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, state->smtp_client_addr), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_PORT, state->smtp_client_port), SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_ADDR, state->smtp_server_addr), SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_PORT, state->smtp_server_port), ATTR_TYPE_END) || vstream_fflush(fp))); /* XXX Note: no read between attr_print() and vstream_fdclose(). */ (void) vstream_fdclose(fp); if (pass_err != 0) { msg_warn("cannot pass connection to service %s: %m", psc_smtpd_service_name); (void) close(server_fd); if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); } else { PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n"); psc_free_session_state(state); } return; } else { /* * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug * where smtp-source sometimes sees the connection being closed after * it has already received the real SMTP server's 220 greeting! */ #if 0 PSC_DEL_CLIENT_STATE(state); #endif PSC_ADD_SERVER_STATE(state, server_fd); PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, (void *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); return; } }