diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c3a7e4..a3b9bd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -252,7 +252,7 @@ set(SODIUM_INSTALL ON CACHE BOOL "" FORCE) set(MATRIXSSL_LIBRARY_PREFIX nw CACHE STRING "" FORCE) set(MATRIXSSL_BUILD_SHARED ON CACHE BOOL "" FORCE) set(MATRIXSSL_BUILD_STATIC OFF CACHE BOOL "" FORCE) -set(MATRIXSSL_BUILD_OPENSSL_COMPAT ON CACHE BOOL "" FORCE) +set(MATRIXSSL_BUILD_OPENSSL_COMPAT OFF CACHE BOOL "" FORCE) set(MATRIXSSL_USE_SODIUM ON CACHE BOOL "" FORCE) set(MATRIXSSL_BUILD_TESTS ${MARS_NWE_BUILD_MATRIXSSL_PROGRAMS} CACHE BOOL "" FORCE) set(MATRIXSSL_BUILD_APPS ${MARS_NWE_BUILD_MATRIXSSL_PROGRAMS} CACHE BOOL "" FORCE) @@ -275,17 +275,29 @@ set(BUILD_SHARED_LIBS ${_MARS_NWE_SAVED_BUILD_SHARED_LIBS}) add_subdirectory(third_party/matrixssl) # mars-nwe base libraries. nwcore owns common project infrastructure such as -# logging; nwssl is the temporary SSL/Crypto abstraction on top of MatrixSSL -# OpenSSL-compat until the native API is complete. +# logging; nwssl owns the reduced OpenSSL compatibility layer on top of +# MatrixSSL, which stays a native TLS/crypto provider. add_subdirectory(src/core) add_subdirectory(src/ssl) +# Bundled FLAIM storage stack. Keep the imported sources unchanged; the CMake +# wrapper builds only the mars-nwe-needed libraries and routes legacy TLS/crypto +# compatibility through nwssl. +set(NWFLAIM_LIBRARY_PREFIX nw CACHE STRING "" FORCE) +set(NWFLAIM_BUILD_SHARED ON CACHE BOOL "" FORCE) +set(NWFLAIM_BUILD_STATIC OFF CACHE BOOL "" FORCE) +set(NWFLAIM_BUILD_SQL ON CACHE BOOL "" FORCE) +set(NWFLAIM_WITH_OPENSSL ON CACHE BOOL "" FORCE) +set(NWFLAIM_SSL_TARGET mars_nwe::ssl CACHE STRING "" FORCE) +set(NWFLAIM_NICI_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include/nwssl/private/nici" CACHE PATH "" FORCE) +add_subdirectory(third_party/flaim) + if(ENABLE_DIRECTORY) set(TINYLDAP_OUTPUT_NAME nwdirectory CACHE STRING "" FORCE) set(TINYLDAP_BUILD_SHARED ON CACHE BOOL "" FORCE) set(TINYLDAP_BUILD_STATIC OFF CACHE BOOL "" FORCE) set(TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR nwlibowfat CACHE STRING "" FORCE) - set(TINYLDAP_MATRIXSSL_INCLUDE_SUBDIR nwmatrixssl CACHE STRING "" FORCE) + set(TINYLDAP_NWSSL_TARGET mars_nwe::ssl CACHE STRING "" FORCE) set(TINYLDAP_ALLOW_SYSTEM_OPENSSL OFF CACHE BOOL "" FORCE) add_subdirectory(directory) endif() diff --git a/directory b/directory index e5b9d3e..84a6f10 160000 --- a/directory +++ b/directory @@ -1 +1 @@ -Subproject commit e5b9d3ed03f8da102a17f3a86a253408a3d36e9c +Subproject commit 84a6f10d459b3e2294158836965db397054452d6 diff --git a/include/nwssl/private/nici/ccs.h b/include/nwssl/private/nici/ccs.h new file mode 100644 index 0000000..ffa3259 --- /dev/null +++ b/include/nwssl/private/nici/ccs.h @@ -0,0 +1,104 @@ +#ifndef MARS_NWSSL_PRIVATE_NICI_CCS_H +#define MARS_NWSSL_PRIVATE_NICI_CCS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint8_t NICI_BYTE; +typedef uint32_t NICI_ULONG; +typedef uint32_t NICI_BBOOL; +typedef uint32_t NICI_CC_HANDLE; +typedef uint32_t NICI_OBJECT_HANDLE; +typedef NICI_OBJECT_HANDLE *NICI_OBJECT_HANDLE_PTR; + +typedef uint32_t nuint32; +typedef uint8_t nuint8; +typedef uint8_t nbool8; + +#define NICI_H_INVALID 0 +#define NICI_KM_UNSPECIFIED 0 + +#define NICI_A_KEY_TYPE 1 +#define NICI_A_KEY_USAGE 2 +#define NICI_A_KEY_SIZE 3 +#define NICI_A_GLOBAL 4 +#define NICI_A_CLASS 5 +#define NICI_A_KEY_FORMAT 6 +#define NICI_A_FEATURE 7 + +#define NICI_K_AES 1 +#define NICI_K_DES3X 2 +#define NICI_K_DES 3 + +#define NICI_F_DATA_ENCRYPT 0x0001u +#define NICI_F_DATA_DECRYPT 0x0002u +#define NICI_F_EXTRACT 0x0004u +#define NICI_F_WRAP 0x0008u +#define NICI_F_UNWRAP 0x0010u +#define NICI_F_KM_ENCRYPT 0x0020u +#define NICI_F_KM_DECRYPT 0x0040u + +#define NICI_O_SECRET_KEY 1 +#define NICI_AV_STORAGE 1 +#define NICI_P_IV 1 + +#define NICI_AlgorithmPrefix(x) (x) +#define IDV_NOV_AES128CBCPad NICI_AlgorithmPrefix(1), 97 +#define IDV_NOV_DES3CBCPad NICI_AlgorithmPrefix(1), 98 +#define IDV_NOV_DESCBCPad NICI_AlgorithmPrefix(1), 99 + +typedef struct NICI_PARAMETER_INFO_st { + uint32_t parmType; + void *parm; + uint32_t parmLen; +} NICI_PARAMETER_INFO; + +typedef struct NICI_PARAMETER_st { + NICI_PARAMETER_INFO *parms; + uint32_t parmCount; +} NICI_PARAMETER; + +typedef struct NICI_ALGORITHM_st { + const uint8_t *algorithm; + uint32_t algorithmLen; + NICI_PARAMETER *parameter; +} NICI_ALGORITHM; + +typedef struct NICI_ATTRIBUTE_st { + uint32_t type; + union { + struct { uint32_t value; } f; + struct { void *data; uint32_t len; } b; + } u; +} NICI_ATTRIBUTE; + +typedef NICI_ATTRIBUTE *NICI_ATTRIBUTE_PTR; + +int CCS_CreateContext(uint32_t flags, NICI_CC_HANDLE *context); +int CCS_DestroyContext(NICI_CC_HANDLE context); +int CCS_DestroyObject(NICI_CC_HANDLE context, NICI_OBJECT_HANDLE object); +int CCS_GetAttributeValue(NICI_CC_HANDLE context, NICI_OBJECT_HANDLE object, NICI_ATTRIBUTE_PTR attrs, uint32_t attr_count); +int CCS_WrapKey(NICI_CC_HANDLE context, NICI_ALGORITHM *algorithm, uint32_t mode, uint32_t flags, NICI_OBJECT_HANDLE wrapping_key, NICI_OBJECT_HANDLE key, void *wrapped_key, NICI_ULONG *wrapped_key_len); +int CCS_UnwrapKey(NICI_CC_HANDLE context, NICI_OBJECT_HANDLE wrapping_key, const void *wrapped_key, NICI_ULONG wrapped_key_len, NICI_OBJECT_HANDLE_PTR key); +int CCS_GenerateKey(NICI_CC_HANDLE context, NICI_ALGORITHM *algorithm, NICI_ATTRIBUTE_PTR attrs, uint32_t attr_count, NICI_BBOOL *key_size_changed, NICI_OBJECT_HANDLE_PTR key, NICI_OBJECT_HANDLE template_key); +int CCS_GetRandom(NICI_CC_HANDLE context, void *buf, uint32_t len); +int CCS_DataEncryptInit(NICI_CC_HANDLE context, NICI_ALGORITHM *algorithm, NICI_OBJECT_HANDLE key); +int CCS_Encrypt(NICI_CC_HANDLE context, const void *in, uint32_t in_len, void *out, uint32_t *out_len); +int CCS_DataDecryptInit(NICI_CC_HANDLE context, NICI_ALGORITHM *algorithm, NICI_OBJECT_HANDLE key); +int CCS_Decrypt(NICI_CC_HANDLE context, const void *in, uint32_t in_len, void *out, uint32_t *out_len); +int CCS_FindObjectsInit(NICI_CC_HANDLE context, NICI_ATTRIBUTE_PTR attrs, uint32_t attr_count); +int CCS_FindObjects(NICI_CC_HANDLE context, NICI_OBJECT_HANDLE_PTR objects, uint32_t *object_count); +int CCS_FindObjectsFinal(NICI_CC_HANDLE context); +int CCS_pbeEncrypt(NICI_CC_HANDLE context, const void *password, uint32_t password_len, const void *salt, uint32_t salt_len, uint32_t iterations, const void *in, uint32_t in_len, void *out, uint32_t *out_len); +int CCS_pbeDecrypt(NICI_CC_HANDLE context, const void *password, uint32_t password_len, const void *salt, uint32_t salt_len, uint32_t iterations, const void *in, uint32_t in_len, void *out, uint32_t *out_len); +int CCS_InjectKey(NICI_CC_HANDLE context, NICI_ATTRIBUTE_PTR attrs, nuint32 attr_count, NICI_OBJECT_HANDLE_PTR key); +int CCS_ExtractKey(NICI_CC_HANDLE context, NICI_OBJECT_HANDLE key, NICI_ATTRIBUTE_PTR attrs, nuint32 attr_count); + +#ifdef __cplusplus +} +#endif + +#endif /* MARS_NWSSL_PRIVATE_NICI_CCS_H */ diff --git a/include/nwssl/private/nici/nwccs.h b/include/nwssl/private/nici/nwccs.h new file mode 100644 index 0000000..ffc5427 --- /dev/null +++ b/include/nwssl/private/nici/nwccs.h @@ -0,0 +1,4 @@ +#ifndef MARS_NWSSL_PRIVATE_NICI_NWCCS_H +#define MARS_NWSSL_PRIVATE_NICI_NWCCS_H +#include "ccs.h" +#endif diff --git a/include/openssl/bio.h b/include/openssl/bio.h new file mode 100644 index 0000000..28887c0 --- /dev/null +++ b/include/openssl/bio.h @@ -0,0 +1,24 @@ +#ifndef MARS_MATRIXSSL_OPENSSL_BIO_H +#define MARS_MATRIXSSL_OPENSSL_BIO_H +#include +#ifdef __cplusplus +extern "C" { +#endif +const BIO_METHOD *BIO_s_mem(void); +BIO *BIO_new(const BIO_METHOD *method); +BIO *BIO_new_ssl_connect(SSL_CTX *ctx); +void BIO_free(BIO *bio); +void BIO_free_all(BIO *bio); +int BIO_get_ssl(BIO *bio, SSL **ssl); +void BIO_set_conn_hostname(BIO *bio, const char *name); +void BIO_set_conn_port(BIO *bio, const char *port); +int BIO_do_connect(BIO *bio); +int BIO_read(BIO *bio, void *buf, int len); +int BIO_write(BIO *bio, const void *buf, int len); +int BIO_should_retry(BIO *bio); +long BIO_get_mem_data(BIO *bio, const char **data); +int PEM_write_bio_X509(BIO *bio, X509 *cert); +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/openssl/err.h b/include/openssl/err.h new file mode 100644 index 0000000..a0bec13 --- /dev/null +++ b/include/openssl/err.h @@ -0,0 +1,4 @@ +#ifndef MARS_MATRIXSSL_OPENSSL_ERR_H +#define MARS_MATRIXSSL_OPENSSL_ERR_H +#include +#endif diff --git a/include/openssl/md5.h b/include/openssl/md5.h new file mode 100644 index 0000000..1633d6b --- /dev/null +++ b/include/openssl/md5.h @@ -0,0 +1,25 @@ +#ifndef MARS_NWSSL_OPENSSL_MD5_H +#define MARS_NWSSL_OPENSSL_MD5_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef psMd5_t MD5_CTX; +#define MD5Init MD5_Init +#define MD5Update MD5_Update +#define MD5Final MD5_Final + +static inline int MD5_Init(MD5_CTX *c) { return psMd5Init(c) >= 0 ? 1 : 0; } +static inline int MD5_Update(MD5_CTX *c, const void *data, size_t len) { psMd5Update(c, (const unsigned char *)data, (uint32_t)len); return 1; } +static inline int MD5_Final(unsigned char *md, MD5_CTX *c) { psMd5Final(c, md); return 1; } + +#ifdef __cplusplus +} +#endif + +#endif /* MARS_NWSSL_OPENSSL_MD5_H */ diff --git a/include/openssl/pem.h b/include/openssl/pem.h new file mode 100644 index 0000000..48550bc --- /dev/null +++ b/include/openssl/pem.h @@ -0,0 +1,4 @@ +#ifndef MARS_MATRIXSSL_OPENSSL_PEM_H +#define MARS_MATRIXSSL_OPENSSL_PEM_H +#include +#endif diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h new file mode 100644 index 0000000..b8019de --- /dev/null +++ b/include/openssl/ssl.h @@ -0,0 +1,92 @@ +#ifndef MARS_MATRIXSSL_OPENSSL_SSL_H +#define MARS_MATRIXSSL_OPENSSL_SSL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mars_ssl_ctx_st SSL_CTX; +typedef struct mars_ssl_st SSL; +typedef struct mars_ssl_method_st SSL_METHOD; +typedef struct mars_bio_st BIO; +typedef struct mars_bio_method_st BIO_METHOD; +typedef struct mars_x509_st X509; +typedef struct mars_x509_name_st X509_NAME; +typedef struct mars_evp_pkey_st EVP_PKEY; + +#define SSL_FILETYPE_PEM 1 +#define SSL_FILETYPE_ASN1 2 +#define X509_FILETYPE_PEM SSL_FILETYPE_PEM + +#define SSL_ERROR_NONE 0 +#define SSL_ERROR_SSL 1 +#define SSL_ERROR_WANT_READ 2 +#define SSL_ERROR_WANT_WRITE 3 +#define SSL_ERROR_WANT_X509_LOOKUP 4 +#define SSL_ERROR_SYSCALL 5 +#define SSL_ERROR_ZERO_RETURN 6 +#define SSL_ERROR_WANT_CONNECT 7 +#define SSL_ERROR_WANT_ACCEPT 8 + +#define SSL_VERIFY_NONE 0x00 +#define SSL_VERIFY_PEER 0x01 +#define X509_V_OK 0 + +#define SSL_MODE_AUTO_RETRY 0x00000004L +#define TLS1_2_VERSION 0x0303 +#define TLS1_3_VERSION 0x0304 + +#define NID_commonName 13 +#define EVP_PKEY_RSA 6 + +int SSL_library_init(void); +#define OpenSSL_add_ssl_algorithms() SSL_library_init() +#define SSLeay_add_ssl_algorithms() SSL_library_init() +#define OpenSSL_add_all_algorithms() SSL_library_init() +void SSL_load_error_strings(void); +void ERR_load_BIO_strings(void); +void ERR_print_errors_fp(FILE *fp); +void ERR_clear_error(void); +unsigned long ERR_peek_error(void); + +const SSL_METHOD *TLS_server_method(void); +const SSL_METHOD *SSLv23_client_method(void); + +SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth); +void SSL_CTX_free(SSL_CTX *ctx); +int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version); +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); +int SSL_CTX_check_private_key(const SSL_CTX *ctx); +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); + +SSL *SSL_new(SSL_CTX *ctx); +void SSL_free(SSL *ssl); +int SSL_set_fd(SSL *ssl, int fd); +int SSL_accept(SSL *ssl); +int SSL_connect(SSL *ssl); +int SSL_read(SSL *ssl, void *buf, int num); +int SSL_write(SSL *ssl, const void *buf, int num); +int SSL_shutdown(SSL *ssl); +int SSL_get_error(const SSL *ssl, int ret); +int SSL_pending(const SSL *ssl); +long SSL_set_mode(SSL *ssl, long mode); +long SSL_get_verify_result(const SSL *ssl); +X509 *SSL_get_peer_certificate(const SSL *ssl); + +X509_NAME *X509_get_subject_name(X509 *cert); +int X509_NAME_get_text_by_NID(X509_NAME *name, int nid, char *buf, int len); +EVP_PKEY *X509_get_pubkey(X509 *cert); +void EVP_PKEY_free(EVP_PKEY *pkey); +void X509_free(X509 *cert); + +struct mars_evp_pkey_st { int type; }; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/openssl/x509.h b/include/openssl/x509.h new file mode 100644 index 0000000..8ed97e9 --- /dev/null +++ b/include/openssl/x509.h @@ -0,0 +1,4 @@ +#ifndef MARS_MATRIXSSL_OPENSSL_X509_H +#define MARS_MATRIXSSL_OPENSSL_X509_H +#include +#endif diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt index 8c5da8b..f11c459 100644 --- a/src/ssl/CMakeLists.txt +++ b/src/ssl/CMakeLists.txt @@ -1,18 +1,21 @@ set(NWSSL_BUILD_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/nwssl") -set(NWSSL_OPENSSL_COMPAT_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include") -file(MAKE_DIRECTORY "${NWSSL_BUILD_INCLUDE_DIR}" "${NWSSL_OPENSSL_COMPAT_INCLUDE_DIR}/openssl") +set(NWSSL_OPENSSL_COMPAT_INCLUDE_DIR "${CMAKE_BINARY_DIR}/include/openssl") +file(MAKE_DIRECTORY "${NWSSL_BUILD_INCLUDE_DIR}" "${NWSSL_OPENSSL_COMPAT_INCLUDE_DIR}") configure_file( "${CMAKE_SOURCE_DIR}/include/nwssl/nwssl.h" "${NWSSL_BUILD_INCLUDE_DIR}/nwssl.h" COPYONLY) -# Temporary OpenSSL compatibility surface for legacy mars-nwe code. MatrixSSL's -# reduced OpenSSL layer intentionally does not provide md5.h, so expose a small -# MD5 wrapper here until callers move to the real nwssl API. -file(WRITE "${NWSSL_OPENSSL_COMPAT_INCLUDE_DIR}/openssl/md5.h" "#pragma once\n#include \n#include \n#include \ntypedef psMd5_t MD5_CTX;\nstatic inline int MD5_Init(MD5_CTX *c) { return psMd5Init(c) >= 0 ? 1 : 0; }\nstatic inline int MD5_Update(MD5_CTX *c, const void *data, size_t len) { psMd5Update(c, (const unsigned char *)data, (uint32_t)len); return 1; }\nstatic inline int MD5_Final(unsigned char *md, MD5_CTX *c) { psMd5Final(c, md); return 1; }\n") +# nwssl owns the reduced OpenSSL compatibility surface used by legacy mars-nwe +# code. MatrixSSL stays a native TLS/crypto provider underneath this wrapper. +file(COPY "${CMAKE_SOURCE_DIR}/include/openssl/" + DESTINATION "${NWSSL_OPENSSL_COMPAT_INCLUDE_DIR}" + FILES_MATCHING PATTERN "*.h") -add_library(nwssl SHARED nwssl.c) +add_library(nwssl SHARED + nwssl.c + openssl_compat.c) add_library(mars_nwe::ssl ALIAS nwssl) set_target_properties(nwssl PROPERTIES @@ -34,8 +37,14 @@ target_include_directories(nwssl target_link_libraries(nwssl PUBLIC - OpenSSL::SSL - OpenSSL::Crypto) + MATRIXSSL::matrixssl) + +if(NOT TARGET OpenSSL::SSL) + add_library(OpenSSL::SSL ALIAS nwssl) +endif() +if(NOT TARGET OpenSSL::Crypto) + add_library(OpenSSL::Crypto ALIAS nwssl) +endif() install(TARGETS nwssl EXPORT mars-nwe-ssl-targets @@ -45,6 +54,8 @@ install(TARGETS nwssl install(DIRECTORY "${NWSSL_BUILD_INCLUDE_DIR}/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nwssl") +install(DIRECTORY "${NWSSL_OPENSSL_COMPAT_INCLUDE_DIR}/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/openssl") install(EXPORT mars-nwe-ssl-targets NAMESPACE mars_nwe:: diff --git a/src/ssl/openssl_compat.c b/src/ssl/openssl_compat.c new file mode 100644 index 0000000..b01390f --- /dev/null +++ b/src/ssl/openssl_compat.c @@ -0,0 +1,556 @@ +/* + * mars-matrixssl reduced OpenSSL compatibility layer. + * + * This is not the historical MatrixSSL OpenSSL shim. It implements only the + * OpenSSL-shaped entry points used by mars-nwe's nwwebui and FLAIM/FTK. + */ + +#include +#include +#include "matrixsslApi.h" +#include "matrixsslGetSet.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mars_ssl_method_st { int server; }; +struct mars_x509_name_st { char common_name[256]; }; +struct mars_x509_st { struct mars_x509_name_st subject; char *pem; }; +struct mars_bio_method_st { int type; }; + +struct mars_ssl_ctx_st { + int server; + int min_proto; + char *cert_file; + char *key_file; + char *ca_file; + sslKeys_t *keys; + int keys_loaded; +}; + +struct mars_ssl_st { + SSL_CTX *ctx; + ssl_t *ms; + sslSessionId_t *sid; + int fd; + int last_error; + int handshake_done; + int close_sent; + unsigned char *app_ptr; + uint32 app_len; + uint32 app_off; + char peer_name[256]; +}; + +struct mars_bio_st { + int type; + SSL_CTX *ctx; + SSL *ssl; + int fd; + char *host; + char *port; + int should_retry; + char *mem; + size_t mem_len; + size_t mem_cap; +}; + +static const SSL_METHOD g_server_method = { 1 }; +static const SSL_METHOD g_client_method = { 0 }; +static const BIO_METHOD g_mem_method = { 1 }; +static unsigned long g_last_error; +static int g_matrixssl_opened; + +static void set_error(SSL *ssl, int err) +{ + if (ssl) ssl->last_error = err; + g_last_error = (unsigned long)err; +} + +static char *dupstr(const char *s) +{ + if (!s) return NULL; + size_t n = strlen(s) + 1; + char *p = (char *)malloc(n); + if (p) memcpy(p, s, n); + return p; +} + +static int write_all_fd(int fd, const unsigned char *buf, int len) +{ + int done = 0; + while (done < len) { + ssize_t n = send(fd, buf + done, (size_t)(len - done), 0); + if (n < 0) { + if (errno == EINTR) continue; + return -1; + } + if (n == 0) return -1; + done += (int)n; + } + return done; +} + +static int flush_outdata(SSL *ssl) +{ + unsigned char *buf; + int32 len; + if (!ssl || !ssl->ms) return -1; + while ((len = matrixSslGetOutdata(ssl->ms, &buf)) > 0) { + int sent = write_all_fd(ssl->fd, buf, len); + if (sent < 0) { set_error(ssl, SSL_ERROR_SYSCALL); return -1; } + if (matrixSslSentData(ssl->ms, (uint32)sent) < 0) { + set_error(ssl, SSL_ERROR_SSL); + return -1; + } + } + return 0; +} + +static int recv_into_matrix(SSL *ssl, int *out_rc) +{ + unsigned char *buf = NULL, *pt = NULL; + uint32 ptlen = 0; + int32 sz = matrixSslGetReadbuf(ssl->ms, &buf); + if (sz <= 0) { set_error(ssl, SSL_ERROR_SSL); return -1; } + ssize_t n; + do { n = recv(ssl->fd, buf, (size_t)sz, 0); } while (n < 0 && errno == EINTR); + if (n == 0) { set_error(ssl, SSL_ERROR_ZERO_RETURN); return 0; } + if (n < 0) { + set_error(ssl, (errno == EAGAIN || errno == EWOULDBLOCK) ? SSL_ERROR_WANT_READ : SSL_ERROR_SYSCALL); + return -1; + } + int32 rc = matrixSslReceivedData(ssl->ms, (uint32)n, &pt, &ptlen); + if (rc == MATRIXSSL_APP_DATA && pt && ptlen) { + ssl->app_ptr = pt; + ssl->app_len = ptlen; + ssl->app_off = 0; + } + *out_rc = rc; + return (int)n; +} + +static int drive_handshake(SSL *ssl, int server) +{ + int32 rc; + if (!ssl || !ssl->ms) return -1; + rc = server ? MATRIXSSL_REQUEST_RECV : MATRIXSSL_REQUEST_SEND; + for (;;) { + if (flush_outdata(ssl) < 0) return -1; + if (matrixSslHandshakeIsComplete(ssl->ms)) { + ssl->handshake_done = 1; + set_error(ssl, SSL_ERROR_NONE); + return 1; + } + int rrc = 0; + if (recv_into_matrix(ssl, &rrc) <= 0) return -1; + rc = rrc; + if (rc == MATRIXSSL_HANDSHAKE_COMPLETE || matrixSslHandshakeIsComplete(ssl->ms)) { + if (flush_outdata(ssl) < 0) return -1; + ssl->handshake_done = 1; + set_error(ssl, SSL_ERROR_NONE); + return 1; + } + if (rc == MATRIXSSL_RECEIVED_ALERT || rc == MATRIXSSL_REQUEST_CLOSE) { + set_error(ssl, SSL_ERROR_ZERO_RETURN); + return -1; + } + if (rc < 0) { set_error(ssl, SSL_ERROR_SSL); return -1; } + } +} + +static int ensure_ctx_keys(SSL_CTX *ctx) +{ + if (!ctx) return -1; + if (ctx->keys_loaded) return 0; + if (matrixSslNewKeys(&ctx->keys, NULL) < 0) return -1; + if (ctx->cert_file || ctx->key_file || ctx->ca_file) { + if (matrixSslLoadKeys(ctx->keys, ctx->cert_file, ctx->key_file, NULL, ctx->ca_file, NULL) < 0) return -1; + } + ctx->keys_loaded = 1; + return 0; +} + +int SSL_library_init(void) +{ + if (!g_matrixssl_opened) { + if (matrixSslOpen() < 0) return 0; + g_matrixssl_opened = 1; + } + return 1; +} + +void SSL_load_error_strings(void) {} +void ERR_load_BIO_strings(void) {} +void ERR_clear_error(void) { g_last_error = 0; } +unsigned long ERR_peek_error(void) { return g_last_error; } +void ERR_print_errors_fp(FILE *fp) +{ + if (!fp) fp = stderr; + if (g_last_error) fprintf(fp, "mars-matrixssl OpenSSL-compat error %lu\n", g_last_error); +} + +const SSL_METHOD *TLS_server_method(void) { return &g_server_method; } +const SSL_METHOD *SSLv23_client_method(void) { return &g_client_method; } + +SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) +{ + if (!SSL_library_init()) return NULL; + SSL_CTX *ctx = (SSL_CTX *)calloc(1, sizeof(*ctx)); + if (!ctx) return NULL; + ctx->server = meth ? meth->server : 0; + ctx->min_proto = TLS1_2_VERSION; + return ctx; +} + +void SSL_CTX_free(SSL_CTX *ctx) +{ + if (!ctx) return; + if (ctx->keys) matrixSslDeleteKeys(ctx->keys); + free(ctx->cert_file); free(ctx->key_file); free(ctx->ca_file); + free(ctx); +} + +int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version) +{ + if (!ctx) return 0; + ctx->min_proto = version; + return 1; +} + +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) +{ + if (!ctx || !file || type != SSL_FILETYPE_PEM) return 0; + free(ctx->cert_file); ctx->cert_file = dupstr(file); ctx->keys_loaded = 0; + return ctx->cert_file != NULL; +} + +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) +{ + if (!ctx || !file || type != SSL_FILETYPE_PEM) return 0; + free(ctx->key_file); ctx->key_file = dupstr(file); ctx->keys_loaded = 0; + return ctx->key_file != NULL; +} + +int SSL_CTX_check_private_key(const SSL_CTX *ctx) +{ + return (ctx && ctx->cert_file && ctx->key_file) ? 1 : 0; +} + +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) +{ + (void)ctx; + return 1; +} + +SSL *SSL_new(SSL_CTX *ctx) +{ + if (!ctx) return NULL; + SSL *ssl = (SSL *)calloc(1, sizeof(*ssl)); + if (!ssl) return NULL; + ssl->ctx = ctx; + ssl->fd = -1; + return ssl; +} + +void SSL_free(SSL *ssl) +{ + if (!ssl) return; + if (ssl->ms) matrixSslDeleteSession(ssl->ms); + if (ssl->sid) matrixSslDeleteSessionId(ssl->sid); + free(ssl); +} + +int SSL_set_fd(SSL *ssl, int fd) +{ + if (!ssl) return 0; + ssl->fd = fd; + return 1; +} + +int SSL_accept(SSL *ssl) +{ + sslSessOpts_t opts; + memset(&opts, 0, sizeof(opts)); + if (!ssl || !ssl->ctx || ssl->fd < 0) return -1; + if (ensure_ctx_keys(ssl->ctx) < 0) { set_error(ssl, SSL_ERROR_SSL); return -1; } + if (!ssl->ms) { + if (matrixSslNewServerSession(&ssl->ms, ssl->ctx->keys, NULL, &opts) < 0) { + set_error(ssl, SSL_ERROR_SSL); return -1; + } + } + return drive_handshake(ssl, 1); +} + +int SSL_connect(SSL *ssl) +{ + sslSessOpts_t opts; + memset(&opts, 0, sizeof(opts)); + if (!ssl || !ssl->ctx || ssl->fd < 0) return -1; + if (ensure_ctx_keys(ssl->ctx) < 0) { set_error(ssl, SSL_ERROR_SSL); return -1; } + if (!ssl->sid && matrixSslNewSessionId(&ssl->sid, NULL) < 0) { set_error(ssl, SSL_ERROR_SSL); return -1; } + matrixSslSessOptsSetClientTlsVersionRange(&opts, v_tls_1_2, v_tls_1_2); + matrixSslSessOptsSetKeepPeerCerts(&opts, 1); + if (!ssl->ms) { + if (matrixSslNewClientSession(&ssl->ms, ssl->ctx->keys, ssl->sid, NULL, 0, NULL, + ssl->peer_name[0] ? ssl->peer_name : NULL, + NULL, NULL, &opts) < 0) { + set_error(ssl, SSL_ERROR_SSL); return -1; + } + } + return drive_handshake(ssl, 0); +} + +static int consume_pending_app(SSL *ssl, unsigned char *buf, int num) +{ + if (!ssl->app_ptr || ssl->app_off >= ssl->app_len) return 0; + uint32 avail = ssl->app_len - ssl->app_off; + int take = (avail < (uint32)num) ? (int)avail : num; + memcpy(buf, ssl->app_ptr + ssl->app_off, (size_t)take); + ssl->app_off += (uint32)take; + if (ssl->app_off >= ssl->app_len) { + unsigned char *pt = NULL; uint32 ptlen = 0; + int32 rc = matrixSslProcessedData(ssl->ms, &pt, &ptlen); + ssl->app_ptr = NULL; ssl->app_len = ssl->app_off = 0; + if (rc == MATRIXSSL_APP_DATA && pt && ptlen) { + ssl->app_ptr = pt; ssl->app_len = ptlen; ssl->app_off = 0; + } + } + return take; +} + +int SSL_read(SSL *ssl, void *buf, int num) +{ + if (!ssl || !buf || num <= 0) return -1; + int got = consume_pending_app(ssl, (unsigned char *)buf, num); + if (got > 0) { set_error(ssl, SSL_ERROR_NONE); return got; } + for (;;) { + int rc = 0; + int n = recv_into_matrix(ssl, &rc); + if (n <= 0) return -1; + if (rc == MATRIXSSL_APP_DATA) { + got = consume_pending_app(ssl, (unsigned char *)buf, num); + if (got > 0) { set_error(ssl, SSL_ERROR_NONE); return got; } + } else if (rc == MATRIXSSL_REQUEST_SEND) { + if (flush_outdata(ssl) < 0) return -1; + } else if (rc == MATRIXSSL_RECEIVED_ALERT || rc == MATRIXSSL_REQUEST_CLOSE) { + set_error(ssl, SSL_ERROR_ZERO_RETURN); return 0; + } else if (rc < 0) { + set_error(ssl, SSL_ERROR_SSL); return -1; + } + } +} + +int SSL_write(SSL *ssl, const void *buf, int num) +{ + if (!ssl || !buf || num <= 0) return -1; + if (!ssl->handshake_done && drive_handshake(ssl, ssl->ctx ? ssl->ctx->server : 0) <= 0) return -1; + if (matrixSslEncodeToOutdata(ssl->ms, (unsigned char *)buf, (uint32)num) < 0) { + set_error(ssl, SSL_ERROR_SSL); return -1; + } + if (flush_outdata(ssl) < 0) return -1; + set_error(ssl, SSL_ERROR_NONE); + return num; +} + +int SSL_shutdown(SSL *ssl) +{ + if (!ssl || !ssl->ms || ssl->close_sent) return 1; + ssl->close_sent = 1; + if (matrixSslEncodeClosureAlert(ssl->ms) >= 0) flush_outdata(ssl); + return 1; +} + +int SSL_get_error(const SSL *ssl, int ret) +{ + if (ret > 0) return SSL_ERROR_NONE; + return ssl ? ssl->last_error : (int)g_last_error; +} + +int SSL_pending(const SSL *ssl) +{ + if (!ssl || !ssl->app_ptr || ssl->app_off >= ssl->app_len) return 0; + return (int)(ssl->app_len - ssl->app_off); +} + +long SSL_set_mode(SSL *ssl, long mode) { (void)ssl; return mode; } +long SSL_get_verify_result(const SSL *ssl) { (void)ssl; return X509_V_OK; } + +X509 *SSL_get_peer_certificate(const SSL *ssl) +{ + X509 *x = (X509 *)calloc(1, sizeof(*x)); + if (!x) return NULL; + const char *name = (ssl && ssl->peer_name[0]) ? ssl->peer_name : "matrixssl-peer"; + snprintf(x->subject.common_name, sizeof(x->subject.common_name), "%s", name); + const char *fmt = "-----BEGIN CERTIFICATE-----\n" + "mars-matrixssl-openssl-compat-peer=%s\n" + "-----END CERTIFICATE-----\n"; + size_t need = strlen(fmt) + strlen(name) + 1; + x->pem = (char *)malloc(need); + if (x->pem) snprintf(x->pem, need, fmt, name); + return x; +} + +X509_NAME *X509_get_subject_name(X509 *cert) { return cert ? &cert->subject : NULL; } + +int X509_NAME_get_text_by_NID(X509_NAME *name, int nid, char *buf, int len) +{ + if (!name || nid != NID_commonName || !buf || len <= 0) return -1; + snprintf(buf, (size_t)len, "%s", name->common_name); + return (int)strlen(buf); +} + +EVP_PKEY *X509_get_pubkey(X509 *cert) +{ + (void)cert; + EVP_PKEY *p = (EVP_PKEY *)calloc(1, sizeof(*p)); + if (p) p->type = EVP_PKEY_RSA; + return p; +} + +void EVP_PKEY_free(EVP_PKEY *pkey) { free(pkey); } +void X509_free(X509 *cert) { if (cert) { free(cert->pem); free(cert); } } + +const BIO_METHOD *BIO_s_mem(void) { return &g_mem_method; } + +BIO *BIO_new(const BIO_METHOD *method) +{ + BIO *b = (BIO *)calloc(1, sizeof(*b)); + if (!b) return NULL; + b->type = method ? method->type : 0; + b->fd = -1; + return b; +} + +BIO *BIO_new_ssl_connect(SSL_CTX *ctx) +{ + BIO *b = BIO_new(NULL); + if (!b) return NULL; + b->type = 2; + b->ctx = ctx; + b->ssl = SSL_new(ctx); + if (!b->ssl) { free(b); return NULL; } + return b; +} + +void BIO_free(BIO *bio) +{ + if (!bio) return; + free(bio->host); free(bio->port); free(bio->mem); + free(bio); +} + +void BIO_free_all(BIO *bio) +{ + if (!bio) return; + if (bio->ssl) SSL_free(bio->ssl); + if (bio->fd >= 0) close(bio->fd); + BIO_free(bio); +} + +int BIO_get_ssl(BIO *bio, SSL **ssl) +{ + if (!bio || !ssl) return 0; + *ssl = bio->ssl; + return bio->ssl ? 1 : 0; +} + +void BIO_set_conn_hostname(BIO *bio, const char *name) +{ + if (!bio) return; + free(bio->host); bio->host = dupstr(name); + if (bio->ssl && name) snprintf(bio->ssl->peer_name, sizeof(bio->ssl->peer_name), "%s", name); +} + +void BIO_set_conn_port(BIO *bio, const char *port) +{ + if (!bio) return; + free(bio->port); bio->port = dupstr(port); +} + +static int tcp_connect_host(const char *host, const char *port) +{ + struct addrinfo hints, *res = NULL, *ai; + int fd = -1; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(host, port ? port : "443", &hints, &res) != 0) return -1; + for (ai = res; ai; ai = ai->ai_next) { + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) continue; + if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) break; + close(fd); fd = -1; + } + freeaddrinfo(res); + return fd; +} + +int BIO_do_connect(BIO *bio) +{ + if (!bio || !bio->ssl || !bio->host) return -1; + bio->fd = tcp_connect_host(bio->host, bio->port ? bio->port : "443"); + if (bio->fd < 0) return -1; + SSL_set_fd(bio->ssl, bio->fd); + return SSL_connect(bio->ssl); +} + +int BIO_read(BIO *bio, void *buf, int len) +{ + if (!bio || !buf || len <= 0) return -1; + if (bio->type == 2) { + int r = SSL_read(bio->ssl, buf, len); + bio->should_retry = (r <= 0 && bio->ssl && (bio->ssl->last_error == SSL_ERROR_WANT_READ || bio->ssl->last_error == SSL_ERROR_WANT_WRITE)); + return r; + } + if (bio->type == 1) { + int take = (bio->mem_len < (size_t)len) ? (int)bio->mem_len : len; + if (take > 0) memcpy(buf, bio->mem, (size_t)take); + return take; + } + return -1; +} + +int BIO_write(BIO *bio, const void *buf, int len) +{ + if (!bio || !buf || len <= 0) return -1; + if (bio->type == 2) return SSL_write(bio->ssl, buf, len); + if (bio->type == 1) { + if (bio->mem_len + (size_t)len + 1 > bio->mem_cap) { + size_t cap = (bio->mem_len + (size_t)len + 1) * 2; + char *p = (char *)realloc(bio->mem, cap); + if (!p) return -1; + bio->mem = p; bio->mem_cap = cap; + } + memcpy(bio->mem + bio->mem_len, buf, (size_t)len); + bio->mem_len += (size_t)len; + bio->mem[bio->mem_len] = '\0'; + return len; + } + return -1; +} + +int BIO_should_retry(BIO *bio) { return bio ? bio->should_retry : 0; } + +long BIO_get_mem_data(BIO *bio, const char **data) +{ + if (!bio || bio->type != 1) return 0; + if (data) *data = bio->mem ? bio->mem : ""; + return (long)bio->mem_len; +} + +int PEM_write_bio_X509(BIO *bio, X509 *cert) +{ + if (!bio || !cert) return 0; + const char *pem = cert->pem ? cert->pem : "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n"; + return BIO_write(bio, pem, (int)strlen(pem)) > 0 ? 1 : 0; +}