aboutsummaryrefslogtreecommitdiffstats
path: root/util-src
diff options
context:
space:
mode:
Diffstat (limited to 'util-src')
-rw-r--r--util-src/GNUmakefile4
-rw-r--r--util-src/compat.c6
-rw-r--r--util-src/crand.c11
-rw-r--r--util-src/crypto.c622
-rw-r--r--util-src/encodings.c10
-rw-r--r--util-src/hashes.c296
-rw-r--r--util-src/managed_pointer.h61
-rw-r--r--util-src/net.c10
-rw-r--r--util-src/poll.c34
-rw-r--r--util-src/pposix.c126
-rw-r--r--util-src/ringbuffer.c8
-rw-r--r--util-src/signal.c100
-rw-r--r--util-src/strbitop.c52
-rw-r--r--util-src/struct.c16
-rw-r--r--util-src/table.c50
-rw-r--r--util-src/time.c5
-rw-r--r--util-src/windows.c5
17 files changed, 1223 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..1e69599d
--- /dev/null
+++ b/util-src/crypto.c
@@ -0,0 +1,622 @@
+/* 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/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 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_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 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_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 },
+
+ { "import_private_pem", Limport_private_pem },
+ { "import_public_pem", Limport_public_pem },
+
+ { "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 },
+ { "get_type", Lpkey_meth_get_type },
+ { 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");