| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.idea.svn.auth; |
| |
| import com.intellij.openapi.util.text.StringUtil; |
| import org.intellij.lang.annotations.MagicConstant; |
| import org.jetbrains.annotations.NotNull; |
| import org.tmatesoft.svn.core.*; |
| import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider; |
| import org.tmatesoft.svn.core.auth.SVNAuthentication; |
| import org.tmatesoft.svn.core.internal.util.SVNBase64; |
| import org.tmatesoft.svn.core.internal.util.SVNHashMap; |
| import org.tmatesoft.svn.core.internal.util.SVNSSLUtil; |
| import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; |
| import org.tmatesoft.svn.core.internal.wc.SVNWCProperties; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.security.cert.*; |
| |
| /** |
| * @author Konstantin Kolosovsky. |
| */ // plus seems that we also should ask for credentials; but we didn't receive realm name yet |
| class SSLServerCertificateAuthenticator extends AbstractAuthenticator { |
| private String myCertificateRealm; |
| private String myCredentialsRealm; |
| private Object myCertificate; |
| private int myResult; |
| private SVNAuthentication myAuthentication; |
| |
| SSLServerCertificateAuthenticator(@NotNull AuthenticationService authenticationService, @NotNull SVNURL url, String realm) { |
| super(authenticationService, url, realm); |
| } |
| |
| @Override |
| public boolean tryAuthenticate() { |
| myResult = ISVNAuthenticationProvider.ACCEPTED_TEMPORARY; |
| myStoreInUsual = false; |
| return super.tryAuthenticate(); |
| } |
| |
| @Override |
| protected boolean getWithPassive(SvnAuthenticationManager passive) throws SVNException { |
| String stored = (String)passive.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm); |
| if (stored == null) return false; |
| |
| myCertificate = createCertificate(stored); |
| myCertificateRealm = myRealm; |
| return true; |
| } |
| |
| @Override |
| public void requestClientAuthentication(SVNURL url, String realm, SVNAuthentication authentication) { |
| if (!myUrl.equals(url)) return; |
| myCredentialsRealm = realm; |
| myAuthentication = authentication; |
| if (myAuthentication != null) { |
| myStoreInUsual &= myAuthentication.isStorageAllowed(); |
| } |
| } |
| |
| @Override |
| public void acceptServerAuthentication(SVNURL url, String realm, Object certificate, @MagicConstant int acceptResult) { |
| if (!myUrl.equals(url)) return; |
| myCertificateRealm = realm; |
| myCertificate = certificate; |
| myResult = acceptResult; |
| } |
| |
| @Override |
| protected boolean afterAuthCall() { |
| myStoreInUsual &= myCertificate != null && ISVNAuthenticationProvider.ACCEPTED == myResult; |
| // TODO: Previous code always returned not null value, so Boolean == null check was always false in tryAuthenticate(). |
| // TODO: This was most likely error in code - check once again. |
| return ISVNAuthenticationProvider.REJECTED != myResult && myCertificate != null; |
| } |
| |
| @Override |
| protected boolean acknowledge(SvnAuthenticationManager manager) throws SVNException { |
| // we should store certificate, if it wasn't accepted (if temporally tmp) |
| if (myCertificate == null) { // this is if certificate was stored only in passive area |
| String stored = (String)manager.getRuntimeAuthStorage().getData("svn.ssl.server", myRealm); |
| if (StringUtil.isEmptyOrSpaces(stored)) { |
| throw new SVNException( |
| SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "No stored server certificate was found in runtime")); |
| } |
| myCertificate = createCertificate(stored); |
| myCertificateRealm = myRealm; |
| } |
| if (myAuthenticationService.getTempDirectory() != null && myCertificate != null) { |
| storeServerCertificate(); |
| |
| if (myAuthentication != null) { |
| final String realm = myCredentialsRealm == null ? myCertificateRealm : myCredentialsRealm; |
| return storeCredentials(manager, myAuthentication, realm); |
| } |
| } |
| return true; |
| } |
| |
| @NotNull |
| private Certificate createCertificate(@NotNull String stored) throws SVNException { |
| CertificateFactory factory; |
| try { |
| factory = CertificateFactory.getInstance("X509"); |
| final byte[] buffer = new byte[stored.length()]; |
| SVNBase64.base64ToByteArray(new StringBuffer(stored), buffer); |
| |
| return factory.generateCertificate(new ByteArrayInputStream(buffer)); |
| } |
| catch (CertificateException e) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, e)); |
| } |
| } |
| |
| private void storeServerCertificate() throws SVNException { |
| if (!(myCertificate instanceof X509Certificate)) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can not store server certificate: " + myCertificate)); |
| } |
| X509Certificate x509Certificate = (X509Certificate)myCertificate; |
| String stored; |
| try { |
| stored = SVNBase64.byteArrayToBase64(x509Certificate.getEncoded()); |
| } |
| catch (CertificateEncodingException e) { |
| throw new SVNException(SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e)); |
| } |
| |
| int failures = SVNSSLUtil.getServerCertificateFailures(x509Certificate, myUrl.getHost()); |
| storeServerCertificate(myAuthenticationService.getTempDirectory(), myCertificateRealm, stored, failures); |
| } |
| |
| private void storeServerCertificate(final File configDir, String realm, String data, int failures) throws SVNException { |
| //noinspection ResultOfMethodCallIgnored |
| configDir.mkdirs(); |
| |
| File file = new File(configDir, "auth/svn.ssl.server/" + SVNFileUtil.computeChecksum(realm)); |
| SVNHashMap map = new SVNHashMap(); |
| map.put("ascii_cert", data); |
| map.put("svn:realmstring", realm); |
| map.put("failures", Integer.toString(failures)); |
| |
| SVNFileUtil.deleteFile(file); |
| |
| File tmpFile = SVNFileUtil.createUniqueFile(configDir, "auth", ".tmp", true); |
| try { |
| SVNWCProperties.setProperties(SVNProperties.wrap(map), file, tmpFile, SVNWCProperties.SVN_HASH_TERMINATOR); |
| } |
| finally { |
| SVNFileUtil.deleteFile(tmpFile); |
| } |
| } |
| } |