#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include "Emile.h" #include "emile_private.h" #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) { #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER < 0x3050000fL) ERR_load_crypto_strings(); SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER < 0x3050000fL */ return EINA_TRUE; } EAPI Eina_Bool emile_binbuf_hmac_sha1(const char *key, unsigned int key_len, const Eina_Binbuf *data, unsigned char digest[20]) { HMAC(EVP_sha1(), key, key_len, eina_binbuf_string_get(data), eina_binbuf_length_get(data), digest, NULL); return EINA_TRUE; } EAPI Eina_Bool emile_binbuf_sha1(const Eina_Binbuf * data, unsigned char digest[20]) { const EVP_MD *md = EVP_sha1(); Eina_Slice slice = eina_binbuf_slice_get(data); #if (LIBRESSL_VERSION_NUMBER >= 0x3050000fL) || ((OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)) EVP_MD_CTX *ctx = EVP_MD_CTX_new(); if (!ctx) return EINA_FALSE; EVP_DigestInit_ex(ctx, md, NULL); if (!EVP_DigestUpdate(ctx, slice.mem, slice.len)) { EVP_MD_CTX_free(ctx); return EINA_FALSE; } EVP_DigestFinal_ex(ctx, digest, NULL); EVP_MD_CTX_free(ctx); #else EVP_MD_CTX ctx; EVP_MD_CTX_init(&ctx); EVP_DigestInit_ex(&ctx, md, NULL); EVP_DigestUpdate(&ctx, slice.mem, slice.len); EVP_DigestFinal_ex(&ctx, digest, NULL); EVP_MD_CTX_cleanup(&ctx); #endif return EINA_TRUE; } EAPI Eina_Binbuf * emile_binbuf_cipher(Emile_Cipher_Algorithm algo, const Eina_Binbuf *data, const char *key, unsigned int length) { /* Cipher declarations */ Eina_Binbuf *result; unsigned char *pointer; unsigned char iv[MAX_IV_LEN]; unsigned char ik[MAX_KEY_LEN]; unsigned char key_material[MAX_IV_LEN + MAX_KEY_LEN]; unsigned int salt; unsigned int tmp = 0; unsigned int crypted_length; /* Openssl declarations*/ EVP_CIPHER_CTX *ctx = NULL; unsigned int *buffer = NULL; int tmp_len; if (algo != EMILE_AES256_CBC) return NULL; if (!emile_cipher_init()) return NULL; /* Openssl salt generation */ if (!RAND_bytes((unsigned char *)&salt, sizeof (unsigned int))) return NULL; result = eina_binbuf_new(); if (!result) return NULL; emile_pbkdf2_sha1(key, length, (unsigned char *)&salt, sizeof(unsigned int), 2048, key_material, MAX_KEY_LEN + MAX_IV_LEN); memcpy(iv, key_material, MAX_IV_LEN); memcpy(ik, key_material + MAX_IV_LEN, MAX_KEY_LEN); memset(key_material, 0, sizeof (key_material)); crypted_length = ((((eina_binbuf_length_get(data) + sizeof (unsigned int)) >> 5) + 1) << 5) + sizeof (unsigned int); eina_binbuf_append_length(result, (unsigned char*) &salt, sizeof (salt)); memset(&salt, 0, sizeof (salt)); tmp = eina_htonl(eina_binbuf_length_get(data)); buffer = malloc(crypted_length - sizeof (int)); if (!buffer) goto on_error; *buffer = tmp; eina_binbuf_append_length(result, (unsigned char *) buffer, crypted_length - sizeof (int)); memcpy(buffer + 1, eina_binbuf_string_get(data), eina_binbuf_length_get(data)); /* Openssl create the corresponding cipher AES with a 256 bit key, Cipher Block Chaining mode */ ctx = EVP_CIPHER_CTX_new(); if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, ik, iv)) goto on_error; memset(iv, 0, sizeof (iv)); memset(ik, 0, sizeof (ik)); pointer = (unsigned char*) eina_binbuf_string_get(result); /* Openssl encrypt */ if (!EVP_EncryptUpdate(ctx, pointer + sizeof (int), &tmp_len, (unsigned char *)buffer, eina_binbuf_length_get(data) + sizeof(unsigned int))) goto on_error; /* Openssl close the cipher */ if (!EVP_EncryptFinal_ex(ctx, pointer + sizeof (int) + tmp_len, &tmp_len)) goto on_error; EVP_CIPHER_CTX_free(ctx); ctx = NULL; free(buffer); return result; on_error: memset(iv, 0, sizeof (iv)); memset(ik, 0, sizeof (ik)); /* Openssl error */ #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER < 0x3050000fL) if (ctx) EVP_CIPHER_CTX_cleanup(ctx); #else if (ctx) { EVP_CIPHER_CTX_cleanup(ctx); EVP_CIPHER_CTX_free(ctx); } #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER < 0x3050000fL */ free(buffer); /* General error */ eina_binbuf_free(result); return NULL; } EAPI Eina_Binbuf * emile_binbuf_decipher(Emile_Cipher_Algorithm algo, const Eina_Binbuf *data, const char *key, unsigned int length) { Eina_Binbuf *result = NULL; unsigned int *over; EVP_CIPHER_CTX *ctx = NULL; unsigned char ik[MAX_KEY_LEN]; unsigned char iv[MAX_IV_LEN]; unsigned char key_material[MAX_KEY_LEN + MAX_IV_LEN]; unsigned int salt; unsigned int size; int tmp_len; int tmp = 0; if (algo != EMILE_AES256_CBC) return NULL; if (!emile_cipher_init()) return NULL; over = (unsigned int*) eina_binbuf_string_get(data); size = eina_binbuf_length_get(data); /* At least the salt and an AES block */ if (size < sizeof(unsigned int) + 16) return NULL; /* Get the salt */ salt = *over; /* Generate the iv and the key with the salt */ emile_pbkdf2_sha1(key, length, (unsigned char *)&salt, sizeof(unsigned int), 2048, key_material, MAX_KEY_LEN + MAX_IV_LEN); memcpy(iv, key_material, MAX_IV_LEN); memcpy(ik, key_material + MAX_IV_LEN, MAX_KEY_LEN); memset(key_material, 0, sizeof (key_material)); memset(&salt, 0, sizeof (salt)); /* Align to AES block size if size is not align */ tmp_len = size - sizeof (unsigned int); if ((tmp_len & 0x1F) != 0) goto on_error; result = eina_binbuf_new(); if (!result) goto on_error; eina_binbuf_append_length(result, (unsigned char*) (over + 1), tmp_len); /* Openssl create the corresponding cipher */ ctx = EVP_CIPHER_CTX_new(); if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, ik, iv)) goto on_error; memset(iv, 0, sizeof (iv)); memset(ik, 0, sizeof (ik)); /* Openssl decrypt */ if (!EVP_DecryptUpdate(ctx, (void*) eina_binbuf_string_get(result), &tmp, (void*) (over + 1), tmp_len)) goto on_error; /* Openssl close the cipher*/ EVP_CIPHER_CTX_free(ctx); ctx = NULL; /* Get the decrypted data size */ tmp = *(unsigned int*)(eina_binbuf_string_get(result)); tmp = eina_ntohl(tmp); if (tmp > tmp_len || tmp <= 0) goto on_error; /* Remove header and padding */ eina_binbuf_remove(result, 0, sizeof (unsigned int)); eina_binbuf_remove(result, tmp, eina_binbuf_length_get(result)); return result; on_error: memset(iv, 0, sizeof (iv)); memset(ik, 0, sizeof (ik)); if (ctx) EVP_CIPHER_CTX_free(ctx); eina_binbuf_free(result); 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; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER < 0x3050000fL) case EMILE_TLSv1: r->ssl_ctx = SSL_CTX_new(TLS_server_method()); break; #endif 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, "aRSA+HIGH:+kEDH:+kRSA:!kSRP:!kPSK:+3DES:!MD5")) 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_OPENSSL(X) \ case (X): \ ERR("%s", #X); \ break #ifdef X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); #endif #ifdef X509_V_ERR_UNABLE_TO_GET_CRL ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_GET_CRL); #endif #ifdef X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE); #endif #ifdef X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE); #endif #ifdef X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); #endif #ifdef X509_V_ERR_CERT_SIGNATURE_FAILURE ERROR_OPENSSL(X509_V_ERR_CERT_SIGNATURE_FAILURE); #endif #ifdef X509_V_ERR_CRL_SIGNATURE_FAILURE ERROR_OPENSSL(X509_V_ERR_CRL_SIGNATURE_FAILURE); #endif #ifdef X509_V_ERR_CERT_NOT_YET_VALID ERROR_OPENSSL(X509_V_ERR_CERT_NOT_YET_VALID); #endif #ifdef X509_V_ERR_CERT_HAS_EXPIRED ERROR_OPENSSL(X509_V_ERR_CERT_HAS_EXPIRED); #endif #ifdef X509_V_ERR_CRL_NOT_YET_VALID ERROR_OPENSSL(X509_V_ERR_CRL_NOT_YET_VALID); #endif #ifdef X509_V_ERR_CRL_HAS_EXPIRED ERROR_OPENSSL(X509_V_ERR_CRL_HAS_EXPIRED); #endif #ifdef X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD ERROR_OPENSSL(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD); #endif #ifdef X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD ERROR_OPENSSL(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); #endif #ifdef X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD ERROR_OPENSSL(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD); #endif #ifdef X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD ERROR_OPENSSL(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); #endif #ifdef X509_V_ERR_OUT_OF_MEM ERROR_OPENSSL(X509_V_ERR_OUT_OF_MEM); #endif #ifdef X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ERROR_OPENSSL(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); #endif #ifdef X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN ERROR_OPENSSL(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN); #endif #ifdef X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); #endif #ifdef X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); #endif #ifdef X509_V_ERR_CERT_CHAIN_TOO_LONG ERROR_OPENSSL(X509_V_ERR_CERT_CHAIN_TOO_LONG); #endif #ifdef X509_V_ERR_CERT_REVOKED ERROR_OPENSSL(X509_V_ERR_CERT_REVOKED); #endif #ifdef X509_V_ERR_INVALID_CA ERROR_OPENSSL(X509_V_ERR_INVALID_CA); #endif #ifdef X509_V_ERR_PATH_LENGTH_EXCEEDED ERROR_OPENSSL(X509_V_ERR_PATH_LENGTH_EXCEEDED); #endif #ifdef X509_V_ERR_INVALID_PURPOSE ERROR_OPENSSL(X509_V_ERR_INVALID_PURPOSE); #endif #ifdef X509_V_ERR_CERT_UNTRUSTED ERROR_OPENSSL(X509_V_ERR_CERT_UNTRUSTED); #endif #ifdef X509_V_ERR_CERT_REJECTED ERROR_OPENSSL(X509_V_ERR_CERT_REJECTED); #endif /* These are 'informational' when looking for issuer cert */ #ifdef X509_V_ERR_SUBJECT_ISSUER_MISMATCH ERROR_OPENSSL(X509_V_ERR_SUBJECT_ISSUER_MISMATCH); #endif #ifdef X509_V_ERR_AKID_SKID_MISMATCH ERROR_OPENSSL(X509_V_ERR_AKID_SKID_MISMATCH); #endif #ifdef X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH ERROR_OPENSSL(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH); #endif #ifdef X509_V_ERR_KEYUSAGE_NO_CERTSIGN ERROR_OPENSSL(X509_V_ERR_KEYUSAGE_NO_CERTSIGN); #endif #ifdef X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER ERROR_OPENSSL(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER); #endif #ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION ERROR_OPENSSL(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION); #endif #ifdef X509_V_ERR_KEYUSAGE_NO_CRL_SIGN ERROR_OPENSSL(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN); #endif #ifdef X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION ERROR_OPENSSL(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION); #endif #ifdef X509_V_ERR_INVALID_NON_CA ERROR_OPENSSL(X509_V_ERR_INVALID_NON_CA); #endif #ifdef X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED ERROR_OPENSSL(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED); #endif #ifdef X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE ERROR_OPENSSL(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE); #endif #ifdef X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED ERROR_OPENSSL(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED); #endif #ifdef X509_V_ERR_INVALID_EXTENSION ERROR_OPENSSL(X509_V_ERR_INVALID_EXTENSION); #endif #ifdef X509_V_ERR_INVALID_POLICY_EXTENSION ERROR_OPENSSL(X509_V_ERR_INVALID_POLICY_EXTENSION); #endif #ifdef X509_V_ERR_NO_EXPLICIT_POLICY ERROR_OPENSSL(X509_V_ERR_NO_EXPLICIT_POLICY); #endif #ifdef X509_V_ERR_DIFFERENT_CRL_SCOPE ERROR_OPENSSL(X509_V_ERR_DIFFERENT_CRL_SCOPE); #endif #ifdef X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE ERROR_OPENSSL(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE); #endif #ifdef X509_V_ERR_UNNESTED_RESOURCE ERROR_OPENSSL(X509_V_ERR_UNNESTED_RESOURCE); #endif #ifdef X509_V_ERR_PERMITTED_VIOLATION ERROR_OPENSSL(X509_V_ERR_PERMITTED_VIOLATION); #endif #ifdef X509_V_ERR_EXCLUDED_VIOLATION ERROR_OPENSSL(X509_V_ERR_EXCLUDED_VIOLATION); #endif #ifdef X509_V_ERR_SUBTREE_MINMAX ERROR_OPENSSL(X509_V_ERR_SUBTREE_MINMAX); #endif #ifdef X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE ERROR_OPENSSL(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE); #endif #ifdef X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX ERROR_OPENSSL(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX); #endif #ifdef X509_V_ERR_UNSUPPORTED_NAME_SYNTAX ERROR_OPENSSL(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX); #endif #ifdef X509_V_ERR_CRL_PATH_VALIDATION_ERROR ERROR_OPENSSL(X509_V_ERR_CRL_PATH_VALIDATION_ERROR); #endif /* The application is not happy */ #ifdef X509_V_ERR_APPLICATION_VERIFICATION ERROR_OPENSSL(X509_V_ERR_APPLICATION_VERIFICATION); #endif } #undef ERROR_OPENSSL } 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); } } 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; EINA_FALLTHROUGH; 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; EINA_FALLTHROUGH; 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; 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_TLSv1: #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER < 0x3050000fL) r->ssl_ctx = SSL_CTX_new(TLS_client_method()); break; #endif default: free(r); return NULL; } if (!SSL_CTX_set_cipher_list(r->ssl_ctx, "aRSA+HIGH:+kEDH:+kRSA:!kSRP:!kPSK:+3DES:!MD5")) goto on_error; return r; on_error: 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; if (emile->ssl_state == EMILE_SSL_STATE_HANDSHAKING) _emile_cipher_client_handshake(emile); if (emile->ssl_state == EMILE_SSL_STATE_ERROR) return -1; else if (emile->ssl_state == EMILE_SSL_STATE_HANDSHAKING) 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; } 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 (!buffer || eina_binbuf_length_get(buffer) <= 0) return 0; if (emile->ssl_state == EMILE_SSL_STATE_HANDSHAKING) _emile_cipher_client_handshake(emile); if (emile->ssl_state == EMILE_SSL_STATE_ERROR) return -1; else if (emile->ssl_state == EMILE_SSL_STATE_HANDSHAKING) 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; } 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; }