-/* $OpenBSD: smtp_session.c,v 1.374 2018/12/12 21:27:49 gilles Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.375 2018/12/14 09:18:03 eric Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
static const char *smtp_strstate(int);
static void smtp_tls_init(struct smtp_session *);
static int smtp_verify_certificate(struct smtp_session *);
+static void smtp_cert_init(struct smtp_session *);
+static void smtp_cert_init_cb(void *, int, const char *, const void *, size_t);
+static void smtp_cert_verify(struct smtp_session *);
+static void smtp_cert_verify_cb(void *, int);
static void smtp_auth_failure_pause(struct smtp_session *);
static void smtp_auth_failure_resume(int, short, void *);
s->flags |= SF_SECURE;
s->helo[0] = '\0';
- if (smtp_verify_certificate(s)) {
- io_pause(s->io, IO_IN);
- break;
- }
-
- if (s->listener->flags & F_TLS_VERIFY) {
- log_info("%016"PRIx64" smtp "
- "disconnected address=%s host=%s reason=no-client-cert",
- s->id, ss_to_text(&s->ss), s->hostname);
- smtp_free(s, "client did not present certificate");
- return;
- }
-
- smtp_tls_verified(s);
+ smtp_cert_verify(s);
break;
case IO_DATAIN:
/* Wait for the client to start tls */
if (s->state == STATE_TLS) {
- smtp_tls_init(s);
+ smtp_cert_init(s);
break;
}
smtp_proceed_connected(struct smtp_session *s)
{
if (s->listener->flags & F_SMTPS)
- smtp_tls_init(s);
+ smtp_cert_init(s);
else
smtp_send_banner(s);
}
return res;
}
+static void
+smtp_cert_init(struct smtp_session *s)
+{
+ const char *name;
+ int fallback;
+
+ if (s->listener->pki_name[0]) {
+ name = s->listener->pki_name;
+ fallback = 0;
+ }
+ else {
+ name = s->smtpname;
+ fallback = 1;
+ }
+
+ if (cert_init(name, fallback, smtp_cert_init_cb, s))
+ tree_xset(&wait_ssl_init, s->id, s);
+}
+
+static void
+smtp_cert_init_cb(void *arg, int status, const char *name, const void *cert,
+ size_t cert_len)
+{
+ struct smtp_session *s = arg;
+ void *ssl, *ssl_ctx;
+
+ tree_pop(&wait_ssl_init, s->id);
+
+ if (status == CA_FAIL) {
+ log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ "reason=ca-failure",
+ s->id, ss_to_text(&s->ss), s->hostname);
+ smtp_free(s, "CA failure");
+ return;
+ }
+
+ ssl_ctx = dict_get(env->sc_ssl_dict, name);
+ ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY);
+ io_set_read(s->io);
+ io_start_tls(s->io, ssl);
+}
+
+static void
+smtp_cert_verify(struct smtp_session *s)
+{
+ const char *name;
+ int fallback;
+
+ if (s->listener->ca_name[0]) {
+ name = s->listener->ca_name;
+ fallback = 0;
+ }
+ else {
+ name = s->smtpname;
+ fallback = 1;
+ }
+
+ if (cert_verify(io_ssl(s->io), name, fallback, smtp_cert_verify_cb, s)) {
+ tree_xset(&wait_ssl_verify, s->id, s);
+ io_pause(s->io, IO_IN);
+ }
+}
+
+static void
+smtp_cert_verify_cb(void *arg, int status)
+{
+ struct smtp_session *s = arg;
+ const char *reason = NULL;
+ int resume;
+
+ resume = tree_pop(&wait_ssl_verify, s->id) != NULL;
+
+ switch (status) {
+ case CERT_OK:
+ reason = "cert-ok";
+ s->flags |= SF_VERIFIED;
+ break;
+ case CERT_NOCA:
+ reason = "no-ca";
+ break;
+ case CERT_NOCERT:
+ reason = "no-client-cert";
+ break;
+ case CERT_INVALID:
+ reason = "cert-invalid";
+ break;
+ default:
+ reason = "cert-check-failed";
+ break;
+ }
+
+ log_debug("smtp: %p: smtp_cert_verify_cb: %s", s, reason);
+
+ if (!(s->flags & SF_VERIFIED) && (s->listener->flags & F_TLS_VERIFY)) {
+ log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ " reason=%s", s->id, ss_to_text(&s->ss), s->hostname,
+ reason);
+ smtp_free(s, "SSL certificate check failed");
+ return;
+ }
+
+ smtp_tls_verified(s);
+ if (resume)
+ io_resume(s->io, IO_IN);
+}
+
static void
smtp_auth_failure_resume(int fd, short event, void *p)
{