diff options
author | Cedric BAIL <cedric.bail@samsung.com> | 2015-01-26 15:49:05 +0100 |
---|---|---|
committer | Cedric BAIL <cedric@osg.samsung.com> | 2015-02-16 14:47:48 +0100 |
commit | d56a8f9fd01c2b8c453b8619cc8c63339d50fe51 (patch) | |
tree | 2c6d330975cac60bfac670874154c4f79629c9a6 | |
parent | a69aa000ee96b6f1c59bb50df1c0a0a79a55bfc9 (diff) | |
download | efl-d56a8f9fd01c2b8c453b8619cc8c63339d50fe51.tar.gz |
emile: Add SSL support.
-rw-r--r-- | src/lib/ecore_con/ecore_con_ssl.c | 2 | ||||
-rw-r--r-- | src/lib/emile/Emile.h | 36 | ||||
-rw-r--r-- | src/lib/emile/emile_cipher.c | 110 | ||||
-rw-r--r-- | src/lib/emile/emile_cipher_gnutls.c | 312 | ||||
-rw-r--r-- | src/lib/emile/emile_cipher_openssl.c | 844 | ||||
-rw-r--r-- | src/lib/emile/emile_main.c | 2 | ||||
-rw-r--r-- | src/lib/emile/emile_private.h | 8 |
7 files changed, 1311 insertions, 3 deletions
diff --git a/src/lib/ecore_con/ecore_con_ssl.c b/src/lib/ecore_con/ecore_con_ssl.c index 7e416ec8ee..5d7bbcbb2b 100644 --- a/src/lib/ecore_con/ecore_con_ssl.c +++ b/src/lib/ecore_con/ecore_con_ssl.c @@ -1498,8 +1498,6 @@ _ecore_con_ssl_client_write_gnutls(Ecore_Con_Client *obj, */ static Ecore_Con_Ssl_Error - -static Ecore_Con_Ssl_Error _ecore_con_ssl_server_prepare_openssl(Ecore_Con_Server *obj, int ssl_type) { diff --git a/src/lib/emile/Emile.h b/src/lib/emile/Emile.h index b97c30eafe..47a9186d3b 100644 --- a/src/lib/emile/Emile.h +++ b/src/lib/emile/Emile.h @@ -106,7 +106,24 @@ EAPI int emile_shutdown(void); * @} */ +typedef struct _Emile_SSL Emile_SSL; + +typedef enum +{ + EMILE_SSLv23, + EMILE_SSLv3, + EMILE_TLSv1 +} Emile_Cipher_Type; + +typedef enum +{ + EMILE_WANT_NOTHING = 0, + EMILE_WANT_READ = 1, + EMILE_WANT_WRITE = 3 +} Emile_Want_Type; + EAPI Eina_Bool emile_cipher_init(void); +EAPI const char *emile_cipher_module_get(void); EAPI Eina_Binbuf *emile_binbuf_cipher(const Eina_Binbuf *in, const char *key, unsigned int length); @@ -114,6 +131,25 @@ EAPI Eina_Binbuf *emile_binbuf_cipher(const Eina_Binbuf *in, EAPI Eina_Binbuf *emile_binbuf_decipher(const Eina_Binbuf *in, const char *key, unsigned int length); +EAPI Emile_SSL *emile_cipher_server_listen(Emile_Cipher_Type t); +EAPI Emile_SSL *emile_cipher_client_connect(Emile_SSL *server, int fd); +EAPI Emile_SSL *emile_cipher_server_connect(Emile_Cipher_Type t); +EAPI Eina_Bool emile_cipher_free(Emile_SSL *emile); + +EAPI Eina_Bool emile_cipher_cafile_add(Emile_SSL *emile, const char *file); +EAPI Eina_Bool emile_cipher_cert_add(Emile_SSL *emile, const char *file); +EAPI Eina_Bool emile_cipher_privkey_add(Emile_SSL *emile, const char *file); +EAPI Eina_Bool emile_cipher_crl_add(Emile_SSL *emile, const char *file); +EAPI int emile_cipher_read(Emile_SSL *emile, Eina_Binbuf *buffer); +EAPI int emile_cipher_write(Emile_SSL *emile, const Eina_Binbuf *buffer); +EAPI const char *emile_cipher_error_get(const Emile_SSL *emile); +EAPI Eina_Bool emile_cipher_verify_name_set(Emile_SSL *emile, const char *name); +EAPI const char *emile_cipher_verify_name_get(const Emile_SSL *emile); +EAPI void emile_cipher_verify_set(Emile_SSL *emile, Eina_Bool verify); +EAPI void emile_cipher_verify_basic_set(Emile_SSL *emile, Eina_Bool verify_basic); +EAPI Eina_Bool emile_cipher_verify_get(const Emile_SSL *emile); +EAPI Eina_Bool emile_cipher_verify_basic_get(const Emile_SSL *emile); + typedef enum { EMILE_ZLIB, diff --git a/src/lib/emile/emile_cipher.c b/src/lib/emile/emile_cipher.c index 3af06886b9..3c5fd185cc 100644 --- a/src/lib/emile/emile_cipher.c +++ b/src/lib/emile/emile_cipher.c @@ -28,3 +28,113 @@ emile_binbuf_decipher(const Eina_Binbuf *data EINA_UNUSED, { return NULL; } + +EAPI Emile_SSL * +emile_cipher_server_listen(Emile_Cipher_Type t EINA_UNUSED) +{ + return NULL; +} + +EAPI Emile_SSL * +emile_cipher_client_connect(Emile_SSL *server EINA_UNUSED, int fd EINA_UNUSED) +{ + return NULL; +} + +EAPI Emile_SSL * +emile_cipher_server_connect(Emile_Cipher_Type t EINA_UNUSED) +{ + return NULL; +} + +EAPI Eina_Bool +emile_cipher_free(Emile_SSL *emile EINA_UNUSED) +{ + return EINA_TRUE; +} + +EAPI Eina_Bool +emile_cipher_cafile_add(Emile_SSL *emile EINA_UNUSED, + const char *file EINA_UNUSED) +{ + return EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_cert_add(Emile_SSL *emile EINA_UNUSED, + const char *file EINA_UNUSED) +{ + return EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_privkey_add(Emile_SSL *emile EINA_UNUSED, + const char *file EINA_UNUSED) +{ + return EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_crl_add(Emile_SSL *emile EINA_UNUSED, + const char *file EINA_UNUSED) +{ + return EINA_FALSE; +} + +EAPI int +emile_cipher_read(Emile_SSL *emile EINA_UNUSED, + Eina_Binbuf *buffer EINA_UNUSED) +{ + return EINA_FALSE; +} + +EAPI int +emile_cipher_write(Emile_SSL *emile EINA_UNUSED, + const Eina_Binbuf *buffer EINA_UNUSED) +{ + return EINA_FALSE; +} + + +EAPI const char * +emile_cipher_error_get(const Emile_SSL *emile EINA_UNUSED) +{ + return NULL; +} + +EAPI Eina_Bool +emile_cipher_verify_name_set(Emile_SSL *emile EINA_UNUSED, + const char *name EINA_UNUSED) +{ + return EINA_FALSE; +} + +EAPI const char * +emile_cipher_verify_name_get(const Emile_SSL *emile EINA_UNUSED) +{ + return NULL; +} + +EAPI void +emile_cipher_verify_set(Emile_SSL *emile EINA_UNUSED, + Eina_Bool verify EINA_UNUSED) +{ +} + +EAPI void +emile_cipher_verify_basic_set(Emile_SSL *emile EINA_UNUSED, + Eina_Bool verify_basic EINA_UNUSED) +{ +} + +EAPI Eina_Bool +emile_cipher_verify_get(const Emile_SSL *emile EINA_UNUSED) +{ + return EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_verify_basic_get(const Emile_SSL *emile EINA_UNUSED) +{ + return EINA_FALSE; +} diff --git a/src/lib/emile/emile_cipher_gnutls.c b/src/lib/emile/emile_cipher_gnutls.c index f69d25d346..ed48af5f92 100644 --- a/src/lib/emile/emile_cipher_gnutls.c +++ b/src/lib/emile/emile_cipher_gnutls.c @@ -19,6 +19,37 @@ #define MAX_KEY_LEN 32 #define MAX_IV_LEN 16 +struct _Emile_SSL +{ + const char *last_error; + const char *cert_file; + const char *name; + + gnutls_certificate_credentials_t cert; + gnutls_session_t session; + + union { + struct { + gnutls_datum_t session_ticket; + } client; + struct { + gnutls_anon_client_credentials_t anoncred_c; + gnutls_anon_server_credentials_t anoncred_s; + gnutls_psk_client_credentials_t pskcred_c; + gnutls_psk_server_credentials_t pskcred_s; + char *cert_file; + gnutls_dh_params_t dh_params; + } server; + } u; + + Emile_Cipher_Type t; + Emile_SSL_State ssl_state; + + Eina_Bool server : 1; + Eina_Bool verify : 1; + Eina_Bool verify_basic : 1; +}; + static int _emile_thread_mutex_init(void **priv) { @@ -383,3 +414,284 @@ on_error: return NULL; } + + +EAPI Eina_Bool +emile_cipher_cafile_add(Emile_SSL *emile, const char *file) +{ + struct stat st; + int count = 0; + + if (stat(file, &st)) return EINA_FALSE; + if (S_ISDIR(st.st_mode)) + { + Eina_File_Direct_Info *info; + Eina_Iterator *it; + int err; + + it = eina_file_direct_ls(file); + EINA_ITERATOR_FOREACH(it, info) + { + if (info->type != EINA_FILE_REG && + info->type != EINA_FILE_LNK) + continue; + + err = gnutls_certificate_set_x509_trust_file(emile->cert, + info->path, + GNUTLS_X509_FMT_PEM); + if (err > 0) count += err; + else DBG("File '%s' could not be loaded.", info->path); + } + eina_iterator_free(it); + } + else + { + count = gnutls_certificate_set_x509_trust_file(emile->cert, + file, + GNUTLS_X509_FMT_PEM); + if (count <= 0) DBG("File '%s' could not be loaded.", file); + } + + return count > 0 ? EINA_TRUE : EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_privkey_add(Emile_SSL *emile, const char *file) +{ + int err; + + err = gnutls_certificate_set_x509_key_file(emile->cert, + emile->cert_file, + file, + GNUTLS_X509_FMT_PEM); + + if (err <= 0) DBG("Could not load certificate/key '%s'.", file); + return err > 0 ? EINA_TRUE : EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_crl_add(Emile_SSL *emile, const char *file) +{ + int err; + + err = gnutls_certificate_set_x509_crl_file(emile->cert, + file, + GNUTLS_X509_FMT_PEM); + if (err <= 0) DBG("Could not load CRL '%s'.", file); + return err > 0 ? EINA_TRUE : EINA_FALSE; +} + +EAPI Emile_SSL * +emile_cipher_server_listen(Emile_Cipher_Type t) +{ + Emile_SSL *r; + int ret; + + if (t != EMILE_SSLv23 && + t != EMILE_SSLv3 && + t != EMILE_TLSv1) + return NULL; + + r = calloc(1, sizeof (Emile_SSL)); + if (!r) return NULL; + + ret = gnutls_certificate_allocate_credentials(&r->cert); + if (ret) goto on_error; + + r->t = t; + r->server = EINA_TRUE; + + return r; + + on_error: + ERR("GNUTLS error: %s - %s.", + gnutls_strerror_name(ret), + gnutls_strerror(ret)); + emile_cipher_free(r); + return NULL; +} + +EAPI Emile_SSL * +emile_cipher_client_connect(Emile_SSL *server, int fd) +{ +} + +EAPI Emile_SSL * +emile_cipher_server_connect(Emile_Cipher_Type t) +{ + const char *priority = "NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT"; + Emile_SSL *r; + int ret; + + switch (t) + { + case EMILE_SSLv23: + break; + case EMILE_SSLv3: + priority = "NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-TLS1.0:!VERS-TLS1.1:!VERS-TLS1.2"; + break; + case EMILE_TLSv1: + priority = "NORMAL:%VERIFY_ALLOW_X509_V1_CA_CRT:!VERS-SSL3.0"; + break; + default: + return NULL; + } + + r = calloc(1, sizeof (Emile_SSL)); + if (!r) return NULL; + + r->server = EINA_FALSE; + + ret = gnutls_certificate_allocate_credentials(&r->cert); + if (ret) goto on_error; + + ret = gnutls_init(&r->session, GNUTLS_CLIENT); + if (ret) goto on_error; + + ret = gnutls_session_ticket_enable_client(r->session); + if (ret) goto on_error; + + // FIXME: Delay that until later access + + ret = gnutls_server_name_set(r->session, GNUTLS_NAME_DNS, + r->name, strlen(r->name)); + if (ret) goto on_error; + + ret = gnutls_priority_set_direct(r->session, priority, NULL); + if (ret) goto on_error; + + gnutls_handshake_set_private_extensions(r->session, 1); + ret = gnutls_credentials_set(r->session, GNUTLS_CRD_CERTIFICATE, r->cert)); + + return r; +} + +EAPI Eina_Bool +emile_cipher_free(Emile_SSL *emile) +{ +} + +EAPI Eina_Bool +emile_cipher_cafile_add(Emile_SSL *emile, const char *file) +{ + Eina_File_Direct_Info *info; + Eina_Iterator *it; + struct stat st; + int count = 0; + int ret; + + if (stat(file, &st)) return EINA_FALSE; + if (S_ISDIR(st.st_mode)) + { + it = eina_file_direct_ls(file); + EINA_ITERATOR_FOREACH(it, info) + { + if (!(info->type == EINA_FILE_UNKNOWN || + info->type == EINA_FILE_REG || + info->type == EINA_FILE_LNK)) + continue ; + + ret = gnutls_certificate_set_x509_trust_file(emile->cert, + file, + GNUTLS_X509_FMT_PEM); + if (ret > 0) count += ret; + } + eina_iterator_free(it); + } + else + { + ret = gnutls_certificate_set_x509_trust_file(emile->cert, + file, + GNUTLS_X509_FMT_PEM); + if (ret > 0) count += ret; + } + + if (!count) ERR("Could not load CA file from '%s'.", file); + return !count ? EINA_FALSE : EINA_TRUE; +} + +EAPI Eina_Bool +emile_cipher_cert_add(Emile_SSL *emile, const char *file) +{ + return eina_stringshare_replace(&emile->cert_file, file); +} + +EAPI Eina_Bool +emile_cipher_privkey_add(Emile_SSL *emile, const char *file) +{ + int ret; + + ret = gnutls_certificate_set_x509_key_file(emile->cert, + emile->cert_file, + file, + GNUTLS_X509_FMT_PEM); + if (ret) + ERR("Could not load certificate/key file ('%s'/'%s').", + emile->cert_file, file); + return ret ? EINA_FALSE : EINA_TRUE; +} + +EAPI Eina_Bool +emile_cipher_crl_add(Emile_SSL *emile, const char *file) +{ + int ret; + + ret = gnutls_certificate_set_x509_crl_file(emile->cert, file, + GNUTLS_X509_FMT_PEM); + if (ret) + ERR("Could not load CRL file from '%s'.", file); + return ret ? EINA_FALSE : EINA_TRUE; +} + +EAPI int +emile_cipher_read(Emile_SSL *emile, Eina_Binbuf *buffer) +{ +} + +EAPI int +emile_cipher_write(Emile_SSL *emile, const Eina_Binbuf *buffer) +{ +} + +EAPI const char * +emile_cipher_error_get(const Emile_SSL *emile) +{ + return emile->last_error; +} + +EAPI Eina_Bool +emile_cipher_verify_name_set(Emile_SSL *emile, const char *name) +{ + return eina_stringshare_replace(&emile->name, name); +} + +EAPI const char * +emile_cipher_verify_name_get(const Emile_SSL *emile) +{ + return emile->name; +} + +EAPI void +emile_cipher_verify_set(Emile_SSL *emile, Eina_Bool verify) +{ + emile->verify = verify; +} + +EAPI void +emile_cipher_verify_basic_set(Emile_SSL *emile, Eina_Bool verify_basic) +{ + emile->verify_basic = verify_basic; +} + +EAPI Eina_Bool +emile_cipher_verify_get(const Emile_SSL *emile) +{ + return emile->verify; +} + +EAPI Eina_Bool +emile_cipher_verify_basic_get(const Emile_SSL *emile) +{ + return emile->verify_basic; +} + diff --git a/src/lib/emile/emile_cipher_openssl.c b/src/lib/emile/emile_cipher_openssl.c index fdb36a5f8e..6c3e746c37 100644 --- a/src/lib/emile/emile_cipher_openssl.c +++ b/src/lib/emile/emile_cipher_openssl.c @@ -10,6 +10,9 @@ #include <openssl/evp.h> #include <openssl/hmac.h> #include <openssl/rand.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/dh.h> #include <Eina.h> @@ -20,6 +23,29 @@ #define MAX_KEY_LEN EVP_MAX_KEY_LENGTH #define MAX_IV_LEN EVP_MAX_IV_LENGTH +struct _Emile_SSL +{ + Emile_SSL *parent; + SSL_CTX *ssl_ctx; + SSL *ssl; + + const char *last_error; + const char *verify_name; + + int ssl_err; + Emile_SSL_State ssl_state; + Emile_Want_Type ssl_want; + + Eina_Bool server : 1; + Eina_Bool listen : 1; + Eina_Bool connecting : 1; + Eina_Bool handshaking : 1; + Eina_Bool upgrade : 1; + Eina_Bool crl_flag : 1; + Eina_Bool verify : 1; + Eina_Bool verify_basic : 1; +}; + Eina_Bool _emile_cipher_init(void) { @@ -281,3 +307,821 @@ on_error: return NULL; } + +EAPI Emile_SSL * +emile_cipher_server_listen(Emile_Cipher_Type t) +{ + Emile_SSL *r; + DH *dh_params = NULL; + int options; + int dh = 0; + + if (!emile_cipher_init()) return NULL; + + r = calloc(1, sizeof (Emile_SSL)); + if (!r) return NULL; + + switch (t) + { + case EMILE_SSLv23: + r->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if (!r->ssl_ctx) goto on_error; + options = SSL_CTX_get_options(r->ssl_ctx); + SSL_CTX_set_options(r->ssl_ctx, + options | SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE); + break; + case EMILE_SSLv3: + r->ssl_ctx = SSL_CTX_new(SSLv3_server_method()); + break; + case EMILE_TLSv1: + r->ssl_ctx = SSL_CTX_new(TLSv1_server_method()); + break; + default: + free(r); + return NULL; + } + + if (!r->ssl_ctx) goto on_error; + + dh_params = DH_new(); + if (!dh_params) goto on_error; + if (!DH_generate_parameters_ex(dh_params, 1024, DH_GENERATOR_5, NULL)) + goto on_error; + if (!DH_check(dh_params, &dh)) + goto on_error; + if ((dh & DH_CHECK_P_NOT_PRIME) || (dh & DH_CHECK_P_NOT_SAFE_PRIME)) + goto on_error; + if (!DH_generate_key(dh_params)) + goto on_error; + if (!SSL_CTX_set_tmp_dh(r->ssl_ctx, dh_params)) + goto on_error; + + DH_free(dh_params); + INF("DH params successfully generated and applied!"); + + if (!SSL_CTX_set_cipher_list(r->ssl_ctx, + "aNULL:!eNULL:!LOW:!EXPORT:@STRENGTH")) + goto on_error; + + return r; + + on_error: + if (dh) + { + if (dh & DH_CHECK_P_NOT_PRIME) + ERR("openssl error: dh_params could not generate a prime!"); + else + ERR("openssl error: dh_params could not generate a safe prime!"); + } + else + { + ERR("openssl error: %s.", ERR_reason_error_string(ERR_get_error())); + } + emile_cipher_free(r); + return NULL; +} + +static void +_emile_cipher_print_verify_error(int error) +{ + switch (error) + { +#define ERROR(X) \ +case (X): \ + ERR("%s", #X); \ + break +#ifdef X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT + ERROR(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); +#endif +#ifdef X509_V_ERR_UNABLE_TO_GET_CRL + ERROR(X509_V_ERR_UNABLE_TO_GET_CRL); +#endif +#ifdef X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE + ERROR(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE); +#endif +#ifdef X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE + ERROR(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE); +#endif +#ifdef X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY + ERROR(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); +#endif +#ifdef X509_V_ERR_CERT_SIGNATURE_FAILURE + ERROR(X509_V_ERR_CERT_SIGNATURE_FAILURE); +#endif +#ifdef X509_V_ERR_CRL_SIGNATURE_FAILURE + ERROR(X509_V_ERR_CRL_SIGNATURE_FAILURE); +#endif +#ifdef X509_V_ERR_CERT_NOT_YET_VALID + ERROR(X509_V_ERR_CERT_NOT_YET_VALID); +#endif +#ifdef X509_V_ERR_CERT_HAS_EXPIRED + ERROR(X509_V_ERR_CERT_HAS_EXPIRED); +#endif +#ifdef X509_V_ERR_CRL_NOT_YET_VALID + ERROR(X509_V_ERR_CRL_NOT_YET_VALID); +#endif +#ifdef X509_V_ERR_CRL_HAS_EXPIRED + ERROR(X509_V_ERR_CRL_HAS_EXPIRED); +#endif +#ifdef X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD + ERROR(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD); +#endif +#ifdef X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD + ERROR(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); +#endif +#ifdef X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD + ERROR(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD); +#endif +#ifdef X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD + ERROR(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); +#endif +#ifdef X509_V_ERR_OUT_OF_MEM + ERROR(X509_V_ERR_OUT_OF_MEM); +#endif +#ifdef X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT + ERROR(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); +#endif +#ifdef X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN + ERROR(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN); +#endif +#ifdef X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY + ERROR(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); +#endif +#ifdef X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE + ERROR(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); +#endif +#ifdef X509_V_ERR_CERT_CHAIN_TOO_LONG + ERROR(X509_V_ERR_CERT_CHAIN_TOO_LONG); +#endif +#ifdef X509_V_ERR_CERT_REVOKED + ERROR(X509_V_ERR_CERT_REVOKED); +#endif +#ifdef X509_V_ERR_INVALID_CA + ERROR(X509_V_ERR_INVALID_CA); +#endif +#ifdef X509_V_ERR_PATH_LENGTH_EXCEEDED + ERROR(X509_V_ERR_PATH_LENGTH_EXCEEDED); +#endif +#ifdef X509_V_ERR_INVALID_PURPOSE + ERROR(X509_V_ERR_INVALID_PURPOSE); +#endif +#ifdef X509_V_ERR_CERT_UNTRUSTED + ERROR(X509_V_ERR_CERT_UNTRUSTED); +#endif +#ifdef X509_V_ERR_CERT_REJECTED + ERROR(X509_V_ERR_CERT_REJECTED); +#endif + /* These are 'informational' when looking for issuer cert */ +#ifdef X509_V_ERR_SUBJECT_ISSUER_MISMATCH + ERROR(X509_V_ERR_SUBJECT_ISSUER_MISMATCH); +#endif +#ifdef X509_V_ERR_AKID_SKID_MISMATCH + ERROR(X509_V_ERR_AKID_SKID_MISMATCH); +#endif +#ifdef X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH + ERROR(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH); +#endif +#ifdef X509_V_ERR_KEYUSAGE_NO_CERTSIGN + ERROR(X509_V_ERR_KEYUSAGE_NO_CERTSIGN); +#endif + +#ifdef X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER + ERROR(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER); +#endif +#ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION + ERROR(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION); +#endif +#ifdef X509_V_ERR_KEYUSAGE_NO_CRL_SIGN + ERROR(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN); +#endif +#ifdef X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION + ERROR(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION); +#endif +#ifdef X509_V_ERR_INVALID_NON_CA + ERROR(X509_V_ERR_INVALID_NON_CA); +#endif +#ifdef X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED + ERROR(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED); +#endif +#ifdef X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE + ERROR(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE); +#endif +#ifdef X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED + ERROR(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED); +#endif + +#ifdef X509_V_ERR_INVALID_EXTENSION + ERROR(X509_V_ERR_INVALID_EXTENSION); +#endif +#ifdef X509_V_ERR_INVALID_POLICY_EXTENSION + ERROR(X509_V_ERR_INVALID_POLICY_EXTENSION); +#endif +#ifdef X509_V_ERR_NO_EXPLICIT_POLICY + ERROR(X509_V_ERR_NO_EXPLICIT_POLICY); +#endif +#ifdef X509_V_ERR_DIFFERENT_CRL_SCOPE + ERROR(X509_V_ERR_DIFFERENT_CRL_SCOPE); +#endif +#ifdef X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE + ERROR(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE); +#endif + +#ifdef X509_V_ERR_UNNESTED_RESOURCE + ERROR(X509_V_ERR_UNNESTED_RESOURCE); +#endif + +#ifdef X509_V_ERR_PERMITTED_VIOLATION + ERROR(X509_V_ERR_PERMITTED_VIOLATION); +#endif +#ifdef X509_V_ERR_EXCLUDED_VIOLATION + ERROR(X509_V_ERR_EXCLUDED_VIOLATION); +#endif +#ifdef X509_V_ERR_SUBTREE_MINMAX + ERROR(X509_V_ERR_SUBTREE_MINMAX); +#endif +#ifdef X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE + ERROR(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE); +#endif +#ifdef X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX + ERROR(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX); +#endif +#ifdef X509_V_ERR_UNSUPPORTED_NAME_SYNTAX + ERROR(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX); +#endif +#ifdef X509_V_ERR_CRL_PATH_VALIDATION_ERROR + ERROR(X509_V_ERR_CRL_PATH_VALIDATION_ERROR); +#endif + + /* The application is not happy */ +#ifdef X509_V_ERR_APPLICATION_VERIFICATION + ERROR(X509_V_ERR_APPLICATION_VERIFICATION); +#endif + } +#undef ERROR +} + +static void +_emile_cipher_session_print(SSL *ssl) +{ + Eina_Strbuf *str; + SSL_SESSION *s; + STACK_OF(X509) *sk; + BIO *b; + BUF_MEM *bptr; + char log[4096]; + + if (!eina_log_domain_level_check(_emile_log_dom_global, EINA_LOG_LEVEL_DBG)) + return ; + + str = eina_strbuf_new(); + if (!str) return ; + + log[0] = '\0'; + b = BIO_new(BIO_s_mem()); + sk = SSL_get_peer_cert_chain(ssl); + if (sk) + { + int i; + + DBG("CERTIFICATES:"); + for (i = 0; i < sk_X509_num(sk); i++) + { + char *p; + + p = X509_NAME_oneline(X509_get_subject_name(sk_X509_value(sk, i)), + log, sizeof (log)); + DBG("%2d s:%s", i, p); + p = X509_NAME_oneline(X509_get_issuer_name(sk_X509_value(sk, i)), + log, sizeof(log)); + DBG(" i:%s", p); + + PEM_write_bio_X509(b, sk_X509_value(sk, i)); + BIO_get_mem_ptr(b, &bptr); + eina_strbuf_append_length(str, bptr->data, bptr->length); + DBG("%s", eina_strbuf_string_get(str)); + eina_strbuf_reset(str); + BIO_free(b); + } + } + + s = SSL_get_session(ssl); + SSL_SESSION_print(b, s); + BIO_get_mem_ptr(b, &bptr); + eina_strbuf_append_length(str, bptr->data, bptr->length); + DBG("%s", eina_strbuf_string_get(str)); + eina_strbuf_free(str); + BIO_free(b); +} + +static void +_emile_cipher_client_handshake(Emile_SSL *client) +{ + X509 *cert; + int ret = -1; + + if (!client) return ; + + switch (client->ssl_state) + { + case EMILE_SSL_STATE_INIT: + client->ssl_state = EMILE_SSL_STATE_HANDSHAKING; + client->handshaking = EINA_TRUE; + + case EMILE_SSL_STATE_HANDSHAKING: + if (!client->ssl) goto on_error; + + ret = SSL_do_handshake(client->ssl); + client->ssl_err = SSL_get_error(client->ssl, ret); + + if ((client->ssl_err == SSL_ERROR_SYSCALL) || + (client->ssl_err == SSL_ERROR_SSL)) + goto on_error; + + if (ret != 1) + { + if (client->ssl_err == SSL_ERROR_WANT_READ) + client->ssl_want = EMILE_WANT_READ; + else if (client->ssl_err == SSL_ERROR_WANT_WRITE) + client->ssl_want = EMILE_WANT_WRITE; + + return ; + } + + client->handshaking = EINA_FALSE; + client->ssl_state = EMILE_SSL_STATE_DONE; + case EMILE_SSL_STATE_DONE: + break; + case EMILE_SSL_STATE_ERROR: + goto on_error; + } + + _emile_cipher_session_print(client->ssl); + if (!client->parent->verify && + !client->parent->verify_basic) + return ; + + SSL_set_verify(client->ssl, SSL_VERIFY_PEER, NULL); + /* use CRL/CA lists to verify */ + cert = SSL_get_peer_certificate(client->ssl); + if (cert) + { + const char *verify_name; + char *cert_name; + char *s; + int clen; + int err; + int name = 0; + + if (client->parent->verify) + { + err = SSL_get_verify_result(client->ssl); + _emile_cipher_print_verify_error(err); + if (err) goto on_error; + } + + clen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), + NID_subject_alt_name, + NULL, 0); + if (clen > 0) + { + name = NID_subject_alt_name; + } + else + { + clen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), + NID_commonName, + NULL, 0); + if (clen <= 0) goto on_error; + name = NID_commonName; + } + + cert_name = alloca(++clen); + X509_NAME_get_text_by_NID(X509_get_subject_name(cert), + name, cert_name, clen); + verify_name = client->parent->verify_name; + + INF("Cert name: '%s' vs verify name: '%s'.", cert_name, verify_name); + + if (!verify_name) goto on_error; + if (strcasecmp(cert_name, verify_name)) goto on_error; + if (verify_name[0] != '*') goto on_error; + + /* verify that their is only one wildcard in the client cert name */ + if (strchr(cert_name + 1, '*')) goto on_error; + /* verify that we have a domain of at least *.X.TLD and not *.TLD */ + if (!strchr(cert_name + 2, '.')) goto on_error; + s = strchr(verify_name, '.'); + if (!s) goto on_error; + /* same as above for the stored name */ + if (!strchr(s + 1, '.')) goto on_error; + if (strcasecmp(s, verify_name + 1)) goto on_error; + + DBG("Successfully verified certificate."); + } + + return ; + + on_error: + DBG("Failed to finish handshake."); + client->ssl_state = EMILE_SSL_STATE_ERROR; + return ; +} + +EAPI Emile_SSL * +emile_cipher_client_connect(Emile_SSL *server, int fd) +{ + Emile_SSL *r; + + if (!server) return NULL; + + r = calloc(1, sizeof (Emile_SSL)); + if (!r) return NULL; + + r->parent = server; + r->ssl = SSL_new(r->parent->ssl_ctx); + if (!r->ssl) goto on_error; + + if (!SSL_set_fd(r->ssl, fd)) + goto on_error; + + SSL_set_accept_state(r->ssl); + + _emile_cipher_client_handshake(r); + + if (r->ssl_state == EMILE_SSL_STATE_ERROR) goto on_error; + + return r; + + on_error: + emile_cipher_free(r); + return NULL; +} + +EAPI Emile_SSL * +emile_cipher_server_connect(Emile_Cipher_Type t) +{ + Emile_SSL *r; + const char *msg; + int options; + int dh = 0; + + if (!emile_cipher_init()) return NULL; + + r = calloc(1, sizeof (Emile_SSL)); + if (!r) return NULL; + + switch (t) + { + case EMILE_SSLv23: + r->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if (!r->ssl_ctx) goto on_error; + options = SSL_CTX_get_options(r->ssl_ctx); + SSL_CTX_set_options(r->ssl_ctx, + options | SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE); + break; + case EMILE_SSLv3: + r->ssl_ctx = SSL_CTX_new(SSLv3_client_method()); + break; + case EMILE_TLSv1: + r->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); + break; + default: + free(r); + return NULL; + } + + if (!SSL_CTX_set_cipher_list(r->ssl_ctx, + "aNULL:!eNULL:!LOW:!EXPORT:!ECDH:RSA:AES:!PSK:@STRENGTH")) + goto on_error; + + return r; + + on_error: + if (dh) + { + if (dh & DH_CHECK_P_NOT_PRIME) + msg = "dh_params could not generate a prime!"; + else + msg = "dh_params could not generate a safe prime!"; + } + else + { + msg = ERR_reason_error_string(ERR_get_error()); + } + + ERR("OpenSSL error: '%s'.", msg); + emile_cipher_free(r); + return NULL; +} + +EAPI Eina_Bool +emile_cipher_free(Emile_SSL *emile) +{ + if (!emile) return EINA_FALSE; + + eina_stringshare_del(emile->last_error); + emile->last_error = NULL; + + eina_stringshare_del(emile->verify_name); + emile->verify_name = NULL; + + if (emile->ssl) + { + if (!SSL_shutdown(emile->ssl)) + SSL_shutdown(emile->ssl); + + SSL_free(emile->ssl); + } + emile->ssl = NULL; + + if (emile->ssl_ctx) + SSL_CTX_free(emile->ssl_ctx); + emile->ssl_ctx = NULL; + + free(emile); + return EINA_TRUE; +} + +EAPI Eina_Bool +emile_cipher_cafile_add(Emile_SSL *emile, const char *file) +{ + struct stat st; + unsigned long err; + + if (stat(file, &st)) return EINA_FALSE; + if (S_ISDIR(st.st_mode)) + { + if (!SSL_CTX_load_verify_locations(emile->ssl_ctx, NULL, file)) + goto on_error; + } + else + { + if (!SSL_CTX_load_verify_locations(emile->ssl_ctx, file, NULL)) + goto on_error; + } + + return EINA_TRUE; + + on_error: + err = ERR_peek_last_error(); + if (!err) return EINA_FALSE; + + DBG("OpenSSL error: '%s'.", ERR_reason_error_string(err)); + eina_stringshare_replace(&emile->last_error, ERR_reason_error_string(err)); + return EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_cert_add(Emile_SSL *emile, const char *file) +{ + Eina_File *f; + void *m; + X509 *cert = NULL; + BIO *bio = NULL; + int err; + + f = eina_file_open(file, EINA_FALSE); + if (!f) return EINA_FALSE; + + m = eina_file_map_all(f, EINA_FILE_WILLNEED); + if (!m) goto on_error; + + bio = BIO_new_mem_buf(m, eina_file_size_get(f)); + if (!bio) goto on_error; + + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (!cert) goto on_error; + + if (SSL_CTX_use_certificate(emile->ssl_ctx, cert) < 1) + goto on_error; + + eina_file_map_free(f, m); + eina_file_close(f); + BIO_free(bio); + + return EINA_TRUE; + + on_error: + err = ERR_peek_last_error(); + + if (m) eina_file_map_free(f, m); + if (f) eina_file_close(f); + if (bio) BIO_free(bio); + + if (!err) return EINA_FALSE; + + DBG("OpenSSL error: '%s'.", ERR_reason_error_string(err)); + eina_stringshare_replace(&emile->last_error, ERR_reason_error_string(err)); + return EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_privkey_add(Emile_SSL *emile, const char *file) +{ + Eina_File *f; + void *m; + EVP_PKEY *privkey = NULL; + BIO *bio = NULL; + int err; + + f = eina_file_open(file, EINA_FALSE); + if (!f) return EINA_FALSE; + + m = eina_file_map_all(f, EINA_FILE_WILLNEED); + if (!m) goto on_error; + + bio = BIO_new_mem_buf(m, eina_file_size_get(f)); + if (!bio) goto on_error; + + privkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!privkey) goto on_error; + + eina_file_map_free(f, m); + m = NULL; + + eina_file_close(f); + f = NULL; + + if (SSL_CTX_use_PrivateKey(emile->ssl_ctx, privkey) < 1) + goto on_error; + + if (SSL_CTX_check_private_key(emile->ssl_ctx) < 1) + goto on_error; + + BIO_free(bio); + + return EINA_TRUE; + + on_error: + err = ERR_peek_last_error(); + + if (m) eina_file_map_free(f, m); + if (f) eina_file_close(f); + if (bio) BIO_free(bio); + + if (!err) return EINA_FALSE; + + DBG("OpenSSL error: '%s'.", ERR_reason_error_string(err)); + eina_stringshare_replace(&emile->last_error, ERR_reason_error_string(err)); + return EINA_FALSE; +} + +EAPI Eina_Bool +emile_cipher_crl_add(Emile_SSL *emile, const char *file) +{ + X509_LOOKUP *lu; + X509_STORE *st; + int err; + + st = SSL_CTX_get_cert_store(emile->ssl_ctx); + if (!st) goto on_error; + + lu = X509_STORE_add_lookup(st, X509_LOOKUP_file()); + if (!lu) goto on_error; + + if (X509_load_crl_file(lu, file, X509_FILETYPE_PEM) < 1) + goto on_error; + + if (!emile->crl_flag) + { + X509_STORE_set_flags(st, + X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); + emile->crl_flag = EINA_TRUE; + } + + return EINA_TRUE; + + on_error: + err = ERR_peek_last_error(); + if (!err) return EINA_FALSE; + + DBG("OpenSSL error: '%s'.", ERR_reason_error_string(err)); + eina_stringshare_replace(&emile->last_error, ERR_reason_error_string(err)); + return EINA_FALSE; +} + +EAPI int +emile_cipher_read(Emile_SSL *emile, Eina_Binbuf *buffer) +{ + int err; + int num; + + if (!emile->ssl) return -1; + if (eina_binbuf_length_get(buffer) <= 0) return 0; + + num = SSL_read(emile->ssl, + (void*) eina_binbuf_string_get(buffer), + eina_binbuf_length_get(buffer)); + emile->ssl_err = SSL_get_error(emile->ssl, num); + + switch (emile->ssl_err) + { + case SSL_ERROR_WANT_READ: emile->ssl_want = EMILE_WANT_READ; break; + case SSL_ERROR_WANT_WRITE: emile->ssl_want = EMILE_WANT_WRITE; break; + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + err = ERR_peek_last_error(); + if (!err) return -1; + + DBG("OpenSSL error: '%s'.", ERR_reason_error_string(err)); + eina_stringshare_replace(&emile->last_error, ERR_reason_error_string(err)); + return -1; + + default: + emile->ssl_want = EMILE_WANT_NOTHING; + break; + } + + if (emile->ssl_state == EMILE_SSL_STATE_HANDSHAKING) + _emile_cipher_client_handshake(emile); + if (emile->ssl_state == EMILE_SSL_STATE_ERROR) + return -1; + + return num < 0 ? 0 : num; +} + +EAPI int +emile_cipher_write(Emile_SSL *emile, const Eina_Binbuf *buffer) +{ + int num; + int err; + + if (!emile->ssl) return -1; + if (eina_binbuf_length_get(buffer) <= 0) return 0; + + num = SSL_write(emile->ssl, + (void*) eina_binbuf_string_get(buffer), + eina_binbuf_length_get(buffer)); + emile->ssl_err = SSL_get_error(emile->ssl, num); + + switch (emile->ssl_err) + { + case SSL_ERROR_WANT_READ: emile->ssl_want = EMILE_WANT_READ; break; + case SSL_ERROR_WANT_WRITE: emile->ssl_want = EMILE_WANT_WRITE; break; + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + err = ERR_peek_last_error(); + if (!err) return -1; + + DBG("OpenSSL error: '%s'.", ERR_reason_error_string(err)); + eina_stringshare_replace(&emile->last_error, ERR_reason_error_string(err)); + return -1; + + default: + emile->ssl_want = EMILE_WANT_NOTHING; + break; + } + + if (emile->ssl_state == EMILE_SSL_STATE_HANDSHAKING) + _emile_cipher_client_handshake(emile); + if (emile->ssl_state == EMILE_SSL_STATE_ERROR) + return -1; + + return num < 0 ? 0 : num; +} + +EAPI const char * +emile_cipher_error_get(const Emile_SSL *emile) +{ + return emile->last_error; +} + +EAPI Eina_Bool +emile_cipher_verify_name_set(Emile_SSL *emile, const char *name) +{ + return eina_stringshare_replace(&emile->verify_name, name); +} + +EAPI const char * +emile_cipher_verify_name_get(const Emile_SSL *emile) +{ + return emile->verify_name; +} + +EAPI void +emile_cipher_verify_set(Emile_SSL *emile, Eina_Bool verify) +{ + emile->verify = verify; +} + +EAPI void +emile_cipher_verify_basic_set(Emile_SSL *emile, Eina_Bool verify_basic) +{ + emile->verify_basic = verify_basic; +} + +EAPI Eina_Bool +emile_cipher_verify_get(const Emile_SSL *emile) +{ + return emile->verify; +} + +EAPI Eina_Bool +emile_cipher_verify_basic_get(const Emile_SSL *emile) +{ + return emile->verify_basic; +} + diff --git a/src/lib/emile/emile_main.c b/src/lib/emile/emile_main.c index d5939e8ad8..eb18a61de5 100644 --- a/src/lib/emile/emile_main.c +++ b/src/lib/emile/emile_main.c @@ -73,7 +73,7 @@ emile_shutdown(void) EINA_LOG_STATE_START, EINA_LOG_STATE_SHUTDOWN); - if (_emile_cipher_init) + if (_emile_cipher_inited) { #ifdef HAVE_GNUTLS /* Note that gnutls has a leak where it doesnt free stuff it alloced diff --git a/src/lib/emile/emile_private.h b/src/lib/emile/emile_private.h index e6b4763f40..f6a005d44a 100644 --- a/src/lib/emile/emile_private.h +++ b/src/lib/emile/emile_private.h @@ -24,6 +24,14 @@ extern int _emile_log_dom_global; #endif /* ifdef CRI */ #define CRI(...) EINA_LOG_DOM_CRIT(_emile_log_dom_global, __VA_ARGS__) +typedef enum +{ + EMILE_SSL_STATE_INIT = 0, + EMILE_SSL_STATE_HANDSHAKING, + EMILE_SSL_STATE_DONE, + EMILE_SSL_STATE_ERROR +} Emile_SSL_State; + Eina_Bool _emile_cipher_init(void); #endif /* EMILE_PRIVATE_H_ */ |