From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- jni/ruby/ext/openssl/ossl_ocsp.c | 1244 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1244 insertions(+) create mode 100644 jni/ruby/ext/openssl/ossl_ocsp.c (limited to 'jni/ruby/ext/openssl/ossl_ocsp.c') diff --git a/jni/ruby/ext/openssl/ossl_ocsp.c b/jni/ruby/ext/openssl/ossl_ocsp.c new file mode 100644 index 0000000..8be60bd --- /dev/null +++ b/jni/ruby/ext/openssl/ossl_ocsp.c @@ -0,0 +1,1244 @@ +/* + * $Id: ossl_ocsp.c 48798 2014-12-12 21:58:22Z nobu $ + * 'OpenSSL for Ruby' project + * Copyright (C) 2003 Michal Rokos + * Copyright (C) 2003 GOTOU Yuuzou + * All rights reserved. + */ +/* + * This program is licenced under the same licence as Ruby. + * (See the file 'LICENCE'.) + */ +#include "ossl.h" + +#if defined(OSSL_OCSP_ENABLED) + +#define WrapOCSPReq(klass, obj, req) do { \ + if(!(req)) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ + (obj) = TypedData_Wrap_Struct((klass), &ossl_ocsp_request_type, (req)); \ +} while (0) +#define GetOCSPReq(obj, req) do { \ + TypedData_Get_Struct((obj), OCSP_REQUEST, &ossl_ocsp_request_type, (req)); \ + if(!(req)) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPReq(obj, req) do { \ + OSSL_Check_Kind((obj), cOCSPReq); \ + GetOCSPReq((obj), (req)); \ +} while (0) + +#define WrapOCSPRes(klass, obj, res) do { \ + if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ + (obj) = TypedData_Wrap_Struct((klass), &ossl_ocsp_response_type, (res)); \ +} while (0) +#define GetOCSPRes(obj, res) do { \ + TypedData_Get_Struct((obj), OCSP_RESPONSE, &ossl_ocsp_response_type, (res)); \ + if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPRes(obj, res) do { \ + OSSL_Check_Kind((obj), cOCSPRes); \ + GetOCSPRes((obj), (res)); \ +} while (0) + +#define WrapOCSPBasicRes(klass, obj, res) do { \ + if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ + (obj) = TypedData_Wrap_Struct((klass), &ossl_ocsp_basicresp_type, (res)); \ +} while (0) +#define GetOCSPBasicRes(obj, res) do { \ + TypedData_Get_Struct((obj), OCSP_BASICRESP, &ossl_ocsp_basicresp_type, (res)); \ + if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPBasicRes(obj, res) do { \ + OSSL_Check_Kind((obj), cOCSPBasicRes); \ + GetOCSPBasicRes((obj), (res)); \ +} while (0) + +#define WrapOCSPCertId(klass, obj, cid) do { \ + if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ + (obj) = TypedData_Wrap_Struct((klass), &ossl_ocsp_certid_type, (cid)); \ +} while (0) +#define GetOCSPCertId(obj, cid) do { \ + TypedData_Get_Struct((obj), OCSP_CERTID, &ossl_ocsp_certid_type, (cid)); \ + if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ +} while (0) +#define SafeGetOCSPCertId(obj, cid) do { \ + OSSL_Check_Kind((obj), cOCSPCertId); \ + GetOCSPCertId((obj), (cid)); \ +} while (0) + +VALUE mOCSP; +VALUE eOCSPError; +VALUE cOCSPReq; +VALUE cOCSPRes; +VALUE cOCSPBasicRes; +VALUE cOCSPCertId; + +static void +ossl_ocsp_request_free(void *ptr) +{ + OCSP_REQUEST_free(ptr); +} + +static const rb_data_type_t ossl_ocsp_request_type = { + "OpenSSL/OCSP/REQUEST", + { + 0, ossl_ocsp_request_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + +static void +ossl_ocsp_response_free(void *ptr) +{ + OCSP_RESPONSE_free(ptr); +} + +static const rb_data_type_t ossl_ocsp_response_type = { + "OpenSSL/OCSP/RESPONSE", + { + 0, ossl_ocsp_response_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + +static void +ossl_ocsp_basicresp_free(void *ptr) +{ + OCSP_BASICRESP_free(ptr); +} + +static const rb_data_type_t ossl_ocsp_basicresp_type = { + "OpenSSL/OCSP/BASICRESP", + { + 0, ossl_ocsp_basicresp_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + +static void +ossl_ocsp_certid_free(void *ptr) +{ + OCSP_CERTID_free(ptr); +} + +static const rb_data_type_t ossl_ocsp_certid_type = { + "OpenSSL/OCSP/CERTID", + { + 0, ossl_ocsp_certid_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, +}; + +/* + * Public + */ +static VALUE +ossl_ocspcertid_new(OCSP_CERTID *cid) +{ + VALUE obj; + WrapOCSPCertId(cOCSPCertId, obj, cid); + return obj; +} + +/* + * OCSP::Resquest + */ +static VALUE +ossl_ocspreq_alloc(VALUE klass) +{ + OCSP_REQUEST *req; + VALUE obj; + + if (!(req = OCSP_REQUEST_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPReq(klass, obj, req); + + return obj; +} + +/* + * call-seq: + * OpenSSL::OCSP::Request.new -> request + * OpenSSL::OCSP::Request.new(request_der) -> request + * + * Creates a new OpenSSL::OCSP::Request. The request may be created empty or + * from a +request_der+ string. + */ + +static VALUE +ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE arg; + const unsigned char *p; + + rb_scan_args(argc, argv, "01", &arg); + if(!NIL_P(arg)){ + OCSP_REQUEST *req = DATA_PTR(self), *x; + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char*)RSTRING_PTR(arg); + x = d2i_OCSP_REQUEST(&req, &p, RSTRING_LEN(arg)); + DATA_PTR(self) = req; + if(!x){ + ossl_raise(eOCSPError, "cannot load DER encoded request"); + } + } + + return self; +} + +/* + * call-seq: + * request.add_nonce(nonce = nil) -> request + * + * Adds a +nonce+ to the OCSP request. If no nonce is given a random one will + * be generated. + * + * The nonce is used to prevent replay attacks but some servers do not support + * it. + */ + +static VALUE +ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) +{ + OCSP_REQUEST *req; + VALUE val; + int ret; + + rb_scan_args(argc, argv, "01", &val); + if(NIL_P(val)) { + GetOCSPReq(self, req); + ret = OCSP_request_add1_nonce(req, NULL, -1); + } + else{ + StringValue(val); + GetOCSPReq(self, req); + ret = OCSP_request_add1_nonce(req, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); + } + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +/* + * call-seq: + * request.check_nonce(response) -> result + * + * Checks the nonce validity for this request and +response+. + * + * The return value is one of the following: + * + * -1 :: nonce in request only. + * 0 :: nonces both present and not equal. + * 1 :: nonces present and equal. + * 2 :: nonces both absent. + * 3 :: nonce present in response only. + * + * For most responses, clients can check +result+ > 0. If a responder doesn't + * handle nonces result.nonzero? may be necessary. A result of + * 0 is always an error. + */ + +static VALUE +ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp) +{ + OCSP_REQUEST *req; + OCSP_BASICRESP *bs; + int res; + + GetOCSPReq(self, req); + SafeGetOCSPBasicRes(basic_resp, bs); + res = OCSP_check_nonce(req, bs); + + return INT2NUM(res); +} + +/* + * call-seq: + * request.add_certid(certificate_id) -> request + * + * Adds +certificate_id+ to the request. + */ + +static VALUE +ossl_ocspreq_add_certid(VALUE self, VALUE certid) +{ + OCSP_REQUEST *req; + OCSP_CERTID *id; + + GetOCSPReq(self, req); + GetOCSPCertId(certid, id); + if(!OCSP_request_add0_id(req, OCSP_CERTID_dup(id))) + ossl_raise(eOCSPError, NULL); + + return self; +} + +/* + * call-seq: + * request.certid -> [certificate_id, ...] + * + * Returns all certificate IDs in this request. + */ + +static VALUE +ossl_ocspreq_get_certid(VALUE self) +{ + OCSP_REQUEST *req; + OCSP_ONEREQ *one; + OCSP_CERTID *id; + VALUE ary, tmp; + int i, count; + + GetOCSPReq(self, req); + count = OCSP_request_onereq_count(req); + ary = (count > 0) ? rb_ary_new() : Qnil; + for(i = 0; i < count; i++){ + one = OCSP_request_onereq_get0(req, i); + if(!(id = OCSP_CERTID_dup(OCSP_onereq_get0_id(one)))) + ossl_raise(eOCSPError, NULL); + WrapOCSPCertId(cOCSPCertId, tmp, id); + rb_ary_push(ary, tmp); + } + + return ary; +} + +/* + * call-seq: + * request.sign(signer_cert, signer_key) -> self + * request.sign(signer_cert, signer_key, certificates) -> self + * request.sign(signer_cert, signer_key, certificates, flags) -> self + * + * Signs this OCSP request using +signer_cert+ and +signer_key+. + * +certificates+ is an optional Array of certificates that may be included in + * the request. + */ + +static VALUE +ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) +{ + VALUE signer_cert, signer_key, certs, flags; + OCSP_REQUEST *req; + X509 *signer; + EVP_PKEY *key; + STACK_OF(X509) *x509s; + unsigned long flg; + int ret; + + rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags); + signer = GetX509CertPtr(signer_cert); + key = GetPrivPKeyPtr(signer_key); + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + if(NIL_P(certs)){ + x509s = sk_X509_new_null(); + flags |= OCSP_NOCERTS; + } + else x509s = ossl_x509_ary2sk(certs); + GetOCSPReq(self, req); + ret = OCSP_request_sign(req, signer, key, EVP_sha1(), x509s, flg); + sk_X509_pop_free(x509s, X509_free); + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +/* + * call-seq: + * request.verify(certificates, store) -> true or false + * request.verify(certificates, store, flags) -> true or false + * + * Verifies this request using the given +certificates+ and X509 +store+. + */ + +static VALUE +ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self) +{ + VALUE certs, store, flags; + OCSP_REQUEST *req; + STACK_OF(X509) *x509s; + X509_STORE *x509st; + int flg, result; + + rb_scan_args(argc, argv, "21", &certs, &store, &flags); + x509st = GetX509StorePtr(store); + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + x509s = ossl_x509_ary2sk(certs); + GetOCSPReq(self, req); + result = OCSP_request_verify(req, x509s, x509st, flg); + sk_X509_pop_free(x509s, X509_free); + if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL)); + + return result ? Qtrue : Qfalse; +} + +/* + * Returns this request as a DER-encoded string + */ + +static VALUE +ossl_ocspreq_to_der(VALUE self) +{ + OCSP_REQUEST *req; + VALUE str; + unsigned char *p; + long len; + + GetOCSPReq(self, req); + if((len = i2d_OCSP_REQUEST(req, NULL)) <= 0) + ossl_raise(eOCSPError, NULL); + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + if(i2d_OCSP_REQUEST(req, &p) <= 0) + ossl_raise(eOCSPError, NULL); + ossl_str_adjust(str, p); + + return str; +} + +/* + * OCSP::Response + */ + +/* call-seq: + * OpenSSL::OCSP::Response.create(status, basic_response = nil) -> response + * + * Creates an OpenSSL::OCSP::Response from +status+ and +basic_response+. + */ + +static VALUE +ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp) +{ + OCSP_BASICRESP *bs; + OCSP_RESPONSE *res; + VALUE obj; + int st = NUM2INT(status); + + if(NIL_P(basic_resp)) bs = NULL; + else GetOCSPBasicRes(basic_resp, bs); /* NO NEED TO DUP */ + if(!(res = OCSP_response_create(st, bs))) + ossl_raise(eOCSPError, NULL); + WrapOCSPRes(klass, obj, res); + + return obj; +} + +static VALUE +ossl_ocspres_alloc(VALUE klass) +{ + OCSP_RESPONSE *res; + VALUE obj; + + if(!(res = OCSP_RESPONSE_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPRes(klass, obj, res); + + return obj; +} + +/* + * call-seq: + * OpenSSL::OCSP::Response.new -> response + * OpenSSL::OCSP::Response.new(response_der) -> response + * + * Creates a new OpenSSL::OCSP::Response. The response may be created empty or + * from a +response_der+ string. + */ + +static VALUE +ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE arg; + const unsigned char *p; + + rb_scan_args(argc, argv, "01", &arg); + if(!NIL_P(arg)){ + OCSP_RESPONSE *res = DATA_PTR(self), *x; + arg = ossl_to_der_if_possible(arg); + StringValue(arg); + p = (unsigned char *)RSTRING_PTR(arg); + x = d2i_OCSP_RESPONSE(&res, &p, RSTRING_LEN(arg)); + DATA_PTR(self) = res; + if(!x){ + ossl_raise(eOCSPError, "cannot load DER encoded response"); + } + } + + return self; +} + +/* + * call-seq: + * response.status -> Integer + * + * Returns the status of the response. + */ + +static VALUE +ossl_ocspres_status(VALUE self) +{ + OCSP_RESPONSE *res; + int st; + + GetOCSPRes(self, res); + st = OCSP_response_status(res); + + return INT2NUM(st); +} + +/* + * call-seq: + * response.status_string -> String + * + * Returns a status string for the response. + */ + +static VALUE +ossl_ocspres_status_string(VALUE self) +{ + OCSP_RESPONSE *res; + int st; + + GetOCSPRes(self, res); + st = OCSP_response_status(res); + + return rb_str_new2(OCSP_response_status_str(st)); +} + +/* + * call-seq: + * response.basic + * + * Returns a BasicResponse for this response + */ + +static VALUE +ossl_ocspres_get_basic(VALUE self) +{ + OCSP_RESPONSE *res; + OCSP_BASICRESP *bs; + VALUE ret; + + GetOCSPRes(self, res); + if(!(bs = OCSP_response_get1_basic(res))) + return Qnil; + WrapOCSPBasicRes(cOCSPBasicRes, ret, bs); + + return ret; +} + +/* + * call-seq: + * response.to_der -> String + * + * Returns this response as a DER-encoded string. + */ + +static VALUE +ossl_ocspres_to_der(VALUE self) +{ + OCSP_RESPONSE *res; + VALUE str; + long len; + unsigned char *p; + + GetOCSPRes(self, res); + if((len = i2d_OCSP_RESPONSE(res, NULL)) <= 0) + ossl_raise(eOCSPError, NULL); + str = rb_str_new(0, len); + p = (unsigned char *)RSTRING_PTR(str); + if(i2d_OCSP_RESPONSE(res, &p) <= 0) + ossl_raise(eOCSPError, NULL); + ossl_str_adjust(str, p); + + return str; +} + +/* + * OCSP::BasicResponse + */ +static VALUE +ossl_ocspbres_alloc(VALUE klass) +{ + OCSP_BASICRESP *bs; + VALUE obj; + + if(!(bs = OCSP_BASICRESP_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPBasicRes(klass, obj, bs); + + return obj; +} + +/* + * call-seq: + * OpenSSL::OCSP::BasicResponse.new(*) -> basic_response + * + * Creates a new BasicResponse and ignores all arguments. + */ + +static VALUE +ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) +{ + return self; +} + +/* + * call-seq: + * basic_response.copy_nonce(request) -> Integer + * + * Copies the nonce from +request+ into this response. Returns 1 on success + * and 0 on failure. + */ + +static VALUE +ossl_ocspbres_copy_nonce(VALUE self, VALUE request) +{ + OCSP_BASICRESP *bs; + OCSP_REQUEST *req; + int ret; + + GetOCSPBasicRes(self, bs); + SafeGetOCSPReq(request, req); + ret = OCSP_copy_nonce(bs, req); + + return INT2NUM(ret); +} + +/* + * call-seq: + * basic_response.add_nonce(nonce = nil) + * + * Adds +nonce+ to this response. If no nonce was provided a random nonce + * will be added. + */ + +static VALUE +ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self) +{ + OCSP_BASICRESP *bs; + VALUE val; + int ret; + + rb_scan_args(argc, argv, "01", &val); + if(NIL_P(val)) { + GetOCSPBasicRes(self, bs); + ret = OCSP_basic_add1_nonce(bs, NULL, -1); + } + else{ + StringValue(val); + GetOCSPBasicRes(self, bs); + ret = OCSP_basic_add1_nonce(bs, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); + } + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +/* + * call-seq: + * basic_response.add_status(certificate_id, status, reason, revocation_time, this_update, next_update, extensions) -> basic_response + * + * Adds a validation +status+ (0 for revoked, 1 for success) to this + * response for +certificate_id+. +reason+ describes the reason for the + * revocation, if any. + * + * The +revocation_time+, +this_update+ and +next_update+ are times for the + * certificate's revocation time, the time of this status and the next update + * time for a new status, respectively. + * + * +extensions+ may be an Array of OpenSSL::X509::Extension that will + * be added to this response or nil. + */ + +static VALUE +ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, + VALUE reason, VALUE revtime, + VALUE thisupd, VALUE nextupd, VALUE ext) +{ + OCSP_BASICRESP *bs; + OCSP_SINGLERESP *single; + OCSP_CERTID *id; + int st, rsn; + ASN1_TIME *ths, *nxt, *rev; + int error, i, rstatus = 0; + VALUE tmp; + + st = NUM2INT(status); + rsn = NIL_P(status) ? 0 : NUM2INT(reason); + if(!NIL_P(ext)){ + /* All ary's members should be X509Extension */ + Check_Type(ext, T_ARRAY); + for (i = 0; i < RARRAY_LEN(ext); i++) + OSSL_Check_Kind(RARRAY_PTR(ext)[i], cX509Ext); + } + + error = 0; + ths = nxt = rev = NULL; + if(!NIL_P(revtime)){ + tmp = rb_protect(rb_Integer, revtime, &rstatus); + if(rstatus) goto err; + rev = X509_gmtime_adj(NULL, NUM2INT(tmp)); + } + tmp = rb_protect(rb_Integer, thisupd, &rstatus); + if(rstatus) goto err; + ths = X509_gmtime_adj(NULL, NUM2INT(tmp)); + tmp = rb_protect(rb_Integer, nextupd, &rstatus); + if(rstatus) goto err; + nxt = X509_gmtime_adj(NULL, NUM2INT(tmp)); + + GetOCSPBasicRes(self, bs); + SafeGetOCSPCertId(cid, id); + if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){ + error = 1; + goto err; + } + + if(!NIL_P(ext)){ + X509_EXTENSION *x509ext; + sk_X509_EXTENSION_pop_free(single->singleExtensions, X509_EXTENSION_free); + single->singleExtensions = NULL; + for(i = 0; i < RARRAY_LEN(ext); i++){ + x509ext = DupX509ExtPtr(RARRAY_PTR(ext)[i]); + if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ + X509_EXTENSION_free(x509ext); + error = 1; + goto err; + } + X509_EXTENSION_free(x509ext); + } + } + + err: + ASN1_TIME_free(ths); + ASN1_TIME_free(nxt); + ASN1_TIME_free(rev); + if(error) ossl_raise(eOCSPError, NULL); + if(rstatus) rb_jump_tag(rstatus); + + return self; +} + +/* + * call-seq: + * basic_response.status -> statuses + * + * Returns an Array of statuses for this response. Each status contains a + * CertificateId, the status (0 for success, 1 for revoked), the reason for + * the status, the revocation time, the time of this update, the time for the + * next update and a list of OpenSSL::X509::Extensions. + */ + +static VALUE +ossl_ocspbres_get_status(VALUE self) +{ + OCSP_BASICRESP *bs; + OCSP_SINGLERESP *single; + OCSP_CERTID *cid; + ASN1_TIME *revtime, *thisupd, *nextupd; + int status, reason; + X509_EXTENSION *x509ext; + VALUE ret, ary, ext; + int count, ext_count, i, j; + + GetOCSPBasicRes(self, bs); + ret = rb_ary_new(); + count = OCSP_resp_count(bs); + for(i = 0; i < count; i++){ + single = OCSP_resp_get0(bs, i); + if(!single) continue; + + revtime = thisupd = nextupd = NULL; + status = OCSP_single_get0_status(single, &reason, &revtime, + &thisupd, &nextupd); + if(status < 0) continue; + if(!(cid = OCSP_CERTID_dup(single->certId))) + ossl_raise(eOCSPError, NULL); + ary = rb_ary_new(); + rb_ary_push(ary, ossl_ocspcertid_new(cid)); + rb_ary_push(ary, INT2NUM(status)); + rb_ary_push(ary, INT2NUM(reason)); + rb_ary_push(ary, revtime ? asn1time_to_time(revtime) : Qnil); + rb_ary_push(ary, thisupd ? asn1time_to_time(thisupd) : Qnil); + rb_ary_push(ary, nextupd ? asn1time_to_time(nextupd) : Qnil); + ext = rb_ary_new(); + ext_count = OCSP_SINGLERESP_get_ext_count(single); + for(j = 0; j < ext_count; j++){ + x509ext = OCSP_SINGLERESP_get_ext(single, j); + rb_ary_push(ext, ossl_x509ext_new(x509ext)); + } + rb_ary_push(ary, ext); + rb_ary_push(ret, ary); + } + + return ret; +} + +/* + * call-seq: + * basic_response.sign(signer_cert, signer_key) -> self + * basic_response.sign(signer_cert, signer_key, certificates) -> self + * basic_response.sign(signer_cert, signer_key, certificates, flags) -> self + * + * Signs this response using the +signer_cert+ and +signer_key+. Additional + * +certificates+ may be added to the signature along with a set of +flags+. + */ + +static VALUE +ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) +{ + VALUE signer_cert, signer_key, certs, flags; + OCSP_BASICRESP *bs; + X509 *signer; + EVP_PKEY *key; + STACK_OF(X509) *x509s; + unsigned long flg; + int ret; + + rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags); + signer = GetX509CertPtr(signer_cert); + key = GetPrivPKeyPtr(signer_key); + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + if(NIL_P(certs)){ + x509s = sk_X509_new_null(); + flg |= OCSP_NOCERTS; + } + else{ + x509s = ossl_x509_ary2sk(certs); + } + GetOCSPBasicRes(self, bs); + ret = OCSP_basic_sign(bs, signer, key, EVP_sha1(), x509s, flg); + sk_X509_pop_free(x509s, X509_free); + if(!ret) ossl_raise(eOCSPError, NULL); + + return self; +} + +/* + * call-seq: + * basic_response.verify(certificates, store) -> true or false + * basic_response.verify(certificates, store, flags) -> true or false + * + * Verifies the signature of the response using the given +certificates+, + * +store+ and +flags+. + */ +static VALUE +ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) +{ + VALUE certs, store, flags, result; + OCSP_BASICRESP *bs; + STACK_OF(X509) *x509s; + X509_STORE *x509st; + int flg; + + rb_scan_args(argc, argv, "21", &certs, &store, &flags); + x509st = GetX509StorePtr(store); + flg = NIL_P(flags) ? 0 : NUM2INT(flags); + x509s = ossl_x509_ary2sk(certs); + GetOCSPBasicRes(self, bs); + result = OCSP_basic_verify(bs, x509s, x509st, flg) > 0 ? Qtrue : Qfalse; + sk_X509_pop_free(x509s, X509_free); + if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL)); + + return result; +} + +/* + * OCSP::CertificateId + */ +static VALUE +ossl_ocspcid_alloc(VALUE klass) +{ + OCSP_CERTID *id; + VALUE obj; + + if(!(id = OCSP_CERTID_new())) + ossl_raise(eOCSPError, NULL); + WrapOCSPCertId(klass, obj, id); + + return obj; +} + +/* + * call-seq: + * OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id + * + * Creates a new OpenSSL::OCSP::CertificateId for the given +subject+ and + * +issuer+ X509 certificates. The +digest+ is used to compute the + * certificate ID and must be an OpenSSL::Digest instance. + */ + +static VALUE +ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) +{ + OCSP_CERTID *id, *newid; + X509 *x509s, *x509i; + VALUE subject, issuer, digest; + const EVP_MD *md; + + if (rb_scan_args(argc, argv, "21", &subject, &issuer, &digest) == 0) { + return self; + } + + x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ + x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ + + if (!NIL_P(digest)) { + md = GetDigestPtr(digest); + newid = OCSP_cert_to_id(md, x509s, x509i); + } else { + newid = OCSP_cert_to_id(NULL, x509s, x509i); + } + if(!newid) + ossl_raise(eOCSPError, NULL); + GetOCSPCertId(self, id); + OCSP_CERTID_free(id); + RDATA(self)->data = newid; + + return self; +} + +/* + * call-seq: + * certificate_id.cmp(other) -> true or false + * + * Compares this certificate id with +other+ and returns true if they are the + * same. + */ +static VALUE +ossl_ocspcid_cmp(VALUE self, VALUE other) +{ + OCSP_CERTID *id, *id2; + int result; + + GetOCSPCertId(self, id); + SafeGetOCSPCertId(other, id2); + result = OCSP_id_cmp(id, id2); + + return (result == 0) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * certificate_id.cmp_issuer(other) -> true or false + * + * Compares this certificate id's issuer with +other+ and returns true if + * they are the same. + */ + +static VALUE +ossl_ocspcid_cmp_issuer(VALUE self, VALUE other) +{ + OCSP_CERTID *id, *id2; + int result; + + GetOCSPCertId(self, id); + SafeGetOCSPCertId(other, id2); + result = OCSP_id_issuer_cmp(id, id2); + + return (result == 0) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * certificate_id.get_serial -> Integer + * + * Returns the serial number of the issuing certificate. + */ + +static VALUE +ossl_ocspcid_get_serial(VALUE self) +{ + OCSP_CERTID *id; + + GetOCSPCertId(self, id); + + return asn1integer_to_num(id->serialNumber); +} + +void +Init_ossl_ocsp(void) +{ + /* + * OpenSSL::OCSP implements Online Certificate Status Protocol requests + * and responses. + * + * Creating and sending an OCSP request requires a subject certificate + * that contains an OCSP URL in an authorityInfoAccess extension and the + * issuer certificate for the subject certificate. First, load the issuer + * and subject certificates: + * + * subject = OpenSSL::X509::Certificate.new subject_pem + * issuer = OpenSSL::X509::Certificate.new issuer_pem + * + * To create the request we need to create a certificate ID for the + * subject certificate so the CA knows which certificate we are asking + * about: + * + * digest = OpenSSL::Digest::SHA1.new + * certificate_id = + * OpenSSL::OCSP::CertificateId.new subject, issuer, digest + * + * Then create a request and add the certificate ID to it: + * + * request = OpenSSL::OCSP::Request.new + * request.add_certid certificate_id + * + * Adding a nonce to the request protects against replay attacks but not + * all CA process the nonce. + * + * request.add_nonce + * + * To submit the request to the CA for verification we need to extract the + * OCSP URI from the subject certificate: + * + * authority_info_access = subject.extensions.find do |extension| + * extension.oid == 'authorityInfoAccess' + * end + * + * descriptions = authority_info_access.value.split "\n" + * ocsp = descriptions.find do |description| + * description.start_with? 'OCSP' + * end + * + * require 'uri' + * + * ocsp_uri = URI ocsp[/URI:(.*)/, 1] + * + * To submit the request we'll POST the request to the OCSP URI (per RFC + * 2560). Note that we only handle HTTP requests and don't handle any + * redirects in this example, so this is insufficient for serious use. + * + * require 'net/http' + * + * http_response = + * Net::HTTP.start ocsp_uri.hostname, ocsp.port do |http| + * http.post ocsp_uri.path, request.to_der, + * 'content-type' => 'application/ocsp-request' + * end + * + * response = OpenSSL::OCSP::Response.new http_response.body + * response_basic = response.basic + * + * First we check if the response has a valid signature. Without a valid + * signature we cannot trust it. If you get a failure here you may be + * missing a system certificate store or may be missing the intermediate + * certificates. + * + * store = OpenSSL::X509::Store.new + * store.set_default_paths + * + * unless response.verify [], store then + * raise 'response is not signed by a trusted certificate' + * end + * + * The response contains the status information (success/fail). We can + * display the status as a string: + * + * puts response.status_string #=> successful + * + * Next we need to know the response details to determine if the response + * matches our request. First we check the nonce. Again, not all CAs + * support a nonce. See Request#check_nonce for the meanings of the + * return values. + * + * p request.check_nonce basic_response #=> value from -1 to 3 + * + * Then extract the status information from the basic response. (You can + * check multiple certificates in a request, but for this example we only + * submitted one.) + * + * response_certificate_id, status, reason, revocation_time, + * this_update, next_update, extensions = basic_response.status + * + * Then check the various fields. + * + * unless response_certificate_id == certificate_id then + * raise 'certificate id mismatch' + * end + * + * now = Time.now + * + * if this_update > now then + * raise 'update date is in the future' + * end + * + * if now > next_update then + * raise 'next update time has passed' + * end + */ + + mOCSP = rb_define_module_under(mOSSL, "OCSP"); + + /* + * OCSP error class. + */ + + eOCSPError = rb_define_class_under(mOCSP, "OCSPError", eOSSLError); + + /* + * An OpenSSL::OCSP::Request contains the certificate information for + * determining if a certificate has been revoked or not. A Request can be + * created for a certificate or from a DER-encoded request created + * elsewhere. + */ + + cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject); + rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc); + rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1); + rb_define_method(cOCSPReq, "add_nonce", ossl_ocspreq_add_nonce, -1); + rb_define_method(cOCSPReq, "check_nonce", ossl_ocspreq_check_nonce, 1); + rb_define_method(cOCSPReq, "add_certid", ossl_ocspreq_add_certid, 1); + rb_define_method(cOCSPReq, "certid", ossl_ocspreq_get_certid, 0); + rb_define_method(cOCSPReq, "sign", ossl_ocspreq_sign, -1); + rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1); + rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0); + + /* + * An OpenSSL::OCSP::Response contains the status of a certificate check + * which is created from an OpenSSL::OCSP::Request. + */ + + cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject); + rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2); + rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc); + rb_define_method(cOCSPRes, "initialize", ossl_ocspres_initialize, -1); + rb_define_method(cOCSPRes, "status", ossl_ocspres_status, 0); + rb_define_method(cOCSPRes, "status_string", ossl_ocspres_status_string, 0); + rb_define_method(cOCSPRes, "basic", ossl_ocspres_get_basic, 0); + rb_define_method(cOCSPRes, "to_der", ossl_ocspres_to_der, 0); + + /* + * An OpenSSL::OCSP::BasicResponse contains the status of a certificate + * check which is created from an OpenSSL::OCSP::Request. A + * BasicResponse is more detailed than a Response. + */ + + cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject); + rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc); + rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1); + rb_define_method(cOCSPBasicRes, "copy_nonce", ossl_ocspbres_copy_nonce, 1); + rb_define_method(cOCSPBasicRes, "add_nonce", ossl_ocspbres_add_nonce, -1); + rb_define_method(cOCSPBasicRes, "add_status", ossl_ocspbres_add_status, 7); + rb_define_method(cOCSPBasicRes, "status", ossl_ocspbres_get_status, 0); + rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1); + rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1); + + /* + * An OpenSSL::OCSP::CertificateId identifies a certificate to the CA so + * that a status check can be performed. + */ + + cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject); + rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc); + rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, -1); + rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1); + rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1); + rb_define_method(cOCSPCertId, "serial", ossl_ocspcid_get_serial, 0); + + /* Internal error in issuer */ + rb_define_const(mOCSP, "RESPONSE_STATUS_INTERNALERROR", INT2NUM(OCSP_RESPONSE_STATUS_INTERNALERROR)); + + /* Illegal confirmation request */ + rb_define_const(mOCSP, "RESPONSE_STATUS_MALFORMEDREQUEST", INT2NUM(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST)); + + /* The certificate was revoked for an unknown reason */ + rb_define_const(mOCSP, "REVOKED_STATUS_NOSTATUS", INT2NUM(OCSP_REVOKED_STATUS_NOSTATUS)); + + /* You must sign the request and resubmit */ + rb_define_const(mOCSP, "RESPONSE_STATUS_SIGREQUIRED", INT2NUM(OCSP_RESPONSE_STATUS_SIGREQUIRED)); + + /* Response has valid confirmations */ + rb_define_const(mOCSP, "RESPONSE_STATUS_SUCCESSFUL", INT2NUM(OCSP_RESPONSE_STATUS_SUCCESSFUL)); + + /* Try again later */ + rb_define_const(mOCSP, "RESPONSE_STATUS_TRYLATER", INT2NUM(OCSP_RESPONSE_STATUS_TRYLATER)); + + /* The certificate subject's name or other information changed */ + rb_define_const(mOCSP, "REVOKED_STATUS_AFFILIATIONCHANGED", INT2NUM(OCSP_REVOKED_STATUS_AFFILIATIONCHANGED)); + + /* This CA certificate was revoked due to a key compromise */ + rb_define_const(mOCSP, "REVOKED_STATUS_CACOMPROMISE", INT2NUM(OCSP_REVOKED_STATUS_CACOMPROMISE)); + + /* The certificate is on hold */ + rb_define_const(mOCSP, "REVOKED_STATUS_CERTIFICATEHOLD", INT2NUM(OCSP_REVOKED_STATUS_CERTIFICATEHOLD)); + + /* The certificate is no longer needed */ + rb_define_const(mOCSP, "REVOKED_STATUS_CESSATIONOFOPERATION", INT2NUM(OCSP_REVOKED_STATUS_CESSATIONOFOPERATION)); + + /* The certificate was revoked due to a key compromise */ + rb_define_const(mOCSP, "REVOKED_STATUS_KEYCOMPROMISE", INT2NUM(OCSP_REVOKED_STATUS_KEYCOMPROMISE)); + + /* The certificate was previously on hold and should now be removed from + * the CRL */ + rb_define_const(mOCSP, "REVOKED_STATUS_REMOVEFROMCRL", INT2NUM(OCSP_REVOKED_STATUS_REMOVEFROMCRL)); + + /* The certificate was superseded by a new certificate */ + rb_define_const(mOCSP, "REVOKED_STATUS_SUPERSEDED", INT2NUM(OCSP_REVOKED_STATUS_SUPERSEDED)); + + /* Your request is unauthorized. */ + rb_define_const(mOCSP, "RESPONSE_STATUS_UNAUTHORIZED", INT2NUM(OCSP_RESPONSE_STATUS_UNAUTHORIZED)); + + /* The certificate was revoked for an unspecified reason */ + rb_define_const(mOCSP, "REVOKED_STATUS_UNSPECIFIED", INT2NUM(OCSP_REVOKED_STATUS_UNSPECIFIED)); + + /* Do not include certificates in the response */ + rb_define_const(mOCSP, "NOCERTS", INT2NUM(OCSP_NOCERTS)); + + /* Do not search certificates contained in the response for a signer */ + rb_define_const(mOCSP, "NOINTERN", INT2NUM(OCSP_NOINTERN)); + + /* Do not check the signature on the response */ + rb_define_const(mOCSP, "NOSIGS", INT2NUM(OCSP_NOSIGS)); + + /* Do not verify the certificate chain on the response */ + rb_define_const(mOCSP, "NOCHAIN", INT2NUM(OCSP_NOCHAIN)); + + /* Do not verify the response at all */ + rb_define_const(mOCSP, "NOVERIFY", INT2NUM(OCSP_NOVERIFY)); + + /* Do not check trust */ + rb_define_const(mOCSP, "NOEXPLICIT", INT2NUM(OCSP_NOEXPLICIT)); + + /* (This flag is not used by OpenSSL 1.0.1g) */ + rb_define_const(mOCSP, "NOCASIGN", INT2NUM(OCSP_NOCASIGN)); + + /* (This flag is not used by OpenSSL 1.0.1g) */ + rb_define_const(mOCSP, "NODELEGATED", INT2NUM(OCSP_NODELEGATED)); + + /* Do not make additional signing certificate checks */ + rb_define_const(mOCSP, "NOCHECKS", INT2NUM(OCSP_NOCHECKS)); + + /* Do not verify additional certificates */ + rb_define_const(mOCSP, "TRUSTOTHER", INT2NUM(OCSP_TRUSTOTHER)); + + /* Identify the response by signing the certificate key ID */ + rb_define_const(mOCSP, "RESPID_KEY", INT2NUM(OCSP_RESPID_KEY)); + + /* Do not include producedAt time in response */ + rb_define_const(mOCSP, "NOTIME", INT2NUM(OCSP_NOTIME)); + + /* Indicates the certificate is not revoked but does not necessarily mean + * the certificate was issued or that this response is within the + * certificate's validity interval */ + rb_define_const(mOCSP, "V_CERTSTATUS_GOOD", INT2NUM(V_OCSP_CERTSTATUS_GOOD)); + /* Indicates the certificate has been revoked either permanently or + * temporarily (on hold). */ + rb_define_const(mOCSP, "V_CERTSTATUS_REVOKED", INT2NUM(V_OCSP_CERTSTATUS_REVOKED)); + + /* Indicates the responder does not know about the certificate being + * requested. */ + rb_define_const(mOCSP, "V_CERTSTATUS_UNKNOWN", INT2NUM(V_OCSP_CERTSTATUS_UNKNOWN)); + + /* The responder ID is based on the key name. */ + rb_define_const(mOCSP, "V_RESPID_NAME", INT2NUM(V_OCSP_RESPID_NAME)); + + /* The responder ID is based on the public key. */ + rb_define_const(mOCSP, "V_RESPID_KEY", INT2NUM(V_OCSP_RESPID_KEY)); +} + +#else /* ! OSSL_OCSP_ENABLED */ +void +Init_ossl_ocsp(void) +{ +} +#endif -- cgit v1.2.3