/***** 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.x509store; import static org.jruby.ext.openssl.x509store.X509Utils.X509_FILETYPE_DEFAULT; import static org.jruby.ext.openssl.x509store.X509Utils.X509_FILETYPE_PEM; import static org.jruby.ext.openssl.x509store.X509Utils.X509_R_CERT_ALREADY_IN_HASH_TABLE; import java.io.FileNotFoundException; import java.io.IOException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.net.ssl.X509TrustManager; import org.jruby.Ruby; /** * c: X509_STORE * * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> */ public class Store implements X509TrustManager { public static interface VerifyFunction extends Function1<StoreContext> { public static final VerifyFunction EMPTY = new VerifyFunction(){ public int call(StoreContext context) { return -1; } }; } public static interface VerifyCallbackFunction extends Function2<StoreContext, Integer> { public static final VerifyCallbackFunction EMPTY = new VerifyCallbackFunction(){ public int call(StoreContext context, Integer outcome) { return -1; } }; } static interface GetIssuerFunction extends Function3<StoreContext, X509AuxCertificate[], X509AuxCertificate> { public static final GetIssuerFunction EMPTY = new GetIssuerFunction(){ public int call(StoreContext context, X509AuxCertificate[] issuer, X509AuxCertificate cert) { return -1; } }; } static interface CheckIssuedFunction extends Function3<StoreContext, X509AuxCertificate, X509AuxCertificate> { public static final CheckIssuedFunction EMPTY = new CheckIssuedFunction(){ public int call(StoreContext context, X509AuxCertificate cert, X509AuxCertificate issuer) throws Exception { return -1; } }; } static interface CheckRevocationFunction extends Function1<StoreContext> { public static final CheckRevocationFunction EMPTY = new CheckRevocationFunction(){ public int call(StoreContext context) { return -1; } }; } static interface GetCRLFunction extends Function3<StoreContext, java.security.cert.X509CRL[], X509AuxCertificate> { public static final GetCRLFunction EMPTY = new GetCRLFunction(){ public int call(StoreContext context, java.security.cert.X509CRL[] crls, X509AuxCertificate cert) { return -1; } }; } static interface CheckCRLFunction extends Function2<StoreContext, java.security.cert.X509CRL> { public static final CheckCRLFunction EMPTY = new CheckCRLFunction(){ public int call(StoreContext context, java.security.cert.X509CRL crl) { return -1; } }; } static interface CertificateCRLFunction extends Function3<StoreContext, java.security.cert.X509CRL, X509AuxCertificate> { public static final CertificateCRLFunction EMPTY = new CertificateCRLFunction(){ public int call(StoreContext context, java.security.cert.X509CRL crl, X509AuxCertificate cert) { return -1; } }; } static interface CleanupFunction extends Function1<StoreContext> { public static final CleanupFunction EMPTY = new CleanupFunction(){ public int call(StoreContext context) { return -1; } }; } // @Deprecated int cache = 1; // not-used private volatile X509Object[] objects = new X509Object[0]; private volatile Lookup[] certLookups = new Lookup[0]; public final VerifyParameter verifyParameter; VerifyFunction verify = VerifyFunction.EMPTY; VerifyCallbackFunction verifyCallback = VerifyCallbackFunction.EMPTY; GetIssuerFunction getIssuer = GetIssuerFunction.EMPTY; CheckIssuedFunction checkIssued = CheckIssuedFunction.EMPTY; CheckRevocationFunction checkRevocation = CheckRevocationFunction.EMPTY; GetCRLFunction getCRL = GetCRLFunction.EMPTY; CheckCRLFunction checkCRL = CheckCRLFunction.EMPTY; CertificateCRLFunction certificateCRL = CertificateCRLFunction.EMPTY; CleanupFunction cleanup = CleanupFunction.EMPTY; private final List<Object> extraData; /** * c: X509_STORE_new */ public Store() { verifyParameter = new VerifyParameter(); extraData = new ArrayList<Object>(10); this.extraData.add(null); this.extraData.add(null); this.extraData.add(null); this.extraData.add(null); this.extraData.add(null); this.extraData.add(null); this.extraData.add(null); this.extraData.add(null); this.extraData.add(null); } public List<X509Object> getObjects() { return Arrays.asList(objects); } public List<Lookup> getCertificateMethods() { return Arrays.asList(certLookups); } public VerifyParameter getVerifyParameter() { return verifyParameter; } public VerifyFunction getVerifyFunction() { return verify; } /** * c: X509_STORE_set_verify_func */ public void setVerifyFunction(VerifyFunction func) { verify = func; } public VerifyCallbackFunction getVerifyCallback() { return verifyCallback; } /** * c: X509_STORE_set_verify_cb_func */ public void setVerifyCallbackFunction(VerifyCallbackFunction func) { verifyCallback = func; } /** * c: X509_STORE_free */ public void free() throws Exception { for (Lookup lu : certLookups) { lu.shutdown(); lu.free(); } if (verifyParameter != null) { verifyParameter.free(); } } /** * c: X509_set_ex_data */ public int setExtraData(int idx, Object data) { synchronized (extraData) { extraData.set(idx, data); return 1; } } /** * c: X509_get_ex_data */ public Object getExtraData(int idx) { synchronized (extraData) { return extraData.get(idx); } } /** * c: X509_STORE_set_depth */ public int setDepth(int depth) { verifyParameter.setDepth(depth); return 1; } /** * c: X509_STORE_set_flags */ public int setFlags(long flags) { return verifyParameter.setFlags(flags); } /** * c: X509_STORE_set_purpose */ public int setPurpose(int purpose) { return verifyParameter.setPurpose(purpose); } /** * c: X509_STORE_set_trust */ public int setTrust(int trust) { return verifyParameter.setTrust(trust); } /** * c: X509_STORE_set1_param */ public int setParam(VerifyParameter pm) { return verifyParameter.set(verifyParameter); } /** * c: X509_STORE_add_lookup */ public Lookup addLookup(final Ruby runtime, final LookupMethod method) { final Lookup[] certLookups = this.certLookups; Lookup foundLookup = findLookupMethod(certLookups, method); if ( foundLookup != null ) return foundLookup; final Lookup newLookup = new Lookup(runtime, method); newLookup.store = this; synchronized (this) { final int length = this.certLookups.length; if ( certLookups.length != length ) { foundLookup = findLookupMethod(this.certLookups, method); if ( foundLookup != null ) return foundLookup; } Lookup[] newCertLookups = Arrays.copyOf(this.certLookups, length + 1); newCertLookups[length] = newLookup; this.certLookups = newCertLookups; } return newLookup; } private static Lookup findLookupMethod(final Lookup[] lookups, final LookupMethod method) { for ( final Lookup lookup : lookups ) { if ( lookup.method.equals(method) ) return lookup; } return null; } /** * c: X509_STORE_add_cert */ public int addCertificate(final X509Certificate cert) { if ( cert == null ) return 0; final Certificate certObj = new Certificate(StoreContext.ensureAux(cert)); final X509Object[] objects = this.objects; if ( matchedObject(objects, certObj) ) { X509Error.addError(X509_R_CERT_ALREADY_IN_HASH_TABLE); return 0; } return addObject(certObj, objects.length); } /** * c: X509_STORE_add_crl */ public int addCRL(final java.security.cert.CRL crl) { if ( crl == null ) return 0; final CRL crlObj = new CRL(); crlObj.crl = crl; final X509Object[] objects = this.objects; if ( matchedObject(objects, crlObj) ) { X509Error.addError(X509_R_CERT_ALREADY_IN_HASH_TABLE); return 0; } return addObject(crlObj, objects.length); } private static boolean matchedObject(final X509Object[] objects, final X509Object xObject) { for ( int i = 0; i< objects.length; i++ ) { if ( objects[i].matches(xObject) ) return true; } return false; } private synchronized int addObject(final X509Object xObject, final int prevLength) { final int length = objects.length; if ( length != prevLength ) { // something added concurrently if ( matchedObject(objects, xObject) ) { X509Error.addError(X509_R_CERT_ALREADY_IN_HASH_TABLE); return 0; } } X509Object[] newObjects = Arrays.copyOf(objects, length + 1); newObjects[ length ] = xObject; objects = newObjects; return 1; } /** * c: X509_STORE_load_locations */ public int loadLocations(Ruby runtime, final String file, final String path) throws Exception { if ( file != null ) { final Lookup lookup = addLookup( runtime, Lookup.fileLookup() ); if ( lookup == null ) { return 0; } if ( lookup.loadFile(new CertificateFile.Path(file, X509_FILETYPE_PEM)) != 1 ) { return 0; } } if ( path != null ) { final Lookup lookup = addLookup( runtime, Lookup.hashDirLookup() ); if ( lookup == null ) { return 0; } if ( lookup.addDir(new CertificateHashDir.Dir(path, X509_FILETYPE_PEM)) != 1 ) { return 0; } } if ( path == null && file == null ) return 0; return 1; } /** * c: X509_STORE_set_default_paths */ public int setDefaultPaths(Ruby runtime) throws Exception { Lookup lookup = addLookup(runtime, Lookup.fileLookup()); //if ( lookup == null ) return 0; try { lookup.loadFile(new CertificateFile.Path(null, X509_FILETYPE_DEFAULT)); } catch (FileNotFoundException e) { // set_default_paths ignores FileNotFound } catch (IOException e) { // this is for older jrubies as they do not have a // org.jruby.util.ResourceException.NotFound if (!e.getClass().getSimpleName().equals("NotFound")) { throw e; } // set_default_paths ignores FileNotFound } lookup = addLookup(runtime, Lookup.hashDirLookup()); //if ( lookup == null ) return 0; try { lookup.addDir(new CertificateHashDir.Dir(null, X509_FILETYPE_DEFAULT)); } catch (FileNotFoundException e) { // set_default_paths ignores FileNotFound } catch (IOException e) { if (!e.getClass().getSimpleName().equals("NotFound")) { throw e; } // set_default_paths ignores FileNotFound } X509Error.clearErrors(); return 1; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { final X509Object[] objects = this.objects; final ArrayList<X509Certificate> issuers = new ArrayList<X509Certificate>(objects.length); for ( int i = 0; i< objects.length; i++ ) { final X509Object object = objects[i]; if ( object instanceof Certificate ) { issuers.add( ( (Certificate) object ).x509 ); } } return issuers.toArray( new X509Certificate[ issuers.size() ] ); } }// X509_STORE