--- a/src/login-common/client-common.h +++ b/src/login-common/client-common.h @@ -203,6 +203,9 @@ ARRAY_TYPE(const_string) forward_fields; int fd; + /* Keep the TLS socketpair endpoint alive until the backend has + received it. macOS can otherwise deliver an early EOF. */ + int pending_plaintext_fd; struct istream *input; struct ostream *output; /* The rawlog streams don't hold any references */ --- a/src/login-common/client-common.c +++ b/src/login-common/client-common.c @@ -281,6 +281,7 @@ p_array_init(&client->module_contexts, client->pool, 5); client->fd = fd; + client->pending_plaintext_fd = -1; client->local_ip = conn->local_ip; client->local_port = conn->local_port; client->ip = conn->remote_ip; @@ -635,6 +636,7 @@ o_stream_unref(&client->output); o_stream_unref(&client->multiplex_orig_output); i_close_fd(&client->fd); + i_close_fd(&client->pending_plaintext_fd); event_unref(&client->event); event_unref(&client->event_auth); client_settings_free(client); --- a/src/login-common/sasl-server.c +++ b/src/login-common/sasl-server.c @@ -158,6 +158,9 @@ enum sasl_server_reply sasl_reply = SASL_SERVER_REPLY_MASTER_FAILED; const char *data = NULL; + /* The backend replies only after receiving the passed descriptor. + Closing it before that can half-close a socketpair on macOS. */ + i_close_fd(&client->pending_plaintext_fd); client->master_tag = 0; client->authenticating = FALSE; if (reply != NULL) { @@ -230,10 +233,12 @@ params.socket_path = client->postlogin_socket_path; params.request = req; params.data = buf->data; + if (close_fd) { + i_assert(client->pending_plaintext_fd == -1); + client->pending_plaintext_fd = fd; + } login_client_request(login_client_list, ¶ms, login_callback, client, &client->master_tag); - if (close_fd) - i_close_fd(&fd); return 0; }