/* ========================================================================== * openssl.c - Lua OpenSSL * -------------------------------------------------------------------------- * Copyright (c) 2012-2015 William Ahern * * 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. * ========================================================================== */ #include /* INT_MAX INT_MIN LLONG_MAX LLONG_MIN UCHAR_MAX ULLONG_MAX */ #include /* uintptr_t */ #include /* memset(3) strerror_r(3) */ #include /* strcasecmp(3) */ #include /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ #include /* struct tm time_t strptime(3) time(2) */ #include /* isdigit(3), isxdigit(3), tolower(3) */ #include /* ENOMEM ENOTSUP EOVERFLOW errno */ #include /* assert */ #include /* ssize_t pid_t */ #include /* struct timeval gettimeofday(2) */ #include /* struct stat stat(2) */ #include /* AF_INET AF_INET6 */ #include /* RUSAGE_SELF struct rusage getrusage(2) */ #include /* struct utsname uname(3) */ #include /* O_RDONLY O_CLOEXEC open(2) */ #include /* close(2) getpid(2) */ #include /* struct in_addr struct in6_addr */ #include /* inet_pton(3) */ #include /* pthread_mutex_init(3) pthread_mutex_lock(3) pthread_mutex_unlock(3) */ #include /* dladdr(3) dlopen(3) */ #if __APPLE__ #include /* mach_absolute_time() */ #define HAVE_ARC4RANDOM #endif #if defined(__FreeBSD_kernel__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(BSD) #define HAVE_ARC4RANDOM #endif #if defined(__linux__) #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0) #define HAVE_GETRANDOM #include #include #else #define HAVE_SYS_SYSCTL_H #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LUA_VERSION_NUM < 502 #include "compat52.h" #endif #define OPENSSL_PREREQ(M, m, p) \ (OPENSSL_VERSION_NUMBER >= (((M) << 28) | ((m) << 20) | ((p) << 12)) && !defined LIBRESSL_VERSION_NUMBER) #define LIBRESSL_PREREQ(M, m, p) \ (LIBRESSL_VERSION_NUMBER >= (((M) << 28) | ((m) << 20) | ((p) << 12))) #ifndef HAVE_DLADDR #define HAVE_DLADDR (!defined _AIX) /* TODO: https://root.cern.ch/drupal/content/aix-and-dladdr */ #endif #ifndef HAVE_SSL_CTX_SET_ALPN_PROTOS #define HAVE_SSL_CTX_SET_ALPN_PROTOS OPENSSL_PREREQ(1, 0, 2) #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_SET_ALPN_PROTOS #define HAVE_SSL_SET_ALPN_PROTOS HAVE_SSL_CTX_SET_ALPN_PROTOS #endif #ifndef HAVE_SSL_GET0_ALPN_SELECTED #define HAVE_SSL_GET0_ALPN_SELECTED HAVE_SSL_CTX_SET_ALPN_PROTOS #endif #ifndef HAVE_DTLSV1_CLIENT_METHOD #define HAVE_DTLSV1_CLIENT_METHOD (!defined OPENSSL_NO_DTLS1) #endif #ifndef HAVE_DTLSV1_SERVER_METHOD #define HAVE_DTLSV1_SERVER_METHOD HAVE_DTLSV1_CLIENT_METHOD #endif #ifndef HAVE_DTLS_CLIENT_METHOD #define HAVE_DTLS_CLIENT_METHOD (OPENSSL_PREREQ(1, 0, 2) && !defined OPENSSL_NO_DTLS1) #endif #ifndef HAVE_DTLS_SERVER_METHOD #define HAVE_DTLS_SERVER_METHOD HAVE_DTLS_CLIENT_METHOD #endif #ifndef HAVE_DTLSV1_2_CLIENT_METHOD #define HAVE_DTLSV1_2_CLIENT_METHOD (OPENSSL_PREREQ(1, 0, 2) && !defined OPENSSL_NO_DTLS1) #endif #ifndef HAVE_DTLSV1_2_SERVER_METHOD #define HAVE_DTLSV1_2_SERVER_METHOD HAVE_DTLSV1_2_CLIENT_METHOD #endif #ifndef STRERROR_R_CHAR_P #define STRERROR_R_CHAR_P (defined __GLIBC__ && (_GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600))) #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_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" /* not a pointer */ #define HMAC_CLASS "HMAC_CTX" /* not a pointer */ #define CIPHER_CLASS "EVP_CIPHER_CTX" /* not a pointer */ #if __GNUC__ #define NOTUSED __attribute__((unused)) #else #define NOTUSED #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)) #define stricmp(a, b) strcasecmp((a), (b)) #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 prepsimple_(a, b, c, ...) prepsimple((a), (b), (c)) #define prepsimple(...) 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() */ /* * 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 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 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 _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)? &strcasecmp : &strcmp; for (int 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() */ NOTUSED 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(...) 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(...) 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(...) 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(...) 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++) { /* copy shared upvalues */ luaL_checkstack(L, nups, "too many upvalues"); for (int 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; 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, ...) \ (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); } } else if (error == auxL_EDYLD) { const char *const fmt = (fun)? "%s: %s" : "%.0s%s"; return lua_pushfstring(L, fmt, (fun)? fun : "", dlerror()); } 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); return lua_error(L); } /* 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() */ /* * dl - dynamically loaded module management * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Prevent loader from unlinking us if we've registered a callback with * OpenSSL by taking another reference to ourselves. */ static int dl_anchor(void) { #if HAVE_DLADDR extern int luaopen__openssl(lua_State *); static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static void *anchor; Dl_info info; int error = 0; if ((error = pthread_mutex_lock(&mutex))) return error; 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: (void)pthread_mutex_unlock(&mutex); 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_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(key->type); } /* compat_EVP_PKEY_base_id() */ #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_X509_GET0_EXT #define X509_get0_ext(crt, i) X509_get_ext((crt), (i)) #endif #if !HAVE_X509_CRL_GET0_EXT #define X509_CRL_get0_ext(crt, i) X509_CRL_get_ext((crt), (i)) #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 /* * X509_STORE_free in OpenSSL versions < 1.0.2 doesn't obey reference count */ #define 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() */ #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) { 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); } /* compat_SSL_CTX_set1_cert_store() */ #endif 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() */ /* 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() */ static int compat_init(void) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static int store_index = -1, ssl_ctx_index = -1, done; int error = 0; if ((error = pthread_mutex_lock(&mutex))) return error; if (done) goto epilog; /* * 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; } done = 1; epilog: if (compat.tmp.store) { X509_STORE_free(compat.tmp.store); compat.tmp.store = NULL; } (void)pthread_mutex_unlock(&mutex); return error; sslerr: error = auxL_EOPENSSL; goto epilog; } /* compat_init() */ /* * 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, }; 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 }, }; static int ex_ondup(CRYPTO_EX_DATA *to NOTUSED, 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() */ static int ex_init(void) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static int done; struct ex_type *type; int error = 0; if ((error = pthread_mutex_lock(&mutex))) return error; 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: (void)pthread_mutex_unlock(&mutex); 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. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 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 }, }; 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(...) 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 (!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() */ static void bn_prepops(lua_State *L, BIGNUM **r, BIGNUM **a, BIGNUM **b, _Bool commute) { _Bool lvalue = 1; lua_settop(L, 2); /* a, b */ *a = checkbig(L, 1, &lvalue); if (!lvalue && commute) lua_pushvalue(L, 1); *b = checkbig(L, 2, &lvalue); if (!lvalue && commute && lua_gettop(L) < 3) lua_pushvalue(L, 2); if (lua_gettop(L) < 3) bn_push(L); *r = *(BIGNUM **)lua_touserdata(L, 3); } /* bn_prepops() */ 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_tobin(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_tobin() */ static int bn__add(lua_State *L) { BIGNUM *r, *a, *b; bn_prepops(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; bn_prepops(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; bn_prepops(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; bn_prepops(L, &r, &a, NULL, 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; bn_prepops(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; bn_prepops(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; bn_prepops(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__pow(lua_State *L) { BIGNUM *r, *a, *b; bn_prepops(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_gcd(lua_State *L) { BIGNUM *r, *a, *b; bn_prepops(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_todec(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:todec"); } /* bn_todec() */ 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 }, { "exp", &bn__pow }, { "gcd", &bn_gcd }, { "lshift", &bn__shl }, { "rshift", &bn__shr }, { "isPrime", &bn_isPrime }, { "tobin", &bn_tobin }, { "todec", &bn_todec }, { "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_todec }, { NULL, NULL }, }; static const auxL_Reg bn_globals[] = { { "new", &bn_new }, { "interpose", &bn_interpose }, { "fromBinary", &bn_fromBinary }, { "generatePrime", &bn_generatePrime }, { NULL, NULL }, }; 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); ud = prepsimple(L, PKEY_CLASS); if (lua_istable(L, 1) || lua_isnil(L, 1)) { int type = EVP_PKEY_RSA; unsigned bits = 1024; unsigned exp = 65537; int curve = NID_X9_62_prime192v1; const char *id; lua_Number n; if (!lua_istable(L, 1)) goto creat; if (loadfield(L, 1, "type", LUA_TSTRING, &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)); } 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 (loadfield(L, 1, "exp", LUA_TNUMBER, &n)) { luaL_argcheck(L, n > 0 && n < UINT_MAX, 1, lua_pushfstring(L, "%f: `exp' invalid", n)); exp = (unsigned)n; } if (loadfield(L, 1, "curve", LUA_TSTRING, &id)) { if (!auxS_txt2nid(&curve, id)) luaL_argerror(L, 1, lua_pushfstring(L, "%s: invalid curve", id)); } creat: if (!(*ud = EVP_PKEY_new())) return auxL_error(L, auxL_EOPENSSL, "pkey.new"); switch (EVP_PKEY_type(type)) { case EVP_PKEY_RSA: { RSA *rsa; if (!(rsa = RSA_generate_key(bits, exp, 0, 0))) 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_generate_parameters(bits, 0, 0, 0, 0, 0, 0))) 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; if (!(dh = DH_generate_parameters(bits, exp, 0, 0))) 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); 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 = key->type; 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() */ static int pk_sign(lua_State *L) { EVP_PKEY *key = checksimple(L, 1, PKEY_CLASS); EVP_MD_CTX *md = luaL_checkudata(L, 2, DIGEST_CLASS); luaL_Buffer B; unsigned n; if (LUAL_BUFFERSIZE < EVP_PKEY_size(key)) return luaL_error(L, "pkey:sign: LUAL_BUFFERSIZE(%u) < EVP_PKEY_size(%u)", (unsigned)LUAL_BUFFERSIZE, (unsigned)EVP_PKEY_size(key)); luaL_buffinit(L, &B); n = LUAL_BUFFERSIZE; if (!EVP_SignFinal(md, (void *)luaL_prepbuffer(&B), &n, key)) return auxL_error(L, auxL_EOPENSSL, "pkey:sign"); luaL_addsize(&B, n); luaL_pushresult(&B); 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 = luaL_checkudata(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_type(key->type)) { 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_type(key->type)); } lua_pushlstring(L, pem, len); BIO_reset(bio); break; #endif default: lua_pushnil(L); break; } /* switch() */ } /* for() */ return lua_gettop(L) - top; } /* pk_toPEM() */ 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 }; switch (which) { case PK_RSA_N: /* RSA public modulus n */ bn_dup_nil(L, key.rsa->n); break; case PK_RSA_E: /* RSA public exponent e */ bn_dup_nil(L, key.rsa->e); break; case PK_RSA_D: /* RSA secret exponent d */ bn_dup_nil(L, key.rsa->d); break; case PK_RSA_P: /* RSA secret prime p */ bn_dup_nil(L, key.rsa->p); break; case PK_RSA_Q: /* RSA secret prime q with p < q */ bn_dup_nil(L, key.rsa->q); break; case PK_RSA_DMP1: /* exponent1 */ bn_dup_nil(L, key.rsa->dmp1); break; case PK_RSA_DMQ1: /* exponent2 */ bn_dup_nil(L, key.rsa->dmq1); break; case PK_RSA_IQMP: /* coefficient */ bn_dup_nil(L, key.rsa->iqmp); break; case PK_DSA_P: bn_dup_nil(L, key.dsa->p); break; case PK_DSA_Q: bn_dup_nil(L, key.dsa->q); break; case PK_DSA_G: bn_dup_nil(L, key.dsa->g); break; case PK_DSA_PUB_KEY: bn_dup_nil(L, key.dsa->pub_key); break; case PK_DSA_PRIV_KEY: bn_dup_nil(L, key.dsa->priv_key); break; case PK_DH_P: bn_dup_nil(L, key.dh->p); break; case PK_DH_G: bn_dup_nil(L, key.dh->g); break; case PK_DH_PUB_KEY: bn_dup_nil(L, key.dh->pub_key); break; case PK_DH_PRIV_KEY: bn_dup_nil(L, key.dh->priv_key); 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() */ static _Bool pk_bn_set_nothrow(BIGNUM **dst, BIGNUM *src) { BIGNUM *tmp; if (!(tmp = BN_dup(src))) return 0; if (*dst) BN_clear_free(*dst); *dst = tmp; return 1; } /* pk_bn_set_nothrow() */ #define pk_bn_set(L, dst, index) do { \ BIGNUM *n = checkbig((L), (index)); \ if (!pk_bn_set_nothrow((dst), n)) \ 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 }; switch (which) { case PK_RSA_N: pk_bn_set(L, &key.rsa->n, index); break; case PK_RSA_E: pk_bn_set(L, &key.rsa->e, index); break; case PK_RSA_D: pk_bn_set(L, &key.rsa->d, index); break; case PK_RSA_P: pk_bn_set(L, &key.rsa->p, index); break; case PK_RSA_Q: pk_bn_set(L, &key.rsa->q, index); break; case PK_RSA_DMP1: pk_bn_set(L, &key.rsa->dmp1, index); break; case PK_RSA_DMQ1: pk_bn_set(L, &key.rsa->dmq1, index); break; case PK_RSA_IQMP: pk_bn_set(L, &key.rsa->iqmp, index); break; case PK_DSA_P: pk_bn_set(L, &key.dsa->p, index); break; case PK_DSA_Q: pk_bn_set(L, &key.dsa->q, index); break; case PK_DSA_G: pk_bn_set(L, &key.dsa->g, index); break; case PK_DSA_PUB_KEY: pk_bn_set(L, &key.dsa->pub_key, index); break; case PK_DSA_PRIV_KEY: pk_bn_set(L, &key.dsa->priv_key, index); break; case PK_DH_P: pk_bn_set(L, &key.dh->p, index); break; case PK_DH_G: pk_bn_set(L, &key.dh->g, index); break; case PK_DH_PUB_KEY: pk_bn_set(L, &key.dh->pub_key, index); break; case PK_DH_PRIV_KEY: pk_bn_set(L, &key.dh->priv_key, index); 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)) { /* * 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 (const char *const *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 }, { "sign", &pk_sign }, { "verify", &pk_verify }, { "toPEM", &pk_toPEM }, { "getParameters", &pk_getParameters }, { "setParameters", &pk_setParameters }, { 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) { if (!auxL_newmetatable(L, PKEY_CLASS, reset)) return; auxL_setfuncs(L, pk_metatable, 0); auxL_newlib(L, pk_methods, 0); for (char **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() */ int luaopen__openssl_pkey(lua_State *L) { initall(L); auxL_newlib(L, pk_globals, 0); return 1; } /* luaopen__openssl_pkey() */ /* * Deprecated module name. */ 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_new_by_nid(int nid) { EC_GROUP *group; if (!(group = EC_GROUP_new_by_curve_name(nid))) return NULL; /* flag as named for benefit of __tostring */ 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; } /* ecg_new_by_nid() */ 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_checkint(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_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[] = { { "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 */ 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_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_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 }, }; 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 = M_ASN1_IA5STRING_new())) 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 *)M_ASN1_STRING_data((o)), M_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 *)M_ASN1_STRING_data(name->d.iPAddress); len = M_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 }, }; 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 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; lua_settop(L, 3); ud = prepsimple(L, X509_EXT_CLASS); if (!lua_isnil(L, 3)) { size_t len; const char *cdata = luaL_checklstring(L, 3, &len); _Bool crit; if (xe_new_isder(value, &crit)) { 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; } BIO *bio = getbio(L); if (BIO_puts(bio, cdata) < 0) goto error; if (!(conf = NCONF_new(NULL))) goto error; if (!NCONF_load_bio(conf, bio, NULL)) goto error; ctx = &cbuf; X509V3_set_nconf(ctx, conf); } /* * 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_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 }, }; 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 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_checkint(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; #if LUA_VERSION_NUM < 502 luaL_buffinit(L, &B); #else luaL_buffinitsize(L, &B, 2 * len); #endif 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(ASN1_TIME *time) { char buf[32] = "", *cp; struct tm tm = { 0 }; int gmtoff = 0, year, i; if (!ASN1_TIME_check(time)) return 0; cp = strncpy(buf, (const char *)ASN1_STRING_data((ASN1_STRING *)time), sizeof buf - 1); if (ASN1_STRING_type(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; 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_optint(L, -1, pathLen); lua_pop(L, 1); lua_getfield(L, 2, "pathLenConstraint"); pathLen = luaL_optint(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_checkint(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 = M_ASN1_INTEGER_new())) 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_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 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) { ASN1_BIT_STRING *pk = X509_get0_pubkey_bitstr(checksimple(L, 1, X509_CERT_CLASS)); const char *id = luaL_optstring(L, 2, "sha1"); const EVP_MD *md; unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int len; if (!(md = EVP_get_digestbyname(id))) return luaL_error(L, "x509.cert:getPublicKeyDigest: %s: invalid digest type", id); if (!EVP_Digest(pk->data, pk->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 const EVP_MD *xc_signature(lua_State *L, int index, EVP_PKEY *key) { const char *id; const EVP_MD *md; if ((id = luaL_optstring(L, index, NULL))) return ((md = EVP_get_digestbyname(id)))? md : EVP_md_null(); switch (EVP_PKEY_type(key->type)) { case EVP_PKEY_RSA: return EVP_sha1(); case EVP_PKEY_DSA: return EVP_dss1(); case EVP_PKEY_EC: return EVP_ecdsa(); default: return EVP_md_null(); } } /* xc_signature() */ 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, xc_signature(L, 3, key))) 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__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 }, { "getExtension", &xc_getExtension }, { "getExtensionCount", &xc_getExtensionCount }, { "isIssuedBy", &xc_isIssuedBy }, { "getPublicKey", &xc_getPublicKey }, { "setPublicKey", &xc_setPublicKey }, { "getPublicKeyDigest", &xc_getPublicKeyDigest }, { "sign", &xc_sign }, { "text", &xc_text }, { "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 }, }; 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_checkint(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.cert: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_setExtensionByNid(lua_State *L, X509_REQ *csr, int target_nid, void* value) { 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 SAN * 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, 0, X509V3_ADD_REPLACE)) 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. */ csr->req_info->enc.modified = 1; lua_pushboolean(L, 1); return 1; error: if (sk) sk_X509_EXTENSION_pop_free(sk, X509_EXTENSION_free); return auxL_error(L, auxL_EOPENSSL, "x509.csr.setExtensionByNid"); } /* xr_setExtensionByNid() */ 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); return xr_setExtensionByNid(L, csr, NID_subject_alt_name, gens); } /* 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_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, xc_signature(L, 3, key))) return auxL_error(L, auxL_EOPENSSL, "x509.csr:sign"); lua_pushboolean(L, 1); return 1; } /* xr_sign() */ 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 }, { "sign", &xr_sign }, { "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 }, }; 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 { if (!(*ud = X509_CRL_new())) return auxL_error(L, auxL_EOPENSSL, "x509.crl.new"); X509_gmtime_adj(X509_CRL_get_lastUpdate(*ud), 0); } 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_checkint(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; ASN1_TIME *time; if ((time = X509_CRL_get_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); /* lastUpdate always present */ if (!ASN1_TIME_set(X509_CRL_get_lastUpdate(crl), updated)) return auxL_error(L, auxL_EOPENSSL, "x509.crl:setLastUpdate"); lua_pushboolean(L, 1); return 1; } /* xx_setLastUpdate() */ static int xx_getNextUpdate(lua_State *L) { X509_CRL *crl = checksimple(L, 1, X509_CRL_CLASS); double updateby = INFINITY; ASN1_TIME *time; if ((time = X509_CRL_get_nextUpdate(crl))) updateby = timeutc(time); if (isfinite(updateby)) lua_pushnumber(L, 1); 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 = NULL; if (X509_CRL_get_nextUpdate(crl)) { if (!ASN1_TIME_set(X509_CRL_get_nextUpdate(crl), updateby)) goto error; } else { if (!(time = ASN1_TIME_new())) goto error; if (!(ASN1_TIME_set(time, updateby))) goto error; if (!X509_CRL_set_nextUpdate(crl, time)) goto error; time = NULL; } lua_pushboolean(L, 1); return 1; error: if (time) 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() */ 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_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, xc_signature(L, 3, key))) return auxL_error(L, auxL_EOPENSSL, "x509.crl:sign"); lua_pushboolean(L, 1); return 1; } /* xx_sign() */ 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__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 }, { "addExtension", &xx_addExtension }, { "getExtension", &xx_getExtension }, { "getExtensionCount", &xx_getExtensionCount }, { "sign", &xx_sign }, { "text", &xx_text }, { "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 }, }; 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; CRYPTO_add(&crt->references, 1, CRYPTO_LOCK_X509); } } 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); ret = prepsimple(L, X509_CERT_CLASS); if (!(*ret = X509_dup(crt))) return auxL_error(L, auxL_EOPENSSL, "x509.chain:__next"); 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 }, }; 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 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); for (i = 2; i <= top; i++) { if (lua_isuserdata(L, i)) { X509 *crt = checksimple(L, i, X509_CERT_CLASS); X509 *dup; if (!(dup = X509_dup(crt))) return auxL_error(L, auxL_EOPENSSL, "x509.store:add"); if (!X509_STORE_add_cert(store, dup)) { X509_free(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 ok = X509_STORE_load_locations(store, path, NULL); if (!ok) return auxL_error(L, auxL_EOPENSSL, "x509.store:add"); } } lua_pushvalue(L, 1); return 1; } /* xs_add() */ 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; 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)))) return auxL_error(L, auxL_EOPENSSL, "x509.store:verify"); n = sk_X509_num(chain); for (i = 0; i < n; i++) { if (!(elm = sk_X509_value(chain, i))) continue; CRYPTO_add(&elm->references, 1, CRYPTO_LOCK_X509); } } if (!X509_STORE_CTX_init(&ctx, store, crt, chain)) { sk_X509_pop_free(chain, X509_free); return auxL_error(L, auxL_EOPENSSL, "x509.store:verify"); } ERR_clear_error(); ok = X509_verify_cert(&ctx); switch (ok) { case 1: /* verified */ *proof = X509_STORE_CTX_get1_chain(&ctx); X509_STORE_CTX_cleanup(&ctx); if (!*proof) return auxL_error(L, auxL_EOPENSSL, "x509.store:verify"); lua_pushboolean(L, 1); lua_pushvalue(L, -2); return 2; case 0: /* not verified */ why = X509_STORE_CTX_get_error(&ctx); X509_STORE_CTX_cleanup(&ctx); lua_pushboolean(L, 0); lua_pushstring(L, X509_verify_cert_error_string(why)); return 2; default: X509_STORE_CTX_cleanup(&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 }, { "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 }, }; int luaopen__openssl_x509_store(lua_State *L) { initall(L); auxL_newlib(L, xs_globals, 0); 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 }, }; 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__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 }, { NULL, NULL }, }; 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 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * NOTE: TLS methods and flags were added in tandem. For example, if the * macro SSL_OP_NO_TLSv1_1 is defined we know TLSv1_1_server_method is also * declared and defined. */ 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 }; /* later versions of SSL declare a const qualifier on the return type */ __typeof__(&TLSv1_client_method) method = &TLSv1_client_method; _Bool srv; SSL_CTX **ud; int options = 0; lua_settop(L, 2); srv = lua_toboolean(L, 2); switch (auxL_checkoption(L, 1, "TLS", opts, 1)) { case 0: /* SSL */ method = (srv)? &SSLv23_server_method : &SSLv23_client_method; options = SSL_OP_NO_SSLv2; break; case 1: /* TLS */ method = (srv)? &SSLv23_server_method : &SSLv23_client_method; options = SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3; break; #ifndef OPENSSL_NO_SSL2 case 2: /* SSLv2 */ method = (srv)? &SSLv2_server_method : &SSLv2_client_method; break; #endif #ifndef OPENSSL_NO_SSL3 case 3: /* SSLv3 */ method = (srv)? &SSLv3_server_method : &SSLv3_client_method; break; #endif case 4: /* SSLv23 */ method = (srv)? &SSLv23_server_method : &SSLv23_client_method; break; case 5: /* TLSv1 */ case 6: /* TLSv1.0 */ method = (srv)? &TLSv1_server_method : &TLSv1_client_method; break; #if defined SSL_OP_NO_TLSv1_1 case 7: /* TLSv1_1 */ case 8: /* TLSv1.1 */ method = (srv)? &TLSv1_1_server_method : &TLSv1_1_client_method; break; #endif #if defined SSL_OP_NO_TLSv1_2 case 9: /* TLSv1_2 */ case 10: /* TLSv1.2 */ method = (srv)? &TLSv1_2_server_method : &TLSv1_2_client_method; break; #endif #if HAVE_DTLS_CLIENT_METHOD case 11: /* DTLS */ method = (srv)? &DTLS_server_method : &DTLS_client_method; break; #endif #if HAVE_DTLSV1_CLIENT_METHOD case 12: /* DTLSv1 */ case 13: /* DTLSv1.0 */ method = (srv)? &DTLSv1_server_method : &DTLSv1_client_method; break; #endif #if HAVE_DTLSV1_2_CLIENT_METHOD case 14: /* DTLSv1_2 */ case 15: /* DTLSv1.2 */ method = (srv)? &DTLSv1_server_method : &DTLSv1_client_method; break; #endif default: return luaL_argerror(L, 1, "invalid option"); } ud = prepsimple(L, SSL_CTX_CLASS); if (!(*ud = SSL_CTX_new(method()))) return auxL_error(L, auxL_EOPENSSL, "ssl.context.new"); SSL_CTX_set_options(*ud, options); 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_setVerify(lua_State *L) { SSL_CTX *ctx = checksimple(L, 1, SSL_CTX_CLASS); int mode = luaL_optint(L, 2, -1); int depth = luaL_optint(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() */ 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() */ 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 SSL *ssl_push(lua_State *, SSL *); 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; /* TODO: Install temporary panic handler to catch OOM errors */ /* pass SSL object as 1st argument */ ssl_push(L, ssl); lua_insert(L, otop + 3); /* 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 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 }, { "setVerify", &sx_setVerify }, { "getVerify", &sx_getVerify }, { "setCertificate", &sx_setCertificate }, { "setPrivateKey", &sx_setPrivateKey }, { "setCipherList", &sx_setCipherList }, { "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 { 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 }, }; 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 SSL *ssl_push(lua_State *L, SSL *ssl) { SSL **ud = prepsimple(L, SSL_CLASS); CRYPTO_add(&(ssl)->references, 1, CRYPTO_LOCK_SSL); *ud = ssl; return *ud; } /* ssl_push() */ static int ssl_new(lua_State *L) { lua_pushnil(L); return 1; } /* ssl_new() */ static int ssl_interpose(lua_State *L) { return interpose(L, SSL_CLASS); } /* ssl_interpose() */ 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() */ 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() */ 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_checkstring(L, 2); 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_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; 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__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[] = { { "setOptions", &ssl_setOptions }, { "getOptions", &ssl_getOptions }, { "clearOptions", &ssl_clearOptions }, { "getPeerCertificate", &ssl_getPeerCertificate }, { "getPeerChain", &ssl_getPeerChain }, { "getCipherInfo", &ssl_getCipherInfo }, { "getHostName", &ssl_getHostName }, { "setHostName", &ssl_setHostName }, { "getVersion", &ssl_getVersion }, { "getClientVersion", &ssl_getClientVersion }, #if HAVE_SSL_GET0_ALPN_SELECTED { "getAlpnSelected", &ssl_getAlpnSelected }, #endif #if HAVE_SSL_SET_ALPN_PROTOS { "setAlpnProtos", &ssl_setAlpnProtos }, #endif { NULL, NULL }, }; static const auxL_Reg ssl_metatable[] = { { "__gc", &ssl__gc }, { NULL, NULL }, }; static const auxL_Reg ssl_globals[] = { { "new", &ssl_new }, { "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 }, }; int luaopen__openssl_ssl(lua_State *L) { initall(L); auxL_newlib(L, ssl_globals, 0); auxL_setintegers(L, ssl_version); auxL_setintegers(L, sx_verify); auxL_setintegers(L, sx_option); return 1; } /* luaopen__openssl_ssl() */ /* * 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 = prepudata(L, sizeof *ctx, DIGEST_CLASS, NULL); EVP_MD_CTX_init(ctx); if (!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 = luaL_checkudata(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 = luaL_checkudata(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_cleanup(ctx); 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 }, }; 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 = prepudata(L, sizeof *ctx, HMAC_CLASS, NULL); HMAC_Init_ex(ctx, key, len, type, NULL); return 1; } /* 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 = luaL_checkudata(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 = luaL_checkudata(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_cleanup(ctx); 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 }, }; 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 = prepudata(L, sizeof *ctx, CIPHER_CLASS, NULL); EVP_CIPHER_CTX_init(ctx); /* * 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)) return auxL_error(L, auxL_EOPENSSL, "cipher.new"); return 1; } /* 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 = luaL_checkudata(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, *pe; size_t block, step, n; int i; block = EVP_CIPHER_CTX_block_size(ctx); if (LUAL_BUFFERSIZE < block * 2) luaL_error(L, "cipher:update: LUAL_BUFFERSIZE(%d) < 2 * EVP_CIPHER_CTX_block_size(%d)", (int)LUAL_BUFFERSIZE, (int)block); step = LUAL_BUFFERSIZE - block; for (i = from; i <= to; i++) { p = (const unsigned char *)luaL_checklstring(L, i, &n); pe = p + n; while (p < pe) { int in = (int)MIN((size_t)(pe - p), step), out; if (!EVP_CipherUpdate(ctx, (void *)luaL_prepbuffer(B), &out, p, in)) return 0; p += in; luaL_addsize(B, out); } } return 1; } /* cipher_update_() */ static int cipher_update(lua_State *L) { EVP_CIPHER_CTX *ctx = luaL_checkudata(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 = luaL_checkudata(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 (LUAL_BUFFERSIZE < block) return luaL_error(L, "cipher:update: LUAL_BUFFERSIZE(%d) < EVP_CIPHER_CTX_block_size(%d)", (int)LUAL_BUFFERSIZE, (int)block); if (!EVP_CipherFinal(ctx, (void *)luaL_prepbuffer(&B), &out)) goto sslerr; luaL_addsize(&B, out); luaL_pushresult(&B); 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_cleanup(ctx); 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 }, }; int luaopen__openssl_cipher(lua_State *L) { initall(L); auxL_newlib(L, cipher_globals, 0); return 1; } /* luaopen__openssl_cipher() */ /* * Rand - openssl.rand * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ struct randL_state { pid_t pid; }; /* struct randL_state */ static struct randL_state *randL_getstate(lua_State *L) { return lua_touserdata(L, lua_upvalueindex(1)); } /* randL_getstate() */ #if HAVE_SYS_SYSCTL_H #include /* CTL_KERN KERN_RANDOM RANDOM_UUID KERN_URND KERN_ARND sysctl(2) */ #endif #ifndef HAVE_RANDOM_UUID #define HAVE_RANDOM_UUID (HAVE_SYS_SYSCTL_H && defined __linux__) /* RANDOM_UUID is an enum, not macro */ #endif static int randL_stir(struct randL_state *st, unsigned rqstd) { unsigned count = 0; int error; unsigned char data[256]; #if defined(HAVE_ARC4RANDOM) while (count < rqstd) { size_t n = MIN(rqstd - count, sizeof data); arc4random(data, n); RAND_seed(data, n); count += n; } #elif defined(HAVE_GETRANDOM) while (count < rqstd) { size_t n = MIN(rqstd - count, sizeof data); n = syscall(SYS_getrandom, data, n, 0); if (n == -1) { break; } RAND_seed(data, n); count += n; } #elif HAVE_RANDOM_UUID int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID }; while (count < rqstd) { 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) goto syserr; 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(); return 0; syserr: error = errno; error:; struct { struct timeval tv; pid_t pid; struct rusage ru; struct utsname un; uintptr_t aslr; #if defined __APPLE__ uint64_t mt; #elif defined __sun struct timespec mt; #endif } junk; gettimeofday(&junk.tv, NULL); junk.pid = getpid(); getrusage(RUSAGE_SELF, &junk.ru); uname(&junk.un); 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 * already has RANDOM_UUID. The BSDs have KERN_URND and KERN_ARND. * 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); st->pid = getpid(); return error; } /* randL_stir() */ static void randL_checkpid(struct randL_state *st) { if (st->pid != getpid()) (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_checkint(L, 1); luaL_Buffer B; int count = 0, n; randL_checkpid(randL_getstate(L)); luaL_buffinit(L, &B); while (count < size) { n = MIN((size - count), LUAL_BUFFERSIZE); if (!RAND_bytes((void *)luaL_prepbuffer(&B), n)) return auxL_error(L, auxL_EOPENSSL, "rand.bytes"); luaL_addsize(&B, n); count += n; } luaL_pushresult(&B); 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: * References: <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 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; if (sizeof (lua_Unsigned) >= sizeof r) { N = luaL_checkunsigned(L, 1); } else { N = luaL_checknumber(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; } if (sizeof (lua_Unsigned) >= sizeof r) { lua_pushunsigned(L, r); } else { lua_pushnumber(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 }, }; 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 }, }; int luaopen__openssl_des(lua_State *L) { initall(L); auxL_newlib(L, des_globals, 0); return 1; } /* luaopen__openssl_des() */ /* * Multithread Reentrancy Protection * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static struct { pthread_mutex_t *lock; int nlock; } mt_state; static void mt_lock(int mode, int type, const char *file NOTUSED, int line NOTUSED) { if (mode & CRYPTO_LOCK) pthread_mutex_lock(&mt_state.lock[type]); else pthread_mutex_unlock(&mt_state.lock[type]); } /* 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 /* thr_self(2) */ #elif __NetBSD__ #include /* _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(); #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() */ static int mt_init(void) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static int done, bound; int error = 0; if ((error = pthread_mutex_lock(&mutex))) return error; 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 ((error = pthread_mutex_init(&mt_state.lock[i], NULL))) { while (i > 0) { pthread_mutex_destroy(&mt_state.lock[--i]); } 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: pthread_mutex_unlock(&mutex); return error; } /* mt_init() */ static void initall(lua_State *L) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static int initssl; int error; if ((error = mt_init())) auxL_error(L, error, "openssl.init"); pthread_mutex_lock(&mutex); if (!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); } pthread_mutex_unlock(&mutex); if ((error = compat_init())) auxL_error(L, error, "openssl.init"); if ((error = ex_init())) 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, 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); } /* initall() */