static int openssl_verify(int preverify_ok, X509_STORE_CTX *ctx) { int depth; SpiceOpenSSLVerify *v; SSL *ssl; X509* cert; ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); v = (SpiceOpenSSLVerify*)SSL_get_app_data(ssl); depth = X509_STORE_CTX_get_error_depth(ctx); if (depth > 0) { if (!preverify_ok) { SPICE_DEBUG("openssl verify failed at depth=%d", depth); v->all_preverify_ok = 0; return 0; } else return 1; } /* depth == 0 */ cert = X509_STORE_CTX_get_current_cert(ctx); if (!cert) { SPICE_DEBUG("failed to get server certificate"); return 0; } if (v->verifyop & SPICE_SSL_VERIFY_OP_PUBKEY && verify_pubkey(cert, v->pubkey, v->pubkey_size)) return 1; if (!v->all_preverify_ok || !preverify_ok) return 0; if (v->verifyop & SPICE_SSL_VERIFY_OP_HOSTNAME && verify_hostname(cert, v->hostname)) return 1; if (v->verifyop & SPICE_SSL_VERIFY_OP_SUBJECT && verify_subject(cert, v)) return 1; return 0; }
static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify) { #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) const SSL_METHOD *meth; #else SSL_METHOD *meth; #endif SSL_CTX *ctx; int ret; X509 *cert; SSL_library_init(); SSL_load_error_strings(); meth = SSLv23_method(); if (!meth) { ssl_socket_perror("SSLv23_method"); return -1; } ctx = SSL_CTX_new(meth); if (!ctx) { ssl_socket_perror("SSL_CTX_new"); return -1; } if (use_tls_only) SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); if (verify) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); if (!SSL_CTX_set_default_verify_paths(ctx)) { ssl_socket_perror("SSL_CTX_set_default_verify_paths"); return -1; } sock->ssl = SSL_new(ctx); if (!sock->ssl) { ssl_socket_perror("SSL_new"); return -1; } if (!SSL_set_rfd(sock->ssl, sock->fd[0])) { ssl_socket_perror("SSL_set_rfd"); return -1; } if (!SSL_set_wfd(sock->ssl, sock->fd[1])) { ssl_socket_perror("SSL_set_wfd"); return -1; } #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME /* * SNI (RFC4366) * OpenSSL does not document this function, but the implementation * returns 1 on success, 0 on failure after calling SSLerr(). */ ret = SSL_set_tlsext_host_name(sock->ssl, server.host); if (ret != 1) warning("SSL_set_tlsext_host_name(%s) failed.", server.host); #endif ret = SSL_connect(sock->ssl); if (ret <= 0) { socket_perror("SSL_connect", sock, ret); return -1; } if (verify) { /* make sure the hostname matches that of the certificate */ cert = SSL_get_peer_certificate(sock->ssl); if (!cert) return error("unable to get peer certificate."); if (verify_hostname(cert, server.host) < 0) return -1; } return 0; }
tcp_stream_t * tcp_stream_create_ssl_from_fd(int fd, const char *hostname, const tcp_ssl_info_t *tsi, char *errbuf, size_t errlen) { char errmsg[120]; tcp_stream_t *ts = calloc(1, sizeof(tcp_stream_t)); ts->ts_fd = fd; if((ts->ts_ssl = SSL_new(ssl_ctx)) == NULL) goto bad_ssl; if(SSL_set_fd(ts->ts_ssl, fd) == 0) goto bad_ssl; if(tsi->key != NULL) { BIO *cbio = BIO_new_mem_buf((char *)tsi->key, -1); EVP_PKEY *key = PEM_read_bio_PrivateKey(cbio, NULL, NULL, NULL); BIO_free(cbio); if(key == NULL) { snprintf(errbuf, errlen, "Unable to load private key"); goto bad; } SSL_use_PrivateKey(ts->ts_ssl, key); EVP_PKEY_free(key); } if(tsi->cert != NULL) { BIO *cbio = BIO_new_mem_buf((char *)tsi->cert, -1); X509 *cert = PEM_read_bio_X509(cbio, NULL, 0, NULL); BIO_free(cbio); if(cert == NULL) { snprintf(errbuf, errlen, "Unable to load certificate"); goto bad; } SSL_use_certificate(ts->ts_ssl, cert); X509_free(cert); } if(SSL_connect(ts->ts_ssl) <= 0) { goto bad_ssl; } SSL_set_mode(ts->ts_ssl, SSL_MODE_AUTO_RETRY); X509 *peer = SSL_get_peer_certificate(ts->ts_ssl); if(peer == NULL) { goto bad_ssl; } int err = SSL_get_verify_result(ts->ts_ssl); if(err != X509_V_OK) { snprintf(errbuf, errlen, "Certificate error: %s", X509_verify_cert_error_string(err)); X509_free(peer); goto bad; } if(verify_hostname(hostname, peer, errbuf, errlen)) { X509_free(peer); goto bad; } X509_free(peer); ts->ts_fd = fd; htsbuf_queue_init(&ts->ts_spill, INT32_MAX); htsbuf_queue_init(&ts->ts_sendq, INT32_MAX); ts->ts_write = ssl_write; ts->ts_read = ssl_read; return ts; bad_ssl: ERR_error_string(ERR_get_error(), errmsg); snprintf(errbuf, errlen, "SSL: %s", errmsg); bad: tcp_close(ts); return NULL; }