Exemple #1
0
static void verify_update_service(VSTREAM *client_stream)
{
    VSTRING *buf = vstring_alloc(10);
    VSTRING *addr = vstring_alloc(10);
    int     addr_status;
    VSTRING *text = vstring_alloc(10);
    const char *status_name;
    const char *raw_data;
    long    probed;
    long    updated;

    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
		  RECV_ATTR_STR(MAIL_ATTR_ADDR, addr),
		  RECV_ATTR_INT(MAIL_ATTR_ADDR_STATUS, &addr_status),
		  RECV_ATTR_STR(MAIL_ATTR_WHY, text),
		  ATTR_TYPE_END) == 3) {
	/* FIX 200501 IPv6 patch did not neuter ":" in address literals. */
	translit(STR(addr), ":", "_");
	if ((status_name = verify_stat2name(addr_status)) == 0) {
	    msg_warn("bad recipient status %d for recipient %s",
		     addr_status, STR(addr));
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_BAD),
		       ATTR_TYPE_END);
	} else {

	    /*
	     * Robustness: don't allow a failed probe to clobber an OK
	     * address before it expires. The failed probe is ignored so that
	     * the address will be re-probed upon the next query. As long as
	     * some probes succeed the address will remain cached as OK.
	     */
	    if (addr_status == DEL_RCPT_STAT_OK
		|| (raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0
		|| STATUS_FROM_RAW_ENTRY(raw_data) != DEL_RCPT_STAT_OK) {
		probed = 0;
		updated = (long) time((time_t *) 0);
		verify_make_entry(buf, addr_status, probed, updated, STR(text));
		if (msg_verbose)
		    msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s",
			STR(addr), addr_status, probed, updated, STR(text));
		dict_cache_update(verify_map, STR(addr), STR(buf));
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_OK),
		       ATTR_TYPE_END);
	}
    }
    vstring_free(buf);
    vstring_free(addr);
    vstring_free(text);
}
Exemple #2
0
static void verify_service(VSTREAM *client_stream, char *unused_service,
			           char **argv)
{
    VSTRING *request = vstring_alloc(10);

    /*
     * Sanity check. This service takes no command-line arguments.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);

    /*
     * This routine runs whenever a client connects to the socket dedicated
     * to the address verification service. All connection-management stuff
     * is handled by the common code in multi_server.c.
     */
    if (attr_scan(client_stream,
		  ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
		  RECV_ATTR_STR(MAIL_ATTR_REQ, request),
		  ATTR_TYPE_END) == 1) {
	if (STREQ(STR(request), VRFY_REQ_UPDATE)) {
	    verify_update_service(client_stream);
	} else if (STREQ(STR(request), VRFY_REQ_QUERY)) {
	    verify_query_service(client_stream);
	} else {
	    msg_warn("unrecognized request: \"%s\", ignored", STR(request));
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_BAD),
		       ATTR_TYPE_END);
	}
    }
    vstream_fflush(client_stream);
    vstring_free(request);
}
Exemple #3
0
static void proxymap_open_service(VSTREAM *client_stream)
{
    int     request_flags;
    DICT   *dict;
    int     reply_status;
    int     reply_flags;

    /*
     * Process the request.
     */
    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
                  ATTR_TYPE_STR, MAIL_ATTR_TABLE, request_map,
                  ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request_flags,
                  ATTR_TYPE_END) != 2) {
        reply_status = PROXY_STAT_BAD;
        reply_flags = 0;
    } else if ((dict = proxy_map_find(STR(request_map), request_flags,
                                      &reply_status)) == 0) {
        reply_flags = 0;
    } else {
        reply_status = PROXY_STAT_OK;
        reply_flags = dict->flags;
    }

    /*
     * Respond to the client.
     */
    attr_print(client_stream, ATTR_FLAG_NONE,
               ATTR_TYPE_INT, MAIL_ATTR_STATUS, reply_status,
               ATTR_TYPE_INT, MAIL_ATTR_FLAGS, reply_flags,
               ATTR_TYPE_END);
}
Exemple #4
0
static void qmqpd_open_file(QMQPD_STATE *state)
{
    int     cleanup_flags;

    /*
     * Connect to the cleanup server. Log client name/address with queue ID.
     */
    cleanup_flags = input_transp_cleanup(CLEANUP_FLAG_MASK_EXTERNAL,
					 qmqpd_input_transp_mask);
    state->dest = mail_stream_service(MAIL_CLASS_PUBLIC, var_cleanup_service);
    if (state->dest == 0
	|| attr_print(state->dest->stream, ATTR_FLAG_NONE,
		      ATTR_TYPE_INT, MAIL_ATTR_FLAGS, cleanup_flags,
		      ATTR_TYPE_END) != 0)
	msg_fatal("unable to connect to the %s %s service",
		  MAIL_CLASS_PUBLIC, var_cleanup_service);
    state->cleanup = state->dest->stream;
    state->queue_id = mystrdup(state->dest->id);
    msg_info("%s: client=%s", state->queue_id, state->namaddr);

    /*
     * Record the time of arrival. Optionally, enable content filtering (not
     * bloody likely, but present for the sake of consistency with all other
     * Postfix points of entrance).
     */
    rec_fprintf(state->cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
		REC_TYPE_TIME_ARG(state->arrival_time));
    if (*var_filter_xport)
	rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
}
Exemple #5
0
int     resolve_proto(RES_CONTEXT *context, VSTREAM *stream)
{
    int     flags;

    if (attr_scan(stream, ATTR_FLAG_STRICT,
		  ATTR_TYPE_STR, MAIL_ATTR_ADDR, query,
		  ATTR_TYPE_END) != 1)
	return (-1);

    resolve_addr(context, STR(query),
		 channel, nexthop, nextrcpt, &flags);

    if (msg_verbose)
	msg_info("%s -> (`%s' `%s' `%s' `%d')", STR(query), STR(channel),
		 STR(nexthop), STR(nextrcpt), flags);

    attr_print(stream, ATTR_FLAG_NONE,
	       ATTR_TYPE_STR, MAIL_ATTR_TRANSPORT, STR(channel),
	       ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, STR(nexthop),
	       ATTR_TYPE_STR, MAIL_ATTR_RECIP, STR(nextrcpt),
	       ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
	       ATTR_TYPE_END);

    if (vstream_fflush(stream) != 0) {
	msg_warn("write resolver reply: %m");
	return (-1);
    }
    return (0);
}
static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request,
				         int status)
{
    DSN    *hop_status;
    int     err;

    /* XXX This DSN structure initialization bypasses integrity checks. */
    static DSN dummy_dsn = {"", "", "", "", "", "", ""};

    /*
     * Send the status and the optional reason.
     */
    if ((hop_status = request->hop_status) == 0)
	hop_status = &dummy_dsn;
    if (msg_verbose)
	msg_info("deliver_request_final: send: \"%s\" %d",
		 hop_status->reason, status);
    attr_print(stream, ATTR_FLAG_NONE,
	       ATTR_TYPE_FUNC, dsn_print, (void *) hop_status,
	       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
	       ATTR_TYPE_END);
    if ((err = vstream_fflush(stream)) != 0)
	if (msg_verbose)
	    msg_warn("send final status: %m");

    /*
     * With some UNIX systems, stream sockets lose data when you close them
     * immediately after writing to them. That is not how sockets are
     * supposed to behave! The workaround is to wait until the receiver
     * closes the connection. Calling VSTREAM_GETC() has the benefit of using
     * whatever timeout is specified in the ipc_timeout parameter.
     */
    (void) VSTREAM_GETC(stream);
    return (err);
}
Exemple #7
0
static void node_attr_print(const acl::xml_node& node, int depth)
{
	const acl::xml_attr* attr = node.first_attr();
	while (attr)
	{
		attr_print(*attr, depth);
		attr = node.next_attr();
	}
}
Exemple #8
0
int     rewrite_proto(VSTREAM *stream)
{
    RWR_CONTEXT *context;
    TOK822 *tree;

    if (attr_scan(stream, ATTR_FLAG_STRICT,
		  ATTR_TYPE_STR, MAIL_ATTR_RULE, ruleset,
		  ATTR_TYPE_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,
	       ATTR_TYPE_INT, MAIL_ATTR_FLAGS, server_flags,
	       ATTR_TYPE_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);
}
Exemple #9
0
static void proxymap_service(VSTREAM *client_stream, char *unused_service,
                             char **argv)
{

    /*
     * Sanity check. This service takes no command-line arguments.
     */
    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) == 1) {
        if (VSTREQ(request, PROXY_REQ_LOOKUP)) {
            proxymap_lookup_service(client_stream);
        } else if (VSTREQ(request, PROXY_REQ_UPDATE)) {
            proxymap_update_service(client_stream);
        } else if (VSTREQ(request, PROXY_REQ_DELETE)) {
            proxymap_delete_service(client_stream);
        } else if (VSTREQ(request, PROXY_REQ_SEQUENCE)) {
            proxymap_sequence_service(client_stream);
        } else if (VSTREQ(request, PROXY_REQ_OPEN)) {
            proxymap_open_service(client_stream);
        } else {
            msg_warn("unrecognized request: \"%s\", ignored", STR(request));
            attr_print(client_stream, ATTR_FLAG_NONE,
                       ATTR_TYPE_INT, MAIL_ATTR_STATUS, PROXY_STAT_BAD,
                       ATTR_TYPE_END);
        }
    }
    vstream_control(client_stream,
                    VSTREAM_CTL_START_DEADLINE,
                    VSTREAM_CTL_END);
    vstream_fflush(client_stream);
}
Exemple #10
0
static void proxymap_update_service(VSTREAM *client_stream)
{
    int     request_flags;
    DICT   *dict;
    int     dict_status;
    int     reply_status;

    /*
     * Process the request.
     *
     * XXX We don't close maps, so we must turn on synchronous update to ensure
     * that the on-disk data is in a consistent state between updates.
     *
     * XXX We ignore duplicates, because the proxymap server would abort
     * otherwise.
     */
    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
                  ATTR_TYPE_STR, MAIL_ATTR_TABLE, request_map,
                  ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request_flags,
                  ATTR_TYPE_STR, MAIL_ATTR_KEY, request_key,
                  ATTR_TYPE_STR, MAIL_ATTR_VALUE, request_value,
                  ATTR_TYPE_END) != 4) {
        reply_status = PROXY_STAT_BAD;
    } else if (proxy_writer == 0) {
        msg_warn("refusing %s update request on non-%s service",
                 STR(request_map), MAIL_SERVICE_PROXYWRITE);
        reply_status = PROXY_STAT_DENY;
    } else if ((dict = proxy_map_find(STR(request_map), request_flags,
                                      &reply_status)) == 0) {
        /* void */ ;
    } else {
        dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
                       | (request_flags & DICT_FLAG_RQST_MASK)
                       | DICT_FLAG_SYNC_UPDATE | DICT_FLAG_DUP_REPLACE);
        dict_status = dict_put(dict, STR(request_key), STR(request_value));
        if (dict_status == 0) {
            reply_status = PROXY_STAT_OK;
        } else if (dict->error == 0) {
            reply_status = PROXY_STAT_NOKEY;
        } else {
            reply_status = PROXY_STAT_RETRY;
        }
    }

    /*
     * Respond to the client.
     */
    attr_print(client_stream, ATTR_FLAG_NONE,
               ATTR_TYPE_INT, MAIL_ATTR_STATUS, reply_status,
               ATTR_TYPE_END);
}
Exemple #11
0
static void post_mail_init(VSTREAM *stream, const char *sender,
			           const char *recipient,
			           int filter_class, int trace_flags,
			           VSTRING *queue_id)
{
    VSTRING *id = queue_id ? queue_id : vstring_alloc(100);
    struct timeval now;
    const char *date;
    int cleanup_flags =
	int_filt_flags(filter_class) | CLEANUP_FLAG_MASK_INTERNAL;

    GETTIMEOFDAY(&now);
    date = mail_date(now.tv_sec);

    /*
     * Negotiate with the cleanup service. Give up if we can't agree.
     */
    if (attr_scan(stream, ATTR_FLAG_STRICT,
		  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
		  ATTR_TYPE_END) != 1
	|| attr_print(stream, ATTR_FLAG_NONE,
		      ATTR_TYPE_INT, MAIL_ATTR_FLAGS, cleanup_flags,
		      ATTR_TYPE_END) != 0)
	msg_fatal("unable to contact the %s service", var_cleanup_service);

    /*
     * Generate a minimal envelope section. The cleanup service will add a
     * size record.
     */
    rec_fprintf(stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
		REC_TYPE_TIME_ARG(now));
    rec_fprintf(stream, REC_TYPE_ATTR, "%s=%s",
		MAIL_ATTR_LOG_ORIGIN, MAIL_ATTR_ORG_LOCAL);
    rec_fprintf(stream, REC_TYPE_ATTR, "%s=%d",
		MAIL_ATTR_TRACE_FLAGS, trace_flags);
    rec_fputs(stream, REC_TYPE_FROM, sender);
    rec_fputs(stream, REC_TYPE_RCPT, recipient);
    rec_fputs(stream, REC_TYPE_MESG, "");

    /*
     * Do the Received: and Date: header lines. This allows us to shave a few
     * cycles by using the expensive date conversion result for both.
     */
    post_mail_fprintf(stream, "Received: by %s (%s)",
		      var_myhostname, var_mail_name);
    post_mail_fprintf(stream, "\tid %s; %s", vstring_str(id), date);
    post_mail_fprintf(stream, "Date: %s", date);
    if (queue_id == 0)
	vstring_free(id);
}
Exemple #12
0
static void proxymap_sequence_service(VSTREAM *client_stream)
{
    int     request_flags;
    DICT   *dict;
    int     request_func;
    const char *reply_key;
    const char *reply_value;
    int     dict_status;
    int     reply_status;

    /*
     * Process the request.
     */
    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
                  ATTR_TYPE_STR, MAIL_ATTR_TABLE, request_map,
                  ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request_flags,
                  ATTR_TYPE_INT, MAIL_ATTR_FUNC, &request_func,
                  ATTR_TYPE_END) != 3
            || (request_func != DICT_SEQ_FUN_FIRST
                && request_func != DICT_SEQ_FUN_NEXT)) {
        reply_status = PROXY_STAT_BAD;
        reply_key = reply_value = "";
    } else if ((dict = proxy_map_find(STR(request_map), request_flags,
                                      &reply_status)) == 0) {
        reply_key = reply_value = "";
    } else {
        dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
                       | (request_flags & DICT_FLAG_RQST_MASK));
        dict_status = dict_seq(dict, request_func, &reply_key, &reply_value);
        if (dict_status == 0) {
            reply_status = PROXY_STAT_OK;
        } else if (dict->error == 0) {
            reply_status = PROXY_STAT_NOKEY;
            reply_key = reply_value = "";
        } else {
            reply_status = PROXY_STAT_RETRY;
            reply_key = reply_value = "";
        }
    }

    /*
     * Respond to the client.
     */
    attr_print(client_stream, ATTR_FLAG_NONE,
               ATTR_TYPE_INT, MAIL_ATTR_STATUS, reply_status,
               ATTR_TYPE_STR, MAIL_ATTR_KEY, reply_key,
               ATTR_TYPE_STR, MAIL_ATTR_VALUE, reply_value,
               ATTR_TYPE_END);
}
Exemple #13
0
static int deliver_request_initial(VSTREAM *stream)
{
    int     err;

    /*
     * The master processes runs a finite number of delivery agent processes
     * to handle service requests. Thus, a delivery agent process must send
     * something to inform the queue manager that it is ready to receive a
     * delivery request; otherwise the queue manager could block in write().
     */
    if (msg_verbose)
	msg_info("deliver_request_initial: send initial status");
    attr_print(stream, ATTR_FLAG_NONE,
	       ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0,
	       ATTR_TYPE_END);
    if ((err = vstream_fflush(stream)) != 0)
	if (msg_verbose)
	    msg_warn("send initial status: %m");
    return (err);
}
Exemple #14
0
static void proxymap_lookup_service(VSTREAM *client_stream)
{
    int     request_flags;
    DICT   *dict;
    const char *reply_value;
    int     reply_status;

    /*
     * Process the request.
     */
    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
                  ATTR_TYPE_STR, MAIL_ATTR_TABLE, request_map,
                  ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request_flags,
                  ATTR_TYPE_STR, MAIL_ATTR_KEY, request_key,
                  ATTR_TYPE_END) != 3) {
        reply_status = PROXY_STAT_BAD;
        reply_value = "";
    } else if ((dict = proxy_map_find(STR(request_map), request_flags,
                                      &reply_status)) == 0) {
        reply_value = "";
    } else if (dict->flags = ((dict->flags & ~DICT_FLAG_RQST_MASK)
                              | (request_flags & DICT_FLAG_RQST_MASK)),
               (reply_value = dict_get(dict, STR(request_key))) != 0) {
        reply_status = PROXY_STAT_OK;
    } else if (dict->error == 0) {
        reply_status = PROXY_STAT_NOKEY;
        reply_value = "";
    } else {
        reply_status = PROXY_STAT_RETRY;
        reply_value = "";
    }

    /*
     * Respond to the client.
     */
    attr_print(client_stream, ATTR_FLAG_NONE,
               ATTR_TYPE_INT, MAIL_ATTR_STATUS, reply_status,
               ATTR_TYPE_STR, MAIL_ATTR_VALUE, reply_value,
               ATTR_TYPE_END);
}
Exemple #15
0
int     verify_clnt_query(const char *addr, int *addr_status, VSTRING *why)
{
    VSTREAM *stream;
    int     request_status;
    int     count = 0;

    /*
     * Do client-server plumbing.
     */
    if (vrfy_clnt == 0)
	verify_clnt_init();

    /*
     * Request status for this address.
     */
    for (;;) {
	stream = clnt_stream_access(vrfy_clnt);
	errno = 0;
	count += 1;
	if (attr_print(stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_STR, MAIL_ATTR_REQ, VRFY_REQ_QUERY,
		       ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
		       ATTR_TYPE_END) != 0
	    || vstream_fflush(stream)
	    || attr_scan(stream, ATTR_FLAG_MISSING,
			 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &request_status,
			 ATTR_TYPE_INT, MAIL_ATTR_ADDR_STATUS, addr_status,
			 ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
			 ATTR_TYPE_END) != 3) {
	    if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
		msg_warn("problem talking to service %s: %m",
			 var_verify_service);
	} else {
	    break;
	}
	sleep(1);
	clnt_stream_recover(vrfy_clnt);
    }
    return (request_status);
}
Exemple #16
0
int     verify_clnt_update(const char *addr, int addr_status, const char *why)
{
    VSTREAM *stream;
    int     request_status;

    /*
     * Do client-server plumbing.
     */
    if (vrfy_clnt == 0)
	verify_clnt_init();

    /*
     * Send status for this address. Supply a default status if the address
     * verification service is unavailable.
     */
    for (;;) {
	stream = clnt_stream_access(vrfy_clnt);
	errno = 0;
	if (attr_print(stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_STR, MAIL_ATTR_REQ, VRFY_REQ_UPDATE,
		       ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
		       ATTR_TYPE_INT, MAIL_ATTR_ADDR_STATUS, addr_status,
		       ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
		       ATTR_TYPE_END) != 0
	    || attr_scan(stream, ATTR_FLAG_MISSING,
			 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &request_status,
			 ATTR_TYPE_END) != 1) {
	    if (msg_verbose || (errno != EPIPE && errno != ENOENT))
		msg_warn("problem talking to service %s: %m",
			 var_verify_service);
	} else {
	    break;
	}
	sleep(1);
	clnt_stream_recover(vrfy_clnt);
    }
    return (request_status);
}
Exemple #17
0
static void tlsp_get_request_event(int event, void *context)
{
    const char *myname = "tlsp_get_request_event";
    TLSP_STATE *state = (TLSP_STATE *) context;
    VSTREAM *plaintext_stream = state->plaintext_stream;
    int     plaintext_fd = vstream_fileno(plaintext_stream);
    static VSTRING *remote_endpt;
    static VSTRING *server_id;
    int     req_flags;
    int     timeout;
    int     ready;

    /*
     * One-time initialization.
     */
    if (remote_endpt == 0) {
        remote_endpt = vstring_alloc(10);
        server_id = vstring_alloc(10);
    }

    /*
     * At this point we still manually manage plaintext read/write/timeout
     * events. Turn off timer events. Below we disable read events on error,
     * and redefine read events on success.
     */
    if (event != EVENT_TIME)
        event_cancel_timer(tlsp_get_request_event, (void *) state);
    else
        errno = ETIMEDOUT;

    /*
     * We must send some data, after receiving the request attributes and
     * before receiving the remote file descriptor. We can't assume
     * UNIX-domain socket semantics here.
     */
    if (event != EVENT_READ
            || attr_scan(plaintext_stream, ATTR_FLAG_STRICT,
                         RECV_ATTR_STR(MAIL_ATTR_REMOTE_ENDPT, remote_endpt),
                         RECV_ATTR_INT(MAIL_ATTR_FLAGS, &req_flags),
                         RECV_ATTR_INT(MAIL_ATTR_TIMEOUT, &timeout),
                         RECV_ATTR_STR(MAIL_ATTR_SERVER_ID, server_id),
                         ATTR_TYPE_END) != 4) {
        msg_warn("%s: receive request attributes: %m", myname);
        event_disable_readwrite(plaintext_fd);
        tlsp_state_free(state);
        return;
    }

    /*
     * If the requested TLS engine is unavailable, hang up after making sure
     * that the plaintext peer has received our "sorry" indication.
     */
    ready = ((req_flags & TLS_PROXY_FLAG_ROLE_SERVER) != 0
             && tlsp_server_ctx != 0);
    if (attr_print(plaintext_stream, ATTR_FLAG_NONE,
                   SEND_ATTR_INT(MAIL_ATTR_STATUS, ready),
                   ATTR_TYPE_END) != 0
            || vstream_fflush(plaintext_stream) != 0
            || ready == 0) {
        read_wait(plaintext_fd, TLSP_INIT_TIMEOUT);	/* XXX */
        event_disable_readwrite(plaintext_fd);
        tlsp_state_free(state);
        return;
    }

    /*
     * XXX We use the same fixed timeout throughout the entire session for
     * both plaintext and ciphertext communication. This timeout is just a
     * safety feature; the real timeout will be enforced by our plaintext
     * peer.
     */
    else {
        state->remote_endpt = mystrdup(STR(remote_endpt));
        state->server_id = mystrdup(STR(server_id));
        msg_info("CONNECT %s %s",
                 (req_flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "from" :
                 (req_flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "to" :
                 "(bogus_direction)", state->remote_endpt);
        state->req_flags = req_flags;
        state->timeout = timeout + 10;		/* XXX */
        event_enable_read(plaintext_fd, tlsp_get_fd_event, (void *) state);
        event_request_timer(tlsp_get_fd_event, (void *) state,
                            TLSP_INIT_TIMEOUT);
        return;
    }
}
Exemple #18
0
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);
}
Exemple #19
0
static void tlsp_strategy(TLSP_STATE *state)
{
    TLS_SESS_STATE *tls_context = state->tls_context;
    NBBIO  *plaintext_buf;
    int     ssl_stat;
    int     ssl_read_err;
    int     ssl_write_err;
    int     handshake_err;

    /*
     * Be sure to complete the TLS handshake before enabling plain-text I/O.
     * In case of an unrecoverable error, this automagically cleans up all
     * pending read/write and timeout event requests.
     */
    if (state->flags & TLSP_FLAG_DO_HANDSHAKE) {
        ssl_stat = SSL_accept(tls_context->con);
        if (ssl_stat != 1) {
            handshake_err = SSL_get_error(tls_context->con, ssl_stat);
            tlsp_eval_tls_error(state, handshake_err);
            /* At this point, state could be a dangling pointer. */
            return;
        }
        if ((state->tls_context = tls_server_post_accept(tls_context)) == 0) {
            tlsp_state_free(state);
            return;
        }
        if ((state->req_flags & TLS_PROXY_FLAG_SEND_CONTEXT) != 0
                && (attr_print(state->plaintext_stream, ATTR_FLAG_NONE,
                               SEND_ATTR_FUNC(tls_proxy_context_print,
                                              (void *) state->tls_context),
                               ATTR_TYPE_END) != 0
                    || vstream_fflush(state->plaintext_stream) != 0)) {
            msg_warn("cannot send TLS context: %m");
            tlsp_state_free(state);
            return;
        }
        state->flags &= ~TLSP_FLAG_DO_HANDSHAKE;
    }

    /*
     * Shutdown and self-destruct after NBBIO error. This automagically
     * cleans up all pending read/write and timeout event requests. Before
     * shutting down TLS, we stop all plain-text I/O events but keep the
     * NBBIO error flags.
     */
    plaintext_buf = state->plaintext_buf;
    if (NBBIO_ERROR_FLAGS(plaintext_buf)) {
        if (NBBIO_ACTIVE_FLAGS(plaintext_buf))
            nbbio_disable_readwrite(state->plaintext_buf);
        ssl_stat = SSL_shutdown(tls_context->con);
        /* XXX Wait for return value 1 if sessions are to be reused? */
        if (ssl_stat < 0) {
            handshake_err = SSL_get_error(tls_context->con, ssl_stat);
            tlsp_eval_tls_error(state, handshake_err);
            /* At this point, state could be a dangling pointer. */
            return;
        }
        tlsp_state_free(state);
        return;
    }

    /*
     * Try to move data from the plaintext input buffer to the TLS engine.
     *
     * XXX We're supposed to repeat the exact same SSL_write() call arguments
     * after an SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE result. Rumor has
     * it that this is because each SSL_write() call reads from the buffer
     * incrementally, and returns > 0 only after the final byte is processed.
     * Rumor also has it that setting SSL_MODE_ENABLE_PARTIAL_WRITE and
     * SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER voids this requirement, and that
     * repeating the request with an increased request size is OK.
     * Unfortunately all this is not or poorly documented, and one has to
     * rely on statements from OpenSSL developers in public mailing archives.
     */
    ssl_write_err = SSL_ERROR_NONE;
    while (NBBIO_READ_PEND(plaintext_buf) > 0) {
        ssl_stat = SSL_write(tls_context->con, NBBIO_READ_BUF(plaintext_buf),
                             NBBIO_READ_PEND(plaintext_buf));
        ssl_write_err = SSL_get_error(tls_context->con, ssl_stat);
        if (ssl_write_err != SSL_ERROR_NONE)
            break;
        /* Allow the plaintext pseudothread to read more data. */
        NBBIO_READ_PEND(plaintext_buf) -= ssl_stat;
        if (NBBIO_READ_PEND(plaintext_buf) > 0)
            memmove(NBBIO_READ_BUF(plaintext_buf),
                    NBBIO_READ_BUF(plaintext_buf) + ssl_stat,
                    NBBIO_READ_PEND(plaintext_buf));
    }

    /*
     * Try to move data from the TLS engine to the plaintext output buffer.
     * Note: data may arrive as a side effect of calling SSL_write(),
     * therefore we call SSL_read() after calling SSL_write().
     *
     * XXX We're supposed to repeat the exact same SSL_read() call arguments
     * after an SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE result. This
     * supposedly means that our plaintext writer must not memmove() the
     * plaintext output buffer until after the SSL_read() call succeeds. For
     * now I'll ignore this, because 1) SSL_read() is documented to return
     * the bytes available, instead of returning > 0 only after the entire
     * buffer is processed like SSL_write() does; and 2) there is no "read"
     * equivalent of the SSL_R_BAD_WRITE_RETRY, SSL_MODE_ENABLE_PARTIAL_WRITE
     * or SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER features.
     */
    ssl_read_err = SSL_ERROR_NONE;
    while (NBBIO_WRITE_PEND(state->plaintext_buf) < NBBIO_BUFSIZE(plaintext_buf)) {
        ssl_stat = SSL_read(tls_context->con,
                            NBBIO_WRITE_BUF(plaintext_buf)
                            + NBBIO_WRITE_PEND(state->plaintext_buf),
                            NBBIO_BUFSIZE(plaintext_buf)
                            - NBBIO_WRITE_PEND(state->plaintext_buf));
        ssl_read_err = SSL_get_error(tls_context->con, ssl_stat);
        if (ssl_read_err != SSL_ERROR_NONE)
            break;
        NBBIO_WRITE_PEND(plaintext_buf) += ssl_stat;
    }

    /*
     * Try to enable/disable ciphertext read/write events. If SSL_write() was
     * satisfied, see if SSL_read() wants to do some work. In case of an
     * unrecoverable error, this automagically destroys the session state
     * after cleaning up all pending read/write and timeout event requests.
     */
    if (tlsp_eval_tls_error(state, ssl_write_err != SSL_ERROR_NONE ?
                            ssl_write_err : ssl_read_err) < 0)
        return;

    /*
     * Try to enable/disable plaintext read/write events. Basically, if we
     * have nothing to write to the postscreen(8) server, see if there is
     * something to read. If the write buffer is empty and the read buffer is
     * full, suspend plaintext I/O until conditions change (but keep the
     * timer active, as a safety mechanism in case ciphertext I/O gets
     * stuck).
     *
     * XXX In theory, if the client keeps writing fast enough then we would
     * never read from postscreen(8), and cause postscreen(8) to block. In
     * practice, postscreen(8) limits the number of client commands, and thus
     * postscreen(8)'s output will fit in a kernel buffer. This may not be
     * true in other scenarios where the tlsproxy(8) server could be used.
     */
    if (NBBIO_WRITE_PEND(plaintext_buf) > 0) {
        if (NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_READ)
            nbbio_disable_readwrite(plaintext_buf);
        if ((NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_WRITE) == 0)
            nbbio_enable_write(plaintext_buf, state->timeout);
    } else if (NBBIO_READ_PEND(plaintext_buf) < NBBIO_BUFSIZE(plaintext_buf)) {
        if (NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_WRITE)
            nbbio_disable_readwrite(plaintext_buf);
        if ((NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_READ) == 0)
            nbbio_enable_read(plaintext_buf, state->timeout);
    } else {
        if (NBBIO_ACTIVE_FLAGS(plaintext_buf))
            nbbio_slumber(plaintext_buf, state->timeout);
    }
}
Exemple #20
0
static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
{
    QMGR_RCPT_LIST list = entry->rcpt_list;
    QMGR_RCPT *recipient;
    QMGR_MESSAGE *message = entry->message;
    VSTRING *sender_buf = 0;
    char   *sender;
    int     flags;

    /*
     * If variable envelope return path is requested, change prefix+@origin
     * into prefix+user=domain@origin. Note that with VERP there is only one
     * recipient per delivery.
     */
    if (message->verp_delims == 0) {
	sender = message->sender;
    } else {
	sender_buf = vstring_alloc(100);
	verp_sender(sender_buf, message->verp_delims,
		    message->sender, list.info->address);
	sender = vstring_str(sender_buf);
    }

    flags = message->tflags
	| (message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT);
    attr_print(stream, ATTR_FLAG_MORE,
	       ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
	       ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
	       ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
	       ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, message->data_offset,
	       ATTR_TYPE_LONG, MAIL_ATTR_SIZE, message->data_size,
	       ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, entry->queue->nexthop,
	       ATTR_TYPE_STR, MAIL_ATTR_ENCODING, message->encoding,
	       ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
	       ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
	       ATTR_TYPE_STR, MAIL_ATTR_RRCPT, message->return_receipt,
	       ATTR_TYPE_LONG, MAIL_ATTR_TIME, message->arrival_time,
	       ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, message->client_name,
	       ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, message->client_addr,
	       ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, message->client_proto,
	       ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, message->client_helo,
	       ATTR_TYPE_END);
    if (sender_buf != 0)
	vstring_free(sender_buf);
    for (recipient = list.info; recipient < list.info + list.len; recipient++)
	attr_print(stream, ATTR_FLAG_MORE,
		   ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
		   ATTR_TYPE_STR, MAIL_ATTR_ORCPT, recipient->orig_rcpt,
		   ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
		   ATTR_TYPE_END);
    attr_print(stream, ATTR_FLAG_NONE,
	       ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
	       ATTR_TYPE_END);
    if (vstream_fflush(stream) != 0) {
	msg_warn("write to process (%s): %m", entry->queue->transport->name);
	return (-1);
    } else {
	if (msg_verbose)
	    msg_info("qmgr_deliver: site `%s'", entry->queue->name);
	return (0);
    }
}
Exemple #21
0
static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
			           char **argv)
{
    static VSTRING *request = 0;
    static VSTRING *cache_type = 0;
    static VSTRING *cache_id = 0;
    static VSTRING *buffer = 0;
    int     len;
    static char wakeup[] = {		/* master wakeup request */
	TRIGGER_REQ_WAKEUP,
	0,
    };
    TLSMGR_SCACHE *ent;
    int     status = TLS_MGR_STAT_FAIL;

    /*
     * Sanity check. This service takes no command-line arguments.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);

    /*
     * Initialize. We're select threaded, so we can use static buffers.
     */
    if (request == 0) {
	request = vstring_alloc(10);
	cache_type = vstring_alloc(10);
	cache_id = vstring_alloc(10);
	buffer = vstring_alloc(10);
    }

    /*
     * This routine runs whenever a client connects to the socket dedicated
     * to the tlsmgr service (including wake up events sent by the master).
     * All connection-management stuff is handled by the common code in
     * multi_server.c.
     */
    if (tlsmgr_request_receive(client_stream, request) == 0) {

	/*
	 * Load session from cache.
	 */
	if (STREQ(STR(request), TLS_MGR_REQ_LOOKUP)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
			  ATTR_TYPE_END) == 2) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_LOOKUP);
		    VSTRING_RESET(buffer);
		} else if (ent->cache_info == 0) {

		    /*
		     * Cache type valid, but not enabled
		     */
		    VSTRING_RESET(buffer);
		} else {
		    status = tls_scache_lookup(ent->cache_info,
					       STR(cache_id), buffer) ?
			TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION,
		       LEN(buffer), STR(buffer),
		       ATTR_TYPE_END);
	}

	/*
	 * Save session to cache.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_UPDATE)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
			  ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, buffer,
			  ATTR_TYPE_END) == 3) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_UPDATE);
		} else if (ent->cache_info != 0) {
		    status =
			tls_scache_update(ent->cache_info, STR(cache_id),
					  STR(buffer), LEN(buffer)) ?
			TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_END);
	}

	/*
	 * Delete session from cache.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_DELETE)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
			  ATTR_TYPE_END) == 2) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_DELETE);
		} else if (ent->cache_info != 0) {
		    status = tls_scache_delete(ent->cache_info,
					       STR(cache_id)) ?
			TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_END);
	}

	/*
	 * RFC 5077 TLS session ticket keys
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_TKTKEY)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYNAME, buffer,
			  ATTR_TYPE_END) == 1) {
		if (LEN(buffer) != 0 && LEN(buffer) != TLS_TICKET_NAMELEN) {
		    msg_warn("invalid session ticket key name length: %ld",
			     (long) LEN(buffer));
		    VSTRING_RESET(buffer);
		} else if (*smtpd_cache.cache_timeout <= 0) {
		    status = TLS_MGR_STAT_ERR;
		    VSTRING_RESET(buffer);
		} else {
		    status = tlsmgr_key(buffer, *smtpd_cache.cache_timeout);
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYBUF,
		       LEN(buffer), STR(buffer),
		       ATTR_TYPE_END);
	}

	/*
	 * Entropy request.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_SEED)) {
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_INT, TLS_MGR_ATTR_SIZE, &len,
			  ATTR_TYPE_END) == 1) {
		VSTRING_RESET(buffer);
		if (len <= 0 || len > 255) {
		    msg_warn("bogus seed length \"%d\" in \"%s\" request",
			     len, TLS_MGR_REQ_SEED);
		} else {
		    VSTRING_SPACE(buffer, len);
		    RAND_bytes((unsigned char *) STR(buffer), len);
		    VSTRING_AT_OFFSET(buffer, len);	/* XXX not part of the
							 * official interface */
		    status = TLS_MGR_STAT_OK;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_DATA, TLS_MGR_ATTR_SEED,
		       LEN(buffer), STR(buffer),
		       ATTR_TYPE_END);
	}

	/*
	 * Caching policy request.
	 */
	else if (STREQ(STR(request), TLS_MGR_REQ_POLICY)) {
	    int     cachable = 0;
	    int     timeout = 0;

	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
			  ATTR_TYPE_END) == 1) {
		for (ent = cache_table; ent->cache_label; ++ent)
		    if (strcmp(ent->cache_label, STR(cache_type)) == 0)
			break;
		if (ent->cache_label == 0) {
		    msg_warn("bogus cache type \"%s\" in \"%s\" request",
			     STR(cache_type), TLS_MGR_REQ_POLICY);
		} else {
		    cachable = (ent->cache_info != 0) ? 1 : 0;
		    timeout = *ent->cache_timeout;
		    status = TLS_MGR_STAT_OK;
		}
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
		       ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable,
		       ATTR_TYPE_INT, TLS_MGR_ATTR_SESSTOUT, timeout,
		       ATTR_TYPE_END);
	}

	/*
	 * Master trigger. Normally, these triggers arrive only after some
	 * other process requested the tlsmgr's service. The purpose is to
	 * restart the tlsmgr after it aborted due to a fatal run-time error,
	 * so that it can continue its housekeeping even while nothing is
	 * using TLS.
	 * 
	 * XXX Which begs the question, if TLS isn't used often, do we need a
	 * tlsmgr background process? It could terminate when the session
	 * caches are empty.
	 */
	else if (STREQ(STR(request), wakeup)) {
	    if (msg_verbose)
		msg_info("received master trigger");
	    multi_server_disconnect(client_stream);
	    return;				/* NOT: vstream_fflush */
	}
    }

    /*
     * Protocol error.
     */
    else {
	attr_print(client_stream, ATTR_FLAG_NONE,
		   ATTR_TYPE_INT, MAIL_ATTR_STATUS, TLS_MGR_STAT_FAIL,
		   ATTR_TYPE_END);
    }
    vstream_fflush(client_stream);
}
Exemple #22
0
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));
}
Exemple #23
0
static void flush_service(VSTREAM *client_stream, char *unused_service,
			          char **argv)
{
    VSTRING *request = vstring_alloc(10);
    VSTRING *site = 0;
    VSTRING *queue_id = 0;
    static char wakeup[] = {		/* master wakeup request */
	TRIGGER_REQ_WAKEUP,
	0,
    };
    int     status = FLUSH_STAT_BAD;

    /*
     * Sanity check. This service takes no command-line arguments.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);

    /*
     * This routine runs whenever a client connects to the UNIX-domain socket
     * dedicated to the fast flush service. What we see below is a little
     * protocol to (1) read a request from the client (the name of the site)
     * and (2) acknowledge that we have received the request.
     * 
     * All connection-management stuff is handled by the common code in
     * single_server.c.
     */
    if (flush_request_receive(client_stream, request) == 0) {
	if (STREQ(STR(request), FLUSH_REQ_ADD)) {
	    site = vstring_alloc(10);
	    queue_id = vstring_alloc(10);
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  RECV_ATTR_STR(MAIL_ATTR_SITE, site),
			  RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
			  ATTR_TYPE_END) == 2
		&& mail_queue_id_ok(STR(queue_id)))
		status = flush_add_service(STR(site), STR(queue_id));
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
		       ATTR_TYPE_END);
	} else if (STREQ(STR(request), FLUSH_REQ_SEND_SITE)) {
	    site = vstring_alloc(10);
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  RECV_ATTR_STR(MAIL_ATTR_SITE, site),
			  ATTR_TYPE_END) == 1)
		status = flush_send_service(STR(site), UNTHROTTLE_BEFORE);
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
		       ATTR_TYPE_END);
	} else if (STREQ(STR(request), FLUSH_REQ_SEND_FILE)) {
	    queue_id = vstring_alloc(10);
	    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
			  RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
			  ATTR_TYPE_END) == 1)
		status = flush_send_file_service(STR(queue_id));
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
		       ATTR_TYPE_END);
	} else if (STREQ(STR(request), FLUSH_REQ_REFRESH)
		   || STREQ(STR(request), wakeup)) {
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, FLUSH_STAT_OK),
		       ATTR_TYPE_END);
	    vstream_fflush(client_stream);
	    (void) flush_refresh_service(var_fflush_refresh);
	} else if (STREQ(STR(request), FLUSH_REQ_PURGE)) {
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, FLUSH_STAT_OK),
		       ATTR_TYPE_END);
	    vstream_fflush(client_stream);
	    (void) flush_refresh_service(0);
	}
    } else
	attr_print(client_stream, ATTR_FLAG_NONE,
		   SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
		   ATTR_TYPE_END);
    vstring_free(request);
    if (site)
	vstring_free(site);
    if (queue_id)
	vstring_free(queue_id);
}
Exemple #24
0
MILTERS *milter_receive(VSTREAM *stream, int count)
{
    MILTERS *milters;
    MILTER *head = 0;
    MILTER *tail = 0;
    MILTER *milter = 0;

    if (msg_verbose)
	msg_info("receive %d milters", count);

    /*
     * XXX We must instantiate a MILTERS structure even when the sender has
     * no active filters, otherwise the cleanup server would try to use its
     * own non_smtpd_milters settings.
     */
#define NO_MILTERS	((char *) 0)
#define NO_TIMEOUTS	0, 0, 0
#define NO_PROTOCOL	((char *) 0)
#define NO_ACTION	((char *) 0)
#define NO_MACROS	((MILTER_MACROS *) 0)

    milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION,
			 NO_MACROS);

    /*
     * XXX Optimization: don't send or receive further information when there
     * aren't any active filters.
     */
    if (count <= 0)
	return (milters);

    /*
     * Receive the global macro name lists.
     */
    milters->macros = milter_macros_alloc(MILTER_MACROS_ALLOC_ZERO);
    if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
		  ATTR_TYPE_FUNC, milter_macros_scan,
		  (void *) milters->macros,
		  ATTR_TYPE_END) != 1) {
	milter_free(milters);
	return (0);
    }

    /*
     * Receive the filters.
     */
    for (; count > 0; count--) {
	if ((milter = milter8_receive(stream, milters)) == 0) {
	    msg_warn("cannot receive milters via service %s socket",
		     VSTREAM_PATH(stream));
	    milter_free(milters);
	    return (0);
	}
	if (head == 0) {
	    /* Coverity: milter_free() depends on milters->milter_list. */
	    milters->milter_list = head = milter;
	} else {
	    tail->next = milter;
	}
	tail = milter;
    }

    /*
     * Over to you.
     */
    (void) attr_print(stream, ATTR_FLAG_NONE,
		      ATTR_TYPE_INT, MAIL_ATTR_STATUS, 0,
		      ATTR_TYPE_END);
    return (milters);
}
Exemple #25
0
int     milter_send(MILTERS *milters, VSTREAM *stream)
{
    MILTER *m;
    int     status = 0;
    int     count = 0;

    /*
     * XXX Optimization: send only the filters that are actually used in the
     * remote process. No point sending a filter that looks at HELO commands
     * to a cleanup server. For now we skip only the filters that are known
     * to be disabled (either in global error state or in global accept
     * state).
     * 
     * XXX We must send *some* information, even when there are no active
     * filters, otherwise the cleanup server would try to apply its own
     * non_smtpd_milters settings.
     */
    if (milters != 0)
	for (m = milters->milter_list; m != 0; m = m->next)
	    if (m->active(m))
		count++;
    (void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count);

    if (msg_verbose)
	msg_info("send %d milters", count);

    /*
     * XXX Optimization: don't send or receive further information when there
     * aren't any active filters.
     */
    if (count <= 0)
	return (0);

    /*
     * Send the filter macro name lists.
     */
    (void) attr_print(stream, ATTR_FLAG_MORE,
		      ATTR_TYPE_FUNC, milter_macros_print,
		      (void *) milters->macros,
		      ATTR_TYPE_END);

    /*
     * Send the filter instances.
     */
    for (m = milters->milter_list; m != 0; m = m->next)
	if (m->active(m) && (status = m->send(m, stream)) != 0)
	    break;

    /*
     * Over to you.
     */
    if (status != 0
	|| attr_scan(stream, ATTR_FLAG_STRICT,
		     ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
		     ATTR_TYPE_END) != 1
	|| status != 0) {
	msg_warn("cannot send milters to service %s", VSTREAM_PATH(stream));
	return (-1);
    }
    return (0);
}
Exemple #26
0
static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
{
    RECIPIENT_LIST list = entry->rcpt_list;
    RECIPIENT *recipient;
    QMGR_MESSAGE *message = entry->message;
    VSTRING *sender_buf = 0;
    MSG_STATS stats;
    char   *sender;
    int     flags;

    /*
     * If variable envelope return path is requested, change prefix+@origin
     * into prefix+user=domain@origin. Note that with VERP there is only one
     * recipient per delivery.
     */
    if (message->verp_delims == 0) {
	sender = message->sender;
    } else {
	sender_buf = vstring_alloc(100);
	verp_sender(sender_buf, message->verp_delims,
		    message->sender, list.info);
	sender = vstring_str(sender_buf);
    }

    flags = message->tflags
	| entry->queue->dflags
	| (message->inspect_xport ? DEL_REQ_FLAG_BOUNCE : DEL_REQ_FLAG_DEFLT);
    (void) QMGR_MSG_STATS(&stats, message);
    attr_print(stream, ATTR_FLAG_NONE,
	       ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
	       ATTR_TYPE_STR, MAIL_ATTR_QUEUE, message->queue_name,
	       ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, message->queue_id,
	       ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, message->data_offset,
	       ATTR_TYPE_LONG, MAIL_ATTR_SIZE, message->cont_length,
	       ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, entry->queue->nexthop,
	       ATTR_TYPE_STR, MAIL_ATTR_ENCODING, message->encoding,
	       ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
	       ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, message->dsn_envid,
	       ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, message->dsn_ret,
	       ATTR_TYPE_FUNC, msg_stats_print, (void *) &stats,
    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
	     ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, message->client_name,
	     ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, message->client_addr,
	     ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, message->client_port,
	     ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, message->client_proto,
	       ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, message->client_helo,
    /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */
	       ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, message->sasl_method,
	     ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, message->sasl_username,
	       ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, message->sasl_sender,
    /* XXX Ditto if we want to pass TLS certificate info. */
	       ATTR_TYPE_STR, MAIL_ATTR_LOG_IDENT, message->log_ident,
	     ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, message->rewrite_context,
	       ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, list.len,
	       ATTR_TYPE_END);
    if (sender_buf != 0)
	vstring_free(sender_buf);
    for (recipient = list.info; recipient < list.info + list.len; recipient++)
	attr_print(stream, ATTR_FLAG_NONE,
		   ATTR_TYPE_FUNC, rcpt_print, (void *) recipient,
		   ATTR_TYPE_END);
    if (vstream_fflush(stream) != 0) {
	msg_warn("write to process (%s): %m", entry->queue->transport->name);
	return (-1);
    } else {
	if (msg_verbose)
	    msg_info("qmgr_deliver: site `%s'", entry->queue->name);
	return (0);
    }
}
Exemple #27
0
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);
}
Exemple #28
0
static void bounce_service(VSTREAM *client, char *service_name, char **argv)
{
    int     command;
    int     status;

    /*
     * Sanity check. This service takes no command-line arguments. The
     * service name should be usable as a subdirectory name.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);
    if (mail_queue_name_ok(service_name) == 0)
	msg_fatal("malformed service name: %s", service_name);

    /*
     * Read and validate the first parameter of the client request. Let the
     * request-specific protocol routines take care of the remainder.
     */
    if (attr_scan(client, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
		  ATTR_TYPE_NUM, MAIL_ATTR_NREQ, &command, 0) != 1) {
	msg_warn("malformed request");
	status = -1;
    } else if (command == BOUNCE_CMD_VERP) {
	status = bounce_verp_proto(service_name, client);
    } else if (command == BOUNCE_CMD_FLUSH) {
	status = bounce_notify_proto(service_name, client,
				     bounce_notify_service);
    } else if (command == BOUNCE_CMD_WARN) {
	status = bounce_notify_proto(service_name, client,
				     bounce_warn_service);
    } else if (command == BOUNCE_CMD_TRACE) {
	status = bounce_notify_proto(service_name, client,
				     bounce_trace_service);
    } else if (command == BOUNCE_CMD_APPEND) {
	status = bounce_append_proto(service_name, client);
    } else if (command == BOUNCE_CMD_ONE) {
	status = bounce_one_proto(service_name, client);
    } else {
	msg_warn("unknown command: %d", command);
	status = -1;
    }

    /*
     * When the request has completed, send the completion status to the
     * client.
     */
    attr_print(client, ATTR_FLAG_NONE,
	       ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
	       ATTR_TYPE_END);
    vstream_fflush(client);

    /*
     * When a cleanup trap was set, delete the log file in case of error.
     * This includes errors while sending the completion status to the
     * client.
     */
    if (bounce_cleanup_path) {
	if (status || vstream_ferror(client))
	    bounce_cleanup_log();
	bounce_cleanup_unregister();
    }
}
Exemple #29
0
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,
	       ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, state->queue_id,
	       ATTR_TYPE_END);
    if (attr_scan(src, ATTR_FLAG_STRICT,
		  ATTR_TYPE_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(src, buf, 0)) > 0)
	     /* void */ ;
    }

    /*
     * 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,
	       ATTR_TYPE_INT, MAIL_ATTR_STATUS, status,
	       ATTR_TYPE_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);
}
Exemple #30
0
VSTREAM *tls_proxy_open(const char *service, int flags,
			        VSTREAM *peer_stream,
			        const char *peer_addr,
			        const char *peer_port,
			        int timeout)
{
    VSTREAM *tlsproxy_stream;
    int     status;
    int     fd;
    static VSTRING *tlsproxy_service = 0;
    static VSTRING *remote_endpt = 0;

    /*
     * Initialize.
     */
    if (tlsproxy_service == 0) {
	tlsproxy_service = vstring_alloc(20);
	remote_endpt = vstring_alloc(20);
    }

    /*
     * Connect to the tlsproxy(8) daemon.
     */
    vstring_sprintf(tlsproxy_service, "%s/%s", MAIL_CLASS_PRIVATE, service);
    if ((fd = LOCAL_CONNECT(STR(tlsproxy_service), BLOCKING,
			    TLSPROXY_INIT_TIMEOUT)) < 0) {
	msg_warn("connect to %s service: %m", STR(tlsproxy_service));
	return (0);
    }

    /*
     * Initial handshake. Send the data attributes now, and send the client
     * file descriptor in a later transaction.
     * 
     * XXX The formatted endpoint should be a state member. Then, we can
     * simplify all the format strings throughout the program.
     */
    tlsproxy_stream = vstream_fdopen(fd, O_RDWR);
    vstring_sprintf(remote_endpt, "[%s]:%s", peer_addr, peer_port);
    attr_print(tlsproxy_stream, ATTR_FLAG_NONE,
	       ATTR_TYPE_STR, MAIL_ATTR_REMOTE_ENDPT, STR(remote_endpt),
	       ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
	       ATTR_TYPE_INT, MAIL_ATTR_TIMEOUT, timeout,
	       ATTR_TYPE_END);
    if (vstream_fflush(tlsproxy_stream) != 0) {
	msg_warn("error sending request to %s service: %m",
		 STR(tlsproxy_service));
	vstream_fclose(tlsproxy_stream);
	return (0);
    }

    /*
     * Receive the "TLS is available" indication.
     * 
     * This may seem out of order, but we must have a read transaction between
     * sending the request attributes and sending the SMTP client file
     * descriptor. We can't assume UNIX-domain socket semantics here.
     */
    if (attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT,
		  ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
		  ATTR_TYPE_END) != 1 || status == 0) {

	/*
	 * The TLS proxy reports that the TLS engine is not available (due to
	 * configuration error, or other causes).
	 */
	msg_warn("%s service role \"%s\" is not available",
		 STR(tlsproxy_service),
		 (flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "server" :
		 (flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "client" :
		 "bogus role");
	vstream_fclose(tlsproxy_stream);
	return (0);
    }

    /*
     * Send the remote SMTP client file descriptor.
     */
    if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream),
		      vstream_fileno(peer_stream)) < 0) {

	/*
	 * Some error: drop the TLS proxy stream.
	 */
	msg_warn("sending file handle to %s service: %m",
		 STR(tlsproxy_service));
	vstream_fclose(tlsproxy_stream);
	return (0);
    }
    return (tlsproxy_stream);
}