Beispiel #1
0
int     bounce_one(int flags, const char *queue, const char *id,
		           const char *encoding, const char *sender,
		           const char *dsn_envid, int dsn_ret,
		           MSG_STATS *stats, RECIPIENT *rcpt,
		           const char *relay, DSN *dsn)
{
    DSN     my_dsn = *dsn;
    DSN    *dsn_res;

    /*
     * Sanity check.
     */
    if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
	msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status);
	my_dsn.status = "5.0.0";
    }

    /*
     * DSN filter (Postfix 2.12).
     */
    if (delivery_status_filter != 0
      && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
	if (dsn_res->status[0] == '4')
	    return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
	my_dsn = *dsn_res;
    }
    return (bounce_one_intern(flags, queue, id, encoding, sender, dsn_envid,
			      dsn_ret, stats, rcpt, relay, &my_dsn));
}
Beispiel #2
0
static int smtp_sasl_auth_cache_valid_value(SMTP_SASL_AUTH_CACHE *auth_cache,
					            const char *entry,
					            const char *password)
{
    ssize_t len = strlen(entry);
    char   *cache_hash = mymalloc(len);
    char   *curr_hash;
    unsigned long now = (unsigned long) time((time_t *) 0);
    unsigned long time_stamp;
    int     valid;

    auth_cache->dsn = myrealloc(auth_cache->dsn, len);
    auth_cache->text = myrealloc(auth_cache->text, len);

    if (sscanf(entry, "%lu;%[^;];%[^;];%[^\n]", &time_stamp, cache_hash,
	       auth_cache->dsn, auth_cache->text) != 4
	|| !dsn_valid(auth_cache->dsn)) {
	msg_warn("bad smtp_sasl_auth_cache entry: %.100s", entry);
	valid = 0;
    } else if (time_stamp + auth_cache->ttl < now) {
	valid = 0;
    } else {
	curr_hash = smtp_sasl_auth_cache_make_pass(password);
	valid = (strcmp(cache_hash, curr_hash) == 0);
	myfree(curr_hash);
    }
    myfree(cache_hash);
    return (valid);
}
Beispiel #3
0
int     bounce_append(int flags, const char *id, MSG_STATS *stats,
		              RECIPIENT *rcpt, const char *relay,
		              DSN *dsn)
{
    DSN     my_dsn = *dsn;
    DSN    *dsn_res;

    /*
     * Sanity check. If we're really confident, change this into msg_panic
     * (remember, this information may be under control by a hostile server).
     */
    if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
	msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status);
	my_dsn.status = "5.0.0";
    }

    /*
     * DSN filter (Postfix 2.12).
     */
    if (delivery_status_filter != 0
      && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) {
	if (dsn_res->status[0] == '4')
	    return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res));
	my_dsn = *dsn_res;
    }
    return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn));
}
Beispiel #4
0
int     defer_append(int flags, const char *id, MSG_STATS *stats,
		             RECIPIENT *rcpt, const char *relay,
		             DSN *dsn)
{
    const char *rcpt_domain;
    DSN     my_dsn = *dsn;
    int     status;

    /*
     * Sanity check.
     */
    if (my_dsn.status[0] != '4' || !dsn_valid(my_dsn.status)) {
	msg_warn("defer_append: ignoring dsn code \"%s\"", my_dsn.status);
	my_dsn.status = "4.0.0";
    }

    /*
     * MTA-requested address verification information is stored in the verify
     * service database.
     */
    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
	my_dsn.action = "undeliverable";
	status = verify_append(id, stats, rcpt, relay, &my_dsn,
			       DEL_RCPT_STAT_DEFER);
	return (status);
    }

    /*
     * User-requested address verification information is logged and mailed
     * to the requesting user.
     */
    if (flags & DEL_REQ_FLAG_USR_VRFY) {
	my_dsn.action = "undeliverable";
	status = trace_append(flags, id, stats, rcpt, relay, &my_dsn);
	return (status);
    }

    /*
     * Normal mail delivery. May also send a delivery record to the user.
     * 
     * XXX DSN We write all deferred recipients to the defer logfile regardless
     * of DSN NOTIFY options, because those options don't apply to mailq(1)
     * reports or to postmaster notifications.
     */
    else {

	/*
	 * Supply default action.
	 */
	my_dsn.action = "delayed";

	if (mail_command_client(MAIL_CLASS_PRIVATE, var_defer_service,
			   ATTR_TYPE_INT, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
				ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
				ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
				ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
				ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
				ATTR_TYPE_END) != 0)
	    msg_warn("%s: %s service failure", id, var_defer_service);
	log_adhoc(id, stats, rcpt, relay, &my_dsn, "deferred");

	/*
	 * Traced delivery.
	 */
	if (flags & DEL_REQ_FLAG_RECORD)
	    if (trace_append(flags, id, stats, rcpt, relay, &my_dsn) != 0)
		msg_warn("%s: %s service failure", id, var_trace_service);

	/*
	 * Notify the fast flush service. XXX Should not this belong in the
	 * bounce/defer daemon? Well, doing it here is more robust.
	 */
	if ((rcpt_domain = strrchr(rcpt->address, '@')) != 0
	    && *++rcpt_domain != 0)
	    switch (flush_add(rcpt_domain, id)) {
	    case FLUSH_STAT_OK:
	    case FLUSH_STAT_DENY:
		break;
	    default:
		msg_warn("%s: %s service failure", id, var_flush_service);
		break;
	    }
	return (-1);
    }
}
Beispiel #5
0
static void qmgr_deliver_update(int unused_event, char *context)
{
    QMGR_ENTRY *entry = (QMGR_ENTRY *) context;
    QMGR_QUEUE *queue = entry->queue;
    QMGR_TRANSPORT *transport = queue->transport;
    QMGR_MESSAGE *message = entry->message;
    static DSN_BUF *dsb;
    int     status;

    /*
     * Release the delivery agent from a "hot" queue entry.
     */
#define QMGR_DELIVER_RELEASE_AGENT(entry) do { \
	event_disable_readwrite(vstream_fileno(entry->stream)); \
	(void) vstream_fclose(entry->stream); \
	entry->stream = 0; \
	qmgr_deliver_concurrency--; \
    } while (0)

    if (dsb == 0)
	dsb = dsb_create();

    /*
     * The message transport has responded. Stop the watchdog timer.
     */
    event_cancel_timer(qmgr_deliver_abort, context);

    /*
     * Retrieve the delivery agent status report. The numerical status code
     * indicates if delivery should be tried again. The reason text is sent
     * only when a site should be avoided for a while, so that the queue
     * manager can log why it does not even try to schedule delivery to the
     * affected recipients.
     */
    status = qmgr_deliver_final_reply(entry->stream, dsb);

    /*
     * The mail delivery process failed for some reason (although delivery
     * may have been successful). Back off with this transport type for a
     * while. Dispose of queue entries for this transport that await
     * selection (the todo lists). Stay away from queue entries that have
     * been selected (the busy lists), or we would have dangling pointers.
     * The queue itself won't go away before we dispose of the current queue
     * entry.
     */
    if (status == DELIVER_STAT_CRASH) {
	message->flags |= DELIVER_STAT_DEFER;
#if 0
	whatsup = concatenate("unknown ", transport->name,
			      " mail transport error", (char *) 0);
	qmgr_transport_throttle(transport,
				DSN_SIMPLE(&dsb->dsn, "4.3.0", whatsup));
	myfree(whatsup);
#else
	qmgr_transport_throttle(transport,
				DSN_SIMPLE(&dsb->dsn, "4.3.0",
					   "unknown mail transport error"));
#endif
	msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
		 transport->name);

	/*
	 * Assume the worst and write a defer logfile record for each
	 * recipient. This omission was already present in the first queue
	 * manager implementation of 199703, and was fixed 200511.
	 * 
	 * To avoid the synchronous qmgr_defer_recipient() operation for each
	 * recipient of this queue entry, release the delivery process and
	 * move the entry back to the todo queue. Let qmgr_defer_transport()
	 * log the recipient asynchronously if possible, and get out of here.
	 * Note: if asynchronous logging is not possible,
	 * qmgr_defer_transport() eventually invokes qmgr_entry_done() and
	 * the entry becomes a dangling pointer.
	 */
	QMGR_DELIVER_RELEASE_AGENT(entry);
	qmgr_entry_unselect(queue, entry);
	qmgr_defer_transport(transport, &dsb->dsn);
	return;
    }

    /*
     * This message must be tried again.
     * 
     * If we have a problem talking to this site, back off with this site for a
     * while; dispose of queue entries for this site that await selection
     * (the todo list); stay away from queue entries that have been selected
     * (the busy list), or we would have dangling pointers. The queue itself
     * won't go away before we dispose of the current queue entry.
     * 
     * XXX Caution: DSN_COPY() will panic on empty status or reason.
     */
#define SUSPENDED	"delivery temporarily suspended: "

    if (status == DELIVER_STAT_DEFER) {
	message->flags |= DELIVER_STAT_DEFER;
	if (VSTRING_LEN(dsb->status)) {
	    /* Sanitize the DSN status/reason from the delivery agent. */
	    if (!dsn_valid(vstring_str(dsb->status)))
		vstring_strcpy(dsb->status, "4.0.0");
	    if (VSTRING_LEN(dsb->reason) == 0)
		vstring_strcpy(dsb->reason, "unknown error");
	    vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
	    if (QMGR_QUEUE_READY(queue)) {
		qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb));
		if (QMGR_QUEUE_THROTTLED(queue))
		    qmgr_defer_todo(queue, &dsb->dsn);
	    }
	}
    }

    /*
     * No problems detected. Mark the transport and queue as alive. The queue
     * itself won't go away before we dispose of the current queue entry.
     */
    if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) {
	qmgr_transport_unthrottle(transport);
	qmgr_queue_unthrottle(queue);
    }

    /*
     * Release the delivery process, and give some other queue entry a chance
     * to be delivered. When all recipients for a message have been tried,
     * decide what to do next with this message: defer, bounce, delete.
     */
    QMGR_DELIVER_RELEASE_AGENT(entry);
    qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
}
Beispiel #6
0
SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
{
    static SMTP_RESP rdata;
    char   *cp;
    int     last_char;
    int     three_digs = 0;
    size_t  len;
    const char *new_reply;
    int     chat_append_flag;
    int     chat_append_skipped = 0;

    /*
     * Initialize the response data buffer.
     */
    if (rdata.str_buf == 0) {
	rdata.dsn_buf = vstring_alloc(10);
	rdata.str_buf = vstring_alloc(100);
    }

    /*
     * Censor out non-printable characters in server responses. Concatenate
     * multi-line server responses. Separate the status code from the text.
     * Leave further parsing up to the application.
     * 
     * We can't parse or store input that exceeds var_line_limit, so we just
     * skip over it to simplify the remainder of the code below.
     */
    VSTRING_RESET(rdata.str_buf);
    for (;;) {
	last_char = smtp_get(session->buffer, session->stream, var_line_limit,
			     SMTP_GET_FLAG_SKIP);
	/* XXX Update the per-line time limit. */
	printable(STR(session->buffer), '?');
	if (last_char != '\n')
	    msg_warn("%s: response longer than %d: %.30s...",
		session->namaddrport, var_line_limit, STR(session->buffer));
	if (msg_verbose)
	    msg_info("< %s: %.100s", session->namaddrport, STR(session->buffer));

	/*
	 * Defend against a denial of service attack by limiting the amount
	 * of multi-line text that we are willing to store.
	 */
	chat_append_flag = (LEN(rdata.str_buf) < var_line_limit);
	if (chat_append_flag)
	    smtp_chat_append(session, "In:  ", STR(session->buffer));
	else {
	    if (chat_append_skipped == 0)
		msg_warn("%s: multi-line response longer than %d %.30s...",
		  session->namaddrport, var_line_limit, STR(rdata.str_buf));
	    if (chat_append_skipped < INT_MAX)
		chat_append_skipped++;
	}

	/*
	 * Server reply substitution, for fault-injection testing, or for
	 * working around broken systems. Use with care.
	 */
	if (smtp_chat_resp_filter != 0) {
	    new_reply = dict_get(smtp_chat_resp_filter, STR(session->buffer));
	    if (new_reply != 0) {
		msg_info("%s: replacing server reply \"%s\" with \"%s\"",
		     session->namaddrport, STR(session->buffer), new_reply);
		vstring_strcpy(session->buffer, new_reply);
		if (chat_append_flag) {
		    smtp_chat_append(session, "Replaced-by: ", "");
		    smtp_chat_append(session, "     ", new_reply);
		}
	    } else if (smtp_chat_resp_filter->error != 0) {
		msg_warn("%s: table %s:%s lookup error for %s",
			 session->state->request->queue_id,
			 smtp_chat_resp_filter->type,
			 smtp_chat_resp_filter->name,
			 printable(STR(session->buffer), '?'));
		vstream_longjmp(session->stream, SMTP_ERR_DATA);
	    }
	}
	if (chat_append_flag) {
	    if (LEN(rdata.str_buf))
		VSTRING_ADDCH(rdata.str_buf, '\n');
	    vstring_strcat(rdata.str_buf, STR(session->buffer));
	}

	/*
	 * Parse into code and text. Do not ignore garbage (see below).
	 */
	for (cp = STR(session->buffer); *cp && ISDIGIT(*cp); cp++)
	     /* void */ ;
	if ((three_digs = (cp - STR(session->buffer) == 3)) != 0) {
	    if (*cp == '-')
		continue;
	    if (*cp == ' ' || *cp == 0)
		break;
	}

	/*
	 * XXX Do not simply ignore garbage in the server reply when ESMTP
	 * command pipelining is turned on.  For example, after sending
	 * ".<CR><LF>QUIT<CR><LF>" and receiving garbage followed by a
	 * legitimate 2XX reply, Postfix recognizes the server's QUIT reply
	 * as the END-OF-DATA reply after garbage, causing mail to be lost.
	 * 
	 * Without the ability to store per-domain status information in queue
	 * files, automatic workarounds are problematic:
	 * 
	 * - Automatically deferring delivery creates a "repeated delivery"
	 * problem when garbage arrives after the DATA stage. Without the
	 * workaround, Postfix delivers only once.
	 * 
	 * - Automatically deferring delivery creates a "no delivery" problem
	 * when the garbage arrives before the DATA stage. Without the
	 * workaround, mail might still get through.
	 * 
	 * - Automatically turning off pipelining for delayed mail affects
	 * deliveries to correctly implemented servers, and may also affect
	 * delivery of large mailing lists.
	 * 
	 * So we leave the decision with the administrator, but we don't force
	 * them to take action, like we would with automatic deferral.  If
	 * loss of mail is not acceptable then they can turn off pipelining
	 * for specific sites, or they can turn off pipelining globally when
	 * they find that there are just too many broken sites.
	 */
	session->error_mask |= MAIL_ERROR_PROTOCOL;
	if (session->features & SMTP_FEATURE_PIPELINING) {
	    msg_warn("%s: non-%s response from %s: %.100s",
		     session->state->request->queue_id,
		     smtp_mode ? "ESMTP" : "LMTP",
		     session->namaddrport, STR(session->buffer));
	    if (var_helpful_warnings)
		msg_warn("to prevent loss of mail, turn off command pipelining "
			 "for %s with the %s parameter",
			 STR(session->iterator->addr),
			 VAR_LMTP_SMTP(EHLO_DIS_MAPS));
	}
    }

    /*
     * Extract RFC 821 reply code and RFC 2034 detail. Use a default detail
     * code if none was given.
     * 
     * Ignore out-of-protocol enhanced status codes: codes that accompany 3XX
     * replies, or codes whose initial digit is out of sync with the reply
     * code.
     * 
     * XXX Potential stability problem. In order to save memory, the queue
     * manager stores DSNs in a compact manner:
     * 
     * - empty strings are represented by null pointers,
     * 
     * - the status and reason are required to be non-empty.
     * 
     * Other Postfix daemons inherit this behavior, because they use the same
     * DSN support code. This means that everything that receives DSNs must
     * cope with null pointers for the optional DSN attributes, and that
     * everything that provides DSN information must provide a non-empty
     * status and reason, otherwise the DSN support code wil panic().
     * 
     * Thus, when the remote server sends a malformed reply (or 3XX out of
     * context) we should not panic() in DSN_COPY() just because we don't
     * have a status. Robustness suggests that we supply a status here, and
     * that we leave it up to the down-stream code to override the
     * server-supplied status in case of an error we can't detect here, such
     * as an out-of-order server reply.
     */
    VSTRING_TERMINATE(rdata.str_buf);
    vstring_strcpy(rdata.dsn_buf, "5.5.0");	/* SAFETY! protocol error */
    if (three_digs != 0) {
	rdata.code = atoi(STR(session->buffer));
	if (strchr("245", STR(session->buffer)[0]) != 0) {
	    for (cp = STR(session->buffer) + 4; *cp == ' '; cp++)
		 /* void */ ;
	    if ((len = dsn_valid(cp)) > 0 && *cp == *STR(session->buffer)) {
		vstring_strncpy(rdata.dsn_buf, cp, len);
	    } else {
		vstring_strcpy(rdata.dsn_buf, "0.0.0");
		STR(rdata.dsn_buf)[0] = STR(session->buffer)[0];
	    }
	}
    } else {
	rdata.code = 0;
    }
    rdata.dsn = STR(rdata.dsn_buf);
    rdata.str = STR(rdata.str_buf);
    return (&rdata);
}
Beispiel #7
0
int     sent(int flags, const char *id, MSG_STATS *stats,
	             RECIPIENT *recipient, const char *relay,
	             DSN *dsn)
{
    DSN     my_dsn = *dsn;
    int     status;

    /*
     * Sanity check.
     */
    if (my_dsn.status[0] != '2' || !dsn_valid(my_dsn.status)) {
	msg_warn("sent: ignoring dsn code \"%s\"", my_dsn.status);
	my_dsn.status = "2.0.0";
    }

    /*
     * MTA-requested address verification information is stored in the verify
     * service database.
     */
    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
	my_dsn.action = "deliverable";
	status = verify_append(id, stats, recipient, relay, &my_dsn,
			       DEL_RCPT_STAT_OK);
	return (status);
    }

    /*
     * User-requested address verification information is logged and mailed
     * to the requesting user.
     */
    if (flags & DEL_REQ_FLAG_USR_VRFY) {
	my_dsn.action = "deliverable";
	status = trace_append(flags, id, stats, recipient, relay, &my_dsn);
	return (status);
    }

    /*
     * Normal mail delivery. May also send a delivery record to the user.
     */
    else {
	if (my_dsn.action == 0 || my_dsn.action[0] == 0)
	    my_dsn.action = "delivered";

	if (((flags & DEL_REQ_FLAG_RECORD) == 0
	  || trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0)
	    && ((recipient->dsn_notify & DSN_NOTIFY_SUCCESS) == 0
	|| trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0)) {
	    log_adhoc(id, stats, recipient, relay, &my_dsn, "sent");
	    status = 0;
	} else {
	    VSTRING *junk = vstring_alloc(100);

	    vstring_sprintf(junk, "%s: %s service failed",
			    id, var_trace_service);
	    my_dsn.reason = vstring_str(junk);
	    my_dsn.status ="4.3.0";
	    status = defer_append(flags, id, stats, recipient, relay, &my_dsn);
	    vstring_free(junk);
	}
	return (status);
    }
}
Beispiel #8
0
int     sent(int flags, const char *id, MSG_STATS *stats,
	             RECIPIENT *recipient, const char *relay,
	             DSN *dsn)
{
    const char *myname="sent.c Sent";
    if (msg_verbose)
 	msg_info("HU--%s Starting",myname);
    
    DSN     my_dsn = *dsn;
    DSN    *dsn_res;
    int     status;

    /*
     * Sanity check.
     */
    if (my_dsn.status[0] != '2' || !dsn_valid(my_dsn.status)) {
	msg_warn("sent: ignoring dsn code \"%s\"", my_dsn.status);
	my_dsn.status = "2.0.0";
    }

    /*
     * DSN filter (Postfix 3.0).
     */
    if (delivery_status_filter != 0
     && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0)
	my_dsn = *dsn_res;

    /*
     * MTA-requested address verification information is stored in the verify
     * service database.
     */
    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
	my_dsn.action = "deliverable";
	status = verify_append(id, stats, recipient, relay, &my_dsn,
			       DEL_RCPT_STAT_OK);
	return (status);
    }

    /*
     * User-requested address verification information is logged and mailed
     * to the requesting user.
     */
    if (flags & DEL_REQ_FLAG_USR_VRFY) {
	my_dsn.action = "deliverable";
	status = trace_append(flags, id, stats, recipient, relay, &my_dsn);
	return (status);
    }

    /*
     * Normal mail delivery. May also send a delivery record to the user.
     */
    else {

	/* Readability macros: record all deliveries, or the delayed ones. */
#define REC_ALL_SENT(flags) (flags & DEL_REQ_FLAG_RECORD)
#define REC_DLY_SENT(flags, rcpt) \
	((flags & DEL_REQ_FLAG_REC_DLY_SENT) \
	&& (rcpt->dsn_notify == 0 || (rcpt->dsn_notify & DSN_NOTIFY_DELAY)))

	if (my_dsn.action == 0 || my_dsn.action[0] == 0)
	    my_dsn.action = "delivered";

	if (((REC_ALL_SENT(flags) == 0 && REC_DLY_SENT(flags, recipient) == 0)
	  || trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0)
	    && ((recipient->dsn_notify & DSN_NOTIFY_SUCCESS) == 0
	|| trace_append(flags, id, stats, recipient, relay, &my_dsn) == 0)) {
	    log_adhoc(id, stats, recipient, relay, &my_dsn, "sent");
	    status = 0;
	} else {
	    VSTRING *junk = vstring_alloc(100);

	    vstring_sprintf(junk, "%s: %s service failed",
			    id, var_trace_service);
	    my_dsn.reason = vstring_str(junk);
	    my_dsn.status = "4.3.0";
	    status = defer_append(flags, id, stats, recipient, relay, &my_dsn);
	    vstring_free(junk);
	}
	return (status);
    }
}