Beispiel #1
0
static int
ssl_do_tx(struct stream *stream)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);

    for (;;) {
        int old_state = SSL_get_state(sslv->ssl);
        int ret = SSL_write(sslv->ssl, sslv->txbuf->data, sslv->txbuf->size);
        if (old_state != SSL_get_state(sslv->ssl)) {
            sslv->rx_want = SSL_NOTHING;
        }
        sslv->tx_want = SSL_NOTHING;
        if (ret > 0) {
            ofpbuf_pull(sslv->txbuf, ret);
            if (sslv->txbuf->size == 0) {
                return 0;
            }
        } else {
            int ssl_error = SSL_get_error(sslv->ssl, ret);
            if (ssl_error == SSL_ERROR_ZERO_RETURN) {
                VLOG_WARN_RL(&rl, "SSL_write: connection closed");
                return EPIPE;
            } else {
                return interpret_ssl_error("SSL_write", ret, ssl_error,
                                           &sslv->tx_want);
            }
        }
    }
}
Beispiel #2
0
static ssize_t
ssl_recv(struct stream *stream, void *buffer, size_t n)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);
    int old_state;
    ssize_t ret;

    /* Behavior of zero-byte SSL_read is poorly defined. */
    ovs_assert(n > 0);

    old_state = SSL_get_state(sslv->ssl);
    ret = SSL_read(sslv->ssl, buffer, n);
    if (old_state != SSL_get_state(sslv->ssl)) {
        sslv->tx_want = SSL_NOTHING;
    }
    sslv->rx_want = SSL_NOTHING;

    if (ret > 0) {
        return ret;
    } else {
        int error = SSL_get_error(sslv->ssl, ret);
        if (error == SSL_ERROR_ZERO_RETURN) {
            return 0;
        } else {
            return -interpret_ssl_error("SSL_read", ret, error,
                                        &sslv->rx_want);
        }
    }
}
Beispiel #3
0
static ssize_t
ssl_send(struct stream *stream, const void *buffer, size_t n)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);

    if (sslv->txbuf) {
        return -EAGAIN;
    } else {
        int error;

        sslv->txbuf = ofpbuf_clone_data(buffer, n);
        error = ssl_do_tx(stream);
        switch (error) {
        case 0:
            ssl_clear_txbuf(sslv);
            return n;
        case EAGAIN:
            leak_checker_claim(buffer);
            return n;
        default:
            sslv->txbuf = NULL;
            return -error;
        }
    }
}
Beispiel #4
0
static void
ssl_run_wait(struct stream *stream)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);

    if (sslv->tx_want != SSL_NOTHING) {
        poll_fd_wait(sslv->fd, want_to_poll_events(sslv->tx_want));
    }
}
Beispiel #5
0
static void
ssl_run(struct stream *stream)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);

    if (sslv->txbuf && ssl_do_tx(stream) != EAGAIN) {
        ssl_clear_txbuf(sslv);
    }
}
Beispiel #6
0
static void
ssl_wait(struct stream *stream, enum stream_wait_type wait)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);

    switch (wait) {
    case STREAM_CONNECT:
        if (stream_connect(stream) != EAGAIN) {
            poll_immediate_wake();
        } else {
            switch (sslv->state) {
            case STATE_TCP_CONNECTING:
                poll_fd_wait(sslv->fd, POLLOUT);
                break;

            case STATE_SSL_CONNECTING:
                /* ssl_connect() called SSL_accept() or SSL_connect(), which
                 * set up the status that we test here. */
                poll_fd_wait(sslv->fd,
                               want_to_poll_events(SSL_want(sslv->ssl)));
                break;

            default:
                OVS_NOT_REACHED();
            }
        }
        break;

    case STREAM_RECV:
        if (sslv->rx_want != SSL_NOTHING) {
            poll_fd_wait(sslv->fd, want_to_poll_events(sslv->rx_want));
        } else {
            poll_immediate_wake();
        }
        break;

    case STREAM_SEND:
        if (!sslv->txbuf) {
            /* We have room in our tx queue. */
            poll_immediate_wake();
        } else {
            /* stream_run_wait() will do the right thing; don't bother with
             * redundancy. */
        }
        break;

    default:
        OVS_NOT_REACHED();
    }
}
Beispiel #7
0
static void
ssl_close(struct stream *stream)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);
    ssl_clear_txbuf(sslv);

    /* Attempt clean shutdown of the SSL connection.  This will work most of
     * the time, as long as the kernel send buffer has some free space and the
     * SSL connection isn't renegotiating, etc.  That has to be good enough,
     * since we don't have any way to continue the close operation in the
     * background. */
    SSL_shutdown(sslv->ssl);

    /* SSL_shutdown() might have signaled an error, in which case we need to
     * flush it out of the OpenSSL error queue or the next OpenSSL operation
     * will falsely signal an error. */
    ERR_clear_error();

    SSL_free(sslv->ssl);
    closesocket(sslv->fd);
    free(sslv);
}
Beispiel #8
0
static int
ssl_connect(struct stream *stream)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);
    int retval;

    switch (sslv->state) {
    case STATE_TCP_CONNECTING:
        retval = check_connection_completion(sslv->fd);
        if (retval) {
            return retval;
        }
        sslv->state = STATE_SSL_CONNECTING;
        setsockopt_tcp_nodelay(sslv->fd);
        /* Fall through. */

    case STATE_SSL_CONNECTING:
        /* Capture the first few bytes of received data so that we can guess
         * what kind of funny data we've been sent if SSL negotiation fails. */
        if (sslv->n_head <= 0) {
            sslv->n_head = recv(sslv->fd, sslv->head, sizeof sslv->head,
                                MSG_PEEK);
        }

        retval = (sslv->type == CLIENT
                   ? SSL_connect(sslv->ssl) : SSL_accept(sslv->ssl));
        if (retval != 1) {
            int error = SSL_get_error(sslv->ssl, retval);
            if (retval < 0 && ssl_wants_io(error)) {
                return EAGAIN;
            } else {
                int unused;

                interpret_ssl_error((sslv->type == CLIENT ? "SSL_connect"
                                     : "SSL_accept"), retval, error, &unused);
                shutdown(sslv->fd, SHUT_RDWR);
                stream_report_content(sslv->head, sslv->n_head, STREAM_SSL,
                                      &this_module, stream_get_name(stream));
                return EPROTO;
            }
        } else if (bootstrap_ca_cert) {
            return do_ca_cert_bootstrap(stream);
        } else if (verify_peer_cert
                   && ((SSL_get_verify_mode(sslv->ssl)
                       & (SSL_VERIFY_NONE | SSL_VERIFY_PEER))
                       != SSL_VERIFY_PEER)) {
            /* Two or more SSL connections completed at the same time while we
             * were in bootstrap mode.  Only one of these can finish the
             * bootstrap successfully.  The other one(s) must be rejected
             * because they were not verified against the bootstrapped CA
             * certificate.  (Alternatively we could verify them against the CA
             * certificate, but that's more trouble than it's worth.  These
             * connections will succeed the next time they retry, assuming that
             * they have a certificate against the correct CA.) */
            VLOG_INFO("rejecting SSL connection during bootstrap race window");
            return EPROTO;
        } else {
            return 0;
        }
    }

    OVS_NOT_REACHED();
}
Beispiel #9
0
static int
do_ca_cert_bootstrap(struct stream *stream)
{
    struct ssl_stream *sslv = ssl_stream_cast(stream);
    STACK_OF(X509) *chain;
    X509 *cert;
    FILE *file;
    int error;
    int fd;

    chain = SSL_get_peer_cert_chain(sslv->ssl);
    if (!chain || !sk_X509_num(chain)) {
        VLOG_ERR("could not bootstrap CA cert: no certificate presented by "
                 "peer");
        return EPROTO;
    }
    cert = sk_X509_value(chain, sk_X509_num(chain) - 1);

    /* Check that 'cert' is self-signed.  Otherwise it is not a CA
     * certificate and we should not attempt to use it as one. */
    error = X509_check_issued(cert, cert);
    if (error) {
        VLOG_ERR("could not bootstrap CA cert: obtained certificate is "
                 "not self-signed (%s)",
                 X509_verify_cert_error_string(error));
        if (sk_X509_num(chain) < 2) {
            VLOG_ERR("only one certificate was received, so probably the peer "
                     "is not configured to send its CA certificate");
        }
        return EPROTO;
    }

    fd = open(ca_cert.file_name, O_CREAT | O_EXCL | O_WRONLY, 0444);
    if (fd < 0) {
        if (errno == EEXIST) {
            VLOG_INFO_RL(&rl, "reading CA cert %s created by another process",
                         ca_cert.file_name);
            stream_ssl_set_ca_cert_file__(ca_cert.file_name, true, true);
            return EPROTO;
        } else {
            VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s",
                     ca_cert.file_name, ovs_strerror(errno));
            return errno;
        }
    }

    file = fdopen(fd, "w");
    if (!file) {
        error = errno;
        VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s",
                 ovs_strerror(error));
        unlink(ca_cert.file_name);
        return error;
    }

    if (!PEM_write_X509(file, cert)) {
        VLOG_ERR("could not bootstrap CA cert: PEM_write_X509 to %s failed: "
                 "%s", ca_cert.file_name,
                 ERR_error_string(ERR_get_error(), NULL));
        fclose(file);
        unlink(ca_cert.file_name);
        return EIO;
    }

    if (fclose(file)) {
        error = errno;
        VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s",
                 ca_cert.file_name, ovs_strerror(error));
        unlink(ca_cert.file_name);
        return error;
    }

    VLOG_INFO("successfully bootstrapped CA cert to %s", ca_cert.file_name);
    log_ca_cert(ca_cert.file_name, cert);
    bootstrap_ca_cert = false;
    ca_cert.read = true;

    /* SSL_CTX_add_client_CA makes a copy of cert's relevant data. */
    SSL_CTX_add_client_CA(ctx, cert);

    SSL_CTX_set_cert_store(ctx, X509_STORE_new());
    if (SSL_CTX_load_verify_locations(ctx, ca_cert.file_name, NULL) != 1) {
        VLOG_ERR("SSL_CTX_load_verify_locations: %s",
                 ERR_error_string(ERR_get_error(), NULL));
        return EPROTO;
    }
    VLOG_INFO("killing successful connection to retry using CA cert");
    return EPROTO;
}