/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2006 Ola Bini <ola@ologix.com> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.openssl; import static org.jruby.ext.openssl.OpenSSL.debugStackTrace; import static org.jruby.ext.openssl.OpenSSL.warn; import static org.jruby.ext.openssl.X509._X509; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyFixnum; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyObject; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; import org.jruby.ext.openssl.x509store.Store; import org.jruby.ext.openssl.x509store.StoreContext; import org.jruby.ext.openssl.x509store.X509AuxCertificate; import org.jruby.ext.openssl.x509store.X509Error; import org.jruby.ext.openssl.x509store.X509Utils; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; /** * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> */ public class X509Store extends RubyObject { private static final long serialVersionUID = -2969708892287379665L; private static ObjectAllocator X509STORE_ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new X509Store(runtime, klass); } }; public static void createX509Store(final Ruby runtime, final RubyModule X509) { RubyClass Store = X509.defineClassUnder("Store", runtime.getObject(), X509STORE_ALLOCATOR); RubyClass OpenSSLError = runtime.getModule("OpenSSL").getClass("OpenSSLError"); X509.defineClassUnder("StoreError", OpenSSLError, OpenSSLError.getAllocator()); final ThreadContext context = runtime.getCurrentContext(); Store.addReadWriteAttribute(context, "error"); Store.addReadWriteAttribute(context, "error_string"); Store.addReadWriteAttribute(context, "chain"); Store.defineAnnotatedMethods(X509Store.class); Store.undefineMethod("dup"); X509StoreContext.createX509StoreContext(runtime, X509); } public X509Store(Ruby runtime, RubyClass type) { super(runtime, type); } private X509Store(Ruby runtime) { super(runtime, _X509(runtime).getClass("Store")); } static X509Store newStore(final Ruby runtime) { final X509Store store = new X509Store(runtime); store.initialize(runtime.getCurrentContext(), NULL_ARRAY); return store; } private final Store store = new Store(); final Store getStore() { return store; } @JRubyMethod(name = "initialize", rest = true, visibility = Visibility.PRIVATE) public IRubyObject initialize(final ThreadContext context, final IRubyObject[] args) { final Ruby runtime = context.runtime; final IRubyObject nil = runtime.getNil(); final IRubyObject zero = RubyFixnum.zero(runtime); store.setVerifyCallbackFunction(verifyCallback); this.set_verify_callback(nil); this.setInstanceVariable("@flags", zero); this.setInstanceVariable("@purpose", zero); this.setInstanceVariable("@trust", zero); this.setInstanceVariable("@error", nil); this.setInstanceVariable("@error_string", nil); this.setInstanceVariable("@chain", nil); this.setInstanceVariable("@time", nil); return this; } @JRubyMethod public IRubyObject verify_callback() { return this.getInstanceVariable("@verify_callback"); } @JRubyMethod(name = "verify_callback=") public IRubyObject set_verify_callback(final IRubyObject callback) { store.setExtraData(1, callback); this.setInstanceVariable("@verify_callback", callback); return callback; } @JRubyMethod(name = "flags=") public IRubyObject set_flags(final IRubyObject arg) { store.setFlags(RubyNumeric.fix2long(arg)); return arg; } @JRubyMethod(name = "purpose=") public IRubyObject set_purpose(final IRubyObject arg) { store.setPurpose(RubyNumeric.fix2int(arg)); return arg; } @JRubyMethod(name = "trust=") public IRubyObject set_trust(final IRubyObject arg) { store.setTrust(RubyNumeric.fix2int(arg)); return arg; } @JRubyMethod(name = "time=") public IRubyObject set_time(final IRubyObject arg) { setInstanceVariable("@time", arg); return arg; } @JRubyMethod public IRubyObject add_path(final ThreadContext context, final IRubyObject arg) { warn(context, "WARNING: unimplemented method called: Store#add_path"); return context.nil; } @JRubyMethod public IRubyObject add_file(final IRubyObject arg) { String file = arg.toString(); final Ruby runtime = getRuntime(); try { store.loadLocations(runtime, file, null); } catch (Exception e) { debugStackTrace(runtime, e); throw newStoreError(runtime, "loading file failed: ", e); } return this; } @JRubyMethod public IRubyObject set_default_paths(final ThreadContext context) { final Ruby runtime = context.runtime; try { store.setDefaultPaths(runtime); } catch (Exception e) { debugStackTrace(runtime, e); throw newStoreError(runtime, "setting default path failed: ", e); } return runtime.getNil(); } @JRubyMethod public X509Store add_cert(final IRubyObject cert) { X509AuxCertificate auxCert = cert instanceof X509Cert ? ((X509Cert) cert).getAuxCert() : null; if ( store.addCertificate(auxCert) != 1 ) { throw newStoreError(getRuntime(), X509Error.getLastErrorMessage()); } return this; } @JRubyMethod public X509Store add_crl(final IRubyObject crl) { java.security.cert.X509CRL jCRL = (crl instanceof X509CRL) ? ((X509CRL) crl).getCRL() : null; if ( store.addCRL(jCRL) != 1 ) { throw newStoreError(getRuntime(), X509Error.getLastErrorMessage()); } return this; } @JRubyMethod(rest = true) public IRubyObject verify(final ThreadContext context, final IRubyObject[] args, final Block block) { final Ruby runtime = context.runtime; final IRubyObject cert = args[0], chain; if ( Arity.checkArgumentCount(runtime, args, 1, 2) == 2 ) { chain = args[1]; } else { chain = runtime.getNil(); } final IRubyObject verify_callback; if (block.isGiven()) { verify_callback = runtime.newProc(Block.Type.PROC, block); } else { verify_callback = getInstanceVariable("@verify_callback"); } final X509StoreContext store_context = X509StoreContext.newStoreContext(context, this, cert, chain); store_context.setInstanceVariable("@verify_callback", verify_callback); IRubyObject result = store_context.callMethod(context, "verify"); this.setInstanceVariable("@error", store_context.error(context)); this.setInstanceVariable("@error_string", store_context.error_string(context)); this.setInstanceVariable("@chain", store_context.chain(context)); return result; } private static Store.VerifyCallbackFunction verifyCallback = new Store.VerifyCallbackFunction() { public int call(final StoreContext context, final Integer outcome) { int ok = outcome.intValue(); IRubyObject proc = (IRubyObject) context.getExtraData(1); if (proc == null) { proc = (IRubyObject) context.getStore().getExtraData(0); } if ( proc == null ) return ok; if ( ! proc.isNil() ) { final Ruby runtime = proc.getRuntime(); X509StoreContext store_context = X509StoreContext.newStoreContext(runtime, context); IRubyObject ret = proc.callMethod(runtime.getCurrentContext(), "call", new IRubyObject[] { runtime.newBoolean(ok != 0), store_context } ); if (ret.isTrue()) { context.setError(X509Utils.V_OK); ok = 1; } else { if (context.getError() == X509Utils.V_OK) { context.setError(X509Utils.V_ERR_CERT_REJECTED); } ok = 0; } } return ok; } }; private static RubyClass _StoreError(final Ruby runtime) { return _X509(runtime).getClass("StoreError"); } private static RaiseException newStoreError(final Ruby runtime, final String message) { return Utils.newError(runtime, _StoreError(runtime), message); } private static RaiseException newStoreError(final Ruby runtime, final String message, final Exception e) { return newStoreError(runtime, message + (e.getMessage() == null ? e.getClass().getSimpleName() : e.getMessage())); } }// X509Store