blob: 7d76a1fa0536b3c42d3f388192ce3cb0e3511952 [file] [log] [blame]
/*
* 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);
}
}
}