diff options
Diffstat (limited to 'util-src')
-rw-r--r-- | util-src/GNUmakefile | 4 | ||||
-rw-r--r-- | util-src/compat.c | 6 | ||||
-rw-r--r-- | util-src/crand.c | 11 | ||||
-rw-r--r-- | util-src/crypto.c | 738 | ||||
-rw-r--r-- | util-src/encodings.c | 10 | ||||
-rw-r--r-- | util-src/hashes.c | 296 | ||||
-rw-r--r-- | util-src/managed_pointer.h | 61 | ||||
-rw-r--r-- | util-src/net.c | 10 | ||||
-rw-r--r-- | util-src/poll.c | 34 | ||||
-rw-r--r-- | util-src/pposix.c | 126 | ||||
-rw-r--r-- | util-src/ringbuffer.c | 8 | ||||
-rw-r--r-- | util-src/signal.c | 100 | ||||
-rw-r--r-- | util-src/strbitop.c | 52 | ||||
-rw-r--r-- | util-src/struct.c | 16 | ||||
-rw-r--r-- | util-src/table.c | 50 | ||||
-rw-r--r-- | util-src/time.c | 5 | ||||
-rw-r--r-- | util-src/windows.c | 5 |
17 files changed, 1339 insertions, 193 deletions
diff --git a/util-src/GNUmakefile b/util-src/GNUmakefile index 810f39f7..3f539387 100644 --- a/util-src/GNUmakefile +++ b/util-src/GNUmakefile @@ -8,7 +8,7 @@ TARGET?=../util/ ALL=encodings.so hashes.so net.so pposix.so signal.so table.so \ ringbuffer.so time.so poll.so compat.so strbitop.so \ - struct.so + struct.so crypto.so ifdef RANDOM ALL+=crand.so @@ -28,7 +28,7 @@ clean: encodings.o: CFLAGS+=$(IDNA_FLAGS) encodings.so: LDLIBS+=$(IDNA_LIBS) -hashes.so: LDLIBS+=$(OPENSSL_LIBS) +crypto.so hashes.so: LDLIBS+=$(OPENSSL_LIBS) crand.o: CFLAGS+=-DWITH_$(RANDOM) crand.so: LDLIBS+=$(RANDOM_LIBS) diff --git a/util-src/compat.c b/util-src/compat.c index 34b35c35..3918bf3e 100644 --- a/util-src/compat.c +++ b/util-src/compat.c @@ -19,7 +19,7 @@ static int lc_xpcall (lua_State *L) { return lua_gettop(L); } -int luaopen_util_compat(lua_State *L) { +int luaopen_prosody_util_compat(lua_State *L) { lua_createtable(L, 0, 2); { lua_pushcfunction(L, lc_xpcall); @@ -27,3 +27,7 @@ int luaopen_util_compat(lua_State *L) { } return 1; } + +int luaopen_util_compat(lua_State *L) { + return luaopen_prosody_util_compat(L); +} diff --git a/util-src/crand.c b/util-src/crand.c index 160ac1f6..6f17c162 100644 --- a/util-src/crand.c +++ b/util-src/crand.c @@ -45,7 +45,7 @@ #endif /* This wasn't present before glibc 2.25 */ -int getrandom(void *buf, size_t buflen, unsigned int flags) { +static int getrandom(void *buf, size_t buflen, unsigned int flags) { return syscall(SYS_getrandom, buf, buflen, flags); } #else @@ -66,7 +66,7 @@ int getrandom(void *buf, size_t buflen, unsigned int flags) { #define SMALLBUFSIZ 32 #endif -int Lrandom(lua_State *L) { +static int Lrandom(lua_State *L) { char smallbuf[SMALLBUFSIZ]; char *buf = &smallbuf[0]; const lua_Integer l = luaL_checkinteger(L, 1); @@ -123,10 +123,8 @@ int Lrandom(lua_State *L) { return 1; } -int luaopen_util_crand(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +int luaopen_prosody_util_crand(lua_State *L) { luaL_checkversion(L); -#endif lua_createtable(L, 0, 2); lua_pushcfunction(L, Lrandom); @@ -144,3 +142,6 @@ int luaopen_util_crand(lua_State *L) { return 1; } +int luaopen_util_crand(lua_State *L) { + return luaopen_prosody_util_crand(L); +} diff --git a/util-src/crypto.c b/util-src/crypto.c new file mode 100644 index 00000000..68733c7c --- /dev/null +++ b/util-src/crypto.c @@ -0,0 +1,738 @@ +/* Prosody IM +-- Copyright (C) 2022 Matthew Wild +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- +*/ + +/* +* crypto.c +* Lua library for cryptographic operations using OpenSSL +*/ + +#include <string.h> +#include <stdlib.h> + +#ifdef _MSC_VER +typedef unsigned __int32 uint32_t; +#else +#include <inttypes.h> +#endif + +#include "lua.h" +#include "lauxlib.h" +#include <openssl/crypto.h> +#include <openssl/ecdsa.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/obj_mac.h> +#include <openssl/param_build.h> +#include <openssl/pem.h> + +#if (LUA_VERSION_NUM == 501) +#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) +#endif + +/* The max size of an encoded 'R' or 'S' value. P-521 = 521 bits = 66 bytes */ +#define MAX_ECDSA_SIG_INT_BYTES 66 + +#include "managed_pointer.h" + +#define PKEY_MT_TAG "util.crypto key" + +static BIO* new_memory_BIO(void) { + return BIO_new(BIO_s_mem()); +} + +MANAGED_POINTER_ALLOCATOR(new_managed_EVP_MD_CTX, EVP_MD_CTX*, EVP_MD_CTX_new, EVP_MD_CTX_free) +MANAGED_POINTER_ALLOCATOR(new_managed_BIO_s_mem, BIO*, new_memory_BIO, BIO_free) +MANAGED_POINTER_ALLOCATOR(new_managed_EVP_CIPHER_CTX, EVP_CIPHER_CTX*, EVP_CIPHER_CTX_new, EVP_CIPHER_CTX_free) + +#define CRYPTO_KEY_TYPE_ERR "unexpected key type: got '%s', expected '%s'" + +static EVP_PKEY* pkey_from_arg(lua_State *L, int idx, const int type, const int require_private) { + EVP_PKEY *pkey = *(EVP_PKEY**)luaL_checkudata(L, idx, PKEY_MT_TAG); + int got_type; + if(type || require_private) { + lua_getuservalue(L, idx); + if(type != 0) { + lua_getfield(L, -1, "type"); + got_type = lua_tointeger(L, -1); + if(got_type != type) { + const char *got_key_type_name = OBJ_nid2sn(got_type); + const char *want_key_type_name = OBJ_nid2sn(type); + lua_pushfstring(L, CRYPTO_KEY_TYPE_ERR, got_key_type_name, want_key_type_name); + luaL_argerror(L, idx, lua_tostring(L, -1)); + } + lua_pop(L, 1); + } + if(require_private != 0) { + lua_getfield(L, -1, "private"); + if(lua_toboolean(L, -1) != 1) { + luaL_argerror(L, idx, "private key expected, got public key only"); + } + lua_pop(L, 1); + } + lua_pop(L, 1); + } + return pkey; +} + +static int Lpkey_finalizer(lua_State *L) { + EVP_PKEY *pkey = pkey_from_arg(L, 1, 0, 0); + EVP_PKEY_free(pkey); + return 0; +} + +static int Lpkey_meth_get_type(lua_State *L) { + EVP_PKEY *pkey = pkey_from_arg(L, 1, 0, 0); + + int key_type = EVP_PKEY_id(pkey); + lua_pushstring(L, OBJ_nid2sn(key_type)); + return 1; +} + +static int Lpkey_meth_derive(lua_State *L) { + size_t outlen; + EVP_PKEY *key = pkey_from_arg(L, 1, 0, 0); + EVP_PKEY *peer = pkey_from_arg(L, 2, 0, 0); + EVP_PKEY_CTX *ctx; + BUF_MEM *buf; + BIO *bio = new_managed_BIO_s_mem(L); + BIO_get_mem_ptr(bio, &buf); + if (!(ctx = EVP_PKEY_CTX_new(key, NULL))) + goto sslerr; + if (EVP_PKEY_derive_init(ctx) <= 0) + goto sslerr; + if (EVP_PKEY_derive_set_peer(ctx, peer) <= 0) + goto sslerr; + if (EVP_PKEY_derive(ctx, NULL, &outlen) <= 0) + goto sslerr; + if (!BUF_MEM_grow_clean(buf, outlen)) + goto sslerr; + if (EVP_PKEY_derive(ctx, (unsigned char*)buf->data, &outlen) <= 0) + goto sslerr; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + lua_pushlstring(L, buf->data, outlen); + BIO_reset(bio); + return 1; +sslerr: + if (ctx) { + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + } + BIO_reset(bio); + return luaL_error(L, "pkey:derive failed"); +} + +static int base_evp_sign(lua_State *L, const int key_type, const EVP_MD *digest_type) { + EVP_PKEY *pkey = pkey_from_arg(L, 1, (key_type!=NID_rsassaPss)?key_type:NID_rsaEncryption, 1); + luaL_Buffer sigbuf; + + size_t msg_len; + const unsigned char* msg = (unsigned char*)lua_tolstring(L, 2, &msg_len); + + size_t sig_len; + unsigned char *sig = NULL; + EVP_MD_CTX *md_ctx = new_managed_EVP_MD_CTX(L); + + if(EVP_DigestSignInit(md_ctx, NULL, digest_type, NULL, pkey) != 1) { + lua_pushnil(L); + return 1; + } + if(key_type == NID_rsassaPss) { + EVP_PKEY_CTX_set_rsa_padding(EVP_MD_CTX_pkey_ctx(md_ctx), RSA_PKCS1_PSS_PADDING); + } + if(EVP_DigestSign(md_ctx, NULL, &sig_len, msg, msg_len) != 1) { + lua_pushnil(L); + return 1; + } + + // COMPAT w/ Lua 5.1 + luaL_buffinit(L, &sigbuf); + sig = memset(luaL_prepbuffer(&sigbuf), 0, sig_len); + + if(EVP_DigestSign(md_ctx, sig, &sig_len, msg, msg_len) != 1) { + lua_pushnil(L); + } + else { + luaL_addsize(&sigbuf, sig_len); + luaL_pushresult(&sigbuf); + return 1; + } + + return 1; +} + +static int base_evp_verify(lua_State *L, const int key_type, const EVP_MD *digest_type) { + EVP_PKEY *pkey = pkey_from_arg(L, 1, (key_type!=NID_rsassaPss)?key_type:NID_rsaEncryption, 0); + + size_t msg_len; + const unsigned char *msg = (unsigned char*)luaL_checklstring(L, 2, &msg_len); + + size_t sig_len; + const unsigned char *sig = (unsigned char*)luaL_checklstring(L, 3, &sig_len); + + EVP_MD_CTX *md_ctx = EVP_MD_CTX_new(); + + if(EVP_DigestVerifyInit(md_ctx, NULL, digest_type, NULL, pkey) != 1) { + lua_pushnil(L); + goto cleanup; + } + if(key_type == NID_rsassaPss) { + EVP_PKEY_CTX_set_rsa_padding(EVP_MD_CTX_pkey_ctx(md_ctx), RSA_PKCS1_PSS_PADDING); + } + int result = EVP_DigestVerify(md_ctx, sig, sig_len, msg, msg_len); + if(result == 0) { + lua_pushboolean(L, 0); + } else if(result != 1) { + lua_pushnil(L); + } + else { + lua_pushboolean(L, 1); + } +cleanup: + EVP_MD_CTX_free(md_ctx); + return 1; +} + +static int Lpkey_meth_public_raw(lua_State *L) { + OSSL_PARAM *params; + EVP_PKEY *pkey = pkey_from_arg(L, 1, 0, 0); + + if (EVP_PKEY_todata(pkey, EVP_PKEY_PUBLIC_KEY, ¶ms)) { + OSSL_PARAM *item = params; + while (item->key) { + if (!strcmp("pub", item->key)) { + lua_pushlstring(L, item->data, item->data_size); + break; + } + item++; + } + if (!item->key) lua_pushnil(L); + OSSL_PARAM_free(params); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int Lpkey_meth_public_pem(lua_State *L) { + char *data; + size_t bytes; + EVP_PKEY *pkey = pkey_from_arg(L, 1, 0, 0); + BIO *bio = new_managed_BIO_s_mem(L); + if(PEM_write_bio_PUBKEY(bio, pkey)) { + bytes = BIO_get_mem_data(bio, &data); + if (bytes > 0) { + lua_pushlstring(L, data, bytes); + } + else { + lua_pushnil(L); + } + } + else { + lua_pushnil(L); + } + return 1; +} + +static int Lpkey_meth_private_pem(lua_State *L) { + char *data; + size_t bytes; + EVP_PKEY *pkey = pkey_from_arg(L, 1, 0, 1); + BIO *bio = new_managed_BIO_s_mem(L); + + if(PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) { + bytes = BIO_get_mem_data(bio, &data); + if (bytes > 0) { + lua_pushlstring(L, data, bytes); + } + else { + lua_pushnil(L); + } + } + else { + lua_pushnil(L); + } + return 1; +} + +static int push_pkey(lua_State *L, EVP_PKEY *pkey, const int type, const int privkey) { + EVP_PKEY **ud = lua_newuserdata(L, sizeof(EVP_PKEY*)); + *ud = pkey; + luaL_newmetatable(L, PKEY_MT_TAG); + lua_setmetatable(L, -2); + + /* Set some info about the key and attach it as a user value */ + lua_newtable(L); + if(type != 0) { + lua_pushinteger(L, type); + lua_setfield(L, -2, "type"); + } + if(privkey != 0) { + lua_pushboolean(L, 1); + lua_setfield(L, -2, "private"); + } + lua_setuservalue(L, -2); + return 1; +} + +static int Lgenerate_ed25519_keypair(lua_State *L) { + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL); + + /* Generate key */ + EVP_PKEY_keygen_init(pctx); + EVP_PKEY_keygen(pctx, &pkey); + EVP_PKEY_CTX_free(pctx); + + push_pkey(L, pkey, NID_ED25519, 1); + return 1; +} + +static int Lgenerate_p256_keypair(lua_State *L) { + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + + /* Generate key */ + if (EVP_PKEY_keygen_init(pctx) <= 0) goto err; + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1) <= 0) goto err; + if (EVP_PKEY_keygen(pctx, &pkey) <= 0) goto err; + EVP_PKEY_CTX_free(pctx); + + push_pkey(L, pkey, NID_X9_62_prime256v1, 1); + return 1; + +err: + if (pctx) EVP_PKEY_CTX_free(pctx); + lua_pushnil(L); + return 1; +} + +static int Limport_private_pem(lua_State *L) { + EVP_PKEY *pkey = NULL; + + size_t privkey_bytes; + const char* privkey_data; + BIO *bio = new_managed_BIO_s_mem(L); + + privkey_data = luaL_checklstring(L, 1, &privkey_bytes); + BIO_write(bio, privkey_data, privkey_bytes); + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (pkey) { + push_pkey(L, pkey, EVP_PKEY_id(pkey), 1); + } + else { + lua_pushnil(L); + } + + return 1; +} + +static int Limport_public_ec_raw(lua_State *L) { + OSSL_PARAM_BLD *param_bld = NULL; + OSSL_PARAM *params = NULL; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + + size_t pubkey_bytes; + const char* pubkey_data = luaL_checklstring(L, 1, &pubkey_bytes); + const char* curve = luaL_checkstring(L, 2); + + param_bld = OSSL_PARAM_BLD_new(); + if (!param_bld) goto err; + if (!OSSL_PARAM_BLD_push_utf8_string(param_bld, "group", curve, 0)) goto err; + if (!OSSL_PARAM_BLD_push_octet_string(param_bld, "pub", pubkey_data, pubkey_bytes)) goto err; + params = OSSL_PARAM_BLD_to_param(param_bld); + if (!params) goto err; + ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (!ctx) goto err; + if (!EVP_PKEY_fromdata_init(ctx)) goto err; + if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) goto err; + + push_pkey(L, pkey, EVP_PKEY_id(pkey), 0); + + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(param_bld); + + return 1; +err: + if (ctx) EVP_PKEY_CTX_free(ctx); + if (params) OSSL_PARAM_free(params); + if (param_bld) OSSL_PARAM_BLD_free(param_bld); + lua_pushnil(L); + return 1; +} + +static int Limport_public_pem(lua_State *L) { + EVP_PKEY *pkey = NULL; + + size_t pubkey_bytes; + const char* pubkey_data; + BIO *bio = new_managed_BIO_s_mem(L); + + pubkey_data = luaL_checklstring(L, 1, &pubkey_bytes); + BIO_write(bio, pubkey_data, pubkey_bytes); + pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + if (pkey) { + push_pkey(L, pkey, EVP_PKEY_id(pkey), 0); + } + else { + lua_pushnil(L); + } + + return 1; +} + +static int Led25519_sign(lua_State *L) { + return base_evp_sign(L, NID_ED25519, NULL); +} + +static int Led25519_verify(lua_State *L) { + return base_evp_verify(L, NID_ED25519, NULL); +} + +/* encrypt(key, iv, plaintext) */ +static int Levp_encrypt(lua_State *L, const EVP_CIPHER *cipher, const unsigned char expected_key_len, const unsigned char expected_iv_len, const size_t tag_len) { + EVP_CIPHER_CTX *ctx; + luaL_Buffer ciphertext_buffer; + + size_t key_len, iv_len, plaintext_len; + int ciphertext_len, final_len; + + const unsigned char *key = (unsigned char*)luaL_checklstring(L, 1, &key_len); + const unsigned char *iv = (unsigned char*)luaL_checklstring(L, 2, &iv_len); + const unsigned char *plaintext = (unsigned char*)luaL_checklstring(L, 3, &plaintext_len); + + if(key_len != expected_key_len) { + return luaL_error(L, "key must be %d bytes", expected_key_len); + } + if(iv_len != expected_iv_len) { + return luaL_error(L, "iv must be %d bytes", expected_iv_len); + } + if(lua_gettop(L) > 3) { + return luaL_error(L, "Expected 3 arguments, got %d", lua_gettop(L)); + } + + // Create and initialise the context + ctx = new_managed_EVP_CIPHER_CTX(L); + + // Initialise the encryption operation + if(1 != EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL)) { + return luaL_error(L, "Error while initializing encryption engine"); + } + + // Initialise key and IV + if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) { + return luaL_error(L, "Error while initializing key/iv"); + } + + luaL_buffinit(L, &ciphertext_buffer); + unsigned char *ciphertext = (unsigned char*)luaL_prepbuffsize(&ciphertext_buffer, plaintext_len+tag_len); + + if(1 != EVP_EncryptUpdate(ctx, ciphertext, &ciphertext_len, plaintext, plaintext_len)) { + return luaL_error(L, "Error while encrypting data"); + } + + /* + * Finalise the encryption. Normally ciphertext bytes may be written at + * this stage, but this does not occur in GCM mode + */ + if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + ciphertext_len, &final_len)) { + return luaL_error(L, "Error while encrypting final data"); + } + if(final_len != 0) { + return luaL_error(L, "Non-zero final data"); + } + + if(tag_len > 0) { + /* Get the tag */ + if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, ciphertext + ciphertext_len)) { + return luaL_error(L, "Unable to read AEAD tag of encrypted data"); + } + /* Append tag */ + luaL_addsize(&ciphertext_buffer, ciphertext_len + tag_len); + } else { + luaL_addsize(&ciphertext_buffer, ciphertext_len); + } + luaL_pushresult(&ciphertext_buffer); + + return 1; +} + +static int Laes_128_gcm_encrypt(lua_State *L) { + return Levp_encrypt(L, EVP_aes_128_gcm(), 16, 12, 16); +} + +static int Laes_256_gcm_encrypt(lua_State *L) { + return Levp_encrypt(L, EVP_aes_256_gcm(), 32, 12, 16); +} + +static int Laes_256_ctr_encrypt(lua_State *L) { + return Levp_encrypt(L, EVP_aes_256_ctr(), 32, 16, 0); +} + +/* decrypt(key, iv, ciphertext) */ +static int Levp_decrypt(lua_State *L, const EVP_CIPHER *cipher, const unsigned char expected_key_len, const unsigned char expected_iv_len, const size_t tag_len) { + EVP_CIPHER_CTX *ctx; + luaL_Buffer plaintext_buffer; + + size_t key_len, iv_len, ciphertext_len; + int plaintext_len, final_len; + + const unsigned char *key = (unsigned char*)luaL_checklstring(L, 1, &key_len); + const unsigned char *iv = (unsigned char*)luaL_checklstring(L, 2, &iv_len); + const unsigned char *ciphertext = (unsigned char*)luaL_checklstring(L, 3, &ciphertext_len); + + if(key_len != expected_key_len) { + return luaL_error(L, "key must be %d bytes", expected_key_len); + } + if(iv_len != expected_iv_len) { + return luaL_error(L, "iv must be %d bytes", expected_iv_len); + } + if(ciphertext_len <= tag_len) { + return luaL_error(L, "ciphertext must be at least %d bytes (including tag)", tag_len); + } + if(lua_gettop(L) > 3) { + return luaL_error(L, "Expected 3 arguments, got %d", lua_gettop(L)); + } + + /* Create and initialise the context */ + ctx = new_managed_EVP_CIPHER_CTX(L); + + /* Initialise the decryption operation. */ + if(!EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL)) { + return luaL_error(L, "Error while initializing decryption engine"); + } + + /* Initialise key and IV */ + if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) { + return luaL_error(L, "Error while initializing key/iv"); + } + + luaL_buffinit(L, &plaintext_buffer); + unsigned char *plaintext = (unsigned char*)luaL_prepbuffsize(&plaintext_buffer, ciphertext_len); + + /* + * Provide the message to be decrypted, and obtain the plaintext output. + * EVP_DecryptUpdate can be called multiple times if necessary + */ + if(!EVP_DecryptUpdate(ctx, plaintext, &plaintext_len, ciphertext, ciphertext_len-tag_len)) { + return luaL_error(L, "Error while decrypting data"); + } + + if(tag_len > 0) { + /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ + if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, (unsigned char*)ciphertext + (ciphertext_len-tag_len))) { + return luaL_error(L, "Error while processing authentication tag"); + } + } + + /* + * Finalise the decryption. A positive return value indicates success, + * anything else is a failure - the plaintext is not trustworthy. + */ + int ret = EVP_DecryptFinal_ex(ctx, plaintext + plaintext_len, &final_len); + + if(ret <= 0) { + /* Verify failed */ + lua_pushnil(L); + lua_pushliteral(L, "verify-failed"); + return 2; + } + + luaL_addsize(&plaintext_buffer, plaintext_len + final_len); + luaL_pushresult(&plaintext_buffer); + return 1; +} + +static int Laes_128_gcm_decrypt(lua_State *L) { + return Levp_decrypt(L, EVP_aes_128_gcm(), 16, 12, 16); +} + +static int Laes_256_gcm_decrypt(lua_State *L) { + return Levp_decrypt(L, EVP_aes_256_gcm(), 32, 12, 16); +} + +static int Laes_256_ctr_decrypt(lua_State *L) { + return Levp_decrypt(L, EVP_aes_256_ctr(), 32, 16, 0); +} + +/* r, s = parse_ecdsa_sig(sig_der) */ +static int Lparse_ecdsa_signature(lua_State *L) { + ECDSA_SIG *sig; + size_t sig_der_len; + const unsigned char *sig_der = (unsigned char*)luaL_checklstring(L, 1, &sig_der_len); + const size_t sig_int_bytes = luaL_checkinteger(L, 2); + const BIGNUM *r, *s; + int rlen, slen; + unsigned char rb[MAX_ECDSA_SIG_INT_BYTES]; + unsigned char sb[MAX_ECDSA_SIG_INT_BYTES]; + + if(sig_int_bytes > MAX_ECDSA_SIG_INT_BYTES) { + luaL_error(L, "requested signature size exceeds supported limit"); + } + + sig = d2i_ECDSA_SIG(NULL, &sig_der, sig_der_len); + + if(sig == NULL) { + lua_pushnil(L); + return 1; + } + + ECDSA_SIG_get0(sig, &r, &s); + + rlen = BN_bn2binpad(r, rb, sig_int_bytes); + slen = BN_bn2binpad(s, sb, sig_int_bytes); + + if (rlen == -1 || slen == -1) { + ECDSA_SIG_free(sig); + luaL_error(L, "encoded integers exceed requested size"); + } + + ECDSA_SIG_free(sig); + + lua_pushlstring(L, (const char*)rb, rlen); + lua_pushlstring(L, (const char*)sb, slen); + + return 2; +} + +/* sig_der = build_ecdsa_signature(r, s) */ +static int Lbuild_ecdsa_signature(lua_State *L) { + ECDSA_SIG *sig = ECDSA_SIG_new(); + BIGNUM *r, *s; + luaL_Buffer sigbuf; + + size_t rlen, slen; + const unsigned char *rbin, *sbin; + + rbin = (unsigned char*)luaL_checklstring(L, 1, &rlen); + sbin = (unsigned char*)luaL_checklstring(L, 2, &slen); + + r = BN_bin2bn(rbin, (int)rlen, NULL); + s = BN_bin2bn(sbin, (int)slen, NULL); + + ECDSA_SIG_set0(sig, r, s); + + luaL_buffinit(L, &sigbuf); + + /* DER structure of an ECDSA signature has 7 bytes plus the integers themselves, + which may gain an extra byte once encoded */ + unsigned char *buffer = (unsigned char*)luaL_prepbuffsize(&sigbuf, (rlen+1)+(slen+1)+7); + int len = i2d_ECDSA_SIG(sig, &buffer); + luaL_addsize(&sigbuf, len); + luaL_pushresult(&sigbuf); + + ECDSA_SIG_free(sig); + + return 1; +} + +#define REG_SIGN_VERIFY(algorithm, digest) \ + { #algorithm "_" #digest "_sign", L ## algorithm ## _ ## digest ## _sign },\ + { #algorithm "_" #digest "_verify", L ## algorithm ## _ ## digest ## _verify }, + +#define IMPL_SIGN_VERIFY(algorithm, key_type, digest) \ + static int L ## algorithm ## _ ## digest ## _sign(lua_State *L) { \ + return base_evp_sign(L, key_type, EVP_ ## digest()); \ + } \ + static int L ## algorithm ## _ ## digest ## _verify(lua_State *L) { \ + return base_evp_verify(L, key_type, EVP_ ## digest()); \ + } + +IMPL_SIGN_VERIFY(ecdsa, NID_X9_62_id_ecPublicKey, sha256) +IMPL_SIGN_VERIFY(ecdsa, NID_X9_62_id_ecPublicKey, sha384) +IMPL_SIGN_VERIFY(ecdsa, NID_X9_62_id_ecPublicKey, sha512) + +IMPL_SIGN_VERIFY(rsassa_pkcs1, NID_rsaEncryption, sha256) +IMPL_SIGN_VERIFY(rsassa_pkcs1, NID_rsaEncryption, sha384) +IMPL_SIGN_VERIFY(rsassa_pkcs1, NID_rsaEncryption, sha512) + +IMPL_SIGN_VERIFY(rsassa_pss, NID_rsassaPss, sha256) +IMPL_SIGN_VERIFY(rsassa_pss, NID_rsassaPss, sha384) +IMPL_SIGN_VERIFY(rsassa_pss, NID_rsassaPss, sha512) + +static const luaL_Reg Reg[] = { + { "ed25519_sign", Led25519_sign }, + { "ed25519_verify", Led25519_verify }, + + REG_SIGN_VERIFY(ecdsa, sha256) + REG_SIGN_VERIFY(ecdsa, sha384) + REG_SIGN_VERIFY(ecdsa, sha512) + + REG_SIGN_VERIFY(rsassa_pkcs1, sha256) + REG_SIGN_VERIFY(rsassa_pkcs1, sha384) + REG_SIGN_VERIFY(rsassa_pkcs1, sha512) + + REG_SIGN_VERIFY(rsassa_pss, sha256) + REG_SIGN_VERIFY(rsassa_pss, sha384) + REG_SIGN_VERIFY(rsassa_pss, sha512) + + { "aes_128_gcm_encrypt", Laes_128_gcm_encrypt }, + { "aes_128_gcm_decrypt", Laes_128_gcm_decrypt }, + { "aes_256_gcm_encrypt", Laes_256_gcm_encrypt }, + { "aes_256_gcm_decrypt", Laes_256_gcm_decrypt }, + + { "aes_256_ctr_encrypt", Laes_256_ctr_encrypt }, + { "aes_256_ctr_decrypt", Laes_256_ctr_decrypt }, + + { "generate_ed25519_keypair", Lgenerate_ed25519_keypair }, + { "generate_p256_keypair", Lgenerate_p256_keypair }, + + { "import_private_pem", Limport_private_pem }, + { "import_public_pem", Limport_public_pem }, + { "import_public_ec_raw", Limport_public_ec_raw }, + + { "parse_ecdsa_signature", Lparse_ecdsa_signature }, + { "build_ecdsa_signature", Lbuild_ecdsa_signature }, + { NULL, NULL } +}; + +static const luaL_Reg KeyMethods[] = { + { "private_pem", Lpkey_meth_private_pem }, + { "public_pem", Lpkey_meth_public_pem }, + { "public_raw", Lpkey_meth_public_raw }, + { "get_type", Lpkey_meth_get_type }, + { "derive", Lpkey_meth_derive }, + { NULL, NULL } +}; + +static const luaL_Reg KeyMetatable[] = { + { "__gc", Lpkey_finalizer }, + { NULL, NULL } +}; + +LUALIB_API int luaopen_prosody_util_crypto(lua_State *L) { +#if (LUA_VERSION_NUM > 501) + luaL_checkversion(L); +#endif + + /* Initialize pkey metatable */ + luaL_newmetatable(L, PKEY_MT_TAG); + luaL_setfuncs(L, KeyMetatable, 0); + lua_newtable(L); + luaL_setfuncs(L, KeyMethods, 0); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + /* Initialize lib table */ + lua_newtable(L); + luaL_setfuncs(L, Reg, 0); + lua_pushliteral(L, "-3.14"); + lua_setfield(L, -2, "version"); +#ifdef OPENSSL_VERSION + lua_pushstring(L, OpenSSL_version(OPENSSL_VERSION)); + lua_setfield(L, -2, "_LIBCRYPTO_VERSION"); +#endif + return 1; +} + +LUALIB_API int luaopen_util_crypto(lua_State *L) { + return luaopen_prosody_util_crypto(L); +} diff --git a/util-src/encodings.c b/util-src/encodings.c index 72264da8..ca2eb8b6 100644 --- a/util-src/encodings.c +++ b/util-src/encodings.c @@ -21,9 +21,6 @@ #include "lua.h" #include "lauxlib.h" -#if (LUA_VERSION_NUM == 501) -#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) -#endif #if (LUA_VERSION_NUM < 504) #define luaL_pushfail lua_pushnil #endif @@ -615,10 +612,8 @@ static const luaL_Reg Reg_idna[] = { /***************** end *****************/ -LUALIB_API int luaopen_util_encodings(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +LUALIB_API int luaopen_prosody_util_encodings(lua_State *L) { luaL_checkversion(L); -#endif #ifdef USE_STRINGPREP_ICU init_icu(); #endif @@ -651,3 +646,6 @@ LUALIB_API int luaopen_util_encodings(lua_State *L) { lua_setfield(L, -2, "version"); return 1; } +LUALIB_API int luaopen_util_encodings(lua_State *L) { + return luaopen_prosody_util_encodings(L); +} diff --git a/util-src/hashes.c b/util-src/hashes.c index 8eefcd6b..3c2b16a7 100644 --- a/util-src/hashes.c +++ b/util-src/hashes.c @@ -28,13 +28,16 @@ typedef unsigned __int32 uint32_t; #include <openssl/md5.h> #include <openssl/hmac.h> #include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/err.h> -#if (LUA_VERSION_NUM == 501) -#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) -#endif -#define HMAC_IPAD 0x36363636 -#define HMAC_OPAD 0x5c5c5c5c +/* Semi-arbitrary limit here. The actual theoretical limit +* is (255*(hash output octets)), but allocating 16KB on the +* stack when in practice we only ever request a few dozen +* bytes seems excessive. +*/ +#define MAX_HKDF_OUTPUT 256 static const char *hex_tab = "0123456789abcdef"; static void toHex(const unsigned char *in, int length, unsigned char *out) { @@ -46,94 +49,228 @@ static void toHex(const unsigned char *in, int length, unsigned char *out) { } } -#define MAKE_HASH_FUNCTION(myFunc, func, size) \ -static int myFunc(lua_State *L) { \ - size_t len; \ - const char *s = luaL_checklstring(L, 1, &len); \ - int hex_out = lua_toboolean(L, 2); \ - unsigned char hash[size], result[size*2]; \ - func((const unsigned char*)s, len, hash); \ - if (hex_out) { \ - toHex(hash, size, result); \ - lua_pushlstring(L, (char*)result, size*2); \ - } else { \ - lua_pushlstring(L, (char*)hash, size);\ - } \ - return 1; \ -} - -MAKE_HASH_FUNCTION(Lsha1, SHA1, SHA_DIGEST_LENGTH) -MAKE_HASH_FUNCTION(Lsha224, SHA224, SHA224_DIGEST_LENGTH) -MAKE_HASH_FUNCTION(Lsha256, SHA256, SHA256_DIGEST_LENGTH) -MAKE_HASH_FUNCTION(Lsha384, SHA384, SHA384_DIGEST_LENGTH) -MAKE_HASH_FUNCTION(Lsha512, SHA512, SHA512_DIGEST_LENGTH) -MAKE_HASH_FUNCTION(Lmd5, MD5, MD5_DIGEST_LENGTH) - -struct hash_desc { - int (*Init)(void *); - int (*Update)(void *, const void *, size_t); - int (*Final)(unsigned char *, void *); - size_t digestLength; - void *ctx, *ctxo; -}; +static int Levp_hash(lua_State *L, const EVP_MD *evp) { + size_t len; + unsigned int size = EVP_MAX_MD_SIZE; + const char *s = luaL_checklstring(L, 1, &len); + int hex_out = lua_toboolean(L, 2); -#define MAKE_HMAC_FUNCTION(myFunc, evp, size, type) \ -static int myFunc(lua_State *L) { \ - unsigned char hash[size], result[2*size]; \ - size_t key_len, msg_len; \ - unsigned int out_len; \ - const char *key = luaL_checklstring(L, 1, &key_len); \ - const char *msg = luaL_checklstring(L, 2, &msg_len); \ - const int hex_out = lua_toboolean(L, 3); \ - HMAC(evp(), key, key_len, (const unsigned char*)msg, msg_len, (unsigned char*)hash, &out_len); \ - if (hex_out) { \ - toHex(hash, out_len, result); \ - lua_pushlstring(L, (char*)result, out_len*2); \ - } else { \ - lua_pushlstring(L, (char*)hash, out_len); \ - } \ - return 1; \ -} - -MAKE_HMAC_FUNCTION(Lhmac_sha1, EVP_sha1, SHA_DIGEST_LENGTH, SHA_CTX) -MAKE_HMAC_FUNCTION(Lhmac_sha256, EVP_sha256, SHA256_DIGEST_LENGTH, SHA256_CTX) -MAKE_HMAC_FUNCTION(Lhmac_sha512, EVP_sha512, SHA512_DIGEST_LENGTH, SHA512_CTX) -MAKE_HMAC_FUNCTION(Lhmac_md5, EVP_md5, MD5_DIGEST_LENGTH, MD5_CTX) + unsigned char hash[EVP_MAX_MD_SIZE], result[EVP_MAX_MD_SIZE * 2]; -static int Lpbkdf2_sha1(lua_State *L) { - unsigned char out[SHA_DIGEST_LENGTH]; + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + + if(ctx == NULL) { + goto fail; + } + + if(!EVP_DigestInit_ex(ctx, evp, NULL)) { + goto fail; + } + + if(!EVP_DigestUpdate(ctx, s, len)) { + goto fail; + } + + if(!EVP_DigestFinal_ex(ctx, hash, &size)) { + goto fail; + } + + EVP_MD_CTX_free(ctx); + + if(hex_out) { + toHex(hash, size, result); + lua_pushlstring(L, (char *)result, size * 2); + } else { + lua_pushlstring(L, (char *)hash, size); + } + + return 1; + +fail: + EVP_MD_CTX_free(ctx); + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); +} + +static int Lsha1(lua_State *L) { + return Levp_hash(L, EVP_sha1()); +} + +static int Lsha224(lua_State *L) { + return Levp_hash(L, EVP_sha224()); +} + +static int Lsha256(lua_State *L) { + return Levp_hash(L, EVP_sha256()); +} + +static int Lsha384(lua_State *L) { + return Levp_hash(L, EVP_sha384()); +} + +static int Lsha512(lua_State *L) { + return Levp_hash(L, EVP_sha512()); +} + +static int Lmd5(lua_State *L) { + return Levp_hash(L, EVP_md5()); +} + +static int Lblake2s256(lua_State *L) { + return Levp_hash(L, EVP_blake2s256()); +} + +static int Lblake2b512(lua_State *L) { + return Levp_hash(L, EVP_blake2b512()); +} + +static int Lsha3_256(lua_State *L) { + return Levp_hash(L, EVP_sha3_256()); +} + +static int Lsha3_512(lua_State *L) { + return Levp_hash(L, EVP_sha3_512()); +} + +static int Levp_hmac(lua_State *L, const EVP_MD *evp) { + unsigned char hash[EVP_MAX_MD_SIZE], result[EVP_MAX_MD_SIZE * 2]; + size_t key_len, msg_len; + unsigned int out_len = EVP_MAX_MD_SIZE; + const char *key = luaL_checklstring(L, 1, &key_len); + const char *msg = luaL_checklstring(L, 2, &msg_len); + const int hex_out = lua_toboolean(L, 3); + + if(HMAC(evp, key, key_len, (const unsigned char*)msg, msg_len, (unsigned char*)hash, &out_len) == NULL) { + goto fail; + } + + if(hex_out) { + toHex(hash, out_len, result); + lua_pushlstring(L, (char *)result, out_len * 2); + } else { + lua_pushlstring(L, (char *)hash, out_len); + } + + return 1; + +fail: + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); +} + +static int Lhmac_sha1(lua_State *L) { + return Levp_hmac(L, EVP_sha1()); +} + +static int Lhmac_sha224(lua_State *L) { + return Levp_hmac(L, EVP_sha224()); +} + +static int Lhmac_sha256(lua_State *L) { + return Levp_hmac(L, EVP_sha256()); +} + +static int Lhmac_sha384(lua_State *L) { + return Levp_hmac(L, EVP_sha384()); +} + +static int Lhmac_sha512(lua_State *L) { + return Levp_hmac(L, EVP_sha512()); +} + +static int Lhmac_md5(lua_State *L) { + return Levp_hmac(L, EVP_md5()); +} + +static int Lhmac_sha3_256(lua_State *L) { + return Levp_hmac(L, EVP_sha3_256()); +} + +static int Lhmac_sha3_512(lua_State *L) { + return Levp_hmac(L, EVP_sha3_512()); +} + +static int Lhmac_blake2s256(lua_State *L) { + return Levp_hmac(L, EVP_blake2s256()); +} + +static int Lhmac_blake2b512(lua_State *L) { + return Levp_hmac(L, EVP_blake2b512()); +} + + +static int Levp_pbkdf2(lua_State *L, const EVP_MD *evp, size_t out_len) { + unsigned char out[EVP_MAX_MD_SIZE]; size_t pass_len, salt_len; const char *pass = luaL_checklstring(L, 1, &pass_len); const unsigned char *salt = (unsigned char *)luaL_checklstring(L, 2, &salt_len); const int iter = luaL_checkinteger(L, 3); - if(PKCS5_PBKDF2_HMAC(pass, pass_len, salt, salt_len, iter, EVP_sha1(), SHA_DIGEST_LENGTH, out) == 0) { - return luaL_error(L, "PKCS5_PBKDF2_HMAC() failed"); + if(PKCS5_PBKDF2_HMAC(pass, pass_len, salt, salt_len, iter, evp, out_len, out) == 0) { + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); } - lua_pushlstring(L, (char *)out, SHA_DIGEST_LENGTH); + lua_pushlstring(L, (char *)out, out_len); return 1; } +static int Lpbkdf2_sha1(lua_State *L) { + return Levp_pbkdf2(L, EVP_sha1(), SHA_DIGEST_LENGTH); +} static int Lpbkdf2_sha256(lua_State *L) { - unsigned char out[SHA256_DIGEST_LENGTH]; + return Levp_pbkdf2(L, EVP_sha256(), SHA256_DIGEST_LENGTH); +} - size_t pass_len, salt_len; - const char *pass = luaL_checklstring(L, 1, &pass_len); - const unsigned char *salt = (unsigned char *)luaL_checklstring(L, 2, &salt_len); - const int iter = luaL_checkinteger(L, 3); - if(PKCS5_PBKDF2_HMAC(pass, pass_len, salt, salt_len, iter, EVP_sha256(), SHA256_DIGEST_LENGTH, out) == 0) { - return luaL_error(L, "PKCS5_PBKDF2_HMAC() failed"); +/* HKDF(length, input, salt, info) */ +static int Levp_hkdf(lua_State *L, const EVP_MD *evp) { + unsigned char out[MAX_HKDF_OUTPUT]; + + size_t input_len, salt_len, info_len; + size_t actual_out_len = luaL_checkinteger(L, 1); + const unsigned char *input = (unsigned char *)luaL_checklstring(L, 2, &input_len); + const unsigned char *salt = (unsigned char *)luaL_optlstring(L, 3, NULL, &salt_len); + const unsigned char *info = (unsigned char *)luaL_checklstring(L, 4, &info_len); + + if(actual_out_len > MAX_HKDF_OUTPUT) + return luaL_error(L, "desired output length %ul exceeds internal limit %ul", actual_out_len, MAX_HKDF_OUTPUT); + + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + + if (EVP_PKEY_derive_init(pctx) <= 0) + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); + + if (EVP_PKEY_CTX_set_hkdf_md(pctx, evp) <= 0) + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); + + if(salt != NULL) { + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); } - lua_pushlstring(L, (char *)out, SHA256_DIGEST_LENGTH); + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, input, input_len) <= 0) + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); + + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); + + if (EVP_PKEY_derive(pctx, out, &actual_out_len) <= 0) + return luaL_error(L, ERR_error_string(ERR_get_error(), NULL)); + + lua_pushlstring(L, (char *)out, actual_out_len); + return 1; } +static int Lhkdf_sha256(lua_State *L) { + return Levp_hkdf(L, EVP_sha256()); +} + +static int Lhkdf_sha384(lua_State *L) { + return Levp_hkdf(L, EVP_sha384()); +} + static int Lhash_equals(lua_State *L) { size_t len1, len2; const char *s1 = luaL_checklstring(L, 1, &len1); @@ -153,21 +290,31 @@ static const luaL_Reg Reg[] = { { "sha384", Lsha384 }, { "sha512", Lsha512 }, { "md5", Lmd5 }, + { "sha3_256", Lsha3_256 }, + { "sha3_512", Lsha3_512 }, + { "blake2s256", Lblake2s256 }, + { "blake2b512", Lblake2b512 }, { "hmac_sha1", Lhmac_sha1 }, + { "hmac_sha224", Lhmac_sha224 }, { "hmac_sha256", Lhmac_sha256 }, + { "hmac_sha384", Lhmac_sha384 }, { "hmac_sha512", Lhmac_sha512 }, { "hmac_md5", Lhmac_md5 }, + { "hmac_sha3_256", Lhmac_sha3_256 }, + { "hmac_sha3_512", Lhmac_sha3_512 }, + { "hmac_blake2s256", Lhmac_blake2s256 }, + { "hmac_blake2b512", Lhmac_blake2b512 }, { "scram_Hi_sha1", Lpbkdf2_sha1 }, /* COMPAT */ { "pbkdf2_hmac_sha1", Lpbkdf2_sha1 }, { "pbkdf2_hmac_sha256", Lpbkdf2_sha256 }, + { "hkdf_hmac_sha256", Lhkdf_sha256 }, + { "hkdf_hmac_sha384", Lhkdf_sha384 }, { "equals", Lhash_equals }, { NULL, NULL } }; -LUALIB_API int luaopen_util_hashes(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +LUALIB_API int luaopen_prosody_util_hashes(lua_State *L) { luaL_checkversion(L); -#endif lua_newtable(L); luaL_setfuncs(L, Reg, 0); lua_pushliteral(L, "-3.14"); @@ -178,3 +325,6 @@ LUALIB_API int luaopen_util_hashes(lua_State *L) { #endif return 1; } +LUALIB_API int luaopen_util_hashes(lua_State *L) { + return luaopen_prosody_util_hashes(L); +} diff --git a/util-src/managed_pointer.h b/util-src/managed_pointer.h new file mode 100644 index 00000000..213b5fd7 --- /dev/null +++ b/util-src/managed_pointer.h @@ -0,0 +1,61 @@ +/* managed_pointer.h + +These macros allow wrapping an allocator/deallocator into an object that is +owned and managed by the Lua garbage collector. + +Why? It is too easy to leak objects that need to be manually released, especially +when dealing with the Lua API which can throw errors from many operations. + +USAGE +----- + +For example, given an object that can be created or released with the following +functions: + + fancy_buffer* new_buffer(); + void free_buffer(fancy_buffer* p_buffer) + +You could declare a managed version like so: + + MANAGED_POINTER_ALLOCATOR(new_managed_buffer, fancy_buffer*, new_buffer, free_buffer) + +And then, when you need to create a new fancy_buffer in your code: + + fancy_buffer *my_buffer = new_managed_buffer(L); + +NOTES +----- + +Managed objects MUST NOT be freed manually. They will automatically be +freed during the next GC sweep after your function exits (even if via an error). + +The managed object is pushed onto the stack, but should generally be ignored, +but you'll need to bear this in mind when creating managed pointers in the +middle of a sequence of stack operations. +*/ + +#define MANAGED_POINTER_MT(wrapped_type) #wrapped_type "_managedptr_mt" + +#define MANAGED_POINTER_ALLOCATOR(name, wrapped_type, wrapped_alloc, wrapped_free) \ + static int _release_ ## name(lua_State *L) { \ + wrapped_type *p = (wrapped_type*)lua_topointer(L, 1); \ + if(*p != NULL) { \ + wrapped_free(*p); \ + } \ + return 0; \ + } \ + static wrapped_type name(lua_State *L) { \ + wrapped_type *p = (wrapped_type*)lua_newuserdata(L, sizeof(wrapped_type)); \ + if(luaL_newmetatable(L, MANAGED_POINTER_MT(wrapped_type)) != 0) { \ + lua_pushcfunction(L, _release_ ## name); \ + lua_setfield(L, -2, "__gc"); \ + } \ + lua_setmetatable(L, -2); \ + *p = wrapped_alloc(); \ + if(*p == NULL) { \ + lua_pushliteral(L, "not enough memory"); \ + lua_error(L); \ + } \ + return *p; \ + } + diff --git a/util-src/net.c b/util-src/net.c index d786e885..1a84c882 100644 --- a/util-src/net.c +++ b/util-src/net.c @@ -30,9 +30,6 @@ #include <lua.h> #include <lauxlib.h> -#if (LUA_VERSION_NUM == 501) -#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) -#endif #if (LUA_VERSION_NUM < 504) #define luaL_pushfail lua_pushnil #endif @@ -192,10 +189,8 @@ static int lc_ntop(lua_State *L) { return 1; } -int luaopen_util_net(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +int luaopen_prosody_util_net(lua_State *L) { luaL_checkversion(L); -#endif luaL_Reg exports[] = { { "local_addresses", lc_local_addresses }, { "pton", lc_pton }, @@ -207,3 +202,6 @@ int luaopen_util_net(lua_State *L) { luaL_setfuncs(L, exports, 0); return 1; } +int luaopen_util_net(lua_State *L) { + return luaopen_prosody_util_net(L); +} diff --git a/util-src/poll.c b/util-src/poll.c index 81caa953..fc70bdb6 100644 --- a/util-src/poll.c +++ b/util-src/poll.c @@ -8,7 +8,6 @@ * */ -#include <unistd.h> #include <string.h> #include <errno.h> @@ -24,15 +23,18 @@ #endif #ifdef USE_EPOLL +#include <unistd.h> #include <sys/epoll.h> #ifndef MAX_EVENTS -#define MAX_EVENTS 64 +/* Maximum number of returned events, retrieved into Lpoll_state */ +#define MAX_EVENTS 256 #endif #endif #ifdef USE_POLL #include <poll.h> -#ifndef MAX_EVENTS -#define MAX_EVENTS 10000 +#ifndef MAX_WATCHED +/* Maximum number of watched sockets, kept in Lpoll_state */ +#define MAX_WATCHED 10000 #endif #endif #ifdef USE_SELECT @@ -44,9 +46,6 @@ #define STATE_MT "util.poll<" POLL_BACKEND ">" -#if (LUA_VERSION_NUM == 501) -#define luaL_setmetatable(L, tname) luaL_getmetatable(L, tname); lua_setmetatable(L, -2) -#endif #if (LUA_VERSION_NUM < 504) #define luaL_pushfail lua_pushnil #endif @@ -62,7 +61,7 @@ typedef struct Lpoll_state { #endif #ifdef USE_POLL nfds_t count; - struct pollfd events[MAX_EVENTS]; + struct pollfd events[MAX_WATCHED]; #endif #ifdef USE_SELECT fd_set wantread; @@ -123,7 +122,7 @@ static int Ladd(lua_State *L) { } } - if(state->count >= MAX_EVENTS) { + if(state->count >= MAX_WATCHED) { luaL_pushfail(L); lua_pushstring(L, strerror(EMFILE)); lua_pushinteger(L, EMFILE); @@ -414,6 +413,12 @@ static int Lwait(lua_State *L) { lua_Number timeout = luaL_checknumber(L, 2); luaL_argcheck(L, timeout >= 0, 1, "positive number expected"); + if(timeout == 0.0) { + lua_pushnil(L); + lua_pushstring(L, "timeout"); + return 2; + } + #ifdef USE_EPOLL ret = epoll_wait(state->epoll_fd, state->events, MAX_EVENTS, timeout * 1000); #endif @@ -540,7 +545,7 @@ static int Lnew(lua_State *L) { state->processed = -1; state->count = 0; - for(nfds_t i = 0; i < MAX_EVENTS; i++) { + for(nfds_t i = 0; i < MAX_WATCHED; i++) { state->events[i].fd = -1; state->events[i].events = 0; state->events[i].revents = 0; @@ -563,10 +568,8 @@ static int Lnew(lua_State *L) { /* * Open library */ -int luaopen_util_poll(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +int luaopen_prosody_util_poll(lua_State *L) { luaL_checkversion(L); -#endif luaL_newmetatable(L, STATE_MT); { @@ -619,3 +622,8 @@ int luaopen_util_poll(lua_State *L) { return 1; } +/* COMPAT */ +int luaopen_util_poll(lua_State *L) { + return luaopen_prosody_util_poll(L); +} + diff --git a/util-src/pposix.c b/util-src/pposix.c index a8e0720f..b2a8dbac 100644 --- a/util-src/pposix.c +++ b/util-src/pposix.c @@ -58,9 +58,6 @@ #include "lualib.h" #include "lauxlib.h" -#if (LUA_VERSION_NUM == 501) -#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) -#endif #if (LUA_VERSION_NUM < 503) #define lua_isinteger(L, n) lua_isnumber(L, n) #endif @@ -510,55 +507,43 @@ static int lc_mkdir(lua_State *L) { * Example usage: * pposix.setrlimit("NOFILE", 1000, 2000) */ -static int string2resource(const char *s) { - if(!strcmp(s, "CORE")) { - return RLIMIT_CORE; - } - - if(!strcmp(s, "CPU")) { - return RLIMIT_CPU; - } - - if(!strcmp(s, "DATA")) { - return RLIMIT_DATA; - } - - if(!strcmp(s, "FSIZE")) { - return RLIMIT_FSIZE; - } - - if(!strcmp(s, "NOFILE")) { - return RLIMIT_NOFILE; - } - - if(!strcmp(s, "STACK")) { - return RLIMIT_STACK; - } +static const char *const resource_strings[] = { + /* Defined by POSIX */ + "CORE", + "CPU", + "DATA", + "FSIZE", + "NOFILE", + "STACK", #if !(defined(sun) || defined(__sun) || defined(__APPLE__)) - - if(!strcmp(s, "MEMLOCK")) { - return RLIMIT_MEMLOCK; - } - - if(!strcmp(s, "NPROC")) { - return RLIMIT_NPROC; - } - - if(!strcmp(s, "RSS")) { - return RLIMIT_RSS; - } - + "MEMLOCK", + "NPROC", + "RSS", #endif #ifdef RLIMIT_NICE + "NICE", +#endif + NULL +}; - if(!strcmp(s, "NICE")) { - return RLIMIT_NICE; - } - +static int resource_constants[] = { + RLIMIT_CORE, + RLIMIT_CPU, + RLIMIT_DATA, + RLIMIT_FSIZE, + RLIMIT_NOFILE, + RLIMIT_STACK, +#if !(defined(sun) || defined(__sun) || defined(__APPLE__)) + RLIMIT_MEMLOCK, + RLIMIT_NPROC, + RLIMIT_RSS, #endif - return -1; -} +#ifdef RLIMIT_NICE + RLIMIT_NICE, +#endif + -1, +}; static rlim_t arg_to_rlimit(lua_State *L, int idx, rlim_t current) { switch(lua_type(L, idx)) { @@ -592,7 +577,7 @@ static int lc_setrlimit(lua_State *L) { return 2; } - rid = string2resource(luaL_checkstring(L, 1)); + rid = resource_constants[luaL_checkoption(L, 1,NULL, resource_strings)]; if(rid == -1) { lua_pushboolean(L, 0); @@ -622,7 +607,6 @@ static int lc_setrlimit(lua_State *L) { static int lc_getrlimit(lua_State *L) { int arguments = lua_gettop(L); - const char *resource = NULL; int rid = -1; struct rlimit lim; @@ -632,8 +616,7 @@ static int lc_getrlimit(lua_State *L) { return 2; } - resource = luaL_checkstring(L, 1); - rid = string2resource(resource); + rid = resource_constants[luaL_checkoption(L, 1, NULL, resource_strings)]; if(rid != -1) { if(getrlimit(rid, &lim)) { @@ -819,6 +802,41 @@ static int lc_atomic_append(lua_State *L) { return 3; } +static int lc_remove_blocks(lua_State *L) { +#if defined(__linux__) + int err; + + FILE *f = *(FILE **) luaL_checkudata(L, 1, LUA_FILEHANDLE); + off_t offset = (off_t)luaL_checkinteger(L, 2); + off_t length = (off_t)luaL_checkinteger(L, 3); + + errno = 0; + + if((err = fallocate(fileno(f), FALLOC_FL_COLLAPSE_RANGE, offset, length))) { + if(errno != 0) { + /* Some old versions of Linux apparently use the return value instead of errno */ + err = errno; + } + + switch(err) { + default: /* Other issues */ + luaL_pushfail(L); + lua_pushstring(L, strerror(err)); + lua_pushinteger(L, err); + return 3; + } + } + + lua_pushboolean(L, err == 0); + return 1; +#else + luaL_pushfail(L); + lua_pushstring(L, strerror(EOPNOTSUPP)); + lua_pushinteger(L, EOPNOTSUPP); + return 3; +#endif +} + static int lc_isatty(lua_State *L) { FILE *f = *(FILE **) luaL_checkudata(L, 1, LUA_FILEHANDLE); const int fd = fileno(f); @@ -828,10 +846,8 @@ static int lc_isatty(lua_State *L) { /* Register functions */ -int luaopen_util_pposix(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +int luaopen_prosody_util_pposix(lua_State *L) { luaL_checkversion(L); -#endif luaL_Reg exports[] = { { "abort", lc_abort }, @@ -866,6 +882,7 @@ int luaopen_util_pposix(lua_State *L) { #endif { "atomic_append", lc_atomic_append }, + { "remove_blocks", lc_remove_blocks }, { "isatty", lc_isatty }, @@ -888,3 +905,6 @@ int luaopen_util_pposix(lua_State *L) { return 1; } +int luaopen_util_pposix(lua_State *L) { + return luaopen_prosody_util_pposix(L); +} diff --git a/util-src/ringbuffer.c b/util-src/ringbuffer.c index 0f250c12..43846142 100644 --- a/util-src/ringbuffer.c +++ b/util-src/ringbuffer.c @@ -313,10 +313,8 @@ static int rb_new(lua_State *L) { return 1; } -int luaopen_util_ringbuffer(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +int luaopen_prosody_util_ringbuffer(lua_State *L) { luaL_checkversion(L); -#endif if(luaL_newmetatable(L, "ringbuffer_mt")) { lua_pushcfunction(L, rb_tostring); @@ -355,3 +353,7 @@ int luaopen_util_ringbuffer(lua_State *L) { lua_setfield(L, -2, "new"); return 1; } + +int luaopen_util_ringbuffer(lua_State *L) { + return luaopen_prosody_util_ringbuffer(L); +} diff --git a/util-src/signal.c b/util-src/signal.c index 1a398fa0..76d25d6f 100644 --- a/util-src/signal.c +++ b/util-src/signal.c @@ -32,13 +32,14 @@ #include <signal.h> #include <stdlib.h> +#ifdef __linux__ +#include <unistd.h> +#include <sys/signalfd.h> +#endif #include "lua.h" #include "lauxlib.h" -#if (LUA_VERSION_NUM == 501) -#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) -#endif #if (LUA_VERSION_NUM < 503) #define lua_isinteger(L, n) lua_isnumber(L, n) #endif @@ -371,21 +372,105 @@ static int l_kill(lua_State *L) { #endif +#ifdef __linux__ +struct lsignalfd { + int fd; + sigset_t mask; +}; + +static int l_signalfd(lua_State *L) { + struct lsignalfd *sfd = lua_newuserdata(L, sizeof(struct lsignalfd)); + + sigemptyset(&sfd->mask); + sigaddset(&sfd->mask, luaL_checkinteger(L, 1)); + + if (sigprocmask(SIG_BLOCK, &sfd->mask, NULL) != 0) { + lua_pushnil(L); + return 1; + }; + + sfd->fd = signalfd(-1, &sfd->mask, SFD_NONBLOCK); + + if(sfd->fd == -1) { + lua_pushnil(L); + return 1; + } + + luaL_setmetatable(L, "signalfd"); + return 1; +} + +static int l_signalfd_getfd(lua_State *L) { + struct lsignalfd *sfd = luaL_checkudata(L, 1, "signalfd"); + + if (sfd->fd == -1) { + lua_pushnil(L); + return 1; + } + + lua_pushinteger(L, sfd->fd); + return 1; +} + +static int l_signalfd_read(lua_State *L) { + struct lsignalfd *sfd = luaL_checkudata(L, 1, "signalfd"); + struct signalfd_siginfo siginfo; + + if(read(sfd->fd, &siginfo, sizeof(siginfo)) < 0) { + return 0; + } + + lua_pushinteger(L, siginfo.ssi_signo); + return 1; +} + +static int l_signalfd_close(lua_State *L) { + struct lsignalfd *sfd = luaL_checkudata(L, 1, "signalfd"); + + if(close(sfd->fd) != 0) { + lua_pushboolean(L, 0); + return 1; + } + + sfd->fd = -1; + lua_pushboolean(L, 1); + return 1; +} +#endif + static const struct luaL_Reg lsignal_lib[] = { {"signal", l_signal}, {"raise", l_raise}, #if defined(__unix__) || defined(__APPLE__) {"kill", l_kill}, #endif +#ifdef __linux__ + {"signalfd", l_signalfd}, +#endif {NULL, NULL} }; -int luaopen_util_signal(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +int luaopen_prosody_util_signal(lua_State *L) { luaL_checkversion(L); -#endif int i = 0; +#ifdef __linux__ + luaL_newmetatable(L, "signalfd"); + lua_pushcfunction(L, l_signalfd_close); + lua_setfield(L, -2, "__gc"); + lua_createtable(L, 0, 1); + { + lua_pushcfunction(L, l_signalfd_getfd); + lua_setfield(L, -2, "getfd"); + lua_pushcfunction(L, l_signalfd_read); + lua_setfield(L, -2, "read"); + lua_pushcfunction(L, l_signalfd_close); + lua_setfield(L, -2, "close"); + } + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +#endif + /* add the library */ lua_newtable(L); luaL_setfuncs(L, lsignal_lib, 0); @@ -413,3 +498,6 @@ int luaopen_util_signal(lua_State *L) { return 1; } +int luaopen_util_signal(lua_State *L) { + return luaopen_prosody_util_signal(L); +} diff --git a/util-src/strbitop.c b/util-src/strbitop.c index 89fce661..2f6bf6e6 100644 --- a/util-src/strbitop.c +++ b/util-src/strbitop.c @@ -8,13 +8,12 @@ #include <lua.h> #include <lauxlib.h> -#if (LUA_VERSION_NUM == 501) -#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) -#endif +#include <sys/param.h> +#include <limits.h> /* TODO Deduplicate code somehow */ -int strop_and(lua_State *L) { +static int strop_and(lua_State *L) { luaL_Buffer buf; size_t a, b, i; const char *str_a = luaL_checklstring(L, 1, &a); @@ -35,7 +34,7 @@ int strop_and(lua_State *L) { return 1; } -int strop_or(lua_State *L) { +static int strop_or(lua_State *L) { luaL_Buffer buf; size_t a, b, i; const char *str_a = luaL_checklstring(L, 1, &a); @@ -56,7 +55,7 @@ int strop_or(lua_State *L) { return 1; } -int strop_xor(lua_State *L) { +static int strop_xor(lua_State *L) { luaL_Buffer buf; size_t a, b, i; const char *str_a = luaL_checklstring(L, 1, &a); @@ -77,11 +76,46 @@ int strop_xor(lua_State *L) { return 1; } -LUA_API int luaopen_util_strbitop(lua_State *L) { +unsigned int clz(unsigned char c) { +#if __GNUC__ + return __builtin_clz((unsigned int) c) - ((sizeof(int)-1)*CHAR_BIT); +#else + if(c & 0x80) return 0; + if(c & 0x40) return 1; + if(c & 0x20) return 2; + if(c & 0x10) return 3; + if(c & 0x08) return 4; + if(c & 0x04) return 5; + if(c & 0x02) return 6; + if(c & 0x01) return 7; + return 8; +#endif +} + +LUA_API int strop_common_prefix_bits(lua_State *L) { + size_t a, b, i; + const char *str_a = luaL_checklstring(L, 1, &a); + const char *str_b = luaL_checklstring(L, 2, &b); + + size_t min_len = MIN(a, b); + + for(i=0; i<min_len; i++) { + if(str_a[i] != str_b[i]) { + lua_pushinteger(L, i*8 + (clz(str_a[i] ^ str_b[i]))); + return 1; + } + } + + lua_pushinteger(L, i*8); + return 1; +} + +LUA_API int luaopen_prosody_util_strbitop(lua_State *L) { luaL_Reg exports[] = { { "sand", strop_and }, { "sor", strop_or }, { "sxor", strop_xor }, + { "common_prefix_bits", strop_common_prefix_bits }, { NULL, NULL } }; @@ -89,3 +123,7 @@ LUA_API int luaopen_util_strbitop(lua_State *L) { luaL_setfuncs(L, exports, 0); return 1; } + +LUA_API int luaopen_util_strbitop(lua_State *L) { + return luaopen_prosody_util_strbitop(L); +} diff --git a/util-src/struct.c b/util-src/struct.c index e80df4e6..a779f53e 100644 --- a/util-src/struct.c +++ b/util-src/struct.c @@ -36,12 +36,6 @@ #include "lauxlib.h" -#if (LUA_VERSION_NUM >= 502) - -#define luaL_register(L,n,f) luaL_newlib(L,f) - -#endif - /* basic integer type */ #if !defined(STRUCT_INT) @@ -140,7 +134,7 @@ static int gettoalign (size_t len, Header *h, int opt, size_t size) { /* -** options to control endianess and alignment +** options to control endianness and alignment */ static void controloptions (lua_State *L, int opt, const char **fmt, Header *h) { @@ -391,11 +385,15 @@ static const struct luaL_Reg thislib[] = { LUALIB_API int luaopen_util_struct (lua_State *L); -LUALIB_API int luaopen_util_struct (lua_State *L) { - luaL_register(L, "struct", thislib); +LUALIB_API int luaopen_prosody_util_struct (lua_State *L) { + luaL_newlib(L, thislib); return 1; } +LUALIB_API int luaopen_util_struct (lua_State *L) { + return luaopen_prosody_util_struct(L); +} + /****************************************************************************** * Copyright (C) 2010-2018 Lua.org, PUC-Rio. All rights reserved. diff --git a/util-src/table.c b/util-src/table.c index 9a9553fc..7e01c072 100644 --- a/util-src/table.c +++ b/util-src/table.c @@ -1,11 +1,17 @@ #include <lua.h> #include <lauxlib.h> +#ifndef LUA_MAXINTEGER +#include <stdint.h> +#define LUA_MAXINTEGER PTRDIFF_MAX +#endif + static int Lcreate_table(lua_State *L) { lua_createtable(L, luaL_checkinteger(L, 1), luaL_checkinteger(L, 2)); return 1; } +/* COMPAT: w/ Lua pre-5.2 */ static int Lpack(lua_State *L) { unsigned int n_args = lua_gettop(L); lua_createtable(L, n_args, 1); @@ -20,14 +26,52 @@ static int Lpack(lua_State *L) { return 1; } -int luaopen_util_table(lua_State *L) { -#if (LUA_VERSION_NUM > 501) +/* COMPAT: w/ Lua pre-5.4 */ +static int Lmove (lua_State *L) { + lua_Integer f = luaL_checkinteger(L, 2); + lua_Integer e = luaL_checkinteger(L, 3); + lua_Integer t = luaL_checkinteger(L, 4); + + int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, tt, LUA_TTABLE); + + if (e >= f) { /* otherwise, nothing to move */ + lua_Integer n, i; + luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, + "too many elements to move"); + n = e - f + 1; /* number of elements to move */ + luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, + "destination wrap around"); + if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { + for (i = 0; i < n; i++) { + lua_rawgeti(L, 1, f + i); + lua_rawseti(L, tt, t + i); + } + } else { + for (i = n - 1; i >= 0; i--) { + lua_rawgeti(L, 1, f + i); + lua_rawseti(L, tt, t + i); + } + } + } + + lua_pushvalue(L, tt); /* return destination table */ + return 1; +} + +int luaopen_prosody_util_table(lua_State *L) { luaL_checkversion(L); -#endif lua_createtable(L, 0, 2); lua_pushcfunction(L, Lcreate_table); lua_setfield(L, -2, "create"); lua_pushcfunction(L, Lpack); lua_setfield(L, -2, "pack"); + lua_pushcfunction(L, Lmove); + lua_setfield(L, -2, "move"); return 1; } + +int luaopen_util_table(lua_State *L) { + return luaopen_prosody_util_table(L); +} diff --git a/util-src/time.c b/util-src/time.c index afef3df5..e5e72a09 100644 --- a/util-src/time.c +++ b/util-src/time.c @@ -23,7 +23,7 @@ static int lc_time_monotonic(lua_State *L) { return 1; } -int luaopen_util_time(lua_State *L) { +int luaopen_prosody_util_time(lua_State *L) { lua_createtable(L, 0, 2); { lua_pushcfunction(L, lc_time_realtime); @@ -33,3 +33,6 @@ int luaopen_util_time(lua_State *L) { } return 1; } +int luaopen_util_time(lua_State *L) { + return luaopen_prosody_util_time(L); +} diff --git a/util-src/windows.c b/util-src/windows.c index 57af79d5..2adb85f5 100644 --- a/util-src/windows.c +++ b/util-src/windows.c @@ -19,9 +19,6 @@ #include "lua.h" #include "lauxlib.h" -#if (LUA_VERSION_NUM == 501) -#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) -#endif #if (LUA_VERSION_NUM < 504) #define luaL_pushfail lua_pushnil #endif @@ -106,9 +103,7 @@ static const luaL_Reg Reg[] = { }; LUALIB_API int luaopen_util_windows(lua_State *L) { -#if (LUA_VERSION_NUM > 501) luaL_checkversion(L); -#endif lua_newtable(L); luaL_setfuncs(L, Reg, 0); lua_pushliteral(L, "-3.14"); |