aboutsummaryrefslogtreecommitdiffstats
path: root/src/openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openssl.c')
-rw-r--r--src/openssl.c11249
1 files changed, 11249 insertions, 0 deletions
diff --git a/src/openssl.c b/src/openssl.c
new file mode 100644
index 0000000..5108713
--- /dev/null
+++ b/src/openssl.c
@@ -0,0 +1,11249 @@
+/* ==========================================================================
+ * openssl.c - Lua OpenSSL
+ * --------------------------------------------------------------------------
+ * Copyright (c)
+ * 2012-2017 William Ahern <william@25thandClement.com>
+ * 2015-2018 Daurnimator <quae@daurnimator.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <limits.h> /* INT_MAX INT_MIN LLONG_MAX LLONG_MIN UCHAR_MAX ULLONG_MAX */
+#include <stdint.h> /* uintptr_t */
+#include <string.h> /* memset(3) strerror_r(3) */
+#include <math.h> /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */
+#include <time.h> /* struct tm time_t strptime(3) time(2) */
+#include <ctype.h> /* isdigit(3), isxdigit(3), tolower(3) */
+#include <errno.h> /* ENOMEM ENOTSUP EOVERFLOW errno */
+#include <assert.h> /* assert */
+
+#include <sys/stat.h> /* struct stat stat(2) */
+#ifdef _WIN32
+#include <winsock2.h> /* AF_INET, AF_INET6 */
+#include <inaddr.h> /* struct in_addr, struct in6_addr */
+#include <ws2tcpip.h> /* inet_pton */
+#include <wincrypt.h> /* CryptAcquireContext(), CryptGenRandom(), CryptReleaseContext() */
+#include <windows.h> /* CreateMutex(), GetLastError(), GetModuleHandleEx(), GetProcessTimes(), InterlockedCompareExchangePointer() */
+#define EXPORT __declspec (dllexport)
+#else
+#include <arpa/inet.h> /* inet_pton(3) */
+#include <dlfcn.h> /* dladdr(3) dlopen(3) */
+#include <fcntl.h> /* O_RDONLY O_CLOEXEC open(2) */
+#include <netinet/in.h> /* struct in_addr struct in6_addr */
+#include <pthread.h> /* pthread_mutex_init(3) pthread_mutex_lock(3) pthread_mutex_unlock(3) */
+#include <sys/resource.h> /* RUSAGE_SELF struct rusage getrusage(2) */
+#include <sys/socket.h> /* AF_INET AF_INET6 */
+#include <sys/time.h> /* struct timeval gettimeofday(2) */
+#include <sys/types.h> /* ssize_t pid_t */
+#include <sys/utsname.h> /* struct utsname uname(3) */
+#include <unistd.h> /* close(2) getpid(2) */
+#define EXPORT
+#endif
+
+#if __APPLE__
+#include <mach/mach_time.h> /* mach_absolute_time() */
+#endif
+
+#include <openssl/opensslconf.h>
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/asn1.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
+#include <openssl/pkcs12.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/des.h>
+#include <openssl/ocsp.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#if LUA_VERSION_NUM < 503
+#include "../vendor/compat53/c-api/compat-5.3.h"
+#endif
+
+#define GNUC_2VER(M, m, p) (((M) * 10000) + ((m) * 100) + (p))
+#define GNUC_PREREQ(M, m, p) (__GNUC__ > 0 && GNUC_2VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) >= GNUC_2VER((M), (m), (p)))
+
+#define MSC_2VER(M, m, p) ((((M) + 6) * 10000000) + ((m) * 1000000) + (p))
+#define MSC_PREREQ(M, m, p) (_MSC_FULL_VER > 0 && _MSC_FULL_VER >= MSC_2VER((M), (m), (p)))
+
+#ifdef LIBRESSL_VERSION_NUMBER
+#define OPENSSL_PREREQ(M, m, p) (0)
+#define LIBRESSL_PREREQ(M, m, p) \
+ (LIBRESSL_VERSION_NUMBER >= (((M) << 28) | ((m) << 20) | ((p) << 12)))
+#else
+#define OPENSSL_PREREQ(M, m, p) \
+ (OPENSSL_VERSION_NUMBER >= (((M) << 28) | ((m) << 20) | ((p) << 12)))
+#define LIBRESSL_PREREQ(M, m, p) (0)
+#endif
+
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_extension
+#define __has_extension(x) 0
+#endif
+
+#ifndef HAVE_C___ASSUME
+#define HAVE_C___ASSUME MSC_PREREQ(8,0,0)
+#endif
+
+#ifndef HAVE_C___BUILTIN_UNREACHABLE
+#define HAVE_C___BUILTIN_UNREACHABLE (GNUC_PREREQ(4,5,0) || __has_builtin(__builtin_unreachable))
+#endif
+
+#ifndef HAVE_C___DECLSPEC_NORETURN
+#define HAVE_C___DECLSPEC_NORETURN MSC_PREREQ(8,0,0)
+#endif
+
+#ifndef HAVE_ASN1_STRING_GET0_DATA
+#define HAVE_ASN1_STRING_GET0_DATA (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DH_GET0_KEY
+#define HAVE_DH_GET0_KEY (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DH_GET0_PQG
+#define HAVE_DH_GET0_PQG (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DH_SET0_KEY
+#define HAVE_DH_SET0_KEY (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DH_SET0_PQG
+#define HAVE_DH_SET0_PQG (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DSA_GET0_KEY
+#define HAVE_DSA_GET0_KEY (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DSA_GET0_PQG
+#define HAVE_DSA_GET0_PQG (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DSA_SET0_KEY
+#define HAVE_DSA_SET0_KEY (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DSA_SET0_PQG
+#define HAVE_DSA_SET0_PQG (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_DTLSV1_CLIENT_METHOD
+#ifdef OPENSSL_NO_DTLS1
+#define HAVE_DTLSV1_CLIENT_METHOD (0)
+#else
+#define HAVE_DTLSV1_CLIENT_METHOD (1)
+#endif
+#endif
+
+#ifndef HAVE_DTLSV1_SERVER_METHOD
+#define HAVE_DTLSV1_SERVER_METHOD HAVE_DTLSV1_CLIENT_METHOD
+#endif
+
+#ifndef HAVE_DTLS_CLIENT_METHOD
+#ifdef OPENSSL_NO_DTLS1
+#define HAVE_DTLS_CLIENT_METHOD (0)
+#else
+#define HAVE_DTLS_CLIENT_METHOD OPENSSL_PREREQ(1,0,2)
+#endif
+#endif
+
+#ifndef HAVE_DTLS_SERVER_METHOD
+#define HAVE_DTLS_SERVER_METHOD HAVE_DTLS_CLIENT_METHOD
+#endif
+
+#ifndef HAVE_DTLSV1_2_CLIENT_METHOD
+#ifdef OPENSSL_NO_DTLS1
+#define HAVE_DTLSV1_2_CLIENT_METHOD (0)
+#else
+#define HAVE_DTLSV1_2_CLIENT_METHOD OPENSSL_PREREQ(1,0,2)
+#endif
+#endif
+
+#ifndef HAVE_DTLSV1_2_SERVER_METHOD
+#define HAVE_DTLSV1_2_SERVER_METHOD HAVE_DTLSV1_2_CLIENT_METHOD
+#endif
+
+#ifndef HAVE_EVP_CIPHER_CTX_FREE
+#define HAVE_EVP_CIPHER_CTX_FREE (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_EVP_CIPHER_CTX_NEW
+#define HAVE_EVP_CIPHER_CTX_NEW (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_EVP_MD_CTX_FREE
+#define HAVE_EVP_MD_CTX_FREE (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_EVP_MD_CTX_NEW
+#define HAVE_EVP_MD_CTX_NEW (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_EVP_PKEY_GET_DEFAULT_DIGEST_NID
+#define HAVE_EVP_PKEY_GET_DEFAULT_DIGEST_NID (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_EVP_PKEY_BASE_ID
+#define HAVE_EVP_PKEY_BASE_ID (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_EVP_PKEY_CTX_NEW
+#define HAVE_EVP_PKEY_CTX_NEW (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_EVP_PKEY_GET0
+#define HAVE_EVP_PKEY_GET0 (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_EVP_PKEY_ID
+#define HAVE_EVP_PKEY_ID (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_HMAC_CTX_FREE
+#define HAVE_HMAC_CTX_FREE (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_HMAC_CTX_NEW
+#define HAVE_HMAC_CTX_NEW (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_I2D_RE_X509_REQ_TBS
+#define HAVE_I2D_RE_X509_REQ_TBS OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_RSA_GET0_CRT_PARAMS
+#define HAVE_RSA_GET0_CRT_PARAMS (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_RSA_GET0_FACTORS
+#define HAVE_RSA_GET0_FACTORS (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_RSA_GET0_KEY
+#define HAVE_RSA_GET0_KEY (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_RSA_SET0_CRT_PARAMS
+#define HAVE_RSA_SET0_CRT_PARAMS (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_RSA_SET0_FACTORS
+#define HAVE_RSA_SET0_FACTORS (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_RSA_SET0_KEY
+#define HAVE_RSA_SET0_KEY (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_SSL_CLIENT_VERSION
+#define HAVE_SSL_CLIENT_VERSION OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_SSL_CTX_GET0_PARAM
+#define HAVE_SSL_CTX_GET0_PARAM (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_SSL_CTX_GET0_CERTIFICATE
+#define HAVE_SSL_CTX_GET0_CERTIFICATE (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_SSL_CTX_SET_CURVES_LIST
+#define HAVE_SSL_CTX_SET_CURVES_LIST (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,5,1))
+#endif
+
+#ifndef HAVE_SSL_CTX_SET_ECDH_AUTO
+#define HAVE_SSL_CTX_SET_ECDH_AUTO ((OPENSSL_PREREQ(1,0,2) && !OPENSSL_PREREQ(1,1,0)) || LIBRESSL_PREREQ(2,1,2))
+#endif
+
+#ifndef HAVE_SSL_CTX_SET_ALPN_PROTOS
+#define HAVE_SSL_CTX_SET_ALPN_PROTOS (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,1,3))
+#endif
+
+#ifndef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
+#define HAVE_SSL_CTX_SET_ALPN_SELECT_CB HAVE_SSL_CTX_SET_ALPN_PROTOS
+#endif
+
+#ifndef HAVE_SSL_CTX_SET_TLSEXT_SERVERNAME_CALLBACK
+#define HAVE_SSL_CTX_SET_TLSEXT_SERVERNAME_CALLBACK (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_SSL_CTX_SET1_CERT_STORE
+#define HAVE_SSL_CTX_SET1_CERT_STORE (HAVE_SSL_CTX_set1_cert_store || OPENSSL_PREREQ(1,1,1)) /* backwards compatible with old macro name */
+#endif
+
+#ifndef HAVE_SSL_CTX_SET1_PARAM
+#define HAVE_SSL_CTX_SET1_PARAM (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,1,0))
+#endif
+
+#ifndef HAVE_SSL_CTX_CERT_STORE
+#define HAVE_SSL_CTX_CERT_STORE (!OPENSSL_PREREQ(1,1,0))
+#endif
+
+#ifndef HAVE_SSL_CTX_SET_TLSEXT_STATUS_TYPE
+#define HAVE_SSL_CTX_SET_TLSEXT_STATUS_TYPE OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_SSL_CTX_GET_TLSEXT_STATUS_TYPE
+#define HAVE_SSL_CTX_GET_TLSEXT_STATUS_TYPE OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_SSL_GET0_ALPN_SELECTED
+#define HAVE_SSL_GET0_ALPN_SELECTED HAVE_SSL_CTX_SET_ALPN_PROTOS
+#endif
+
+#ifndef HAVE_SSL_GET0_PARAM
+#define HAVE_SSL_GET0_PARAM (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_SSL_SET_ALPN_PROTOS
+#define HAVE_SSL_SET_ALPN_PROTOS HAVE_SSL_CTX_SET_ALPN_PROTOS
+#endif
+
+#ifndef HAVE_SSL_SET1_CHAIN_CERT_STORE
+#define HAVE_SSL_SET1_CHAIN_CERT_STORE OPENSSL_PREREQ(1,0,2)
+#endif
+
+#ifndef HAVE_SSL_SET1_VERIFY_CERT_STORE
+#define HAVE_SSL_SET1_VERIFY_CERT_STORE OPENSSL_PREREQ(1,0,2)
+#endif
+
+#ifndef HAVE_SSL_SET_CURVES_LIST
+#define HAVE_SSL_SET_CURVES_LIST (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,5,1))
+#endif
+
+#ifndef HAVE_SSL_SET1_PARAM
+#define HAVE_SSL_SET1_PARAM (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,5,1))
+#endif
+
+#ifndef HAVE_SSL_GET_CLIENT_RANDOM
+#define HAVE_SSL_GET_CLIENT_RANDOM (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_SSL_GET_SERVER_TMP_KEY
+#define HAVE_SSL_GET_SERVER_TMP_KEY OPENSSL_PREREQ(1,0,2)
+#endif
+
+#ifndef HAVE_SSL_GET_TLSEXT_STATUS_TYPE
+#define HAVE_SSL_GET_TLSEXT_STATUS_TYPE OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_SSL_UP_REF
+#define HAVE_SSL_UP_REF (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_SSL_OP_NO_SSL_MASK
+#ifdef SSL_OP_NO_SSL_MASK
+#define HAVE_SSL_OP_NO_SSL_MASK (1)
+#else
+#define HAVE_SSL_OP_NO_SSL_MASK (0)
+#endif
+#endif
+
+#ifndef HAVE_SSL_OP_NO_DTLS_MASK
+#ifdef SSL_OP_NO_DTLS_MASK
+#define HAVE_SSL_OP_NO_DTLS_MASK (1)
+#else
+#define HAVE_SSL_OP_NO_DTLS_MASK (0)
+#endif
+#endif
+
+#ifndef HAVE_SSL_SESSION_MASTER_KEY
+#define HAVE_SSL_SESSION_MASTER_KEY (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_STACK_OPENSSL_STRING_FUNCS
+#define HAVE_STACK_OPENSSL_STRING_FUNCS (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_X509_CRL_GET0_LASTUPDATE
+#define HAVE_X509_CRL_GET0_LASTUPDATE (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_CRL_GET0_NEXTUPDATE
+#define HAVE_X509_CRL_GET0_NEXTUPDATE (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_CRL_GET0_BY_CERT
+#define HAVE_X509_CRL_GET0_BY_CERT (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_X509_CRL_GET0_BY_SERIAL
+#define HAVE_X509_CRL_GET0_BY_SERIAL (OPENSSL_PREREQ(1,0,0) || LIBRESSL_PREREQ(2,0,0))
+#endif
+
+#ifndef HAVE_X509_CRL_SET1_LASTUPDATE
+#define HAVE_X509_CRL_SET1_LASTUPDATE (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_CRL_SET1_NEXTUPDATE
+#define HAVE_X509_CRL_SET1_NEXTUPDATE (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_GET_SIGNATURE_NID
+#define HAVE_X509_GET_SIGNATURE_NID (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_STORE_REFERENCES
+#define HAVE_X509_STORE_REFERENCES (!OPENSSL_PREREQ(1,1,0))
+#endif
+
+#ifndef HAVE_X509_STORE_UP_REF
+#define HAVE_X509_STORE_UP_REF (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_UP_REF
+#define HAVE_X509_UP_REF (OPENSSL_PREREQ(1,1,0) || LIBRESSL_PREREQ(2,5,1))
+#endif
+
+#ifndef HAVE_X509_VERIFY_PARAM_ADD1_HOST
+#define HAVE_X509_VERIFY_PARAM_ADD1_HOST (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_VERIFY_PARAM_SET_AUTH_LEVEL
+#define HAVE_X509_VERIFY_PARAM_SET_AUTH_LEVEL OPENSSL_PREREQ(1,1,0)
+#endif
+
+#ifndef HAVE_X509_VERIFY_PARAM_SET1_EMAIL
+#define HAVE_X509_VERIFY_PARAM_SET1_EMAIL (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_VERIFY_PARAM_SET1_HOST
+#define HAVE_X509_VERIFY_PARAM_SET1_HOST (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HAVE_X509_VERIFY_PARAM_SET1_IP_ASC
+#define HAVE_X509_VERIFY_PARAM_SET1_IP_ASC (OPENSSL_PREREQ(1,0,2) || LIBRESSL_PREREQ(2,7,0))
+#endif
+
+#ifndef HMAC_INIT_EX_INT
+#define HMAC_INIT_EX_INT OPENSSL_PREREQ(1,0,0)
+#endif
+
+#ifndef STRERROR_R_CHAR_P
+#ifdef __GLIBC__
+#define STRERROR_R_CHAR_P (_GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600))
+#else
+#define STRERROR_R_CHAR_P (0)
+#endif
+#endif
+
+#ifndef LIST_HEAD
+#define LIST_HEAD(name, type) struct name { struct type *lh_first; }
+#define LIST_ENTRY(type) struct { struct type *le_next, **le_prev; }
+#define LIST_INIT(head) do { LIST_FIRST((head)) = NULL; } while (0)
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+#define LIST_REMOVE(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+} while (0)
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+#endif
+
+#define BIGNUM_CLASS "BIGNUM*"
+#define PKEY_CLASS "EVP_PKEY*"
+#define EC_GROUP_CLASS "EVP_GROUP*"
+#define X509_NAME_CLASS "X509_NAME*"
+#define X509_GENS_CLASS "GENERAL_NAMES*"
+#define X509_EXT_CLASS "X509_EXTENSION*"
+#define X509_CERT_CLASS "X509*"
+#define X509_CHAIN_CLASS "STACK_OF(X509)*"
+#define X509_CSR_CLASS "X509_REQ*"
+#define X509_CRL_CLASS "X509_CRL*"
+#define X509_STORE_CLASS "X509_STORE*"
+#define X509_VERIFY_PARAM_CLASS "X509_VERIFY_PARAM*"
+#define X509_STCTX_CLASS "X509_STORE_CTX*"
+#define PKCS12_CLASS "PKCS12*"
+#define SSL_CTX_CLASS "SSL_CTX*"
+#define SSL_CLASS "SSL*"
+#define DIGEST_CLASS "EVP_MD_CTX*"
+#define HMAC_CLASS "HMAC_CTX*"
+#define CIPHER_CLASS "EVP_CIPHER_CTX*"
+#define OCSP_RESPONSE_CLASS "OCSP_RESPONSE*"
+#define OCSP_BASICRESP_CLASS "OCSP_BASICRESP*"
+
+
+#if __GNUC__
+#define NOTUSED __attribute__((unused))
+#else
+#define NOTUSED
+#endif
+
+#if HAVE_C___BUILTIN_UNREACHABLE
+#define NOTREACHED __builtin_unreachable()
+#elif HAVE_C___ASSUME
+#define NOTREACHED __assume(0)
+#else
+#define NOTREACHED (void)0
+#endif
+
+#define countof(a) (sizeof (a) / sizeof *(a))
+#define endof(a) (&(a)[countof(a)])
+
+#define CLAMP(i, min, max) (((i) < (min))? (min) : ((i) > (max))? (max) : (i))
+
+#undef MIN
+#define MIN(a, b) (((a) < (b))? (a) : (b))
+
+#ifdef _WIN32
+#if !defined(S_ISDIR) && defined(_S_IFDIR) && defined(_S_IFDIR)
+#define S_ISDIR(m) (((m) & _S_IFDIR) == _S_IFDIR)
+#endif
+
+#define stricmp(a, b) _stricmp((a), (b))
+#else
+#include <strings.h> /* strcasecmp(3) */
+#define stricmp(a, b) strcasecmp((a), (b))
+#endif
+#define strieq(a, b) (!stricmp((a), (b)))
+
+#define xtolower(c) tolower((unsigned char)(c))
+
+#define SAY_(file, func, line, fmt, ...) \
+ fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__)
+
+#define SAY(...) SAY_(__FILE__, __func__, __LINE__, __VA_ARGS__, "\n")
+
+#define HAI SAY("hai")
+
+
+#define xitoa_putc(c) do { if (p < lim) dst[p] = (c); p++; } while (0)
+
+static const char *xitoa(char *dst, size_t lim, long i) {
+ size_t p = 0;
+ unsigned long d = 1000000000UL, n = 0, r;
+
+ if (i < 0) {
+ xitoa_putc('-');
+ i *= -1;
+ }
+
+ if ((i = MIN(2147483647L, i))) {
+ do {
+ if ((r = i / d) || n) {
+ i -= r * d;
+ n++;
+ xitoa_putc('0' + r);
+ }
+ } while (d /= 10);
+ } else {
+ xitoa_putc('0');
+ }
+
+ if (lim)
+ dst[MIN(p, lim - 1)] = '\0';
+
+ return dst;
+} /* xitoa() */
+
+
+static _Bool optbool(lua_State *L, int idx, _Bool d) {
+ if (lua_isnoneornil(L, idx))
+ return d;
+ luaL_checktype(L, idx, LUA_TBOOLEAN);
+ return lua_toboolean(L, idx);
+} /* optbool() */
+
+
+static void *prepudata(lua_State *L, size_t size, const char *tname, int (*gc)(lua_State *)) {
+ void *p = memset(lua_newuserdata(L, size), 0, size);
+
+ if (tname) {
+ luaL_setmetatable(L, tname);
+ } else {
+ lua_newtable(L);
+ lua_pushcfunction(L, gc);
+ lua_setfield(L, -2, "__gc");
+ lua_setmetatable(L, -2);
+ }
+
+ return p;
+} /* prepudata() */
+
+
+static void *prepsimple(lua_State *L, const char *tname, int (*gc)(lua_State *)) {
+ void **p = prepudata(L, sizeof (void *), tname, gc);
+ return p;
+} /* prepsimple() */
+
+#define EXPAND( x ) x
+#define prepsimple_(a, b, c, ...) prepsimple((a), (b), (c))
+#define prepsimple(...) EXPAND( prepsimple_(__VA_ARGS__, 0, 0) )
+
+
+static void *checksimple(lua_State *L, int index, const char *tname) {
+ void **p;
+
+ if (tname) {
+ p = luaL_checkudata(L, index, tname);
+ } else {
+ luaL_checktype(L, index, LUA_TUSERDATA);
+ p = lua_touserdata(L, index);
+ }
+
+ return *p;
+} /* checksimple() */
+
+
+static void *testsimple(lua_State *L, int index, const char *tname) {
+ void **p;
+
+ if (tname) {
+ p = luaL_testudata(L, index, tname);
+ } else {
+ luaL_checktype(L, index, LUA_TUSERDATA);
+ p = lua_touserdata(L, index);
+ }
+
+ return (p)? *p : (void *)0;
+} /* testsimple() */
+
+
+static int auxL_swapmetatable(lua_State *, const char *);
+static int auxL_swapmetasubtable(lua_State *, const char *, const char *);
+
+static int interpose(lua_State *L, const char *mt) {
+ if (!strncmp("__", luaL_checkstring(L, lua_absindex(L, -2)), 2)) {
+ return auxL_swapmetatable(L, mt);
+ } else {
+ return auxL_swapmetasubtable(L, mt, "__index");
+ }
+} /* interpose() */
+
+static int auxL_checkoption(lua_State *, int, const char *, const char *const *, _Bool);
+
+#define X509_ANY 0x01
+#define X509_PEM 0x02
+#define X509_DER 0x04
+#define X509_TXT 0x08 /* "pretty" */
+#define X509_ALL (X509_PEM|X509_DER)
+
+static int optencoding(lua_State *L, int index, const char *def, int allow) {
+ static const char *const opts[] = { "*", "pem", "der", "pretty", NULL };
+ int type = 0;
+
+ switch (auxL_checkoption(L, index, def, opts, 1)) {
+ case 0:
+ type = X509_ANY;
+ break;
+ case 1:
+ type = X509_PEM;
+ break;
+ case 2:
+ type = X509_DER;
+ break;
+ case 3:
+ type = X509_TXT;
+ break;
+ }
+
+ if (!(type & allow))
+ luaL_argerror(L, index, lua_pushfstring(L, "invalid option %s", luaL_checkstring(L, index)));
+
+ return type;
+} /* optencoding() */
+
+
+static _Bool rawgeti(lua_State *L, int index, int n) {
+ lua_rawgeti(L, index, n);
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+
+ return 0;
+ } else {
+ return 1;
+ }
+} /* rawgeti() */
+
+
+/* check ALPN protocols and add to buffer of length-prefixed strings */
+static void checkprotos(luaL_Buffer *B, lua_State *L, int index) {
+ int n;
+
+ luaL_checktype(L, index, LUA_TTABLE);
+
+ for (n = 1; rawgeti(L, index, n); n++) {
+ const char *tmp;
+ size_t len;
+
+ switch (lua_type(L, -1)) {
+ case LUA_TSTRING:
+ break;
+ default:
+ luaL_argerror(L, index, "array of strings expected");
+ }
+
+ tmp = luaL_checklstring(L, -1, &len);
+ luaL_argcheck(L, len > 0 && len <= UCHAR_MAX, index, "proto string length invalid");
+ luaL_addchar(B, (unsigned char)len);
+ luaL_addlstring(B, tmp, len);
+ lua_pop(L, 1);
+ }
+} /* checkprotos() */
+
+static void pushprotos(lua_State *L, const unsigned char *p, size_t n) {
+ const unsigned char *pe = &p[n];
+ int i = 0;
+
+ lua_newtable(L);
+
+ while (p < pe) {
+ n = *p++;
+
+ if ((size_t)(pe - p) < n)
+ luaL_error(L, "corrupt ALPN protocol list (%zu > %zu)", n, (size_t)(pe - p));
+
+ lua_pushlstring(L, (const void *)p, n);
+ lua_rawseti(L, -2, ++i);
+ p += n;
+ }
+} /* pushprotos() */
+
+
+static _Bool getfield(lua_State *L, int index, const char *k) {
+ lua_getfield(L, index, k);
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+
+ return 0;
+ } else {
+ return 1;
+ }
+} /* getfield() */
+
+
+static _Bool loadfield(lua_State *L, int index, const char *k, int type, void *p) {
+ if (!getfield(L, index, k))
+ return 0;
+
+ switch (type) {
+ case LUA_TSTRING:
+ *(const char **)p = luaL_checkstring(L, -1);
+ break;
+ case LUA_TNUMBER:
+ *(lua_Number *)p = luaL_checknumber(L, -1);
+ break;
+ default:
+ luaL_error(L, "loadfield(type=%d): invalid type", type);
+ break;
+ } /* switch() */
+
+ lua_pop(L, 1); /* table keeps reference */
+
+ return 1;
+} /* loadfield() */
+
+
+static void *loadfield_udata(lua_State *L, int index, const char *k, const char *tname) {
+ if (!getfield(L, index, k))
+ return NULL;
+
+ void **p = luaL_checkudata(L, -1, tname);
+
+ lua_pop(L, 1); /* table keeps reference */
+
+ return *p;
+} /* loadfield_udata() */
+
+
+/* Forward declaration */
+static void ssl_push(lua_State *, SSL *);
+
+/* push an ssl object into lua in a way that is safe from OOM
+ * Lua 5.1 does not support normally returning values from lua_cpcall
+ * to return a value, we instead return it via an error object
+ */
+static int ssl_pushsafe_helper(lua_State *L) {
+ ssl_push(L, lua_touserdata(L, 1));
+#if LUA_VERSION_NUM <= 501
+ return lua_error(L);
+#else
+ return 1;
+#endif
+}
+
+static int ssl_pushsafe(lua_State *L, SSL *ssl) {
+ int status;
+#if LUA_VERSION_NUM <= 501
+ status = lua_cpcall(L, ssl_pushsafe_helper, ssl);
+ if (status == LUA_ERRRUN)
+ status = LUA_OK;
+ else if (status == LUA_OK)
+ /* this should be impossible */
+ status = LUA_ERRRUN;
+ else
+ lua_pop(L, 1);
+#else
+ lua_pushcfunction(L, ssl_pushsafe_helper);
+ lua_pushlightuserdata(L, ssl);
+ status = lua_pcall(L, 1, 1, 0);
+ if (status != LUA_OK)
+ lua_pop(L, 1);
+#endif
+ return status;
+}
+
+
+/*
+ * Auxiliary C routines
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define AUX_MIN(a, b) (((a) < (b))? (a) : (b))
+
+static size_t aux_strlcpy(char *dst, const char *src, size_t lim) {
+ size_t n = strlen(src);
+
+ if (lim > 0) {
+ size_t m = AUX_MIN(lim - 1, n);
+
+ memcpy(dst, src, m);
+ dst[m] = '\0';
+ }
+
+ return n;
+} /* aux_strlcpy() */
+
+#define aux_strerror(error) aux_strerror_r((error), (char[256]){ 0 }, 256)
+
+static const char *aux_strerror_r(int error, char *dst, size_t lim) {
+ static const char unknown[] = "Unknown error: ";
+ size_t n;
+
+#if _WIN32
+ errno_t rv = strerror_s(dst, lim, error);
+
+ if (rv)
+ return dst;
+#elif STRERROR_R_CHAR_P
+ char *rv = strerror_r(error, dst, lim);
+
+ if (rv != NULL)
+ return dst;
+#else
+ int rv = strerror_r(error, dst, lim);
+
+ if (0 == rv)
+ return dst;
+#endif
+
+ /*
+ * glibc snprintf can fail on memory pressure, so format our number
+ * manually.
+ */
+ n = MIN(sizeof unknown - 1, lim);
+ memcpy(dst, unknown, n);
+
+ return xitoa(&dst[n], lim - n, error);
+} /* aux_strerror_r() */
+
+
+/*
+ * Auxiliary OpenSSL API routines
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void auxS_bn_free_and_set0(BIGNUM **dst, BIGNUM *src) {
+ if (*dst) {
+ BN_clear_free(*dst);
+ }
+ *dst = src;
+} /* auxS_bn_free_and_set0() */
+
+static size_t auxS_nid2sn(void *dst, size_t lim, int nid) {
+ const char *sn;
+
+ if (nid == NID_undef || !(sn = OBJ_nid2sn(nid)))
+ return 0;
+
+ return aux_strlcpy(dst, sn, lim);
+} /* aux2_nid2sn() */
+
+static size_t auxS_obj2sn(void *dst, size_t lim, const ASN1_OBJECT *obj) {
+ return auxS_nid2sn(dst, lim, OBJ_obj2nid(obj));
+} /* auxS_obj2sn() */
+
+static size_t auxS_nid2ln(void *dst, size_t lim, int nid) {
+ const char *ln;
+
+ if (nid == NID_undef || !(ln = OBJ_nid2ln(nid)))
+ return 0;
+
+ return aux_strlcpy(dst, ln, lim);
+} /* aux2_nid2ln() */
+
+static size_t auxS_obj2ln(void *dst, size_t lim, const ASN1_OBJECT *obj) {
+ return auxS_nid2ln(dst, lim, OBJ_obj2nid(obj));
+} /* auxS_obj2ln() */
+
+static size_t auxS_obj2id(void *dst, size_t lim, const ASN1_OBJECT *obj) {
+ int n = OBJ_obj2txt(dst, AUX_MIN(lim, INT_MAX), obj, 1);
+
+ /* TODO: push custom errors onto error stack */
+ if (n == 0) {
+ return 0; /* obj->data == NULL */
+ } else if (n < 0) {
+ return 0; /* memory allocation error */
+ } else {
+ return n;
+ }
+} /* auxS_obj2id() */
+
+static size_t auxS_nid2id(void *dst, size_t lim, int nid) {
+ ASN1_OBJECT *obj;
+
+ /* TODO: push custom error onto error stack */
+ if (!(obj = OBJ_nid2obj(nid)))
+ return 0;
+
+ return auxS_obj2id(dst, lim, obj);
+} /* auxS_nid2id() */
+
+static size_t auxS_nid2txt(void *dst, size_t lim, int nid) {
+ size_t n;
+
+ if ((n = auxS_nid2sn(dst, lim, nid)))
+ return n;
+ if ((n = auxS_nid2ln(dst, lim, nid)))
+ return n;
+
+ return auxS_nid2id(dst, lim, nid);
+} /* auxS_nid2txt() */
+
+static size_t auxS_obj2txt(void *dst, size_t lim, const ASN1_OBJECT *obj) {
+ size_t n;
+
+ if ((n = auxS_obj2sn(dst, lim, obj)))
+ return n;
+ if ((n = auxS_obj2ln(dst, lim, obj)))
+ return n;
+
+ return auxS_obj2id(dst, lim, obj);
+} /* auxS_obj2txt() */
+
+static const EVP_MD *auxS_todigest(const char *name, EVP_PKEY *key, const EVP_MD *def);
+
+static _Bool auxS_isoid(const char *txt) {
+ return (*txt >= '0' && *txt <= '9');
+} /* auxS_isoid() */
+
+static _Bool auxS_txt2obj(ASN1_OBJECT **obj, const char *txt) {
+ int nid;
+
+ if ((nid = OBJ_sn2nid(txt)) != NID_undef
+ || (nid = OBJ_ln2nid(txt)) != NID_undef) {
+ return NULL != (*obj = OBJ_nid2obj(nid));
+ } else if (auxS_isoid(txt)) {
+ return NULL != (*obj = OBJ_txt2obj(txt, 1));
+ } else {
+ *obj = NULL;
+ return 1;
+ }
+} /* auxS_txt2obj() */
+
+static _Bool auxS_txt2nid(int *nid, const char *txt) {
+ /* try builtins first */
+ if ((*nid = OBJ_sn2nid(txt)) != NID_undef
+ || (*nid = OBJ_ln2nid(txt)) != NID_undef) {
+ return 1;
+ }
+
+ /* OBJ_txt2nid creates a temporary ASN1_OBJECT; call sparingly */
+ if (auxS_isoid(txt) && (*nid = OBJ_txt2nid(txt)) != NID_undef) {
+ return 1;
+ }
+
+ return 0;
+} /* auxS_txt2nid() */
+
+
+/*
+ * Auxiliary Lua API routines
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+typedef int auxref_t;
+typedef int auxtype_t;
+
+static void auxL_unref(lua_State *L, auxref_t *ref) {
+ luaL_unref(L, LUA_REGISTRYINDEX, *ref);
+ *ref = LUA_NOREF;
+} /* auxL_unref() */
+
+static void auxL_ref(lua_State *L, int index, auxref_t *ref) {
+ auxL_unref(L, ref);
+ lua_pushvalue(L, index);
+ *ref = luaL_ref(L, LUA_REGISTRYINDEX);
+} /* auxL_ref() */
+
+NOTUSED static auxtype_t auxL_getref(lua_State *L, auxref_t ref) {
+ if (ref == LUA_NOREF || ref == LUA_REFNIL) {
+ lua_pushnil(L);
+ } else {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+ }
+
+ return lua_type(L, -1);
+} /* auxL_getref() */
+
+static int auxL_testoption(lua_State *L, int index, const char *def, const char *const *optlist, _Bool nocase) {
+ const char *optname = (def)? luaL_optstring(L, index, def) : luaL_checkstring(L, index);
+ int (*optcmp)() = (nocase)?
+#ifdef _WIN32
+ &_stricmp
+#else
+ &strcasecmp
+#endif
+ : &strcmp;
+ int i;
+
+ for (i = 0; optlist[i]; i++) {
+ if (0 == optcmp(optlist[i], optname))
+ return i;
+ }
+
+ return -1;
+} /* auxL_testoption() */
+
+static int auxL_checkoption(lua_State *L, int index, const char *def, const char *const *optlist, _Bool nocase) {
+ int i;
+
+ if ((i = auxL_testoption(L, index, def, optlist, nocase)) >= 0)
+ return i;
+
+ return luaL_argerror(L, index, lua_pushfstring(L, "invalid option '%s'", luaL_optstring(L, index, def)));
+} /* auxL_checkoption() */
+
+/*
+ * Lua 5.3 distinguishes integers and numbers, and by default uses 64-bit
+ * integers. The following routines try to preserve this distinction and
+ * where possible detect range issues.
+ *
+ * The signed range checking assumes two's complement, no padding bits, and
+ * sizeof lua_Integer <= sizeof long long. Which is a safe bet where OpenSSL
+ * is typically used.
+ */
+#define auxL_Integer long long
+#define auxL_IntegerMin LLONG_MIN
+#define auxL_IntegerMax LLONG_MAX
+#define auxL_Unsigned unsigned long long
+#define auxL_UnsignedMin 0
+#define auxL_UnsignedMax ULLONG_MAX
+
+#define lua_IntegerMax ((1ULL << (sizeof (lua_Integer) * 8 - 1)) - 1)
+#define lua_IntegerMin (-lua_IntegerMax - 1)
+
+static void auxL_pushinteger(lua_State *L, auxL_Integer i) {
+ /*
+ * TODO: Check value explicitly, but will need to silence compiler
+ * diagnostics about useless comparisons.
+ */
+ if (sizeof (lua_Integer) >= sizeof i) {
+ lua_pushinteger(L, i);
+ } else {
+ /* TODO: Check overflow. */
+ lua_pushnumber(L, i);
+ }
+} /* auxL_pushinteger() */
+
+static void auxL_pushunsigned(lua_State *L, auxL_Unsigned i) {
+ if (i <= lua_IntegerMax) {
+ lua_pushinteger(L, i);
+ } else if (i == (auxL_Unsigned)(lua_Number)i) {
+ lua_pushnumber(L, i);
+ } else {
+ luaL_error(L, "unsigned integer value not representable as lua_Integer or lua_Number");
+ }
+} /* auxL_pushunsigned() */
+
+#define auxL_checkinteger_(a, b, c, d, ...) auxL_checkinteger((a), (b), (c), (d))
+#define auxL_checkinteger(...) EXPAND( auxL_checkinteger_(__VA_ARGS__, auxL_IntegerMin, auxL_IntegerMax, 0) )
+
+static auxL_Integer (auxL_checkinteger)(lua_State *L, int index, auxL_Integer min, auxL_Integer max) {
+ auxL_Integer i;
+
+ if (sizeof (lua_Integer) >= sizeof (auxL_Integer)) {
+ i = luaL_checkinteger(L, index);
+ } else {
+ /* TODO: Check overflow. */
+ i = (auxL_Integer)luaL_checknumber(L, index);
+ }
+
+ if (i < min || i > max)
+ luaL_error(L, "integer value out of range");
+
+ return i;
+} /* auxL_checkinteger() */
+
+#define auxL_optinteger_(a, b, c, d, e, ...) auxL_optinteger((a), (b), (c), (d), (e))
+#define auxL_optinteger(...) EXPAND( auxL_optinteger_(__VA_ARGS__, auxL_IntegerMin, auxL_IntegerMax, 0))
+
+static auxL_Integer (auxL_optinteger)(lua_State *L, int index, auxL_Integer def, auxL_Integer min, auxL_Integer max) {
+ return (lua_isnoneornil(L, index))? def : auxL_checkinteger(L, index, min, max);
+} /* auxL_optinteger() */
+
+#define auxL_checkunsigned_(a, b, c, d, ...) auxL_checkunsigned((a), (b), (c), (d))
+#define auxL_checkunsigned(...) EXPAND( auxL_checkunsigned_(__VA_ARGS__, auxL_UnsignedMin, auxL_UnsignedMax, 0))
+
+static auxL_Unsigned (auxL_checkunsigned)(lua_State *L, int index, auxL_Unsigned min, auxL_Unsigned max) {
+ auxL_Unsigned i;
+
+ if (sizeof (lua_Integer) >= sizeof (auxL_Unsigned)) {
+ /* TODO: Check sign. */
+ i = luaL_checkinteger(L, index);
+ } else {
+ /* TODO: Check sign and overflow. */
+ i = (auxL_Integer)luaL_checknumber(L, index);
+ }
+
+ if (i < min || i > max)
+ luaL_error(L, "integer value out of range");
+
+ return i;
+} /* auxL_checkunsigned() */
+
+#define auxL_optunsigned_(a, b, c, d, e, ...) auxL_optunsigned((a), (b), (c), (d), (e))
+#define auxL_optunsigned(...) EXPAND( auxL_optunsigned_(__VA_ARGS__, auxL_UnsignedMin, auxL_UnsignedMax, 0) )
+
+static auxL_Unsigned (auxL_optunsigned)(lua_State *L, int index, auxL_Unsigned def, auxL_Unsigned min, auxL_Unsigned max) {
+ return (lua_isnoneornil(L, index))? def : auxL_checkunsigned(L, index, min, max);
+} /* auxL_optunsigned() */
+
+static int auxL_size2int(lua_State *L, size_t n) {
+ if (n > INT_MAX)
+ luaL_error(L, "integer value out of range (%zu > INT_MAX)", n);
+
+ return (int)n;
+} /* auxL_size2int() */
+
+typedef struct {
+ const char *name;
+ auxL_Integer value;
+} auxL_IntegerReg;
+
+static void auxL_setintegers(lua_State *L, const auxL_IntegerReg *l) {
+ for (; l->name; l++) {
+ auxL_pushinteger(L, l->value);
+ lua_setfield(L, -2, l->name);
+ }
+} /* auxL_setintegers() */
+
+#define AUXL_REG_NULL (&(auxL_Reg[]){ 0 })
+
+typedef struct {
+ const char *name;
+ lua_CFunction func;
+ unsigned nups; /* in addition to nups specified to auxL_setfuncs */
+} auxL_Reg;
+
+static inline size_t auxL_liblen(const auxL_Reg *l) {
+ size_t n = 0;
+
+ while ((l++)->name)
+ n++;
+
+ return n;
+} /* auxL_liblen() */
+
+#define auxL_newlibtable(L, l) \
+ lua_createtable((L), 0, countof((l)) - 1)
+
+#define auxL_newlib(L, l, nups) \
+ (auxL_newlibtable((L), (l)), lua_insert((L), -(nups + 1)), auxL_setfuncs((L), (l), (nups)))
+
+static void auxL_setfuncs(lua_State *L, const auxL_Reg *l, int nups) {
+ for (; l->name; l++) {
+ int i;
+
+ /* copy shared upvalues */
+ luaL_checkstack(L, nups, "too many upvalues");
+ for (i = 0; i < nups; i++)
+ lua_pushvalue(L, -nups);
+
+ /* nil-fill local upvalues */
+ luaL_checkstack(L, l->nups, "too many upvalues");
+ lua_settop(L, lua_gettop(L) + l->nups);
+
+ /* set closure */
+ luaL_checkstack(L, 1, "too many upvalues");
+ lua_pushcclosure(L, l->func, nups + l->nups);
+ lua_setfield(L, -(nups + 2), l->name);
+ }
+
+ lua_pop(L, nups);
+
+ return;
+} /* auxL_setfuncs() */
+
+static void auxL_clear(lua_State *L, int tindex) {
+ tindex = lua_absindex(L, tindex);
+
+ lua_pushnil(L);
+ while (lua_next(L, tindex)) {
+ lua_pop(L, 1);
+ lua_pushvalue(L, -1);
+ lua_pushnil(L);
+ lua_rawset(L, tindex);
+ }
+} /* auxL_clear() */
+
+static _Bool auxL_newmetatable(lua_State *L, const char *name, _Bool reset) {
+ if (luaL_newmetatable(L, name))
+ return 1;
+ if (!reset)
+ return 0;
+
+ /*
+ * NB: Keep existing table as it may be cached--e.g. in
+ * another module that isn't being reloaded. But scrub it
+ * clean so function interposition--which will presumably
+ * run again if the C module is being reloaded--doesn't
+ * result in loops.
+ */
+ auxL_clear(L, -1);
+ lua_pushnil(L);
+ lua_setmetatable(L, -2);
+#if LUA_VERSION_NUM >= 502
+ lua_pushnil(L);
+ lua_setuservalue(L, -2);
+#endif
+
+ return 0;
+} /* auxL_newmetatable() */
+
+static _Bool auxL_newclass(lua_State *L, const char *name, const auxL_Reg *methods, const auxL_Reg *metamethods, _Bool reset) {
+ _Bool fresh = auxL_newmetatable(L, name, reset);
+ int n;
+
+ if (!reset && !fresh)
+ return fresh;
+
+ auxL_setfuncs(L, metamethods, 0);
+
+ if ((n = auxL_liblen(methods))) {
+ lua_createtable(L, 0, auxL_size2int(L, n));
+ auxL_setfuncs(L, methods, 0);
+ lua_setfield(L, -2, "__index");
+ }
+
+ return fresh;
+} /* auxL_newclass() */
+
+#define auxL_addclass(L, ...) \
+ EXPAND( (auxL_newclass((L), __VA_ARGS__), lua_pop((L), 1)) )
+
+static int auxL_swaptable(lua_State *L, int index) {
+ index = lua_absindex(L, index);
+
+ lua_pushvalue(L, -2); /* push key */
+ lua_gettable(L, index); /* push old value */
+
+ lua_pushvalue(L, -3); /* push key */
+ lua_pushvalue(L, -3); /* push new value */
+ lua_settable(L, index); /* replace old value */
+
+ lua_replace(L, -3);
+ lua_pop(L, 1);
+
+ return 1; /* return old value */
+} /* auxL_swaptable() */
+
+static int auxL_swapmetatable(lua_State *L, const char *name) {
+ luaL_getmetatable(L, name);
+
+ lua_pushvalue(L, -3);
+ lua_pushvalue(L, -3);
+ auxL_swaptable(L, -3);
+
+ lua_replace(L, -4);
+ lua_pop(L, 2);
+
+ return 1;
+} /* auxL_swapmetatable() */
+
+static int auxL_swapmetasubtable(lua_State *L, const char *name, const char *subname) {
+ luaL_getmetatable(L, name);
+ lua_getfield(L, -1, subname);
+
+ lua_pushvalue(L, -4);
+ lua_pushvalue(L, -4);
+ auxL_swaptable(L, -3);
+
+ lua_replace(L, -5);
+ lua_pop(L, 3);
+
+ return 1;
+} /* auxL_swapmetasubtable() */
+
+#define auxL_EDYLD -2
+#define auxL_EOPENSSL -1
+
+static const char *auxL_pusherror(lua_State *L, int error, const char *fun) {
+ if (error == auxL_EOPENSSL) {
+ unsigned long code;
+ const char *path, *file;
+ int line;
+ char txt[256];
+
+ if (!ERR_peek_error())
+ return lua_pushstring(L, "oops: no OpenSSL errors set");
+
+ code = ERR_get_error_line(&path, &line);
+
+ if ((file = strrchr(path, '/'))) {
+ ++file;
+ } else {
+ file = path;
+ }
+
+ ERR_clear_error();
+
+ ERR_error_string_n(code, txt, sizeof txt);
+
+ if (fun) {
+ return lua_pushfstring(L, "%s: %s:%d:%s", fun, file, line, txt);
+ } else {
+ return lua_pushfstring(L, "%s:%d:%s", file, line, txt);
+ }
+#if HAVE_DLADDR
+ } else if (error == auxL_EDYLD) {
+ const char *const fmt = (fun)? "%s: %s" : "%.0s%s";
+
+ return lua_pushfstring(L, fmt, (fun)? fun : "", dlerror());
+#endif
+ } else {
+ const char *const fmt = (fun)? "%s: %s" : "%.0s%s";
+
+ return lua_pushfstring(L, fmt, (fun)? fun : "", aux_strerror(error));
+ }
+} /* auxL_pusherror() */
+
+static int auxL_error(lua_State *L, int error, const char *fun) {
+ auxL_pusherror(L, error, fun);
+ lua_error(L);
+ NOTREACHED;
+ return 0;
+} /* auxL_error() */
+
+static const char *auxL_pushnid(lua_State *L, int nid) {
+ char txt[256] = { 0 };
+ size_t n;
+
+ if (!(n = auxS_nid2txt(txt, sizeof txt, nid)) || n >= sizeof txt)
+ luaL_error(L, "%d: invalid ASN.1 NID", nid);
+
+ lua_pushlstring(L, txt, n);
+
+ return lua_tostring(L, -1);
+} /* auxL_pushnid() */
+
+static const EVP_MD *auxL_optdigest(lua_State *L, int index, EVP_PKEY *key, const EVP_MD *def);
+
+
+/*
+ * dl - dynamically loaded module management
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Prevent loader from unlinking us if we've registered a callback with
+ * OpenSSL by taking another reference to ourselves.
+ */
+/* dl_anchor must not be called from multiple threads at once */
+static int dl_anchor(void) {
+#if _WIN32
+ EXPORT extern int luaopen__openssl(lua_State *);
+
+ HMODULE dummy;
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN|GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (void *)&luaopen__openssl, &dummy))
+ return GetLastError();
+
+ return 0;
+#elif HAVE_DLADDR
+ extern int luaopen__openssl(lua_State *);
+ static void *anchor;
+ Dl_info info;
+ int error = 0;
+
+ if (anchor)
+ goto epilog;
+
+ if (!dladdr((void *)&luaopen__openssl, &info))
+ goto dlerr;
+
+ if (!(anchor = dlopen(info.dli_fname, RTLD_NOW|RTLD_LOCAL)))
+ goto dlerr;
+epilog:
+ return error;
+dlerr:
+ error = auxL_EDYLD;
+
+ goto epilog;
+#else
+ return 0;//ENOTSUP;
+#endif
+} /* dl_anchor() */
+
+
+/*
+ * compat - OpenSSL API compatibility and bug workarounds
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define COMPAT_X509_STORE_FREE_BUG 0x01
+
+static struct {
+ int flags;
+
+ void (*X509_STORE_free)(X509_STORE *);
+
+ struct {
+ X509_STORE *store;
+ } tmp;
+} compat = {
+ .flags = 0,
+ .X509_STORE_free = &X509_STORE_free,
+};
+
+#if !HAVE_ASN1_STRING_GET0_DATA
+#define ASN1_STRING_get0_data(s) ASN1_STRING_data((s))
+#endif
+
+#if !HAVE_DH_GET0_KEY
+#define DH_get0_key(...) EXPAND( compat_DH_get0_key(__VA_ARGS__) )
+
+static void compat_DH_get0_key(const DH *d, const BIGNUM **pub_key, const BIGNUM **priv_key) {
+ if (pub_key)
+ *pub_key = d->pub_key;
+ if (priv_key)
+ *priv_key = d->priv_key;
+} /* compat_DH_get0_key() */
+#endif
+
+#if !HAVE_DH_GET0_PQG
+#define DH_get0_pqg(...) EXPAND( compat_DH_get0_pqg(__VA_ARGS__) )
+
+static void compat_DH_get0_pqg(const DH *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) {
+ if (p)
+ *p = d->p;
+ if (q)
+ *q = d->q;
+ if (g)
+ *g = d->g;
+} /* compat_DH_get0_pqg() */
+#endif
+
+#if !HAVE_DH_SET0_KEY
+#define DH_set0_key(...) EXPAND( compat_DH_set0_key(__VA_ARGS__) )
+
+static void compat_DH_set0_key(DH *d, BIGNUM *pub_key, BIGNUM *priv_key) {
+ if (pub_key)
+ auxS_bn_free_and_set0(&d->pub_key, pub_key);
+ if (priv_key)
+ auxS_bn_free_and_set0(&d->priv_key, priv_key);
+} /* compat_DH_set0_key() */
+#endif
+
+#if !HAVE_DH_SET0_PQG
+#define DH_set0_pqg(...) EXPAND( compat_DH_set0_pqg(__VA_ARGS__) )
+
+static void compat_DH_set0_pqg(DH *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
+ if (p)
+ auxS_bn_free_and_set0(&d->p, p);
+ if (q)
+ auxS_bn_free_and_set0(&d->q, q);
+ if (g)
+ auxS_bn_free_and_set0(&d->g, g);
+} /* compat_DH_set0_pqg() */
+#endif
+
+#if !HAVE_DSA_GET0_KEY
+#define DSA_get0_key(...) EXPAND( compat_DSA_get0_key(__VA_ARGS__) )
+
+static void compat_DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key) {
+ if (pub_key)
+ *pub_key = d->pub_key;
+ if (priv_key)
+ *priv_key = d->priv_key;
+} /* compat_DSA_get0_key() */
+#endif
+
+#if !HAVE_DSA_GET0_PQG
+#define DSA_get0_pqg(...) EXPAND( compat_DSA_get0_pqg(__VA_ARGS__) )
+
+static void compat_DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) {
+ if (p)
+ *p = d->p;
+ if (q)
+ *q = d->q;
+ if (g)
+ *g = d->g;
+} /* compat_DSA_get0_pqg() */
+#endif
+
+#if !HAVE_DSA_SET0_KEY
+#define DSA_set0_key(...) EXPAND( compat_DSA_set0_key(__VA_ARGS__) )
+
+static void compat_DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) {
+ if (pub_key)
+ auxS_bn_free_and_set0(&d->pub_key, pub_key);
+ if (priv_key)
+ auxS_bn_free_and_set0(&d->priv_key, priv_key);
+} /* compat_DSA_set0_key() */
+#endif
+
+#if !HAVE_DSA_SET0_PQG
+#define DSA_set0_pqg(...) EXPAND( compat_DSA_set0_pqg(__VA_ARGS__) )
+
+static void compat_DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) {
+ if (p)
+ auxS_bn_free_and_set0(&d->p, p);
+ if (q)
+ auxS_bn_free_and_set0(&d->q, q);
+ if (g)
+ auxS_bn_free_and_set0(&d->g, g);
+} /* compat_DSA_set0_pqg() */
+#endif
+
+#if !HAVE_EVP_CIPHER_CTX_FREE
+#define EVP_CIPHER_CTX_free(ctx) compat_EVP_CIPHER_CTX_free((ctx))
+
+static void compat_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx) {
+ EVP_CIPHER_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+} /* compat_EVP_CIPHER_CTX_free() */
+#endif
+
+#if !HAVE_EVP_CIPHER_CTX_NEW
+#define EVP_CIPHER_CTX_new() compat_EVP_CIPHER_CTX_new()
+
+static EVP_CIPHER_CTX *compat_EVP_CIPHER_CTX_new(void) {
+ EVP_CIPHER_CTX *ctx;
+
+ if (!(ctx = OPENSSL_malloc(sizeof *ctx)))
+ return NULL;
+ memset(ctx, 0, sizeof *ctx);
+ EVP_CIPHER_CTX_init(ctx);
+
+ return ctx;
+} /* compat_EVP_CIPHER_CTX_new() */
+#endif
+
+#if !HAVE_EVP_MD_CTX_FREE
+#define EVP_MD_CTX_free(md) EVP_MD_CTX_destroy((md))
+#endif
+
+#if !HAVE_EVP_MD_CTX_NEW
+#define EVP_MD_CTX_new() EVP_MD_CTX_create()
+#endif
+
+#if !HAVE_EVP_PKEY_ID
+#define EVP_PKEY_id(key) ((key)->type)
+#endif
+
+#if !HAVE_EVP_PKEY_BASE_ID
+#define EVP_PKEY_base_id(key) compat_EVP_PKEY_base_id((key))
+
+static int compat_EVP_PKEY_base_id(EVP_PKEY *key) {
+ return EVP_PKEY_type(EVP_PKEY_id(key));
+} /* compat_EVP_PKEY_base_id() */
+#endif
+
+#if !HAVE_EVP_PKEY_GET_DEFAULT_DIGEST_NID
+#define EVP_PKEY_get_default_digest_nid(...) \
+ EXPAND( compat_EVP_PKEY_get_default_digest_nid(__VA_ARGS__) )
+
+static int compat_EVP_PKEY_get_default_digest_nid(EVP_PKEY *key, int *nid) {
+ switch (EVP_PKEY_base_id(key)) {
+ case EVP_PKEY_RSA:
+ *nid = EVP_MD_nid(EVP_sha1());
+ break;
+ case EVP_PKEY_DSA:
+ *nid = EVP_MD_nid(EVP_dss1());
+ break;
+ case EVP_PKEY_EC:
+ *nid = EVP_MD_nid(EVP_ecdsa());
+ break;
+ default:
+ *nid = EVP_MD_nid(EVP_sha1());
+ break;
+ }
+
+ return 1;
+} /* compat_EVP_PKEY_get_default_digest_nid() */
+#endif
+
+#if !HAVE_EVP_PKEY_GET0
+#define EVP_PKEY_get0(key) compat_EVP_PKEY_get0((key))
+
+static void *compat_EVP_PKEY_get0(EVP_PKEY *key) {
+ void *ptr = NULL;
+
+ switch (EVP_PKEY_base_id(key)) {
+ case EVP_PKEY_RSA:
+ if ((ptr = EVP_PKEY_get1_RSA(key)))
+ RSA_free(ptr);
+ break;
+ case EVP_PKEY_DSA:
+ if ((ptr = EVP_PKEY_get1_DSA(key)))
+ DSA_free(ptr);
+ break;
+ case EVP_PKEY_DH:
+ if ((ptr = EVP_PKEY_get1_DH(key)))
+ DH_free(ptr);
+ break;
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC:
+ if ((ptr = EVP_PKEY_get1_EC_KEY(key)))
+ EC_KEY_free(ptr);
+ break;
+#endif
+ default:
+ /* TODO: Use ERR_put_error */
+
+ break;
+ }
+
+ return ptr;
+} /* compat_EVP_PKEY_get0() */
+#endif
+
+#if !HAVE_HMAC_CTX_FREE
+#define HMAC_CTX_free(ctx) compat_HMAC_CTX_free((ctx))
+
+static void compat_HMAC_CTX_free(HMAC_CTX *ctx) {
+ HMAC_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+} /* compat_HMAC_CTX_free() */
+#endif
+
+#if !HAVE_HMAC_CTX_NEW
+#define HMAC_CTX_new() compat_HMAC_CTX_new()
+
+static HMAC_CTX *compat_HMAC_CTX_new(void) {
+ HMAC_CTX *ctx;
+
+ if (!(ctx = OPENSSL_malloc(sizeof *ctx)))
+ return NULL;
+ memset(ctx, 0, sizeof *ctx);
+
+ return ctx;
+} /* compat_HMAC_CTX_new() */
+#endif
+
+#if !HAVE_RSA_GET0_CRT_PARAMS
+#define RSA_get0_crt_params(...) EXPAND( compat_RSA_get0_crt_params(__VA_ARGS__) )
+
+static void compat_RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp) {
+ if (dmp1)
+ *dmp1 = r->dmp1;
+ if (dmq1)
+ *dmq1 = r->dmq1;
+ if (iqmp)
+ *iqmp = r->iqmp;
+} /* compat_RSA_get0_crt_params() */
+#endif
+
+#if !HAVE_RSA_GET0_FACTORS
+#define RSA_get0_factors(...) EXPAND( compat_RSA_get0_factors(__VA_ARGS__) )
+
+static void compat_RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) {
+ if (p)
+ *p = r->p;
+ if (q)
+ *q = r->q;
+} /* compat_RSA_get0_factors() */
+#endif
+
+#if !HAVE_RSA_GET0_KEY
+#define RSA_get0_key(...) EXPAND( compat_RSA_get0_key(__VA_ARGS__) )
+
+static void compat_RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) {
+ if (n)
+ *n = r->n;
+ if (e)
+ *e = r->e;
+ if (d)
+ *d = r->d;
+} /* compat_RSA_get0_key() */
+#endif
+
+#if !HAVE_RSA_SET0_CRT_PARAMS
+#define RSA_set0_crt_params(...) EXPAND( compat_RSA_set0_crt_params(__VA_ARGS__) )
+
+static void compat_RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) {
+ if (dmp1)
+ auxS_bn_free_and_set0(&r->dmp1, dmp1);
+ if (dmq1)
+ auxS_bn_free_and_set0(&r->dmq1, dmq1);
+ if (iqmp)
+ auxS_bn_free_and_set0(&r->iqmp, iqmp);
+} /* compat_RSA_set0_crt_params() */
+#endif
+
+#if !HAVE_RSA_SET0_FACTORS
+#define RSA_set0_factors(...) EXPAND( compat_RSA_set0_factors(__VA_ARGS__) )
+
+static void compat_RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) {
+ if (p)
+ auxS_bn_free_and_set0(&r->p, p);
+ if (q)
+ auxS_bn_free_and_set0(&r->q, q);
+} /* compat_RSA_set0_factors() */
+#endif
+
+#if !HAVE_RSA_SET0_KEY
+#define RSA_set0_key(...) EXPAND( compat_RSA_set0_key(__VA_ARGS__) )
+
+static void compat_RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) {
+ if (n)
+ auxS_bn_free_and_set0(&r->n, n);
+ if (e)
+ auxS_bn_free_and_set0(&r->e, e);
+ if (d)
+ auxS_bn_free_and_set0(&r->d, d);
+} /* compat_RSA_set0_key() */
+#endif
+
+#if !HAVE_SSL_GET_CLIENT_RANDOM
+#define SSL_get_client_random(...) EXPAND( compat_SSL_get_client_random(__VA_ARGS__) )
+static size_t compat_SSL_get_client_random(const SSL *ssl, unsigned char *out, size_t outlen) {
+ if (outlen == 0)
+ return sizeof(ssl->s3->client_random);
+ if (outlen > sizeof(ssl->s3->client_random))
+ outlen = sizeof(ssl->s3->client_random);
+ memcpy(out, ssl->s3->client_random, outlen);
+ return outlen;
+}
+#endif
+
+#if !HAVE_SSL_SESSION_MASTER_KEY
+#define SSL_SESSION_get_master_key(...) EXPAND( compat_SSL_SESSION_get_master_key(__VA_ARGS__) )
+static size_t compat_SSL_SESSION_get_master_key(const SSL_SESSION *session, unsigned char *out, size_t outlen) {
+ if (session->master_key_length < 0) {
+ /* Should never happen */
+ return 0;
+ }
+ if (outlen == 0)
+ return session->master_key_length;
+ if (outlen > (size_t)session->master_key_length)
+ outlen = session->master_key_length;
+ memcpy(out, session->master_key, outlen);
+ return outlen;
+}
+#endif
+
+#if !HAVE_SSL_CLIENT_VERSION
+#define SSL_client_version(...) EXPAND( compat_SSL_client_version(__VA_ARGS__) )
+
+static int compat_SSL_client_version(const SSL *ssl) {
+ return ssl->client_version;
+} /* compat_SSL_client_version() */
+#endif
+
+#if !HAVE_SSL_GET0_PARAM
+#define SSL_get0_param(ssl) compat_SSL_get0_param((ssl))
+
+static X509_VERIFY_PARAM *compat_SSL_get0_param(SSL *ssl) {
+ return ssl->param;
+} /* compat_SSL_get0_param() */
+#endif
+
+#if !HAVE_SSL_SET1_PARAM
+#define SSL_set1_param(ssl, vpm) compat_SSL_set1_param((ssl), (vpm))
+
+static int compat_SSL_set1_param(SSL *ssl, X509_VERIFY_PARAM *vpm) {
+ return X509_VERIFY_PARAM_set1(ssl->param, vpm);
+} /* compat_SSL_set1_param() */
+#endif
+
+#if !HAVE_SSL_UP_REF
+#define SSL_up_ref(...) EXPAND( compat_SSL_up_ref(__VA_ARGS__) )
+
+static int compat_SSL_up_ref(SSL *ssl) {
+ /* our caller should already have had a proper reference */
+ if (CRYPTO_add(&ssl->references, 1, CRYPTO_LOCK_SSL) < 2)
+ return 0; /* fail */
+
+ return 1;
+} /* compat_SSL_up_ref() */
+#endif
+
+#if !HAVE_SSL_OP_NO_SSL_MASK
+/* SSL_OP_NO_SSL_MASK was introduced in 1.0.2
+ 1.0.1 had up to TLSv1_2
+ 0.9.8-1.0.0 had up to TLSv1
+*/
+#ifdef SSL_OP_NO_TLSv1_2
+#define SSL_OP_NO_SSL_MASK (SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2)
+#else
+#define SSL_OP_NO_SSL_MASK (SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1)
+#endif
+#endif
+
+#if !HAVE_SSL_OP_NO_DTLS_MASK && HAVE_DTLS_CLIENT_METHOD
+#define SSL_OP_NO_DTLS_MASK (SSL_OP_NO_DTLSv1|SSL_OP_NO_DTLSv1_2)
+#endif
+
+#if !HAVE_SSL_CTX_GET0_PARAM
+#define SSL_CTX_get0_param(ctx) compat_SSL_CTX_get0_param((ctx))
+
+static X509_VERIFY_PARAM *compat_SSL_CTX_get0_param(SSL_CTX *ctx) {
+ return ctx->param;
+} /* compat_SSL_CTX_get0_param() */
+#endif
+
+#if !HAVE_SSL_CTX_SET1_PARAM
+#define SSL_CTX_set1_param(ctx, vpm) compat_SSL_CTX_set1_param((ctx), (vpm))
+
+static int compat_SSL_CTX_set1_param(SSL_CTX *ctx, X509_VERIFY_PARAM *vpm) {
+ return X509_VERIFY_PARAM_set1(ctx->param, vpm);
+} /* compat_SSL_CTX_set1_param() */
+#endif
+
+#if !HAVE_STACK_OPENSSL_STRING_FUNCS
+#define sk_OPENSSL_STRING_num(s) sk_num(s)
+#define sk_OPENSSL_STRING_value(s, i) sk_value((s), (i))
+#define sk_OPENSSL_STRING_free(s) X509_email_free(s)
+#endif
+
+#if !HAVE_X509_GET0_EXT
+#define X509_get0_ext(crt, i) X509_get_ext((crt), (i))
+#endif
+
+#if !HAVE_X509_GET_SIGNATURE_NID
+#define X509_get_signature_nid(crt) OBJ_obj2nid((crt)->sig_alg->algorithm)
+#endif
+
+#if !HAVE_X509_CRL_GET0_EXT
+#define X509_CRL_get0_ext(crt, i) X509_CRL_get_ext((crt), (i))
+#endif
+
+#if !HAVE_X509_CRL_GET0_LASTUPDATE
+#define X509_CRL_get0_lastUpdate(crl) ((const ASN1_TIME*)X509_CRL_get_lastUpdate(crl))
+#endif
+
+#if !HAVE_X509_CRL_GET0_NEXTUPDATE
+#define X509_CRL_get0_nextUpdate(crl) ((const ASN1_TIME*)X509_CRL_get_nextUpdate(crl))
+#endif
+
+#if !HAVE_X509_CRL_SET1_LASTUPDATE
+#define X509_CRL_set1_lastUpdate(crl, s) X509_CRL_set_lastUpdate((crl), (ASN1_TIME*)(s))
+#endif
+
+#if !HAVE_X509_CRL_SET1_NEXTUPDATE
+#define X509_CRL_set1_nextUpdate(crl, s) X509_CRL_set_nextUpdate((crl), (ASN1_TIME*)(s))
+#endif
+
+#if !HAVE_X509_EXTENSION_GET0_OBJECT
+#define X509_EXTENSION_get0_object(ext) X509_EXTENSION_get_object((ext))
+#endif
+
+#if !HAVE_X509_EXTENSION_GET0_DATA
+#define X509_EXTENSION_get0_data(ext) X509_EXTENSION_get_data((ext))
+#endif
+
+#if HAVE_X509_STORE_REFERENCES
+/*
+ * X509_STORE_free in OpenSSL versions < 1.0.2 doesn't obey reference count
+ */
+#define X509_STORE_free(store) \
+ (compat.X509_STORE_free)((store))
+
+/* to support preprocessor detection below */
+#define compat_X509_STORE_free(store) \
+ compat_X509_STORE_free((store))
+
+static void (compat_X509_STORE_free)(X509_STORE *store) {
+ int i;
+
+ i = CRYPTO_add(&store->references, -1, CRYPTO_LOCK_X509_STORE);
+ if (i > 0)
+ return;
+
+ (X509_STORE_free)(store);
+} /* compat_X509_STORE_free() */
+#endif
+
+#if !HAVE_SSL_CTX_SET1_CERT_STORE
+
+#define SSL_CTX_set1_cert_store(ctx, store) \
+ compat_SSL_CTX_set1_cert_store((ctx), (store))
+
+static void (compat_SSL_CTX_set1_cert_store)(SSL_CTX *ctx, X509_STORE *store) {
+#if !HAVE_SSL_CTX_CERT_STORE || !HAVE_X509_STORE_REFERENCES
+ if (store != NULL)
+ X509_STORE_up_ref(store);
+ SSL_CTX_set_cert_store(ctx, store);
+#else
+ int n;
+
+ /*
+ * This isn't thead-safe, but using X509_STORE or SSL_CTX objects
+ * from different threads isn't safe generally.
+ */
+ if (ctx->cert_store) {
+ X509_STORE_free(ctx->cert_store);
+ ctx->cert_store = NULL;
+ }
+
+ n = store->references;
+
+ SSL_CTX_set_cert_store(ctx, store);
+
+ if (n == store->references)
+ CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE);
+#endif
+} /* compat_SSL_CTX_set1_cert_store() */
+
+#endif
+
+#if HAVE_SSL_CTX_CERT_STORE
+
+static void compat_init_SSL_CTX_onfree(void *_ctx, void *data NOTUSED, CRYPTO_EX_DATA *ad NOTUSED, int idx NOTUSED, long argl NOTUSED, void *argp NOTUSED) {
+ SSL_CTX *ctx = _ctx;
+
+ if (ctx->cert_store) {
+ X509_STORE_free(ctx->cert_store);
+ ctx->cert_store = NULL;
+ }
+} /* compat_init_SSL_CTX_onfree() */
+
+#endif
+
+#if defined compat_X509_STORE_free
+/* helper routine to determine if X509_STORE_free obeys reference count */
+static void compat_init_X509_STORE_onfree(void *store, void *data NOTUSED, CRYPTO_EX_DATA *ad NOTUSED, int idx NOTUSED, long argl NOTUSED, void *argp NOTUSED) {
+ /* unfortunately there's no way to remove a handler */
+ if (store != compat.tmp.store)
+ return;
+
+ /* signal that we were freed by nulling our reference */
+ compat.tmp.store = NULL;
+} /* compat_init_X509_STORE_onfree() */
+#endif
+
+
+#if !HAVE_X509_STORE_UP_REF
+#define X509_STORE_up_ref(...) EXPAND( compat_X509_STORE_up_ref(__VA_ARGS__) )
+
+static int compat_X509_STORE_up_ref(X509_STORE *crt) {
+ /* our caller should already have had a proper reference */
+ if (CRYPTO_add(&crt->references, 1, CRYPTO_LOCK_X509_STORE) < 2)
+ return 0; /* fail */
+
+ return 1;
+} /* compat_X509_STORE_up_ref() */
+#endif
+
+#if !HAVE_X509_UP_REF
+#define X509_up_ref(...) EXPAND( compat_X509_up_ref(__VA_ARGS__) )
+
+static int compat_X509_up_ref(X509 *crt) {
+ /* our caller should already have had a proper reference */
+ if (CRYPTO_add(&crt->references, 1, CRYPTO_LOCK_X509) < 2)
+ return 0; /* fail */
+
+ return 1;
+} /* compat_X509_up_ref() */
+#endif
+
+#if !HAVE_X509_VERIFY_PARAM_SET1_EMAIL
+/*
+ * NB: Cannot emulate. Requires dereferencing X509_VERIFY_PARAM_ID objects,
+ * which were always opaque.
+ */
+#endif
+
+#if !HAVE_X509_VERIFY_PARAM_SET1_HOST
+/*
+ * NB: See HAVE_X509_VERIFY_PARAM_SET1_EMAIL.
+ */
+#endif
+
+/* compat_init must not be called from multiple threads at once */
+static int compat_init(void) {
+ static int store_index = -1, ssl_ctx_index = -1, done;
+ int error = 0;
+
+ if (done)
+ goto epilog;
+
+#if defined compat_X509_STORE_free
+ /*
+ * We need to unconditionally install at least one external
+ * application data callback. Because these can never be
+ * uninstalled, we can never be unloaded.
+ */
+ if ((error = dl_anchor()))
+ goto epilog;
+
+ /*
+ * Test if X509_STORE_free obeys reference counts by installing an
+ * onfree callback.
+ */
+ if (store_index == -1
+ && -1 == (store_index = CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE, 0, NULL, NULL, NULL, &compat_init_X509_STORE_onfree)))
+ goto sslerr;
+
+ if (!(compat.tmp.store = X509_STORE_new()))
+ goto sslerr;
+
+ CRYPTO_add(&compat.tmp.store->references, 1, CRYPTO_LOCK_X509_STORE);
+ X509_STORE_free(compat.tmp.store);
+
+ if (compat.tmp.store) {
+ /*
+ * Because our onfree callback didn't execute, we assume
+ * X509_STORE_free obeys reference counts. Alternatively,
+ * our callback might not have executed for some other
+ * reason. We assert the truth of our assumption by checking
+ * again after calling X509_STORE_free once more.
+ */
+ X509_STORE_free(compat.tmp.store);
+ assert(compat.tmp.store == NULL);
+ compat.tmp.store = NULL; /* in case assertions disabled */
+ } else {
+ /*
+ * Because our onfree callback was invoked, X509_STORE_free
+ * appears not to obey reference counts. Use our fixed
+ * version in our own code.
+ */
+ compat.X509_STORE_free = &compat_X509_STORE_free;
+
+ /*
+ * Ensure that our fixed version is called on SSL_CTX
+ * destruction.
+ *
+ * NB: We depend on the coincidental order of operations in
+ * SSL_CTX_free that user data destruction occurs before
+ * free'ing the cert_store member. Ruby's OpenSSL bindings
+ * also depend on this order as we both use the onfree
+ * callback to clear the member.
+ */
+ if (ssl_ctx_index == -1
+ && -1 == (ssl_ctx_index = CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_CTX, 0, NULL, NULL, NULL, &compat_init_SSL_CTX_onfree)))
+ goto sslerr;
+
+ compat.flags |= COMPAT_X509_STORE_FREE_BUG;
+ }
+#endif
+
+ done = 1;
+epilog:
+ if (compat.tmp.store) {
+ X509_STORE_free(compat.tmp.store);
+ compat.tmp.store = NULL;
+ }
+
+ return error;
+sslerr:
+ error = auxL_EOPENSSL;
+
+ goto epilog;
+} /* compat_init() */
+
+
+/*
+ * Auxiliary OpenSSL API routines (with dependencies on OpenSSL compat)
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static const EVP_MD *auxS_todigest(const char *name, EVP_PKEY *key, const EVP_MD *def) {
+ const EVP_MD *md;
+ int nid;
+
+ if (name) {
+ if ((md = EVP_get_digestbyname(name)))
+ return md;
+ } else if (key) {
+ if ((EVP_PKEY_get_default_digest_nid(key, &nid) > 0)) {
+ if ((md = EVP_get_digestbynid(nid)))
+ return md;
+ }
+ }
+
+ return def;
+} /* auxS_todigest() */
+
+
+/*
+ * Auxiliary Lua API routines (with dependencies on OpenSSL compat)
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static const EVP_MD *auxL_optdigest(lua_State *L, int index, EVP_PKEY *key, const EVP_MD *def) {
+ const char *name = luaL_optstring(L, index, NULL);
+ const EVP_MD *md;
+
+ if ((md = auxS_todigest(name, key, NULL)))
+ return md;
+
+ if (name) {
+ luaL_argerror(L, index, lua_pushfstring(L, "invalid digest type (%s)", name));
+ NOTREACHED;
+ } else if (key) {
+ luaL_argerror(L, index, lua_pushfstring(L, "no digest type for key type (%d)", EVP_PKEY_base_id(key)));
+ NOTREACHED;
+ }
+
+ return def;
+} /* auxL_optdigest() */
+
+
+/*
+ * External Application Data Hooks
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct ex_state {
+ lua_State *L;
+ LIST_HEAD(, ex_data) data;
+}; /* struct ex_state */
+
+#ifndef EX_DATA_MAXARGS
+#define EX_DATA_MAXARGS 8
+#endif
+
+struct ex_data {
+ struct ex_state *state;
+ int refs;
+ auxref_t arg[EX_DATA_MAXARGS];
+ LIST_ENTRY(ex_data) le;
+}; /* struct ex_data */
+
+enum {
+ EX_SSL_CTX_ALPN_SELECT_CB,
+ EX_SSL_CTX_TLSEXT_SERVERNAME_CB,
+};
+
+static struct ex_type {
+ int class_index; /* OpenSSL object type identifier */
+ int index; /* OpenSSL-allocated external data identifier */
+ void *(*get_ex_data)();
+ int (*set_ex_data)();
+} ex_type[] = {
+ [EX_SSL_CTX_ALPN_SELECT_CB] = { CRYPTO_EX_INDEX_SSL_CTX, -1, &SSL_CTX_get_ex_data, &SSL_CTX_set_ex_data },
+ [EX_SSL_CTX_TLSEXT_SERVERNAME_CB] = { CRYPTO_EX_INDEX_SSL_CTX, -1, &SSL_CTX_get_ex_data, &SSL_CTX_set_ex_data },
+};
+
+#if OPENSSL_PREREQ(1,1,0)
+typedef const CRYPTO_EX_DATA const_CRYPTO_EX_DATA;
+#else
+typedef CRYPTO_EX_DATA const_CRYPTO_EX_DATA;
+#endif
+
+static int ex_ondup(CRYPTO_EX_DATA *to NOTUSED, const_CRYPTO_EX_DATA *from NOTUSED, void *from_d, int idx NOTUSED, long argl NOTUSED, void *argp NOTUSED) {
+ struct ex_data **data = from_d;
+
+ if (*data)
+ (*data)->refs++;
+
+ return 1;
+} /* ex_ondup() */
+
+static void ex_onfree(void *parent NOTUSED, void *_data, CRYPTO_EX_DATA *ad NOTUSED, int idx NOTUSED, long argl NOTUSED, void *argp NOTUSED) {
+ struct ex_data *data = _data;
+
+ if (!data || --data->refs > 0)
+ return;
+
+ if (data->state) {
+ int i;
+
+ for (i = 0; i < (int)countof(data->arg); i++) {
+ auxL_unref(data->state->L, &data->arg[i]);
+ }
+
+ LIST_REMOVE(data, le);
+ }
+
+ free(data);
+} /* ex_onfree() */
+
+/* ex_init must not be called from multiple threads at once */
+static int ex_init(void) {
+ static int done;
+ struct ex_type *type;
+ int error = 0;
+
+ if (done)
+ goto epilog;
+
+ /*
+ * Our callbacks can never be uninstalled, so ensure we're never
+ * unloaded.
+ */
+ if ((error = dl_anchor()))
+ goto epilog;
+
+ for (type = ex_type; type < endof(ex_type); type++) {
+ if (type->index != -1)
+ continue;
+
+ if (-1 == (type->index = CRYPTO_get_ex_new_index(type->class_index, 0, NULL, NULL, &ex_ondup, &ex_onfree)))
+ goto sslerr;
+ };
+
+ done = 1;
+epilog:
+ return error;
+sslerr:
+ error = auxL_EOPENSSL;
+
+ goto epilog;
+} /* ex_init() */
+
+static int ex__gc(lua_State *L) {
+ struct ex_state *state = lua_touserdata(L, 1);
+ struct ex_data *data;
+
+ if (!state)
+ return 0;
+
+ /* invalidate back references to Lua state */
+ for (data = LIST_FIRST(&state->data); data; data = LIST_NEXT(data, le)) {
+ data->state = NULL;
+ }
+
+ return 0;
+} /* ex__gc() */
+
+static _Bool ex_hasstate(lua_State *L) {
+ _Bool has;
+
+ lua_pushlightuserdata(L, (void *)&ex__gc);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ has = !lua_isnil(L, -1);
+ lua_pop(L, 1);
+
+ return has;
+} /* ex_hasstate() */
+
+static void ex_newstate(lua_State *L) {
+ struct ex_state *state;
+ struct lua_State *thr;
+
+ if (ex_hasstate(L))
+ return;
+
+ state = prepudata(L, sizeof *state, NULL, &ex__gc);
+ LIST_INIT(&state->data);
+
+ /*
+ * XXX: Don't reuse mainthread because if an error occurs in a
+ * callback Lua might longjmp across the OpenSSL call stack.
+ * Instead, we'll install our own panic handlers.
+ */
+#if defined LUA_RIDX_MAINTHREAD
+ (void)thr;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
+ state->L = lua_tothread(L, -1);
+ lua_pop(L, 1);
+#else
+ lua_pushvalue(L, -1);
+ thr = lua_newthread(L);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ state->L = thr;
+#endif
+
+ lua_pushlightuserdata(L, (void *)&ex__gc);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_REGISTRYINDEX);
+
+ lua_pop(L, 1);
+} /* ex_newstate() */
+
+static struct ex_state *ex_getstate(lua_State *L) {
+ struct ex_state *state;
+
+ lua_pushlightuserdata(L, (void *)&ex__gc);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+
+ luaL_checktype(L, -1, LUA_TUSERDATA);
+ state = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ return state;
+} /* ex_getstate() */
+
+static size_t ex_getdata(lua_State **L, int _type, void *obj) {
+ struct ex_type *type = &ex_type[_type];
+ struct ex_data *data;
+ size_t i;
+
+ if (!(data = type->get_ex_data(obj, type->index)))
+ return 0;
+ if (!data->state)
+ return 0;
+
+ if (!*L)
+ *L = data->state->L;
+
+ if (!lua_checkstack(*L, countof(data->arg)))
+ return 0;
+
+ for (i = 0; i < countof(data->arg) && data->arg[i] != LUA_NOREF; i++) {
+ lua_rawgeti(*L, LUA_REGISTRYINDEX, data->arg[i]);
+ }
+
+ return i;
+} /* ex_getdata() */
+
+/* returns 0 on success, otherwise error (>0 == errno, -1 == OpenSSL error) */
+static int ex_setdata(lua_State *L, int _type, void *obj, size_t n) {
+ struct ex_type *type = &ex_type[_type];
+ struct ex_state *state;
+ struct ex_data *data;
+ size_t i, j;
+
+ if (n > countof(data->arg))
+ return EOVERFLOW;
+
+ if ((data = type->get_ex_data(obj, type->index)) && data->state) {
+ for (i = 0; i < countof(data->arg); i++) {
+ auxL_unref(L, &data->arg[i]);
+ }
+ } else {
+ state = ex_getstate(L);
+
+ if (!(data = malloc(sizeof *data)))
+ return errno;
+
+ if (!type->set_ex_data(obj, type->index, data))
+ return auxL_EOPENSSL;
+
+ data->state = state;
+ data->refs = 1;
+ for (i = 0; i < countof(data->arg); i++)
+ data->arg[i] = LUA_NOREF;
+ LIST_INSERT_HEAD(&state->data, data, le);
+ }
+
+ for (i = n, j = 0; i > 0 && j < countof(data->arg); i--, j++) {
+ auxL_ref(L, -(int)i, &data->arg[j]);
+ }
+
+ lua_pop(L, n);
+
+ return 0;
+} /* ex_setdata() */
+
+static void initall(lua_State *L);
+
+
+/*
+ * compat - Lua OpenSSL
+ *
+ * Bindings to our internal feature detection, compatability, and workaround
+ * code.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+EXPORT int luaopen__openssl_compat(lua_State *L) {
+ initall(L);
+
+ lua_newtable(L);
+ lua_pushboolean(L, !!(compat.flags & COMPAT_X509_STORE_FREE_BUG));
+ lua_setfield(L, -2, "X509_STORE_FREE_BUG");
+
+ return 1;
+} /* luaopen__openssl_compat() */
+
+
+/*
+ * OPENSSL - openssl
+ *
+ * Miscellaneous global interfaces.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int ossl_version(lua_State *L) {
+ if (lua_isnoneornil(L, 1)) {
+ auxL_pushunsigned(L, SSLeay());
+ } else {
+ lua_pushstring(L, SSLeay_version(auxL_checkinteger(L, 1, INT_MIN, INT_MAX)));
+ }
+
+ return 1;
+} /* ossl_version() */
+
+static const auxL_Reg ossl_globals[] = {
+ { "version", &ossl_version },
+ { NULL, NULL },
+};
+
+/*
+ * NOTE: Compile-time cipher exclusions from openssl-1.0.1i/util/mkdef.pl.
+ */
+static const char opensslconf_no[][20] = {
+#ifdef OPENSSL_NO_RC2
+ { "NO_RC2" },
+#endif
+#ifdef OPENSSL_NO_RC4
+ { "NO_RC4" },
+#endif
+#ifdef OPENSSL_NO_RC5
+ { "NO_RC5" },
+#endif
+#ifdef OPENSSL_NO_IDEA
+ { "NO_IDEA" },
+#endif
+#ifdef OPENSSL_NO_DES
+ { "NO_DES" },
+#endif
+#ifdef OPENSSL_NO_BF
+ { "NO_BF" },
+#endif
+#ifdef OPENSSL_NO_CAST
+ { "NO_CAST" },
+#endif
+#ifdef OPENSSL_NO_WHIRLPOOL
+ { "NO_WHIRLPOOL" },
+#endif
+#ifdef OPENSSL_NO_CAMELLIA
+ { "NO_CAMELLIA" },
+#endif
+#ifdef OPENSSL_NO_SEED
+ { "NO_SEED" },
+#endif
+#ifdef OPENSSL_NO_MD2
+ { "NO_MD2" },
+#endif
+#ifdef OPENSSL_NO_MD4
+ { "NO_MD4" },
+#endif
+#ifdef OPENSSL_NO_MD5
+ { "NO_MD5" },
+#endif
+#ifdef OPENSSL_NO_SHA
+ { "NO_SHA" },
+#endif
+#ifdef OPENSSL_NO_RIPEMD
+ { "NO_RIPEMD" },
+#endif
+#ifdef OPENSSL_NO_MDC2
+ { "NO_MDC2" },
+#endif
+#ifdef OPENSSL_NO_RSA
+ { "NO_RSA" },
+#endif
+#ifdef OPENSSL_NO_DSA
+ { "NO_DSA" },
+#endif
+#ifdef OPENSSL_NO_DH
+ { "NO_DH" },
+#endif
+#ifdef OPENSSL_NO_HMAC
+ { "NO_HMAC" },
+#endif
+#ifdef OPENSSL_NO_AES
+ { "NO_AES" },
+#endif
+#ifdef OPENSSL_NO_KRB5
+ { "NO_KRB5" },
+#endif
+#ifdef OPENSSL_NO_EC
+ { "NO_EC" },
+#endif
+#ifdef OPENSSL_NO_ECDSA
+ { "NO_ECDSA" },
+#endif
+#ifdef OPENSSL_NO_ECDH
+ { "NO_ECDH" },
+#endif
+#ifdef OPENSSL_NO_ENGINE
+ { "NO_ENGINE" },
+#endif
+#ifdef OPENSSL_NO_HW
+ { "NO_HW" },
+#endif
+#ifdef OPENSSL_NO_FP_API
+ { "NO_FP_API" },
+#endif
+#ifdef OPENSSL_NO_STATIC_ENGINE
+ { "NO_STATIC_ENGINE" },
+#endif
+#ifdef OPENSSL_NO_GMP
+ { "NO_GMP" },
+#endif
+#ifdef OPENSSL_NO_DEPRECATED
+ { "NO_DEPRECATED" },
+#endif
+#ifdef OPENSSL_NO_RFC3779
+ { "NO_RFC3779" },
+#endif
+#ifdef OPENSSL_NO_PSK
+ { "NO_PSK" },
+#endif
+#ifdef OPENSSL_NO_TLSEXT
+ { "NO_TLSEXT" },
+#endif
+#ifdef OPENSSL_NO_CMS
+ { "NO_CMS" },
+#endif
+#ifdef OPENSSL_NO_CAPIENG
+ { "NO_CAPIENG" },
+#endif
+#ifdef OPENSSL_NO_JPAKE
+ { "NO_JPAKE" },
+#endif
+#ifdef OPENSSL_NO_SRP
+ { "NO_SRP" },
+#endif
+#ifdef OPENSSL_NO_SSL2
+ { "NO_SSL2" },
+#endif
+#ifdef OPENSSL_NO_EC2M
+ { "NO_EC2M" },
+#endif
+#ifdef OPENSSL_NO_NISTP_GCC
+ { "NO_NISTP_GCC" },
+#endif
+#ifdef OPENSSL_NO_NEXTPROTONEG
+ { "NO_NEXTPROTONEG" },
+#endif
+#ifdef OPENSSL_NO_SCTP
+ { "NO_SCTP" },
+#endif
+#ifdef OPENSSL_NO_UNIT_TEST
+ { "NO_UNIT_TEST" },
+#endif
+ { "" } /* in case nothing is defined above */
+}; /* opensslconf_no[] */
+
+static const auxL_IntegerReg ssleay_version[] = {
+#ifdef SSLEAY_VERSION_NUMBER
+ { "SSLEAY_VERSION_NUMBER", SSLEAY_VERSION_NUMBER },
+#endif
+#ifdef SSLEAY_VERSION
+ { "SSLEAY_VERSION", SSLEAY_VERSION },
+#endif
+#ifdef SSLEAY_OPTIONS
+ { "SSLEAY_OPTIONS", SSLEAY_OPTIONS },
+#endif
+#ifdef SSLEAY_CFLAGS
+ { "SSLEAY_CFLAGS", SSLEAY_CFLAGS },
+#endif
+#ifdef SSLEAY_BUILT_ON
+ { "SSLEAY_BUILT_ON", SSLEAY_BUILT_ON },
+#endif
+#ifdef SSLEAY_PLATFORM
+ { "SSLEAY_PLATFORM", SSLEAY_PLATFORM },
+#endif
+#ifdef SSLEAY_DIR
+ { "SSLEAY_DIR", SSLEAY_DIR },
+#endif
+ { NULL, 0 },
+};
+
+EXPORT int luaopen__openssl(lua_State *L) {
+ size_t i;
+
+ auxL_newlib(L, ossl_globals, 0);
+
+ for (i = 0; i < countof(opensslconf_no); i++) {
+ if (*opensslconf_no[i]) {
+ lua_pushboolean(L, 1);
+ lua_setfield(L, -2, opensslconf_no[i]);
+ }
+ }
+
+ auxL_setintegers(L, ssleay_version);
+
+ auxL_pushinteger(L, OPENSSL_VERSION_NUMBER);
+ lua_setfield(L, -2, "VERSION_NUMBER");
+
+ lua_pushstring(L, OPENSSL_VERSION_TEXT);
+ lua_setfield(L, -2, "VERSION_TEXT");
+
+ lua_pushstring(L, SHLIB_VERSION_HISTORY);
+ lua_setfield(L, -2, "SHLIB_VERSION_HISTORY");
+
+ lua_pushstring(L, SHLIB_VERSION_NUMBER);
+ lua_setfield(L, -2, "SHLIB_VERSION_NUMBER");
+
+#if defined LIBRESSL_VERSION_NUMBER
+ auxL_pushinteger(L, LIBRESSL_VERSION_NUMBER);
+ lua_setfield(L, -2, "LIBRESSL_VERSION_NUMBER");
+#endif
+
+ return 1;
+} /* luaopen__openssl() */
+
+
+/*
+ * BIGNUM - openssl.bignum
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static BIGNUM *bn_push(lua_State *L) {
+ BIGNUM **ud = prepsimple(L, BIGNUM_CLASS);
+
+ if (!(*ud = BN_new()))
+ auxL_error(L, auxL_EOPENSSL, "bignum.new");
+
+ return *ud;
+} /* bn_push() */
+
+
+static BIGNUM *bn_dup(lua_State *L, const BIGNUM *src) {
+ BIGNUM **ud = prepsimple(L, BIGNUM_CLASS);
+
+ if (!(*ud = BN_dup(src)))
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+
+ return *ud;
+} /* bn_dup() */
+
+
+static BIGNUM *bn_dup_nil(lua_State *L, const BIGNUM *src) {
+ return (src)? bn_dup(L, src) : (lua_pushnil(L), (BIGNUM *)0);
+} /* bn_dup_nil() */
+
+
+#define checkbig_(a, b, c, ...) checkbig((a), (b), (c))
+#define checkbig(...) EXPAND( checkbig_(__VA_ARGS__, &(_Bool){ 0 }, 0) )
+
+static BIGNUM *(checkbig)(lua_State *, int, _Bool *);
+
+static int bn_new(lua_State *L) {
+ int i, n;
+
+ if ((n = lua_gettop(L)) > 0) {
+ for (i = 1; i <= n; i++)
+ checkbig(L, i);
+
+ return n;
+ } else {
+ bn_push(L);
+
+ return 1;
+ }
+} /* bn_new() */
+
+
+static int bn_fromBinary(lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ BIGNUM *bn = bn_push(L);
+ if (!BN_bin2bn((const unsigned char*)s, len, bn)) {
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+ }
+ return 1;
+} /* bn_fromBinary() */
+
+
+static int bn_interpose(lua_State *L) {
+ return interpose(L, BIGNUM_CLASS);
+} /* bn_interpose() */
+
+
+/* return integral part */
+static inline double intof(double f) {
+ return (isfinite(f))? floor(fabs(f)) : 0.0;
+} /* intof() */
+
+
+/* convert integral to BN_ULONG. returns success or failure. */
+static _Bool int2ul(BN_ULONG *ul, double f) {
+ int exp;
+
+ frexp(f, &exp);
+
+ if (exp > (int)sizeof *ul * 8)
+ return 0;
+
+ *ul = (BN_ULONG)f;
+
+ return 1;
+} /* int2ul() */
+
+
+/* convert integral BIGNUM. returns success or failure. */
+static _Bool int2bn(BIGNUM **bn, double q) {
+ unsigned char nib[32], bin[32], *p;
+ size_t i, n;
+ double r;
+
+ p = nib;
+
+ while (q >= 1.0 && p < endof(nib)) {
+ r = fmod(q, 256.0);
+ *p++ = r;
+ q = round((q - r) / 256.0);
+ }
+
+ n = p - nib;
+
+ for (i = 0; i < n; i++) {
+ bin[i] = *--p;
+ }
+
+ if (!(*bn = BN_bin2bn(bin, n, *bn)))
+ return 0;
+
+ return 1;
+} /* int2bn() */
+
+
+/* convert double to BIGNUM. returns success or failure. */
+static _Bool f2bn(BIGNUM **bn, double f) {
+ double i = intof(f);
+ BN_ULONG lu;
+
+ if (int2ul(&lu, i)) {
+ if (!*bn && !(*bn = BN_new()))
+ return 0;
+
+ if (!BN_set_word(*bn, lu))
+ return 0;
+ } else if (!int2bn(bn, i))
+ return 0;
+
+ BN_set_negative(*bn, signbit(f));
+
+ return 1;
+} /* f2bn() */
+
+
+static BIGNUM *(checkbig)(lua_State *L, int index, _Bool *lvalue) {
+ BIGNUM **bn;
+ const char *str;
+ size_t len, i;
+ _Bool neg, hex;
+
+ index = lua_absindex(L, index);
+
+ switch (lua_type(L, index)) {
+ case LUA_TSTRING:
+ *lvalue = 0;
+
+ str = lua_tolstring(L, index, &len);
+
+ neg = (str[0] == '-');
+ hex = (str[neg] == '0' && (str[neg+1] == 'x' || str[neg+1] == 'X'));
+
+ if (hex) {
+ luaL_argcheck(L, len > 2+(size_t)neg, index, "invalid hex string");
+ for (i = 2+neg; i < len; i++) {
+ if (!isxdigit((unsigned char)str[i]))
+ luaL_argerror(L, 1, "invalid hex string");
+ }
+ } else {
+ luaL_argcheck(L, len > neg, index, "invalid decimal string");
+ for (i = neg; i < len; i++) {
+ if (!isdigit((unsigned char)str[i]))
+ luaL_argerror(L, 1, "invalid decimal string");
+ }
+ }
+
+ bn = prepsimple(L, BIGNUM_CLASS);
+
+ if (hex) {
+ if (!BN_hex2bn(bn, str+2+neg))
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+ if (neg)
+ BN_set_negative(*bn, 1);
+ } else {
+ if (!BN_dec2bn(bn, str))
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+ }
+
+ lua_replace(L, index);
+
+ return *bn;
+ case LUA_TNUMBER:
+ *lvalue = 0;
+
+ bn = prepsimple(L, BIGNUM_CLASS);
+
+#if LUA_VERSION_NUM >= 503
+ if (lua_isinteger(L, index)) {
+ lua_Integer n = lua_tointeger(L, index);
+ auxL_Unsigned lu;
+
+ if (!*bn && !(*bn = BN_new()))
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+
+ neg = n < 0;
+ lu = neg ? (0 - n) : n;
+
+ if (!BN_set_word(*bn, lu))
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+
+ if (neg)
+ BN_set_negative(*bn, 1);
+ } else
+#endif
+ if (!f2bn(bn, lua_tonumber(L, index)))
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+
+ lua_replace(L, index);
+
+ return *bn;
+ default:
+ *lvalue = 1;
+
+ return checksimple(L, index, BIGNUM_CLASS);
+ } /* switch() */
+} /* checkbig() */
+
+
+/* prepare number at top of stack for unary operation, and push result object onto stack */
+static void bn_prepuop(lua_State *L, BIGNUM **r, BIGNUM **a, _Bool commute) {
+ _Bool lvalue = 1;
+
+ *a = checkbig(L, -1, &lvalue);
+
+ if (!lvalue && commute) {
+ lua_pushvalue(L, -1);
+ } else {
+ bn_push(L);
+ }
+
+ *r = *(BIGNUM **)lua_touserdata(L, -1);
+} /* bn_prepuop() */
+
+
+/* prepare numbers at top of stack for binary operation, and push result object onto stack */
+static void bn_prepbop(lua_State *L, BIGNUM **r, BIGNUM **a, BIGNUM **b, _Bool commute) {
+ _Bool a_lvalue, b_lvalue;
+
+ *a = checkbig(L, -2, &a_lvalue);
+ *b = checkbig(L, -1, &b_lvalue);
+
+ if (commute && !a_lvalue) {
+ lua_pushvalue(L, -2);
+ } else if (commute && !b_lvalue) {
+ lua_pushvalue(L, -1);
+ } else {
+ bn_push(L);
+ }
+
+ *r = *(BIGNUM **)lua_touserdata(L, -1);
+} /* bn_prepbop() */
+
+
+/* prepare numbers at top of stack for ternary operation, and push result object onto stack */
+static void bn_preptop(lua_State *L, BIGNUM **r, BIGNUM **a, BIGNUM **b, BIGNUM **c) {
+ _Bool a_lvalue, b_lvalue, c_lvalue;
+
+ *a = checkbig(L, 1, &a_lvalue);
+ *b = checkbig(L, 2, &b_lvalue);
+ *c = checkbig(L, 3, &c_lvalue);
+
+ bn_push(L);
+
+ *r = *(BIGNUM **)lua_touserdata(L, -1);
+} /* bn_preptop() */
+
+
+static int ctx__gc(lua_State *L) {
+ BN_CTX **ctx = lua_touserdata(L, 1);
+
+ if (*ctx) {
+ BN_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+
+ return 0;
+} /* ctx__gc() */
+
+static BN_CTX *getctx(lua_State *L) {
+ BN_CTX **ctx;
+
+ lua_pushlightuserdata(L, (void *)&ctx__gc);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+
+ ctx = prepsimple(L, NULL, &ctx__gc);
+
+ if (!(*ctx = BN_CTX_new()))
+ auxL_error(L, auxL_EOPENSSL, "bignum");
+
+ lua_pushlightuserdata(L, (void *)&ctx__gc);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+
+ ctx = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ return *ctx;
+} /* getctx() */
+
+
+static int bn_toBinary(lua_State *L) {
+ BIGNUM *bn = checksimple(L, 1, BIGNUM_CLASS);
+ size_t len;
+ void *dst;
+
+ len = BN_num_bytes(bn);
+ dst = lua_newuserdata(L, len);
+ BN_bn2bin(bn, dst);
+ lua_pushlstring(L, dst, len);
+
+ return 1;
+} /* bn_toBinary() */
+
+
+static int bn__add(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 1);
+
+ if (!BN_add(r, a, b))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__add");
+
+ return 1;
+} /* bn__add() */
+
+
+static int bn__sub(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 0);
+
+ if (!BN_sub(r, a, b))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__sub");
+
+ return 1;
+} /* bn__sub() */
+
+
+static int bn__mul(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 1);
+
+ if (!BN_mul(r, a, b, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__mul");
+
+ return 1;
+} /* bn__mul() */
+
+
+static int bn_sqr(lua_State *L) {
+ BIGNUM *r, *a;
+
+ lua_settop(L, 1);
+ bn_prepuop(L, &r, &a, 1);
+
+ if (!BN_sqr(r, a, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:sqr");
+
+ return 1;
+} /* bn_sqr() */
+
+
+static int bn__idiv(lua_State *L) {
+ BIGNUM *dv, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &dv, &a, &b, 0);
+
+ if (!BN_div(dv, NULL, a, b, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__idiv");
+
+ return 1;
+} /* bn__idiv() */
+
+
+static int bn__mod(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 0);
+
+ if (!BN_mod(r, a, b, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__mod");
+
+ /* lua has different rounding behaviour for mod than C */
+ if (!BN_is_zero(r) && (BN_is_negative(a) ^ BN_is_negative(b))) {
+ if (!BN_add(r, r, b))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__mod");
+ }
+
+ return 1;
+} /* bn__mod() */
+
+
+static int bn_nnmod(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 0);
+
+ if (!BN_nnmod(r, a, b, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:nnmod");
+
+ return 1;
+} /* bn_nnmod() */
+
+
+static int bn_mod_add(lua_State *L) {
+ BIGNUM *r, *a, *b, *c;
+
+ lua_settop(L, 3);
+ bn_preptop(L, &r, &a, &b, &c);
+
+ if (!BN_mod_add(r, a, b, c, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:mod_add");
+
+ return 1;
+} /* bn_mod_add() */
+
+
+static int bn_mod_sub(lua_State *L) {
+ BIGNUM *r, *a, *b, *c;
+
+ lua_settop(L, 3);
+ bn_preptop(L, &r, &a, &b, &c);
+
+ if (!BN_mod_sub(r, a, b, c, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:mod_sub");
+
+ return 1;
+} /* bn_mod_sub() */
+
+
+static int bn_mod_mul(lua_State *L) {
+ BIGNUM *r, *a, *b, *c;
+
+ lua_settop(L, 3);
+ bn_preptop(L, &r, &a, &b, &c);
+
+ if (!BN_mod_mul(r, a, b, c, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:mod_mul");
+
+ return 1;
+} /* bn_mod_mul() */
+
+
+static int bn_mod_sqr(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 0);
+
+ if (!BN_mod_sqr(r, a, b, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:mod_sqr");
+
+ return 1;
+} /* bn_mod_sqr() */
+
+
+static int bn__pow(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 0);
+
+ if (!BN_exp(r, a, b, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__pow");
+
+ return 1;
+} /* bn__pow() */
+
+
+static int bn_mod_exp(lua_State *L) {
+ BIGNUM *r, *a, *b, *c;
+
+ lua_settop(L, 3);
+ bn_preptop(L, &r, &a, &b, &c);
+
+ if (!BN_mod_exp(r, a, b, c, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:mod_exp");
+
+ return 1;
+} /* bn_mod_exp() */
+
+
+static int bn_gcd(lua_State *L) {
+ BIGNUM *r, *a, *b;
+
+ lua_settop(L, 2);
+ bn_prepbop(L, &r, &a, &b, 1);
+
+ if (!BN_gcd(r, a, b, getctx(L)))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:gcd");
+
+ return 1;
+} /* bn_gcd() */
+
+
+static int bn__shl(lua_State *L) {
+ BIGNUM *r, *a;
+ int n;
+
+ a = checkbig(L, 1);
+ n = luaL_checkinteger(L, 2);
+ r = bn_push(L);
+
+ if (!BN_lshift(r, a, n))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__shl");
+
+ return 1;
+} /* bn__shl() */
+
+
+static int bn__shr(lua_State *L) {
+ BIGNUM *r, *a;
+ int n;
+
+ a = checkbig(L, 1);
+ n = luaL_checkinteger(L, 2);
+ r = bn_push(L);
+
+ if (!BN_rshift(r, a, n))
+ return auxL_error(L, auxL_EOPENSSL, "bignum:__shr");
+
+ return 1;
+} /* bn__shr() */
+
+
+static int bn__unm(lua_State *L) {
+ BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS);
+ BIGNUM *r = bn_dup(L, a);
+
+ BN_set_negative(r, !BN_is_negative(a));
+
+ return 1;
+} /* bn__unm() */
+
+
+static int bn__eq(lua_State *L) {
+ BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS);
+ BIGNUM *b = checksimple(L, 2, BIGNUM_CLASS);
+
+ lua_pushboolean(L, 0 == BN_cmp(a, b));
+
+ return 1;
+} /* bn__eq() */
+
+
+static int bn__lt(lua_State *L) {
+ BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS);
+ BIGNUM *b = checksimple(L, 2, BIGNUM_CLASS);
+ int cmp = BN_cmp(a, b);
+
+ lua_pushboolean(L, cmp == -1);
+
+ return 1;
+} /* bn__lt() */
+
+
+static int bn__le(lua_State *L) {
+ BIGNUM *a = checksimple(L, 1, BIGNUM_CLASS);
+ BIGNUM *b = checksimple(L, 2, BIGNUM_CLASS);
+ int cmp = BN_cmp(a, b);
+
+ lua_pushboolean(L, cmp <= 0);
+
+ return 1;
+} /* bn__le() */
+
+
+static int bn__gc(lua_State *L) {
+ BIGNUM **ud = luaL_checkudata(L, 1, BIGNUM_CLASS);
+
+ if (*ud) {
+ BN_clear_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* bn__gc() */
+
+
+static int bn_generatePrime(lua_State *L) {
+ int bits = luaL_checkinteger(L, 1);
+ _Bool safe = optbool(L, 2, 0);
+ const BIGNUM *add = lua_isnoneornil(L, 3) ? NULL : checkbig(L, 3);
+ const BIGNUM *rem = lua_isnoneornil(L, 4) ? NULL : checkbig(L, 4);
+ BIGNUM *bn = bn_push(L);
+
+ if (!BN_generate_prime_ex(bn, bits, safe, add, rem, NULL))
+ return auxL_error(L, auxL_EOPENSSL, "bignum.generatePrime");
+
+ return 1;
+} /* bn_generatePrime() */
+
+
+static int bn_isPrime(lua_State *L) {
+ BIGNUM *bn = checksimple(L, 1, BIGNUM_CLASS);
+ int nchecks = luaL_optinteger(L, 2, BN_prime_checks);
+ int res = BN_is_prime_ex(bn, nchecks, getctx(L), NULL);
+
+ if (res == -1)
+ return auxL_error(L, auxL_EOPENSSL, "bignum:isPrime");
+
+ lua_pushboolean(L, res);
+
+ return 1;
+} /* bn_isPrime() */
+
+
+static BIO *getbio(lua_State *);
+
+static int bn_toDecimal(lua_State *L) {
+ BIGNUM *bn = checksimple(L, 1, BIGNUM_CLASS);
+ char *txt = NULL;
+ BIO *bio;
+ BUF_MEM *buf;
+
+ if (!(txt = BN_bn2dec(bn)))
+ goto sslerr;
+
+ /* use GC-visible BIO as temporary buffer */
+ bio = getbio(L);
+
+ if (BIO_puts(bio, txt) < 0)
+ goto sslerr;
+
+ OPENSSL_free(txt);
+ txt = NULL;
+
+ BIO_get_mem_ptr(bio, &buf);
+ lua_pushlstring(L, buf->data, buf->length);
+
+ return 1;
+sslerr:
+ OPENSSL_free(txt);
+
+ return auxL_error(L, auxL_EOPENSSL, "bignum:toDecimal");
+} /* bn_toDecimal() */
+
+
+static int bn_toHex(lua_State *L) {
+ BIGNUM *bn = checksimple(L, 1, BIGNUM_CLASS);
+ char *txt = NULL;
+ BIO *bio;
+ BUF_MEM *buf;
+
+ if (!(txt = BN_bn2hex(bn)))
+ goto sslerr;
+
+ /* use GC-visible BIO as temporary buffer */
+ bio = getbio(L);
+
+ if (BIO_puts(bio, txt) < 0)
+ goto sslerr;
+
+ OPENSSL_free(txt);
+ txt = NULL;
+
+ BIO_get_mem_ptr(bio, &buf);
+ lua_pushlstring(L, buf->data, buf->length);
+
+ return 1;
+sslerr:
+ OPENSSL_free(txt);
+
+ return auxL_error(L, auxL_EOPENSSL, "bignum:toHex");
+} /* bn_toHex() */
+
+
+static const auxL_Reg bn_methods[] = {
+ { "add", &bn__add },
+ { "sub", &bn__sub },
+ { "mul", &bn__mul },
+ { "sqr", &bn_sqr },
+ { "idiv", &bn__idiv },
+ { "mod", &bn__mod },
+ { "nnmod", &bn_nnmod },
+ { "mod_add", &bn_mod_add },
+ { "mod_sub", &bn_mod_sub },
+ { "mod_mul", &bn_mod_mul },
+ { "mod_sqr", &bn_mod_sqr },
+ { "exp", &bn__pow },
+ { "mod_exp", &bn_mod_exp },
+ { "gcd", &bn_gcd },
+ { "lshift", &bn__shl },
+ { "rshift", &bn__shr },
+ { "isPrime", &bn_isPrime },
+ { "toBinary", &bn_toBinary },
+ { "toDecimal", &bn_toDecimal },
+ { "toHex", &bn_toHex },
+ /* deprecated */
+ { "tobin", &bn_toBinary },
+ { "todec", &bn_toDecimal },
+ { "tohex", &bn_toHex },
+ { NULL, NULL },
+};
+
+static const auxL_Reg bn_metatable[] = {
+ { "__add", &bn__add },
+ { "__sub", &bn__sub },
+ { "__mul", &bn__mul },
+ { "__div", &bn__idiv },
+ { "__idiv", &bn__idiv },
+ { "__mod", &bn__mod },
+ { "__pow", &bn__pow },
+ { "__unm", &bn__unm },
+ { "__shl", &bn__shl },
+ { "__shr", &bn__shr },
+ { "__eq", &bn__eq },
+ { "__lt", &bn__lt },
+ { "__le", &bn__le },
+ { "__gc", &bn__gc },
+ { "__tostring", &bn_toDecimal },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg bn_globals[] = {
+ { "new", &bn_new },
+ { "interpose", &bn_interpose },
+ { "fromBinary", &bn_fromBinary },
+ { "generatePrime", &bn_generatePrime },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_bignum(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, bn_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_bignum() */
+
+
+/*
+ * EVP_PKEY - openssl.pkey
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int bio__gc(lua_State *L) {
+ BIO **bio = lua_touserdata(L, 1);
+
+ if (*bio) {
+ BIO_free(*bio);
+ *bio = NULL;
+ }
+
+ return 0;
+} /* bio__gc() */
+
+static BIO *getbio(lua_State *L) {
+ BIO **bio;
+
+ lua_pushlightuserdata(L, (void *)&bio__gc);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+
+ bio = prepsimple(L, NULL, &bio__gc);
+
+ if (!(*bio = BIO_new(BIO_s_mem())))
+ auxL_error(L, auxL_EOPENSSL, "BIO_new");
+
+ lua_pushlightuserdata(L, (void *)&bio__gc);
+ lua_pushvalue(L, -2);
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+
+ bio = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ BIO_reset(*bio);
+
+ return *bio;
+} /* getbio() */
+
+
+static int pk_new(lua_State *L) {
+ EVP_PKEY **ud;
+
+ /* #1 table or key; if key, #2 format and #3 type */
+ lua_settop(L, 3);
+
+ if (lua_istable(L, 1) || lua_isnil(L, 1)) {
+ int type = EVP_PKEY_RSA;
+ unsigned bits = 1024;
+ BIGNUM *exp = NULL;
+ int generator = 2;
+ int curve = NID_X9_62_prime192v1;
+ const char *id;
+ const char *dhparam = NULL;
+ lua_Number n;
+
+ if (lua_istable(L, 1)) {
+ if (loadfield(L, 1, "type", LUA_TSTRING, (void*)&id)) {
+ static const struct { int nid; const char *sn; } types[] = {
+ { EVP_PKEY_RSA, "RSA" },
+ { EVP_PKEY_DSA, "DSA" },
+ { EVP_PKEY_DH, "DH" },
+ { EVP_PKEY_EC, "EC" },
+ };
+ unsigned i;
+
+ if (NID_undef == (type = EVP_PKEY_type(OBJ_sn2nid(id)))) {
+ for (i = 0; i < countof(types); i++) {
+ if (strieq(id, types[i].sn)) {
+ type = types[i].nid;
+ break;
+ }
+ }
+ }
+
+ luaL_argcheck(L, type != NID_undef, 1, lua_pushfstring(L, "%s: invalid key type", id));
+ }
+
+ switch(type) {
+ case EVP_PKEY_RSA:
+ if (loadfield(L, 1, "bits", LUA_TNUMBER, &n)) {
+ luaL_argcheck(L, n > 0 && n < UINT_MAX, 1, lua_pushfstring(L, "%f: `bits' invalid", n));
+ bits = (unsigned)n;
+ }
+
+ if (getfield(L, 1, "exp")) {
+ exp = checkbig(L, -1);
+ }
+ break;
+ case EVP_PKEY_DH:
+ /* dhparam field can contain a PEM encoded string.
+ The "dhparam" field takes precedence over "bits" */
+ if (loadfield(L, 1, "dhparam", LUA_TSTRING, (void*)&dhparam))
+ break;
+
+ if (loadfield(L, 1, "bits", LUA_TNUMBER, &n)) {
+ luaL_argcheck(L, n > 0 && n < UINT_MAX, 1, lua_pushfstring(L, "%f: `bits' invalid", n));
+ bits = (unsigned)n;
+ }
+
+ /* compat: DH used to use the 'exp' field for the generator */
+ if (loadfield(L, 1, "generator", LUA_TNUMBER, &n) || loadfield(L, 1, "exp", LUA_TNUMBER, &n)) {
+ luaL_argcheck(L, n > 0 && n <= INT_MAX, 1, lua_pushfstring(L, "%f: `exp' invalid", n));
+ generator = (int)n;
+ }
+ break;
+ case EVP_PKEY_EC:
+ if (loadfield(L, 1, "curve", LUA_TSTRING, (void*)&id)) {
+ if (!auxS_txt2nid(&curve, id))
+ luaL_argerror(L, 1, lua_pushfstring(L, "%s: invalid curve", id));
+ }
+ break;
+ }
+ }
+
+ /* defaults that require allocation */
+ switch (type) {
+ case EVP_PKEY_RSA:
+ if(!exp) {
+ /* default to 65537 */
+ exp = bn_push(L);
+ if (!BN_add_word(exp, 65537))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+ break;
+ }
+
+ ud = prepsimple(L, PKEY_CLASS);
+
+ if (!(*ud = EVP_PKEY_new()))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ switch (type) {
+ case EVP_PKEY_RSA: {
+ RSA *rsa;
+
+ if (!(rsa = RSA_new()))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ if (!RSA_generate_key_ex(rsa, bits, exp, 0)) {
+ RSA_free(rsa);
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+
+ EVP_PKEY_set1_RSA(*ud, rsa);
+
+ RSA_free(rsa);
+
+ break;
+ }
+ case EVP_PKEY_DSA: {
+ DSA *dsa;
+
+ if (!(dsa = DSA_new()))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ if (!DSA_generate_parameters_ex(dsa, bits, 0, 0, 0, 0, 0)) {
+ DSA_free(dsa);
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+
+ if (!DSA_generate_key(dsa)) {
+ DSA_free(dsa);
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+
+ EVP_PKEY_set1_DSA(*ud, dsa);
+
+ DSA_free(dsa);
+
+ break;
+ }
+ case EVP_PKEY_DH: {
+ DH *dh;
+
+ /* DH Parameter Generation can take a long time, therefore we look
+ * at the "dhparam" field, provided by the user.
+ * The "dhparam" field takes precedence over "bits"
+ */
+ if (dhparam) {
+ BIO *bio = BIO_new_mem_buf((void*)dhparam, strlen(dhparam));
+ if (!bio)
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ if (!dh)
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ } else {
+ if (!(dh = DH_new()))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ if (!DH_generate_parameters_ex(dh, bits, generator, 0)) {
+ DH_free(dh);
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+ }
+
+
+ if (!DH_generate_key(dh)) {
+ DH_free(dh);
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+
+ EVP_PKEY_set1_DH(*ud, dh);
+
+ DH_free(dh);
+
+ break;
+ }
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC: {
+ EC_GROUP *grp;
+ EC_KEY *key;
+
+ if (!(grp = EC_GROUP_new_by_curve_name(curve)))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ EC_GROUP_set_asn1_flag(grp, OPENSSL_EC_NAMED_CURVE);
+
+ /* compressed points patented */
+ EC_GROUP_set_point_conversion_form(grp, POINT_CONVERSION_UNCOMPRESSED);
+
+ if (!(key = EC_KEY_new())) {
+ EC_GROUP_free(grp);
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+
+ EC_KEY_set_group(key, grp);
+
+ EC_GROUP_free(grp);
+
+ if (!EC_KEY_generate_key(key)) {
+ EC_KEY_free(key);
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+ }
+
+ EVP_PKEY_set1_EC_KEY(*ud, key);
+
+ EC_KEY_free(key);
+
+ break;
+ }
+#endif
+ default:
+ return luaL_error(L, "%d: unsupported EVP_PKEY base type", EVP_PKEY_type(type));
+ } /* switch() */
+ } else if (lua_isstring(L, 1)) {
+ int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER);
+ int pubonly = 0, prvtonly = 0;
+ const char *opt, *data;
+ size_t len;
+ BIO *bio;
+ EVP_PKEY *pub = NULL, *prvt = NULL;
+ int goterr = 0;
+
+ /* check if specified publickey or privatekey */
+ if ((opt = luaL_optstring(L, 3, NULL))) {
+ if (xtolower(opt[0]) == 'p' && xtolower(opt[1]) == 'u') {
+ pubonly = 1;
+ } else if (xtolower(opt[0]) == 'p' && xtolower(opt[1]) == 'r') {
+ prvtonly = 1;
+ } else {
+ return luaL_argerror(L, 3, lua_pushfstring(L, "invalid option %s", opt));
+ }
+ }
+
+ data = luaL_checklstring(L, 1, &len);
+
+ ud = prepsimple(L, PKEY_CLASS);
+
+ if (!(bio = BIO_new_mem_buf((void *)data, len)))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ if (type == X509_PEM || type == X509_ANY) {
+ if (!prvtonly && !pub) {
+ /*
+ * BIO_reset is a rewind for read-only
+ * memory buffers. See mem_ctrl in
+ * crypto/bio/bss_mem.c of OpenSSL source.
+ */
+ BIO_reset(bio);
+
+ if (!(pub = PEM_read_bio_PUBKEY(bio, NULL, 0, "")))
+ goterr = 1;
+ }
+
+ if (!pubonly && !prvt) {
+ BIO_reset(bio);
+
+ if (!(prvt = PEM_read_bio_PrivateKey(bio, NULL, 0, "")))
+ goterr = 1;
+ }
+ }
+
+ if (type == X509_DER || type == X509_ANY) {
+ if (!prvtonly && !pub) {
+ BIO_reset(bio);
+
+ if (!(pub = d2i_PUBKEY_bio(bio, NULL)))
+ goterr = 1;
+ }
+
+ if (!pubonly && !prvt) {
+ BIO_reset(bio);
+
+ if (!(prvt = d2i_PrivateKey_bio(bio, NULL)))
+ goterr = 1;
+ }
+ }
+
+ if (prvt) {
+#if 0
+ /* TODO: Determine if this is necessary. */
+ if (pub && EVP_PKEY_missing_parameters(prvt)) {
+ if (!EVP_PKEY_copy_parameters(prvt, pub)) {
+ /*
+ * NOTE: It's not necessarily true
+ * that any internal errors were
+ * set. But we fixed pusherror() to
+ * handle that situation.
+ */
+ goterr = 1;
+
+ goto done;
+ }
+ }
+#endif
+
+ *ud = prvt;
+ prvt = NULL;
+ } else if (pub) {
+ *ud = pub;
+ pub = NULL;
+ }
+done:
+ BIO_free(bio);
+
+ if (pub)
+ EVP_PKEY_free(pub);
+
+ if (prvt)
+ EVP_PKEY_free(prvt);
+
+ if (!*ud) {
+ if (goterr)
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ /* we should never get here */
+ return luaL_error(L, "failed to load key for some unexpected reason");
+ } else if (goterr) {
+ /* clean up our mess from testing input formats */
+ ERR_clear_error();
+ }
+ } else {
+ return luaL_error(L, "%s: unknown key initializer", lua_typename(L, lua_type(L, 1)));
+ }
+
+ return 1;
+} /* pk_new() */
+
+
+static int pk_interpose(lua_State *L) {
+ lua_settop(L, 2);
+
+ luaL_getmetatable(L, PKEY_CLASS);
+ if (!strncmp("__", luaL_checkstring(L, 1), 2)) {
+ lua_insert(L, 1);
+ } else {
+ lua_getfield(L, -1, "__index");
+ lua_getupvalue(L, -1, 1);
+ lua_insert(L, 1);
+ lua_pop(L, 2);
+ }
+
+ return auxL_swaptable(L, 1);
+} /* pk_interpose() */
+
+
+static int pk_type(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ int nid = EVP_PKEY_id(key);
+
+ auxL_pushnid(L, nid);
+
+ return 1;
+} /* pk_type() */
+
+
+static int pk_setPublicKey(lua_State *L) {
+ EVP_PKEY **key = luaL_checkudata(L, 1, PKEY_CLASS);
+ const char *data;
+ size_t len;
+ BIO *bio;
+ int type, ok = 0;
+
+ data = luaL_checklstring(L, 2, &len);
+ type = optencoding(L, 3, "*", X509_ANY|X509_PEM|X509_DER);
+
+ if (!(bio = BIO_new_mem_buf((void *)data, len)))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ if (type == X509_ANY || type == X509_PEM) {
+ ok = !!PEM_read_bio_PUBKEY(bio, key, 0, "");
+ }
+
+ if (!ok && (type == X509_ANY || type == X509_DER)) {
+ ok = !!d2i_PUBKEY_bio(bio, key);
+ }
+
+ BIO_free(bio);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* pk_setPublicKey() */
+
+
+static int pk_setPrivateKey(lua_State *L) {
+ EVP_PKEY **key = luaL_checkudata(L, 1, PKEY_CLASS);
+ const char *data;
+ size_t len;
+ BIO *bio;
+ int type, ok = 0;
+
+ data = luaL_checklstring(L, 2, &len);
+ type = optencoding(L, 3, "*", X509_ANY|X509_PEM|X509_DER);
+
+ if (!(bio = BIO_new_mem_buf((void *)data, len)))
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ if (type == X509_ANY || type == X509_PEM) {
+ ok = !!PEM_read_bio_PrivateKey(bio, key, 0, "");
+ }
+
+ if (!ok && (type == X509_ANY || type == X509_DER)) {
+ ok = !!d2i_PrivateKey_bio(bio, key);
+ }
+
+ BIO_free(bio);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "pkey.new");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* pk_setPrivateKey() */
+
+#if HAVE_EVP_PKEY_CTX_NEW
+static int pk_decrypt(lua_State *L) {
+ size_t outlen, inlen;
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ EVP_PKEY_CTX *ctx;
+ const char *str = luaL_checklstring(L, 2, &inlen);
+ BIO *bio;
+ BUF_MEM *buf;
+ int rsaPadding = RSA_PKCS1_PADDING; /* default for `openssl rsautl` */
+ int base_type = EVP_PKEY_base_id(key);
+
+ if (lua_istable(L, 3)) {
+ if (base_type == EVP_PKEY_RSA) {
+ lua_getfield(L, 3, "rsaPadding");
+ rsaPadding = luaL_optinteger(L, -1, rsaPadding);
+ lua_pop(L, 1);
+ }
+ }
+
+ bio = getbio(L);
+ BIO_get_mem_ptr(bio, &buf);
+
+ if (!(ctx = EVP_PKEY_CTX_new(key, NULL)))
+ goto sslerr;
+
+ if (EVP_PKEY_decrypt_init(ctx) <= 0)
+ goto sslerr;
+
+ if (base_type == EVP_PKEY_RSA && !EVP_PKEY_CTX_set_rsa_padding(ctx, rsaPadding))
+ goto sslerr;
+
+ if (EVP_PKEY_decrypt(ctx, NULL, &outlen, (const unsigned char *)str, inlen) <= 0)
+ goto sslerr;
+
+ if (!BUF_MEM_grow_clean(buf, outlen))
+ goto sslerr;
+
+ if (EVP_PKEY_decrypt(ctx, (unsigned char *)buf->data, &outlen, (const unsigned char *)str, inlen) <= 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 auxL_error(L, auxL_EOPENSSL, "pkey:decrypt");
+} /* pk_decrypt() */
+#endif
+
+#if HAVE_EVP_PKEY_CTX_NEW
+static int pk_encrypt(lua_State *L) {
+ size_t outlen, inlen;
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ EVP_PKEY_CTX *ctx;
+ const char *str = luaL_checklstring(L, 2, &inlen);
+ BIO *bio;
+ BUF_MEM *buf;
+ int rsaPadding = RSA_PKCS1_PADDING; /* default for `openssl rsautl` */
+ int base_type = EVP_PKEY_base_id(key);
+
+ if (lua_istable(L, 3)) {
+ if (base_type == EVP_PKEY_RSA) {
+ lua_getfield(L, 3, "rsaPadding");
+ rsaPadding = luaL_optinteger(L, -1, rsaPadding);
+ lua_pop(L, 1);
+ }
+ }
+
+ bio = getbio(L);
+ BIO_get_mem_ptr(bio, &buf);
+
+ if (!(ctx = EVP_PKEY_CTX_new(key, NULL)))
+ goto sslerr;
+
+ if (EVP_PKEY_encrypt_init(ctx) <= 0)
+ goto sslerr;
+
+ if (base_type == EVP_PKEY_RSA && !EVP_PKEY_CTX_set_rsa_padding(ctx, rsaPadding))
+ goto sslerr;
+
+ if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (const unsigned char *)str, inlen) <= 0)
+ goto sslerr;
+
+ if (!BUF_MEM_grow_clean(buf, outlen))
+ goto sslerr;
+
+ if (EVP_PKEY_encrypt(ctx, (unsigned char *)buf->data, &outlen, (const unsigned char *)str, inlen) <= 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 auxL_error(L, auxL_EOPENSSL, "pkey:encrypt");
+} /* pk_encrypt() */
+#endif
+
+static int pk_sign(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ EVP_MD_CTX *md = checksimple(L, 2, DIGEST_CLASS);
+ luaL_Buffer B;
+ unsigned n = EVP_PKEY_size(key);
+
+ if (!EVP_SignFinal(md, (void *)luaL_buffinitsize(L, &B, n), &n, key))
+ return auxL_error(L, auxL_EOPENSSL, "pkey:sign");
+
+ luaL_pushresultsize(&B, n);
+
+ return 1;
+} /* pk_sign() */
+
+
+static int pk_verify(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ size_t len;
+ const void *sig = luaL_checklstring(L, 2, &len);
+ EVP_MD_CTX *md = checksimple(L, 3, DIGEST_CLASS);
+
+ switch (EVP_VerifyFinal(md, sig, len, key)) {
+ case 0: /* WRONG */
+ ERR_clear_error();
+ lua_pushboolean(L, 0);
+
+ break;
+ case 1: /* OK */
+ lua_pushboolean(L, 1);
+
+ break;
+ default:
+ return auxL_error(L, auxL_EOPENSSL, "pkey:verify");
+ }
+
+ return 1;
+} /* pk_verify() */
+
+
+static int pk_toPEM(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ int top, i, ok;
+ BIO *bio;
+ char *pem;
+ long len;
+
+ if (1 == (top = lua_gettop(L))) {
+ lua_pushstring(L, "publickey");
+ ++top;
+ }
+
+ bio = getbio(L);
+
+ for (i = 2; i <= top; i++) {
+ static const char *const opts[] = {
+ "public", "PublicKey",
+ "private", "PrivateKey",
+// "params", "Parameters",
+ NULL,
+ };
+
+ switch (auxL_checkoption(L, i, NULL, opts, 1)) {
+ case 0: case 1: /* public, PublicKey */
+ if (!PEM_write_bio_PUBKEY(bio, key))
+ return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring");
+
+ len = BIO_get_mem_data(bio, &pem);
+ lua_pushlstring(L, pem, len);
+ BIO_reset(bio);
+
+ break;
+ case 2: case 3: /* private, PrivateKey */
+ if (!PEM_write_bio_PrivateKey(bio, key, 0, 0, 0, 0, 0))
+ return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring");
+
+ len = BIO_get_mem_data(bio, &pem);
+ lua_pushlstring(L, pem, len);
+ BIO_reset(bio);
+
+ break;
+#if 0
+ case 4: case 5: /* params, Parameters */
+ /* EVP_PKEY_base_id not in OS X */
+ switch (EVP_PKEY_base_id(key)) {
+ case EVP_PKEY_RSA:
+ break;
+ case EVP_PKEY_DSA: {
+ DSA *dsa = EVP_PKEY_get1_DSA(key);
+
+ ok = !!PEM_write_bio_DSAparams(bio, dsa);
+
+ DSA_free(dsa);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring");
+
+ break;
+ }
+ case EVP_PKEY_DH: {
+ DH *dh = EVP_PKEY_get1_DH(key);
+
+ ok = !!PEM_write_bio_DHparams(bio, dh);
+
+ DH_free(dh);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring");
+
+ break;
+ }
+#ifndef OPENSSL_NO_EC
+ case EVP_PKEY_EC: {
+ EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key);
+ const EC_GROUP *grp = EC_KEY_get0_group(ec);
+
+ ok = !!PEM_write_bio_ECPKParameters(bio, grp);
+
+ EC_KEY_free(ec);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring");
+
+ break;
+ }
+#endif
+ default:
+ return luaL_error(L, "%d: unsupported EVP_PKEY base type", EVP_PKEY_base_id(key));
+ }
+
+ lua_pushlstring(L, pem, len);
+
+ BIO_reset(bio);
+
+ break;
+#endif
+ default:
+ lua_pushnil(L);
+
+ break;
+ } /* switch() */
+ } /* for() */
+
+ return lua_gettop(L) - top;
+} /* pk_toPEM() */
+
+
+static int pk_getDefaultDigestName(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ int nid;
+
+ if (!(EVP_PKEY_get_default_digest_nid(key, &nid) > 0))
+ return auxL_error(L, auxL_EOPENSSL, "pkey:getDefaultDigestName");
+
+ auxL_pushnid(L, nid);
+
+ return 1;
+} /* pk_getDefaultDigestName() */
+
+
+enum pk_param {
+#define PK_RSA_OPTLIST { "n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp", NULL }
+#define PK_RSA_OPTOFFSET PK_RSA_N
+ PK_RSA_N = 1,
+ PK_RSA_E,
+ PK_RSA_D,
+ PK_RSA_P,
+ PK_RSA_Q,
+ PK_RSA_DMP1,
+ PK_RSA_DMQ1,
+ PK_RSA_IQMP,
+
+#define PK_DSA_OPTLIST { "p", "q", "g", "pub_key", "priv_key", NULL }
+#define PK_DSA_OPTOFFSET PK_DSA_P
+ PK_DSA_P,
+ PK_DSA_Q,
+ PK_DSA_G,
+ PK_DSA_PUB_KEY,
+ PK_DSA_PRIV_KEY,
+
+#define PK_DH_OPTLIST { "p", "g", "pub_key", "priv_key", NULL }
+#define PK_DH_OPTOFFSET PK_DH_P
+ PK_DH_P,
+ PK_DH_G,
+ PK_DH_PUB_KEY,
+ PK_DH_PRIV_KEY,
+
+/*
+ * NB: group MUST come before pub_key as setting pub_key requires the group
+ * to be defined. :setParameters will do the requested assignments in the
+ * order defined by this array.
+ */
+#define PK_EC_OPTLIST { "group", "pub_key", "priv_key", NULL }
+#define PK_EC_OPTOFFSET PK_EC_GROUP
+ PK_EC_GROUP,
+ PK_EC_PUB_KEY,
+ PK_EC_PRIV_KEY,
+}; /* enum pk_param */
+
+static const char *const pk_rsa_optlist[] = PK_RSA_OPTLIST;
+static const char *const pk_dsa_optlist[] = PK_DSA_OPTLIST;
+static const char *const pk_dh_optlist[] = PK_DH_OPTLIST;
+static const char *const pk_ec_optlist[] = PK_EC_OPTLIST;
+
+const char *const *pk_getoptlist(int type, int *_nopts, int *_optoffset) {
+ const char *const *optlist = NULL;
+ int nopts = 0, optoffset = 0;
+
+ switch (type) {
+ case EVP_PKEY_RSA:
+ optlist = pk_rsa_optlist;
+ nopts = countof(pk_rsa_optlist) - 1;
+ optoffset = PK_RSA_OPTOFFSET;
+
+ break;
+ case EVP_PKEY_DSA:
+ optlist = pk_dsa_optlist;
+ nopts = countof(pk_dsa_optlist) - 1;
+ optoffset = PK_DSA_OPTOFFSET;
+
+ break;
+ case EVP_PKEY_DH:
+ optlist = pk_dh_optlist;
+ nopts = countof(pk_dh_optlist) - 1;
+ optoffset = PK_DH_OPTOFFSET;
+
+ break;
+ case EVP_PKEY_EC:
+ optlist = pk_ec_optlist;
+ nopts = countof(pk_ec_optlist) - 1;
+ optoffset = PK_EC_OPTOFFSET;
+
+ break;
+ }
+
+ if (_nopts)
+ *_nopts = nopts;
+ if (_optoffset)
+ *_optoffset = optoffset;
+
+ return optlist;
+} /* pk_getoptlist() */
+
+#ifndef OPENSSL_NO_EC
+static EC_GROUP *ecg_dup_nil(lua_State *, const EC_GROUP *);
+#endif
+
+static void pk_pushparam(lua_State *L, void *base_key, enum pk_param which) {
+ union {
+ RSA *rsa;
+ DH *dh;
+ DSA *dsa;
+#ifndef OPENSSL_NO_EC
+ EC_KEY *ec;
+#endif
+ } key = { base_key };
+ const BIGNUM *i;
+
+ switch (which) {
+ case PK_RSA_N:
+ /* RSA public modulus n */
+ RSA_get0_key(key.rsa, &i, NULL, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_RSA_E:
+ /* RSA public exponent e */
+ RSA_get0_key(key.rsa, NULL, &i, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_RSA_D:
+ /* RSA secret exponent d */
+ RSA_get0_key(key.rsa, NULL, NULL, &i);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_RSA_P:
+ /* RSA secret prime p */
+ RSA_get0_factors(key.rsa, &i, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_RSA_Q:
+ /* RSA secret prime q with p < q */
+ RSA_get0_factors(key.rsa, NULL, &i);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_RSA_DMP1:
+ /* exponent1 */
+ RSA_get0_crt_params(key.rsa, &i, NULL, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_RSA_DMQ1:
+ /* exponent2 */
+ RSA_get0_crt_params(key.rsa, NULL, &i, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_RSA_IQMP:
+ /* coefficient */
+ RSA_get0_crt_params(key.rsa, NULL, NULL, &i);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DSA_P:
+ DSA_get0_pqg(key.dsa, &i, NULL, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DSA_Q:
+ DSA_get0_pqg(key.dsa, NULL, &i, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DSA_G:
+ DSA_get0_pqg(key.dsa, NULL, NULL, &i);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DSA_PUB_KEY:
+ DSA_get0_key(key.dsa, &i, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DSA_PRIV_KEY:
+ DSA_get0_key(key.dsa, NULL, &i);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DH_P:
+ DH_get0_pqg(key.dh, &i, NULL, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DH_G:
+ DH_get0_pqg(key.dh, NULL, NULL, &i);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DH_PUB_KEY:
+ DH_get0_key(key.dh, &i, NULL);
+ bn_dup_nil(L, i);
+
+ break;
+ case PK_DH_PRIV_KEY:
+ DH_get0_key(key.dh, NULL, &i);
+ bn_dup_nil(L, i);
+
+ break;
+#ifndef OPENSSL_NO_EC
+ case PK_EC_GROUP:
+ ecg_dup_nil(L, EC_KEY_get0_group(key.ec));
+
+ break;
+ case PK_EC_PUB_KEY: {
+ const EC_GROUP *group;
+ const EC_POINT *pub_key;
+
+ if ((group = EC_KEY_get0_group(key.ec)) && (pub_key = EC_KEY_get0_public_key(key.ec))) {
+ bn_dup_nil(L, EC_POINT_point2bn(group, pub_key, EC_KEY_get_conv_form(key.ec), NULL, getctx(L)));
+ } else {
+ lua_pushnil(L);
+ }
+
+ break;
+ }
+ case PK_EC_PRIV_KEY:
+ bn_dup_nil(L, EC_KEY_get0_private_key(key.ec));
+
+ break;
+#endif
+ default:
+ luaL_error(L, "%d: invalid EVP_PKEY parameter", which);
+ }
+
+ return;
+} /* pk_pushparam() */
+
+
+#define pk_setparam_bn_dup(L, index, dst) do { \
+ BIGNUM *tmp = checkbig((L), (index)); \
+ if (!(*dst = BN_dup(tmp))) \
+ goto sslerr; \
+} while (0)
+
+static void pk_setparam(lua_State *L, void *base_key, enum pk_param which, int index) {
+ union {
+ RSA *rsa;
+ DH *dh;
+ DSA *dsa;
+#ifndef OPENSSL_NO_EC
+ EC_KEY *ec;
+#endif
+ } key = { base_key };
+ BIGNUM *i;
+
+ switch (which) {
+ case PK_RSA_N:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_key(key.rsa, i, NULL, NULL);
+
+ break;
+ case PK_RSA_E:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_key(key.rsa, NULL, i, NULL);
+
+ break;
+ case PK_RSA_D:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_key(key.rsa, NULL, NULL, i);
+
+ break;
+ case PK_RSA_P:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_factors(key.rsa, i, NULL);
+
+ break;
+ case PK_RSA_Q:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_factors(key.rsa, NULL, i);
+
+ break;
+ case PK_RSA_DMP1:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_crt_params(key.rsa, i, NULL, NULL);
+
+ break;
+ case PK_RSA_DMQ1:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_crt_params(key.rsa, NULL, i, NULL);
+
+ break;
+ case PK_RSA_IQMP:
+ pk_setparam_bn_dup(L, index, &i);
+ RSA_set0_crt_params(key.rsa, NULL, NULL, i);
+
+ break;
+ case PK_DSA_P:
+ pk_setparam_bn_dup(L, index, &i);
+ DSA_set0_pqg(key.dsa, i, NULL, NULL);
+
+ break;
+ case PK_DSA_Q:
+ pk_setparam_bn_dup(L, index, &i);
+ DSA_set0_pqg(key.dsa, NULL, i, NULL);
+
+ break;
+ case PK_DSA_G:
+ pk_setparam_bn_dup(L, index, &i);
+ DSA_set0_pqg(key.dsa, NULL, NULL, i);
+
+ break;
+ case PK_DSA_PUB_KEY:
+ pk_setparam_bn_dup(L, index, &i);
+ DSA_set0_key(key.dsa, i, NULL);
+
+ break;
+ case PK_DSA_PRIV_KEY:
+ pk_setparam_bn_dup(L, index, &i);
+ DSA_set0_key(key.dsa, NULL, i);
+
+ break;
+ case PK_DH_P:
+ pk_setparam_bn_dup(L, index, &i);
+ DH_set0_pqg(key.dh, i, NULL, NULL);
+
+ break;
+ case PK_DH_G:
+ pk_setparam_bn_dup(L, index, &i);
+ DH_set0_pqg(key.dh, NULL, NULL, i);
+
+ break;
+ case PK_DH_PUB_KEY:
+ pk_setparam_bn_dup(L, index, &i);
+ DH_set0_key(key.dh, i, NULL);
+
+ break;
+ case PK_DH_PRIV_KEY:
+ pk_setparam_bn_dup(L, index, &i);
+ DH_set0_key(key.dh, NULL, i);
+
+ break;
+#ifndef OPENSSL_NO_EC
+ case PK_EC_GROUP: {
+ const EC_GROUP *group = checksimple(L, index, EC_GROUP_CLASS);
+
+ if (!EC_KEY_set_group(key.ec, group))
+ goto sslerr;
+
+ break;
+ }
+ case PK_EC_PUB_KEY: {
+ const BIGNUM *n = checkbig(L, index);
+ const EC_GROUP *group;
+ EC_POINT *pub_key;
+ _Bool okay;
+
+ if (!(group = EC_KEY_get0_group(key.ec)))
+ luaL_error(L, "unable to set EC pub_key (no group defined)");
+
+ if (!(pub_key = EC_POINT_bn2point(group, n, NULL, getctx(L))))
+ goto sslerr;
+
+ /* NB: copies key, doesn't share or take ownership */
+ okay = EC_KEY_set_public_key(key.ec, pub_key);
+ EC_POINT_free(pub_key);
+ if (!okay)
+ goto sslerr;
+
+ break;
+ }
+ case PK_EC_PRIV_KEY: {
+ const BIGNUM *n = checkbig(L, index);
+
+ /* NB: copies key, doesn't share or take ownership */
+ if (!EC_KEY_set_private_key(key.ec, n))
+ goto sslerr;
+
+ break;
+ }
+#endif
+ default:
+ luaL_error(L, "%d: invalid EVP_PKEY parameter", which);
+ }
+
+ return;
+sslerr:
+ auxL_error(L, auxL_EOPENSSL, "pkey:setParameters");
+
+ return;
+} /* pk_setparam() */
+
+
+static int pk_getParameters(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ int base_type = EVP_PKEY_base_id(key);
+ void *base_key;
+ const char *const *optlist;
+ int nopts, optoffset, otop, index, tindex;
+
+ if (!(base_key = EVP_PKEY_get0(key)))
+ goto sslerr;
+
+ if (!(optlist = pk_getoptlist(base_type, &nopts, &optoffset)))
+ return luaL_error(L, "%d: unsupported EVP_PKEY base type", base_type);
+
+ if (lua_isnoneornil(L, 2)) {
+ const char *const *optname;
+
+ /*
+ * Use special "{" parameter to tell loop to push table.
+ * Subsequent parameters will be assigned as fields.
+ */
+ lua_pushstring(L, "{");
+ luaL_checkstack(L, nopts, "too many arguments");
+ for (optname = optlist; *optname; optname++) {
+ lua_pushstring(L, *optname);
+ }
+ }
+
+ otop = lua_gettop(L);
+
+ /* provide space for results and working area */
+ luaL_checkstack(L, (otop - 1) + LUA_MINSTACK, "too many arguments");
+
+ /* no table index, yet */
+ tindex = 0;
+
+ for (index = 2; index <= otop; index++) {
+ const char *optname = luaL_checkstring(L, index);
+ int optid;
+
+ if (*optname == '{') {
+ lua_newtable(L);
+ tindex = lua_gettop(L);
+ } else {
+ optid = luaL_checkoption(L, index, NULL, optlist) + optoffset;
+ pk_pushparam(L, base_key, optid);
+
+ if (tindex) {
+ lua_setfield(L, tindex, optname);
+ }
+ }
+ }
+
+ return lua_gettop(L) - otop;
+sslerr:
+ return auxL_error(L, auxL_EOPENSSL, "pkey:getParameters");
+} /* pk_getParameters() */
+
+
+static int pk_setParameters(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ int base_type = EVP_PKEY_base_id(key);
+ void *base_key;
+ const char *const *optlist;
+ int optindex, optoffset;
+
+ luaL_checktype(L, 2, LUA_TTABLE);
+
+ if (!(base_key = EVP_PKEY_get0(key)))
+ goto sslerr;
+
+ if (!(optlist = pk_getoptlist(base_type, NULL, &optoffset)))
+ return luaL_error(L, "%d: unsupported EVP_PKEY base type", base_type);
+
+ for (optindex = 0; optlist[optindex]; optindex++) {
+ if (getfield(L, 2, optlist[optindex])) {
+ pk_setparam(L, base_key, optindex + optoffset, -1);
+ lua_pop(L, 1);
+ }
+ }
+
+ return 0;
+sslerr:
+ return auxL_error(L, auxL_EOPENSSL, "pkey:setParameters");
+} /* pk_setParameters() */
+
+
+static int pk__tostring(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ int type = optencoding(L, 2, "pem", X509_PEM|X509_DER);
+ BIO *bio = getbio(L);
+ char *data;
+ long len;
+
+ switch (type) {
+ case X509_PEM:
+ if (!PEM_write_bio_PUBKEY(bio, key))
+ return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring");
+ break;
+ case X509_DER:
+ if (!i2d_PUBKEY_bio(bio, key))
+ return auxL_error(L, auxL_EOPENSSL, "pkey:__tostring");
+ break;
+ } /* switch() */
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* pk__tostring() */
+
+
+static int pk__index(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ void *base_key;
+ const char *const *optlist;
+ int optoffset, listoffset;
+
+ lua_pushvalue(L, lua_upvalueindex(1));
+ lua_pushvalue(L, 2);
+ lua_gettable(L, -2);
+
+ if (!lua_isnil(L, -1))
+ return 1;
+
+ if (!lua_isstring(L, 2))
+ return 0;
+ if (!(base_key = EVP_PKEY_get0(key)))
+ return 0;
+ if (!(optlist = pk_getoptlist(EVP_PKEY_base_id(key), NULL, &optoffset)))
+ return 0;
+ if (-1 == (listoffset = auxL_testoption(L, 2, NULL, optlist, 0)))
+ return 0;
+
+ pk_pushparam(L, base_key, listoffset + optoffset);
+
+ return 1;
+} /* pk__index() */
+
+
+static int pk__newindex(lua_State *L) {
+ EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS);
+ void *base_key;
+ const char *const *optlist;
+ int optoffset, listoffset;
+
+ if (!lua_isstring(L, 2))
+ return 0;
+ if (!(base_key = EVP_PKEY_get0(key)))
+ return 0;
+ if (!(optlist = pk_getoptlist(EVP_PKEY_base_id(key), NULL, &optoffset)))
+ return 0;
+ if (-1 == (listoffset = auxL_testoption(L, 2, NULL, optlist, 0)))
+ return 0;
+
+ pk_setparam(L, base_key, listoffset + optoffset, 3);
+
+ return 0;
+} /* pk__newindex() */
+
+
+static int pk__gc(lua_State *L) {
+ EVP_PKEY **ud = luaL_checkudata(L, 1, PKEY_CLASS);
+
+ if (*ud) {
+ EVP_PKEY_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* pk__gc() */
+
+
+static const auxL_Reg pk_methods[] = {
+ { "type", &pk_type },
+ { "setPublicKey", &pk_setPublicKey },
+ { "setPrivateKey", &pk_setPrivateKey },
+ { "getDefaultDigestName", &pk_getDefaultDigestName },
+ { "getParameters", &pk_getParameters },
+ { "setParameters", &pk_setParameters },
+#if HAVE_EVP_PKEY_CTX_NEW
+ { "decrypt", &pk_decrypt },
+ { "encrypt", &pk_encrypt },
+#endif
+ { "sign", &pk_sign },
+ { "toPEM", &pk_toPEM },
+ { "tostring", &pk__tostring },
+ { "verify", &pk_verify },
+ { NULL, NULL },
+};
+
+static const auxL_Reg pk_metatable[] = {
+ { "__tostring", &pk__tostring },
+ { "__index", &pk__index, 1 },
+ { "__newindex", &pk__newindex, 1 },
+ { "__gc", &pk__gc },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg pk_globals[] = {
+ { "new", &pk_new },
+ { "interpose", &pk_interpose },
+ { NULL, NULL },
+};
+
+static void pk_luainit(lua_State *L, _Bool reset) {
+ char **k;
+ if (!auxL_newmetatable(L, PKEY_CLASS, reset))
+ return;
+ auxL_setfuncs(L, pk_metatable, 0);
+ auxL_newlib(L, pk_methods, 0);
+ for (k = (char *[]){ "__index", "__newindex", 0 }; *k; k++) {
+ lua_getfield(L, -2, *k); /* closure */
+ lua_pushvalue(L, -2); /* method table */
+ lua_setupvalue(L, -2, 1);
+ }
+ lua_pop(L, 2);
+} /* pk_luainit() */
+
+static const auxL_IntegerReg pk_rsa_pad_opts[] = {
+ { "RSA_PKCS1_PADDING", RSA_PKCS1_PADDING }, // PKCS#1 padding
+ { "RSA_SSLV23_PADDING", RSA_SSLV23_PADDING }, // SSLv23 padding
+ { "RSA_NO_PADDING", RSA_NO_PADDING }, // no padding
+ { "RSA_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING }, // OAEP padding (encrypt and decrypt only)
+ { "RSA_X931_PADDING", RSA_X931_PADDING }, // (signature operations only)
+#if RSA_PKCS1_PSS_PADDING
+ { "RSA_PKCS1_PSS_PADDING", RSA_PKCS1_PSS_PADDING }, // (sign and verify only)
+#endif
+ { NULL, 0 },
+};
+
+EXPORT int luaopen__openssl_pkey(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, pk_globals, 0);
+ auxL_setintegers(L, pk_rsa_pad_opts);
+
+ return 1;
+} /* luaopen__openssl_pkey() */
+
+
+/*
+ * Deprecated module name.
+ */
+EXPORT int luaopen__openssl_pubkey(lua_State *L) {
+ return luaopen__openssl_pkey(L);
+} /* luaopen__openssl_pubkey() */
+
+
+/*
+ * EC_GROUP - openssl.ec.group
+ *
+ * NOTE: Ensure copy-by-value semantics when passing EC_GROUP objects as it
+ * doesn't support reference counting. The only persistent reference should
+ * be the Lua userdata value.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef OPENSSL_NO_EC
+
+static EC_GROUP *ecg_dup(lua_State *L, const EC_GROUP *src) {
+ EC_GROUP **ud = prepsimple(L, EC_GROUP_CLASS);
+
+ if (!(*ud = EC_GROUP_dup(src)))
+ auxL_error(L, auxL_EOPENSSL, "group");
+
+ return *ud;
+} /* ecg_dup() */
+
+static EC_GROUP *ecg_dup_nil(lua_State *L, const EC_GROUP *src) {
+ return (src)? ecg_dup(L, src) : (lua_pushnil(L), (EC_GROUP *)0);
+} /* ecg_dup_nil() */
+
+static EC_GROUP *ecg_push_by_nid(lua_State *L, int nid) {
+ EC_GROUP **group = prepsimple(L, EC_GROUP_CLASS);
+
+ if (!(*group = EC_GROUP_new_by_curve_name(nid)))
+ goto oops;
+
+ EC_GROUP_set_asn1_flag(*group, OPENSSL_EC_NAMED_CURVE);
+
+ /* compressed points may be patented */
+ EC_GROUP_set_point_conversion_form(*group, POINT_CONVERSION_UNCOMPRESSED);
+
+ return *group;
+oops:
+ lua_pop(L, 1);
+
+ return NULL;
+} /* ecg_push_by_nid() */
+
+static int ecg_new(lua_State *L) {
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING: {
+ const char *data;
+ size_t datalen;
+ int nid, type, goterr;
+ BIO *bio;
+ EC_GROUP **group;
+
+ data = luaL_checklstring(L, 1, &datalen);
+
+ if (auxS_txt2nid(&nid, data)) {
+ if (!ecg_push_by_nid(L, nid))
+ goto sslerr;
+ } else {
+ type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER);
+ group = prepsimple(L, EC_GROUP_CLASS);
+
+ luaL_argcheck(L, datalen < INT_MAX, 1, "string too long");
+ if (!(bio = BIO_new_mem_buf((void *)data, datalen)))
+ return auxL_error(L, auxL_EOPENSSL, "group.new");
+
+ goterr = 0;
+
+ if (type == X509_PEM || type == X509_ANY) {
+ goterr |= !(*group = PEM_read_bio_ECPKParameters(bio, NULL, 0, ""));
+ }
+
+ if (!*group && (type == X509_DER || type == X509_ANY)) {
+ BIO_reset(bio);
+ goterr |= !(*group = d2i_ECPKParameters_bio(bio, NULL));
+ }
+
+ BIO_free(bio);
+
+ if (!*group)
+ return auxL_error(L, auxL_EOPENSSL, "group.new");
+ if (goterr)
+ ERR_clear_error();
+ }
+
+ return 1;
+ }
+ case LUA_TNUMBER: {
+ int nid = luaL_checkinteger(L, 2);
+
+ if (!ecg_push_by_nid(L, nid))
+ goto sslerr;
+
+ return 1;
+ }
+ default:
+ return luaL_error(L, "%s: unknown group initializer", lua_typename(L, lua_type(L, 1)));
+ } /* switch() */
+
+ return 0;
+sslerr:
+ return auxL_error(L, auxL_EOPENSSL, "group.new");
+} /* ecg_new() */
+
+static int ecg_interpose(lua_State *L) {
+ return interpose(L, EC_GROUP_CLASS);
+} /* ecg_interpose() */
+
+
+static int ecg_toPEM(lua_State *L) {
+ EC_GROUP *group = checksimple(L, 1, EC_GROUP_CLASS);
+ BIO *bio = getbio(L);
+ size_t len;
+ char *bytes;
+
+ if (!PEM_write_bio_ECPKParameters(bio, group))
+ return auxL_error(L, auxL_EOPENSSL, "group:toPEM");
+
+ len = BIO_get_mem_data(bio, &bytes);
+ lua_pushlstring(L, bytes, len);
+
+ return 1;
+} /* ecg_toPEM() */
+
+
+static int ecg_tostring(lua_State *L) {
+ EC_GROUP *group = checksimple(L, 1, EC_GROUP_CLASS);
+ int how = optencoding(L, 2, "pem", X509_PEM|X509_DER|X509_TXT);
+ BIO *bio = getbio(L);
+ char *bytes;
+ int len, indent;
+
+ switch (how) {
+ case X509_PEM:
+ if (!PEM_write_bio_ECPKParameters(bio, group))
+ goto sslerr;
+ break;
+ case X509_DER:
+ if (!i2d_ECPKParameters_bio(bio, group))
+ goto sslerr;
+ break;
+ case X509_TXT:
+ indent = auxL_optinteger(L, 3, 0, 0, INT_MAX);
+ if (!ECPKParameters_print(bio, group, indent))
+ goto sslerr;
+ break;
+ }
+
+ len = BIO_get_mem_data(bio, &bytes);
+ lua_pushlstring(L, bytes, len);
+
+ return 1;
+sslerr:
+ return auxL_error(L, auxL_EOPENSSL, "group:__tostring");
+} /* ecg_tostring() */
+
+static int ecg__tostring(lua_State *L) {
+ return ecg_tostring(L);
+} /* ecg__tostring() */
+
+static int ecg__gc(lua_State *L) {
+ EC_GROUP **ud = luaL_checkudata(L, 1, EC_GROUP_CLASS);
+
+ if (*ud) {
+ EC_GROUP_clear_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* ecg__gc() */
+
+static const auxL_Reg ecg_methods[] = {
+ { "toPEM", &ecg_toPEM },
+ { "tostring", &ecg_tostring },
+ { NULL, NULL },
+};
+
+static const auxL_Reg ecg_metatable[] = {
+ { "__tostring", &ecg__tostring },
+ { "__gc", &ecg__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg ecg_globals[] = {
+ { "new", &ecg_new },
+ { "interpose", &ecg_interpose },
+ { NULL, NULL },
+};
+
+#endif /* OPENSSL_NO_EC */
+
+EXPORT int luaopen__openssl_ec_group(lua_State *L) {
+#ifndef OPENSSL_NO_EC
+ initall(L);
+
+ auxL_newlib(L, ecg_globals, 0);
+
+ return 1;
+#else
+ return 0;
+#endif
+} /* luaopen__openssl_ec_group() */
+
+
+/*
+ * X509_NAME - openssl.x509.name
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static X509_NAME *xn_dup(lua_State *L, X509_NAME *name) {
+ X509_NAME **ud = prepsimple(L, X509_NAME_CLASS);
+
+ if (!(*ud = X509_NAME_dup(name)))
+ auxL_error(L, auxL_EOPENSSL, "x509.name.dup");
+
+ return *ud;
+} /* xn_dup() */
+
+
+static int xn_new(lua_State *L) {
+ X509_NAME **ud = prepsimple(L, X509_NAME_CLASS);
+
+ if (!(*ud = X509_NAME_new()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.name.new");
+
+ return 1;
+} /* xn_new() */
+
+
+static int xn_interpose(lua_State *L) {
+ return interpose(L, X509_NAME_CLASS);
+} /* xn_interpose() */
+
+
+static int xn_add(lua_State *L) {
+ X509_NAME *name = checksimple(L, 1, X509_NAME_CLASS);
+ const char *nid = luaL_checkstring(L, 2);
+ size_t len;
+ const char *txt = luaL_checklstring(L, 3, &len);
+ ASN1_OBJECT *obj;
+ int ok;
+
+ if (!(obj = OBJ_txt2obj(nid, 0)))
+ return luaL_error(L, "x509.name:add: %s: invalid NID", nid);
+
+ ok = !!X509_NAME_add_entry_by_OBJ(name, obj, MBSTRING_ASC, (unsigned char *)txt, len, -1, 0);
+
+ ASN1_OBJECT_free(obj);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "x509.name:add");
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+} /* xn_add() */
+
+
+static int xn_all(lua_State *L) {
+ X509_NAME *name = checksimple(L, 1, X509_NAME_CLASS);
+ int count = X509_NAME_entry_count(name);
+ X509_NAME_ENTRY *entry;
+ ASN1_OBJECT *obj;
+ const char *id;
+ char txt[256];
+ int i, nid, len;
+
+ lua_newtable(L);
+
+ for (i = 0; i < count; i++) {
+ if (!(entry = X509_NAME_get_entry(name, i)))
+ continue;
+
+ lua_newtable(L);
+
+ obj = X509_NAME_ENTRY_get_object(entry);
+ nid = OBJ_obj2nid(obj);
+
+ if (0 > (len = OBJ_obj2txt(txt, sizeof txt, obj, 1)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.name:all");
+
+ lua_pushlstring(L, txt, len);
+
+ if (nid != NID_undef && ((id = OBJ_nid2ln(nid)) || (id = OBJ_nid2sn(nid))))
+ lua_pushstring(L, id);
+ else
+ lua_pushvalue(L, -1);
+
+ if (nid != NID_undef && (id = OBJ_nid2sn(nid)))
+ lua_pushstring(L, id);
+ else
+ lua_pushvalue(L, -1);
+
+ lua_setfield(L, -4, "sn");
+ lua_setfield(L, -3, "ln");
+ lua_setfield(L, -2, "id");
+
+ len = ASN1_STRING_length(X509_NAME_ENTRY_get_data(entry));
+ lua_pushlstring(L, (char *)ASN1_STRING_get0_data(X509_NAME_ENTRY_get_data(entry)), len);
+
+ lua_setfield(L, -2, "blob");
+
+ lua_rawseti(L, -2, i + 1);
+ }
+
+ return 1;
+} /* xn_all() */
+
+
+static int xn__next(lua_State *L) {
+ X509_NAME *name = checksimple(L, lua_upvalueindex(1), X509_NAME_CLASS);
+ X509_NAME_ENTRY *entry;
+ ASN1_OBJECT *obj;
+ char txt[256];
+ int i, n, len;
+
+ lua_settop(L, 0);
+
+ i = lua_tointeger(L, lua_upvalueindex(2));
+ n = X509_NAME_entry_count(name);
+
+ while (i < n) {
+ if (!(entry = X509_NAME_get_entry(name, i++)))
+ continue;
+
+ obj = X509_NAME_ENTRY_get_object(entry);
+
+ if (!(len = auxS_obj2txt(txt, sizeof txt, obj)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.name:__pairs");
+ lua_pushlstring(L, txt, len);
+
+ len = ASN1_STRING_length(X509_NAME_ENTRY_get_data(entry));
+ lua_pushlstring(L, (char *)ASN1_STRING_get0_data(X509_NAME_ENTRY_get_data(entry)), len);
+
+ break;
+ }
+
+ lua_pushinteger(L, i);
+ lua_replace(L, lua_upvalueindex(2));
+
+ return lua_gettop(L);
+} /* xn__next() */
+
+static int xn__pairs(lua_State *L) {
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+
+ lua_pushcclosure(L, &xn__next, 2);
+
+ return 1;
+} /* xn__pairs() */
+
+
+static int xn__gc(lua_State *L) {
+ X509_NAME **ud = luaL_checkudata(L, 1, X509_NAME_CLASS);
+
+ if (*ud) {
+ X509_NAME_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* xn__gc() */
+
+
+static int xn__tostring(lua_State *L) {
+ X509_NAME *name = checksimple(L, 1, X509_NAME_CLASS);
+ char txt[1024] = { 0 };
+
+ /* FIXME: oneline is deprecated */
+ X509_NAME_oneline(name, txt, sizeof txt);
+
+ lua_pushstring(L, txt);
+
+ return 1;
+} /* xn__tostring() */
+
+
+static const auxL_Reg xn_methods[] = {
+ { "add", &xn_add },
+ { "all", &xn_all },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xn_metatable[] = {
+ { "__pairs", &xn__pairs },
+ { "__gc", &xn__gc },
+ { "__tostring", &xn__tostring },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg xn_globals[] = {
+ { "new", &xn_new },
+ { "interpose", &xn_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_name(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xn_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_x509_name() */
+
+
+/*
+ * GENERAL_NAMES - openssl.x509.altname
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static GENERAL_NAMES *gn_dup(lua_State *L, GENERAL_NAMES *gens) {
+ GENERAL_NAMES **ud = prepsimple(L, X509_GENS_CLASS);
+
+ if (!(*ud = sk_GENERAL_NAME_dup(gens)))
+ auxL_error(L, auxL_EOPENSSL, "x509.altname.dup");
+
+ return *ud;
+} /* gn_dup() */
+
+
+static int gn_new(lua_State *L) {
+ GENERAL_NAMES **ud = prepsimple(L, X509_GENS_CLASS);
+
+ if (!(*ud = sk_GENERAL_NAME_new_null()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.altname.new");
+
+ return 1;
+} /* gn_new() */
+
+
+static int gn_interpose(lua_State *L) {
+ return interpose(L, X509_GENS_CLASS);
+} /* gn_interpose() */
+
+
+static int gn_checktype(lua_State *L, int index) {
+ static const struct { int type; const char *name; } table[] = {
+ { GEN_EMAIL, "RFC822Name" },
+ { GEN_EMAIL, "RFC822" },
+ { GEN_EMAIL, "email" },
+ { GEN_URI, "UniformResourceIdentifier" },
+ { GEN_URI, "URI" },
+ { GEN_DNS, "DNSName" },
+ { GEN_DNS, "DNS" },
+ { GEN_IPADD, "IPAddress" },
+ { GEN_IPADD, "IP" },
+ { GEN_DIRNAME, "DirName" },
+ };
+ const char *type = luaL_checkstring(L, index);
+ unsigned i;
+
+ for (i = 0; i < countof(table); i++) {
+ if (strieq(table[i].name, type))
+ return table[i].type;
+ }
+
+ return luaL_error(L, "%s: invalid type", type), 0;
+} /* gn_checktype() */
+
+
+static int gn_add(lua_State *L) {
+ GENERAL_NAMES *gens = checksimple(L, 1, X509_GENS_CLASS);
+ int type = gn_checktype(L, 2);
+ X509_NAME *name;
+ size_t len;
+ const char *txt;
+ GENERAL_NAME *gen = NULL;
+ union { struct in6_addr in6; struct in_addr in; } ip;
+
+ switch (type) {
+ case GEN_DIRNAME:
+ name = checksimple(L, 3, X509_NAME_CLASS);
+
+ if (!(gen = GENERAL_NAME_new()))
+ goto error;
+
+ gen->type = type;
+
+ if (!(gen->d.dirn = X509_NAME_dup(name)))
+ goto error;
+
+ break;
+ case GEN_IPADD:
+ txt = luaL_checkstring(L, 3);
+
+ if (strchr(txt, ':')) {
+ if (1 != inet_pton(AF_INET6, txt, &ip.in6))
+ return luaL_error(L, "%s: invalid address", txt);
+
+ txt = (char *)ip.in6.s6_addr;
+ len = 16;
+ } else {
+ if (1 != inet_pton(AF_INET, txt, &ip.in))
+ return luaL_error(L, "%s: invalid address", txt);
+
+ txt = (char *)&ip.in.s_addr;
+ len = 4;
+ }
+
+ goto text;
+ default:
+ txt = luaL_checklstring(L, 3, &len);
+text:
+ if (!(gen = GENERAL_NAME_new()))
+ goto error;
+
+ gen->type = type;
+
+ if (!(gen->d.ia5 = ASN1_STRING_type_new(V_ASN1_IA5STRING)))
+ goto error;
+
+ if (!ASN1_STRING_set(gen->d.ia5, (unsigned char *)txt, len))
+ goto error;
+ break;
+ } /* switch() */
+
+ sk_GENERAL_NAME_push(gens, gen);
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+error:
+ GENERAL_NAME_free(gen);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.altname:add");
+} /* gn_add() */
+
+
+#define GN_PUSHSTRING(L, o) \
+ lua_pushlstring((L), (char *)ASN1_STRING_get0_data((o)), ASN1_STRING_length((o)))
+
+static int gn__next(lua_State *L) {
+ GENERAL_NAMES *gens = checksimple(L, lua_upvalueindex(1), X509_GENS_CLASS);
+ int i = lua_tointeger(L, lua_upvalueindex(2));
+ int n = sk_GENERAL_NAME_num(gens);
+
+ lua_settop(L, 0);
+
+ while (i < n) {
+ GENERAL_NAME *name;
+ const char *txt;
+ size_t len;
+ union { struct in_addr in; struct in6_addr in6; } ip;
+ char buf[INET6_ADDRSTRLEN + 1];
+ int af;
+
+ if (!(name = sk_GENERAL_NAME_value(gens, i++)))
+ continue;
+
+ switch (name->type) {
+ case GEN_EMAIL:
+ lua_pushstring(L, "email");
+ GN_PUSHSTRING(L, name->d.rfc822Name);
+
+ break;
+ case GEN_URI:
+ lua_pushstring(L, "URI");
+ GN_PUSHSTRING(L, name->d.uniformResourceIdentifier);
+
+ break;
+ case GEN_DNS:
+ lua_pushstring(L, "DNS");
+ GN_PUSHSTRING(L, name->d.dNSName);
+
+ break;
+ case GEN_IPADD:
+ txt = (char *)ASN1_STRING_get0_data(name->d.iPAddress);
+ len = ASN1_STRING_length(name->d.iPAddress);
+
+ switch (len) {
+ case 16:
+ memcpy(ip.in6.s6_addr, txt, 16);
+ af = AF_INET6;
+
+ break;
+ case 4:
+ memcpy(&ip.in.s_addr, txt, 4);
+ af = AF_INET;
+
+ break;
+ default:
+ continue;
+ }
+
+ if (!(txt = inet_ntop(af, &ip, buf, sizeof buf)))
+ continue;
+
+ len = strlen(txt);
+
+ lua_pushstring(L, "IP");
+ lua_pushlstring(L, txt, len);
+
+ break;
+ case GEN_DIRNAME:
+ lua_pushstring(L, "DirName");
+ xn_dup(L, name->d.dirn);
+
+ break;
+ default:
+ continue;
+ } /* switch() */
+
+ break;
+ } /* while() */
+
+ lua_pushinteger(L, i);
+ lua_replace(L, lua_upvalueindex(2));
+
+ return lua_gettop(L);
+} /* gn__next() */
+
+static int gn__pairs(lua_State *L) {
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, &gn__next, 2);
+
+ return 1;
+} /* gn__pairs() */
+
+
+static int gn__gc(lua_State *L) {
+ GENERAL_NAMES **ud = luaL_checkudata(L, 1, X509_GENS_CLASS);
+
+ if (*ud) {
+ sk_GENERAL_NAME_pop_free(*ud, GENERAL_NAME_free);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* gn__gc() */
+
+
+static const auxL_Reg gn_methods[] = {
+ { "add", &gn_add },
+ { NULL, NULL },
+};
+
+static const auxL_Reg gn_metatable[] = {
+ { "__pairs", &gn__pairs },
+ { "__gc", &gn__gc },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg gn_globals[] = {
+ { "new", &gn_new },
+ { "interpose", &gn_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_altname(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, gn_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_x509_altname() */
+
+
+/*
+ * X509_EXTENSION - openssl.x509.extension
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static _Bool xe_new_isder(const char *value, _Bool *crit) {
+ if (!strcmp(value, "critical,DER"))
+ return (*crit = 1), 1;
+ if (!strcmp(value, "DER"))
+ return (*crit = 0), 1;
+
+ return 0;
+} /* xs_new_isder() */
+
+static CONF* loadconf(lua_State *L, int idx) {
+ CONF *conf;
+ size_t len;
+ const char *cdata = luaL_checklstring(L, idx, &len);
+ BIO *bio = getbio(L);
+ if (BIO_write(bio, cdata, len) < 0)
+ return NULL;
+
+ if (!(conf = NCONF_new(NULL)))
+ return NULL;
+
+ if (!NCONF_load_bio(conf, bio, NULL)) {
+ NCONF_free(conf);
+ return NULL;
+ }
+
+ return conf;
+}
+
+static int xe_new(lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ const char *value = luaL_checkstring(L, 2);
+ ASN1_OBJECT *obj = NULL;
+ ASN1_STRING *oct = NULL;
+ CONF *conf = NULL;
+ X509V3_CTX cbuf = { 0 }, *ctx = NULL;
+ X509_EXTENSION **ud;
+ _Bool crit;
+
+ lua_settop(L, 3);
+ ud = prepsimple(L, X509_EXT_CLASS);
+
+ if (xe_new_isder(value, &crit)) {
+ size_t len;
+ const char *cdata = lua_tolstring(L, 3, &len);
+ if (!(obj = OBJ_txt2obj(name, 0)))
+ goto error;
+ if (!(oct = ASN1_STRING_new()))
+ goto error;
+ if (!ASN1_STRING_set(oct, cdata, len))
+ goto error;
+ if (!(*ud = X509_EXTENSION_create_by_OBJ(NULL, obj, crit, oct)))
+ goto error;
+
+ ASN1_OBJECT_free(obj);
+ ASN1_STRING_free(oct);
+
+ return 1;
+ }
+
+ switch (lua_type(L, 3)) {
+ case LUA_TNONE:
+ case LUA_TNIL:
+ break;
+ case LUA_TSTRING: {
+ if (!(conf = loadconf(L, 3)))
+ goto error;
+
+ ctx = &cbuf;
+ X509V3_set_nconf(ctx, conf);
+ break;
+ }
+ case LUA_TTABLE: {
+ X509 *issuer = NULL;
+ X509 *subject = NULL;
+ X509_REQ *request = NULL;
+ X509_CRL *crl = NULL;
+ int flags = 0;
+
+ ctx = &cbuf;
+
+ if (lua_getfield(L, 3, "db") != LUA_TNIL) {
+ if (!(conf = loadconf(L, -1)))
+ goto error;
+ X509V3_set_nconf(ctx, conf);
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 3, "issuer") != LUA_TNIL) {
+ issuer = checksimple(L, -1, X509_CERT_CLASS);
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 3, "subject") != LUA_TNIL) {
+ subject = checksimple(L, -1, X509_CERT_CLASS);
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 3, "request") != LUA_TNIL) {
+ request = checksimple(L, -1, X509_CSR_CLASS);
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 3, "crl") != LUA_TNIL) {
+ crl = checksimple(L, -1, X509_CRL_CLASS);
+ }
+ lua_pop(L, 1);
+
+ if (lua_getfield(L, 3, "flags") != LUA_TNIL) {
+ flags = luaL_checkinteger(L, -1);
+ }
+ lua_pop(L, 1);
+
+ X509V3_set_ctx(ctx, issuer, subject, request, crl, flags);
+ break;
+ }
+ default:
+ return luaL_argerror(L, 3, "invalid extra parameter (expected string, table or nil)");
+ }
+
+ /*
+ * NOTE: AFAICT neither name nor value are modified. The API just
+ * doesn't have the proper const-qualifiers. See
+ * crypto/x509v3/v3_conf.c in OpenSSL.
+ *
+ * Also seems to be okay to pass NULL conf. Both NCONF_get_section
+ * and sk_CONF_VALUE_num can handle NULL arguments. See do_ext_nconf
+ * in v3_conf.c.
+ */
+ if (!(*ud = X509V3_EXT_nconf(conf, ctx, (char *)name, (char *)value)))
+ goto error;
+
+ if (conf)
+ NCONF_free(conf);
+
+ return 1;
+error:
+ if (obj)
+ ASN1_OBJECT_free(obj);
+ if (oct)
+ ASN1_STRING_free(oct);
+ if (conf)
+ NCONF_free(conf);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.extension.new");
+} /* xe_new() */
+
+
+static int xe_interpose(lua_State *L) {
+ return interpose(L, X509_EXT_CLASS);
+} /* xe_interpose() */
+
+
+static int xe_getID(lua_State *L) {
+ X509_EXTENSION *ext = checksimple(L, 1, X509_EXT_CLASS);
+ ASN1_OBJECT *obj = X509_EXTENSION_get0_object(ext);
+ char txt[256];
+ int len;
+
+ if (!(len = auxS_obj2id(txt, sizeof txt, obj)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.extension:getID");
+
+ lua_pushlstring(L, txt, len);
+
+ return 1;
+} /* xe_getID() */
+
+
+static int xe_getName(lua_State *L) {
+ X509_EXTENSION *ext = checksimple(L, 1, X509_EXT_CLASS);
+ char txt[256];
+ int len;
+
+ if (!(len = auxS_obj2txt(txt, sizeof txt, X509_EXTENSION_get0_object(ext))))
+ return auxL_error(L, auxL_EOPENSSL, "x509.extension:getName");
+
+ lua_pushlstring(L, txt, len);
+
+ return 1;
+} /* xe_getName() */
+
+
+static int xe_getShortName(lua_State *L) {
+ X509_EXTENSION *ext = checksimple(L, 1, X509_EXT_CLASS);
+ char txt[256];
+ int len;
+
+ if (!(len = auxS_obj2sn(txt, sizeof txt, X509_EXTENSION_get0_object(ext))))
+ return 0;
+
+ lua_pushlstring(L, txt, len);
+
+ return 1;
+} /* xe_getShortName() */
+
+
+static int xe_getLongName(lua_State *L) {
+ X509_EXTENSION *ext = checksimple(L, 1, X509_EXT_CLASS);
+ char txt[256];
+ int len;
+
+ if (!(len = auxS_obj2ln(txt, sizeof txt, X509_EXTENSION_get0_object(ext))))
+ return 0;
+
+ lua_pushlstring(L, txt, len);
+
+ return 1;
+} /* xe_getLongName() */
+
+
+static int xe_getData(lua_State *L) {
+ ASN1_STRING *data = X509_EXTENSION_get0_data(checksimple(L, 1, X509_EXT_CLASS));
+
+ lua_pushlstring(L, (char *)ASN1_STRING_get0_data(data), ASN1_STRING_length(data));
+
+ return 1;
+} /* xe_getData() */
+
+
+static int xe_getCritical(lua_State *L) {
+ lua_pushboolean(L, X509_EXTENSION_get_critical(checksimple(L, 1, X509_EXT_CLASS)));
+
+ return 1;
+} /* xe_getCritical() */
+
+
+static int xe_text(lua_State *L) {
+ X509_EXTENSION *ext = checksimple(L, 1, X509_EXT_CLASS);
+ unsigned long flags = auxL_optunsigned(L, 2, 0, 0, ULONG_MAX);
+ int indent = auxL_optinteger(L, 3, 0, 0, INT_MAX);
+ BIO *bio = getbio(L);
+ char *data;
+ size_t len;
+
+ if (!X509V3_EXT_print(bio, ext, flags, indent))
+ return auxL_error(L, auxL_EOPENSSL, "x509.extension:text");
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* xe_text() */
+
+
+static int xe__gc(lua_State *L) {
+ X509_EXTENSION **ud = luaL_checkudata(L, 1, X509_EXT_CLASS);
+
+ if (*ud) {
+ X509_EXTENSION_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* xe__gc() */
+
+
+static const auxL_Reg xe_methods[] = {
+ { "getID", &xe_getID },
+ { "getName", &xe_getName },
+ { "getShortName", &xe_getShortName },
+ { "getLongName", &xe_getLongName },
+ { "getData", &xe_getData },
+ { "getCritical", &xe_getCritical },
+ { "text", &xe_text },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xe_metatable[] = {
+ { "__gc", &xe__gc },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg xe_globals[] = {
+ { "new", &xe_new },
+ { "interpose", &xe_interpose },
+ { NULL, NULL },
+};
+
+static const auxL_IntegerReg xe_textopts[] = {
+ { "UNKNOWN_MASK", X509V3_EXT_UNKNOWN_MASK },
+ { "DEFAULT", X509V3_EXT_DEFAULT },
+ { "ERROR_UNKNOWN", X509V3_EXT_ERROR_UNKNOWN },
+ { "PARSE_UNKNOWN", X509V3_EXT_PARSE_UNKNOWN },
+ { "DUMP_UNKNOWN", X509V3_EXT_DUMP_UNKNOWN },
+ { NULL, 0 },
+};
+
+EXPORT int luaopen__openssl_x509_extension(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xe_globals, 0);
+ auxL_setintegers(L, xe_textopts);
+
+ return 1;
+} /* luaopen__openssl_x509_extension() */
+
+
+/*
+ * X509 - openssl.x509.cert
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void xc_dup(lua_State *L, X509 *x509) {
+ X509 **ud = prepsimple(L, X509_CERT_CLASS);
+
+ if (!(*ud = X509_dup(x509)))
+ goto error;
+
+ return;
+error:
+ auxL_error(L, auxL_EOPENSSL, "X509_dup");
+} /* xc_dup() */
+
+
+static int xc_new(lua_State *L) {
+ const char *data;
+ size_t len;
+ X509 **ud;
+
+ lua_settop(L, 2);
+
+ ud = prepsimple(L, X509_CERT_CLASS);
+
+ if ((data = luaL_optlstring(L, 1, NULL, &len))) {
+ int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER);
+ BIO *tmp;
+ int ok = 0;
+
+ if (!(tmp = BIO_new_mem_buf((char *)data, len)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert.new");
+
+ if (type == X509_PEM || type == X509_ANY) {
+ ok = !!(*ud = PEM_read_bio_X509(tmp, NULL, 0, "")); /* no password */
+ }
+
+ if (!ok && (type == X509_DER || type == X509_ANY)) {
+ ok = !!(*ud = d2i_X509_bio(tmp, NULL));
+ }
+
+ BIO_free(tmp);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert.new");
+ } else {
+ if (!(*ud = X509_new()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert.new");
+
+ X509_gmtime_adj(X509_get_notBefore(*ud), 0);
+ X509_gmtime_adj(X509_get_notAfter(*ud), 0);
+ }
+
+ return 1;
+} /* xc_new() */
+
+
+static int xc_interpose(lua_State *L) {
+ return interpose(L, X509_CERT_CLASS);
+} /* xc_interpose() */
+
+
+static int xc_getVersion(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ lua_pushinteger(L, X509_get_version(crt) + 1);
+
+ return 1;
+} /* xc_getVersion() */
+
+
+static int xc_setVersion(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ int version = luaL_checkinteger(L, 2);
+
+ if (!X509_set_version(crt, version - 1))
+ return luaL_error(L, "x509.cert:setVersion: %d: invalid version", version);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setVersion() */
+
+
+static int xc_getSerial(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ BIGNUM *serial = bn_push(L);
+ ASN1_INTEGER *i;
+
+ if ((i = X509_get_serialNumber(crt))) {
+ if (!ASN1_INTEGER_to_BN(i, serial))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:getSerial");
+ }
+
+ return 1;
+} /* xc_getSerial() */
+
+
+static int xc_setSerial(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ ASN1_INTEGER *serial;
+
+ if (!(serial = BN_to_ASN1_INTEGER(checkbig(L, 2), NULL)))
+ goto error;
+
+ if (!X509_set_serialNumber(crt, serial))
+ goto error;
+
+ ASN1_INTEGER_free(serial);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+error:
+ ASN1_INTEGER_free(serial);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setSerial");
+} /* xc_setSerial() */
+
+
+static int xc_digest(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ const char *type = luaL_optstring(L, 2, "sha1");
+ int format = luaL_checkoption(L, 3, "x", (const char *[]){ "s", "x", "n", NULL });
+ const EVP_MD *ctx;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ unsigned len;
+
+ lua_settop(L, 3); /* self, type, hex */
+
+ if (!(ctx = EVP_get_digestbyname(type)))
+ return luaL_error(L, "x509.cert:digest: %s: invalid digest type", type);
+
+ X509_digest(crt, ctx, md, &len);
+
+ switch (format) {
+ case 2: {
+ BIGNUM *bn = bn_push(L);
+
+ if (!BN_bin2bn(md, len, bn))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:digest");
+
+ break;
+ }
+ case 1: {
+ static const unsigned char x[16] = "0123456789abcdef";
+ luaL_Buffer B;
+ unsigned i;
+
+ luaL_buffinitsize(L, &B, 2 * len);
+
+ for (i = 0; i < len; i++) {
+ luaL_addchar(&B, x[0x0f & (md[i] >> 4)]);
+ luaL_addchar(&B, x[0x0f & (md[i] >> 0)]);
+ }
+
+ luaL_pushresult(&B);
+
+ break;
+ }
+ default:
+ lua_pushlstring(L, (const char *)md, len);
+
+ break;
+ } /* switch() */
+
+ return 1;
+} /* xc_digest() */
+
+
+static _Bool isleap(int year) {
+ if (year >= 0)
+ return !(year % 4) && ((year % 100) || !(year % 400));
+ else
+ return isleap(-(year + 1));
+} /* isleap() */
+
+
+static int yday(int year, int mon, int mday) {
+ static const int past[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ int yday = past[CLAMP(mon, 0, 11)] + CLAMP(mday, 1, 31) - 1;
+
+ return yday + (mon > 1 && isleap(year));
+} /* yday() */
+
+
+static int tm_yday(const struct tm *tm) {
+ return (tm->tm_yday)? tm->tm_yday : yday(1900 + tm->tm_year, tm->tm_mon, tm->tm_mday);
+} /* tm_yday() */
+
+
+static int leaps(int year) {
+ if (year >= 0)
+ return (year / 400) + (year / 4) - (year / 100);
+ else
+ return -(leaps(-(year + 1)) + 1);
+} /* leaps() */
+
+
+static double tm2unix(const struct tm *tm, int gmtoff) {
+ int year = tm->tm_year + 1900;
+ double ts;
+
+ ts = 86400.0 * 365.0 * (year - 1970);
+ ts += 86400.0 * (leaps(year - 1) - leaps(1969));
+ ts += 86400 * tm_yday(tm);
+ ts += 3600 * tm->tm_hour;
+ ts += 60 * tm->tm_min;
+ ts += CLAMP(tm->tm_sec, 0, 59);
+ ts += (year < 1970)? gmtoff : -gmtoff;
+
+ return ts;
+} /* tm2unix() */
+
+
+static _Bool scan(int *i, char **cp, int n, int signok) {
+ int sign = 1;
+
+ *i = 0;
+
+ if (signok) {
+ if (**cp == '-') {
+ sign = -1;
+ ++*cp;
+ } else if (**cp == '+') {
+ ++*cp;
+ }
+ }
+
+ while (n-- > 0) {
+ if (**cp < '0' || **cp > '9')
+ return 0;
+
+ *i *= 10;
+ *i += *(*cp)++ - '0';
+ }
+
+ *i *= sign;
+
+ return 1;
+} /* scan() */
+
+
+static double timeutc(const ASN1_TIME *time) {
+ char buf[32] = "", *cp;
+ struct tm tm = { 0 };
+ int gmtoff = 0, year, i;
+
+ if (!ASN1_TIME_check((ASN1_STRING *)time))
+ return 0;
+
+ cp = strncpy(buf, (const char *)ASN1_STRING_get0_data((ASN1_STRING *)time), sizeof buf - 1);
+
+ if (ASN1_STRING_type((ASN1_STRING *)time) == V_ASN1_GENERALIZEDTIME) {
+ if (!scan(&year, &cp, 4, 1))
+ goto badfmt;
+ } else {
+ if (!scan(&year, &cp, 2, 0))
+ goto badfmt;
+ year += (year < 50)? 2000 : 1999;
+ }
+
+ tm.tm_year = year - 1900;
+
+ if (!scan(&i, &cp, 2, 0))
+ goto badfmt;
+
+ tm.tm_mon = CLAMP(i, 1, 12) - 1;
+
+ if (!scan(&i, &cp, 2, 0))
+ goto badfmt;
+
+ tm.tm_mday = CLAMP(i, 1, 31);
+
+ tm.tm_yday = yday(year, tm.tm_mon, tm.tm_mday);
+
+ if (!scan(&i, &cp, 2, 0))
+ goto badfmt;
+
+ tm.tm_hour = CLAMP(i, 0, 23);
+
+ if (!scan(&i, &cp, 2, 0))
+ goto badfmt;
+
+ tm.tm_min = CLAMP(i, 0, 59);
+
+ if (*cp >= '0' && *cp <= '9') {
+ if (!scan(&i, &cp, 2, 0))
+ goto badfmt;
+
+ tm.tm_sec = CLAMP(i, 0, 59);
+ }
+
+ if (*cp == '+' || *cp == '-') {
+ int sign = (*cp++ == '-')? -1 : 1;
+ int hh, mm;
+
+ if (!scan(&hh, &cp, 2, 0) || !scan(&mm, &cp, 2, 0))
+ goto badfmt;
+
+ gmtoff = (CLAMP(hh, 0, 23) * 3600)
+ + (CLAMP(mm, 0, 59) * 60);
+
+ gmtoff *= sign;
+ }
+
+ return tm2unix(&tm, gmtoff);
+badfmt:
+ return INFINITY;
+} /* timeutc() */
+
+
+static int xc_getLifetime(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ double begin = INFINITY, end = INFINITY;
+ const ASN1_TIME *time;
+
+ if ((time = X509_get_notBefore(crt)))
+ begin = timeutc(time);
+
+ if ((time = X509_get_notAfter(crt)))
+ end = timeutc(time);
+
+ if (isfinite(begin))
+ lua_pushnumber(L, begin);
+ else
+ lua_pushnil(L);
+
+ if (isfinite(end))
+ lua_pushnumber(L, end);
+ else
+ lua_pushnil(L);
+
+ if (isfinite(begin) && isfinite(end) && begin <= end)
+ lua_pushnumber(L, fabs(end - begin));
+ else
+ lua_pushnumber(L, 0.0);
+
+ return 3;
+} /* xc_getLifetime() */
+
+
+static int xc_setLifetime(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ double ut;
+ const char *dt;
+
+ lua_settop(L, 3);
+
+ if (lua_isnumber(L, 2)) {
+ ut = lua_tonumber(L, 2);
+
+ if (!ASN1_TIME_set(X509_get_notBefore(crt), ut))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setLifetime");
+#if 0
+ } else if ((dt = luaL_optstring(L, 2, 0))) {
+ if (!ASN1_TIME_set_string(X509_get_notBefore(crt), dt))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setLifetime");
+#endif
+ }
+
+ if (lua_isnumber(L, 3)) {
+ ut = lua_tonumber(L, 3);
+
+ if (!ASN1_TIME_set(X509_get_notAfter(crt), ut))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setLifetime");
+#if 0
+ } else if ((dt = luaL_optstring(L, 3, 0))) {
+ if (!ASN1_TIME_set_string(X509_get_notAfter(crt), dt))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setLifetime");
+#endif
+ }
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setLifetime() */
+
+
+static int xc_getIssuer(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_NAME *name;
+
+ if (!(name = X509_get_issuer_name(crt)))
+ return 0;
+
+ xn_dup(L, name);
+
+ return 1;
+} /* xc_getIssuer() */
+
+
+static int xc_setIssuer(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_NAME *name = checksimple(L, 2, X509_NAME_CLASS);
+
+ if (!X509_set_issuer_name(crt, name))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setIssuer");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setIssuer() */
+
+
+static int xc_getSubject(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_NAME *name;
+
+ if (!(name = X509_get_subject_name(crt)))
+ return 0;
+
+ xn_dup(L, name);
+
+ return 1;
+} /* xc_getSubject() */
+
+
+static int xc_setSubject(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_NAME *name = checksimple(L, 2, X509_NAME_CLASS);
+
+ if (!X509_set_subject_name(crt, name))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setSubject");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setSubject() */
+
+
+static void xc_setCritical(X509 *crt, int nid, _Bool yes) {
+ X509_EXTENSION *ext;
+ int loc;
+
+ if ((loc = X509_get_ext_by_NID(crt, nid, -1)) >= 0
+ && (ext = X509_get_ext(crt, loc)))
+ X509_EXTENSION_set_critical(ext, yes);
+} /* xc_setCritical() */
+
+
+static _Bool xc_getCritical(X509 *crt, int nid) {
+ X509_EXTENSION *ext;
+ int loc;
+
+ if ((loc = X509_get_ext_by_NID(crt, nid, -1)) >= 0
+ && (ext = X509_get_ext(crt, loc)))
+ return X509_EXTENSION_get_critical(ext);
+ else
+ return 0;
+} /* xc_getCritical() */
+
+
+static int xc_getIssuerAlt(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ GENERAL_NAMES *gens;
+
+ if (!(gens = X509_get_ext_d2i(crt, NID_issuer_alt_name, 0, 0)))
+ return 0;
+
+ gn_dup(L, gens);
+
+ return 1;
+} /* xc_getIssuerAlt() */
+
+
+static int xc_setIssuerAlt(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ GENERAL_NAMES *gens = checksimple(L, 2, X509_GENS_CLASS);
+
+ if (!X509_add1_ext_i2d(crt, NID_issuer_alt_name, gens, 0, X509V3_ADD_REPLACE))
+ return auxL_error(L, auxL_EOPENSSL, "x509.altname:setIssuerAlt");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setIssuerAlt() */
+
+
+static int xc_getSubjectAlt(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ GENERAL_NAMES *gens;
+
+ if (!(gens = X509_get_ext_d2i(crt, NID_subject_alt_name, 0, 0)))
+ return 0;
+
+ gn_dup(L, gens);
+
+ return 1;
+} /* xc_getSubjectAlt() */
+
+
+static int xc_setSubjectAlt(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ GENERAL_NAMES *gens = checksimple(L, 2, X509_GENS_CLASS);
+
+ if (!X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, X509V3_ADD_REPLACE))
+ return auxL_error(L, auxL_EOPENSSL, "x509.altname:setSubjectAlt");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setSubjectAlt() */
+
+
+static int xc_getIssuerAltCritical(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ lua_pushboolean(L, xc_getCritical(crt, NID_issuer_alt_name));
+
+ return 1;
+} /* xc_getIssuerAltCritical() */
+
+
+static int xc_setIssuerAltCritical(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ luaL_checkany(L, 2);
+ xc_setCritical(crt, NID_issuer_alt_name, lua_toboolean(L, 2));
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setIssuerAltCritical() */
+
+
+static int xc_getSubjectAltCritical(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ lua_pushboolean(L, xc_getCritical(crt, NID_subject_alt_name));
+
+ return 1;
+} /* xc_getSubjectAltCritical() */
+
+
+static int xc_setSubjectAltCritical(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ luaL_checkany(L, 2);
+ xc_setCritical(crt, NID_subject_alt_name, lua_toboolean(L, 2));
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setSubjectAltCritical() */
+
+
+static int xc_getBasicConstraint(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ BASIC_CONSTRAINTS *bs;
+ int CA, pathLen;
+
+ if (!(bs = X509_get_ext_d2i(crt, NID_basic_constraints, 0, 0))) {
+ /* FIXME: detect error or just non-existent */
+
+ if (lua_gettop(L) > 1)
+ return 0;
+
+ lua_newtable(L);
+
+ return 1;
+ }
+
+ CA = bs->ca;
+ pathLen = ASN1_INTEGER_get(bs->pathlen);
+
+ BASIC_CONSTRAINTS_free(bs);
+
+ if (lua_gettop(L) > 1) {
+ int n = 0, i, top;
+
+ for (i = 2, top = lua_gettop(L); i <= top; i++) {
+ switch (auxL_checkoption(L, i, 0, (const char *[]){ "CA", "pathLen", "pathLenConstraint", NULL }, 1)) {
+ case 0:
+ lua_pushboolean(L, CA);
+ n++;
+ break;
+ case 1:
+ /* FALL THROUGH */
+ case 2:
+ lua_pushinteger(L, pathLen);
+ n++;
+ break;
+ }
+ }
+
+ return n;
+ } else {
+ lua_newtable(L);
+
+ lua_pushboolean(L, CA);
+ lua_setfield(L, -2, "CA");
+
+ lua_pushinteger(L, pathLen);
+ lua_setfield(L, -2, "pathLen");
+
+ return 1;
+ }
+} /* xc_getBasicConstraint() */
+
+
+static int xc_setBasicConstraint(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ BASIC_CONSTRAINTS *bs = 0;
+ int CA = -1, pathLen = -1;
+ int critical = 0;
+
+ luaL_checkany(L, 2);
+
+ if (lua_istable(L, 2)) {
+ lua_getfield(L, 2, "CA");
+ if (!lua_isnil(L, -1))
+ CA = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "pathLen");
+ pathLen = luaL_optinteger(L, -1, pathLen);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 2, "pathLenConstraint");
+ pathLen = luaL_optinteger(L, -1, pathLen);
+ lua_pop(L, 1);
+
+ if (!(bs = BASIC_CONSTRAINTS_new()))
+ goto error;
+ } else {
+ lua_settop(L, 3);
+
+ switch (auxL_checkoption(L, 2, 0, (const char *[]){ "CA", "pathLen", "pathLenConstraint", NULL }, 1)) {
+ case 0:
+ luaL_checktype(L, 3, LUA_TBOOLEAN);
+ CA = lua_toboolean(L, 3);
+
+ break;
+ case 1:
+ /* FALL THROUGH */
+ case 2:
+ pathLen = luaL_checkinteger(L, 3);
+
+ break;
+ }
+
+ if (!(bs = X509_get_ext_d2i(crt, NID_basic_constraints, &critical, 0))) {
+ /* FIXME: detect whether error or just non-existent */
+ if (!(bs = BASIC_CONSTRAINTS_new()))
+ goto error;
+ }
+ }
+
+ if (CA != -1)
+ bs->ca = CA;
+
+ if (pathLen >= 0) {
+ ASN1_INTEGER_free(bs->pathlen);
+
+ if (!(bs->pathlen = ASN1_STRING_type_new(V_ASN1_INTEGER)))
+ goto error;
+
+ if (!ASN1_INTEGER_set(bs->pathlen, pathLen))
+ goto error;
+ }
+
+ if (!X509_add1_ext_i2d(crt, NID_basic_constraints, bs, critical, X509V3_ADD_REPLACE))
+ goto error;
+
+ BASIC_CONSTRAINTS_free(bs);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+error:
+ BASIC_CONSTRAINTS_free(bs);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setBasicConstraint");
+} /* xc_setBasicConstraint() */
+
+
+static int xc_getBasicConstraintsCritical(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ lua_pushboolean(L, xc_getCritical(crt, NID_basic_constraints));
+
+ return 1;
+} /* xc_getBasicConstraintsCritical() */
+
+
+static int xc_setBasicConstraintsCritical(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ luaL_checkany(L, 2);
+ xc_setCritical(crt, NID_basic_constraints, lua_toboolean(L, 2));
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setBasicConstraintsCritical() */
+
+
+static int xc_addExtension(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_EXTENSION *ext = checksimple(L, 2, X509_EXT_CLASS);
+
+ /* NOTE: Will dup extension in X509v3_add_ext. */
+ if (!X509_add_ext(crt, ext, -1))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:addExtension");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_addExtension() */
+
+
+static int xc_setExtension(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_EXTENSION *ext = checksimple(L, 2, X509_EXT_CLASS);
+ int nid, crit;
+ void *value;
+
+ nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
+ crit = X509_EXTENSION_get_critical(ext);
+ value = X509_EXTENSION_get_data(ext);
+
+ if (!X509_add1_ext_i2d(crt, nid, value, crit, X509V3_ADD_REPLACE))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setExtension");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setExtension() */
+
+
+static int xc_getExtension(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509_EXTENSION *ext = NULL, **ud;
+ int i;
+
+ luaL_checkany(L, 2);
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ /* NB: Lua 1-based indexing */
+ i = auxL_checkinteger(L, 2, 1, INT_MAX) - 1;
+ } else {
+ ASN1_OBJECT *obj;
+
+ if (!auxS_txt2obj(&obj, luaL_checkstring(L, 2))) {
+ goto error;
+ } else if (!obj) {
+ goto undef;
+ }
+
+ i = X509_get_ext_by_OBJ(crt, obj, -1);
+
+ ASN1_OBJECT_free(obj);
+ }
+
+ ud = prepsimple(L, X509_EXT_CLASS);
+
+ if (i < 0 || !(ext = X509_get0_ext(crt, i)))
+ goto undef;
+
+ if (!(*ud = X509_EXTENSION_dup(ext)))
+ goto error;
+
+ return 1;
+undef:
+ return 0;
+error:
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:getExtension");
+} /* xc_getExtension() */
+
+
+static int xc_getExtensionCount(lua_State *L) {
+ auxL_pushinteger(L, X509_get_ext_count(checksimple(L, 1, X509_CERT_CLASS)));
+
+ return 1;
+} /* xc_getExtensionCount() */
+
+
+static int sk_openssl_string__gc(lua_State *L) {
+ STACK_OF(OPENSSL_STRING) **res = lua_touserdata(L, 1);
+
+ if (*res) {
+ sk_OPENSSL_STRING_free(*res);
+ *res = NULL;
+ }
+
+ return 0;
+} /* sk_openssl_string__gc() */
+
+
+static int xc_getOCSP(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ STACK_OF(OPENSSL_STRING) **res = prepsimple(L, NULL, &sk_openssl_string__gc);
+ int num, i;
+
+ *res = X509_get1_ocsp(crt);
+ if (!*res)
+ return 0;
+
+ num = sk_OPENSSL_STRING_num(*res);
+ luaL_checkstack(L, num, "too many authorityInfoAccess");
+ for (i = 0; i < num; i++) {
+ lua_pushstring(L, sk_OPENSSL_STRING_value(*res, i));
+ }
+
+ sk_OPENSSL_STRING_free(*res);
+ *res = NULL;
+
+ return num;
+} /* xc_getOCSP */
+
+
+static int xc_isIssuedBy(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ X509 *issuer = checksimple(L, 2, X509_CERT_CLASS);
+ EVP_PKEY *key;
+ int ok, why = 0;
+
+ ERR_clear_error();
+
+ if (X509_V_OK != (why = X509_check_issued(issuer, crt)))
+ goto done;
+
+ if (!(key = X509_get_pubkey(issuer))) {
+ why = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
+ goto done;
+ }
+
+ ok = (1 == X509_verify(crt, key));
+
+ EVP_PKEY_free(key);
+
+ if (!ok)
+ why = X509_V_ERR_CERT_SIGNATURE_FAILURE;
+
+done:
+ if (why != X509_V_OK) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, X509_verify_cert_error_string(why));
+
+ return 2;
+ } else {
+ lua_pushboolean(L, 1);
+
+ return 1;
+ }
+} /* xc_isIssuedBy() */
+
+
+static int xc_getPublicKey(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ EVP_PKEY **key = prepsimple(L, PKEY_CLASS);
+
+ if (!(*key = X509_get_pubkey(crt)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:getPublicKey");
+
+ return 1;
+} /* xc_getPublicKey() */
+
+
+static int xc_setPublicKey(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+
+ if (!X509_set_pubkey(crt, key))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:setPublicKey");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_setPublicKey() */
+
+
+static int xc_getPublicKeyDigest(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ EVP_PKEY *key;
+ const EVP_MD *md;
+ ASN1_BIT_STRING *bitstr;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int len;
+
+ if (!(key = X509_get_pubkey(crt)))
+ return luaL_argerror(L, 1, "no public key");
+ md = auxL_optdigest(L, 2, key, NULL);
+ bitstr = X509_get0_pubkey_bitstr(crt);
+
+ if (!EVP_Digest(bitstr->data, bitstr->length, digest, &len, md, NULL))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:getPublicKeyDigest");
+ lua_pushlstring(L, (char *)digest, len);
+
+ return 1;
+} /* xc_getPublicKeyDigest() */
+
+
+static int xc_getSignatureName(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ int nid;
+
+ if (NID_undef == (nid = X509_get_signature_nid(crt)))
+ return 0;
+
+ auxL_pushnid(L, nid);
+
+ return 1;
+} /* xc_getSignatureName() */
+
+
+static int xc_sign(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+
+ if (!X509_sign(crt, key, auxL_optdigest(L, 3, key, NULL)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:sign");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xc_sign() */
+
+
+static int xc_text(lua_State *L) {
+ static const struct { const char *kw; unsigned int flag; } map[] = {
+ { "no_header", X509_FLAG_NO_HEADER },
+ { "no_version", X509_FLAG_NO_VERSION },
+ { "no_serial", X509_FLAG_NO_SERIAL },
+ { "no_signame", X509_FLAG_NO_SIGNAME },
+ { "no_validity", X509_FLAG_NO_VALIDITY },
+ { "no_subject", X509_FLAG_NO_SUBJECT },
+ { "no_issuer", X509_FLAG_NO_ISSUER },
+ { "no_pubkey", X509_FLAG_NO_PUBKEY },
+ { "no_extensions", X509_FLAG_NO_EXTENSIONS },
+ { "no_sigdump", X509_FLAG_NO_SIGDUMP },
+ { "no_aux", X509_FLAG_NO_AUX },
+ { "no_attributes", X509_FLAG_NO_ATTRIBUTES },
+ { "ext_default", X509V3_EXT_DEFAULT },
+ { "ext_error", X509V3_EXT_ERROR_UNKNOWN },
+ { "ext_parse", X509V3_EXT_PARSE_UNKNOWN },
+ { "ext_dump", X509V3_EXT_DUMP_UNKNOWN }
+ };
+
+ lua_settop(L, 2);
+
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+
+ unsigned int flags = 0;
+ const char *kw;
+ int found;
+ unsigned int i;
+
+ BIO *bio = getbio(L);
+ char *data;
+ long len;
+
+ if (!lua_isnil(L, 2)) {
+ lua_pushnil(L);
+ while (lua_next(L, 2)) {
+ kw = luaL_checkstring(L, -1);
+ found = 0;
+ for (i = 0; i < countof(map); i++)
+ if (!strcmp(kw, map[i].kw)) {
+ flags |= map[i].flag;
+ found = 1;
+ }
+ if (!found)
+ luaL_argerror(L, 2, lua_pushfstring(L, "invalid flag: %s", kw));
+ lua_pop(L, 1);
+ }
+ }
+
+ if (!X509_print_ex(bio, crt, 0, flags))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:text");
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* xc_text() */
+
+
+static int xc_toPEM(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ BIO *bio = getbio(L);
+ size_t len;
+ char *bytes;
+
+ if (!PEM_write_bio_X509(bio, crt))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:toPEM");
+
+ len = BIO_get_mem_data(bio, &bytes);
+ lua_pushlstring(L, bytes, len);
+
+ return 1;
+} /* xc_toPEM() */
+
+
+static int xc__tostring(lua_State *L) {
+ X509 *crt = checksimple(L, 1, X509_CERT_CLASS);
+ int type = optencoding(L, 2, "pem", X509_PEM|X509_DER);
+ BIO *bio = getbio(L);
+ char *data;
+ long len;
+
+ switch (type) {
+ case X509_PEM:
+ if (!PEM_write_bio_X509(bio, crt))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:__tostring");
+ break;
+ case X509_DER:
+ if (!i2d_X509_bio(bio, crt))
+ return auxL_error(L, auxL_EOPENSSL, "x509.cert:__tostring");
+ break;
+ } /* switch() */
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* xc__tostring() */
+
+
+static int xc__gc(lua_State *L) {
+ X509 **ud = luaL_checkudata(L, 1, X509_CERT_CLASS);
+
+ if (*ud) {
+ X509_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* xc__gc() */
+
+
+static const auxL_Reg xc_methods[] = {
+ { "getVersion", &xc_getVersion },
+ { "setVersion", &xc_setVersion },
+ { "getSerial", &xc_getSerial },
+ { "setSerial", &xc_setSerial },
+ { "digest", &xc_digest },
+ { "getLifetime", &xc_getLifetime },
+ { "setLifetime", &xc_setLifetime },
+ { "getIssuer", &xc_getIssuer },
+ { "setIssuer", &xc_setIssuer },
+ { "getSubject", &xc_getSubject },
+ { "setSubject", &xc_setSubject },
+ { "getIssuerAlt", &xc_getIssuerAlt },
+ { "setIssuerAlt", &xc_setIssuerAlt },
+ { "getSubjectAlt", &xc_getSubjectAlt },
+ { "setSubjectAlt", &xc_setSubjectAlt },
+ { "getIssuerAltCritical", &xc_getIssuerAltCritical },
+ { "setIssuerAltCritical", &xc_setIssuerAltCritical },
+ { "getSubjectAltCritical", &xc_getSubjectAltCritical },
+ { "setSubjectAltCritical", &xc_setSubjectAltCritical },
+ { "getBasicConstraints", &xc_getBasicConstraint },
+ { "getBasicConstraint", &xc_getBasicConstraint },
+ { "setBasicConstraints", &xc_setBasicConstraint },
+ { "setBasicConstraint", &xc_setBasicConstraint },
+ { "getBasicConstraintsCritical", &xc_getBasicConstraintsCritical },
+ { "setBasicConstraintsCritical", &xc_setBasicConstraintsCritical },
+ { "addExtension", &xc_addExtension },
+ { "setExtension", &xc_setExtension },
+ { "getExtension", &xc_getExtension },
+ { "getExtensionCount", &xc_getExtensionCount },
+ { "getOCSP", &xc_getOCSP },
+ { "isIssuedBy", &xc_isIssuedBy },
+ { "getPublicKey", &xc_getPublicKey },
+ { "setPublicKey", &xc_setPublicKey },
+ { "getPublicKeyDigest", &xc_getPublicKeyDigest },
+ { "getSignatureName", &xc_getSignatureName },
+ { "sign", &xc_sign },
+ { "text", &xc_text },
+ { "toPEM", &xc_toPEM },
+ { "tostring", &xc__tostring },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xc_metatable[] = {
+ { "__tostring", &xc__tostring },
+ { "__gc", &xc__gc },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg xc_globals[] = {
+ { "new", &xc_new },
+ { "interpose", &xc_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_cert(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xc_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_x509_cert() */
+
+
+/*
+ * X509_REQ - openssl.x509.csr
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int xr_new(lua_State *L) {
+ const char *data;
+ size_t len;
+ X509_REQ **ud;
+ X509 *crt;
+
+ lua_settop(L, 2);
+
+ ud = prepsimple(L, X509_CSR_CLASS);
+
+ if ((crt = testsimple(L, 1, X509_CERT_CLASS))) {
+ if (!(*ud = X509_to_X509_REQ(crt, 0, 0)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr.new");
+ } else if ((data = luaL_optlstring(L, 1, NULL, &len))) {
+ int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER);
+ BIO *tmp;
+ int ok = 0;
+
+ if (!(tmp = BIO_new_mem_buf((char *)data, len)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr.new");
+
+ if (type == X509_PEM || type == X509_ANY) {
+ ok = !!(*ud = PEM_read_bio_X509_REQ(tmp, NULL, 0, "")); /* no password */
+ }
+
+ if (!ok && (type == X509_DER || type == X509_ANY)) {
+ ok = !!(*ud = d2i_X509_REQ_bio(tmp, NULL));
+ }
+
+ BIO_free(tmp);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr.new");
+ } else {
+ if (!(*ud = X509_REQ_new()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr.new");
+ }
+
+ return 1;
+} /* xr_new() */
+
+
+static int xr_interpose(lua_State *L) {
+ return interpose(L, X509_CSR_CLASS);
+} /* xr_interpose() */
+
+
+static int xr_getVersion(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+
+ lua_pushinteger(L, X509_REQ_get_version(csr) + 1);
+
+ return 1;
+} /* xr_getVersion() */
+
+
+static int xr_setVersion(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ int version = luaL_checkinteger(L, 2);
+
+ if (!X509_REQ_set_version(csr, version - 1))
+ return luaL_error(L, "x509.csr:setVersion: %d: invalid version", version);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xr_setVersion() */
+
+
+static int xr_getSubject(lua_State *L) {
+ X509_REQ *crt = checksimple(L, 1, X509_CSR_CLASS);
+ X509_NAME *name;
+
+ if (!(name = X509_REQ_get_subject_name(crt)))
+ return 0;
+
+ xn_dup(L, name);
+
+ return 1;
+} /* xr_getSubject() */
+
+
+static int xr_setSubject(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ X509_NAME *name = checksimple(L, 2, X509_NAME_CLASS);
+
+ if (!X509_REQ_set_subject_name(csr, name))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:setSubject");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xr_setSubject() */
+
+
+static int xr_getPublicKey(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ EVP_PKEY **key = prepsimple(L, PKEY_CLASS);
+
+ if (!(*key = X509_REQ_get_pubkey(csr)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:getPublicKey");
+
+ return 1;
+} /* xr_getPublicKey() */
+
+
+static int xr_setPublicKey(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+
+ if (!X509_REQ_set_pubkey(csr, key))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:setPublicKey");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xr_setPublicKey() */
+
+
+static int xr_modifyRequestedExtension(X509_REQ *csr, int target_nid, int crit, void* value, unsigned long flags) {
+ STACK_OF(X509_EXTENSION) *sk = NULL;
+ int has_attrs=0;
+
+ /*
+ * Replace existing if it's there. Extensions are stored in a CSR in
+ * an interesting way:
+ *
+ * They are stored as a list under either (most likely) the
+ * "official" NID_ext_req or under NID_ms_ext_req which means
+ * everything is stored under a list in a single "attribute" so we
+ * can't use X509_REQ_add1_attr or similar.
+ *
+ * Instead we have to get the extensions, find and replace the extension
+ * if it's in there, then *replace* the extensions in the list of
+ * attributes. (If we just try to add it the old ones are found
+ * first and don't take priority.)
+ */
+ has_attrs = X509_REQ_get_attr_count(csr);
+
+ sk = X509_REQ_get_extensions(csr);
+ if (!X509V3_add1_i2d(&sk, target_nid, value, crit, flags))
+ goto error;
+ if (X509_REQ_add_extensions(csr, sk) == 0)
+ goto error;
+ sk_X509_EXTENSION_pop_free(sk, X509_EXTENSION_free);
+ sk = NULL;
+
+ /*
+ * Delete the old extensions attribute, so that the one we just
+ * added takes priority.
+ */
+ if (has_attrs) {
+ X509_ATTRIBUTE *attr = NULL;
+ int idx, *pnid;
+
+ for (pnid = X509_REQ_get_extension_nids(); *pnid != NID_undef; pnid++) {
+ idx = X509_REQ_get_attr_by_NID(csr, *pnid, -1);
+ if (idx == -1)
+ continue;
+ if (!(attr = X509_REQ_delete_attr(csr, idx)))
+ goto error;
+ X509_ATTRIBUTE_free(attr);
+ break;
+ }
+ if (!attr)
+ goto error;
+ }
+
+ /*
+ * We have to mark the encoded form as invalid, otherwise when we
+ * write it out again it will use the loaded version.
+ */
+#if HAVE_I2D_RE_X509_REQ_TBS
+ (void)i2d_re_X509_REQ_tbs(csr, NULL); /* sets csr->req_info->enc.modified */
+#else
+ csr->req_info->enc.modified = 1;
+#endif
+
+ return 0;
+error:
+ if (sk)
+ sk_X509_EXTENSION_pop_free(sk, X509_EXTENSION_free);
+
+ return 1;
+} /* xr_modifyRequestedExtension() */
+
+
+static int xr_setSubjectAlt(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ GENERAL_NAMES *gens = checksimple(L, 2, X509_GENS_CLASS);
+
+ if (xr_modifyRequestedExtension(csr, NID_subject_alt_name, 0, gens, X509V3_ADD_REPLACE))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:setSubjectAlt");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xr_setSubjectAlt */
+
+
+static int xr_getSubjectAlt(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ STACK_OF(X509_EXTENSION) *exts;
+ GENERAL_NAMES *gens;
+
+ exts = X509_REQ_get_extensions(csr);
+ gens = X509V3_get_d2i(exts, NID_subject_alt_name, NULL, NULL);
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ if (!gens) goto error;
+
+ gn_dup(L, gens);
+
+ return 1;
+error:
+ return 0;
+} /* xr_getSubjectAlt() */
+
+
+static int xr_addRequestedExtension(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ X509_EXTENSION *ext = checksimple(L, 2, X509_EXT_CLASS);
+ int nid, crit;
+ void *value;
+
+ nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
+ crit = X509_EXTENSION_get_critical(ext);
+ value = X509_EXTENSION_get_data(ext);
+
+ if (xr_modifyRequestedExtension(csr, nid, crit, value, X509V3_ADD_APPEND))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:addRequestedExtension");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xr_addRequestedExtension() */
+
+
+static int xr_setRequestedExtension(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ X509_EXTENSION *ext = checksimple(L, 2, X509_EXT_CLASS);
+ int nid, crit;
+ void *value;
+
+ nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
+ crit = X509_EXTENSION_get_critical(ext);
+ value = X509_EXTENSION_get_data(ext);
+
+ if (xr_modifyRequestedExtension(csr, nid, crit, value, X509V3_ADD_REPLACE))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:setRequestedExtension");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xr_setRequestedExtension() */
+
+
+static int xr_getRequestedExtension(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ STACK_OF(X509_EXTENSION) *exts = NULL;
+ X509_EXTENSION *ext = NULL, **ud;
+ int i;
+
+ luaL_checkany(L, 2);
+
+ ud = prepsimple(L, X509_EXT_CLASS);
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ /* NB: Lua 1-based indexing */
+ i = auxL_checkinteger(L, 2, 1, INT_MAX) - 1;
+ exts = X509_REQ_get_extensions(csr);
+ } else {
+ ASN1_OBJECT *obj;
+
+ if (!auxS_txt2obj(&obj, luaL_checkstring(L, 2))) {
+ goto error;
+ } else if (!obj) {
+ goto undef;
+ }
+
+ exts = X509_REQ_get_extensions(csr);
+ i = X509v3_get_ext_by_OBJ(exts, obj, -1);
+
+ ASN1_OBJECT_free(obj);
+ }
+
+ if (i < 0 || !(ext = X509v3_get_ext(exts, i)))
+ goto undef;
+
+ if (!(*ud = X509_EXTENSION_dup(ext)))
+ goto error;
+
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ exts = NULL;
+
+ return 1;
+undef:
+ if (exts)
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ return 0;
+error:
+ if (exts)
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:getRequestedExtension");
+} /* xr_getRequestedExtension() */
+
+
+static int xr_getRequestedExtensionCount(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ STACK_OF(X509_EXTENSION) *exts = NULL;
+ auxL_Integer len = 0;
+
+ exts = X509_REQ_get_extensions(csr);
+ len = sk_X509_EXTENSION_num(exts);
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+
+ auxL_pushinteger(L, len);
+
+ return 1;
+} /* xr_getRequestedExtensionCount() */
+
+
+static int xr_sign(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+
+ if (!X509_REQ_sign(csr, key, auxL_optdigest(L, 3, key, NULL)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:sign");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xr_sign() */
+
+
+static int xr_toPEM(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ BIO *bio = getbio(L);
+ size_t len;
+ char *bytes;
+
+ if (!PEM_write_bio_X509_REQ(bio, csr))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:toPEM");
+
+ len = BIO_get_mem_data(bio, &bytes);
+ lua_pushlstring(L, bytes, len);
+
+ return 1;
+} /* xr_toPEM() */
+
+
+static int xr__tostring(lua_State *L) {
+ X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS);
+ int type = optencoding(L, 2, "pem", X509_PEM|X509_DER);
+ BIO *bio = getbio(L);
+ char *data;
+ long len;
+
+ switch (type) {
+ case X509_PEM:
+ if (!PEM_write_bio_X509_REQ(bio, csr))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:__tostring");
+ break;
+ case X509_DER:
+ if (!i2d_X509_REQ_bio(bio, csr))
+ return auxL_error(L, auxL_EOPENSSL, "x509.csr:__tostring");
+ break;
+ } /* switch() */
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* xr__tostring() */
+
+
+static int xr__gc(lua_State *L) {
+ X509_REQ **ud = luaL_checkudata(L, 1, X509_CSR_CLASS);
+
+ if (*ud) {
+ X509_REQ_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* xr__gc() */
+
+static const auxL_Reg xr_methods[] = {
+ { "getVersion", &xr_getVersion },
+ { "setVersion", &xr_setVersion },
+ { "getSubject", &xr_getSubject },
+ { "setSubject", &xr_setSubject },
+ { "getPublicKey", &xr_getPublicKey },
+ { "setPublicKey", &xr_setPublicKey },
+ { "getSubjectAlt", &xr_getSubjectAlt },
+ { "setSubjectAlt", &xr_setSubjectAlt },
+ { "getRequestedExtension", &xr_getRequestedExtension },
+ { "getRequestedExtensionCount", &xr_getRequestedExtensionCount },
+ { "addRequestedExtension", &xr_addRequestedExtension },
+ { "setRequestedExtension", &xr_setRequestedExtension },
+ { "sign", &xr_sign },
+ { "toPEM", &xr_toPEM },
+ { "tostring", &xr__tostring },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xr_metatable[] = {
+ { "__tostring", &xr__tostring },
+ { "__gc", &xr__gc },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg xr_globals[] = {
+ { "new", &xr_new },
+ { "interpose", &xr_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_csr(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xr_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_x509_csr() */
+
+
+/*
+ * X509_CRL - openssl.x509.crl
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int xx_new(lua_State *L) {
+ const char *data;
+ size_t len;
+ X509_CRL **ud;
+
+ lua_settop(L, 2);
+
+ ud = prepsimple(L, X509_CRL_CLASS);
+
+ if ((data = luaL_optlstring(L, 1, NULL, &len))) {
+ int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER);
+ BIO *tmp;
+ int ok = 0;
+
+ if (!(tmp = BIO_new_mem_buf((char *)data, len)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl.new");
+
+ if (type == X509_PEM || type == X509_ANY) {
+ ok = !!(*ud = PEM_read_bio_X509_CRL(tmp, NULL, 0, "")); /* no password */
+ }
+
+ if (!ok && (type == X509_DER || type == X509_ANY)) {
+ ok = !!(*ud = d2i_X509_CRL_bio(tmp, NULL));
+ }
+
+ BIO_free(tmp);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl.new");
+ } else {
+ ASN1_TIME *tm;
+
+ if (!(*ud = X509_CRL_new()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl.new");
+
+ /* initialize last updated time to now */
+ if (!(tm = ASN1_TIME_set(NULL, time(NULL))))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl.new");
+
+ if (!X509_CRL_set1_lastUpdate(*ud, tm)) {
+ ASN1_TIME_free(tm);
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl.new");
+ }
+
+ ASN1_TIME_free(tm);
+ }
+
+ return 1;
+} /* xx_new() */
+
+
+static int xx_interpose(lua_State *L) {
+ return interpose(L, X509_CRL_CLASS);
+} /* xx_interpose() */
+
+
+static int xx_getVersion(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+
+ lua_pushinteger(L, X509_CRL_get_version(crl) + 1);
+
+ return 1;
+} /* xx_getVersion() */
+
+
+static int xx_setVersion(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ int version = luaL_checkinteger(L, 2);
+
+ if (!X509_CRL_set_version(crl, version - 1))
+ return luaL_error(L, "x509.crl:setVersion: %d: invalid version", version);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xx_setVersion() */
+
+
+static int xx_getLastUpdate(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ double updated = INFINITY;
+ const ASN1_TIME *time;
+
+ if ((time = X509_CRL_get0_lastUpdate(crl)))
+ updated = timeutc(time);
+
+ if (isfinite(updated))
+ lua_pushnumber(L, updated);
+ else
+ lua_pushnil(L);
+
+ return 1;
+} /* xx_getLastUpdate() */
+
+
+static int xx_setLastUpdate(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ double updated = luaL_checknumber(L, 2);
+ ASN1_TIME *time;
+
+ if (!(time = ASN1_TIME_set(NULL, updated)))
+ goto error;
+
+ if (!X509_CRL_set1_lastUpdate(crl, time))
+ goto error;
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+error:
+ ASN1_TIME_free(time);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:setLastUpdate");
+} /* xx_setLastUpdate() */
+
+
+static int xx_getNextUpdate(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ double updateby = INFINITY;
+ const ASN1_TIME *time;
+
+ if ((time = X509_CRL_get0_nextUpdate(crl)))
+ updateby = timeutc(time);
+
+ if (isfinite(updateby))
+ lua_pushnumber(L, updateby);
+ else
+ lua_pushnil(L);
+
+ return 1;
+} /* xx_getNextUpdate() */
+
+
+static int xx_setNextUpdate(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ double updateby = luaL_checknumber(L, 2);
+ ASN1_TIME *time;
+
+ if (!(time = ASN1_TIME_set(NULL, updateby)))
+ goto error;
+
+ if (!X509_CRL_set1_nextUpdate(crl, time))
+ goto error;
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+error:
+ ASN1_TIME_free(time);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:setNextUpdate");
+} /* xx_setNextUpdate() */
+
+
+static int xx_getIssuer(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ X509_NAME *name;
+
+ if (!(name = X509_CRL_get_issuer(crl)))
+ return 0;
+
+ xn_dup(L, name);
+
+ return 1;
+} /* xx_getIssuer() */
+
+
+static int xx_setIssuer(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ X509_NAME *name = checksimple(L, 2, X509_NAME_CLASS);
+
+ if (!X509_CRL_set_issuer_name(crl, name))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:setIssuer");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xx_setIssuer() */
+
+
+static int xx_add(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ BIGNUM *bn = checkbig(L, 2);
+ double ut = luaL_optnumber(L, 3, time(NULL));
+ X509_REVOKED *rev = NULL;
+ ASN1_INTEGER *serial = NULL;
+ ASN1_TIME *date = NULL;
+
+ if (!(rev = X509_REVOKED_new()))
+ goto error;
+
+ if (!(serial = BN_to_ASN1_INTEGER(bn, NULL)))
+ goto error;
+
+ if (!X509_REVOKED_set_serialNumber(rev, serial)) /* duplicates serial */
+ goto error;
+
+ ASN1_INTEGER_free(serial);
+ serial = NULL;
+
+ if (!(date = ASN1_TIME_new()))
+ goto error;
+
+ if (!ASN1_TIME_set(date, ut))
+ goto error;
+
+ if (!X509_REVOKED_set_revocationDate(rev, date)) /* duplicates date */
+ goto error;
+
+ ASN1_TIME_free(date);
+ date = NULL;
+
+ if (!X509_CRL_add0_revoked(crl, rev)) /* takes ownership of rev */
+ goto error;
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+error:
+ if (date)
+ ASN1_TIME_free(date);
+ if (serial)
+ ASN1_INTEGER_free(serial);
+ if (rev)
+ X509_REVOKED_free(rev);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:add");
+} /* xx_add() */
+
+
+#if HAVE_X509_CRL_GET0_BY_SERIAL
+static int xx_lookupSerial(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ ASN1_INTEGER *serial;
+ int status;
+
+ if (!(serial = BN_to_ASN1_INTEGER(checkbig(L, 2), NULL)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:lookupSerial");
+
+ status = X509_CRL_get0_by_serial(crl, NULL, serial);
+
+ ASN1_INTEGER_free(serial);
+
+ switch(status) {
+ case 0: /* failure (not on CRL) */
+ lua_pushnil(L);
+ return 1;
+ case 1: /* succeeds (on CRL) */
+ lua_pushboolean(L, 1);
+ return 1;
+ case 2: /* *was* on CRL, but not any more */
+ lua_pushboolean(L, 0);
+ return 1;
+ default:
+ return luaL_error(L, "x509.crl:lookupSerial: unexpected return value");
+ }
+} /* xx_lookupSerial() */
+#endif
+
+
+#if HAVE_X509_CRL_GET0_BY_CERT
+static int xx_lookupCertificate(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ X509 *crt = checksimple(L, 2, X509_CERT_CLASS);
+
+ switch(X509_CRL_get0_by_cert(crl, NULL, crt)) {
+ case 0: /* failure (not on CRL) */
+ lua_pushnil(L);
+ return 1;
+ case 1: /* succeeds (on CRL) */
+ lua_pushboolean(L, 1);
+ return 1;
+ case 2: /* *was* on CRL, but not any more */
+ lua_pushboolean(L, 0);
+ return 1;
+ default:
+ return luaL_error(L, "x509.crl:lookupCertificate: unexpected return value");
+ }
+} /* xx_lookupCertificate() */
+#endif
+
+
+static int xx_addExtension(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ X509_EXTENSION *ext = checksimple(L, 2, X509_EXT_CLASS);
+
+ /* NOTE: Will dup extension in X509v3_add_ext. */
+ if (!X509_CRL_add_ext(crl, ext, -1))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:addExtension");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xx_addExtension() */
+
+
+static int xx_setExtension(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ X509_EXTENSION *ext = checksimple(L, 2, X509_EXT_CLASS);
+ int nid, crit;
+ void *value;
+
+ nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
+ crit = X509_EXTENSION_get_critical(ext);
+ value = X509_EXTENSION_get_data(ext);
+
+ if (!X509_CRL_add1_ext_i2d(crl, nid, value, crit, X509V3_ADD_REPLACE))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:setExtension");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xx_setExtension() */
+
+
+static int xx_getExtension(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ X509_EXTENSION *ext = NULL, **ud;
+ int i;
+
+ luaL_checkany(L, 2);
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ /* NB: Lua 1-based indexing */
+ i = auxL_checkinteger(L, 2, 1, INT_MAX) - 1;
+ } else {
+ ASN1_OBJECT *obj;
+
+ if (!auxS_txt2obj(&obj, luaL_checkstring(L, 2))) {
+ goto error;
+ } else if (!obj) {
+ goto undef;
+ }
+
+ i = X509_CRL_get_ext_by_OBJ(crl, obj, -1);
+
+ ASN1_OBJECT_free(obj);
+ }
+
+ ud = prepsimple(L, X509_EXT_CLASS);
+
+ if (i < 0 || !(ext = X509_CRL_get0_ext(crl, i)))
+ goto undef;
+
+ if (!(*ud = X509_EXTENSION_dup(ext)))
+ goto error;
+
+ return 1;
+undef:
+ return 0;
+error:
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:getExtension");
+} /* xx_getExtension() */
+
+
+static int xx_getExtensionCount(lua_State *L) {
+ auxL_pushinteger(L, X509_CRL_get_ext_count(checksimple(L, 1, X509_CRL_CLASS)));
+
+ return 1;
+} /* xx_getExtensionCount() */
+
+
+static int xx_sign(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+
+ if (!X509_CRL_sign(crl, key, auxL_optdigest(L, 3, key, NULL)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:sign");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xx_sign() */
+
+
+static int xx_verify(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+
+ if (!X509_CRL_verify(crl, key))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:verify");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* xx_verify() */
+
+
+static int xx_text(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+
+ BIO *bio = getbio(L);
+ char *data;
+ long len;
+
+ if (!X509_CRL_print(bio, crl))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:text");
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* xx_text() */
+
+
+static int xx_toPEM(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ BIO *bio = getbio(L);
+ size_t len;
+ char *bytes;
+
+ if (!PEM_write_bio_X509_CRL(bio, crl))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:toPEM");
+
+ len = BIO_get_mem_data(bio, &bytes);
+ lua_pushlstring(L, bytes, len);
+
+ return 1;
+} /* xx_toPEM() */
+
+
+static int xx__tostring(lua_State *L) {
+ X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS);
+ int type = optencoding(L, 2, "pem", X509_PEM|X509_DER);
+ BIO *bio = getbio(L);
+ char *data;
+ long len;
+
+ switch (type) {
+ case X509_PEM:
+ if (!PEM_write_bio_X509_CRL(bio, crl))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:__tostring");
+ break;
+ case X509_DER:
+ if (!i2d_X509_CRL_bio(bio, crl))
+ return auxL_error(L, auxL_EOPENSSL, "x509.crl:__tostring");
+ break;
+ } /* switch() */
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* xx__tostring() */
+
+
+static int xx__gc(lua_State *L) {
+ X509_CRL **ud = luaL_checkudata(L, 1, X509_CRL_CLASS);
+
+ if (*ud) {
+ X509_CRL_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* xx__gc() */
+
+static const auxL_Reg xx_methods[] = {
+ { "getVersion", &xx_getVersion },
+ { "setVersion", &xx_setVersion },
+ { "getLastUpdate", &xx_getLastUpdate },
+ { "setLastUpdate", &xx_setLastUpdate },
+ { "getNextUpdate", &xx_getNextUpdate },
+ { "setNextUpdate", &xx_setNextUpdate },
+ { "getIssuer", &xx_getIssuer },
+ { "setIssuer", &xx_setIssuer },
+ { "add", &xx_add },
+#if HAVE_X509_CRL_GET0_BY_SERIAL
+ { "lookupSerial", &xx_lookupSerial },
+#endif
+#if HAVE_X509_CRL_GET0_BY_CERT
+ { "lookupCertificate", &xx_lookupCertificate },
+#endif
+ { "addExtension", &xx_addExtension },
+ { "setExtension", &xx_setExtension },
+ { "getExtension", &xx_getExtension },
+ { "getExtensionCount", &xx_getExtensionCount },
+ { "sign", &xx_sign },
+ { "verify", &xx_verify },
+ { "text", &xx_text },
+ { "toPEM", &xx_toPEM },
+ { "tostring", &xx__tostring },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xx_metatable[] = {
+ { "__tostring", &xx__tostring },
+ { "__gc", &xx__gc },
+ { NULL, NULL },
+};
+
+
+static const auxL_Reg xx_globals[] = {
+ { "new", &xx_new },
+ { "interpose", &xx_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_crl(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xx_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_x509_crl() */
+
+
+/*
+ * STACK_OF(X509) - openssl.x509.chain
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void xl_dup(lua_State *L, STACK_OF(X509) *src, _Bool copy) {
+ STACK_OF(X509) **dst = prepsimple(L, X509_CHAIN_CLASS);
+ X509 *crt;
+ int i, n;
+
+ if (copy) {
+ if (!(*dst = sk_X509_new_null()))
+ goto error;
+
+ n = sk_X509_num(src);
+
+ for (i = 0; i < n; i++) {
+ if (!(crt = sk_X509_value(src, i)))
+ continue;
+
+ if (!(crt = X509_dup(crt)))
+ goto error;
+
+ if (!sk_X509_push(*dst, crt)) {
+ X509_free(crt);
+ goto error;
+ }
+ }
+ } else {
+ if (!(*dst = sk_X509_dup(src)))
+ goto error;
+
+ n = sk_X509_num(*dst);
+
+ for (i = 0; i < n; i++) {
+ if (!(crt = sk_X509_value(*dst, i)))
+ continue;
+ X509_up_ref(crt);
+ }
+ }
+
+ return;
+error:
+ auxL_error(L, auxL_EOPENSSL, "sk_X509_dup");
+} /* xl_dup() */
+
+
+static int xl_new(lua_State *L) {
+ STACK_OF(X509) **chain = prepsimple(L, X509_CHAIN_CLASS);
+
+ if (!(*chain = sk_X509_new_null()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.chain.new");
+
+ return 1;
+} /* xl_new() */
+
+
+static int xl_interpose(lua_State *L) {
+ return interpose(L, X509_CHAIN_CLASS);
+} /* xl_interpose() */
+
+
+static int xl_add(lua_State *L) {
+ STACK_OF(X509) *chain = checksimple(L, 1, X509_CHAIN_CLASS);
+ X509 *crt = checksimple(L, 2, X509_CERT_CLASS);
+ X509 *dup;
+
+ if (!(dup = X509_dup(crt)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.chain:add");
+
+ if (!sk_X509_push(chain, dup)) {
+ X509_free(dup);
+ return auxL_error(L, auxL_EOPENSSL, "x509.chain:add");
+ }
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+} /* xl_add() */
+
+
+static int xl__next(lua_State *L) {
+ STACK_OF(X509) *chain = checksimple(L, lua_upvalueindex(1), X509_CHAIN_CLASS);
+ int i = lua_tointeger(L, lua_upvalueindex(2));
+ int n = sk_X509_num(chain);
+
+ lua_settop(L, 0);
+
+ while (i < n) {
+ X509 *crt, **ret;
+
+ if (!(crt = sk_X509_value(chain, i++)))
+ continue;
+
+ lua_pushinteger(L, i);
+
+ xc_dup(L, crt);
+
+ break;
+ }
+
+ lua_pushinteger(L, i);
+ lua_replace(L, lua_upvalueindex(2));
+
+ return lua_gettop(L);
+} /* xl__next() */
+
+static int xl__pairs(lua_State *L) {
+ lua_settop(L, 1);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, &xl__next, 2);
+
+ return 1;
+} /* xl__pairs() */
+
+
+static int xl__gc(lua_State *L) {
+ STACK_OF(X509) **chain = luaL_checkudata(L, 1, X509_CHAIN_CLASS);
+
+ if (*chain) {
+ sk_X509_pop_free(*chain, X509_free);
+ *chain = NULL;
+ }
+
+ return 0;
+} /* xl__gc() */
+
+
+static const auxL_Reg xl_methods[] = {
+ { "add", &xl_add },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xl_metatable[] = {
+ { "__pairs", &xl__pairs },
+ { "__ipairs", &xl__pairs },
+ { "__gc", &xl__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xl_globals[] = {
+ { "new", &xl_new },
+ { "interpose", &xl_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_chain(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xl_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_x509_chain() */
+
+
+/*
+ * X509_STORE - openssl.x509.store
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int xs_new(lua_State *L) {
+ X509_STORE **ud = prepsimple(L, X509_STORE_CLASS);
+
+ if (!(*ud = X509_STORE_new()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.store");
+
+ return 1;
+} /* xs_new() */
+
+
+static X509_STORE *xs_push(lua_State *L, X509_STORE *store) {
+ X509_STORE **ud = prepsimple(L, X509_STORE_CLASS);
+
+ X509_STORE_up_ref(store);
+ *ud = store;
+
+ return *ud;
+} /* xs_push() */
+
+
+static int xs_interpose(lua_State *L) {
+ return interpose(L, X509_STORE_CLASS);
+} /* xs_interpose() */
+
+
+static int xs_add(lua_State *L) {
+ X509_STORE *store = checksimple(L, 1, X509_STORE_CLASS);
+ int i, top = lua_gettop(L);
+ X509 *crt, *crt_dup;
+ X509_CRL *crl, *crl_dup;
+
+ for (i = 2; i <= top; i++) {
+ if ((crt = testsimple(L, i, X509_CERT_CLASS))) {
+ if (!(crt_dup = X509_dup(crt)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.store:add");
+
+ if (!X509_STORE_add_cert(store, crt_dup)) {
+ X509_free(crt_dup);
+ return auxL_error(L, auxL_EOPENSSL, "x509.store:add");
+ }
+ } else if ((crl = testsimple(L, i, X509_CRL_CLASS))) {
+ if (!(crl_dup = X509_CRL_dup(crl)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.store:add");
+
+ if (!X509_STORE_add_crl(store, crl_dup)) {
+ X509_CRL_free(crl_dup);
+ return auxL_error(L, auxL_EOPENSSL, "x509.store:add");
+ }
+ } else {
+ const char *path = luaL_checkstring(L, i);
+ struct stat st;
+ int ok;
+
+ if (0 != stat(path, &st))
+ return luaL_error(L, "%s: %s", path, aux_strerror(errno));
+
+ if (S_ISDIR(st.st_mode))
+ ok = X509_STORE_load_locations(store, NULL, path);
+ else {
+ /* X509_STORE_load_locations on a file returns 0 if no certs were found */
+ ERR_clear_error();
+ ok = X509_STORE_load_locations(store, path, NULL);
+ if (ok == 0 && !ERR_peek_error())
+ ok = 1;
+ }
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "x509.store:add");
+ }
+ }
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+} /* xs_add() */
+
+
+static int xs_addDefaults(lua_State *L) {
+ X509_STORE *store = checksimple(L, 1, X509_STORE_CLASS);
+
+ if (!X509_STORE_set_default_paths(store))
+ return auxL_error(L, auxL_EOPENSSL, "x509.store:addDefaults");
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+} /* xs_addDefaults() */
+
+
+static int xs_verify(lua_State *L) {
+ X509_STORE *store = checksimple(L, 1, X509_STORE_CLASS);
+ X509 *crt = checksimple(L, 2, X509_CERT_CLASS);
+ STACK_OF(X509) *chain = NULL, **proof;
+ X509_STORE_CTX *ctx = NULL;
+ int ok, why;
+
+ /* pre-allocate space for a successful return */
+ lua_settop(L, 3);
+ proof = prepsimple(L, X509_CHAIN_CLASS);
+
+ if (!lua_isnoneornil(L, 3)) {
+ X509 *elm;
+ int i, n;
+
+ if (!(chain = sk_X509_dup(checksimple(L, 3, X509_CHAIN_CLASS))))
+ goto eossl;
+
+ n = sk_X509_num(chain);
+
+ for (i = 0; i < n; i++) {
+ if (!(elm = sk_X509_value(chain, i)))
+ continue;
+ X509_up_ref(elm);
+ }
+ }
+
+ if (!(ctx = X509_STORE_CTX_new()) || !X509_STORE_CTX_init(ctx, store, crt, chain)) {
+ sk_X509_pop_free(chain, X509_free);
+ goto eossl;
+ }
+
+ ERR_clear_error();
+
+ ok = X509_verify_cert(ctx);
+
+ switch (ok) {
+ case 1: /* verified */
+ if (!(*proof = X509_STORE_CTX_get1_chain(ctx)))
+ goto eossl;
+ X509_STORE_CTX_free(ctx);
+
+ lua_pushboolean(L, 1);
+ lua_pushvalue(L, -2);
+
+ return 2;
+ case 0: /* not verified */
+ why = X509_STORE_CTX_get_error(ctx);
+ X509_STORE_CTX_free(ctx);
+
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, X509_verify_cert_error_string(why));
+
+ return 2;
+ default:
+ goto eossl;
+ }
+
+eossl:
+ if (ctx)
+ X509_STORE_CTX_free(ctx);
+
+ return auxL_error(L, auxL_EOPENSSL, "x509.store:verify");
+} /* xs_verify() */
+
+
+static int xs__gc(lua_State *L) {
+ X509_STORE **ud = luaL_checkudata(L, 1, X509_STORE_CLASS);
+
+ if (*ud) {
+ X509_STORE_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* xs__gc() */
+
+
+static const auxL_Reg xs_methods[] = {
+ { "add", &xs_add },
+ { "addDefaults", &xs_addDefaults },
+ { "verify", &xs_verify },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xs_metatable[] = {
+ { "__gc", &xs__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xs_globals[] = {
+ { "new", &xs_new },
+ { "interpose", &xs_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_store(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xs_globals, 0);
+
+ lua_pushstring(L, X509_get_default_cert_dir());
+ lua_setfield(L, -2, "CERT_DIR");
+ lua_pushstring(L, X509_get_default_cert_file());
+ lua_setfield(L, -2, "CERT_FILE");
+ lua_pushstring(L, X509_get_default_cert_dir_env());
+ lua_setfield(L, -2, "CERT_DIR_EVP");
+ lua_pushstring(L, X509_get_default_cert_file_env());
+ lua_setfield(L, -2, "CERT_FILE_EVP");
+
+ return 1;
+} /* luaopen__openssl_x509_store() */
+
+
+/*
+ * X509_STORE_CTX - openssl.x509.store.context
+ *
+ * This object is intended to be a temporary container in OpenSSL, so the
+ * memory management is quite clumsy. In particular, it doesn't take
+ * ownership of the X509_STORE object, which means the reference must be
+ * held externally for the life of the X509_STORE_CTX object.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#if 0
+static int stx_new(lua_State *L) {
+ X509_STORE_CTX **ud = prepsimple(L, X509_STCTX_CLASS);
+ STACK_OF(X509) *chain;
+
+ if (!(*ud = X509_STORE_CTX_new()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.store.context");
+
+ return 1;
+} /* stx_new() */
+
+
+static int stx_interpose(lua_State *L) {
+ return interpose(L, X509_STCTX_CLASS);
+} /* stx_interpose() */
+
+
+static int stx_add(lua_State *L) {
+ X509_STORE_CTX *ctx = checksimple(L, 1, X509_STCTX_CLASS);
+
+ return 0;
+} /* stx_add() */
+
+
+static int stx__gc(lua_State *L) {
+ X509_STORE **ud = luaL_checkudata(L, 1, X509_STORE_CLASS);
+
+ if (*ud) {
+ X509_STORE_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* stx__gc() */
+
+
+static const auxL_Reg stx_methods[] = {
+ { "add", &stx_add },
+ { NULL, NULL },
+};
+
+static const auxL_Reg stx_metatable[] = {
+ { "__gc", &stx__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg stx_globals[] = {
+ { "new", &stx_new },
+ { "interpose", &stx_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_x509_store_context(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, stx_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_x509_store_context() */
+#endif
+
+
+/*
+ * PKCS12 - openssl.pkcs12
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int p12_new(lua_State *L) {
+ char *pass = NULL;
+ loadfield(L, 1, "password", LUA_TSTRING, &pass);
+
+ EVP_PKEY *key = loadfield_udata(L, 1, "key", PKEY_CLASS);
+ STACK_OF(X509) *certs = loadfield_udata(L, 1, "certs", X509_CHAIN_CLASS);
+
+ PKCS12 **ud = prepsimple(L, PKCS12_CLASS);
+
+ int i;
+ int no_kcert = 0;
+ X509 *cert = NULL;
+ X509 *kcert = NULL;
+ STACK_OF(X509) *ca;
+
+ if (!(ca = sk_X509_new_null()))
+ goto error;
+
+ for (i = 0; i < sk_X509_num(certs); i++) {
+ cert = sk_X509_value(certs, i);
+ if (key && X509_check_private_key(cert, key)) {
+ if (!(kcert = X509_dup(cert)))
+ goto error;
+ X509_keyid_set1(kcert, NULL, 0);
+ X509_alias_set1(kcert, NULL, 0);
+ }
+ else sk_X509_push(ca, cert);
+ }
+ if (key && !kcert) {
+ no_kcert = 1;
+ goto error;
+ }
+
+ if (!(*ud = PKCS12_create(pass, NULL, key, kcert, ca, 0, 0, 0, 0, 0)))
+ goto error;
+
+ if (kcert)
+ X509_free(kcert);
+ sk_X509_free(ca);
+
+ return 1;
+
+error:
+ if (kcert)
+ X509_free(kcert);
+ if (ca)
+ sk_X509_free(ca);
+
+ if (no_kcert)
+ luaL_argerror(L, 1, lua_pushfstring(L, "certificate matching the key not found"));
+
+ return auxL_error(L, auxL_EOPENSSL, "pkcs12.new");
+} /* p12_new() */
+
+
+static int p12_interpose(lua_State *L) {
+ return interpose(L, PKCS12_CLASS);
+} /* p12_interpose() */
+
+
+static int p12_parse(lua_State *L) {
+ /* parse a p12 binary string and return the parts */
+ PKCS12 *p12;
+
+ /* gather input parameters */
+ size_t len;
+ const char *blob = luaL_checklstring(L, 1, &len);
+ const char *passphrase = luaL_optstring(L, 2, NULL);
+
+ /* prepare return values */
+ EVP_PKEY **ud_pkey = prepsimple(L, PKEY_CLASS);
+ X509 **ud_cert = prepsimple(L, X509_CERT_CLASS);
+ STACK_OF(X509) **ud_chain = prepsimple(L, X509_CHAIN_CLASS);
+ /* Note: *ud_chain must be initialised to NULL, which prepsimple does. */
+
+ /* read PKCS#12 data into OpenSSL memory buffer */
+ BIO *bio = BIO_new_mem_buf((void*)blob, len);
+ if (!bio)
+ return auxL_error(L, auxL_EOPENSSL, "pkcs12.parse");
+ p12 = d2i_PKCS12_bio(bio, NULL);
+ BIO_free(bio);
+ if (!p12)
+ return auxL_error(L, auxL_EOPENSSL, "pkcs12.parse");
+
+ /* the p12 pointer holds the data we're interested in */
+ int rc = PKCS12_parse(p12, passphrase, ud_pkey, ud_cert, ud_chain);
+ PKCS12_free(p12);
+ if (!rc)
+ auxL_error(L, auxL_EOPENSSL, "pkcs12.parse");
+
+ /* replace the return values by nil if the ud pointers are NULL */
+ if (*ud_pkey == NULL) {
+ lua_pushnil(L);
+ lua_replace(L, -4);
+ }
+
+ if (*ud_cert == NULL) {
+ lua_pushnil(L);
+ lua_replace(L, -3);
+ }
+
+ /* other certificates (a chain, STACK_OF(X509) *) */
+ if (*ud_chain == NULL) {
+ lua_pop(L, 1);
+ lua_pushnil(L);
+ }
+
+ return 3;
+} /* p12_parse() */
+
+
+static int p12__tostring(lua_State *L) {
+ PKCS12 *p12 = checksimple(L, 1, PKCS12_CLASS);
+ BIO *bio = getbio(L);
+ char *data;
+ long len;
+
+ if (!i2d_PKCS12_bio(bio, p12))
+ return auxL_error(L, auxL_EOPENSSL, "pkcs12:__tostring");
+
+ len = BIO_get_mem_data(bio, &data);
+
+ lua_pushlstring(L, data, len);
+
+ return 1;
+} /* p12__tostring() */
+
+
+static int p12__gc(lua_State *L) {
+ PKCS12 **ud = luaL_checkudata(L, 1, PKCS12_CLASS);
+
+ if (*ud) {
+ PKCS12_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* p12__gc() */
+
+
+static const auxL_Reg p12_methods[] = {
+ { "tostring", &p12__tostring },
+ { NULL, NULL },
+};
+
+static const auxL_Reg p12_metatable[] = {
+ { "__tostring", &p12__tostring },
+ { "__gc", &p12__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg p12_globals[] = {
+ { "new", &p12_new },
+ { "interpose", &p12_interpose },
+ { "parse", &p12_parse },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_pkcs12(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, p12_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_pkcs12() */
+
+
+/*
+ * SSL_CTX - openssl.ssl.context
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int sx_new(lua_State *L) {
+ static const char *const opts[] = {
+ [0] = "SSL",
+ [1] = "TLS",
+ [2] = "SSLv2",
+ [3] = "SSLv3",
+ [4] = "SSLv23",
+ [5] = "TLSv1", [6] = "TLSv1.0",
+ [7] = "TLSv1_1", [8] = "TLSv1.1",
+ [9] = "TLSv1_2", [10] = "TLSv1.2",
+ [11] = "DTLS",
+ [12] = "DTLSv1", [13] = "DTLSv1.0",
+ [14] = "DTLSv1_2", [15] = "DTLSv1.2",
+ NULL
+ };
+ int method_enum;
+ _Bool srv;
+ SSL_CTX **ud;
+ int options = 0;
+
+ lua_settop(L, 2);
+ method_enum = auxL_checkoption(L, 1, "TLS", opts, 1);
+ srv = lua_toboolean(L, 2);
+
+ switch (method_enum) {
+ case 0: /* SSL */
+ options = SSL_OP_NO_SSLv2;
+ break;
+ case 1: /* TLS */
+ options = SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3;
+ break;
+ case 2: /* SSLv2 */
+ options = SSL_OP_NO_SSL_MASK & ~SSL_OP_NO_SSLv2;
+ break;
+ case 3: /* SSLv3 */
+ options = SSL_OP_NO_SSL_MASK & ~SSL_OP_NO_SSLv3;
+ break;
+ case 4: /* SSLv23 */
+ break;
+ case 5: /* TLSv1 */
+ case 6: /* TLSv1.0 */
+ options = SSL_OP_NO_SSL_MASK & ~SSL_OP_NO_TLSv1;
+ break;
+#if defined SSL_OP_NO_TLSv1_1
+ case 7: /* TLSv1_1 */
+ case 8: /* TLSv1.1 */
+ options = SSL_OP_NO_SSL_MASK & ~SSL_OP_NO_TLSv1_1;
+ break;
+#endif
+#if defined SSL_OP_NO_TLSv1_2
+ case 9: /* TLSv1_2 */
+ case 10: /* TLSv1.2 */
+ options = SSL_OP_NO_SSL_MASK & ~SSL_OP_NO_TLSv1_2;
+ break;
+#endif
+#if HAVE_DTLS_CLIENT_METHOD
+ case 11: /* DTLS */
+ break;
+#ifdef SSL_OP_NO_DTLSv1
+ case 12: /* DTLSv1 */
+ case 13: /* DTLSv1.0 */
+ options = SSL_OP_NO_DTLS_MASK & ~SSL_OP_NO_DTLSv1;
+ break;
+#endif
+#ifdef SSL_OP_NO_DTLSv1_2
+ case 14: /* DTLSv1_2 */
+ case 15: /* DTLSv1.2 */
+ options = SSL_OP_NO_DTLS_MASK & ~SSL_OP_NO_DTLSv1_2;
+ break;
+#endif
+#endif
+ default:
+ return luaL_argerror(L, 1, "invalid option");
+ }
+
+ ud = prepsimple(L, SSL_CTX_CLASS);
+
+ switch (method_enum) {
+ case 0: /* SSL */
+ case 1: /* TLS */
+ case 2: /* SSLv2 */
+ case 3: /* SSLv3 */
+ case 4: /* SSLv23 */
+ case 5: /* TLSv1 */
+ case 6: /* TLSv1.0 */
+ case 7: /* TLSv1_1 */
+ case 8: /* TLSv1.1 */
+ case 9: /* TLSv1_2 */
+ case 10: /* TLSv1.2 */
+ *ud = SSL_CTX_new(srv?SSLv23_server_method():SSLv23_client_method());
+ break;
+#if HAVE_DTLS_CLIENT_METHOD
+ case 11: /* DTLS */
+ case 12: /* DTLSv1 */
+ case 13: /* DTLSv1.0 */
+ case 14: /* DTLSv1_2 */
+ case 15: /* DTLSv1.2 */
+ *ud = SSL_CTX_new(srv?DTLS_server_method():DTLS_client_method());
+ break;
+#endif
+ default:
+ NOTREACHED;
+ }
+
+ if (!*ud)
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context.new");
+
+ SSL_CTX_set_options(*ud, options);
+
+#if HAVE_SSL_CTX_SET_ECDH_AUTO
+ /* OpenSSL 1.0.2 introduced SSL_CTX_set_ecdh_auto to automatically select
+ * from the curves set via SSL_CTX_set1_curves_list. However as of OpenSSL
+ * 1.1.0, the functionality was turned on permanently and the option
+ * removed. */
+ if (!SSL_CTX_set_ecdh_auto(*ud, 1))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context.new");
+#endif
+
+ return 1;
+} /* sx_new() */
+
+
+static int sx_interpose(lua_State *L) {
+ return interpose(L, SSL_CTX_CLASS);
+} /* sx_interpose() */
+
+
+static int sx_setOptions(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ auxL_Integer options = auxL_checkinteger(L, 2);
+
+ auxL_pushinteger(L, SSL_CTX_set_options(ctx, options));
+
+ return 1;
+} /* sx_setOptions() */
+
+
+static int sx_getOptions(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+
+ auxL_pushinteger(L, SSL_CTX_get_options(ctx));
+
+ return 1;
+} /* sx_getOptions() */
+
+
+static int sx_clearOptions(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ auxL_Integer options = auxL_checkinteger(L, 2);
+
+ auxL_pushinteger(L, SSL_CTX_clear_options(ctx, options));
+
+ return 1;
+} /* sx_clearOptions() */
+
+
+static int sx_setStore(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ X509_STORE *store = checksimple(L, 2, X509_STORE_CLASS);
+
+ SSL_CTX_set1_cert_store(ctx, store);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setStore() */
+
+
+static int sx_getStore(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ X509_STORE *store;
+
+ if((store = SSL_CTX_get_cert_store(ctx))) {
+ xs_push(L, store);
+ } else {
+ lua_pushnil(L);
+ }
+
+ return 1;
+} /* sx_getStore() */
+
+
+static int sx_setParam(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ X509_VERIFY_PARAM *xp = checksimple(L, 2, X509_VERIFY_PARAM_CLASS);
+
+ if (!SSL_CTX_set1_param(ctx, xp))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setParam");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setParam() */
+
+
+static int sx_getParam(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ X509_VERIFY_PARAM **ud, *from;
+
+ /* X509_VERIFY_PARAM is not refcounted; create a new object and copy into it. */
+ ud = prepsimple(L, X509_VERIFY_PARAM_CLASS);
+ if (!(*ud = X509_VERIFY_PARAM_new()))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:getParam");
+
+ from = SSL_CTX_get0_param(ctx);
+
+ if (!(X509_VERIFY_PARAM_set1(*ud, from)))
+ /* Note: openssl doesn't set an error as it should for some cases */
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:getParam");
+
+ return 1;
+} /* sx_getParam() */
+
+
+static int sx_setVerify(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ int mode = luaL_optinteger(L, 2, -1);
+ int depth = luaL_optinteger(L, 3, -1);
+
+ if (mode != -1)
+ SSL_CTX_set_verify(ctx, mode, 0);
+
+ if (depth != -1)
+ SSL_CTX_set_verify_depth(ctx, depth);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setVerify() */
+
+
+static int sx_getVerify(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+
+ lua_pushinteger(L, SSL_CTX_get_verify_mode(ctx));
+ lua_pushinteger(L, SSL_CTX_get_verify_depth(ctx));
+
+ return 2;
+} /* sx_getVerify() */
+
+
+static int sx_setCertificate(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ X509 *crt = X509_dup(checksimple(L, 2, X509_CERT_CLASS));
+ int ok;
+
+ ok = SSL_CTX_use_certificate(ctx, crt);
+ X509_free(crt);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setCertificate");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setCertificate() */
+
+
+#if HAVE_SSL_CTX_GET0_CERTIFICATE
+static int sx_getCertificate(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ X509 *x509;
+
+ if (!(x509 = SSL_CTX_get0_certificate(ctx)))
+ return 0;
+
+ xc_dup(L, x509);
+
+ return 1;
+} /* sx_getCertificate() */
+#endif
+
+
+static int sx_setPrivateKey(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+
+ /*
+ * NOTE: No easy way to dup the key, but a shared reference should
+ * be okay as keys are less mutable than certificates.
+ *
+ * FIXME: SSL_CTX_use_PrivateKey will return true even if the
+ * EVP_PKEY object has no private key. Instead, we'll just get a
+ * segfault during the SSL handshake. We need to check that a
+ * private key is actually defined in the object.
+ */
+ if (!SSL_CTX_use_PrivateKey(ctx, key))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setPrivateKey");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setPrivateKey() */
+
+
+static int sx_setCipherList(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ const char *ciphers = luaL_checkstring(L, 2);
+
+ if (!SSL_CTX_set_cipher_list(ctx, ciphers))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setCipherList");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setCipherList() */
+
+
+#if HAVE_SSL_CTX_SET_CURVES_LIST
+static int sx_setCurvesList(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ const char *curves = luaL_checkstring(L, 2);
+
+ if (!SSL_CTX_set1_curves_list(ctx, curves))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setCurvesList");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setCurvesList() */
+#endif
+
+
+static int sx_setEphemeralKey(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+ void *tmp;
+
+ /*
+ * NOTE: SSL_CTX_set_tmp duplicates the keys, so we don't need to
+ * worry about lifetimes. EVP_PKEY_get0 doesn't increment the
+ * reference count.
+ */
+ switch (EVP_PKEY_base_id(key)) {
+ case EVP_PKEY_RSA:
+ if (!(tmp = EVP_PKEY_get0(key)))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setEphemeralKey");
+
+ if (!SSL_CTX_set_tmp_rsa(ctx, tmp))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setEphemeralKey");
+
+ break;
+ case EVP_PKEY_DH:
+ if (!(tmp = EVP_PKEY_get0(key)))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setEphemeralKey");
+
+ if (!SSL_CTX_set_tmp_dh(ctx, tmp))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setEphemeralKey");
+
+ break;
+ case EVP_PKEY_EC:
+ if (!(tmp = EVP_PKEY_get0(key)))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setEphemeralKey");
+
+ if (!SSL_CTX_set_tmp_ecdh(ctx, tmp))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setEphemeralKey");
+
+ break;
+ default:
+ return luaL_error(L, "%d: unsupported EVP base type", EVP_PKEY_base_id(key));
+ } /* switch() */
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setEphemeralKey() */
+
+
+#if HAVE_SSL_CTX_SET_ALPN_PROTOS
+static int sx_setAlpnProtos(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ luaL_Buffer B;
+ size_t len;
+ const char *tmp;
+
+ luaL_buffinit(L, &B);
+ checkprotos(&B, L, 2);
+ luaL_pushresult(&B);
+ tmp = lua_tolstring(L, -1, &len);
+
+ /* OpenSSL 1.0.2 doesn't update the error stack on failure. */
+ ERR_clear_error();
+ if (0 != SSL_CTX_set_alpn_protos(ctx, (const unsigned char*)tmp, len)) {
+ if (!ERR_peek_error()) {
+ return luaL_error(L, "unable to set ALPN protocols: %s", aux_strerror(ENOMEM));
+ } else {
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setAlpnProtos");
+ }
+ }
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setAlpnProtos() */
+#endif
+
+
+#if HAVE_SSL_CTX_SET_ALPN_SELECT_CB
+static int sx_setAlpnSelect_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *_ctx) {
+ SSL_CTX *ctx = _ctx;
+ lua_State *L = NULL;
+ size_t n, protolen, tmpsiz;
+ int otop, status;
+ const void *proto;
+ void *tmpbuf;
+
+ *out = NULL;
+ *outlen = 0;
+
+ /* expect at least two values: return buffer and closure */
+ if ((n = ex_getdata(&L, EX_SSL_CTX_ALPN_SELECT_CB, ctx)) < 2)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ otop = lua_gettop(L) - n;
+
+ /* pass SSL object as 1st argument */
+ if (ssl_pushsafe(L, ssl))
+ goto fatal;
+ lua_insert(L, otop + 3);
+
+ /* TODO: Install temporary panic handler to catch OOM errors */
+ /* pass table of protocol names as 2nd argument */
+ pushprotos(L, in, inlen);
+ lua_insert(L, otop + 4);
+
+ if (LUA_OK != (status = lua_pcall(L, 2 + (n - 2), 1, 0)))
+ goto fatal;
+
+ /* did we get a string result? */
+ if (!(proto = lua_tolstring(L, -1, &protolen)))
+ goto noack;
+
+ /* will it fit in our return buffer? */
+ if (!(tmpbuf = lua_touserdata(L, otop + 1)))
+ goto fatal;
+
+ tmpsiz = lua_rawlen(L, otop + 1);
+
+ if (protolen > tmpsiz)
+ goto fatal;
+
+ memcpy(tmpbuf, proto, protolen);
+
+ /*
+ * NB: Our return buffer is anchored using the luaL_ref API, so even
+ * once we pop the stack it will remain valid.
+ */
+ *out = tmpbuf;
+ *outlen = protolen;
+
+ lua_settop(L, otop);
+
+ return SSL_TLSEXT_ERR_OK;
+fatal:
+ lua_settop(L, otop);
+
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+noack:
+ lua_settop(L, otop);
+
+ return SSL_TLSEXT_ERR_NOACK;
+} /* sx_setAlpnSelect_cb() */
+
+static int sx_setAlpnSelect(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ int error;
+
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ /* allocate space to store the selected protocol in our callback */
+ lua_newuserdata(L, UCHAR_MAX);
+ lua_insert(L, 2);
+
+ if ((error = ex_setdata(L, EX_SSL_CTX_ALPN_SELECT_CB, ctx, lua_gettop(L) - 1))) {
+ if (error > 0) {
+ return luaL_error(L, "unable to set ALPN protocol selection callback: %s", aux_strerror(error));
+ } else if (error == auxL_EOPENSSL && !ERR_peek_error()) {
+ return luaL_error(L, "unable to set ALPN protocol selection callback: Unknown internal error");
+ } else {
+ return auxL_error(L, error, "ssl.context:setAlpnSelect");
+ }
+ }
+
+ SSL_CTX_set_alpn_select_cb(ctx, &sx_setAlpnSelect_cb, ctx);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setAlpnSelect() */
+#endif
+
+
+#if HAVE_SSL_CTX_SET_TLSEXT_SERVERNAME_CALLBACK
+static int sx_setHostNameCallback_cb(SSL *ssl, int *ad, void *_ctx) {
+ SSL_CTX *ctx = _ctx;
+ lua_State *L = NULL;
+ size_t n;
+ int otop, status, ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ *ad = SSL_AD_INTERNAL_ERROR;
+
+ /* expect at least one value: closure */
+ if ((n = ex_getdata(&L, EX_SSL_CTX_TLSEXT_SERVERNAME_CB, ctx)) < 1)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ otop = lua_gettop(L) - n;
+
+ /* pass SSL object as 1st argument */
+ if (ssl_pushsafe(L, ssl))
+ goto done;
+
+ lua_insert(L, otop + 2);
+
+ if (LUA_OK != (status = lua_pcall(L, 1 + (n - 1), 2, 0)))
+ goto done;
+
+ /* callback should return a boolean for OK/NOACK
+ * or nil + an integer for a controlled error
+ * everything else will be a fatal internal error
+ */
+ if (lua_isboolean(L, -2)) {
+ ret = lua_toboolean(L, -2) ? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
+ } else {
+ ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+ if (lua_isnil(L, -2) && lua_isinteger(L, -1))
+ *ad = lua_tointeger(L, -1);
+ }
+
+done:
+ lua_settop(L, otop);
+
+ return ret;
+} /* sx_setHostNameCallback_cb() */
+
+
+static int sx_setHostNameCallback(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ int error;
+
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ if ((error = ex_setdata(L, EX_SSL_CTX_TLSEXT_SERVERNAME_CB, ctx, lua_gettop(L) - 1))) {
+ if (error > 0) {
+ return luaL_error(L, "unable to set hostname selection callback: %s", aux_strerror(error));
+ } else if (error == auxL_EOPENSSL && !ERR_peek_error()) {
+ return luaL_error(L, "unable to set hostname selection callback: Unknown internal error");
+ } else {
+ return auxL_error(L, error, "ssl.context:setHostNameCallback");
+ }
+ }
+ SSL_CTX_set_tlsext_servername_callback(ctx, sx_setHostNameCallback_cb);
+ SSL_CTX_set_tlsext_servername_arg(ctx, ctx);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setHostNameCallback() */
+#endif
+
+
+int TLSEXT_STATUSTYPEs[] = { TLSEXT_STATUSTYPE_ocsp };
+const char *TLSEXT_STATUSTYPEs_names[] = { "ocsp", NULL };
+#define checkTLSEXT_STATUSTYPE(L, idx) \
+ (TLSEXT_STATUSTYPEs[luaL_checkoption((L), (idx), NULL, TLSEXT_STATUSTYPEs_names)])
+
+
+#if HAVE_SSL_CTX_SET_TLSEXT_STATUS_TYPE
+static int sx_setTLSextStatusType(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ int type = checkTLSEXT_STATUSTYPE(L, 2);
+
+ if(!SSL_CTX_set_tlsext_status_type(ctx, type))
+ return auxL_error(L, auxL_EOPENSSL, "ssl.context:setTLSextStatusType");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* sx_setTLSextStatusType() */
+#endif
+
+
+#if HAVE_SSL_CTX_GET_TLSEXT_STATUS_TYPE
+static int sx_getTLSextStatusType(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CLASS);
+
+ int type = SSL_CTX_get_tlsext_status_type(ctx);
+ switch(type) {
+ case -1:
+ lua_pushnil(L);
+ break;
+ case TLSEXT_STATUSTYPE_ocsp:
+ lua_pushliteral(L, "ocsp");
+ break;
+ default:
+ luaL_error(L, "unknown TLS extension %d", type);
+ }
+
+ return 1;
+} /* sx_getTLSextStatusType() */
+#endif
+
+
+static int sx__gc(lua_State *L) {
+ SSL_CTX **ud = luaL_checkudata(L, 1, SSL_CTX_CLASS);
+
+ if (*ud) {
+ SSL_CTX_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* sx__gc() */
+
+
+static const auxL_Reg sx_methods[] = {
+ { "setOptions", &sx_setOptions },
+ { "getOptions", &sx_getOptions },
+ { "clearOptions", &sx_clearOptions },
+ { "setStore", &sx_setStore },
+ { "getStore", &sx_getStore },
+ { "setParam", &sx_setParam },
+ { "getParam", &sx_getParam },
+ { "setVerify", &sx_setVerify },
+ { "getVerify", &sx_getVerify },
+ { "setCertificate", &sx_setCertificate },
+#if HAVE_SSL_CTX_GET0_CERTIFICATE
+ { "getCertificate", &sx_getCertificate },
+#endif
+ { "setPrivateKey", &sx_setPrivateKey },
+ { "setCipherList", &sx_setCipherList },
+#if HAVE_SSL_CTX_SET_CURVES_LIST
+ { "setCurvesList", &sx_setCurvesList },
+#endif
+ { "setEphemeralKey", &sx_setEphemeralKey },
+#if HAVE_SSL_CTX_SET_ALPN_PROTOS
+ { "setAlpnProtos", &sx_setAlpnProtos },
+#endif
+#if HAVE_SSL_CTX_SET_ALPN_SELECT_CB
+ { "setAlpnSelect", &sx_setAlpnSelect },
+#endif
+#if HAVE_SSL_CTX_SET_TLSEXT_SERVERNAME_CALLBACK
+ { "setHostNameCallback", &sx_setHostNameCallback },
+#endif
+#if HAVE_SSL_CTX_SET_TLSEXT_STATUS_TYPE
+ { "setTLSextStatusType", &sx_setTLSextStatusType },
+#endif
+#if HAVE_SSL_CTX_GET_TLSEXT_STATUS_TYPE
+ { "getTLSextStatusType", &sx_getTLSextStatusType },
+#endif
+ { NULL, NULL },
+};
+
+static const auxL_Reg sx_metatable[] = {
+ { "__gc", &sx__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg sx_globals[] = {
+ { "new", &sx_new },
+ { "interpose", &sx_interpose },
+ { NULL, NULL },
+};
+
+static const auxL_IntegerReg sx_verify[] = {
+ { "VERIFY_NONE", SSL_VERIFY_NONE },
+ { "VERIFY_PEER", SSL_VERIFY_PEER },
+ { "VERIFY_FAIL_IF_NO_PEER_CERT", SSL_VERIFY_FAIL_IF_NO_PEER_CERT },
+ { "VERIFY_CLIENT_ONCE", SSL_VERIFY_CLIENT_ONCE },
+ { NULL, 0 },
+};
+
+static const auxL_IntegerReg sx_option[] = {
+ { "OP_MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG },
+ { "OP_NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG },
+ { "OP_LEGACY_SERVER_CONNECT", SSL_OP_LEGACY_SERVER_CONNECT },
+ { "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
+ { "OP_SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG },
+ { "OP_MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER },
+ { "OP_MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING },
+ { "OP_SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG },
+ { "OP_TLS_D5_BUG", SSL_OP_TLS_D5_BUG },
+ { "OP_TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG },
+#if defined SSL_OP_NO_TLSv1_1
+ { "OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1 },
+#endif
+ { "OP_DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
+ { "OP_ALL", SSL_OP_ALL },
+ { "OP_NO_QUERY_MTU", SSL_OP_NO_QUERY_MTU },
+ { "OP_COOKIE_EXCHANGE", SSL_OP_COOKIE_EXCHANGE },
+ { "OP_NO_TICKET", SSL_OP_NO_TICKET },
+ { "OP_CISCO_ANYCONNECT", SSL_OP_CISCO_ANYCONNECT },
+ { "OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
+#if defined SSL_OP_NO_COMPRESSION
+ { "OP_NO_COMPRESSION", SSL_OP_NO_COMPRESSION },
+#endif
+ { "OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
+ { "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE },
+ { "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE },
+ { "OP_EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA },
+ { "OP_CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE },
+ { "OP_TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG },
+ { "OP_NO_SSLv2", SSL_OP_NO_SSLv2 },
+ { "OP_NO_SSLv3", SSL_OP_NO_SSLv3 },
+ { "OP_NO_TLSv1", SSL_OP_NO_TLSv1 },
+#if defined SSL_OP_NO_TLSv1_2
+ { "OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2 },
+#endif
+ { "OP_PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1 },
+ { "OP_PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2 },
+ { "OP_NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG },
+ { "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG },
+#if defined SSL_OP_CRYPTOPRO_TLSEXT_BUG
+ { "OP_CRYPTOPRO_TLSEXT_BUG", SSL_OP_CRYPTOPRO_TLSEXT_BUG },
+#endif
+ { NULL, 0 },
+};
+
+EXPORT int luaopen__openssl_ssl_context(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, sx_globals, 0);
+ auxL_setintegers(L, sx_verify);
+ auxL_setintegers(L, sx_option);
+
+ return 1;
+} /* luaopen__openssl_ssl_context() */
+
+
+/*
+ * SSL - openssl.ssl
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static void ssl_push(lua_State *L, SSL *ssl) {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, (void *)&initall);
+ if (LUA_TNIL == lua_rawgetp(L, -1, ssl)) {
+ SSL **ud;
+
+ lua_pop(L, 1); /* pop nil */
+
+ ud = prepsimple(L, SSL_CLASS);
+
+ SSL_up_ref(ssl);
+ *ud = ssl;
+
+ /* Add to SSL* cache */
+ lua_pushvalue(L, -1);
+ lua_rawsetp(L, -3, ssl);
+ }
+ lua_remove(L, -2);
+} /* ssl_push() */
+
+
+static int ssl_pushffi(lua_State *L) {
+ SSL *ptr;
+
+ lua_pushvalue(L, lua_upvalueindex(1));
+ lua_pushvalue(L, 1);
+ lua_call(L, 1, 1);
+ luaL_argcheck(L, lua_toboolean(L, -1), 1, "SSL* ffi pointer expected");
+ lua_pop(L, 1);
+ ptr = *(SSL**)lua_topointer(L, 1);
+ luaL_argcheck(L, ptr, 1, "SSL* pointer must be non-null");
+
+ ssl_push(L, ptr);
+
+ return 1;
+} /* ssl_pushffi() */
+
+
+static int ssl_new(lua_State *L) {
+ SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS);
+ SSL **ud = prepsimple(L, SSL_CLASS);
+
+ *ud = SSL_new(ctx);
+
+ if (!*ud)
+ return auxL_error(L, auxL_EOPENSSL, "ssl.new");
+
+ /* Add to SSL* cache */
+ lua_rawgetp(L, LUA_REGISTRYINDEX, (void *)&initall);
+ lua_pushvalue(L, -2);
+ lua_rawsetp(L, -2, *ud);
+ lua_pop(L, 1);
+
+ return 1;
+} /* ssl_new() */
+
+
+static int ssl_interpose(lua_State *L) {
+ return interpose(L, SSL_CLASS);
+} /* ssl_interpose() */
+
+
+static int ssl_setContext(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ SSL_CTX *ctx = checksimple(L, 2, SSL_CTX_CLASS);
+
+ if (!SSL_set_SSL_CTX(ssl, ctx))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setContext");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setContext() */
+
+static int ssl_setOptions(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ auxL_Integer options = auxL_checkinteger(L, 2);
+
+ auxL_pushinteger(L, SSL_set_options(ssl, options));
+
+ return 1;
+} /* ssl_setOptions() */
+
+
+static int ssl_getOptions(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+
+ auxL_pushinteger(L, SSL_get_options(ssl));
+
+ return 1;
+} /* ssl_getOptions() */
+
+
+static int ssl_clearOptions(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ auxL_Integer options = auxL_checkinteger(L, 2);
+
+ auxL_pushinteger(L, SSL_clear_options(ssl, options));
+
+ return 1;
+} /* ssl_clearOptions() */
+
+
+#if HAVE_SSL_SET1_CHAIN_CERT_STORE
+static int ssl_setChainStore(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ X509_STORE *store = checksimple(L, 2, X509_STORE_CLASS);
+
+ SSL_set1_chain_cert_store(ssl, store);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setChainStore() */
+#endif
+
+
+#if HAVE_SSL_SET1_VERIFY_CERT_STORE
+static int ssl_setVerifyStore(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ X509_STORE *store = checksimple(L, 2, X509_STORE_CLASS);
+
+ SSL_set1_verify_cert_store(ssl, store);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setVerifyStore() */
+#endif
+
+
+static int ssl_setParam(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ X509_VERIFY_PARAM *xp = checksimple(L, 2, X509_VERIFY_PARAM_CLASS);
+
+ if (!SSL_set1_param(ssl, xp))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setParam");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setParam() */
+
+
+static int ssl_getParam(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ X509_VERIFY_PARAM **ud, *from;
+
+ /* X509_VERIFY_PARAM is not refcounted; create a new object and copy into it. */
+ ud = prepsimple(L, X509_VERIFY_PARAM_CLASS);
+ if (!(*ud = X509_VERIFY_PARAM_new()))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:getParam");
+
+ from = SSL_get0_param(ssl);
+
+ if (!(X509_VERIFY_PARAM_set1(*ud, from)))
+ /* Note: openssl doesn't set an error as it should for some cases */
+ return auxL_error(L, auxL_EOPENSSL, "ssl:getParam");
+
+ return 1;
+} /* ssl_getParam() */
+
+
+static int ssl_setVerify(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ int mode = luaL_optinteger(L, 2, -1);
+ int depth = luaL_optinteger(L, 3, -1);
+
+ if (mode != -1)
+ SSL_set_verify(ssl, mode, 0);
+
+ if (depth != -1)
+ SSL_set_verify_depth(ssl, depth);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setVerify() */
+
+
+static int ssl_getVerify(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+
+ lua_pushinteger(L, SSL_get_verify_mode(ssl));
+ lua_pushinteger(L, SSL_get_verify_depth(ssl));
+
+ return 2;
+} /* ssl_getVerify() */
+
+
+static int ssl_getVerifyResult(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ long res = SSL_get_verify_result(ssl);
+ lua_pushinteger(L, res);
+ lua_pushstring(L, X509_verify_cert_error_string(res));
+ return 2;
+} /* ssl_getVerifyResult() */
+
+
+static int ssl_setCertificate(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ X509 *crt = X509_dup(checksimple(L, 2, X509_CERT_CLASS));
+ int ok;
+
+ ok = SSL_use_certificate(ssl, crt);
+ X509_free(crt);
+
+ if (!ok)
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setCertificate");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setCertificate() */
+
+
+static int ssl_setPrivateKey(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ EVP_PKEY *key = checksimple(L, 2, PKEY_CLASS);
+ /*
+ * NOTE: No easy way to dup the key, but a shared reference should
+ * be okay as keys are less mutable than certificates.
+ *
+ * FIXME: SSL_use_PrivateKey will return true even if the
+ * EVP_PKEY object has no private key. Instead, we'll just get a
+ * segfault during the SSL handshake. We need to check that a
+ * private key is actually defined in the object.
+ */
+ if (!SSL_use_PrivateKey(ssl, key))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setPrivateKey");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setPrivateKey() */
+
+
+static int ssl_getCertificate(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ X509 *x509;
+
+ if (!(x509 = SSL_get_certificate(ssl)))
+ return 0;
+
+ xc_dup(L, x509);
+
+ return 1;
+} /* ssl_getCertificate() */
+
+
+static int ssl_getPeerCertificate(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ X509 **x509 = prepsimple(L, X509_CERT_CLASS);
+
+ if (!(*x509 = SSL_get_peer_certificate(ssl)))
+ return 0;
+
+ return 1;
+} /* ssl_getPeerCertificate() */
+
+
+static int ssl_getPeerChain(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ STACK_OF(X509) *chain;
+
+ if (!(chain = SSL_get_peer_cert_chain(ssl)))
+ return 0;
+
+ xl_dup(L, chain, 0);
+
+ return 1;
+} /* ssl_getPeerChain() */
+
+
+static int ssl_getCipherInfo(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ const SSL_CIPHER *cipher;
+ char descr[256];
+
+ if (!(cipher = SSL_get_current_cipher(ssl)))
+ return 0;
+
+ lua_newtable(L);
+
+ lua_pushstring(L, SSL_CIPHER_get_name(cipher));
+ lua_setfield(L, -2, "name");
+
+ lua_pushinteger(L, SSL_CIPHER_get_bits(cipher, 0));
+ lua_setfield(L, -2, "bits");
+
+ lua_pushstring(L, SSL_CIPHER_get_version(cipher));
+ lua_setfield(L, -2, "version");
+
+ lua_pushstring(L, SSL_CIPHER_description(cipher, descr, sizeof descr));
+ lua_setfield(L, -2, "description");
+
+ return 1;
+} /* ssl_getCipherInfo() */
+
+
+#if HAVE_SSL_SET_CURVES_LIST
+static int ssl_setCurvesList(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ const char *curves = luaL_checkstring(L, 2);
+
+ if (!SSL_set1_curves_list(ssl, curves))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setCurvesList");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setCurvesList() */
+#endif
+
+
+static int ssl_getHostName(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ const char *host;
+
+ if (!(host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)))
+ return 0;
+
+ lua_pushstring(L, host);
+
+ return 1;
+} /* ssl_getHostName() */
+
+
+static int ssl_setHostName(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ const char *host = luaL_optstring(L, 2, NULL);
+
+ if (!SSL_set_tlsext_host_name(ssl, host))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setHostName");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setHostName() */
+
+
+static int ssl_getVersion(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ int format = luaL_checkoption(L, 2, "d", (const char *[]){ "d", ".", "f", NULL });
+ int version = SSL_version(ssl);
+ int major, minor;
+
+ switch (format) {
+ case 1: case 2:
+ major = 0xff & ((version >> 8));
+ minor = (0xff & version);
+
+ luaL_argcheck(L, minor < 10, 2, "unable to convert SSL version to float because minor version >= 10");
+ lua_pushnumber(L, major + ((double)minor / 10));
+
+ break;
+ default:
+ lua_pushinteger(L, version);
+
+ break;
+ }
+
+ return 1;
+} /* ssl_getVersion() */
+
+
+static int ssl_getClientRandom(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ luaL_Buffer B;
+ size_t len;
+ unsigned char *out;
+
+ len = SSL_get_client_random(ssl, NULL, 0);
+ out = (unsigned char*)luaL_buffinitsize(L, &B, len);
+ len = SSL_get_client_random(ssl, out, len);
+ luaL_pushresultsize(&B, len);
+
+ return 1;
+} /* ssl_getClientRandom() */
+
+
+static int ssl_getMasterKey(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ SSL_SESSION *session;
+ luaL_Buffer B;
+ size_t len;
+ unsigned char *out;
+
+ session = SSL_get0_session(ssl);
+ if (!session) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ len = SSL_SESSION_get_master_key(session, NULL, 0);
+ out = (unsigned char*)luaL_buffinitsize(L, &B, len);
+ len = SSL_SESSION_get_master_key(session, out, len);
+ luaL_pushresultsize(&B, len);
+
+ return 1;
+} /* ssl_getMasterKey() */
+
+
+#if HAVE_SSL_GET_SERVER_TMP_KEY
+static int ssl_getServerTemporaryKey(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ EVP_PKEY **key = prepsimple(L, PKEY_CLASS);
+
+ if (!SSL_get_server_tmp_key(ssl, key))
+ return 0;
+
+ return 1;
+} /* ssl_getServerTemporaryKey() */
+#endif
+
+static int ssl_getClientVersion(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ int format = luaL_checkoption(L, 2, "d", (const char *[]){ "d", ".", "f", NULL });
+ int version = SSL_client_version(ssl);
+ int major, minor;
+
+ switch (format) {
+ case 1: case 2:
+ major = 0xff & ((version >> 8));
+ minor = (0xff & version);
+
+ luaL_argcheck(L, minor < 10, 2, "unable to convert SSL client version to float because minor version >= 10");
+ lua_pushnumber(L, major + ((double)minor / 10));
+
+ break;
+ default:
+ lua_pushinteger(L, version);
+
+ break;
+ }
+
+ return 1;
+} /* ssl_getClientVersion() */
+
+
+#if HAVE_SSL_GET0_ALPN_SELECTED
+static int ssl_getAlpnSelected(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ const unsigned char *data;
+ unsigned len;
+ SSL_get0_alpn_selected(ssl, &data, &len);
+ if (0 == len) {
+ lua_pushnil(L);
+ } else {
+ lua_pushlstring(L, (const char *)data, len);
+ }
+ return 1;
+} /* ssl_getAlpnSelected() */
+#endif
+
+
+#if HAVE_SSL_SET_ALPN_PROTOS
+static int ssl_setAlpnProtos(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ luaL_Buffer B;
+ size_t len;
+ const char *tmp;
+
+ luaL_buffinit(L, &B);
+ checkprotos(&B, L, 2);
+ luaL_pushresult(&B);
+ tmp = lua_tolstring(L, -1, &len);
+
+ /* OpenSSL 1.0.2 doesn't update the error stack on failure. */
+ ERR_clear_error();
+ if (0 != SSL_set_alpn_protos(ssl, (const unsigned char*)tmp, len)) {
+ if (!ERR_peek_error()) {
+ return luaL_error(L, "unable to set ALPN protocols: %s", aux_strerror(ENOMEM));
+ } else {
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setAlpnProtos");
+ }
+ }
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setAlpnProtos() */
+#endif
+
+
+static int ssl_setTLSextStatusType(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ int type = checkTLSEXT_STATUSTYPE(L, 2);
+
+ if(!SSL_set_tlsext_status_type(ssl, type))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setTLSextStatusType");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setTLSextStatusType() */
+
+
+#if HAVE_SSL_GET_TLSEXT_STATUS_TYPE
+static int ssl_getTLSextStatusType(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+
+ int type = SSL_get_tlsext_status_type(ssl);
+ switch(type) {
+ case -1:
+ lua_pushnil(L);
+ break;
+ case TLSEXT_STATUSTYPE_ocsp:
+ lua_pushliteral(L, "ocsp");
+ break;
+ default:
+ luaL_error(L, "unknown TLS extension %d", type);
+ }
+
+ return 1;
+} /* ssl_getTLSextStatusType() */
+#endif
+
+
+static int ssl_setTLSextStatusOCSPResp(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+ OCSP_RESPONSE *or = testsimple(L, 2, OCSP_RESPONSE_CLASS);
+
+ unsigned char *resp = NULL;
+ long resp_len;
+
+ if (or) {
+ resp_len = i2d_OCSP_RESPONSE(or, &resp);
+ if (resp_len <= 0)
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setTLSextStatusOCSPResp");
+ } else {
+ resp_len = 0;
+ }
+
+ if (!SSL_set_tlsext_status_ocsp_resp(ssl, resp, resp_len))
+ return auxL_error(L, auxL_EOPENSSL, "ssl:setTLSextStatusOCSPResp");
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* ssl_setTLSextStatusOCSPResp() */
+
+
+static int ssl_getTLSextStatusOCSPResp(lua_State *L) {
+ SSL *ssl = checksimple(L, 1, SSL_CLASS);
+
+ OCSP_RESPONSE **ud = prepsimple(L, OCSP_RESPONSE_CLASS);
+ const unsigned char *resp;
+ long resp_len;
+
+ resp_len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp);
+ if (resp == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+ if (resp_len == -1)
+ return auxL_error(L, auxL_EOPENSSL, "ssl:getTLSextStatusOCSPResp");
+
+ *ud = d2i_OCSP_RESPONSE(NULL, &resp, resp_len);
+ if(*ud == NULL)
+ return auxL_error(L, auxL_EOPENSSL, "ssl:getTLSextStatusOCSPResp");
+
+ return 1;
+} /* ssl_getTLSextStatusOCSPResp() */
+
+
+static int ssl__gc(lua_State *L) {
+ SSL **ud = luaL_checkudata(L, 1, SSL_CLASS);
+
+ if (*ud) {
+ SSL_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* ssl__gc() */
+
+
+static const auxL_Reg ssl_methods[] = {
+ { "setContext", &ssl_setContext },
+ { "setOptions", &ssl_setOptions },
+ { "getOptions", &ssl_getOptions },
+ { "clearOptions", &ssl_clearOptions },
+#if HAVE_SSL_SET1_CHAIN_CERT_STORE
+ { "setChainStore", &ssl_setChainStore },
+#endif
+#if HAVE_SSL_SET1_VERIFY_CERT_STORE
+ { "setVerifyStore", &ssl_setVerifyStore },
+#endif
+ { "setParam", &ssl_setParam },
+ { "getParam", &ssl_getParam },
+ { "setVerify", &ssl_setVerify },
+ { "getVerify", &ssl_getVerify },
+ { "getVerifyResult", &ssl_getVerifyResult },
+ { "setCertificate", &ssl_setCertificate },
+ { "setPrivateKey", &ssl_setPrivateKey },
+ { "getCertificate", &ssl_getCertificate },
+ { "getPeerCertificate", &ssl_getPeerCertificate },
+ { "getPeerChain", &ssl_getPeerChain },
+ { "getCipherInfo", &ssl_getCipherInfo },
+#if HAVE_SSL_SET_CURVES_LIST
+ { "setCurvesList", &ssl_setCurvesList },
+#endif
+ { "getHostName", &ssl_getHostName },
+ { "setHostName", &ssl_setHostName },
+ { "getVersion", &ssl_getVersion },
+ { "getClientRandom", &ssl_getClientRandom },
+ { "getMasterKey", &ssl_getMasterKey },
+#if HAVE_SSL_GET_SERVER_TMP_KEY
+ { "getServerTemporaryKey", &ssl_getServerTemporaryKey },
+#endif
+ { "getClientVersion", &ssl_getClientVersion },
+#if HAVE_SSL_GET0_ALPN_SELECTED
+ { "getAlpnSelected", &ssl_getAlpnSelected },
+#endif
+#if HAVE_SSL_SET_ALPN_PROTOS
+ { "setAlpnProtos", &ssl_setAlpnProtos },
+#endif
+ { "setTLSextStatusType", &ssl_setTLSextStatusType },
+#if HAVE_SSL_GET_TLSEXT_STATUS_TYPE
+ { "getTLSextStatusType", &ssl_getTLSextStatusType },
+#endif
+ { "setTLSextStatusOCSPResp", &ssl_setTLSextStatusOCSPResp },
+ { "getTLSextStatusOCSPResp", &ssl_getTLSextStatusOCSPResp },
+ { NULL, NULL },
+};
+
+static const auxL_Reg ssl_metatable[] = {
+ { "__gc", &ssl__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg ssl_globals[] = {
+ { "new", &ssl_new },
+ { "pushffi", &ssl_pushffi, 1 },
+ { "interpose", &ssl_interpose },
+ { NULL, NULL },
+};
+
+static const auxL_IntegerReg ssl_version[] = {
+ { "SSL2_VERSION", SSL2_VERSION },
+ { "SSL3_VERSION", SSL3_VERSION },
+ { "TLS1_VERSION", TLS1_VERSION },
+#if defined TLS1_1_VERSION
+ { "TLS1_1_VERSION", TLS1_1_VERSION },
+#endif
+#if defined TLS1_2_VERSION
+ { "TLS1_2_VERSION", TLS1_2_VERSION },
+#endif
+ { NULL, 0 },
+};
+
+
+EXPORT int luaopen__openssl_ssl(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, ssl_globals, 0);
+ /* FFI argument checking */
+ lua_getfield(L, -1, "pushffi");
+ luaL_loadstring(L,
+ "local ffi = require 'ffi'\n" \
+ "if not pcall(ffi.typeof, 'SSL*') then\n" \
+ " ffi.cdef 'typedef struct ssl_st SSL;'\n" \
+ "end\n" \
+ "local ffi_istype = ffi.istype\n" \
+ "local SSLp = ffi.typeof('SSL*')\n" \
+ "return function(p) return ffi_istype(SSLp, p) end\n"
+ );
+ if (lua_pcall(L, 0, 1, 0)) {
+ /* failed (probably no ffi library available) */
+ lua_pop(L, 1);
+ /* use dummy function instead */
+ luaL_loadstring(L, "return false\n");
+ };
+ lua_setupvalue(L, -2, 1);
+ lua_pop(L, 1);
+
+ auxL_setintegers(L, ssl_version);
+ auxL_setintegers(L, sx_verify);
+ auxL_setintegers(L, sx_option);
+
+ return 1;
+} /* luaopen__openssl_ssl() */
+
+
+/*
+ * X509_VERIFY_PARAM
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int xp_new(lua_State *L) {
+ X509_VERIFY_PARAM **ud = prepsimple(L, X509_VERIFY_PARAM_CLASS);
+
+ if (!(*ud = X509_VERIFY_PARAM_new()))
+ return auxL_error(L, auxL_EOPENSSL, "x509.verify_param.new");
+
+ return 1;
+} /* xp_new() */
+
+
+static int xp_interpose(lua_State *L) {
+ return interpose(L, X509_VERIFY_PARAM_CLASS);
+} /* xp_interpose() */
+
+
+/*
+ * NB: Per the OpenSSL source, "[t]he 'inh_flags' field determines how this
+ * function behaves". (Referring to X509_VERIFY_PARAM_inherit.) The way to
+ * set inh_flags prior to OpenSSL 1.1 was by OR'ing flags into the inh_flags
+ * member and restoring it after the call. The OpenSSL 1.1 API makes the
+ * X509_VERIFY_PARAM object opaque, X509_VERIFY_PARAM_inherit, and there's
+ * no other function to set the flags argument; therefore it's not possible
+ * to control the inherit behavior from OpenSSL 1.1.
+ *
+ * For more details see
+ * https://github.com/openssl/openssl/issues/2054 and the original
+ * https://github.com/wahern/luaossl/pull/76/commits/db6e414d68c0f94c2497d363f6131b4de1710ba9
+ */
+static int xp_inherit(lua_State *L) {
+ X509_VERIFY_PARAM *dest = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ X509_VERIFY_PARAM *src = checksimple(L, 2, X509_VERIFY_PARAM_CLASS);
+ int ret;
+
+ ret = X509_VERIFY_PARAM_inherit(dest, src);
+ if (!ret)
+ /* Note: openssl doesn't set an error as it should for some cases */
+ return auxL_error(L, auxL_EOPENSSL, "x509.verify_param:inherit");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_inherit() */
+
+
+static const X509_PURPOSE *purpose_checktype(lua_State *L, int index) {
+ const char *purpose_name;
+ int purpose_id;
+ int purpose_idx;
+ const X509_PURPOSE *purpose;
+
+ if (lua_isnumber(L, index)) {
+ purpose_id = luaL_checkinteger(L, index);
+ purpose_idx = X509_PURPOSE_get_by_id(purpose_id);
+ if (purpose_idx < 0)
+ luaL_argerror(L, index, lua_pushfstring(L, "%d: invalid purpose", purpose_id));
+ } else {
+ purpose_name = luaL_checkstring(L, index);
+ purpose_idx = X509_PURPOSE_get_by_sname((char*)purpose_name);
+ if (purpose_idx < 0)
+ luaL_argerror(L, index, lua_pushfstring(L, "%s: invalid purpose", purpose_name));
+ }
+
+ purpose = X509_PURPOSE_get0(purpose_idx);
+ return purpose;
+} /* purpose_checktype() */
+
+
+static int xp_setPurpose(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ const X509_PURPOSE *purpose = purpose_checktype(L, 2);
+
+ if (!X509_VERIFY_PARAM_set_purpose(xp, X509_PURPOSE_get_id((X509_PURPOSE*)purpose)))
+ return auxL_error(L, auxL_EOPENSSL, "x509.verify_param:setPurpose");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_setPurpose() */
+
+
+static int xp_setTime(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ time_t t = luaL_checkinteger(L, 2);
+
+ X509_VERIFY_PARAM_set_time(xp, t);
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_setTime() */
+
+
+static int xp_setDepth(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ int depth = luaL_checkinteger(L, 2);
+
+ X509_VERIFY_PARAM_set_depth(xp, depth);
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_setDepth() */
+
+
+static int xp_getDepth(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+
+ int depth = X509_VERIFY_PARAM_get_depth(xp);
+
+ lua_pushinteger(L, depth);
+ return 1;
+} /* xp_getDepth() */
+
+
+#if HAVE_X509_VERIFY_PARAM_SET_AUTH_LEVEL
+static int xp_setAuthLevel(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ int auth_level = luaL_checkinteger(L, 2);
+
+ X509_VERIFY_PARAM_set_auth_level(xp, auth_level);
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_setAuthLevel() */
+
+
+static int xp_getAuthLevel(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+
+ int auth_level = X509_VERIFY_PARAM_get_auth_level(xp);
+
+ lua_pushinteger(L, auth_level);
+ return 1;
+} /* xp_getAuthLevel() */
+#endif
+
+
+#if HAVE_X509_VERIFY_PARAM_SET1_HOST
+static int xp_setHost(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ size_t len;
+ const char *str = luaL_optlstring(L, 2, NULL, &len); /* NULL = clear hosts */
+
+ if (!X509_VERIFY_PARAM_set1_host(xp, str, len))
+ /* Note: openssl doesn't set an error as it should for some cases */
+ return auxL_error(L, auxL_EOPENSSL, "x509.verify_param:setHost");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_setHost() */
+#endif
+
+
+#if HAVE_X509_VERIFY_PARAM_ADD1_HOST
+static int xp_addHost(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ size_t len;
+ const char *str = luaL_checklstring(L, 2, &len);
+
+ if (!X509_VERIFY_PARAM_add1_host(xp, str, len))
+ /* Note: openssl doesn't set an error as it should for some cases */
+ return auxL_error(L, auxL_EOPENSSL, "x509.verify_param:addHost");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_addHost() */
+#endif
+
+
+#if HAVE_X509_VERIFY_PARAM_SET1_EMAIL
+static int xp_setEmail(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ size_t len;
+ const char *str = luaL_checklstring(L, 2, &len);
+
+ if (!X509_VERIFY_PARAM_set1_email(xp, str, len))
+ /* Note: openssl doesn't set an error as it should for some cases */
+ return auxL_error(L, auxL_EOPENSSL, "x509.verify_param:setEmail");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_setEmail() */
+#endif
+
+
+#if HAVE_X509_VERIFY_PARAM_SET1_IP_ASC
+static int xp_setIP(lua_State *L) {
+ X509_VERIFY_PARAM *xp = checksimple(L, 1, X509_VERIFY_PARAM_CLASS);
+ const char *str = luaL_checkstring(L, 2);
+
+ if (!X509_VERIFY_PARAM_set1_ip_asc(xp, str))
+ /* Note: openssl doesn't set an error as it should for some cases */
+ return auxL_error(L, auxL_EOPENSSL, "x509.verify_param:setIP");
+
+ lua_pushboolean(L, 1);
+ return 1;
+} /* xp_setIP() */
+#endif
+
+
+static int xp__gc(lua_State *L) {
+ X509_VERIFY_PARAM **ud = luaL_checkudata(L, 1, X509_VERIFY_PARAM_CLASS);
+
+ X509_VERIFY_PARAM_free(*ud);
+ *ud = NULL;
+
+ return 0;
+} /* xp__gc() */
+
+
+static const auxL_Reg xp_methods[] = {
+ { "inherit", &xp_inherit },
+ { "setPurpose", &xp_setPurpose },
+ { "setTime", &xp_setTime },
+ { "setDepth", &xp_setDepth },
+ { "getDepth", &xp_getDepth },
+#if HAVE_X509_VERIFY_PARAM_SET_AUTH_LEVEL
+ { "setAuthLevel", &xp_setAuthLevel },
+ { "getAuthLevel", &xp_getAuthLevel },
+#endif
+#if HAVE_X509_VERIFY_PARAM_SET1_HOST
+ { "setHost", &xp_setHost },
+#endif
+#if HAVE_X509_VERIFY_PARAM_ADD1_HOST
+ { "addHost", &xp_addHost },
+#endif
+#if HAVE_X509_VERIFY_PARAM_SET1_EMAIL
+ { "setEmail", &xp_setEmail },
+#endif
+#if HAVE_X509_VERIFY_PARAM_SET1_IP_ASC
+ { "setIP", &xp_setIP },
+#endif
+ { NULL, NULL },
+};
+
+static const auxL_Reg xp_metatable[] = {
+ { "__gc", &xp__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg xp_globals[] = {
+ { "new", &xp_new },
+ { "interpose", &xp_interpose },
+ { NULL, NULL },
+};
+
+static const auxL_IntegerReg xp_inherit_flags[] = {
+ { "DEFAULT", X509_VP_FLAG_DEFAULT },
+ { "OVERWRITE", X509_VP_FLAG_OVERWRITE },
+ { "RESET_FLAGS", X509_VP_FLAG_RESET_FLAGS },
+ { "LOCKED", X509_VP_FLAG_LOCKED },
+ { "ONCE", X509_VP_FLAG_ONCE },
+ { NULL, 0 }
+};
+
+EXPORT int luaopen__openssl_x509_verify_param(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, xp_globals, 0);
+ auxL_setintegers(L, xp_inherit_flags);
+
+ return 1;
+} /* luaopen__openssl_x509_verify_param() */
+
+
+/*
+ * Digest - openssl.digest
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static const EVP_MD *md_optdigest(lua_State *L, int index) {
+ const char *name = luaL_optstring(L, index, "sha1");
+ const EVP_MD *type;
+
+ if (!(type = EVP_get_digestbyname(name)))
+ luaL_argerror(L, index, lua_pushfstring(L, "%s: invalid digest type", name));
+
+ return type;
+} /* md_optdigest() */
+
+
+static int md_new(lua_State *L) {
+ const EVP_MD *type = md_optdigest(L, 1);
+ EVP_MD_CTX **ctx;
+
+ ctx = prepsimple(L, DIGEST_CLASS, NULL);
+ if (!(*ctx = EVP_MD_CTX_new()) || !EVP_DigestInit_ex(*ctx, type, NULL))
+ return auxL_error(L, auxL_EOPENSSL, "digest.new");
+
+ return 1;
+} /* md_new() */
+
+
+static int md_interpose(lua_State *L) {
+ return interpose(L, DIGEST_CLASS);
+} /* md_interpose() */
+
+
+static void md_update_(lua_State *L, EVP_MD_CTX *ctx, int from, int to) {
+ int i;
+
+ for (i = from; i <= to; i++) {
+ const void *p;
+ size_t n;
+
+ p = luaL_checklstring(L, i, &n);
+
+ if (!EVP_DigestUpdate(ctx, p, n))
+ auxL_error(L, auxL_EOPENSSL, "digest:update");
+ }
+} /* md_update_() */
+
+
+static int md_update(lua_State *L) {
+ EVP_MD_CTX *ctx = checksimple(L, 1, DIGEST_CLASS);
+
+ md_update_(L, ctx, 2, lua_gettop(L));
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+} /* md_update() */
+
+
+static int md_final(lua_State *L) {
+ EVP_MD_CTX *ctx = checksimple(L, 1, DIGEST_CLASS);
+ unsigned char md[EVP_MAX_MD_SIZE];
+ unsigned len;
+
+ md_update_(L, ctx, 2, lua_gettop(L));
+
+ if (!EVP_DigestFinal_ex(ctx, md, &len))
+ return auxL_error(L, auxL_EOPENSSL, "digest:final");
+
+ lua_pushlstring(L, (char *)md, len);
+
+ return 1;
+} /* md_final() */
+
+
+static int md__gc(lua_State *L) {
+ EVP_MD_CTX **ctx = luaL_checkudata(L, 1, DIGEST_CLASS);
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+} /* md__gc() */
+
+
+static const auxL_Reg md_methods[] = {
+ { "update", &md_update },
+ { "final", &md_final },
+ { NULL, NULL },
+};
+
+static const auxL_Reg md_metatable[] = {
+ { "__gc", &md__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg md_globals[] = {
+ { "new", &md_new },
+ { "interpose", &md_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_digest(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, md_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_digest() */
+
+
+/*
+ * HMAC - openssl.hmac
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int hmac_new(lua_State *L) {
+ const void *key;
+ size_t len;
+ const EVP_MD *type;
+ HMAC_CTX **ctx;
+
+ key = luaL_checklstring(L, 1, &len);
+ type = md_optdigest(L, 2);
+
+ ctx = prepsimple(L, HMAC_CLASS, NULL);
+ if (!(*ctx = HMAC_CTX_new()))
+ goto eossl;
+
+#if HMAC_INIT_EX_INT
+ if (!HMAC_Init_ex(*ctx, key, len, type, NULL))
+ goto eossl;
+#else
+ HMAC_Init_ex(*ctx, key, len, type, NULL);
+#endif
+
+ return 1;
+eossl:
+ return auxL_error(L, auxL_EOPENSSL, "hmac.new");
+} /* hmac_new() */
+
+
+static int hmac_interpose(lua_State *L) {
+ return interpose(L, HMAC_CLASS);
+} /* hmac_interpose() */
+
+
+static void hmac_update_(lua_State *L, HMAC_CTX *ctx, int from, int to) {
+ int i;
+
+ for (i = from; i <= to; i++) {
+ const void *p;
+ size_t n;
+
+ p = luaL_checklstring(L, i, &n);
+
+ HMAC_Update(ctx, p, n);
+ }
+} /* hmac_update_() */
+
+
+static int hmac_update(lua_State *L) {
+ HMAC_CTX *ctx = checksimple(L, 1, HMAC_CLASS);
+
+ hmac_update_(L, ctx, 2, lua_gettop(L));
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+} /* hmac_update() */
+
+
+static int hmac_final(lua_State *L) {
+ HMAC_CTX *ctx = checksimple(L, 1, HMAC_CLASS);
+ unsigned char hmac[EVP_MAX_MD_SIZE];
+ unsigned len;
+
+ hmac_update_(L, ctx, 2, lua_gettop(L));
+
+ HMAC_Final(ctx, hmac, &len);
+
+ lua_pushlstring(L, (char *)hmac, len);
+
+ return 1;
+} /* hmac_final() */
+
+
+static int hmac__gc(lua_State *L) {
+ HMAC_CTX **ctx = luaL_checkudata(L, 1, HMAC_CLASS);
+
+ HMAC_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+} /* hmac__gc() */
+
+
+static const auxL_Reg hmac_methods[] = {
+ { "update", &hmac_update },
+ { "final", &hmac_final },
+ { NULL, NULL },
+};
+
+static const auxL_Reg hmac_metatable[] = {
+ { "__gc", &hmac__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg hmac_globals[] = {
+ { "new", &hmac_new },
+ { "interpose", &hmac_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_hmac(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, hmac_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_hmac() */
+
+
+/*
+ * Cipher - openssl.cipher
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static const EVP_CIPHER *cipher_checktype(lua_State *L, int index) {
+ const char *name = luaL_checkstring(L, index);
+ const EVP_CIPHER *type;
+
+ if (!(type = EVP_get_cipherbyname(name)))
+ luaL_argerror(L, index, lua_pushfstring(L, "%s: invalid cipher type", name));
+
+ return type;
+} /* cipher_checktype() */
+
+
+static int cipher_new(lua_State *L) {
+ const EVP_CIPHER *type;
+ EVP_CIPHER_CTX **ctx;
+ unsigned char key[EVP_MAX_KEY_LENGTH] = { 0 };
+
+ type = cipher_checktype(L, 1);
+
+ ctx = prepsimple(L, CIPHER_CLASS, NULL);
+ if (!(*ctx = EVP_CIPHER_CTX_new()))
+ goto eossl;
+
+ /*
+ * NOTE: For some ciphers like AES calling :update or :final without
+ * setting a key causes a SEGV. Set a dummy key here. Same solution
+ * as used by Ruby OSSL.
+ */
+ if (!EVP_CipherInit_ex(*ctx, type, NULL, key, NULL, -1))
+ goto eossl;
+
+ return 1;
+eossl:
+ return auxL_error(L, auxL_EOPENSSL, "cipher.new");
+} /* cipher_new() */
+
+
+static int cipher_interpose(lua_State *L) {
+ return interpose(L, CIPHER_CLASS);
+} /* cipher_interpose() */
+
+
+static int cipher_init(lua_State *L, _Bool encrypt) {
+ EVP_CIPHER_CTX *ctx = checksimple(L, 1, CIPHER_CLASS);
+ const void *key, *iv;
+ size_t n, m;
+
+ key = luaL_checklstring(L, 2, &n);
+ m = (size_t)EVP_CIPHER_CTX_key_length(ctx);
+ luaL_argcheck(L, n == m, 2, lua_pushfstring(L, "%d: invalid key length (should be %d)", (int)n, (int)m));
+
+ iv = luaL_optlstring(L, 3, NULL, &n);
+ m = (size_t)EVP_CIPHER_CTX_iv_length(ctx);
+ luaL_argcheck(L, n == m, 3, lua_pushfstring(L, "%d: invalid IV length (should be %d)", (int)n, (int)m));
+
+ if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, encrypt))
+ goto sslerr;
+
+ if (!lua_isnoneornil(L, 4)) {
+ luaL_checktype(L, 4, LUA_TBOOLEAN);
+
+ if (!EVP_CIPHER_CTX_set_padding(ctx, lua_toboolean(L, 4)))
+ goto sslerr;
+ }
+
+ lua_settop(L, 1);
+
+ return 1;
+sslerr:
+ return auxL_error(L, auxL_EOPENSSL, (encrypt)? "cipher:encrypt" : "cipher:decrypt");
+} /* cipher_init() */
+
+
+static int cipher_encrypt(lua_State *L) {
+ return cipher_init(L, 1);
+} /* cipher_encrypt() */
+
+
+static int cipher_decrypt(lua_State *L) {
+ return cipher_init(L, 0);
+} /* cipher_decrypt() */
+
+
+static _Bool cipher_update_(lua_State *L, EVP_CIPHER_CTX *ctx, luaL_Buffer *B, int from, int to) {
+ const unsigned char *p;
+ size_t n;
+ int i, out;
+
+ for (i = from; i <= to; i++) {
+ p = (const unsigned char *)luaL_checklstring(L, i, &n);
+
+ if (!EVP_CipherUpdate(ctx, (void *)luaL_prepbuffsize(B, n+EVP_MAX_BLOCK_LENGTH), &out, p, n))
+ return 0;
+
+ luaL_addsize(B, out);
+ }
+
+ return 1;
+} /* cipher_update_() */
+
+
+static int cipher_update(lua_State *L) {
+ EVP_CIPHER_CTX *ctx = checksimple(L, 1, CIPHER_CLASS);
+ luaL_Buffer B;
+
+ luaL_buffinit(L, &B);
+
+ if (!cipher_update_(L, ctx, &B, 2, lua_gettop(L)))
+ goto sslerr;
+
+ luaL_pushresult(&B);
+
+ return 1;
+sslerr:
+ lua_pushnil(L);
+ auxL_pusherror(L, auxL_EOPENSSL, NULL);
+
+ return 2;
+} /* cipher_update() */
+
+
+static int cipher_final(lua_State *L) {
+ EVP_CIPHER_CTX *ctx = checksimple(L, 1, CIPHER_CLASS);
+ luaL_Buffer B;
+ size_t block;
+ int out;
+
+ luaL_buffinit(L, &B);
+
+ if (!cipher_update_(L, ctx, &B, 2, lua_gettop(L)))
+ goto sslerr;
+
+ block = EVP_CIPHER_CTX_block_size(ctx);
+
+ if (!EVP_CipherFinal(ctx, (void *)luaL_prepbuffsize(&B, block), &out))
+ goto sslerr;
+
+ luaL_pushresultsize(&B, out);
+
+ return 1;
+sslerr:
+ lua_pushnil(L);
+ auxL_pusherror(L, auxL_EOPENSSL, NULL);
+
+ return 2;
+} /* cipher_final() */
+
+
+static int cipher__gc(lua_State *L) {
+ EVP_CIPHER_CTX **ctx = luaL_checkudata(L, 1, CIPHER_CLASS);
+
+ EVP_CIPHER_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+} /* cipher__gc() */
+
+
+static const auxL_Reg cipher_methods[] = {
+ { "encrypt", &cipher_encrypt },
+ { "decrypt", &cipher_decrypt },
+ { "update", &cipher_update },
+ { "final", &cipher_final },
+ { NULL, NULL },
+};
+
+static const auxL_Reg cipher_metatable[] = {
+ { "__gc", &cipher__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg cipher_globals[] = {
+ { "new", &cipher_new },
+ { "interpose", &cipher_interpose },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_cipher(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, cipher_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_cipher() */
+
+
+/*
+ * OCSP_RESPONSE - openssl.ocsp.response
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int or_tostring(lua_State *L) {
+ OCSP_RESPONSE *resp = checksimple(L, 1, OCSP_RESPONSE_CLASS);
+ BIO *bio = getbio(L);
+ size_t len;
+ char *bytes;
+
+ if (!OCSP_RESPONSE_print(bio, resp, 0))
+ return auxL_error(L, auxL_EOPENSSL, "OCSP_RESPONSE:tostring");
+
+ len = BIO_get_mem_data(bio, &bytes);
+ lua_pushlstring(L, bytes, len);
+
+ return 1;
+} /* or__tostring() */
+
+
+static int or_toPEM(lua_State *L) {
+ OCSP_RESPONSE *resp = checksimple(L, 1, OCSP_RESPONSE_CLASS);
+ BIO *bio = getbio(L);
+ size_t len;
+ char *bytes;
+
+ if (!PEM_write_bio_OCSP_RESPONSE(bio, resp))
+ return auxL_error(L, auxL_EOPENSSL, "OCSP_RESPONSE:toPEM");
+
+ len = BIO_get_mem_data(bio, &bytes);
+ lua_pushlstring(L, bytes, len);
+
+ return 1;
+} /* or_toPEM() */
+
+
+static int or_getBasic(lua_State *L) {
+ OCSP_RESPONSE *resp = checksimple(L, 1, OCSP_RESPONSE_CLASS);
+
+ OCSP_BASICRESP **basic = prepsimple(L, OCSP_BASICRESP_CLASS);
+
+ *basic = OCSP_response_get1_basic(resp);
+ if (!*basic)
+ return auxL_error(L, auxL_EOPENSSL, "OCSP_RESPONSE:getBasic");
+
+ return 1;
+} /* or_getBasic() */
+
+
+static int or__gc(lua_State *L) {
+ OCSP_RESPONSE **ud = luaL_checkudata(L, 1, OCSP_RESPONSE_CLASS);
+
+ if (*ud) {
+ OCSP_RESPONSE_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* or__gc() */
+
+static const auxL_Reg or_methods[] = {
+ { "tostring", &or_tostring },
+ { "toPEM", &or_toPEM },
+ { "getBasic", &or_getBasic },
+ { NULL, NULL },
+};
+
+static const auxL_Reg or_metatable[] = {
+ { "__tostring", &or_tostring },
+ { "__gc", &or__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg or_globals[] = {
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_ocsp_response(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, or_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_ocsp_response() */
+
+
+/*
+ * OCSP_BASICRESP - openssl.ocsp.basic
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int ob_verify(lua_State *L) {
+ OCSP_BASICRESP *basic = checksimple(L, 1, OCSP_BASICRESP_CLASS);
+ STACK_OF(X509) *certs = testsimple(L, 2, X509_CHAIN_CLASS);
+ X509_STORE *store = testsimple(L, 3, X509_STORE_CLASS);
+ unsigned long flags = luaL_optinteger(L, 4, 0);
+
+ int res = OCSP_basic_verify(basic, certs, store, flags);
+ if (res == -1)
+ return auxL_error(L, auxL_EOPENSSL, "OCSP_BASICRESP:verify");
+
+ lua_pushboolean(L, res);
+ if (res) {
+ return 1;
+ } else {
+ auxL_pusherror(L, auxL_EOPENSSL, NULL);
+ return 2;
+ }
+} /* ob_verify() */
+
+
+static int ob__gc(lua_State *L) {
+ OCSP_BASICRESP **ud = luaL_checkudata(L, 1, OCSP_BASICRESP_CLASS);
+
+ if (*ud) {
+ OCSP_BASICRESP_free(*ud);
+ *ud = NULL;
+ }
+
+ return 0;
+} /* or__gc() */
+
+
+static const auxL_Reg ob_methods[] = {
+ { "verify", &ob_verify },
+ { NULL, NULL },
+};
+
+static const auxL_Reg ob_metatable[] = {
+ { "__gc", &ob__gc },
+ { NULL, NULL },
+};
+
+static const auxL_Reg ob_globals[] = {
+ { NULL, NULL },
+};
+
+static const auxL_IntegerReg ob_verify_flags[] = {
+ { "NOSIGS", OCSP_NOSIGS},
+ { "NOVERIFY", OCSP_NOVERIFY},
+ { "NOCHAIN", OCSP_NOCHAIN},
+ { "NOCHECKS", OCSP_NOCHECKS},
+ { "NOEXPLICIT", OCSP_NOEXPLICIT},
+ { "TRUSTOTHER", OCSP_TRUSTOTHER},
+ { "NOINTERN", OCSP_NOINTERN},
+ { "TRUSTOTHER", OCSP_TRUSTOTHER},
+ { NULL, 0 },
+};
+
+EXPORT int luaopen__openssl_ocsp_basic(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, ob_globals, 0);
+ auxL_setintegers(L, ob_verify_flags);
+
+ return 1;
+} /* luaopen__openssl_ocsp_basic() */
+
+
+/*
+ * Rand - openssl.rand
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct randL_state {
+#ifdef _WIN32
+ DWORD pid;
+#else
+ pid_t pid;
+#endif
+}; /* struct randL_state */
+
+static struct randL_state *randL_getstate(lua_State *L) {
+ return lua_touserdata(L, lua_upvalueindex(1));
+} /* randL_getstate() */
+
+#if HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h> /* SYS_getrandom syscall(2) */
+#endif
+
+#if HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h> /* CTL_KERN KERN_RANDOM RANDOM_UUID sysctl(2) */
+#endif
+
+static int randL_stir(struct randL_state *st, unsigned rqstd) {
+ unsigned count = 0;
+ int error;
+ unsigned char data[256];
+
+#ifdef _WIN32
+ HCRYPTPROV hCryptProv;
+ BOOL ok;
+
+ if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+ error = GetLastError();
+ goto error;
+ }
+ while (count < rqstd) {
+ ok = CryptGenRandom(hCryptProv, sizeof data, (BYTE*)data);
+ if (!ok) {
+ CryptReleaseContext(hCryptProv, 0);
+ error = GetLastError();
+ goto error;
+ }
+
+ RAND_seed(data, sizeof data);
+
+ count += sizeof data;
+ }
+
+ CryptReleaseContext(hCryptProv, 0);
+
+ st->pid = GetCurrentProcessId();
+#else
+#if HAVE_ARC4RANDOM_BUF
+ while (count < rqstd) {
+ size_t n = MIN(rqstd - count, sizeof data);
+
+ arc4random_buf(data, n);
+
+ RAND_seed(data, n);
+
+ count += n;
+ }
+#endif
+
+#if HAVE_SYSCALL && HAVE_DECL_SYS_GETRANDOM
+ while (count < rqstd) {
+ size_t lim = MIN(rqstd - count, sizeof data);
+ int n;
+
+ n = syscall(SYS_getrandom, data, lim, 0);
+
+ if (n == -1) {
+ break;
+ }
+
+ RAND_seed(data, n);
+
+ count += n;
+ }
+#endif
+
+#if HAVE_SYS_SYSCTL_H && HAVE_DECL_RANDOM_UUID
+ while (count < rqstd) {
+ int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID };
+ size_t n = MIN(rqstd - count, sizeof data);
+
+ if (0 != sysctl(mib, countof(mib), data, &n, (void *)0, 0))
+ break;
+
+ RAND_seed(data, n);
+
+ count += n;
+ }
+
+#endif
+
+ if (count < rqstd) {
+#if defined O_CLOEXEC && (!defined _AIX /* O_CLOEXEC overflows int */)
+ int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
+#else
+ int fd = open("/dev/urandom", O_RDONLY);
+#endif
+
+ if (fd == -1) {
+ error = errno;
+ goto error;
+ }
+
+ while (count < rqstd) {
+ ssize_t n = read(fd, data, MIN(rqstd - count, sizeof data));
+
+ switch (n) {
+ case 0:
+ errno = EIO;
+
+ /* FALL THROUGH */
+ case -1:
+ if (errno == EINTR)
+ continue;
+
+ error = errno;
+
+ close(fd);
+
+ goto error;
+ default:
+ RAND_seed(data, n);
+
+ count += n;
+ }
+ }
+
+ close(fd);
+ }
+
+ st->pid = getpid();
+#endif /* _WIN32 */
+
+ return 0;
+error:;
+ struct {
+#ifdef _WIN32
+ DWORD pid;
+ SYSTEMTIME tv;
+ FILETIME ftCreation, ftExit, ftKernel, ftUser;
+#else
+ pid_t pid;
+ struct timeval tv;
+ struct rusage ru;
+ struct utsname un;
+#endif
+ uintptr_t aslr;
+#if defined __APPLE__
+ uint64_t mt;
+#elif defined __sun
+ struct timespec mt;
+#endif
+ } junk;
+
+#ifdef _WIN32
+ junk.pid = GetCurrentProcessId();
+ GetSystemTime(&junk.tv);
+ GetProcessTimes(GetCurrentProcess(), &junk.ftCreation, &junk.ftExit, &junk.ftKernel, &junk.ftUser);
+#else
+ junk.pid = getpid();
+ gettimeofday(&junk.tv, NULL);
+ getrusage(RUSAGE_SELF, &junk.ru);
+ uname(&junk.un);
+#endif
+ junk.aslr = (uintptr_t)&strcpy ^ (uintptr_t)&randL_stir;
+#if defined __APPLE__
+ junk.mt = mach_absolute_time();
+#elif defined __sun
+ /*
+ * NOTE: Linux requires -lrt for clock_gettime, and in any event
+ * should have RANDOM_UUID or getrandom. (Though, some middle-aged
+ * kernels might have neither). The BSDs have arc4random which
+ * should be using KERN_URND, KERN_ARND, and more recently
+ * getentropy. (Though, again, some older BSD kernels used an
+ * arc4random implementation that opened /dev/urandom.)
+ *
+ * Just do this for Solaris to keep things simple. We've already
+ * crossed the line of what can be reasonably accomplished on
+ * unreasonable platforms.
+ */
+ clock_gettime(CLOCK_MONOTONIC, &junk.mt);
+#endif
+
+ RAND_add(&junk, sizeof junk, 0.1);
+
+#ifdef _WIN32
+ st->pid = GetCurrentProcessId();
+#else
+ st->pid = getpid();
+#endif
+
+ return error;
+} /* randL_stir() */
+
+
+static void randL_checkpid(struct randL_state *st) {
+#ifdef _WIN32
+ if (st->pid != GetCurrentProcessId())
+#else
+ if (st->pid != getpid())
+#endif
+ (void)randL_stir(st, 16);
+} /* randL_checkpid() */
+
+
+static int rand_stir(lua_State *L) {
+ int error = randL_stir(randL_getstate(L), auxL_optunsigned(L, 1, 16, 0, UINT_MAX));
+
+ if (error) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, aux_strerror(error));
+ lua_pushinteger(L, error);
+
+ return 3;
+ } else {
+ lua_pushboolean(L, 1);
+
+ return 1;
+ }
+} /* rand_stir() */
+
+
+static int rand_add(lua_State *L) {
+ const void *buf;
+ size_t len;
+ lua_Number entropy;
+
+ buf = luaL_checklstring(L, 1, &len);
+ entropy = luaL_optnumber(L, 2, len);
+
+ RAND_add(buf, len, entropy);
+
+ lua_pushboolean(L, 1);
+
+ return 1;
+} /* rand_add() */
+
+
+static int rand_bytes(lua_State *L) {
+ int size = luaL_checkinteger(L, 1);
+ luaL_Buffer B;
+
+ randL_checkpid(randL_getstate(L));
+
+ if (!RAND_bytes((void *)luaL_buffinitsize(L, &B, size), size))
+ return auxL_error(L, auxL_EOPENSSL, "rand.bytes");
+
+ luaL_pushresultsize(&B, size);
+
+ return 1;
+} /* rand_bytes() */
+
+
+static int rand_ready(lua_State *L) {
+ lua_pushboolean(L, RAND_status() == 1);
+
+ return 1;
+} /* rand_ready() */
+
+
+static unsigned long long rand_llu(lua_State *L) {
+ unsigned long long llu;
+
+ if (!RAND_bytes((void *)&llu, sizeof llu))
+ auxL_error(L, auxL_EOPENSSL, "rand.uniform");
+
+ return llu;
+} /* rand_llu() */
+
+/*
+ * The following algorithm for rand_uniform() is taken from OpenBSD's
+ * arc4random_uniform, written by Otto Moerbeek, with subsequent
+ * simplification by Jorden Verwer. Otto's source code comment reads
+ *
+ * Uniformity is achieved by generating new random numbers until the one
+ * returned is outside the range [0, 2**32 % upper_bound). This guarantees
+ * the selected random number will be inside [2**32 % upper_bound, 2**32)
+ * which maps back to [0, upper_bound) after reduction modulo upper_bound.
+ *
+ * --
+ * A more bit-efficient approach by the eminent statistician Herman Rubin
+ * can be found in this sci.crypt Usenet post.
+ *
+ * From: hrubin@odds.stat.purdue.edu (Herman Rubin)
+ * Newsgroups: sci.crypt
+ * Subject: Re: Generating a random number between 0 and N-1
+ * Date: 14 Nov 2002 11:20:37 -0500
+ * Organization: Purdue University Statistics Department
+ * Lines: 40
+ * Message-ID: <ar0igl$1ak2@odds.stat.purdue.edu>
+ * References: <yO%y9.19646$RO1.373975@weber.videotron.net> <3DCD8D75.40408@nospam.com>
+ * NNTP-Posting-Host: odds.stat.purdue.edu
+ * X-Trace: mozo.cc.purdue.edu 1037290837 9316 128.210.141.13 (14 Nov 2002 16:20:37 GMT)
+ * X-Complaints-To: ne...@news.purdue.edu
+ * NNTP-Posting-Date: Thu, 14 Nov 2002 16:20:37 +0000 (UTC)
+ * Xref: archiver1.google.com sci.crypt:78935
+ *
+ * In article <3DCD8D7...@nospam.com>,
+ * Michael Amling <nos...@nospam.com> wrote:
+ * >Carlos Moreno wrote:
+ *
+ * I have already posted on this, but a repeat might be
+ * in order.
+ *
+ * If one can trust random bits, the most bitwise efficient
+ * manner to get a single random integer between 0 and N-1
+ * can be obtained as follows; the code can be made more
+ * computationally efficient. I believe it is easier to
+ * understand with gotos. I am assuming N>1.
+ *
+ * i = 0; j = 1;
+ *
+ * loop: j=2*j; i=2*i+RANBIT;
+ * if (j < N) goto loop;
+ * if (i >= N) {
+ * i = i - N;
+ * j = j - N;
+ * goto loop:}
+ * else return (i);
+ *
+ * The algorithm works because at each stage i is uniform
+ * between 0 and j-1.
+ *
+ * Another possibility is to generate k bits, where 2^k >= N.
+ * If 2^k = c*N + remainder, generate the appropriate value
+ * if a k-bit random number is less than c*N.
+ *
+ * For N = 17 (numbers just larger than powers of 2 are "bad"),
+ * the amount of information is about 4.09 bits, the best
+ * algorithm to generate one random number takes about 5.765
+ * bits, taking k = 5 uses 9.412 bits, taking k = 6 or 7 uses
+ * 7.529 bits. These are averages, but the tails are not bad.
+ *
+ * (https://groups.google.com/forum/message/raw?msg=sci.crypt/DMslf6tSrD8/rv9rk6oP3r4J)
+ */
+static int rand_uniform(lua_State *L) {
+ unsigned long long r;
+
+ randL_checkpid(randL_getstate(L));
+
+ if (lua_isnoneornil(L, 1)) {
+ r = rand_llu(L);
+ } else {
+ unsigned long long N, m;
+
+ N = auxL_checkunsigned(L, 1);
+
+ luaL_argcheck(L, N > 1, 1, lua_pushfstring(L, "[0, %d): interval is empty", (int)N));
+
+ m = -N % N;
+
+ do {
+ r = rand_llu(L);
+ } while (r < m);
+
+ r = r % N;
+ }
+
+ auxL_pushunsigned(L, r);
+
+ return 1;
+} /* rand_uniform() */
+
+
+static const auxL_Reg rand_globals[] = {
+ { "stir", &rand_stir },
+ { "add", &rand_add },
+ { "bytes", &rand_bytes },
+ { "ready", &rand_ready },
+ { "uniform", &rand_uniform },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_rand(lua_State *L) {
+ struct randL_state *st;
+
+ initall(L);
+
+ st = lua_newuserdata(L, sizeof *st);
+ memset(st, 0, sizeof *st);
+ auxL_newlib(L, rand_globals, 1);
+
+ return 1;
+} /* luaopen__openssl_rand() */
+
+
+/*
+ * DES - openssl.des
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static int de5_string_to_key(lua_State *L) {
+ DES_cblock key;
+
+ DES_string_to_key(luaL_checkstring(L, 1), &key);
+ lua_pushlstring(L, (char *)key, sizeof key);
+
+ return 1;
+} /* de5_string_to_key() */
+
+static int de5_set_odd_parity(lua_State *L) {
+ const char *src;
+ size_t len;
+ DES_cblock key;
+
+ src = luaL_checklstring(L, 1, &len);
+ memset(&key, 0, sizeof key);
+ memcpy(&key, src, MIN(len, sizeof key));
+
+ DES_set_odd_parity(&key);
+ lua_pushlstring(L, (char *)key, sizeof key);
+
+ return 1;
+} /* de5_set_odd_parity() */
+
+static const auxL_Reg des_globals[] = {
+ { "string_to_key", &de5_string_to_key },
+ { "set_odd_parity", &de5_set_odd_parity },
+ { NULL, NULL },
+};
+
+EXPORT int luaopen__openssl_des(lua_State *L) {
+ initall(L);
+
+ auxL_newlib(L, des_globals, 0);
+
+ return 1;
+} /* luaopen__openssl_des() */
+
+
+#if !OPENSSL_PREREQ(1,1,0)
+/*
+ * Multithread Reentrancy Protection
+ *
+ * Pre-1.0.2, OpenSSL needs to be given locking primitives
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+static struct {
+#if _WIN32
+ HANDLE *lock;
+#else
+ pthread_mutex_t *lock;
+#endif
+ int nlock;
+} mt_state;
+
+static void mt_lock(int mode, int type, const char *file NOTUSED, int line NOTUSED) {
+ if (mode & CRYPTO_LOCK)
+#if _WIN32
+ WaitForSingleObject(mt_state.lock[type], INFINITE);
+#else
+ pthread_mutex_lock(&mt_state.lock[type]);
+#endif
+ else
+#if _WIN32
+ ReleaseMutex(mt_state.lock[type]);
+#else
+ pthread_mutex_unlock(&mt_state.lock[type]);
+#endif
+} /* mt_lock() */
+
+/*
+ * Sources include Google and especially the Wine Project. See get_unix_tid
+ * at http://source.winehq.org/git/wine.git/?a=blob;f=dlls/ntdll/server.c.
+ */
+#if __FreeBSD__
+#include <sys/thr.h> /* thr_self(2) */
+#elif __NetBSD__
+#include <lwp.h> /* _lwp_self(2) */
+#endif
+
+static unsigned long mt_gettid(void) {
+#if __APPLE__
+ return pthread_mach_thread_np(pthread_self());
+#elif __DragonFly__
+ return lwp_gettid();
+#elif __FreeBSD__
+ long id;
+
+ thr_self(&id);
+
+ return id;
+#elif __NetBSD__
+ return _lwp_self();
+#elif _WIN32
+ return GetCurrentThreadId();
+#else
+ /*
+ * pthread_t is an integer on Solaris and Linux, an unsigned integer
+ * on AIX, and a unique pointer on OpenBSD.
+ */
+ return (unsigned long)pthread_self();
+#endif
+} /* mt_gettid() */
+
+/* mt_init must not be called from multiple threads at once */
+static int mt_init(void) {
+ static int done, bound;
+ int error = 0;
+
+ if (done)
+ goto epilog;
+
+ if (!CRYPTO_get_locking_callback()) {
+ if (!mt_state.lock) {
+ int i;
+
+ mt_state.nlock = CRYPTO_num_locks();
+
+ if (!(mt_state.lock = malloc(mt_state.nlock * sizeof *mt_state.lock))) {
+ error = errno;
+ goto epilog;
+ }
+
+ for (i = 0; i < mt_state.nlock; i++) {
+#if _WIN32
+ if (!(mt_state.lock[i] = CreateMutex(NULL, FALSE, NULL))) {
+ error = GetLastError();
+#else
+ if ((error = pthread_mutex_init(&mt_state.lock[i], NULL))) {
+#endif
+ while (i > 0) {
+#if _WIN32
+ CloseHandle(mt_state.lock[--i]);
+#else
+ pthread_mutex_destroy(&mt_state.lock[--i]);
+#endif
+ }
+
+ free(mt_state.lock);
+ mt_state.lock = NULL;
+
+ goto epilog;
+ }
+ }
+ }
+
+ CRYPTO_set_locking_callback(&mt_lock);
+ bound = 1;
+ }
+
+ if (!CRYPTO_get_id_callback()) {
+ CRYPTO_set_id_callback(&mt_gettid);
+ bound = 1;
+ }
+
+ if (bound && (error = dl_anchor()))
+ goto epilog;
+
+ done = 1;
+epilog:
+ return error;
+} /* mt_init() */
+
+#endif /* !OPENSSL_PREREQ(1,1,0) */
+
+
+static void initall(lua_State *L) {
+ static int initssl;
+ int error = 0;
+
+#if _WIN32
+ static volatile HANDLE mutex = NULL;
+ if (mutex == NULL) {
+ HANDLE p;
+ if (!(p = CreateMutex(NULL, FALSE, NULL)))
+ auxL_error(L, GetLastError(), "openssl.init");
+ if (InterlockedCompareExchangePointer((PVOID*)&mutex, (PVOID)p, NULL) != NULL)
+ CloseHandle(p);
+ }
+ if (WaitForSingleObject(mutex, INFINITE) == WAIT_FAILED)
+ auxL_error(L, GetLastError(), "openssl.init");
+#else
+ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_lock(&mutex);
+#endif
+
+#if !OPENSSL_PREREQ(1,1,0)
+ if (!error)
+ error = mt_init();
+
+ if (!error && !initssl) {
+ initssl = 1;
+
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+
+ /*
+ * TODO: Figure out a way to detect whether OpenSSL has
+ * already been configured.
+ */
+ OPENSSL_config(NULL);
+ }
+#endif
+
+ if (!error)
+ error = compat_init();
+
+ if (!error)
+ error = ex_init();
+
+#if _WIN32
+ ReleaseMutex(mutex);
+#else
+ pthread_mutex_unlock(&mutex);
+#endif
+
+ if (error)
+ auxL_error(L, error, "openssl.init");
+
+ ex_newstate(L);
+
+ auxL_addclass(L, BIGNUM_CLASS, bn_methods, bn_metatable, 0);
+ pk_luainit(L, 0);
+#ifndef OPENSSL_NO_EC
+ auxL_addclass(L, EC_GROUP_CLASS, ecg_methods, ecg_metatable, 0);
+#endif
+ auxL_addclass(L, X509_NAME_CLASS, xn_methods, xn_metatable, 0);
+ auxL_addclass(L, X509_GENS_CLASS, gn_methods, gn_metatable, 0);
+ auxL_addclass(L, X509_EXT_CLASS, xe_methods, xe_metatable, 0);
+ auxL_addclass(L, X509_CERT_CLASS, xc_methods, xc_metatable, 0);
+ auxL_addclass(L, X509_CSR_CLASS, xr_methods, xr_metatable, 0);
+ auxL_addclass(L, X509_CRL_CLASS, xx_methods, xx_metatable, 0);
+ auxL_addclass(L, X509_CHAIN_CLASS, xl_methods, xl_metatable, 0);
+ auxL_addclass(L, X509_STORE_CLASS, xs_methods, xs_metatable, 0);
+ auxL_addclass(L, X509_VERIFY_PARAM_CLASS, xp_methods, xp_metatable, 0);
+ auxL_addclass(L, PKCS12_CLASS, p12_methods, p12_metatable, 0);
+ auxL_addclass(L, SSL_CTX_CLASS, sx_methods, sx_metatable, 0);
+ auxL_addclass(L, SSL_CLASS, ssl_methods, ssl_metatable, 0);
+ auxL_addclass(L, DIGEST_CLASS, md_methods, md_metatable, 0);
+ auxL_addclass(L, HMAC_CLASS, hmac_methods, hmac_metatable, 0);
+ auxL_addclass(L, CIPHER_CLASS, cipher_methods, cipher_metatable, 0);
+ auxL_addclass(L, OCSP_RESPONSE_CLASS, or_methods, or_metatable, 0);
+ auxL_addclass(L, OCSP_BASICRESP_CLASS, ob_methods, ob_metatable, 0);
+
+ /* Create cache for pointers */
+ lua_newtable(L);
+ lua_createtable(L, 0, 2);
+ lua_pushliteral(L, "kv");
+ lua_setfield(L, -2, "__mode");
+ lua_pushliteral(L, "luaossl cache");
+ lua_setfield(L, -2, "__name");
+ lua_setmetatable(L, -2);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, (void *)&initall);
+} /* initall() */
+