/* * 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; }