Beispiel #1
0
static void multi_server_execute(int unused_event, char *context)
{
    VSTREAM *stream = (VSTREAM *) context;
    HTABLE *attr = (vstream_flags(stream) == multi_server_saved_flags ?
		    (HTABLE *) vstream_context(stream) : 0);

    if (multi_server_lock != 0
	&& myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
		   MYFLOCK_OP_NONE) < 0)
	msg_fatal("select unlock: %m");

    /*
     * Do not bother the application when the client disconnected. Don't drop
     * the already accepted client request after "postfix reload"; that would
     * be rude.
     */
    if (peekfd(vstream_fileno(stream)) > 0) {
	if (master_notify(var_pid, multi_server_generation, MASTER_STAT_TAKEN) < 0)
	     /* void */ ;
	multi_server_service(stream, multi_server_name, multi_server_argv);
	if (master_notify(var_pid, multi_server_generation, MASTER_STAT_AVAIL) < 0)
	    multi_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
    } else {
	multi_server_disconnect(stream);
    }
    if (attr)
	htable_free(attr, myfree);
}
Beispiel #2
0
static void rewrite_service(VSTREAM *stream, char *unused_service, char **argv)
{
    int     status = -1;

#ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT
    static time_t last;
    time_t  now;
    const char *table;

#endif

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

    /*
     * Client connections are long-lived. Be sure to refesh timely.
     */
#ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT
    if (server_flags == 0 && (now = event_time()) - last > 10) {
	if ((table = dict_changed_name()) != 0) {
	    msg_info("table %s has changed -- restarting", table);
	    if (multi_server_drain() == 0)
		server_flags = 1;
	}
	last = now;
    }
#endif

    /*
     * This routine runs whenever a client connects to the UNIX-domain socket
     * dedicated to address rewriting. All connection-management stuff is
     * handled by the common code in multi_server.c.
     */
    if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
		  ATTR_TYPE_STR, MAIL_ATTR_REQ, command,
		  ATTR_TYPE_END) == 1) {
	if (strcmp(vstring_str(command), REWRITE_ADDR) == 0) {
	    status = rewrite_proto(stream);
	} else if (strcmp(vstring_str(command), RESOLVE_REGULAR) == 0) {
	    status = resolve_proto(&resolve_regular, stream);
	} else if (strcmp(vstring_str(command), RESOLVE_VERIFY) == 0) {
	    status = resolve_proto(&resolve_verify, stream);
	} else {
	    msg_warn("bad command %.30s", printable(vstring_str(command), '?'));
	}
    }
    if (status < 0)
	multi_server_disconnect(stream);
}
Beispiel #3
0
static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv)
{
    static VSTRING *request;
    static VSTRING *ident;
    static const ANVIL_REQ_TABLE request_table[] = {
	ANVIL_REQ_CONN, anvil_remote_connect,
	ANVIL_REQ_MAIL, anvil_remote_mail,
	ANVIL_REQ_RCPT, anvil_remote_rcpt,
	ANVIL_REQ_NTLS, anvil_remote_newtls,
	ANVIL_REQ_DISC, anvil_remote_disconnect,
	ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat,
	ANVIL_REQ_LOOKUP, anvil_remote_lookup,
	0, 0,
    };
    const ANVIL_REQ_TABLE *rp;

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

    /*
     * Initialize.
     */
    if (request == 0) {
	request = vstring_alloc(10);
	ident = vstring_alloc(10);
    }

    /*
     * This routine runs whenever a client connects to the socket dedicated
     * to the client connection rate management service. All
     * connection-management stuff is handled by the common code in
     * multi_server.c.
     */
    if (msg_verbose)
	msg_info("--- start request ---");
    if (attr_scan_plain(client_stream,
			ATTR_FLAG_MISSING | ATTR_FLAG_STRICT,
			ATTR_TYPE_STR, ANVIL_ATTR_REQ, request,
			ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident,
			ATTR_TYPE_END) == 2) {
	for (rp = request_table; /* see below */ ; rp++) {
	    if (rp->name == 0) {
		msg_warn("unrecognized request: \"%s\", ignored", STR(request));
		attr_print_plain(client_stream, ATTR_FLAG_NONE,
			  ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL,
				 ATTR_TYPE_END);
		break;
	    }
	    if (STREQ(rp->name, STR(request))) {
		rp->action(client_stream, STR(ident));
		break;
	    }
	}
	vstream_fflush(client_stream);
    } else {
	/* Note: invokes anvil_service_done() */
	multi_server_disconnect(client_stream);
    }
    if (msg_verbose)
	msg_info("--- end request ---");
}
Beispiel #4
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);
}