openssl3 should now solve licensing issues with openssl. there is no good reason to keep gnutls support anymore especially since there just isn't anyoen who wants to maintain that extra ifdef'd code (and that code has some gotchas that don't match the full features of openssl too). so this removed "code cruft" to maintain, complexity and maintenance work as well as build complexity.pull/14/head
parent
faf9745538
commit
d1f1af054f
20 changed files with 42 additions and 2009 deletions
@ -1,232 +0,0 @@ |
||||
//Compile with:
|
||||
// gcc -o ecore_fd_handler_gnutls_example ecore_fd_handler_gnutls_example.c `pkg-config --cflags --libs ecore gnutls`
|
||||
|
||||
#ifdef HAVE_CONFIG_H |
||||
#include "config.h" |
||||
#endif |
||||
|
||||
#include <fcntl.h> |
||||
#ifdef HAVE_NETINET_TCP_H |
||||
# include <netinet/tcp.h> |
||||
#endif |
||||
#ifdef HAVE_NETINET_IN_H |
||||
# include <netinet/in.h> |
||||
#endif |
||||
#ifdef HAVE_SYS_SOCKET_H |
||||
# include <sys/socket.h> |
||||
#endif |
||||
#ifdef HAVE_ARPA_INET_H |
||||
# include <arpa/inet.h> |
||||
#endif |
||||
#include <stdio.h> |
||||
#include <errno.h> |
||||
#include <unistd.h> |
||||
#include <gnutls/gnutls.h> |
||||
#include <Ecore.h> |
||||
|
||||
/* Ecore_Fd_Handler example
|
||||
* 2010 Mike Blumenkrantz |
||||
* compile with gcc $(pkgconfig --cflags --libs gnutls ecore) |
||||
*/ |
||||
|
||||
#define print(...) \ |
||||
do { \
|
||||
fprintf(stderr, "line %i: ", __LINE__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n");\
|
||||
} while(0) |
||||
|
||||
static int done = 0; |
||||
|
||||
static void |
||||
tls_log_func(int level, const char *str) |
||||
{ |
||||
fprintf(stderr, "|<%d>| %s", level, str); |
||||
} |
||||
|
||||
static const char * |
||||
SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_description_t status) |
||||
{ |
||||
switch (status) |
||||
{ |
||||
case GNUTLS_HANDSHAKE_HELLO_REQUEST: |
||||
return "Hello request"; |
||||
|
||||
case GNUTLS_HANDSHAKE_CLIENT_HELLO: |
||||
return "Client hello"; |
||||
|
||||
case GNUTLS_HANDSHAKE_SERVER_HELLO: |
||||
return "Server hello"; |
||||
|
||||
case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: |
||||
return "Certificate packet"; |
||||
|
||||
case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: |
||||
return "Server key exchange"; |
||||
|
||||
case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: |
||||
return "Certificate request"; |
||||
|
||||
case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: |
||||
return "Server hello done"; |
||||
|
||||
case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: |
||||
return "Certificate verify"; |
||||
|
||||
case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE: |
||||
return "Client key exchange"; |
||||
|
||||
case GNUTLS_HANDSHAKE_FINISHED: |
||||
return "Finished"; |
||||
|
||||
case GNUTLS_HANDSHAKE_SUPPLEMENTAL: |
||||
return "Supplemental"; |
||||
default: |
||||
return "Uncaught state"; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
/* Connects to the peer and returns a socket
|
||||
* descriptor. |
||||
*/ |
||||
static int |
||||
tcp_connect(void) |
||||
{ |
||||
const char *PORT = "443"; |
||||
const char *SERVER = "69.58.181.89"; //verisign.com
|
||||
int err, sd; |
||||
int flag = 1, curstate = 0; |
||||
struct sockaddr_in sa; |
||||
|
||||
/* sets some fd options such as nonblock */ |
||||
sd = socket(AF_INET, SOCK_STREAM, 0); |
||||
if (sd < 0) abort(); |
||||
if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) perror("fcntl"); |
||||
eina_file_close_on_exec(sd, EINA_TRUE); |
||||
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate, sizeof(curstate)) < 0) perror("setsockopt"); |
||||
|
||||
if (setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0) perror("setsockopt"); |
||||
|
||||
memset(&sa, '\0', sizeof (sa)); |
||||
sa.sin_family = AF_INET; |
||||
sa.sin_port = eina_htons(atoi(PORT)); |
||||
if (inet_pton(AF_INET, SERVER, &sa.sin_addr)) perror("inet_pton"); |
||||
|
||||
/* connects to server
|
||||
*/ |
||||
err = connect(sd, (struct sockaddr *)&sa, sizeof (sa)); |
||||
if ((err < 0) && (errno != EINPROGRESS)) |
||||
{ |
||||
print("Connect error\n"); |
||||
exit(1); |
||||
} |
||||
|
||||
return sd; |
||||
} |
||||
|
||||
/* closes the given socket descriptor.
|
||||
*/ |
||||
static void |
||||
tcp_close(int sd) |
||||
{ |
||||
#ifdef _WIN32 |
||||
shutdown(sd, SD_BOTH); /* no more receptions */ |
||||
closesocket(sd); |
||||
#else |
||||
shutdown(sd, SHUT_RDWR); /* no more receptions */ |
||||
close(sd); |
||||
#endif |
||||
} |
||||
|
||||
static Eina_Bool |
||||
_process_data(gnutls_session_t client, Ecore_Fd_Handler *fd_handler) |
||||
{ |
||||
static int ret, lastret; |
||||
static unsigned int count = 0; |
||||
|
||||
if (!done) |
||||
{ |
||||
lastret = ret; |
||||
ret = gnutls_handshake(client); |
||||
count++; |
||||
if (gnutls_record_get_direction(client)) |
||||
ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_WRITE); |
||||
else |
||||
ecore_main_fd_handler_active_set(fd_handler, ECORE_FD_READ); |
||||
/* avoid printing messages infinity times */ |
||||
if (lastret != ret) |
||||
{ |
||||
print("gnutls returned with: %s - %s", gnutls_strerror_name(ret), gnutls_strerror(ret)); |
||||
if ((ret == GNUTLS_E_WARNING_ALERT_RECEIVED) || (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)) |
||||
print("Also received alert: %s", gnutls_alert_get_name(gnutls_alert_get(client))); |
||||
print("last out: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_out(client))); |
||||
print("last in: %s", SSL_GNUTLS_PRINT_HANDSHAKE_STATUS(gnutls_handshake_get_last_in(client))); |
||||
} |
||||
|
||||
if (gnutls_error_is_fatal(ret)) |
||||
{ |
||||
print("yarrr this be an error!"); |
||||
exit(1); |
||||
} |
||||
} |
||||
if (ret == GNUTLS_E_SUCCESS) |
||||
{ |
||||
done = 1; |
||||
print("Handshake successful in %u handshake calls!", count); |
||||
ecore_main_loop_quit(); |
||||
} |
||||
|
||||
return ECORE_CALLBACK_RENEW; |
||||
} |
||||
|
||||
int |
||||
main(void) |
||||
{ |
||||
/* credentials */ |
||||
gnutls_anon_client_credentials_t c_anoncred; |
||||
gnutls_certificate_credentials_t c_certcred; |
||||
|
||||
gnutls_session_t client; |
||||
int sd; |
||||
|
||||
/* General init. */ |
||||
gnutls_global_init(); |
||||
ecore_init(); |
||||
gnutls_global_set_log_function(tls_log_func); |
||||
gnutls_global_set_log_level(6); |
||||
|
||||
/* Init client */ |
||||
gnutls_anon_allocate_client_credentials(&c_anoncred); |
||||
gnutls_certificate_allocate_credentials(&c_certcred); |
||||
gnutls_init(&client, GNUTLS_CLIENT); |
||||
/* set very specific priorities */ |
||||
gnutls_priority_set_direct(client, "NONE:%VERIFY_ALLOW_X509_V1_CA_CRT:+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+COMP-DEFLATE:+COMP-NULL:+CTYPE-X509:+SHA1:+SHA256:+SHA384:+SHA512:+AES-256-CBC:+AES-128-CBC:+3DES-CBC:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0:+VERS-SSL3.0", NULL); |
||||
gnutls_credentials_set(client, GNUTLS_CRD_ANON, c_anoncred); |
||||
gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, c_certcred); |
||||
gnutls_server_name_set(client, GNUTLS_NAME_DNS, "www.verisign.com", strlen("www.verisign.com")); |
||||
|
||||
/* connect to the peer
|
||||
*/ |
||||
sd = tcp_connect(); |
||||
|
||||
/* associate gnutls with socket */ |
||||
gnutls_transport_set_ptr(client, (gnutls_transport_ptr_t)(uintptr_t)sd); |
||||
/* add a callback for data being available for send/receive on socket */ |
||||
if (!ecore_main_fd_handler_add(sd, ECORE_FD_READ | ECORE_FD_WRITE, (Ecore_Fd_Cb)_process_data, client, NULL, NULL)) |
||||
{ |
||||
print("could not create fd handler!"); |
||||
exit(1); |
||||
} |
||||
/* begin main loop */ |
||||
ecore_main_loop_begin(); |
||||
|
||||
gnutls_bye(client, GNUTLS_SHUT_RDWR); |
||||
|
||||
gnutls_deinit(client); |
||||
|
||||
tcp_close(sd); |
||||
|
||||
return 0; |
||||
} |
||||
|
@ -1,362 +0,0 @@ |
||||
#include <gnutls/gnutls.h> |
||||
#include <gnutls/x509.h> |
||||
|
||||
struct _Efl_Net_Ssl_Conn { |
||||
gnutls_session_t session; |
||||
gnutls_datum_t ticket; |
||||
Eo *sock; |
||||
const char *hostname; |
||||
Efl_Net_Ssl_Verify_Mode verify_mode; |
||||
Eina_Bool hostname_verify; |
||||
Eina_Bool is_dialer; |
||||
}; |
||||
|
||||
static ssize_t |
||||
_efl_net_ssl_conn_write(gnutls_transport_ptr_t transp, const void *buf, size_t len) |
||||
{ |
||||
Eina_Slice slice = { |
||||
.mem = buf, |
||||
.len = len |
||||
}; |
||||
Efl_Net_Ssl_Conn *conn = transp; |
||||
Eina_Error err; |
||||
|
||||
if ((!buf) || (len == 0)) return 0; |
||||
if (!conn) return 0; |
||||
|
||||
if (!efl_io_writer_can_write_get(conn->sock)) |
||||
{ |
||||
DBG("socket=%p would block if written!", conn->sock); |
||||
gnutls_transport_set_errno(conn->session, EAGAIN); |
||||
return -1; |
||||
} |
||||
|
||||
err = efl_io_writer_write(conn->sock, &slice, NULL); |
||||
if (err) |
||||
{ |
||||
gnutls_transport_set_errno(conn->session, err); |
||||
return -1; |
||||
} |
||||
|
||||
gnutls_transport_set_errno(conn->session, 0); |
||||
return slice.len; |
||||
} |
||||
|
||||
static ssize_t |
||||
_efl_net_ssl_conn_read(gnutls_transport_ptr_t transp, void *buf, size_t len) |
||||
{ |
||||
Eina_Rw_Slice slice = { |
||||
.mem = buf, |
||||
.len = len |
||||
}; |
||||
Efl_Net_Ssl_Conn *conn = transp; |
||||
Eina_Error err; |
||||
|
||||
if ((!buf) || (len == 0)) return 0; |
||||
if (!conn) return 0; |
||||
|
||||
if (!efl_io_reader_can_read_get(conn->sock)) |
||||
{ |
||||
DBG("socket=%p would block if read!", conn->sock); |
||||
gnutls_transport_set_errno(conn->session, EAGAIN); |
||||
return -1; |
||||
} |
||||
|
||||
err = efl_io_reader_read(conn->sock, &slice); |
||||
if (err) |
||||
{ |
||||
gnutls_transport_set_errno(conn->session, err); |
||||
return -1; |
||||
} |
||||
|
||||
gnutls_transport_set_errno(conn->session, 0); |
||||
return slice.len; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_conn_setup(Efl_Net_Ssl_Conn *conn, Eina_Bool is_dialer, Efl_Net_Socket *sock, Efl_Net_Ssl_Context *context) |
||||
{ |
||||
gnutls_certificate_request_t req; |
||||
int r; |
||||
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(conn->session != NULL, EALREADY); |
||||
|
||||
conn->is_dialer = is_dialer; |
||||
|
||||
conn->session = efl_net_ssl_context_connection_new(context); |
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(conn->session, ENOSYS); |
||||
|
||||
gnutls_handshake_set_private_extensions(conn->session, 1); |
||||
|
||||
switch (conn->verify_mode) |
||||
{ |
||||
case EFL_NET_SSL_VERIFY_MODE_NONE: |
||||
req = GNUTLS_CERT_IGNORE; |
||||
break; |
||||
case EFL_NET_SSL_VERIFY_MODE_OPTIONAL: |
||||
req = GNUTLS_CERT_REQUEST; |
||||
break; |
||||
case EFL_NET_SSL_VERIFY_MODE_REQUIRED: |
||||
default: |
||||
req = GNUTLS_CERT_REQUIRE; |
||||
} |
||||
gnutls_certificate_server_set_request(conn->session, req); |
||||
|
||||
if (is_dialer) |
||||
{ |
||||
r = gnutls_session_ticket_enable_client(conn->session); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_conn=%p could not enable session's ticket client: %s", conn, gnutls_strerror(r)); |
||||
goto error; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
r = gnutls_session_ticket_key_generate(&conn->ticket); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_conn=%p could not generate session ticket: %s", conn, gnutls_strerror(r)); |
||||
goto error; |
||||
} |
||||
|
||||
r = gnutls_session_ticket_enable_server(conn->session, &conn->ticket); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_conn=%p could not enable session's ticket server: %s", conn, gnutls_strerror(r)); |
||||
goto error_ticket; |
||||
} |
||||
} |
||||
|
||||
conn->sock = sock; |
||||
gnutls_transport_set_ptr(conn->session, conn); |
||||
gnutls_transport_set_push_function(conn->session, _efl_net_ssl_conn_write); |
||||
gnutls_transport_set_pull_function(conn->session, _efl_net_ssl_conn_read); |
||||
|
||||
return 0; |
||||
|
||||
error_ticket: |
||||
gnutls_free(conn->ticket.data); |
||||
conn->ticket.data = NULL; |
||||
|
||||
error: |
||||
gnutls_deinit(conn->session); |
||||
conn->session = NULL; |
||||
return ENOSYS; |
||||
} |
||||
|
||||
static void |
||||
efl_net_ssl_conn_teardown(Efl_Net_Ssl_Conn *conn) |
||||
{ |
||||
if (conn->session) |
||||
{ |
||||
gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); |
||||
gnutls_deinit(conn->session); |
||||
conn->session = NULL; |
||||
} |
||||
|
||||
if (conn->ticket.data) |
||||
{ |
||||
gnutls_free(conn->ticket.data); |
||||
conn->ticket.data = NULL; |
||||
} |
||||
|
||||
eina_stringshare_replace(&conn->hostname, NULL); |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_conn_write(Efl_Net_Ssl_Conn *conn, Eina_Slice *slice) |
||||
{ |
||||
ssize_t r = gnutls_record_send(conn->session, slice->mem, slice->len); |
||||
if (r < 0) |
||||
{ |
||||
slice->len = 0; |
||||
if (gnutls_error_is_fatal(r)) |
||||
{ |
||||
ERR("ssl_conn=%p could not send %zd bytes: %s", conn, slice->len, gnutls_strerror(r)); |
||||
return EINVAL; |
||||
} |
||||
DBG("ssl_conn=%p could not send %zd bytes: %s", conn, slice->len, gnutls_strerror(r)); |
||||
return EAGAIN; |
||||
} |
||||
slice->len = r; |
||||
return 0; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_conn_read(Efl_Net_Ssl_Conn *conn, Eina_Rw_Slice *slice) |
||||
{ |
||||
ssize_t r = gnutls_record_recv(conn->session, slice->mem, slice->len); |
||||
if (r < 0) |
||||
{ |
||||
slice->len = 0; |
||||
if (gnutls_error_is_fatal(r)) |
||||
{ |
||||
ERR("ssl_conn=%p could not receive %zd bytes: %s", conn, slice->len, gnutls_strerror(r)); |
||||
return EINVAL; |
||||
} |
||||
DBG("ssl_conn=%p could not receive %zd bytes: %s", conn, slice->len, gnutls_strerror(r)); |
||||
return EAGAIN; |
||||
} |
||||
slice->len = r; |
||||
return 0; |
||||
} |
||||
|
||||
static Eina_Error |
||||
_efl_net_ssl_conn_verify(Efl_Net_Ssl_Conn *conn) |
||||
{ |
||||
unsigned status = 0; |
||||
int r; |
||||
|
||||
r = gnutls_certificate_verify_peers2(conn->session, &status); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_conn=%p could not verify peer: %s", conn, gnutls_strerror(r)); |
||||
return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE; |
||||
} |
||||
|
||||
if (!status) return 0; |
||||
|
||||
if (status & GNUTLS_CERT_INVALID) |
||||
WRN("ssl_conn=%p The certificate is not trusted.", conn); |
||||
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) |
||||
WRN("ssl_conn=%p The certificate hasn't got a known issuer.", conn); |
||||
if (status & GNUTLS_CERT_REVOKED) |
||||
WRN("ssl_conn=%p The certificate has been revoked.", conn); |
||||
if (status & GNUTLS_CERT_EXPIRED) |
||||
WRN("ssl_conn=%p The certificate has expired", conn); |
||||
if (status & GNUTLS_CERT_NOT_ACTIVATED) |
||||
WRN("ssl_conn=%p The certificate is not yet activated", conn); |
||||
|
||||
return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED; |
||||
} |
||||
|
||||
static Eina_Error |
||||
_efl_net_ssl_conn_hostname_verify(Efl_Net_Ssl_Conn *conn) |
||||
{ |
||||
const gnutls_datum_t *list; |
||||
unsigned int size; |
||||
gnutls_x509_crt_t cert = NULL; |
||||
int r; |
||||
|
||||
if ((!conn->hostname) || (conn->hostname[0] == '\0')) |
||||
{ |
||||
ERR("ssl_conn=%p no hostname, cannot verify", conn); |
||||
return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED; |
||||
} |
||||
|
||||
if (gnutls_certificate_type_get(conn->session) != GNUTLS_CRT_X509) |
||||
{ |
||||
ERR("ssl_conn=%p PGP certificates are not yet supported!", conn); |
||||
return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED; |
||||
} |
||||
|
||||
list = gnutls_certificate_get_peers(conn->session, &size); |
||||
if (!list) |
||||
{ |
||||
ERR("ssl_conn=%p no peer certificate!", conn); |
||||
return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE; |
||||
} |
||||
|
||||
r = gnutls_x509_crt_init(&cert); |
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(r < 0, EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED); |
||||
|
||||
r = gnutls_x509_crt_import(cert, &list[0], GNUTLS_X509_FMT_DER); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_conn=%p could not import x509 certificate to verify: %s", conn, gnutls_strerror(r)); |
||||
gnutls_x509_crt_deinit(cert); |
||||
return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED; |
||||
} |
||||
|
||||
r = gnutls_x509_crt_check_hostname(cert, conn->hostname); |
||||
gnutls_x509_crt_deinit(cert); |
||||
|
||||
if (r == 1) |
||||
return 0; |
||||
|
||||
ERR("ssl_conn=%p hostname='%s' doesn't match certificate.", |
||||
conn, conn->hostname); |
||||
return EFL_NET_SOCKET_SSL_ERROR_CERTIFICATE_VERIFY_FAILED; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_conn_handshake(Efl_Net_Ssl_Conn *conn, Eina_Bool *done) |
||||
{ |
||||
int r = gnutls_handshake(conn->session); |
||||
if (r < 0) |
||||
{ |
||||
*done = EINA_FALSE; |
||||
if (gnutls_error_is_fatal(r)) |
||||
{ |
||||
ERR("ssl_conn=%p could not handshake: %s", conn, gnutls_strerror(r)); |
||||
return EFL_NET_SOCKET_SSL_ERROR_HANDSHAKE; |
||||
} |
||||
|
||||
DBG("ssl_conn=%p did not finish handshake: %s", conn, gnutls_strerror(r)); |
||||
return 0; |
||||
} |
||||
|
||||
if (conn->verify_mode != EFL_NET_SSL_VERIFY_MODE_NONE) |
||||
{ |
||||
Eina_Error err = _efl_net_ssl_conn_verify(conn); |
||||
if (err) |
||||
return err; |
||||
} |
||||
|
||||
if (conn->hostname_verify) |
||||
{ |
||||
Eina_Error err = _efl_net_ssl_conn_hostname_verify(conn); |
||||
if (err) |
||||
return err; |
||||
} |
||||
|
||||
*done = EINA_TRUE; |
||||
DBG("ssl_conn=%p handshake finished!", conn); |
||||
return 0; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_conn_verify_mode_set(Efl_Net_Ssl_Conn *conn, Efl_Net_Ssl_Verify_Mode verify_mode) |
||||
{ |
||||
gnutls_certificate_request_t req; |
||||
conn->verify_mode = verify_mode; |
||||
|
||||
switch (conn->verify_mode) |
||||
{ |
||||
case EFL_NET_SSL_VERIFY_MODE_NONE: |
||||
req = GNUTLS_CERT_IGNORE; |
||||
break; |
||||
case EFL_NET_SSL_VERIFY_MODE_OPTIONAL: |
||||
req = GNUTLS_CERT_REQUEST; |
||||
break; |
||||
case EFL_NET_SSL_VERIFY_MODE_REQUIRED: |
||||
default: |
||||
req = GNUTLS_CERT_REQUIRE; |
||||
} |
||||
gnutls_certificate_server_set_request(conn->session, req); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_conn_hostname_verify_set(Efl_Net_Ssl_Conn *conn, Eina_Bool hostname_verify) |
||||
{ |
||||
conn->hostname_verify = hostname_verify; |
||||
return 0; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_conn_hostname_override_set(Efl_Net_Ssl_Conn *conn, const char *hostname) |
||||
{ |
||||
int r; |
||||
eina_stringshare_replace(&conn->hostname, hostname); |
||||
if (!hostname) hostname = ""; |
||||
r = gnutls_server_name_set(conn->session, GNUTLS_NAME_DNS, hostname, strlen(hostname)); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_conn=%p could not set server name '%s': %s", conn, hostname, gnutls_strerror(r)); |
||||
return EINVAL; |
||||
} |
||||
return 0; |
||||
} |
@ -1,307 +0,0 @@ |
||||
#include <gnutls/gnutls.h> |
||||
|
||||
struct _Efl_Net_Ssl_Ctx { |
||||
gnutls_certificate_credentials_t x509_cred; |
||||
gnutls_priority_t priority; |
||||
Eina_Bool is_dialer; |
||||
}; |
||||
|
||||
static Eina_Error |
||||
_efl_net_ssl_ctx_load_lists(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg) |
||||
{ |
||||
Eina_List *n, *n_next, *pk_node; |
||||
const char *path; |
||||
unsigned certificates_count = eina_list_count(*cfg.certificates); |
||||
unsigned private_keys_count = eina_list_count(*cfg.private_keys); |
||||
unsigned certificate_revocation_lists_count = eina_list_count(*cfg.certificate_revocation_lists); |
||||
unsigned certificate_authorities_count = eina_list_count(*cfg.certificate_authorities); |
||||
int r; |
||||
|
||||
ctx->is_dialer = cfg.is_dialer; |
||||
|
||||
if (cfg.load_defaults) |
||||
{ |
||||
r = gnutls_certificate_set_x509_system_trust(ctx->x509_cred); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not load default paths: %s", ctx, gnutls_strerror(r)); |
||||
return ENOSYS; |
||||
} |
||||
DBG("ssl_ctx=%p loaded default paths", ctx); |
||||
} |
||||
else |
||||
DBG("ssl_ctx=%p did not load default paths", ctx); |
||||
|
||||
/* GNUTLS needs certificate-key pairs, so we do:
|
||||
* |
||||
* - if no private keys, use certificate as its own key; |
||||
* |
||||
* - if a private keys, walk the list alongside certificates, but |
||||
* do NOT delete elements if list sizes are different. Stop at |
||||
* last private key, allowing a single private key for multiple |
||||
* certificates. |
||||
*/ |
||||
pk_node = *cfg.private_keys; |
||||
EINA_LIST_FOREACH_SAFE(*cfg.certificates, n, n_next, path) |
||||
{ |
||||
const char *key = pk_node ? pk_node->data : path; |
||||
|
||||
r = gnutls_certificate_set_x509_key_file(ctx->x509_cred, path, key, GNUTLS_X509_FMT_PEM); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not use certificate from '%s' with key '%s': %s", |
||||
ctx, path, key, gnutls_strerror(r)); |
||||
|
||||
if (pk_node) |
||||
{ |
||||
if (eina_list_count(*cfg.private_keys) == eina_list_count(*cfg.certificates)) |
||||
{ |
||||
pk_node = pk_node->next; |
||||
eina_stringshare_del(key); |
||||
*cfg.private_keys = eina_list_remove_list(*cfg.private_keys, pk_node->prev); |
||||
} |
||||
else if (pk_node->next) pk_node = pk_node->next; |
||||
} |
||||
|
||||
eina_stringshare_del(path); |
||||
*cfg.certificates = eina_list_remove_list(*cfg.certificates, n); |
||||
continue; |
||||
} |
||||
else |
||||
{ |
||||
if (pk_node->next) pk_node = pk_node->next; |
||||
} |
||||
|
||||
DBG("ssl_ctx=%p loaded certificate '%s' with key '%s'", ctx, path, key); |
||||
} |
||||
if (certificates_count && !*cfg.certificates) |
||||
{ |
||||
ERR("ssl_ctx=%p none of the required certificates were loaded!", ctx); |
||||
return EINVAL; |
||||
} |
||||
|
||||
if (private_keys_count && !*cfg.private_keys) |
||||
{ |
||||
ERR("ssl_ctx=%p none of the required private keys were loaded!", ctx); |
||||
return EINVAL; |
||||
} |
||||
else if (pk_node != eina_list_last(*cfg.private_keys)) |
||||
{ |
||||
do |
||||
{ |
||||
n = pk_node->next; |
||||
path = n->data; |
||||
ERR("ssl_ctx=%p extra private key is unused '%s'", ctx, path); |
||||
eina_stringshare_del(path); |
||||
*cfg.private_keys = eina_list_remove_list(*cfg.private_keys, n); |
||||
} |
||||
while (pk_node->next); |
||||
} |
||||
|
||||
EINA_LIST_FOREACH_SAFE(*cfg.certificate_revocation_lists, n, n_next, path) |
||||
{ |
||||
r = gnutls_certificate_set_x509_crl_file(ctx->x509_cred, path, GNUTLS_X509_FMT_PEM); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not use certificate revocation lists from %s: %s", |
||||
ctx, path, gnutls_strerror(r)); |
||||
eina_stringshare_del(path); |
||||
*cfg.certificate_revocation_lists = eina_list_remove_list(*cfg.certificate_revocation_lists, n); |
||||
continue; |
||||
} |
||||
|
||||
DBG("ssl_ctx=%p loaded certificate revocation lists '%s'", ctx, path); |
||||
} |
||||
if (certificate_revocation_lists_count && !*cfg.certificate_revocation_lists) |
||||
{ |
||||
ERR("ssl_ctx=%p none of the required certificate revocation lists were loaded!", ctx); |
||||
return EINVAL; |
||||
} |
||||
|
||||
EINA_LIST_FOREACH_SAFE(*cfg.certificate_authorities, n, n_next, path) |
||||
{ |
||||
struct stat st; |
||||
|
||||
r = 0; |
||||
if (stat(path, &st) != 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not load certificate authorities from '%s': %s", ctx, path, eina_error_msg_get(errno)); |
||||
eina_stringshare_del(path); |
||||
*cfg.certificate_authorities = eina_list_remove_list(*cfg.certificate_authorities, n); |
||||
continue; |
||||
} |
||||
else if (S_ISDIR(st.st_mode)) |
||||
r = gnutls_certificate_set_x509_trust_dir(ctx->x509_cred, path, GNUTLS_X509_FMT_PEM); |
||||
else |
||||
r = gnutls_certificate_set_x509_trust_file(ctx->x509_cred, path, GNUTLS_X509_FMT_PEM); |
||||
|
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not use certificate authorities from '%s': %s", ctx, path, gnutls_strerror(r)); |
||||
eina_stringshare_del(path); |
||||
*cfg.certificate_authorities = eina_list_remove_list(*cfg.certificate_authorities, n); |
||||
continue; |
||||
} |
||||
|
||||
DBG("ssl_ctx=%p loaded certificate authorities '%s'", ctx, path); |
||||
} |
||||
if (certificate_authorities_count && !*cfg.certificate_authorities) |
||||
{ |
||||
ERR("ssl_ctx=%p none of the required certificate authorities were loaded!", ctx); |
||||
return EINVAL; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void * |
||||
efl_net_ssl_ctx_connection_new(Efl_Net_Ssl_Ctx *ctx) |
||||
{ |
||||
gnutls_session_t session; |
||||
int r; |
||||
|
||||
r = gnutls_init(&session, ctx->is_dialer ? GNUTLS_CLIENT : GNUTLS_SERVER); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not create %s session: %s", |
||||
ctx, ctx->is_dialer ? "dialer" : "server", gnutls_strerror(r)); |
||||
return NULL; |
||||
} |
||||
|
||||
if (!ctx->priority) |
||||
{ |
||||
r = gnutls_set_default_priority(session); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not set default cipher priority: %s", ctx, gnutls_strerror(r)); |
||||
goto error; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
r = gnutls_priority_set(session, ctx->priority); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not set cipher priority: %s", ctx, gnutls_strerror(r)); |
||||
goto error; |
||||
} |
||||
} |
||||
|
||||
r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctx->x509_cred); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not set session credentials: %s", ctx, gnutls_strerror(r)); |
||||
goto error; |
||||
} |
||||
|
||||
return session; |
||||
|
||||
error: |
||||
gnutls_deinit(session); |
||||
return NULL; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_ctx_setup(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg) |
||||
{ |
||||
Eina_Error err; |
||||
const char *priority; |
||||
int r; |
||||
|
||||
EINA_SAFETY_ON_TRUE_RETURN_VAL(ctx->x509_cred != NULL, EALREADY); |
||||
|
||||
switch (cfg.cipher) |
||||
{ |
||||
case EFL_NET_SSL_CIPHER_AUTO: |
||||
priority = NULL; |
||||
break; |
||||
case EFL_NET_SSL_CIPHER_TLSV1: |
||||
priority = "NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-SSL3.0!VERS-TLS1.1:!VERS-TLS1.2"; |
||||
break; |
||||
case EFL_NET_SSL_CIPHER_TLSV1_1: |
||||
priority = "NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.2"; |
||||
break; |
||||
case EFL_NET_SSL_CIPHER_TLSV1_2: |
||||
priority = "NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1"; |
||||
break; |
||||
default: |
||||
ERR("ssl_ctx=%p unsupported cipher %d", ctx, cfg.cipher); |
||||
return EINVAL; |
||||
} |
||||
|
||||
if (priority) |
||||
{ |
||||
const char *err_pos = NULL; |
||||
r = gnutls_priority_init(&ctx->priority, priority, &err_pos); |
||||
if (r < 0) |
||||
{ |
||||
size_t off = err_pos - priority; |
||||
if (r == GNUTLS_E_INVALID_REQUEST) |
||||
{ |
||||
ERR("ssl_ctx=%p invalid syntax on GNUTLS priority string offset %zd: '%s'", ctx, off, priority); |
||||
return EINVAL; |
||||
} |
||||
ERR("ssl_ctx=%p could not set GNUTLS priority offset %zd '%s': %s", ctx, off, priority, gnutls_strerror(r)); |
||||
return EINVAL; |
||||
} |
||||
} |
||||
|
||||
r = gnutls_certificate_allocate_credentials(&ctx->x509_cred); |
||||
if (r < 0) |
||||
{ |
||||
ERR("ssl_ctx=%p could not allocate X509 credentials: %s", ctx, gnutls_strerror(r)); |
||||
err = ENOSYS; |
||||
goto err_cert_alloc; |
||||
} |
||||
|
||||
err = _efl_net_ssl_ctx_load_lists(ctx, cfg); |
||||
if (err) |
||||
{ |
||||
ERR("ssl_ctx=%p failed to load certificate, private keys, CRL or CA", ctx); |
||||
goto err_load; |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
err_load: |
||||
gnutls_certificate_free_credentials(ctx->x509_cred); |
||||
ctx->x509_cred = NULL; |
||||
err_cert_alloc: |
||||
gnutls_priority_deinit(ctx->priority); |
||||
ctx->priority = NULL; |
||||
return err; |
||||
} |
||||
|
||||
static void |
||||
efl_net_ssl_ctx_teardown(Efl_Net_Ssl_Ctx *ctx) |
||||
{ |
||||
if (ctx->x509_cred) |
||||
{ |
||||
gnutls_certificate_free_credentials(ctx->x509_cred); |
||||
ctx->x509_cred = NULL; |
||||
} |
||||
|
||||
if (ctx->priority) |
||||
{ |
||||
gnutls_priority_deinit(ctx->priority); |
||||
ctx->priority = NULL; |
||||
} |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_ctx_verify_mode_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, Efl_Net_Ssl_Verify_Mode verify_mode EINA_UNUSED) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_ctx_hostname_verify_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, Eina_Bool hostname_verify EINA_UNUSED) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
static Eina_Error |
||||
efl_net_ssl_ctx_hostname_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, const char *hostname EINA_UNUSED) |
||||
{ |
||||
return 0; |
||||
} |