static lagopus_result_t connect_tls(struct session *s, const char *host, const char *port) { int ret; BIO *sbio; (void) host; (void) port; lagopus_msg_info("tls handshake start.\n"); if (IS_TLS_NOT_INIT(s)) { SSL_CTX *ssl_ctx; ssl_ctx = get_ssl_ctx(GET_TLS_CTX(s)->ca_dir, GET_TLS_CTX(s)->cert, GET_TLS_CTX(s)->key); if (ssl_ctx == NULL) { lagopus_msg_warning("get_ssl_ctx() fail.\n"); return LAGOPUS_RESULT_TLS_CONN_ERROR; } GET_TLS_CTX(s)->ctx = ssl_ctx; } if (GET_TLS_CTX(s)->ssl == NULL) { SSL *ssl; ssl = SSL_new(GET_TLS_CTX(s)->ctx); if (ssl == NULL) { lagopus_msg_warning("no memory.\n"); return LAGOPUS_RESULT_TLS_CONN_ERROR; } GET_TLS_CTX(s)->ssl = ssl; } if (SSL_get_rbio(GET_TLS_CTX(s)->ssl) == NULL) { sbio = BIO_new_socket(s->sock, BIO_NOCLOSE); SSL_set_bio(GET_TLS_CTX(s)->ssl, sbio, sbio); } ret = SSL_connect(GET_TLS_CTX(s)->ssl); if (ret == 0) { lagopus_msg_warning("tls handshake failed.\n"); return LAGOPUS_RESULT_TLS_CONN_ERROR; } else if (ret < 0 && (SSL_get_error(GET_TLS_CTX(s)->ssl, ret) != SSL_ERROR_WANT_READ && SSL_get_error(GET_TLS_CTX(s)->ssl, ret) != SSL_ERROR_WANT_READ)) { lagopus_msg_warning("tls error (%s:%d).\n", ERR_error_string((unsigned long) SSL_get_error(GET_TLS_CTX(s)->ssl, ret), NULL), (int) SSL_get_error(GET_TLS_CTX(s)->ssl, ret)); return LAGOPUS_RESULT_TLS_CONN_ERROR; } else if (ret < 0) { lagopus_msg_info("tls error (%s:%d), but continue.\n", ERR_error_string((unsigned long) SSL_get_error(GET_TLS_CTX(s)->ssl, ret), NULL), (int) SSL_get_error(GET_TLS_CTX(s)->ssl, ret)); return LAGOPUS_RESULT_EINPROGRESS; } else { ret = check_cert_chain(GET_TLS_CTX(s)->ssl); if (ret < 0) { lagopus_msg_warning("certificate error.\n"); return LAGOPUS_RESULT_TLS_CONN_ERROR; } GET_TLS_CTX(s)->verified = true; lagopus_msg_info("tls handshake end.\n"); } return LAGOPUS_RESULT_OK; }
spocp_result_t tls_start(conn_t * conn, ruleset_t * rs) { SSL *ssl; SSL_CTX *ctx = (SSL_CTX *) conn->srv->ctx; int maxbits, r, n = 0; char *sid_ctx = "spocp"; SSL_CIPHER *cipher; if (conn->ssl != NULL) { tls_error(SPOCP_WARNING, conn, "STARTTLS received on already encrypted connection"); return SPOCP_STATE_VIOLATION; } if (!(ssl = SSL_new(ctx))) { tls_error(SPOCP_ERR, conn, "Error creating SSL context"); return SPOCP_OPERATIONSERROR; } /* * do these never fail ?? */ SSL_set_session_id_context(ssl, (unsigned char *) sid_ctx, strlen(sid_ctx)); if (SSL_set_fd(ssl, conn->fd) == 0) { traceLog(LOG_ERR,"Couldn't set filedescriptor in SSL"); return SPOCP_OPERATIONSERROR; } n = iobuf_content(conn->in); traceLog(LOG_INFO,"tls_start: %d bytes in input buffer", n); if (n) { traceLog(LOG_INFO,"tls_start: %x%x%x%x", conn->in->r[0], conn->in->r[1], conn->in->r[2], conn->in->r[3]); } LOG(SPOCP_DEBUG) traceLog(LOG_DEBUG,"Waiting for client on %d to initiate handshake", conn->fd); /* * waits for the client to initiate the handshake */ { fd_set rset ; int retval ; FD_ZERO( &rset ); FD_SET( conn->fd, &rset ); traceLog(LOG_DEBUG, "Waiting for the client" ) ; retval = select(conn->fd+1,&rset,NULL,NULL,0) ; } if ((r = SSL_accept(ssl)) <= 0) { int se ; if ((se = SSL_get_error(ssl, r)) == SSL_ERROR_WANT_READ) { traceLog(LOG_DEBUG,"Want_read"); } else if (se == SSL_ERROR_SYSCALL) { unsigned long err ; err = ERR_get_error(); if( err == 0L && r == 0 ) { traceLog(LOG_DEBUG,"EOF observed") ; } else traceLog(LOG_ERR,"I/O error occured (%ld/%d)", err, r); } else { traceLog(LOG_ERR,"SSL_get_error: %d", se); tls_error(SPOCP_ERR, conn, "SSL accept error"); SSL_free(ssl); } conn->status = CNST_ACTIVE; return SPOCP_SSL_ERR; } /* * } */ LOG(SPOCP_DEBUG) { traceLog(LOG_DEBUG,"SSL accept done"); traceLog(LOG_DEBUG,"Checking client certificate"); } if (!check_cert_chain(conn, ssl, rs)) { traceLog(LOG_ERR,"Certificate chain check failed"); SSL_free(ssl); conn->status = CNST_ACTIVE; return SPOCP_CERT_ERR; } /* * So the cert is OK and the hostname is in the DN, but do I want to * talk to this guy ?? */ cipher = SSL_get_current_cipher(ssl); conn->cipher = Strdup((char *) SSL_CIPHER_get_name(cipher)); conn->ssl_vers = Strdup(SSL_CIPHER_get_version(cipher)); if (server_access(conn) == 0) { traceLog(LOG_ERR,"Client not allowed access"); SSL_free(ssl); conn->status = CNST_ACTIVE; return SPOCP_CERT_ERR; } LOG(SPOCP_DEBUG) traceLog(LOG_DEBUG,"SSL accept done"); /* * TLS has been set up. Change input/output to read via TLS instead */ conn->readn = ssl_socket_readn; conn->writen = ssl_socket_writen; conn->close = tls_close; conn->ssl = (void *) ssl; conn->tls_ssf = SSL_CIPHER_get_bits(cipher, &maxbits); conn->status = CNST_ACTIVE; return SPOCP_SUCCESS; }