/*=========================================================================*\ * ocsp.c * X509 certificate sign request routines for lua-openssl binding * * Author: george zhao \*=========================================================================*/ #include "openssl.h" #include "private.h" #include "openssl/ocsp.h" #define MYNAME "ocsp" #define MYVERSION MYNAME " library for " LUA_VERSION " / Nov 2014 / "\ "based on OpenSSL " SHLIB_VERSION_NUMBER static int openssl_ocsp_request_new(lua_State*L) { OCSP_REQUEST *req = NULL; if (lua_isstring(L, 1)) { BIO* bio = load_bio_object(L, 1); req = d2i_OCSP_REQUEST_bio(bio, NULL); /* if (!req) { BIO_reset(bio); req = PEM_read_bio_OCSP_REQUEST(bio, NULL, NULL); } */ BIO_free(bio); } else { X509 *issuer = CHECK_OBJECT(1, X509, "openssl.x509"); X509_NAME *iname = X509_get_subject_name(issuer); ASN1_BIT_STRING *ikey = X509_get0_pubkey_bitstr(issuer); OCSP_CERTID *id = NULL; OCSP_ONEREQ *one = NULL; char buf[1024]; int nonce = lua_gettop(L) > 2 ? auxiliar_checkboolean(L, 3) : 0; req = OCSP_REQUEST_new(); if (lua_istable(L, 2)) { int len = lua_rawlen(L, 2); int i; for (i = 1; i <= len; i++) { lua_rawgeti(L, 2, i); if (auxiliar_isclass(L, "openssl.x509", -1)) { X509 *cert = CHECK_OBJECT(2, X509, "openssl.x509"); id = OCSP_cert_to_id(NULL, cert, issuer); one = OCSP_request_add0_id(req, id); } else { size_t size; char *serial = (char *)luaL_checklstring(L, -1, &size); ASN1_INTEGER *sno = ASN1_INTEGER_new(); BIO* bio = BIO_new(BIO_s_mem()); BIO_write(bio, serial, size); if (a2i_ASN1_INTEGER(bio, sno, buf, 1024) == 1) { id = OCSP_cert_id_new(EVP_sha1(), iname, ikey, sno); one = OCSP_request_add0_id(req, id); } ASN1_INTEGER_free(sno); BIO_free(bio); } if (!one) { OCSP_CERTID_free(id); OCSP_REQUEST_free(req); req = NULL; lua_pop(L, 1); break; } lua_pop(L, 1); } } else if (auxiliar_isclass(L, "openssl.x509", 2)) { X509 *cert = CHECK_OBJECT(2, X509, "openssl.x509"); id = OCSP_cert_to_id(NULL, cert, issuer); one = OCSP_request_add0_id(req, id); if (!one) { OCSP_CERTID_free(id); OCSP_REQUEST_free(req); req = NULL; } } else { ASN1_INTEGER *sno = ASN1_INTEGER_new(); BIO* bio = load_bio_object(L, 2); if (a2i_ASN1_INTEGER(bio, sno, buf, 1024) == 1) { id = OCSP_cert_id_new(EVP_sha1(), iname, ikey, sno); one = OCSP_request_add0_id(req, id); if (!one) { OCSP_CERTID_free(id); OCSP_REQUEST_free(req); req = NULL; } } ASN1_INTEGER_free(sno); BIO_free(bio); } if (nonce) OCSP_request_add1_nonce(req, NULL, -1); } if (req) { PUSH_OBJECT(req, "openssl.ocsp_request"); } else lua_pushnil(L); return 1; } static int openssl_ocsp_request_export(lua_State*L) { OCSP_REQUEST *req = CHECK_OBJECT(1, OCSP_REQUEST, "openssl.ocsp_request"); int pem = lua_gettop(L) > 1 ? auxiliar_checkboolean(L, 2) : 0; int ret = 0; BIO* bio; BUF_MEM *buf; bio = BIO_new(BIO_s_mem()); if (pem) { ret = PEM_write_bio_OCSP_REQUEST(bio, req); } else { ret = i2d_OCSP_REQUEST_bio(bio, req); } if (ret) { BIO_get_mem_ptr(bio, &buf); lua_pushlstring(L, buf->data, buf->length); } BIO_free(bio); return ret; } static int openssl_ocsp_request_free(lua_State*L) { OCSP_REQUEST *req = CHECK_OBJECT(1, OCSP_REQUEST, "openssl.ocsp_request"); OCSP_REQUEST_free(req); return 0; } static int openssl_ocsp_request_sign(lua_State*L) { OCSP_REQUEST *req = CHECK_OBJECT(1, OCSP_REQUEST, "openssl.ocsp_request"); X509 *signer = CHECK_OBJECT(2, X509, "openssl.x509"); EVP_PKEY *pkey = CHECK_OBJECT(3, EVP_PKEY, "openssl.evp_pkey"); STACK_OF(X509) *others = NULL; const EVP_MD *md = EVP_sha1(); int ret; int sflags = 0; if (lua_isnoneornil(L, 4)) { sflags = OCSP_NOCERTS; } else { others = openssl_sk_x509_fromtable(L, 4); } sflags = luaL_optint(L, 6, sflags); if (!lua_isnoneornil(L, 7)) md = CHECK_OBJECT(7, EVP_MD, "openssl.evp_digest"); ret = OCSP_request_sign(req, signer, pkey, md, others, sflags); lua_pushboolean(L, ret); return 1; } static int openssl_push_ocsp_certid(lua_State*L, OCSP_CERTID* cid) { ASN1_OCTET_STRING *iNameHash = NULL; ASN1_OBJECT *md = NULL; ASN1_OCTET_STRING *ikeyHash = NULL; ASN1_INTEGER *serial = NULL; int ret = OCSP_id_get0_info(&iNameHash, &md, &ikeyHash, &serial, cid); if (ret == 1) { lua_newtable(L); PUSH_ASN1_OCTET_STRING(L, iNameHash); lua_setfield(L, -2, "issuerNameHash"); PUSH_ASN1_OCTET_STRING(L, ikeyHash); lua_setfield(L, -2, "issuerKeyHash"); PUSH_ASN1_INTEGER(L, serial); lua_setfield(L, -2, "serialNumber"); PUSH_OBJECT(md, "openssl.asn1_object"); lua_setfield(L, -2, "hashAlgorithm"); } else lua_pushnil(L); return 1; } static int openssl_ocsp_request_parse(lua_State*L) { OCSP_REQUEST *req = CHECK_OBJECT(1, OCSP_REQUEST, "openssl.ocsp_request"); #if OPENSSL_VERSION_NUMBER < 0x10100000L OCSP_REQINFO *inf = req->tbsRequest; OCSP_SIGNATURE *sig = req->optionalSignature; #endif BIO* bio = BIO_new(BIO_s_mem()); int i, num; lua_newtable(L); #if OPENSSL_VERSION_NUMBER < 0x10100000L AUXILIAR_SET(L, -1, "version", ASN1_INTEGER_get(inf->version), integer); if (inf->requestorName) { openssl_push_general_name(L, inf->requestorName); lua_setfield(L, -2, "requestorName"); } num = sk_OCSP_ONEREQ_num(inf->requestList); #endif num = OCSP_request_onereq_count(req); lua_newtable(L); for (i = 0; i < num; i++) { OCSP_ONEREQ *one = OCSP_request_onereq_get0(req, i); OCSP_CERTID *cid = OCSP_onereq_get0_id(one); openssl_push_ocsp_certid(L, cid); lua_rawseti(L, -2, i + 1); } lua_setfield(L, -2, "requestList"); num = OCSP_REQUEST_get_ext_count(req); lua_newtable(L); for (i = 0; i < num; i++) { X509_EXTENSION* e = OCSP_REQUEST_get_ext(req, i); PUSH_OBJECT(e, "openssl.x509_extension"); lua_rawseti(L, -2, i + 1); } lua_setfield(L, -2, "extensions"); #if OPENSSL_VERSION_NUMBER < 0x10100000L if (sig) { (void)BIO_reset(bio); X509_signature_print(bio, sig->signatureAlgorithm, sig->signature); for (i = 0; i < sk_X509_num(sig->certs); i++) { X509_print(bio, sk_X509_value(sig->certs, i)); PEM_write_bio_X509(bio, sk_X509_value(sig->certs, i)); } } #endif BIO_free(bio); return 1; } static int openssl_ocsp_response(lua_State *L) { OCSP_RESPONSE *res = NULL; if (lua_isstring(L, 1)) { BIO* bio = load_bio_object(L, 1); res = d2i_OCSP_RESPONSE_bio(bio, NULL); /* BIO_reset(bio); if (!res) { res = PEM_read_bio_OCSP_RESPONSE(bio, NULL, NULL); } */ BIO_free(bio); } else { ASN1_TIME* thispnd, *nextpnd; OCSP_CERTID *ca_id, *cid; OCSP_BASICRESP *bs; OCSP_REQUEST *req = CHECK_OBJECT(1, OCSP_REQUEST, "openssl.ocsp_request"); X509* ca = CHECK_OBJECT(2, X509, "openssl.x509"); X509* rcert = CHECK_OBJECT(3, X509, "openssl.x509"); EVP_PKEY *rkey = CHECK_OBJECT(4, EVP_PKEY, "openssl.evp_pkey"); unsigned long flag = luaL_optint(L, 6, 0); int nmin = luaL_optint(L, 7, 0); int nday = luaL_optint(L, 8, 1); STACK_OF(X509) *rother = lua_isnoneornil(L, 9) ? NULL : openssl_sk_x509_fromtable(L, 9); int i, id_count, type; BIO* bio = NULL; type = lua_type(L, 5); if (type != LUA_TFUNCTION && type != LUA_TTABLE) { luaL_error(L, "#5 must be a table or function that to get status of certificate"); } bio = BIO_new(BIO_s_mem()); ca_id = OCSP_cert_to_id(EVP_sha1(), NULL, ca); bs = OCSP_BASICRESP_new(); thispnd = X509_gmtime_adj(NULL, 0); nextpnd = X509_gmtime_adj(NULL, nmin * 60 + nday * 3600 * 24); id_count = OCSP_request_onereq_count(req); for (i = 0; i < id_count; i++) { OCSP_ONEREQ *one; ASN1_INTEGER *serial; ASN1_OBJECT* inst = NULL; ASN1_TIME* revtm = NULL; ASN1_GENERALIZEDTIME *invtm = NULL; OCSP_SINGLERESP *single = NULL; int reason = OCSP_REVOKED_STATUS_UNSPECIFIED, status = V_OCSP_CERTSTATUS_UNKNOWN; one = OCSP_request_onereq_get0(req, i); cid = OCSP_onereq_get0_id(one); if (OCSP_id_issuer_cmp(ca_id, cid)) { OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_UNKNOWN, 0, NULL, thispnd, nextpnd); continue; } OCSP_id_get0_info(NULL, NULL, NULL, &serial, cid); if (lua_istable(L, 5)) { BUF_MEM *buf; (void)BIO_reset(bio); i2a_ASN1_INTEGER(bio, serial); BIO_get_mem_ptr(bio, &buf); lua_pushlstring(L, buf->data, buf->length); lua_gettable(L, 5); if (lua_isnil(L, -1)) status = V_OCSP_CERTSTATUS_UNKNOWN; else { luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "revoked"); if (lua_toboolean(L, -1)) { lua_pop(L, 1); status = V_OCSP_CERTSTATUS_REVOKED; lua_getfield(L, -1, "revoked_time"); if (!lua_isnil(L, -1)) { revtm = ASN1_TIME_new(); ASN1_TIME_set(revtm, luaL_checkint(L, -1)); } lua_pop(L, 1); lua_getfield(L, -1, "reason"); if (lua_isstring(L, -1)) reason = openssl_s2i_revoke_reason(lua_tostring(L, -1)); else reason = luaL_checkint(L, -1); lua_pop(L, 1); } else { lua_pop(L, 1); status = V_OCSP_CERTSTATUS_GOOD; } } } else { //TODO: } if (reason == 7) reason = OCSP_REVOKED_STATUS_REMOVEFROMCRL; else if (reason == 8) { reason = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; //inst = OBJ_txt2obj(str, 0); } else if (reason == 9 || reason == 10) { if ( reason == 9 ) reason = OCSP_REVOKED_STATUS_KEYCOMPROMISE; else if (reason == 10) reason = OCSP_REVOKED_STATUS_CACOMPROMISE; /* invtm = ASN1_GENERALIZEDTIME_new(); if (!ASN1_GENERALIZEDTIME_set_string(invtm, arg_str)) */ } single = OCSP_basic_add1_status(bs, cid, status, reason, revtm, thispnd, nextpnd); if (invtm) { OCSP_SINGLERESP_add1_ext_i2d(single, NID_invalidity_date, invtm, 0, 0); ASN1_TIME_free(revtm); } if (inst) { OCSP_SINGLERESP_add1_ext_i2d(single, NID_hold_instruction_code, inst, 0, 0); ASN1_OBJECT_free(inst); } if (invtm) ASN1_GENERALIZEDTIME_free(invtm); } OCSP_copy_nonce(bs, req); OCSP_basic_sign(bs, rcert, rkey, EVP_sha1(), rother, flag); res = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs); BIO_free(bio); } if (res) { PUSH_OBJECT(res, "openssl.ocsp_response"); } else lua_pushnil(L); return 1; } static int openssl_ocsp_response_export(lua_State*L) { OCSP_RESPONSE *res = CHECK_OBJECT(1, OCSP_RESPONSE, "openssl.ocsp_response"); int pem = lua_gettop(L) > 1 ? auxiliar_checkboolean(L, 2) : 0; int ret = 0; BIO* bio; BUF_MEM *buf; bio = BIO_new(BIO_s_mem()); if (pem) { ret = PEM_write_bio_OCSP_RESPONSE(bio, res); } else { ret = i2d_OCSP_RESPONSE_bio(bio, res); } if (ret) { BIO_get_mem_ptr(bio, &buf); lua_pushlstring(L, buf->data, buf->length); } BIO_free(bio); return ret; } static int openssl_ocsp_response_parse(lua_State *L) { luaL_error(L, "NYI"); return 1; } int openssl_ocsp_response_free(lua_State*L) { OCSP_RESPONSE *res = CHECK_OBJECT(1, OCSP_RESPONSE, "openssl.ocsp_response"); OCSP_RESPONSE_free(res); return 0; } static luaL_Reg ocsp_req_cfuns[] = { {"export", openssl_ocsp_request_export }, {"parse", openssl_ocsp_request_parse }, {"sign", openssl_ocsp_request_sign }, {"__tostring", auxiliar_tostring }, {"__gc", openssl_ocsp_request_free }, {NULL, NULL } }; static luaL_Reg ocsp_res_cfuns[] = { {"export", openssl_ocsp_response_export }, {"parse", openssl_ocsp_response_parse }, {"__gc", openssl_ocsp_response_free }, {"__tostring", auxiliar_tostring }, {NULL, NULL } }; static luaL_Reg R[] = { {"request_read", openssl_ocsp_request_new}, {"request_new", openssl_ocsp_request_new}, {"response_new", openssl_ocsp_response}, {"response_read", openssl_ocsp_response}, {NULL, NULL} }; int luaopen_ocsp(lua_State *L) { auxiliar_newclass(L, "openssl.ocsp_request", ocsp_req_cfuns); auxiliar_newclass(L, "openssl.ocsp_response", ocsp_res_cfuns); lua_newtable(L); luaL_setfuncs(L, R, 0); lua_pushliteral(L, "version"); /** version */ lua_pushliteral(L, MYVERSION); lua_settable(L, -3); return 1; }