MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 272 lines 8.1 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <stdbool.h> 4#include <stdint.h> 5#include <stdlib.h> 6#include <string.h> 7 8#include <tlsuv/tlsuv.h> 9#include <tlsuv/tls_engine.h> 10 11#include "ant.h" 12#include "ptr.h" 13#include "errors.h" 14 15#include "modules/tls.h" 16#include "modules/buffer.h" 17 18typedef struct ant_tls_context_wrap_s { 19 ant_value_t obj; 20 tls_context *ctx; 21 tlsuv_private_key_t key; 22 tlsuv_certificate_t cert; 23 bool closed; 24} ant_tls_context_wrap_t; 25 26enum { TLS_CONTEXT_NATIVE_TAG = 0x544c5343u }; // TLSC 27static ant_value_t g_tls_context_proto = 0; 28 29static void tls_context_free(ant_tls_context_wrap_t *wrap) { 30 if (!wrap || wrap->closed) return; 31 wrap->closed = true; 32 33 if (wrap->cert && wrap->cert->free) wrap->cert->free(wrap->cert); 34 if (wrap->key && wrap->key->free) wrap->key->free(wrap->key); 35 if (wrap->ctx && wrap->ctx->free_ctx) wrap->ctx->free_ctx(wrap->ctx); 36 37 wrap->cert = NULL; 38 wrap->key = NULL; 39 wrap->ctx = NULL; 40} 41 42static ant_tls_context_wrap_t *tls_context_data(ant_value_t value) { 43 if (!js_check_native_tag(value, TLS_CONTEXT_NATIVE_TAG)) return NULL; 44 return (ant_tls_context_wrap_t *)js_get_native_ptr(value); 45} 46 47static bool tls_value_bytes( 48 ant_t *js, 49 ant_value_t value, 50 const char **bytes_out, 51 size_t *len_out, 52 ant_value_t *error_out 53) { 54 ant_value_t str_value = 0; 55 const uint8_t *buffer_bytes = NULL; 56 57 if (error_out) *error_out = js_mkundef(); 58 if (bytes_out) *bytes_out = NULL; 59 if (len_out) *len_out = 0; 60 61 if (vtype(value) == T_UNDEF || vtype(value) == T_NULL) return true; 62 if (buffer_source_get_bytes(js, value, &buffer_bytes, len_out)) { 63 if (bytes_out) *bytes_out = (const char *)buffer_bytes; 64 return true; 65 } 66 67 str_value = js_tostring_val(js, value); 68 if (is_err(str_value)) { 69 if (error_out) *error_out = str_value; 70 return false; 71 } 72 73 if (!bytes_out || !len_out) return true; 74 *bytes_out = js_getstr(js, str_value, len_out); 75 76 if (!*bytes_out) { 77 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TLS input"); 78 return false; 79 } 80 81 return true; 82} 83 84static ant_value_t tls_make_error(ant_t *js, tls_context *ctx, long code, const char *fallback) { 85 const char *message = fallback; 86 if (ctx && ctx->strerror) { 87 const char *detail = ctx->strerror(code); 88 if (detail && *detail) message = detail; 89 } 90 return js_mkerr_typed(js, JS_ERR_TYPE, "%s", message ? message : "TLS error"); 91} 92 93static ant_value_t js_tls_context_close(ant_t *js, ant_value_t *args, int nargs) { 94 ant_tls_context_wrap_t *wrap = tls_context_data(js_getthis(js)); 95 if (!wrap) return js_getthis(js); 96 97 tls_context_free(wrap); 98 js_set_native_ptr(wrap->obj, NULL); 99 js_set_native_tag(wrap->obj, 0); 100 return js_getthis(js); 101} 102 103static void tls_init_context_proto(ant_t *js) { 104 if (g_tls_context_proto) return; 105 106 g_tls_context_proto = js_mkobj(js); 107 js_set(js, g_tls_context_proto, "close", js_mkfun(js_tls_context_close)); 108} 109 110static ant_value_t js_tls_create_context(ant_t *js, ant_value_t *args, int nargs) { 111 ant_value_t options = nargs > 0 ? args[0] : js_mkundef(); 112 ant_value_t obj = 0; 113 ant_value_t error = js_mkundef(); 114 115 ant_tls_context_wrap_t *wrap = NULL; 116 ant_value_t ca_v = js_mkundef(); 117 ant_value_t key_v = js_mkundef(); 118 ant_value_t cert_v = js_mkundef(); 119 ant_value_t partial_v = js_mkundef(); 120 121 const char *ca = NULL; 122 const char *key_data = NULL; 123 const char *cert_data = NULL; 124 125 size_t ca_len = 0; 126 size_t key_len = 0; 127 size_t cert_len = 0; 128 int rc = 0; 129 130 if (vtype(options) != T_UNDEF && vtype(options) != T_NULL && vtype(options) != T_OBJ) 131 return js_mkerr_typed(js, JS_ERR_TYPE, "TLS context options must be an object"); 132 133 tls_init_context_proto(js); 134 135 if (vtype(options) == T_OBJ) { 136 ca_v = js_get(js, options, "ca"); 137 key_v = js_get(js, options, "key"); 138 cert_v = js_get(js, options, "cert"); 139 partial_v = js_get(js, options, "allowPartialChain"); 140 } 141 142 if (!tls_value_bytes(js, ca_v, &ca, &ca_len, &error)) return error; 143 if (!tls_value_bytes(js, key_v, &key_data, &key_len, &error)) return error; 144 if (!tls_value_bytes(js, cert_v, &cert_data, &cert_len, &error)) return error; 145 146 wrap = calloc(1, sizeof(*wrap)); 147 if (!wrap) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 148 149 wrap->ctx = default_tls_context(ca, ca_len); 150 if (!wrap->ctx) { 151 free(wrap); 152 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to create TLS context"); 153 } 154 155 if (wrap->ctx->allow_partial_chain && js_truthy(js, partial_v)) { 156 rc = wrap->ctx->allow_partial_chain(wrap->ctx, 1); 157 if (rc != 0) { 158 ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to enable partial chain verification"); 159 tls_context_free(wrap); 160 free(wrap); 161 return err; 162 }} 163 164 if (key_data) { 165 if (!wrap->ctx->load_key) { 166 tls_context_free(wrap); 167 free(wrap); 168 return js_mkerr_typed(js, JS_ERR_TYPE, "TLS engine does not support loading private keys"); 169 } 170 171 rc = wrap->ctx->load_key(&wrap->key, key_data, key_len); 172 if (rc != 0) { 173 ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to load TLS private key"); 174 tls_context_free(wrap); 175 free(wrap); 176 return err; 177 } 178 179 if (cert_data) { 180 if (!wrap->ctx->load_cert) { 181 tls_context_free(wrap); 182 free(wrap); 183 return js_mkerr_typed(js, JS_ERR_TYPE, "TLS engine does not support loading certificates"); 184 } 185 186 rc = wrap->ctx->load_cert(&wrap->cert, cert_data, cert_len); 187 if (rc != 0) { 188 ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to load TLS certificate"); 189 tls_context_free(wrap); 190 free(wrap); 191 return err; 192 }} 193 194 if (!wrap->ctx->set_own_cert) { 195 tls_context_free(wrap); 196 free(wrap); 197 return js_mkerr_typed(js, JS_ERR_TYPE, "TLS engine does not support own certificate configuration"); 198 } 199 200 rc = wrap->ctx->set_own_cert(wrap->ctx, wrap->key, wrap->cert); 201 if (rc != 0) { 202 ant_value_t err = tls_make_error(js, wrap->ctx, rc, "Failed to configure TLS certificate"); 203 tls_context_free(wrap); 204 free(wrap); 205 return err; 206 } 207 } else if (cert_data) { 208 tls_context_free(wrap); 209 free(wrap); 210 return js_mkerr_typed(js, JS_ERR_TYPE, "TLS certificate requires a private key"); 211 } 212 213 obj = js_mkobj(js); 214 js_set_proto_init(obj, g_tls_context_proto); 215 wrap->obj = obj; 216 217 js_set_native_ptr(obj, wrap); 218 js_set_native_tag(obj, TLS_CONTEXT_NATIVE_TAG); 219 220 return obj; 221} 222 223static ant_value_t js_tls_is_context(ant_t *js, ant_value_t *args, int nargs) { 224 ant_tls_context_wrap_t *wrap = nargs > 0 ? tls_context_data(args[0]) : NULL; 225 return js_bool(wrap && !wrap->closed && wrap->ctx); 226} 227 228static ant_value_t js_tls_set_config_path(ant_t *js, ant_value_t *args, int nargs) { 229 ant_value_t str_value = js_mkundef(); 230 const char *path = NULL; 231 int rc = 0; 232 233 if (nargs < 1 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL) { 234 rc = tlsuv_set_config_path(NULL); 235 if (rc != 0) return js_mkerr_typed(js, JS_ERR_TYPE, "%s", uv_strerror(rc)); 236 return js_mkundef(); 237 } 238 239 str_value = js_tostring_val(js, args[0]); 240 if (is_err(str_value)) return str_value; 241 242 path = js_getstr(js, str_value, NULL); 243 if (!path) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TLS config path"); 244 245 rc = tlsuv_set_config_path(path); 246 if (rc != 0) return js_mkerr_typed(js, JS_ERR_TYPE, "%s", uv_strerror(rc)); 247 248 return js_mkundef(); 249} 250 251ant_value_t internal_tls_library(ant_t *js) { 252 ant_value_t lib = js_mkobj(js); 253 ant_value_t version = js_mkstr(js, "unknown", 7); 254 255 tls_context *ctx = default_tls_context(NULL, 0); 256 const char *version_str = NULL; 257 258 if (ctx && ctx->version) { 259 version_str = ctx->version(); 260 if (version_str) version = js_mkstr(js, version_str, strlen(version_str)); 261 } 262 263 if (ctx && ctx->free_ctx) ctx->free_ctx(ctx); 264 tls_init_context_proto(js); 265 266 js_set(js, lib, "version", version); 267 js_set(js, lib, "createContext", js_mkfun(js_tls_create_context)); 268 js_set(js, lib, "isContext", js_mkfun(js_tls_is_context)); 269 js_set(js, lib, "setConfigPath", js_mkfun(js_tls_set_config_path)); 270 271 return lib; 272}