+static int
+do_ca_cert_bootstrap(struct vconn *vconn)
+{
+ struct ssl_vconn *sslv = ssl_vconn_cast(vconn);
+ STACK_OF(X509) *chain;
+ X509 *ca_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;
+ }
+ ca_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
+
+ /* Check that 'ca_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(ca_cert, ca_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, O_CREAT | O_EXCL | O_WRONLY, 0444);
+ if (fd < 0) {
+ VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s",
+ ca_cert_file, strerror(errno));
+ return errno;
+ }
+
+ file = fdopen(fd, "w");
+ if (!file) {
+ int error = errno;
+ VLOG_ERR("could not bootstrap CA cert: fdopen failed: %s",
+ strerror(error));
+ unlink(ca_cert_file);
+ return error;
+ }
+
+ if (!PEM_write_X509(file, ca_cert)) {
+ VLOG_ERR("could not bootstrap CA cert: PEM_write_X509 to %s failed: "
+ "%s", ca_cert_file, ERR_error_string(ERR_get_error(), NULL));
+ fclose(file);
+ unlink(ca_cert_file);
+ return EIO;
+ }
+
+ if (fclose(file)) {
+ int error = errno;
+ VLOG_ERR("could not bootstrap CA cert: writing %s failed: %s",
+ ca_cert_file, strerror(error));
+ unlink(ca_cert_file);
+ return error;
+ }
+
+ VLOG_WARN("successfully bootstrapped CA cert to %s", ca_cert_file);
+ log_ca_cert(ca_cert_file, ca_cert);
+ bootstrap_ca_cert = false;
+ has_ca_cert = true;
+
+ /* SSL_CTX_add_client_CA makes a copy of ca_cert's relevant data. */
+ SSL_CTX_add_client_CA(ctx, ca_cert);
+
+ /* SSL_CTX_use_certificate() takes ownership of the certificate passed in.
+ * 'ca_cert' is owned by sslv->ssl, so we need to duplicate it. */
+ ca_cert = X509_dup(ca_cert);
+ if (!ca_cert) {
+ out_of_memory();
+ }
+ if (SSL_CTX_load_verify_locations(ctx, ca_cert_file, NULL) != 1) {
+ VLOG_ERR("SSL_CTX_load_verify_locations: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return EPROTO;
+ }
+ VLOG_WARN("killing successful connection to retry using CA cert");
+ return EPROTO;
+}
+