1 19 package org.netbeans.modules.subversion.client; 20 21 import java.awt.Dialog ; 22 import java.io.File ; 23 import java.io.IOException ; 24 import java.io.InputStream ; 25 import java.io.OutputStream ; 26 import java.io.UnsupportedEncodingException ; 27 import java.net.Socket ; 28 29 import java.security.KeyManagementException ; 30 import java.security.MessageDigest ; 31 import java.security.NoSuchAlgorithmException ; 32 import java.security.cert.CertificateEncodingException ; 33 import java.security.cert.CertificateExpiredException ; 34 import java.security.cert.CertificateNotYetValidException ; 35 import java.security.cert.X509Certificate ; 36 import java.util.ArrayList ; 37 import java.util.List ; 38 import javax.net.ssl.SSLContext; 39 import javax.net.ssl.SSLPeerUnverifiedException; 40 import javax.net.ssl.SSLSocket; 41 import javax.net.ssl.SSLSocketFactory; 42 import javax.net.ssl.TrustManager; 43 import javax.net.ssl.X509TrustManager; 44 45 import javax.swing.JButton ; 46 import org.netbeans.modules.proxy.ConnectivitySettings; 47 import org.netbeans.modules.subversion.Diagnostics; 48 import org.netbeans.modules.subversion.SvnModuleConfig; 49 import org.netbeans.modules.subversion.config.CertificateFile; 50 import org.netbeans.modules.subversion.config.SvnConfigFiles; 51 import org.netbeans.modules.subversion.ui.repository.Repository; 52 import org.netbeans.modules.subversion.ui.repository.RepositoryConnection; 53 import org.netbeans.modules.subversion.util.FileUtils; 54 import org.netbeans.modules.subversion.util.SvnUtils; 55 import org.openide.DialogDescriptor; 56 import org.openide.DialogDisplayer; 57 import org.openide.ErrorManager; 58 import org.openide.util.HelpCtx; 59 import org.openide.util.NbBundle; 60 import org.tigris.subversion.svnclientadapter.ISVNClientAdapter; 61 import org.tigris.subversion.svnclientadapter.SVNClientException; 62 import org.tigris.subversion.svnclientadapter.SVNUrl; 63 64 68 public class SvnClientExceptionHandler extends ExceptionHandler { 69 70 private final ISVNClientAdapter adapter; 71 private final SvnClient client; 72 private static final String NEWLINE = System.getProperty("line.separator"); private final String CHARSET_NAME = "ASCII7"; private final int handledExceptions; 75 76 private class CertificateFailure { 77 int mask; 78 String error; 79 String message; 80 CertificateFailure(int mask, String error, String message) { 81 this.mask = mask; 82 this.error = error; 83 this.message = message; 84 } 85 }; 86 87 private CertificateFailure[] failures = new CertificateFailure[] { 88 new CertificateFailure (1, "certificate is not yet valid" , NbBundle.getMessage(SvnClientExceptionHandler.class, "MSG_CertFailureNotYetValid")), new CertificateFailure (2, "certificate has expired" , NbBundle.getMessage(SvnClientExceptionHandler.class, "MSG_CertFailureHasExpired")), new CertificateFailure (4, "certificate issued for a different hostname" , NbBundle.getMessage(SvnClientExceptionHandler.class, "MSG_CertFailureWrongHostname")), new CertificateFailure (8, "issuer is not trusted" , NbBundle.getMessage(SvnClientExceptionHandler.class, "MSG_CertFailureNotTrusted")) }; 93 94 public SvnClientExceptionHandler(SVNClientException exception, ISVNClientAdapter adapter, SvnClient client, int handledExceptions) { 95 super(exception); 96 this.adapter = adapter; 97 this.client = client; 98 this.handledExceptions = handledExceptions; 99 } 100 101 public boolean handleException() throws Exception { 102 int exceptionMask = getExceptionMask(); 103 if(exceptionMask != EX_UNKNOWN) { 104 if( (handledExceptions & exceptionMask & EX_NO_HOST_CONNECTION) == exceptionMask) { 105 return handleRepositoryConnectError(); 106 } if( (handledExceptions & exceptionMask & EX_NO_CERTIFICATE) == exceptionMask) { 107 return handleNoCertificateError(); 108 } if( (handledExceptions & exceptionMask & EX_AUTHENTICATION) == exceptionMask) { 109 return handleRepositoryConnectError(); 110 } 111 } 112 throw getException(); 113 } 114 115 private boolean handleRepositoryConnectError() { 116 SVNUrl url = client.getSvnUrl(); 117 String title = org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "MSG_Error_ConnectionParameters"); Repository repository = new Repository(title); 119 repository.selectUrl(url, true); 120 121 JButton retryButton = new JButton (org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "CTL_Action_Retry")); Object option = repository.show(org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "MSG_Error_AuthFailed"), new HelpCtx(this.getClass()), 124 new Object [] {retryButton, org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "CTL_Action_Cancel")}); 126 127 boolean ret = (option == retryButton); 128 if(ret) { 129 RepositoryConnection rc = repository.getSelectedRC(); 130 String username = rc.getUsername(); 131 String password = rc.getPassword(); 132 133 adapter.setUsername(username); 134 adapter.setPassword(password); 135 SvnModuleConfig.getDefault().insertRecentUrl(rc); 136 } 137 return ret; 138 } 139 140 private boolean handleNoCertificateError() throws Exception { 141 142 SVNUrl url = client.getSvnUrl(); 144 String hostString = SvnUtils.ripUserFromHost(url.getHost()); 145 String realmString = url.getProtocol() + "://" + hostString + ":" + url.getPort(); File certFile = CertificateFile.getSystemCertFile(realmString); 147 File nbCertFile = CertificateFile.getNBCertFile(realmString); 148 if( !nbCertFile.exists() && certFile.exists() ) { 149 FileUtils.copyFile(certFile, CertificateFile.getNBCertFile(realmString)); 150 return true; 151 } 152 153 TrustManager[] trust = new TrustManager[] { 155 new X509TrustManager() { 156 public X509Certificate [] getAcceptedIssuers() { return null; } 157 public void checkClientTrusted(X509Certificate [] certs, String authType) { } 158 public void checkServerTrusted(X509Certificate [] certs, String authType) { } 159 } 160 }; 161 162 SSLContext context = null; 163 try { 164 context = SSLContext.getInstance("SSL"); context.init(null, trust, null); 166 } catch (NoSuchAlgorithmException ex) { 167 ErrorManager.getDefault().notify(ex); 168 return false; 169 } catch (KeyManagementException ex) { 170 ErrorManager.getDefault().notify(ex); 171 return false; 172 } 173 174 SSLSocketFactory factory = context.getSocketFactory(); 175 SSLSocket socket = null; 176 try { 177 socket = (SSLSocket) factory.createSocket(hostString, url.getPort()); 178 socket.startHandshake(); 179 } catch (IOException ex) { 180 throw ex; 181 } 182 183 X509Certificate cert = null; 184 java.security.cert.Certificate [] serverCerts = null; 185 try { 186 serverCerts = socket.getSession().getPeerCertificates(); 187 } catch (SSLPeerUnverifiedException ex) { 188 ErrorManager.getDefault().notify(ex); 189 return false; 190 } 191 for (int i = 0; i < serverCerts.length; i++) { 192 Diagnostics.println("Cert[" + i + "] type - " + serverCerts[i].getType()); if(serverCerts[i] instanceof X509Certificate ) { 194 cert = (X509Certificate ) serverCerts[i]; 195 Diagnostics.println("Cert[" + i + "] - notAfter " + cert.getNotAfter()); Diagnostics.println("Cert[" + i + "] - notBefore " + cert.getNotBefore()); try { 198 cert.checkValidity(); 199 } catch (CertificateExpiredException ex) { 200 Diagnostics.println("Cert[" + i + "] - " + ex); continue; } catch (CertificateNotYetValidException ex) { 203 Diagnostics.println("Cert[" + i + "] - " + ex); continue; } 206 break; 207 } 208 } 209 210 AcceptCertificatePanel acceptCertificatePanel = new AcceptCertificatePanel(); 211 acceptCertificatePanel.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "CTL_Error_CertFailed")); acceptCertificatePanel.certificatePane.setText(getCertMessage(cert, hostString)); 213 DialogDescriptor dialogDescriptor = new DialogDescriptor(acceptCertificatePanel, org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "CTL_Error_CertFailed")); Dialog dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor); 215 JButton permanentlyButton = new JButton (org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "CTL_Cert_AcceptPermanently")); JButton temporarilyButton = new JButton (org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "CTL_Cert_AcceptTemp")); JButton rejectButton = new JButton (org.openide.util.NbBundle.getMessage(SvnClientExceptionHandler.class, "CTL_Cert_Reject")); dialogDescriptor.setOptions(new Object [] {permanentlyButton, temporarilyButton, rejectButton}); 219 220 showDialog(dialogDescriptor); 221 222 if(dialogDescriptor.getValue()!=permanentlyButton && dialogDescriptor.getValue()!=temporarilyButton) { 223 return false; 224 } 225 226 CertificateFile cf = null; 227 try { 228 boolean temporarily = dialogDescriptor.getValue() == temporarilyButton; 229 cf = new CertificateFile(cert, url.getProtocol() + "://" + hostString + ":" + url.getPort(), getFailuresMask(), temporarily); cf.store(); 231 } catch (CertificateEncodingException ex) { 232 ErrorManager.getDefault().notify(ex); 233 return false; 234 } catch (IOException ex) { 235 ErrorManager.getDefault().notify(ex); 236 return false; 237 } 238 239 return true; 240 } 241 242 private void showDialog(DialogDescriptor dialogDescriptor) { 243 dialogDescriptor.setModal(true); 244 dialogDescriptor.setHelpCtx(new HelpCtx(this.getClass())); 245 dialogDescriptor.setValid(false); 246 247 Dialog dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor); 248 dialog.setVisible(true); 249 } 250 251 private void connectProxy(Socket tunnel, String host, int port, String proxyHost, int proxyPort) throws IOException { 252 253 String connectString = "CONNECT "+ host + ":" + port + " HTTP/1.0\r\n" + "Connection: Keep-Alive\r\n\r\n"; 255 byte connectBytes[]; 256 try { 257 connectBytes = connectString.getBytes(CHARSET_NAME); 258 } catch (UnsupportedEncodingException ignored) { 259 connectBytes = connectString.getBytes(); 260 } 261 262 OutputStream out = tunnel.getOutputStream(); 263 out.write(connectBytes); 264 out.flush(); 265 266 byte reply[] = new byte[200]; 267 int replyLen = 0; 268 int newlinesSeen = 0; 269 boolean headerDone = false; 270 InputStream in = tunnel.getInputStream(); 271 272 while (newlinesSeen < 2) { 273 byte b = (byte) in.read(); 274 if (b < 0) { 275 throw new IOException ("Unexpected EOF from proxy"); } 277 if (b == '\n') { 278 headerDone = true; 279 ++newlinesSeen; 280 } else if (b != '\r') { 281 newlinesSeen = 0; 282 if (!headerDone && replyLen < reply.length) { 283 reply[replyLen++] = b; 284 } 285 } 286 } 287 288 String ret = ""; try { 290 ret = new String (reply, 0, replyLen, CHARSET_NAME); 291 } catch (UnsupportedEncodingException ignored) { 292 ret = new String (reply, 0, replyLen); 293 } 294 if (!isOKresponse(ret.toLowerCase())) { 295 throw new IOException ("Unable to connect through proxy " + proxyHost + ":" + proxyPort + ". Proxy returns \"" + ret + "\""); } 299 } 300 301 private boolean isOKresponse(String ret) { 302 return ret.startsWith("http/1.1 200") || ret.startsWith("http/1.0 200"); } 304 305 private String getCertMessage(X509Certificate cert, String host) { 306 CertificateFailure[] certFailures = getCertFailures(); 307 Object [] param = new Object [6]; 308 param[0] = host; 309 param[1] = cert.getNotBefore(); 310 param[2] = cert.getNotAfter(); 311 param[3] = cert.getIssuerDN().getName(); 312 param[4] = getFingerprint(cert, "SHA1"); param[5] = getFingerprint(cert, "MD5"); 315 String message = NbBundle.getMessage(SvnClientExceptionHandler.class, "MSG_BadCertificate", param); for (int i = 0; i < certFailures.length; i++) { 317 message = certFailures[i].message + message; 318 } 319 return message; 320 } 321 322 private CertificateFailure[] getCertFailures() { 323 List <CertificateFailure> ret = new ArrayList <CertificateFailure>(); 324 String exceptionMessage = getException().getMessage(); 325 for (int i = 0; i < failures.length; i++) { 326 if(exceptionMessage.indexOf(failures[i].error) > -1) { 327 ret.add(failures[i]); 328 } 329 } 330 return ret.toArray(new CertificateFailure[ret.size()]); 331 } 332 333 private int getFailuresMask() { 334 CertificateFailure[] certFailures = getCertFailures(); 335 if(certFailures.length == 0) { 336 return 15; } 338 int mask = 0; 339 for (int i = 0; i < certFailures.length; i++) { 340 mask |= certFailures[i].mask; 341 } 342 return mask; 343 } 344 345 private String getFingerprint(X509Certificate cert, String alg) { 346 String str; 347 try { 348 str = new sun.misc.BASE64Encoder().encode(cert.getEncoded()); 349 str = str.replace(NEWLINE, ""); MessageDigest md5 = MessageDigest.getInstance(alg); 351 byte[] url = new sun.misc.BASE64Decoder().decodeBuffer(str); 352 md5.update(url); 353 byte[] md5digest = md5.digest(); 354 String ret = ""; for (int i = 0; i < md5digest.length; i++) { 356 String hex = Integer.toHexString(md5digest[i] & 0x000000FF); 357 if(hex.length()==1) { 358 hex = "0" + hex; } 360 ret += hex + (i < md5digest.length - 1 ? ":" : ""); } 362 return ret; 363 } catch (CertificateEncodingException ex) { 364 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); } catch (NoSuchAlgorithmException ex) { 366 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); } catch (IOException ex) { 368 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); } 370 return ""; } 372 373 } 374 | Popular Tags |