Page MenuHomePhorge

tls.c
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None
#include <compat.h> // IWYU pragma: keep
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <tlsuv/tlsuv.h>
#include <tlsuv/tls_engine.h>
#include "ant.h"
#include "ptr.h"
#include "errors.h"
#include "modules/tls.h"
#include "modules/buffer.h"
typedef struct ant_tls_context_wrap_s {
ant_value_t obj;
tls_context *ctx;
tlsuv_private_key_t key;
tlsuv_certificate_t cert;
bool closed;
} ant_tls_context_wrap_t;
enum { TLS_CONTEXT_NATIVE_TAG = 0x544c5343u }; // TLSC
static ant_value_t g_tls_context_proto = 0;
static void tls_context_free(ant_tls_context_wrap_t *wrap) {
if (!wrap || wrap->closed) return;
wrap->closed = true;
if (wrap->cert && wrap->cert->free) wrap->cert->free(wrap->cert);
if (wrap->key && wrap->key->free) wrap->key->free(wrap->key);
if (wrap->ctx && wrap->ctx->free_ctx) wrap->ctx->free_ctx(wrap->ctx);
wrap->cert = NULL;
wrap->key = NULL;
wrap->ctx = NULL;
}
static ant_tls_context_wrap_t *tls_context_data(ant_value_t value) {
if (!js_check_native_tag(value, TLS_CONTEXT_NATIVE_TAG)) return NULL;
return (ant_tls_context_wrap_t *)js_get_native_ptr(value);
}
static bool tls_value_bytes(
ant_t *js,
ant_value_t value,
const char **bytes_out,
size_t *len_out,
ant_value_t *error_out
) {
ant_value_t str_value = 0;
const uint8_t *buffer_bytes = NULL;
if (error_out) *error_out = js_mkundef();
if (bytes_out) *bytes_out = NULL;
if (len_out) *len_out = 0;
if (vtype(value) == T_UNDEF || vtype(value) == T_NULL) return true;
if (buffer_source_get_bytes(js, value, &buffer_bytes, len_out)) {
if (bytes_out) *bytes_out = (const char *)buffer_bytes;
return true;
}
str_value = js_tostring_val(js, value);
if (is_err(str_value)) {
if (error_out) *error_out = str_value;
return false;
}
if (!bytes_out || !len_out) return true;
*bytes_out = js_getstr(js, str_value, len_out);
if (!*bytes_out) {
if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TLS input");
return false;
}
return true;
}
static ant_value_t tls_make_error(ant_t *js, tls_context *ctx, long code, const char *fallback) {
const char *message = fallback;
if (ctx && ctx->strerror) {
const char *detail = ctx->strerror(code);
if (detail && *detail) message = detail;
}
return js_mkerr_typed(js, JS_ERR_TYPE, "%s", message ? message : "TLS error");
}
static ant_value_t js_tls_context_close(ant_t *js, ant_value_t *args, int nargs) {
ant_tls_context_wrap_t *wrap = tls_context_data(js_getthis(js));
if (!wrap) return js_getthis(js);
tls_context_free(wrap);
js_set_native_ptr(wrap->obj, NULL);
js_set_native_tag(wrap->obj, 0);
return js_getthis(js);
}
static void tls_init_context_proto(ant_t *js) {
if (g_tls_context_proto) return;
g_tls_context_proto = js_mkobj(js);
js_set(js, g_tls_context_proto, "close", js_mkfun(js_tls_context_close));
}
static ant_value_t js_tls_create_context(ant_t *js, ant_value_t *args, int nargs) {
ant_value_t options = nargs > 0 ? args[0] : js_mkundef();
ant_value_t obj = 0;
ant_value_t error = js_mkundef();
ant_tls_context_wrap_t *wrap = NULL;
ant_value_t ca_v = js_mkundef();
ant_value_t key_v = js_mkundef();
ant_value_t cert_v = js_mkundef();
ant_value_t partial_v = js_mkundef();
const char *ca = NULL;
const char *key_data = NULL;
const char *cert_data = NULL;
size_t ca_len = 0;
size_t key_len = 0;
size_t cert_len = 0;
int rc = 0;
if (vtype(options) != T_UNDEF && vtype(options) != T_NULL && vtype(options) != T_OBJ)
return js_mkerr_typed(js, JS_ERR_TYPE, "TLS context options must be an object");
tls_init_context_proto(js);
if (vtype(options) == T_OBJ) {
ca_v = js_get(js, options, "ca");
key_v = js_get(js, options, "key");
cert_v = js_get(js, options, "cert");
partial_v = js_get(js, options, "allowPartialChain");
}
if (!tls_value_bytes(js, ca_v, &ca, &ca_len, &error)) return error;
if (!tls_value_bytes(js, key_v, &key_data, &key_len, &error)) return error;
if (!tls_value_bytes(js, cert_v, &cert_data, &cert_len, &error)) return error;
wrap = calloc(1, sizeof(*wrap));
if (!wrap) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
wrap->ctx = default_tls_context(ca, ca_len);
if (!wrap->ctx) {
free(wrap);
return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to create TLS context");
}
if (wrap->ctx->allow_partial_chain && js_truthy(js, partial_v)) {
rc = wrap->ctx->allow_partial_chain(wrap->ctx, 1);
if (rc != 0) {
ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to enable partial chain verification");
tls_context_free(wrap);
free(wrap);
return err;
}}
if (key_data) {
if (!wrap->ctx->load_key) {
tls_context_free(wrap);
free(wrap);
return js_mkerr_typed(js, JS_ERR_TYPE, "TLS engine does not support loading private keys");
}
rc = wrap->ctx->load_key(&wrap->key, key_data, key_len);
if (rc != 0) {
ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to load TLS private key");
tls_context_free(wrap);
free(wrap);
return err;
}
if (cert_data) {
if (!wrap->ctx->load_cert) {
tls_context_free(wrap);
free(wrap);
return js_mkerr_typed(js, JS_ERR_TYPE, "TLS engine does not support loading certificates");
}
rc = wrap->ctx->load_cert(&wrap->cert, cert_data, cert_len);
if (rc != 0) {
ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to load TLS certificate");
tls_context_free(wrap);
free(wrap);
return err;
}}
if (!wrap->ctx->set_own_cert) {
tls_context_free(wrap);
free(wrap);
return js_mkerr_typed(js, JS_ERR_TYPE, "TLS engine does not support own certificate configuration");
}
rc = wrap->ctx->set_own_cert(wrap->ctx, wrap->key, wrap->cert);
if (rc != 0) {
ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to configure TLS certificate");
tls_context_free(wrap);
free(wrap);
return err;
}
} else if (cert_data) {
tls_context_free(wrap);
free(wrap);
return js_mkerr_typed(js, JS_ERR_TYPE, "TLS certificate requires a private key");
}
obj = js_mkobj(js);
js_set_proto_init(obj, g_tls_context_proto);
wrap->obj = obj;
js_set_native_ptr(obj, wrap);
js_set_native_tag(obj, TLS_CONTEXT_NATIVE_TAG);
return obj;
}
static ant_value_t js_tls_is_context(ant_t *js, ant_value_t *args, int nargs) {
ant_tls_context_wrap_t *wrap = nargs > 0 ? tls_context_data(args[0]) : NULL;
return js_bool(wrap && !wrap->closed && wrap->ctx);
}
static ant_value_t js_tls_set_config_path(ant_t *js, ant_value_t *args, int nargs) {
ant_value_t str_value = js_mkundef();
const char *path = NULL;
int rc = 0;
if (nargs < 1 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) {
rc = tlsuv_set_config_path(NULL);
if (rc != 0) return js_mkerr_typed(js, JS_ERR_TYPE, "%s", uv_strerror(rc));
return js_mkundef();
}
str_value = js_tostring_val(js, args[0]);
if (is_err(str_value)) return str_value;
path = js_getstr(js, str_value, NULL);
if (!path) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TLS config path");
rc = tlsuv_set_config_path(path);
if (rc != 0) return js_mkerr_typed(js, JS_ERR_TYPE, "%s", uv_strerror(rc));
return js_mkundef();
}
ant_value_t internal_tls_library(ant_t *js) {
ant_value_t lib = js_mkobj(js);
ant_value_t version = js_mkstr(js, "unknown", 7);
tls_context *ctx = default_tls_context(NULL, 0);
const char *version_str = NULL;
if (ctx && ctx->version) {
version_str = ctx->version();
if (version_str) version = js_mkstr(js, version_str, strlen(version_str));
}
if (ctx && ctx->free_ctx) ctx->free_ctx(ctx);
tls_init_context_proto(js);
js_set(js, lib, "version", version);
js_set(js, lib, "createContext", js_mkfun(js_tls_create_context));
js_set(js, lib, "isContext", js_mkfun(js_tls_is_context));
js_set(js, lib, "setConfigPath", js_mkfun(js_tls_set_config_path));
return lib;
}

File Metadata

Mime Type
text/x-c
Expires
Sat, Apr 4, 12:58 AM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
520893
Default Alt Text
tls.c (7 KB)

Event Timeline