Beispiel #1
0
void
mta_session_imsg(struct mproc *p, struct imsg *imsg)
{
	struct ca_vrfy_resp_msg	*resp_ca_vrfy;
	struct ca_cert_resp_msg	*resp_ca_cert;
	struct mta_session	*s;
	struct mta_host		*h;
	struct msg		 m;
	uint64_t		 reqid;
	const char		*name;
	void			*ssl;
	int			 dnserror, status;

	switch (imsg->hdr.type) {

	case IMSG_QUEUE_MESSAGE_FD:
		m_msg(&m, imsg);
		m_get_id(&m, &reqid);
		m_end(&m);

		s = mta_tree_pop(&wait_fd, reqid);
		if (s == NULL) {
			if (imsg->fd != -1)
				close(imsg->fd);
			return;
		}

		if (imsg->fd == -1) {
			log_debug("debug: mta: failed to obtain msg fd");
			mta_flush_task(s, IMSG_DELIVERY_TEMPFAIL,
			    "Could not get message fd", 0, 0);
			mta_enter_state(s, MTA_READY);
			io_reload(&s->io);
			return;
		}

		s->datafp = fdopen(imsg->fd, "r");
		if (s->datafp == NULL)
			fatal("mta: fdopen");

		if (mta_check_loop(s->datafp)) {
			log_debug("debug: mta: loop detected");
			fclose(s->datafp);
			s->datafp = NULL;
			mta_flush_task(s, IMSG_DELIVERY_LOOP,
			    "Loop detected", 0, 0);
			mta_enter_state(s, MTA_READY);
		} else {
			mta_enter_state(s, MTA_MAIL);
		}
		io_reload(&s->io);
		return;

	case IMSG_DNS_PTR:
		m_msg(&m, imsg);
		m_get_id(&m, &reqid);
		m_get_int(&m, &dnserror);
		if (dnserror)
			name = NULL;
		else
			m_get_string(&m, &name);
		m_end(&m);
		s = mta_tree_pop(&wait_ptr, reqid);
		if (s == NULL)
			return;

		h = s->route->dst;
		h->lastptrquery = time(NULL);
		if (name)
			h->ptrname = xstrdup(name, "mta: ptr");
		waitq_run(&h->ptrname, h->ptrname);
		return;

	case IMSG_LKA_SSL_INIT:
		resp_ca_cert = imsg->data;
		s = mta_tree_pop(&wait_ssl_init, resp_ca_cert->reqid);
		if (s == NULL)
			return;

		if (resp_ca_cert->status == CA_FAIL) {
			if (s->relay->pki_name) {
				log_info("smtp-out: Disconnecting session %016"PRIx64
				    ": CA failure", s->id);
				mta_free(s);
				return;
			}
			else {
				ssl = ssl_mta_init(NULL, 0, NULL, 0);
				if (ssl == NULL)
					fatal("mta: ssl_mta_init");
				io_start_tls(&s->io, ssl);
				return;
			}
		}

		resp_ca_cert = xmemdup(imsg->data, sizeof *resp_ca_cert, "mta:ca_cert");
		resp_ca_cert->cert = xstrdup((char *)imsg->data +
		    sizeof *resp_ca_cert, "mta:ca_cert");
		resp_ca_cert->key = xstrdup((char *)imsg->data +
		    sizeof *resp_ca_cert + resp_ca_cert->cert_len,
		    "mta:ca_key");
		ssl = ssl_mta_init(resp_ca_cert->cert, resp_ca_cert->cert_len,
		    resp_ca_cert->key, resp_ca_cert->key_len);
		if (ssl == NULL)
			fatal("mta: ssl_mta_init");
		io_start_tls(&s->io, ssl);

		memset(resp_ca_cert->cert, 0, resp_ca_cert->cert_len);
		memset(resp_ca_cert->key, 0, resp_ca_cert->key_len);
		free(resp_ca_cert->cert);
		free(resp_ca_cert->key);
		free(resp_ca_cert);
		return;

	case IMSG_LKA_SSL_VERIFY:
		resp_ca_vrfy = imsg->data;
		s = mta_tree_pop(&wait_ssl_verify, resp_ca_vrfy->reqid);
		if (s == NULL)
			return;

		if (resp_ca_vrfy->status == CA_OK)
			s->flags |= MTA_VERIFIED;
		else if (s->relay->flags & F_TLS_VERIFY) {
			errno = 0;
			mta_error(s, "SSL certificate check failed");
			mta_free(s);
			return;
		}

		mta_io(&s->io, IO_TLSVERIFIED);
		io_resume(&s->io, IO_PAUSE_IN);
		io_reload(&s->io);
		return;

	case IMSG_LKA_HELO:
		m_msg(&m, imsg);
		m_get_id(&m, &reqid);
		m_get_int(&m, &status);
		if (status == LKA_OK)
			m_get_string(&m, &name);
		m_end(&m);

		s = mta_tree_pop(&wait_helo, reqid);
		if (s == NULL)
			return;

		if (status == LKA_OK) {
			s->helo = xstrdup(name, "mta_session_imsg");
			mta_connect(s);
		} else {
			mta_source_error(s->relay, s->route,
			    "Failed to retrieve helo string");
			mta_free(s);
		}
		return;

	default:
		errx(1, "mta_session_imsg: unexpected %s imsg",
		    imsg_to_str(imsg->hdr.type));
	}
}
Beispiel #2
0
void
session_pickup(struct session *s, struct submit_status *ss)
{
	void	*ssl;

	s->s_flags &= ~F_WAITIMSG;

	if ((ss != NULL && ss->code == 421) ||
	    (s->s_dstatus & DS_TEMPFAILURE)) {
		stat_increment("smtp.tempfail", 1);
		session_respond(s, "421 Service temporarily unavailable");
		session_enter_state(s, S_QUIT);
		io_reload(&s->s_io);
		return;
	}

	switch (s->s_state) {

	case S_CONNECTED:
		session_enter_state(s, S_INIT);
		s->s_msg.session_id = s->s_id;
		s->s_msg.ss = s->s_ss;
		session_imsg(s, PROC_MFA, IMSG_MFA_CONNECT, 0, 0, -1,
			     &s->s_msg, sizeof(s->s_msg));
		break;

	case S_INIT:
		if (ss->code != 250) {
			session_destroy(s, "rejected by filter");
			return;
		}

		if (s->s_l->flags & F_SMTPS) {
			ssl = ssl_smtp_init(s->s_l->ssl_ctx);
			io_set_read(&s->s_io);
			io_start_tls(&s->s_io, ssl);
			return;
		}

		session_respond(s, SMTPD_BANNER, env->sc_hostname);
		session_enter_state(s, S_GREETED);
		break;

	case S_AUTH_FINALIZE:
		if (s->s_flags & F_AUTHENTICATED)
			session_respond(s, "235 Authentication succeeded");
		else
			session_respond(s, "535 Authentication failed");
		session_enter_state(s, S_HELO);
		break;

	case S_RSET:
		session_respond(s, "250 2.0.0 Reset state");
		session_enter_state(s, S_HELO);
		break;

	case S_HELO:
		if (ss->code != 250) {
			session_enter_state(s, S_GREETED);
			session_respond(s, "%d Helo rejected", ss->code);
			break;
		}

		session_respond(s, "250%c%s Hello %s [%s], pleased to meet you",
		    (s->s_flags & F_EHLO) ? '-' : ' ',
		    env->sc_hostname, s->s_msg.helo, ss_to_text(&s->s_ss));

		if (s->s_flags & F_EHLO) {
			/* unconditionnal extensions go first */
			session_respond(s, "250-8BITMIME");
			session_respond(s, "250-ENHANCEDSTATUSCODES");

			/* XXX - we also want to support reading SIZE from MAIL parameters */
			session_respond(s, "250-SIZE %zu", env->sc_maxsize);

			if (ADVERTISE_TLS(s))
				session_respond(s, "250-STARTTLS");

			if (ADVERTISE_AUTH(s))
				session_respond(s, "250-AUTH PLAIN LOGIN");
			session_respond(s, "250 HELP");
		}
		break;

	case S_MAIL_MFA:
		if (ss->code != 250) {
			session_enter_state(s, S_HELO);
			session_respond(s, "%d Sender rejected", ss->code);
			break;
		}

		session_enter_state(s, S_MAIL_QUEUE);
		s->s_msg.sender = ss->u.maddr;

		session_imsg(s, PROC_QUEUE, IMSG_QUEUE_CREATE_MESSAGE, 0, 0, -1,
		    &s->s_msg, sizeof(s->s_msg));
		break;

	case S_MAIL_QUEUE:
		session_enter_state(s, S_MAIL);
		session_respond(s, "%d 2.1.0 Sender ok", ss->code);
		break;

	case S_RCPT_MFA:
		/* recipient was not accepted */
		if (ss->code != 250) {
			/* We do not have a valid recipient, downgrade state */
			if (s->rcptcount == 0)
				session_enter_state(s, S_MAIL);
			else
				session_enter_state(s, S_RCPT);
			session_respond(s, "%d 5.0.0 Recipient rejected: %s@%s", ss->code,
			    s->s_msg.rcpt.user,
			    s->s_msg.rcpt.domain);
			break;
		}

		session_enter_state(s, S_RCPT);
		s->rcptcount++;
		s->s_msg.dest = ss->u.maddr;
		session_respond(s, "%d 2.0.0 Recipient ok", ss->code);
		break;

	case S_DATA_QUEUE:
		session_enter_state(s, S_DATACONTENT);
		session_respond(s, "354 Enter mail, end with \".\" on a line by"
		    " itself");

		fprintf(s->datafp, "Received: from %s (%s [%s])\n",
		    s->s_msg.helo, s->s_hostname, ss_to_text(&s->s_ss));
		fprintf(s->datafp, "\tby %s (OpenSMTPD) with %sSMTP id %08x",
		    env->sc_hostname, s->s_flags & F_EHLO ? "E" : "",
		    evpid_to_msgid(s->s_msg.id));

		if (s->s_flags & F_SECURE) {
			fprintf(s->datafp, "\n\t(version=%s cipher=%s bits=%d)",
			    SSL_get_cipher_version(s->s_io.ssl),
			    SSL_get_cipher_name(s->s_io.ssl),
			    SSL_get_cipher_bits(s->s_io.ssl, NULL));
		}
		if (s->rcptcount == 1)
			fprintf(s->datafp, "\n\tfor <%s@%s>; ",
			    s->s_msg.rcpt.user,
			    s->s_msg.rcpt.domain);
		else
			fprintf(s->datafp, ";\n\t");

		fprintf(s->datafp, "%s\n", time_to_text(time(NULL)));
		break;

	case S_DATACONTENT:
		if (ss->code != 250)
			s->s_dstatus |= DS_PERMFAILURE;
		session_read_data(s, ss->u.dataline);
		break;

	case S_DONE:
		session_respond(s, "250 2.0.0 %08x Message accepted for delivery",
		    evpid_to_msgid(s->s_msg.id));
		log_info("%08x: from=<%s%s%s>, size=%ld, nrcpts=%zu, proto=%s, "
		    "relay=%s [%s]",
		    evpid_to_msgid(s->s_msg.id),
		    s->s_msg.sender.user,
		    s->s_msg.sender.user[0] == '\0' ? "" : "@",
		    s->s_msg.sender.domain,
		    s->s_datalen,
		    s->rcptcount,
		    s->s_flags & F_EHLO ? "ESMTP" : "SMTP",
		    s->s_hostname,
		    ss_to_text(&s->s_ss));

		session_enter_state(s, S_HELO);
		s->s_msg.id = 0;
		bzero(&s->s_nresp, sizeof(s->s_nresp));
		break;

	default:
		fatal("session_pickup: unknown state");
	}

	io_reload(&s->s_io);
}
Beispiel #3
0
/*
 * Handle a response to an SMTP command
 */
static void
mta_response(struct mta_session *s, char *line)
{
	void		*ssl;
	struct envelope	*evp;

	switch (s->state) {

	case MTA_SMTP_BANNER:
		mta_enter_state(s, MTA_SMTP_EHLO);
		break;

	case MTA_SMTP_EHLO:
		if (line[0] != '2') {
			if ((s->flags & MTA_USE_AUTH) ||
			    !(s->flags & MTA_ALLOW_PLAIN)) {
				mta_route_error(s->route, line);
				mta_enter_state(s, MTA_DONE);
				return;
			}
			mta_enter_state(s, MTA_SMTP_HELO);
			return;
		}
		mta_enter_state(s, MTA_SMTP_STARTTLS);
		break;

	case MTA_SMTP_HELO:
		if (line[0] != '2') {
			mta_route_error(s->route, line);
			mta_enter_state(s, MTA_DONE);
			return;
		}
		mta_enter_state(s, MTA_SMTP_READY);
		break;

	case MTA_SMTP_STARTTLS:
		if (line[0] != '2') {
			if (s->flags & MTA_ALLOW_PLAIN) {
				mta_enter_state(s, MTA_SMTP_AUTH);
				return;
			}
			/* stop here if ssl can't be used */
			mta_route_error(s->route, line);
			mta_enter_state(s, MTA_DONE);
			return;
		}
		ssl = ssl_mta_init(s->ssl);
		if (ssl == NULL)
			fatal("mta: ssl_mta_init");
		s->is_reading = 0;
		io_set_write(&s->io);
		io_start_tls(&s->io, ssl);
		break;

	case MTA_SMTP_AUTH:
		if (line[0] != '2') {
			mta_route_error(s->route, line);
			mta_enter_state(s, MTA_DONE);
			return;
		}
		mta_enter_state(s, MTA_SMTP_READY);
		break;

	case MTA_SMTP_MAIL:
		if (line[0] != '2') {
			mta_status(s, 0, line);
			mta_enter_state(s, MTA_SMTP_RSET);
			return;
		}
		mta_enter_state(s, MTA_SMTP_RCPT);
		break;

	case MTA_SMTP_RCPT:
		evp = s->currevp;
		s->currevp = TAILQ_NEXT(s->currevp, entry);
		if (line[0] != '2') {
			mta_envelope_done(s->task, evp, line);
			if (TAILQ_EMPTY(&s->task->envelopes)) {
				free(s->task);
				s->task = NULL;
				stat_decrement("mta.task.running", 1);
				mta_enter_state(s, MTA_SMTP_RSET);
				break;
			}
		}
		if (s->currevp == NULL)
			mta_enter_state(s, MTA_SMTP_DATA);
		else
			mta_enter_state(s, MTA_SMTP_RCPT);
		break;

	case MTA_SMTP_DATA:
		if (line[0] != '2' && line[0] != '3') {
			mta_status(s, 0, line);
			mta_enter_state(s, MTA_SMTP_RSET);
			return;
		}
		mta_enter_state(s, MTA_SMTP_BODY);
		break;

	case MTA_SMTP_DONE:
		mta_status(s, 0, line);
		if (line[0] == '2')
			s->msgcount++;
		mta_enter_state(s, MTA_SMTP_READY);
		break;

	case MTA_SMTP_RSET:
		mta_enter_state(s, MTA_SMTP_READY);
		break;

	default:
		fatalx("mta_response() bad state");
	}
}
Beispiel #4
0
void
session_io(struct io *io, int evt)
{
	struct session	*s = io->arg;
	void		*ssl;
	char		*line;
	size_t		 len;

	log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt), io_strio(io));

	switch(evt) {

	case IO_TLSREADY:
		s->s_flags |= F_SECURE;
		if (s->s_l->flags & F_SMTPS)
			stat_increment("smtp.smtps", 1);
		if (s->s_l->flags & F_STARTTLS)
			stat_increment("smtp.tls", 1);
		if (s->s_state == S_INIT) {
			io_set_write(&s->s_io);
			session_respond(s, SMTPD_BANNER, env->sc_hostname);
		}
		session_enter_state(s, S_GREETED);
		break;

	case IO_DATAIN:
	    nextline:
		line = iobuf_getline(&s->s_iobuf, &len);
		if ((line == NULL && iobuf_len(&s->s_iobuf) >= SMTP_LINE_MAX) ||
		    (line && len >= SMTP_LINE_MAX)) {
			session_respond(s, "500 5.0.0 Line too long");
			session_enter_state(s, S_QUIT);
			io_set_write(io);
			return;
		}

		if (line == NULL) {
			iobuf_normalize(&s->s_iobuf);
			return;
		}

		if (s->s_state == S_DATACONTENT && strcmp(line, ".")) {
			/* more data to come */
			session_line(s, line, len);
			goto nextline;
		}

		/* pipelining not supported */
		if (iobuf_len(&s->s_iobuf)) {
			session_respond(s, "500 5.0.0 Pipelining not supported");
			session_enter_state(s, S_QUIT);
			io_set_write(io);
			return;
		}

		session_line(s, line, len);
		iobuf_normalize(&s->s_iobuf);
		io_set_write(io);
		break;

	case IO_LOWAT:
		if (s->s_state == S_QUIT) {
			session_destroy(s, "done");
			break;
		}

		io_set_read(io);

		/* wait for the client to start tls */
		if (s->s_state == S_TLS) {
			ssl = ssl_smtp_init(s->s_l->ssl_ctx);
			io_start_tls(io, ssl);
		}
		break;

	case IO_TIMEOUT:
		session_destroy(s, "timeout");
		break;

	case IO_DISCONNECTED:
		session_destroy(s, "disconnected");
		break;

	case IO_ERROR:
		session_destroy(s, "error");
		break;

	default:
		fatal("session_io()");
	}
}
Beispiel #5
0
void
mta_session_imsg(struct imsgev *iev, struct imsg *imsg)
{
	uint64_t		 id;
	struct mta_session	*s;
	struct mta_host		*host;
	struct secret		*secret;
	struct dns		*dns;
	const char		*error;
	void			*ptr;

	switch(imsg->hdr.type) {

	case IMSG_QUEUE_MESSAGE_FD:
		id = *(uint64_t*)(imsg->data);
		if (imsg->fd == -1)
			fatalx("mta: cannot obtain msgfd");
		s = tree_xget(&sessions, id);
		s->datafp = fdopen(imsg->fd, "r");
		if (s->datafp == NULL)
			fatal("mta: fdopen");

		if (mta_check_loop(s->datafp)) {
			log_debug("mta: loop detected");
			fclose(s->datafp);
			s->datafp = NULL;
			mta_status(s, 0, "646 Loop detected");
			mta_enter_state(s, MTA_SMTP_READY);
		} else {
			mta_enter_state(s, MTA_SMTP_MAIL);
		}
		return;

	case IMSG_LKA_SECRET:
		/* LKA responded to AUTH lookup. */
		secret = imsg->data;
		s = tree_xget(&sessions, secret->id);
		s->secret = xstrdup(secret->secret, "mta: secret");
		if (s->secret[0] == '\0') {
			mta_route_error(s->route, "secrets lookup failed");
			mta_enter_state(s, MTA_DONE);
		} else
			mta_enter_state(s, MTA_MX);
		return;

	case IMSG_DNS_HOST:
		dns = imsg->data;
		s = tree_xget(&sessions, dns->id);
		host = xcalloc(1, sizeof *host, "mta: host");
		host->sa = dns->ss;
		TAILQ_INSERT_TAIL(&s->hosts, host, entry);
		return;

	case IMSG_DNS_HOST_END:
		/* LKA responded to DNS lookup. */
		dns = imsg->data;
		s = tree_xget(&sessions, dns->id);
		if (!dns->error) {
			mta_enter_state(s, MTA_CONNECT);
			return;
		}
		if (dns->error == DNS_RETRY)
			error = "100 MX lookup failed temporarily";
		else if (dns->error == DNS_EINVAL)
			error = "600 Invalid domain name";
		else if (dns->error == DNS_ENONAME)
			error = "600 Domain does not exist";
		else if (dns->error == DNS_ENOTFOUND)
			error = "600 No MX address found for domain";
		else
			error = "100 Weird error";
		mta_route_error(s->route, error);
		mta_enter_state(s, MTA_CONNECT);
		return;

	case IMSG_DNS_PTR:
		dns = imsg->data;
		s = tree_xget(&sessions, dns->id);
		host = TAILQ_FIRST(&s->hosts);
		if (dns->error)
			strlcpy(host->fqdn, "<unknown>", sizeof host->fqdn);
		else
			strlcpy(host->fqdn, dns->host, sizeof host->fqdn);
		log_debug("mta: %p: connected to %s", s, host->fqdn);

		/* check if we need to start tls now... */
		if (((s->flags & MTA_FORCE_ANYSSL) && host->used == 1) ||
		    (s->flags & MTA_FORCE_SMTPS)) {
			log_debug("mta: %p: trying smtps (ssl=%p)...", s, s->ssl);
			if ((ptr = ssl_mta_init(s->ssl)) == NULL)
				fatalx("mta: ssl_mta_init");
			io_start_tls(&s->io, ptr);
		} else {
			mta_enter_state(s, MTA_SMTP_BANNER);
		}
		break;
	default:
		errx(1, "mta_session_imsg: unexpected %s imsg",
		    imsg_to_str(imsg->hdr.type));
	}
}