From 635c0bcfcce571d6ada1d368cb7abf568810aaed Mon Sep 17 00:00:00 2001 From: Wiedemann Matthias Date: Mon, 11 May 2020 08:14:22 +0200 Subject: [PATCH] initial code from sourceforge --- ChangeLog | 1061 ++++++ LICENSE.txt | 30 + README | 229 ++ build.bat | 17 + build.sh | 25 + build.xml | 97 + examples/AES.java | 149 + examples/ChangePassphrase.java | 73 + examples/Compression.java | 149 + examples/Daemon.java | 175 + examples/Exec.java | 187 + examples/KeyGen.java | 82 + examples/KnownHosts.java | 182 + examples/Logger.java | 160 + examples/OpenSSHConfig.java | 137 + examples/PortForwardingL.java | 155 + examples/PortForwardingR.java | 156 + examples/README | 182 + examples/ScpFrom.java | 249 ++ examples/ScpTo.java | 232 ++ examples/ScpToNoneCipher.java | 218 ++ examples/Sftp.java | 547 +++ examples/Shell.java | 123 + examples/StreamForwarding.java | 155 + examples/Subsystem.java | 164 + examples/Sudo.java | 184 + examples/UserAuthKI.java | 152 + examples/UserAuthPubKey.java | 154 + examples/ViaHTTP.java | 155 + examples/ViaSOCKS5.java | 154 + examples/X11Forwarding.java | 158 + jsch.iml | 14 + pom.xml | 113 + src/main/java/com/jcraft/jsch/Buffer.java | 297 ++ src/main/java/com/jcraft/jsch/Channel.java | 772 +++++ .../jcraft/jsch/ChannelAgentForwarding.java | 266 ++ .../com/jcraft/jsch/ChannelDirectTCPIP.java | 170 + .../java/com/jcraft/jsch/ChannelExec.java | 83 + .../jcraft/jsch/ChannelForwardedTCPIP.java | 331 ++ .../java/com/jcraft/jsch/ChannelSession.java | 276 ++ .../java/com/jcraft/jsch/ChannelSftp.java | 3055 +++++++++++++++++ .../java/com/jcraft/jsch/ChannelShell.java | 70 + .../com/jcraft/jsch/ChannelSubsystem.java | 83 + src/main/java/com/jcraft/jsch/ChannelX11.java | 273 ++ src/main/java/com/jcraft/jsch/Cipher.java | 40 + src/main/java/com/jcraft/jsch/CipherNone.java | 42 + .../java/com/jcraft/jsch/Compression.java | 38 + .../com/jcraft/jsch/ConfigRepository.java | 55 + src/main/java/com/jcraft/jsch/DH.java | 43 + src/main/java/com/jcraft/jsch/DHEC256.java | 37 + src/main/java/com/jcraft/jsch/DHEC384.java | 37 + src/main/java/com/jcraft/jsch/DHEC521.java | 37 + src/main/java/com/jcraft/jsch/DHECN.java | 187 + src/main/java/com/jcraft/jsch/DHG1.java | 196 ++ src/main/java/com/jcraft/jsch/DHG14.java | 215 ++ src/main/java/com/jcraft/jsch/DHGEX.java | 245 ++ src/main/java/com/jcraft/jsch/DHGEX256.java | 36 + src/main/java/com/jcraft/jsch/ECDH.java | 37 + .../com/jcraft/jsch/ForwardedTCPIPDaemon.java | 36 + src/main/java/com/jcraft/jsch/GSSContext.java | 38 + src/main/java/com/jcraft/jsch/HASH.java | 37 + src/main/java/com/jcraft/jsch/HostKey.java | 141 + .../com/jcraft/jsch/HostKeyRepository.java | 94 + src/main/java/com/jcraft/jsch/IO.java | 132 + src/main/java/com/jcraft/jsch/Identity.java | 83 + .../java/com/jcraft/jsch/IdentityFile.java | 136 + .../com/jcraft/jsch/IdentityRepository.java | 115 + src/main/java/com/jcraft/jsch/JSch.java | 590 ++++ .../jcraft/jsch/JSchAuthCancelException.java | 45 + .../java/com/jcraft/jsch/JSchException.java | 48 + .../jcraft/jsch/JSchPartialAuthException.java | 45 + .../java/com/jcraft/jsch/KeyExchange.java | 325 ++ src/main/java/com/jcraft/jsch/KeyPair.java | 1255 +++++++ src/main/java/com/jcraft/jsch/KeyPairDSA.java | 332 ++ .../java/com/jcraft/jsch/KeyPairECDSA.java | 408 +++ .../java/com/jcraft/jsch/KeyPairGenDSA.java | 39 + .../java/com/jcraft/jsch/KeyPairGenECDSA.java | 37 + .../java/com/jcraft/jsch/KeyPairGenRSA.java | 43 + .../java/com/jcraft/jsch/KeyPairPKCS8.java | 363 ++ src/main/java/com/jcraft/jsch/KeyPairRSA.java | 418 +++ src/main/java/com/jcraft/jsch/KnownHosts.java | 589 ++++ .../jcraft/jsch/LocalIdentityRepository.java | 151 + src/main/java/com/jcraft/jsch/Logger.java | 54 + src/main/java/com/jcraft/jsch/MAC.java | 39 + .../java/com/jcraft/jsch/OpenSSHConfig.java | 278 ++ src/main/java/com/jcraft/jsch/PBKDF.java | 34 + src/main/java/com/jcraft/jsch/Packet.java | 115 + .../java/com/jcraft/jsch/PortWatcher.java | 209 ++ src/main/java/com/jcraft/jsch/Proxy.java | 40 + src/main/java/com/jcraft/jsch/ProxyHTTP.java | 180 + .../java/com/jcraft/jsch/ProxySOCKS4.java | 212 ++ .../java/com/jcraft/jsch/ProxySOCKS5.java | 349 ++ src/main/java/com/jcraft/jsch/Random.java | 34 + src/main/java/com/jcraft/jsch/Request.java | 69 + .../jcraft/jsch/RequestAgentForwarding.java | 53 + src/main/java/com/jcraft/jsch/RequestEnv.java | 54 + .../java/com/jcraft/jsch/RequestExec.java | 58 + .../java/com/jcraft/jsch/RequestPtyReq.java | 78 + .../java/com/jcraft/jsch/RequestSftp.java | 49 + .../java/com/jcraft/jsch/RequestShell.java | 51 + .../java/com/jcraft/jsch/RequestSignal.java | 49 + .../com/jcraft/jsch/RequestSubsystem.java | 53 + .../com/jcraft/jsch/RequestWindowChange.java | 68 + src/main/java/com/jcraft/jsch/RequestX11.java | 63 + .../com/jcraft/jsch/ServerSocketFactory.java | 37 + src/main/java/com/jcraft/jsch/Session.java | 2840 +++++++++++++++ src/main/java/com/jcraft/jsch/SftpATTRS.java | 294 ++ .../java/com/jcraft/jsch/SftpException.java | 51 + .../com/jcraft/jsch/SftpProgressMonitor.java | 39 + .../java/com/jcraft/jsch/SftpStatVFS.java | 122 + src/main/java/com/jcraft/jsch/Signature.java | 37 + .../java/com/jcraft/jsch/SignatureDSA.java | 35 + .../java/com/jcraft/jsch/SignatureECDSA.java | 35 + .../java/com/jcraft/jsch/SignatureRSA.java | 35 + .../java/com/jcraft/jsch/SocketFactory.java | 40 + .../jcraft/jsch/UIKeyboardInteractive.java | 38 + src/main/java/com/jcraft/jsch/UserAuth.java | 53 + .../jcraft/jsch/UserAuthGSSAPIWithMIC.java | 227 ++ .../jsch/UserAuthKeyboardInteractive.java | 203 ++ .../java/com/jcraft/jsch/UserAuthNone.java | 129 + .../com/jcraft/jsch/UserAuthPassword.java | 193 ++ .../com/jcraft/jsch/UserAuthPublicKey.java | 234 ++ src/main/java/com/jcraft/jsch/UserInfo.java | 39 + src/main/java/com/jcraft/jsch/Util.java | 526 +++ .../java/com/jcraft/jsch/jce/AES128CBC.java | 75 + .../java/com/jcraft/jsch/jce/AES128CTR.java | 75 + .../java/com/jcraft/jsch/jce/AES192CBC.java | 73 + .../java/com/jcraft/jsch/jce/AES192CTR.java | 73 + .../java/com/jcraft/jsch/jce/AES256CBC.java | 73 + .../java/com/jcraft/jsch/jce/AES256CTR.java | 73 + .../java/com/jcraft/jsch/jce/ARCFOUR.java | 70 + .../java/com/jcraft/jsch/jce/ARCFOUR128.java | 73 + .../java/com/jcraft/jsch/jce/ARCFOUR256.java | 73 + .../java/com/jcraft/jsch/jce/BlowfishCBC.java | 73 + src/main/java/com/jcraft/jsch/jce/DH.java | 100 + .../java/com/jcraft/jsch/jce/ECDH256.java | 36 + .../java/com/jcraft/jsch/jce/ECDH384.java | 36 + .../java/com/jcraft/jsch/jce/ECDH521.java | 36 + src/main/java/com/jcraft/jsch/jce/ECDHN.java | 146 + src/main/java/com/jcraft/jsch/jce/HMAC.java | 82 + .../java/com/jcraft/jsch/jce/HMACMD5.java | 42 + .../java/com/jcraft/jsch/jce/HMACMD596.java | 46 + .../java/com/jcraft/jsch/jce/HMACSHA1.java | 38 + .../java/com/jcraft/jsch/jce/HMACSHA196.java | 47 + .../java/com/jcraft/jsch/jce/HMACSHA256.java | 38 + .../java/com/jcraft/jsch/jce/HMACSHA512.java | 38 + .../com/jcraft/jsch/jce/KeyPairGenDSA.java | 62 + .../com/jcraft/jsch/jce/KeyPairGenECDSA.java | 96 + .../com/jcraft/jsch/jce/KeyPairGenRSA.java | 72 + src/main/java/com/jcraft/jsch/jce/MD5.java | 51 + src/main/java/com/jcraft/jsch/jce/PBKDF.java | 59 + src/main/java/com/jcraft/jsch/jce/Random.java | 81 + src/main/java/com/jcraft/jsch/jce/SHA1.java | 51 + src/main/java/com/jcraft/jsch/jce/SHA256.java | 51 + src/main/java/com/jcraft/jsch/jce/SHA384.java | 49 + src/main/java/com/jcraft/jsch/jce/SHA512.java | 49 + .../com/jcraft/jsch/jce/SignatureDSA.java | 157 + .../jcraft/jsch/jce/SignatureECDSA256.java | 41 + .../jcraft/jsch/jce/SignatureECDSA384.java | 41 + .../jcraft/jsch/jce/SignatureECDSA521.java | 41 + .../com/jcraft/jsch/jce/SignatureECDSAN.java | 192 ++ .../com/jcraft/jsch/jce/SignatureRSA.java | 82 + .../com/jcraft/jsch/jce/TripleDESCBC.java | 86 + .../com/jcraft/jsch/jce/TripleDESCTR.java | 86 + .../com/jcraft/jsch/jcraft/Compression.java | 140 + .../java/com/jcraft/jsch/jcraft/HMAC.java | 108 + .../java/com/jcraft/jsch/jcraft/HMACMD5.java | 51 + .../com/jcraft/jsch/jcraft/HMACMD596.java | 50 + .../java/com/jcraft/jsch/jcraft/HMACSHA1.java | 51 + .../com/jcraft/jsch/jcraft/HMACSHA196.java | 50 + .../com/jcraft/jsch/jgss/GSSContextKrb5.java | 177 + tools/bin/ant | 134 + tools/bin/ant.bat | 110 + tools/bin/antRun | 9 + tools/bin/antRun.bat | 20 + tools/bin/lcp.bat | 9 + tools/bin/runant.pl | 131 + tools/bin/runant.py | 96 + 178 files changed, 29734 insertions(+) create mode 100644 ChangeLog create mode 100644 LICENSE.txt create mode 100644 README create mode 100644 build.bat create mode 100644 build.sh create mode 100644 build.xml create mode 100644 examples/AES.java create mode 100644 examples/ChangePassphrase.java create mode 100644 examples/Compression.java create mode 100644 examples/Daemon.java create mode 100644 examples/Exec.java create mode 100644 examples/KeyGen.java create mode 100644 examples/KnownHosts.java create mode 100644 examples/Logger.java create mode 100644 examples/OpenSSHConfig.java create mode 100644 examples/PortForwardingL.java create mode 100644 examples/PortForwardingR.java create mode 100644 examples/README create mode 100644 examples/ScpFrom.java create mode 100644 examples/ScpTo.java create mode 100644 examples/ScpToNoneCipher.java create mode 100644 examples/Sftp.java create mode 100644 examples/Shell.java create mode 100644 examples/StreamForwarding.java create mode 100644 examples/Subsystem.java create mode 100644 examples/Sudo.java create mode 100644 examples/UserAuthKI.java create mode 100644 examples/UserAuthPubKey.java create mode 100644 examples/ViaHTTP.java create mode 100644 examples/ViaSOCKS5.java create mode 100644 examples/X11Forwarding.java create mode 100644 jsch.iml create mode 100644 pom.xml create mode 100644 src/main/java/com/jcraft/jsch/Buffer.java create mode 100644 src/main/java/com/jcraft/jsch/Channel.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelAgentForwarding.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelExec.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelSession.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelSftp.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelShell.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelSubsystem.java create mode 100644 src/main/java/com/jcraft/jsch/ChannelX11.java create mode 100644 src/main/java/com/jcraft/jsch/Cipher.java create mode 100644 src/main/java/com/jcraft/jsch/CipherNone.java create mode 100644 src/main/java/com/jcraft/jsch/Compression.java create mode 100644 src/main/java/com/jcraft/jsch/ConfigRepository.java create mode 100644 src/main/java/com/jcraft/jsch/DH.java create mode 100644 src/main/java/com/jcraft/jsch/DHEC256.java create mode 100644 src/main/java/com/jcraft/jsch/DHEC384.java create mode 100644 src/main/java/com/jcraft/jsch/DHEC521.java create mode 100644 src/main/java/com/jcraft/jsch/DHECN.java create mode 100644 src/main/java/com/jcraft/jsch/DHG1.java create mode 100644 src/main/java/com/jcraft/jsch/DHG14.java create mode 100644 src/main/java/com/jcraft/jsch/DHGEX.java create mode 100644 src/main/java/com/jcraft/jsch/DHGEX256.java create mode 100644 src/main/java/com/jcraft/jsch/ECDH.java create mode 100644 src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java create mode 100644 src/main/java/com/jcraft/jsch/GSSContext.java create mode 100644 src/main/java/com/jcraft/jsch/HASH.java create mode 100644 src/main/java/com/jcraft/jsch/HostKey.java create mode 100644 src/main/java/com/jcraft/jsch/HostKeyRepository.java create mode 100644 src/main/java/com/jcraft/jsch/IO.java create mode 100644 src/main/java/com/jcraft/jsch/Identity.java create mode 100644 src/main/java/com/jcraft/jsch/IdentityFile.java create mode 100644 src/main/java/com/jcraft/jsch/IdentityRepository.java create mode 100644 src/main/java/com/jcraft/jsch/JSch.java create mode 100644 src/main/java/com/jcraft/jsch/JSchAuthCancelException.java create mode 100644 src/main/java/com/jcraft/jsch/JSchException.java create mode 100644 src/main/java/com/jcraft/jsch/JSchPartialAuthException.java create mode 100644 src/main/java/com/jcraft/jsch/KeyExchange.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPair.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPairDSA.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPairECDSA.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPairGenDSA.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPairGenRSA.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPairPKCS8.java create mode 100644 src/main/java/com/jcraft/jsch/KeyPairRSA.java create mode 100644 src/main/java/com/jcraft/jsch/KnownHosts.java create mode 100644 src/main/java/com/jcraft/jsch/LocalIdentityRepository.java create mode 100644 src/main/java/com/jcraft/jsch/Logger.java create mode 100644 src/main/java/com/jcraft/jsch/MAC.java create mode 100644 src/main/java/com/jcraft/jsch/OpenSSHConfig.java create mode 100644 src/main/java/com/jcraft/jsch/PBKDF.java create mode 100644 src/main/java/com/jcraft/jsch/Packet.java create mode 100644 src/main/java/com/jcraft/jsch/PortWatcher.java create mode 100644 src/main/java/com/jcraft/jsch/Proxy.java create mode 100644 src/main/java/com/jcraft/jsch/ProxyHTTP.java create mode 100644 src/main/java/com/jcraft/jsch/ProxySOCKS4.java create mode 100644 src/main/java/com/jcraft/jsch/ProxySOCKS5.java create mode 100644 src/main/java/com/jcraft/jsch/Random.java create mode 100644 src/main/java/com/jcraft/jsch/Request.java create mode 100644 src/main/java/com/jcraft/jsch/RequestAgentForwarding.java create mode 100644 src/main/java/com/jcraft/jsch/RequestEnv.java create mode 100644 src/main/java/com/jcraft/jsch/RequestExec.java create mode 100644 src/main/java/com/jcraft/jsch/RequestPtyReq.java create mode 100644 src/main/java/com/jcraft/jsch/RequestSftp.java create mode 100644 src/main/java/com/jcraft/jsch/RequestShell.java create mode 100644 src/main/java/com/jcraft/jsch/RequestSignal.java create mode 100644 src/main/java/com/jcraft/jsch/RequestSubsystem.java create mode 100644 src/main/java/com/jcraft/jsch/RequestWindowChange.java create mode 100644 src/main/java/com/jcraft/jsch/RequestX11.java create mode 100644 src/main/java/com/jcraft/jsch/ServerSocketFactory.java create mode 100644 src/main/java/com/jcraft/jsch/Session.java create mode 100644 src/main/java/com/jcraft/jsch/SftpATTRS.java create mode 100644 src/main/java/com/jcraft/jsch/SftpException.java create mode 100644 src/main/java/com/jcraft/jsch/SftpProgressMonitor.java create mode 100644 src/main/java/com/jcraft/jsch/SftpStatVFS.java create mode 100644 src/main/java/com/jcraft/jsch/Signature.java create mode 100644 src/main/java/com/jcraft/jsch/SignatureDSA.java create mode 100644 src/main/java/com/jcraft/jsch/SignatureECDSA.java create mode 100644 src/main/java/com/jcraft/jsch/SignatureRSA.java create mode 100644 src/main/java/com/jcraft/jsch/SocketFactory.java create mode 100644 src/main/java/com/jcraft/jsch/UIKeyboardInteractive.java create mode 100644 src/main/java/com/jcraft/jsch/UserAuth.java create mode 100644 src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java create mode 100644 src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java create mode 100644 src/main/java/com/jcraft/jsch/UserAuthNone.java create mode 100644 src/main/java/com/jcraft/jsch/UserAuthPassword.java create mode 100644 src/main/java/com/jcraft/jsch/UserAuthPublicKey.java create mode 100644 src/main/java/com/jcraft/jsch/UserInfo.java create mode 100644 src/main/java/com/jcraft/jsch/Util.java create mode 100644 src/main/java/com/jcraft/jsch/jce/AES128CBC.java create mode 100644 src/main/java/com/jcraft/jsch/jce/AES128CTR.java create mode 100644 src/main/java/com/jcraft/jsch/jce/AES192CBC.java create mode 100644 src/main/java/com/jcraft/jsch/jce/AES192CTR.java create mode 100644 src/main/java/com/jcraft/jsch/jce/AES256CBC.java create mode 100644 src/main/java/com/jcraft/jsch/jce/AES256CTR.java create mode 100644 src/main/java/com/jcraft/jsch/jce/ARCFOUR.java create mode 100644 src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java create mode 100644 src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java create mode 100644 src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java create mode 100644 src/main/java/com/jcraft/jsch/jce/DH.java create mode 100644 src/main/java/com/jcraft/jsch/jce/ECDH256.java create mode 100644 src/main/java/com/jcraft/jsch/jce/ECDH384.java create mode 100644 src/main/java/com/jcraft/jsch/jce/ECDH521.java create mode 100644 src/main/java/com/jcraft/jsch/jce/ECDHN.java create mode 100644 src/main/java/com/jcraft/jsch/jce/HMAC.java create mode 100644 src/main/java/com/jcraft/jsch/jce/HMACMD5.java create mode 100644 src/main/java/com/jcraft/jsch/jce/HMACMD596.java create mode 100644 src/main/java/com/jcraft/jsch/jce/HMACSHA1.java create mode 100644 src/main/java/com/jcraft/jsch/jce/HMACSHA196.java create mode 100644 src/main/java/com/jcraft/jsch/jce/HMACSHA256.java create mode 100644 src/main/java/com/jcraft/jsch/jce/HMACSHA512.java create mode 100644 src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java create mode 100644 src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java create mode 100644 src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java create mode 100644 src/main/java/com/jcraft/jsch/jce/MD5.java create mode 100644 src/main/java/com/jcraft/jsch/jce/PBKDF.java create mode 100644 src/main/java/com/jcraft/jsch/jce/Random.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SHA1.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SHA256.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SHA384.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SHA512.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SignatureDSA.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SignatureECDSA256.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SignatureECDSA384.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SignatureECDSA521.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java create mode 100644 src/main/java/com/jcraft/jsch/jce/SignatureRSA.java create mode 100644 src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java create mode 100644 src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java create mode 100644 src/main/java/com/jcraft/jsch/jcraft/Compression.java create mode 100644 src/main/java/com/jcraft/jsch/jcraft/HMAC.java create mode 100644 src/main/java/com/jcraft/jsch/jcraft/HMACMD5.java create mode 100644 src/main/java/com/jcraft/jsch/jcraft/HMACMD596.java create mode 100644 src/main/java/com/jcraft/jsch/jcraft/HMACSHA1.java create mode 100644 src/main/java/com/jcraft/jsch/jcraft/HMACSHA196.java create mode 100644 src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java create mode 100644 tools/bin/ant create mode 100644 tools/bin/ant.bat create mode 100644 tools/bin/antRun create mode 100644 tools/bin/antRun.bat create mode 100644 tools/bin/lcp.bat create mode 100644 tools/bin/runant.pl create mode 100644 tools/bin/runant.py diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..1d0baa8d --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1061 @@ +ChangeLog of JSch +==================================================================== +Last modified: Thu Nov 22 01:06:27 UTC 2018 + + +Changes since version 0.1.54: +- bugfix: fixed vulnerabilities in examples; + ScpTo.java, ScpFrom.java and ScpNoneCipher.java, + https://gist.github.com/ymnk/2318108/revisions#diff-a5ec82fe8ccb2efa64aa42a5592bb137 + https://gist.github.com/ymnk/2318108/revisions#diff-c1b069ab3a670f4fd3270d0f57550007 + https://gist.github.com/ymnk/2318108/revisions#diff-a20032aa3cc9119fa627ec948b9ada46 + thanks to Dylan Katz(http://dylankatz.com). +- bugfix: OpenSSHConfig#getUser() should not overwrite the given user-name. +- bugfix: fixed 'Invalid encoding for signature' errors in ssh-dss. +- bugfix: fixed bugs in the key-exchange for ecdsa-sha2-nistp384, + ecdsa-sha2-nistp521. +- bugfix: failed to generate the key pair from private keys, + ecdsa 384 and 521. +- bugfix: failed to load the ecdsa 521 key identity from ssh-add command. +- change: updating copyright messages; 2016 -> 2018 +- feature: supporting key files on EBCDIC environment. + + +Changes since version 0.1.53: +- bugfix: fixed CVS-2016-5725 + Refer to following links, + http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2016-5725 + https://github.com/tintinweb/pub/tree/master/pocs/cve-2016-5725 + Thanks a lot for tintinweb's contributions. +- bugfix: sftp-put may send the garbage data in some rare case. +- bugfix: fixed a deadlock bug in KnownHosts#getHostKey(). +- bugfix: SftpProgressMonitor#init() was not invoked in sftp-put + by using the output-stream. +- change: KnownHosts#setKnownHosts() should accept the non-existing file. +- change: excluding the user interaction time from the timeout value. +- change: addressing SFTP slow file transfer speed with Titan FTP. +- change: updating copyright messages; 2015 -> 2016 + + +Changes since version 0.1.52: +- bugfix: the rekey initiated by the remote may crash the session. +- change: Logjam: use ecdh-sha2-nistp* if available, + ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, + diffie-hellman-group14-sha1, + diffie-hellman-group-exchange-sha256, + diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1 +- change: Logjam: diffie-hellman-group-exchange-sha256 and + diffie-hellman-group-exchange-sha1 will use 2048-bit key on + Java8's SunJCE, thanks to JDK-6521495 and JDK-7044060. +- change: key words for OpenSSH's config file should be case-insensitive. +- change: there should be the host name in "WARNING: REMOTE HOST + IDENTIFICATION HAS CHANGED" message. + + +Changes since version 0.1.51: +- bugfix: resource leak: duplicate keys in LocalIdentityRepository. +- feature: added the support for SSH ECC defined in RFC5656, + ecdsa-sha2-nistp256, + ecdsa-sha2-nistp384, + ecdsa-sha2-nistp521, + ecdh-sha2-nistp256, + ecdh-sha2-nistp384, + ecdh-sha2-nistp521 + This functionality requires Java7 or later. +- feature: added the support for server host keys in + ecdsa-sha2-nistp256, + ecdsa-sha2-nistp384, + ecdsa-sha2-nistp521 +- feature: generating key-pairs in + ecdh-sha2-nistp256, + ecdh-sha2-nistp384, + ecdh-sha2-nistp521 +- change: aes192-ctr, aes256-ctr and + diffie-hellman-group-exchange-sha256 have been enabled + by the default. +- change: key exchange methods, ecdh-sha2-nistp256, + ecdh-sha2-nistp384 and ecdh-sha2-nistp521 have been enabled + by the default. +- change: the support for host keys in ecdsa-sha2-nistp256, + ecdsa-sha2-nistp384 and ecdsa-sha2-nistp521 have been enabled + by the default. +- change: 'examples/KeyGen.java' demonstrates how to generate + ecdsa-sha2-* key-pairs. +- change: updating copyright messages; 2014 -> 2015 +- TODO: The ECC support is not functional on Java6 with BouncyCastle. + + +Changes since version 0.1.50: +- bugfix: reproducibility of "verify: false". FIXED. + Hundreds of thousands of connections had caused that exception. +- bugfix: resource leaks at the failure of making local port forwarding. FIXED. +- bugfix: NPE in connecting to the non-standard TCP port. FIXED. + This problem had appeared if a host-key does not exist in + "known_host" file. +- bugfix: TCP connection may not be dropped if error messages from + the remote are too long. FIXED. +- bugfix: SftpATTRS#getAtimeString() returns the wrong string. FIXED. +- bugfix: bytes to be added by SSH_MSG_CHANNEL_WINDOW_ADJUST must be in + unsigned integer. FIXED. +- bugfix: Util#checkTilde() should not convert a tilde in + "C:\PROGRA~1\". FIXED. +- bugfix: A long long command for ChannelExec will cause + an ArrayIndexOutOfBoundsException. FIXED. +- bugfix: ChannelSftp should not send bulk request greedily even if the remote + window has the enough space. FIXED. +- bugfix: Util.createSocket() should throw an exception with 'cause'. FIXED. +- bugfix: failed to parse .ssh/config in the EBCDIC environment. FIXED. +- bugfix: com.jcraft.jsch.jcraft.HMACSHA1(used only for MacOSX) is not + reusable. FIXED. +- bugfix: NPE caused by the delayed response for channel opening + requests. FIXED. +- bugfix: hung-up in uploading huge data to ProFTPd without the config + 'SFTPClientMatch "JSCH.*" channelWindowSize 1GB' FIXED. +- bugfix: Cipher#init() may cause an infinite loop with 100% cpu use due to + https://bugs.openjdk.java.net/browse/JDK-8028627 FIXED. +- bugfix: in some case, JSche#setKnowHosts(InputStream stream) may fail + to close the given stream. FIXED +- change: com.jcraft.jsch.jcraft.HMAC* will not be used. + It seems Java6 on Mac OS X has fixed some memory leak bug in JCE, + so there is no reason to use c.j.j.j.HMAC* introduced at 0.1.30. +- change: updating copyright messages; 2013 -> 2014 +- change: allowed to create the symbolic/hard link to the relative path by + ChannelSftp#symlink(String oldpath, String newpath) + ChannelSftp#hardlink(String oldpath, String newpath) +- change: the availability of ciphers listed in "CheckCiphers" config will + not be checked if they are not used. +- change: Util#fromBase64() will throw JSchException in stead of + RuntimeException, if the given string is not in base64 format. +- feature: added the support for private keys in PKCS#8 format. +- feature: introduced the interface com.jcraft.jsch.PBKDF to abstract + the implementation of Password-Based Key Derivation Function, + and added its implementation com.jcraft.jsch.jce.PBKDF by using JCE. + + +Changes since version 0.1.49: +- bugfix: "verify: false" error on Java7u6(and later). FIXED. + http://stackoverflow.com/questions/12279836/ssh-using-jschexception-verify-false-sometimes-fails + https://issues.apache.org/jira/browse/IVY-1374 +- bugfix: Session#setPortForwardingL(String bind_address, + int lport, String host, int rport) + will not work for the long host name. FIXED. +- change: changed JSch#getIdentityRepository() to be public. +- feature: added the following method to choose a canceled remote + port-forwarding with the specified bind address, + Session#delPortForwardingR(String bind_address, int rport) +- feature: added support for following OpenSSH's sftp extensions, + posix-rename@openssh.com, + statvfs@openssh.com, + hardlink@openssh.com, + and some methods and a class to use those functionalities, + ChannelSftp#hardlink(String oldpath, String newpath), + ChannelSftp#statVFS(String path) + SftpStatVFS +- feature: added support for OpenSSH's configuration file, + JSch#setConfigRepository(ConfigRepository configRepository) + JSch#getConfigRepository() + OpenSSHConfig class + Session#getSession(String host) + and added an example to demonstrate how to use it, + examples/OpenSSHConfig.java + OpenSSHConfig class will recognize the following keywords, + Host + User + Hostname + Port + PreferredAuthentications + IdentityFile + NumberOfPasswordPrompts + ConnectTimeout + HostKeyAlias + UserKnownHostsFile + KexAlgorithms + HostKeyAlgorithms + Ciphers + Macs + Compression + CompressionLevel + ForwardAgent + RequestTTY + ServerAliveInterval + LocalForward + RemoteForward + ClearAllForwardings +- feature: added support for "diffie-hellman-group-exchange-sha256" +- feature: allowed to use tilde(~) in the file name, + JSch#setIdentity(String prvkey, String pubkey) + JSch#setKnownHosts(String prvkey, String pubkey) +- feature: added support for known_hosts file, which may include + markers(@revoke) and comments. + HostKey(String host, int type, byte[] key, String comment) + HostKey(String marker, String host, int type, + byte[] key, String comment) + HostKey#getComment() + HostKey#getMarker() +- feature: added following methods to KeyPar class, + writePrivateKey(java.io.OutputStream out, byte[] passphrase) + writePrivateKey(String name, byte[] passphrase) +- feature: allowed to set the connection timeout for the local port-forwarding, + and added following methods, + Session#setPortForwardingL(String bind_address, + int lport, String host, int rport, + ServerSocketFactory ssf, + int connectTimeout) + ChannelDirectTCPIP#connect(int connectTimeout) +- feature: added the following method to Session class + getStreamForwarder(String host, int port) + and updated example/StreamForwarding.java to use that method. +- feature: added following methods to Session class, + setPortForwardingL(String conf) + setPortForwardingR(String conf) +- feature: allowed to have the session local HostkeyRepository, + Session#setHostKeyRepository(HostKeyRepository hostkeyRepository) + Session#getHostKeyRepository() +- feature: added support for OpenSSH's local extension, + "no-more-sessions@openssh.com" and the method, + Session#noMoreSessionChannels() + + +Changes since version 0.1.48: +- bugfix: Some sftp servers will sometimes fail to handle bulk requests, + and whenever detecting such failures, we should re-send + requests again and again. FIXED +- bugfix: KeyPair#getFingerPrint() should return a fingerprint instead + of keysize + " " + fingerprint. FIXED +- bugfix: KeyPair#getKeySize() should return its key size. FIXED +- bugfix: SftpATTRS#isDir() should return false for unix domain + socket files. FIXED +- change: improved the heuristics for the password prompt in + the keyboard-interactive authentication. It may not be + started with "password:". +- change: ChannelSftp#put(InputStream src, String dst) will not check + if dst is directory or not, and if an exception is thrown, + the check will be done. +- change: if the compression is enabled without jzlib.jar, + an exception will be thrown. +- feature: JSch#addIdentity() and KeyPair#load() methods will accept + Putty's private key files. + Note that Putty encrypts its private key with "aes256-cbc". + So, to handle such key files, "Java Cryptography + Extension (JCE) Unlimited Strength Jurisdiction Policy Files" + must be installed. +- feature: hmac-sha2-256 defined in RFC6668 is supported. +- feature: added following methods to KeyPair class, + byte[] getSignature(byte[] data) + Signature getVerifier() + byte[] forSSHAgent() + void setPublicKeyComment(String comment) +- feature: added following methods to SftpATTR class, + boolean isChr() + boolean isBlk() + boolean isFifo() + boolean isSock() + + +Changes since version 0.1.47: +- change: the file transfer speed with ChannelSftp#get(String src) has been + improved; sending multiple requests at any one time. +- change: by the default, at most, 16 requests will be sent at any one time + in ChannelSftp. +- feature: added Session#{setIdentityRepository(),getIdentityRepository()} + + +Changes since version 0.1.46: +- bugfix: failed to initialize channels for the stream forwarding. FIXED +- change: Session#getHostKey() will return the given hostkey + even if session is not established. +- change: Logger will record additional messages about algorithm negotiations. +- feature: added ChannelSftp#ls(String path, LsEntrySelector selector) method. +- feature: added IdentityRepository#{getName(),getStatus()} methods. + + +Changes since version 0.1.45: +- bugfix: in the agent forwarding mode, "ssh-add -l" on the remote + will freeze. FIXED +- bugfix: requests should not be sent to the closed channel. FIXED +- bugfix: ChannelShell#setAgentForwarding(true) will cause + resource leaks. FIXED +- change: for the efficiency, channel opening will be delayed + in local port forwarding. +- change: added examples/Sudo.java to demonstrate sudo on exec channel. +- change: authentication trials will be failed at 6 failures by the default. +- change: updating copyright messages; 2011 -> 2012 +- feature: added JSch#setIdentityRepository(IdentityRepository irepo) to + integrate with jsch-agent-proxy. + + +Changes since version 0.1.44: +- bugfix: fields referred by multiple threads simultaneously should be + volatile. FIXED +- bugfix: use local window size offered by the remote in sftp put. + FIXED +- bugfix: SftpProgressMonitor#init was not invoked in sftp-put + for input-stream. FIXED +- bugfix: sftp protocol version 3, 4 and 5 should allow only + UTF-8 encoding. FIXED +- bugfix: Channel Subsystem had failed to set X forwarding flag. + FIXED +- bugfix: Channel X11 had leaked some resources. + FIXED +- bugfix: packet compression may break sessions + in some case(transferring deflated data). FIXED +- bugfix: failed to set dev-null for logger + FIXED +- bugfix: even in sftp protocol version 3 session, some sftpd sends data + packets defined in sftp protocol 6 ;-( working around it. FIXED +- bugfix: ChannelSftp file globbing logic had missed + the string "foo\\\*bar" as a pattern. FIXED +- bugfix: sequential accesses to ChannelSftp by multiple threads may + break its I/O channel. + https://bugs.eclipse.org/bugs/show_bug.cgi?id=359184 FIXED +- bugfix: KeyPair.load can not handle private keys cyphered with AES. FIXED +- change: to improve sftp-put performance, send multiple packet at one time. +- change: wait/notify will be used instead of sleep loop + in establishing channel connections. +- change: increasing local window size for sftp get. +- change: updating copyright messages; 2010 -> 2011 +- change: src/com -> src/main/java/com +- feature: key-exchange method "diffie-hellman-group14-sha1" + (RFC4253#section-8.2) +- feature: KeyPair#getPlulicKeyCommment() is added. + + +Changes since version 0.1.43: +- bugfix: hmac-md5-96 and hmac-sha1-96 are broken. FIXED. +- bugfix: working around OOME in parsing broken data from the remote. FIXED. +- bugfix: failed to send very long command for exec channels. FIXED. +- bugfix: in some case, failed to get the response + for remote port-forwarding request. FIXED. +- feature: support for private keys ciphered with aes192-cbc and aes128-cbc. + + +Changes since version 0.1.42: +- bugfix: the remote window size must be in unsigned int. FIXED. +- bugfix: support for EBCDIC environment. FIXED. +- bugfix: data may be written to the closed channel. FIXED. +- bugfix: NPE in closing channels. FIXED. +- bugfix: the private key file may include garbage data before its header. FIXED. +- bugfix: the session down may not be detected during the re-keying process. FIXED. +- change: try keyboard-interactive auth with the given password if UserInfo is not given. +- change: working around the wrong auth method list sent by some SSHD + in the partial auth success. +- change: working around the CPNI-957037 Plain-text Recovery Attack. +- change: in searching for [host]:non-default port in known_hosts, + host:22 should be also checked. +- change: updating copyright messages; 2009 -> 2010 + + +Changes since version 0.1.41: +- bugfix: making exec request during re-keying process will cause + the dead lock for the session. FIXED. + Many thanks for PanLi at Prominic dot NET and www.prominic.net, + US based hosting company. Without their testing JSch with + hundreds of hosts and their bug reports, this problem + was not fixed. +- change: updating copyright messages; 2008 -> 2009 + + +Changes since version 0.1.40: +- bugfix: canceling the remote port-forwarding with the incorrect + bind-address. FIXED. +- bugfix: sftp had missed to close the file in some case. FIXED. +- bugfix: ls(sftp) will throw an exception for the empty directory + in connecting to some sftpd server. FIXED. +- change: dropping the session gently in accepting incorrect packets. +- change: by the default, aes128-ctr will be chosen if it is available + on the local and the remote. +- feature: adding the support for the private key ciphered in AES256. +- feature: new ciphers: aes128-ctr,aes192-ctr,aes256-ctr, + 3des-ctr,arcfour,arcfour128 ,arcfour256 + + +Changes since version 0.1.39: +- bugfix: ProxySOCKS4 had not been functional. FIXED. +- bugfix: NPE at closing the session when it is not opened. FIXED. +- change: JSch#getConfing has become public. + + +Changes since version 0.1.38: +- bugfix: session will be dropped at rekeying. FIXED. +- bugfix: NPE should not be thrown at unexpected session drop. FIXED. +- change: Channel#getSession() may throw JSchExecption. + + +Changes since version 0.1.37: +- bugfix: NPE should not be thrown at unexpected session drop. FIXED. +- bugfix: AIOOBE at Session#connect(). FIXED. +- bugfix: Even if 'true' is given for + Channel#setOutputStream(OutputStream out, boolean dontclose) + as the second paramter, 'out' will be closed. FIXED. +- change: 'examples/Sftp.java' has been modified to demonstrate + ChannelSftp#reaplpath(String path) +- change: setEnv(Hashtable env) for exec and shell channels have been + marked as @deprecated +- feature: setEnv(String name, String value) has been added to exec + and shell channels. +- feature: setEnv(byte[] name, byte[] value) has been added to exec + and shell channels. +- feature: ChannelSftp#realpath(String path) has been added. +- feature: ChannelExec#setCommand(byte[] command) has been added. +- feature: com.jcraft.jsch.ChannelSftp.LsEntry has implemented + java.lang.Comparable +- feature: Session#getServerAliveInterval(), Session#getServerAliveCountMaX() + have been added. + + +Changes since version 0.1.36: +- bugfix: some sftpd will send invalid data in sftp protocol + point of view, and we need to work around such data. FIXED. +- bugfix: the stream forwarding had been broken since 0.1.30. FIXED. +- bugfix: failed to detect 'SSH_MSG_CHANNEL_OPEN_FAILURE'. FIXED. +- bugfix: ChannelSftp will generate the unfavorable absolute pathname + in some case. FIXED. +- bugfix: failed to ignore the invalid public-key file. FIXED. +- change: ignoring empty data sent by 'SSH_MSG_CHANNEL_DATA' and + 'SSH_MSG_CHANNEL_EXTENDED_DATA'. +- change: updating copyright messages; 2007 -> 2008 +- change: build.xml will enable 'javac.debug' option by the default. +- change: added logging messages to IndentityFile and Session class. +- change: followings are deprecated methods, + InputStream ChannelSftp#get(String src, + int mode) + InputStream ChannelSftp#get(String src, + SftpProgressMonitor, + int mode) +- feature: following method is added, + InputStream ChannelSftp#get(String src, + SftpProgressMonitor monitor, + long skip) + + +Changes since version 0.1.35: +- bugfix: ChannelSftp can not handle the local filenames correctly on Windows. FIXED. +- bugfix: '/' must be handled as the file separator on JVM for Windows. FIXED. +- change: the system property + "javax.security.auth.useSubjectCredsOnly" + will be set to "false" for "gssapi-with-mic" + if that property is not given explicitly. +- change: added changes about ChannelSftp#{pwd(), home()} to + ChangeLog; 'Changes since version 0.1.34:' section. + + +Changes since version 0.1.34: +- bugfix: the OutputStream from the channel may make the JVM + lockup in some case. FIXED. + There was a possibility that Channel#connect() may be failed + to initialize its internal without throwing the JSchException. + On such case, the write operation for OutputStream from + that channel will cause the system(JVM) to lock up. +- bugfix: ChannelSftp had problems filename globbing. FIXED. +- bugfix: the message included in SSH_FXP_STATUS must be UTF-8. FIXED. +- change: ChannelSftp supports the filename globbing for + the filename in multi-byte characters. +- change: ChannelSftp will internally handle filenames in UTF-8 encoding. +- change: ChannelSftp#pwd() may throw an SftpException. +- change: ChannelSftp#home() may throw an SftpException. +- feature: following methods have been added in ChannelSftp + String getServerVersion() + String getClientVersion() + void setFilenameEncoding(String encoding) + String getExtension(String key) + +Changes since version 0.1.33: +- bugfix: there had a possibility that the session may be broken + if ciphers for upward/downward streams are different. FIXED. +- bugfix: the authentication method "keyboard-interactive" had + not been tried without UserInfo. FIXED. +- bugfix: ChannelShell#setTerminalMode(byte[] terminal_mode) had + not been functional. FIXED. +- bugfix: the remote port-forwarding to the daemon had been broken + since 0.1.30. FIXED. +- change: the cipher "aes128-cbc" will be used if AES is available. +- change: the interface 'com.jcraft.jsch.ForwardedTCPIPDaemon' has been changed. +- change: the data transfer rate will be improved on some environment. +- feature: ChannelExec can control the size of pty; + ChannelExec#setPtySize(int col, int row, int wp, int hp) is + added. +- feature: the property "CheckCiphers" has been added. + Refer to 'examples/AES.java'. +- feature: Session#setConfig(String key, String value), + JSch#setConfig(String key, String value) have been added. + + +Changes since version 0.1.32: +- bugfix: freeze in 'diffie-hellman-group-exchange-sha1'. FIXED. + By the default, 'diffie-hellman-group1-sha1' will be used + and if you have not chosen 'diffie-hellman-group-exchange-sha1' + explicitly, you don't have to worry about it. +- bugfix: there should be timeout mechanism in opening a socket + for remote port forwarding. FIXED. + At the failure or timeout, 'SSH_MSG_CHANNEL_OPEN_FAILURE' + will be sent to sshd. +- bugfix: there should be timeout mechanism in opening a socket + for X11 forwarding. FIXED. + At the failure or timeout, 'SSH_MSG_CHANNEL_OPEN_FAILURE' + will be sent to sshd. + + +Changes since version 0.1.31: +- bugfix: remote port forwarding will be hanged at its closing. FIXED. +- bugfix: X forwarding channels will be hanged and some resources + will be leaked whenever they are closed. FIXED. +- bugfix: failed to detect "Connection refused". FIXED. +- bugfix: at the failure for keyboard-interactive auth method, + a session will be terminated. FIXED. +- bugfix: due to the cancel for keyboard-interactive auth method, + a session will be terminated. FIXED. +- change: com.jcraft.jsch.jcraft.Compression#uncompress will respect + the argument "start". +- change: "gssapi-with-mic" will choose the default credential. +- feature: Session#setPortForwardingL will return the assigned local + TCP port number; TCP port will be assigned dynamically if lport==0. +- feature: support for SSH_MSG_UNIMPLEMENTED. +- feature: support for PASSWD_CHANGEREQ. + + +Changes since version 0.1.30: +- bugfix: a problem in padding for ciphered private key. + PKCS#5 padding should be used. FIXED. +- bugfix: crash in parsing invalid public key file. FIXED. +- bugfix: a public key may not have a comment. FIXED. +- bugfix: output stream from ChannelSftp#put will hang if it is closed + twice. FIXED. +- feature: agent forwarding. To enable this functionality, + Channel{Exec,Shell,Sftp}#setAgentForwarding(boolean enable) are added. +- feature: ChannelShell#setTerminalMode(byte[] terminal_mode) is added. +- feature: Session#setDaemonThread(boolean true) to run internal threads as + daemon threads. +- feature: an option "PreferredAuthentications" is added. + The default value is "gssapi-with-mic,publickey,keyboard-interactive,password". +- change: if alias-name is given, non-standard TCP port number will not be + saved in 'known_hosts' file. + + +Changes since version 0.1.29: +- bugfix: ChannelSftp#cd() will not throw an exception even if + a file is given. FIXED. +- bugfix: ChannelSftp#put() has a bug which will appear in using + on the slow network. FIXED. +- bugfix: ChannelSftp#ls() has a bug which will appear in using + on the slow network. FIXED. +- bugfix: a bug had sneaked in the remote port forwarding. FIXED. +- bugfix: some APIs from JCE have the memory leak on Mac OS X, + so we have re-written the code(com.jcraft.jsch.jcraft.HMAC* + classes) without them. On Mac OS X, such new classes will + be used automatically. FIXED. +- bugfix: the session will be crashed by the long banner message. FIXED. +- change: '/dev/random' will not be referred on Gnu/Linux. +- change: if non-standard TCP port number is used, that number will + be saved in known_hosts file as recent OpenSSH's ssh client does. +- change: Channel#getOutputStream will not use Piped{Output,Input}Stream. +- change: com.jcraft.jsch.HostKeyRepository interface has been + slightly modified. +- feature: Session#setPortForwardingR(String bind_address, ...) has been added. +- feature: the packet compression method 'zlib@openssh.com' has been supported. +- feature: the hashed known_hosts file has been supported. + Refer to 'examples/KnownHosts.java'. +- feature: the authentication method 'gssapi-with-mic' has been + experimentally supported. +- feature: com.jcraft.jsch.Logger interface and + JSch#setLogger(Logger logger) have been added. + Refer to 'examples/Logger.java' for the usage. + + +Changes since version 0.1.28: +- bugfix: ChannelSftp#put will stack in some situations FIXED. +- bugfix: ChannelSftp invoked 'glob_remote' redundantly. FIXED. +- bugfix: ChannelSftp failed to make globbing for some file names. FIXED. +- bugfix: ChannelSftp did not carefully read its input-stream. FIXED. +- bugfix: ChannelSftp#lstat did not try globbing for given path. FIXED. +- bugfix: at closing channel, eof_lcoal and eof_remote did not + become true. FIXED. +- bugfix: IdentityFile did not carefully read file input-streams. FIXED. +- bugfix: KeyPair did not carefully read file input-streams. FIXED. +- bugfix: ProxySOCKS4 did not carefully read file input-streams. FIXED. +- bugfix: ProxySOCKS5 did not carefully read file input-streams. FIXED. +- bugfix: ForwardedTCPIPDaemom may fail in some situation. FIXED. +- bugfix: X forwarding failed to handle the magic-cookie + in some case FIXED. + Thanks to Walter Pfannenmller. +- bugfix: setKnownHosts in KnownHosts.java doesn't read the last + line if linefeed is missing FIXED. + Thanks to Henrik Langos. +- bugfix: With StrictHostKeyChecking set to yes connect() + shouldn't ask. FIXED. + Thanks to Henrik Langos. +- change: Identity#setPassphrase(String passphrase) is replaced with + Identity#setPassphrase(byte[] passphrase). +- change: IdentityFile will clear its internal secrets at finalizing. +- change: KeyPair will clear its internal secrets at finalizing. +- change: KeyPair will clear its internal secrets at finalizing. +- change: MAC#doFinal() is replaced with + MAC#doFile(byte[] buf, int offset) +- change: at TCP socket reading timeout, keep-alive message will be sent + to the remote sshd. To disable this functionality, invoke + explicitly Session.setServerAliveCountMax(0) +- change: PortWatcher stops to use InetAddress.getByAddress(). +- change: in the user authentication, username, password and passphrase + will be encoded in UTF-8. +- change: JSch#addIdentity will check duplicate keys. +- change: SftpException#message has been deleted. +- change: SftpException#getMessage() will return the detailed message. +- feature: IdentityFile#setPassphrase(byte[] passphrase) is added. +- feature: IdentityFile#clear() is added to clear its internal secrets. +- feature: KeyPair#decrypt(byte[] passphrase) is added. +- feature: JSch#addIdentity(String path, byte[] passphrase) is added. +- feature: JSch#getIdentityNames() is added. +- feature: JSch#removeIdentity(String name) is added. +- feature: JSch#removeAllIdentity() is added. +- feature: ChannelSftp#readlink(String path) is added. +- feature: ChannelSftp#getHome() is added. +- feature: Channel#connect(int connectTimeout) is added. +- feature: ChannelShell#setPtyType(String ttype) is added. +- feature: Session#setPassword(byte[] password) is added. +- feature: Session#setHostKeyAlias(String alias) is added. +- feature: KeepAlive is implemented and + Session#setServerAliveInterval(int interval) and + Session#setServerAliveCountMax(int count) are added. +- feature: Session#sendKeepAliveMsg() is added. +- feature: JSchException#getCause() may return a reason. +- feature: SftpException#getCause() may return a reason. +- feature: ChannelExec#setErrStream(OutputStream out, boolean dontclose) + is added. + + +Changes since version 0.1.27: +- bugfix: ChannelSftp#localAbsolutePath did not work correctly. FIXED. +- bugfix: ChannelSftp#chmod did not work for directory. FIXED. +- bugfix: ProxyHTTP had a bug in processing HTTP headers. FIXED. +- bugfix: messages before server's version string should be ignored. FIXED. +- feature: Environment Variable Passing. + + +Changes since version 0.1.26: +- bugfix: there was a session crash bug. That occurrence is rare, but + depends on the thread scheduling. FIXED. +- bugfix: problems in generating remote/local absolute paths on sftp. FIXED. +- bugfix: problems in handling cancel operations for sftp. FIXED. +- bugfix: ChannelX11s were not terminated for a wrong cookie. FIXED. +- bugfix: NoSuchAlgorithmException should be thrown if JCE is not + accessible. FIXED. +- bugfix: ProxyHTTP should check the return code from proxy. FIXED. +- bugfix: server's version string should be checked carefully. FIXED. +- feature: some more improvements on sftp uploading. +- feature: 'getUserName' method is added to Session class. + + +Changes since version 0.1.25: +- bugfix: infinite loop/hang on connection at unexpected error during + key-exchanging operation. FIXED +- bugfix: delays on sftp uploading. FIXED +- bugfix: failed to purge a host-key from its repository in some case. FIXED. +- feature: SOCKS4 proxy + + +Changes since version 0.1.24: +- bugfix: remote port forwarding is not established. FIXED. +- bugfix: failed to parse known_hosts file if it has a long public key blob. + FIXED. +- bugfix: sftp put/get operations keep failing. FIXED. +- bugfix: ChannelShell.setXForwarding always set xforwarding to be true. FIXED. +- change: ChannelShell.setPty is added. +- change: Proxy interface is free from session object. +- change: added examples/ScpToNoneCipher.java to demonstrate NONE Cipher switching. +- feature: added NONE Cipher switching support. +- feature: timeout check will be enabled for proxy connections. + + +Changes since version 0.1.23: +- bugfix: there was resource leak problems in closing local port forwardings. + FIXED. +- bugfix: there was a session crash problems in using aes-cbc cipher. FIXED. +- change: ChannelSession.setPtySize was redefined. +- feature: added SocketFactory for remote port forwarding. + Session.setPortForwardingR(int rport, String host, int lport, + SocketFactory sf) +- feature: added ServerSocketFactory for local port forwarding. + Session.setPortForwardingL(String boundaddress, + int lport, String host, int rport, + ServerSocketFactory ssf) + +Changes since version 0.1.22: +- bugfix: there was a freeze problem at fails on key-exchanging. FIXED. +- bugfix: race-condition forcefully disconnects session in closing channels. + FIXED. + + +Changes since version 0.1.21: +- bugfix: there is a bug in read() method implementation for the + input-stream returned from ChannelSftp.get(). FIXED. +- bugfix: at fail on requesting for the remote port forwarding, + an exception should be thrown. FIXED. +- bugfix: SSH_MSG_CHANNEL_OPEN request for the X11 forwarding should not + be accepted if clients don not expect it. FIXED. +- bugfix: there is a problem in transferring large data(mote than 1GB) + to sshd from recent OpenSSH(3.6.1 or later). FIXED. + For security concerns, those sshd will re-key periodically and + jsch had failed to handle it. +- bugfix: 'exec', 'shell' and 'sftp' channels will fail if the acceptable + packet size by remote sshd is not so large. FIXED. +- bugfix: there are problems in 'InputStream ChannelSftp.get(String)' + and 'OutputStream put(String)'. FIXED. +- feature: added boolean flag 'dontclose' to + * setInputStream(), + * setOutputStream() and + * setExtOutputStream() + methods of Channel class. +- feature: added 'com.jcraft.jsch.ChannelSubsystem' +- feature: allowed to control the compression level in the packet compression. + Refer to 'examples/Compression.java'. +- change: modified 'com/jcraft/jsch/jce/KeyPairGenRSA.java' to be complied + on JDK 1.2. +- change: 'examples/ScpTo.java' and 'examples/ScpFrom.java' will use + 'long' type for the file size instead of 'int'. +- change: 'Identity.getSignature' method will not expect 'session'. +- change: while waiting for socket connection establishment, Thread.join + will be used instead of Thread.sleep. + + +Changes since version 0.1.20: +- known issue: there are problems in 'InputStream ChannelSftp.get(String)' + and 'OutputStream put(String)'. They will be re-implemented + in the next release. +- bugfix: EOF packets should not be sent twice. This bug had crashed + the session on every channel close. FIXED. +- bugfix: at the fail on opening connection to remote sshd, + a misleading exception "invalid server's version string" + had been thrown. FIXED. +- bugfix: fixed a bug in hadling the size of remote channel window. +- bugfix: channels should not be closed even if EOF is received. FIXED. +- bugfix: fixed bugs in file name globbing on sftp. +- change: to transfer packets efficiently, the size of internal buffer + for each channel has been increased. +- change: ChannelSftp.ls will return a vector of + com.jcraft.jsch.ChannelSftp.LsEntry. Sorry for inconveniences. +- feature: added ForwardedTCPIPDaemon. Refer to 'examples/Daemon.java', + which demonstrates to provide network services like inetd. +- feature: ChannelExec.setPty() method to request for assigning pseudo tty. +- feature: added ciphers "aes128-cbc", "aes192-cbc" and "aes256-cbc". + Refer to 'examples/AES.java'. +- feature: local port-forwarding settings can be dynamically deleted + by the bound address. +- feature: added 'Channel.isClosed()'. Channel.getExitStatus() should be + invoked after Channel.isClosed()==true. + + +Changes since version 0.1.19: +- ClassCastException while calling ChannelExec.finalize() method. FIXED. + Thanks to wswiatek at ais dot pl. + + +Changes since version 0.1.18: +- fixed problems related to thread-safety. + Thanks to Eric Meek at cs dot utk dot edu. +- At the lost of the network connectivity to the remote SSHD, clients + connected to the local port were never made aware of the + disconnection. FIXED. +- fixed confusions in handling EOFs from local input stream and + the input stream for remote process. +- 'com.jcraft.jsch.jce.AES128CBC' is added, but it is not be functional in + this release. It will work in the next release. +- Some sshd(Foxit-WAC-Serve) waits for SSH_MSG_NEWKEYS request before + sending it. FIXED. +- fixed a problem in connecting to Cisco Devices. + Thanks to Jason Jeffords at comcast dot net. +- changed the method 'add' of 'HostKeyRepository' interface. +- 'UIKeyborarInteracetive' will ignore empty prompt by sshd. +- added 'sendIgnore()' method to 'Session' class. +- added '-p' for scp command in 'examples/ScpTo.java' to preserve + modification times, access times, and modes from the original file. + + +Changes since version 0.1.17: +- added 'com.jcraft.jsch.HostKeyRepository' interface. + It will allow you to handle host keys in your own repository + (for example, RDB) instead of using 'known_hosts' file. +- added methods to get the finger-print of host keys. + refer to 'examples/KnownHosts.java'. +- improved 'known_hosts' file handling. + - comment lines will be kept. + - SSH1 host keys will be kept. + - each hostname can have multiple host keys. +- fixed a crash bug in processing private keys which have too long key-bits. + + +Changes since version 0.1.16: +- 'com.jcraft.jsch.jce.DHG1' and 'com.jcraft.jsch.jce.DHGEX' are moved to + 'com.jcraft.jsch' package. +- added APIs to handle hostkeys included in 'known_hosts', + JSch.getHostKeys(), + JSch.removeHostKey() +- allowing to set timeout value in opening sockets, + Session.connect(int timeout) + + +Changes since version 0.1.15: +- adding support of setting mtime for remote files on sftp channel. +- setKnownHosts method of JSch class will accept InputStream. +- implementation of Basic password authentication for HTTP proxy. +- fixed a bug in checking which ssh protocol version remote sshd supports +- SSH_MSG_CHANNEL_OPEN_FAILURE will be handled correctly. +- methods in SftpATTRS class has been public. +- working around SIGBLOB bug hidden in older sshd. + + +Changes since version 0.1.14: +- fixed a crash bug in accepting keep-alive messages. +- the parent directory of 'known_hosts' file will be created + if it does not exist. +- the Subsystem channel support was removed. + + +Changes since version 0.1.13: +- added 'setClientVersion' method to Session class. +- fixed hung-up problem on SftpChannel in connecting to + the sshd, which does not support sftp. +- fixed OutOfMemory exception problem in decrypting a private key + with bad passphrase. +- fixed hung-up problem in communicating with the sshd, + whose window size is small. +- RuntimeExceptions will be thrown from jsch APIs. +- SSH_MSG_CHANNEL_SUCCESS and SSH_MSG_CHANNEL_FAILURE requests + have been supported. + + +Changes since version 0.1.12: +- added the partial authentication support. +- allowing to change the passphrase of a private key file + instead of creating a new private key. +- added 'examples/ChangePassphrase.java' +- the interface 'UIKeyboardInteractive' has been modified. + + +Changes since version 0.1.11: +- RSA keypair generation. +- added the method 'getFingerPrint' to KeyPair class, + which will return the finger print of the public key. +- fixed a bug in generating non-ciphered private key. + + +Changes since version 0.1.10: +- fixed a bug in the password authentication, sneaked in + 0.1.9. By this bug, the password authentication had failed every time. + + +Changes since version 0.1.9: +- username and password can be in UTF-8 encoding. +- DSA keypair generation. +- added 'examples/KeyGen.java', which demonstrates + the DSA keypair generation. + + +Changes since version 0.1.8: +- fixed crash problems on the local port forwarding. +- fixed crash problems on the remote port forwarding. +- added setErrStream() and getErrStream() to ChannelExec. +- added keyboard-interactive authentication support. +- modified TripleDESCBC to support IBM's JDK 1.4.1. +- added 'examples/UserAuthKI.java', which demonstrates keyboard-interactive + authentication. + + +Changes since version 0.1.7: +- added APIs for sftp resume downloads and uploads. + The author greatly appreciates + elpernotador(webmaster at unlix dot com dot ar), + who motivated him to hack this functionality. +- 'examples/Sftp.java' demonstrates sftp resume functionality. + Please refer to "put-resume", "put-append", "get-resume" and + "get-append" command. +- added the support of 'window-change' request. +- fixed a freeze bug in 'Inputstream get(String src)' method of 'ChannelSftp' + class. + + +Changes since version 0.1.6: +- added 'int getExitStatus()' method to 'Channel' class. +- fixed crash bugs in closing channels for port forwarding. +- fixed glitches in managing forwarded ports. + + +Changes since version 0.1.5: +- fixed crash bugs in port forwarding. +- modified to use "ssh-rsa" for key-exchanging by the default. +- the port forwarding setting can be canceled dynamically. +- fixed a freeze bug in getting an empty file on sftp channel. + + +Changes since version 0.1.4: +- fixed a bug in managing local window size. + The local window should be consumed by CHANNEL_EXTENDED_DATA packet. +- checking the availability of the ssh session in opening channels. + In some case, ssh session had been freezed. +- java.io.File.separator will be refereed in generating local pathname + on sftp channel. +- absolute local pathname will be handled correctly on sftp channel. + + +Changes since version 0.1.3: +- fixed a serious bug, which had leaked resources related to + ChannelExec. +- added the older SFTP protocol(version 0, 1, 2) support. +- fixed a problem in the authentication step for FSecure's sshd. +- fixed a bug, which had broken Diffie-Hellman key exchange in some case. +- implemented the file name globbing for local files on sftp session. +- fixed a bug in the file name globbing. +- added an interface 'SftpProgressMonitor'. +- modified 'examples/Sftp.java' to demonstrate displaying progress-bar + in transferring files. + + +Changes since version 0.1.2: +- modified 'build.xml' to allow Ant users to compile jsch with debug + support (i.e. line-number tables) by overriding the property + javac.debug on the command line. +- added a property 'StrictHostKeyChecking'. +- added 'UserAuthNone' class to request a list of authentication methods on + remote sshd. +- channels will be managed in each sessions. +- added 'ChannelSubsystem', which allows users to use their own + implementations for subsystems. +- added 'isEOF()' method to 'Channel' class. +- supported key pair files in DOS file format. + + +Changes since version 0.1.1: +- added the file name globbing support on sftp session. +- fixed a bug in the public key authentication. + When there was not a public key in ~/.ssh/, that problem occurred. +- improved the 'setTimeout' method. +- fixed a typo in 'LICENSE.txt' + + +Changes since version 0.1.0: +- added 'rekey' method to 'Session' class for key re-exchanging. +- added 'rekey' and 'compression' command to 'examples/Sftp.java'. +- added new 'get' and 'put' methods to 'ChannelSftp'. + Those methods will operate I/O streams. +- methods in 'ChannelSftp' will throw 'SftpException' +- 'ChannelSftp.Ssh_exp_name' is added for the output of 'ls'. + Thanks to Graeme Vetterlein. +- added 'setTimeout' and 'getTimeout' methods to 'Session' class. +- guess will be done in the algorithm negotiation step. +- FSecure's DSA private key has been supported partially. +- hostkeys will be saved into 'known_hosts' file. +- fixed a bug in 'Util.toBase64' method. +- 'Identity' will reject unrecognized keys. +- 'build.xml' will check if jzlib is available or not. + Thanks to Stefan Bodewig. +- added javadoc target in 'build.xml'. + Thanks to Robert Anderson. + + +Changes since version 0.0.13: +- fixed a bug in connecting to Fsecure's sshd on Windows. +- the license is changed to BSD style. + + +Changes since version 0.0.12: +- fixed a bug in verifying DAS signatures. +- added 'SftpATTR' class, which allow you to get attributes of remote files on + sftp channel, and 'stat', 'lstat' method are added to 'ChannelSftp' class. +- added 'getInputStream' and 'getOutputStream' methods Channel class, which + return passive I/O streams. +- 'setIdentity' method is deleted from 'Session' class and + 'addIdentity' method is added to 'JSch' class +- 'setUserName' method is deleted from 'Session' class and + 'getSession' method of 'JSch' class is changed. +- 'isConnected' method is added to 'Session' class. +- 'UserInfo' interface is changed. + + +Changes since version 0.0.11: +- taking care of remote window size. +- adding 'disconnect' method to 'Channel' and 'Session' classes. +- signal sending support. +- 'mkdir' command for sftp. +- 'fromBase64' method has been moved to Util class and 'toBase64' method has + also been added to that class. +- 'KnownHosts' class for checking host-key in 'known_host' file. +- 'examples/KnownHosts.java' has been added. +- 'setUserName' and 'setPassword' methods have been added to Session class. +- 'UserInfo' interface has been changed. +- The implementation of compression has moved to 'com.jcraft.jsch.jcraft' + package. +- fixed a bug in handling 'SSH_MSG_CHANNEL_REQUET' request. +- fixed a bug in sending multiple requests on a single session. + + +Changes since version 0.0.10: +- Diffie-Hellman key exchange 'diffie-hellman-group1-sha1' is supported. + Refer to 'src/com/jcraft/jsch/jce/DHG1.java'. + Thanks to Mitsugu Kitano, whose feedback was very helpful. +- By the default, 'diffie-hellman-group1-sha1' will be used in the + key exchange step. +- The file attribute on 'SSH File Transfer Protocol' is supported. + Now, we can say JSch supports 'SSH File Transfer Protocol'. +- 'examples/Sftp.java' is updated. + 'chgrp','chown','chmod' commands are supported. + + +Changes since version 0.0.9: +- SSH File Transfer Protocol is supported partially. +- 'examples/Sftp.java' is added. + This example is a tiny sftp command and supports 'cd','put','get','rm',etc. +- 'close' method is added to Channel interface. +- build.xml for examples is added. + Thanks to Ronald Broberg. + + +Changes since version 0.0.8: +- the tunneling through a SOCKS5 proxy is supported. +- 'examples/ScpFrom.java' is added. +- 'com.jcraft.jsch.UserInfo' interface is modified. + + +Changes since version 0.0.7: +- Packet comression is supported. +- 'examples/Compression.java' is added. +- JZlib is included. + + +Changes since version 0.0.6: +- RSA host key is supported. +- RSA public key authentication is supported. + + +Changes since version 0.0.5: +- DSA public key authentication is supported. +- examples/UserAuthPubKey.java is added. +- examples/ScpTo.java is added. + + +Changes since version 0.0.4: +- 3des-cbc is supported. +- hmac-sha1 is supported. +- hmac-md5-96 is supported. +- hmac-sha1-96 is supported. + + +Changes since version 0.0.3: +- port forwarding, similar to the -L option of SSH. +- examples/PortForwardingL.java is added. +- examples/StreamForwarding.java is added. +- examples/Exec.java is renamed as examples/Shell.java +- stream forwarding is added. +- ChannelSftp class is added for implementing filexfer. +- interfaces for jsch users are changed. + + +Changes since version 0.0.2: +- remote exec is supported. +- examples/Exec.java is added. +- build.xml and some scripts for Ant are added. (lbruand) +- Const class is added. (lbruand) + + +Changes since version 0.0.1: +- the tunneling via HTTP proxy is supported. +- port forwarding like option -R of ssh command. + the given port on the remote host will be forwarded to the given host + and port on the local side. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..303096bf --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,30 @@ +JSch 0.0.* was released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2002-2015 Atsuhiko Yamanaka, JCraft,Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README b/README new file mode 100644 index 00000000..d00a2192 --- /dev/null +++ b/README @@ -0,0 +1,229 @@ + + JSch + + Java Secure Channel + by ymnk@jcraft.com, JCraft,Inc. + + http://www.jcraft.com/jsch/ + +Last modified: Thu Mar 18 13:58:16 UTC 2015 + + +Description +=========== +JSch is a pure Java implementation of SSH2. JSch allows you to +connect to an sshd server and use port forwarding, X11 forwarding, +file transfer, etc., and you can integrate its functionality +into your own Java programs. JSch is licensed under BSD style license. + + +Documentation +============= +* README files all over the source tree have info related to the stuff + in the directories. +* ChangeLog: what changed from the previous version? + + +Directories & Files in the Source Tree +====================================== +* src/com/ has source trees of JSch +* example/ has some samples, which demonstrate the usages. +* tools/ has scripts for Ant. + + +Why JSch? +========== +Our intension in developing this stuff is to enable users of our pure +java X servers, WiredX(http://wiredx.net/) and WeirdX, to enjoy secure X +sessions. Our efforts have mostly targeted the SSH2 protocol in relation +to X Window System and X11 forwarding. Of course, we are also interested in +adding other functionality - port forward, file transfer, terminal emulation, etc. + + +Features +======== +* JSch is in pure Java, but it depends on JavaTM Cryptography + Extension (JCE). JSch is know to work with: + o J2SE 1.4.0 or later (no additional libraries required). + o J2SE 1.3 and Sun's JCE reference implementation that can be + obtained at http://java.sun.com/products/jce/ + o J2SE 1.2.2 and later and Bouncycastle's JCE implementation that + can be obtained at http://www.bouncycastle.org/ +* SSH2 protocol support. +* Key exchange: diffie-hellman-group-exchange-sha1, + diffie-hellman-group1-sha1, + diffie-hellman-group14-sha1, + diffie-hellman-group-exchange-sha256, + ecdh-sha2-nistp256, + ecdh-sha2-nistp384, + ecdh-sha2-nistp521 +* Cipher: blowfish-cbc,3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc + 3des-ctr,aes128-ctr,aes192-ctr,aes256-ctc, + arcfour,arcfour128,arcfour256 +* MAC: hmac-md5,hmac-md5-96,hmac-sha1,hmac-sha1-96 +* Host key type: ssh-dss,ssh-rsa, + ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521 +* Userauth: password +* Userauth: publickey(DSA,RSA,ECDSA) +* Userauth: keyboard-interactive +* Userauth: gssapi-with-mic +* X11 forwarding. +* xauth spoofing. +* connection through HTTP proxy. +* connection through SOCKS5, SOCKS4 proxy. +* port forwarding. +* stream forwarding. +* signal sending. + The unofficial patch for sshd of openssh will be find in the thread + http://marc.theaimsgroup.com/?l=openssh-unix-dev&m=104295745607575&w=2 +* envrironment variable passing. +* remote exec. +* generating DSA and RSA key pairs. +* supporting private keys in OpenSSL(traditional SSLeay) and PKCS#8 format. +* SSH File Transfer Protocol(version 0, 1, 2, 3) +* partial authentication +* packet compression: zlib, zlib@openssh.com + JZlib(http://www.jcraft.com/jzlib/) has been used. +* hashed known_hosts file. +* NONE Cipher switching. + http://www.psc.edu/networking/projects/hpn-ssh/none.php +* JSch is licensed under BSD style license(refer to LICENSE.txt). + + +How To Try +========== +This archive does not include java byte code, so please compile +the source code by your self. + $ cd jsch-?.?.?/src + $ javac com/jcraft/jsch/*java com/jcraft/jsch/jce/*java com/jcraft/jzlib/*.java +'/examples/' directory has included some samples to demonstrate what +JSch can do. Please refer to '/examples/README' file. + + +AES cipher +========== +JSch supports aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr, +aes256-ctr but you require AES support in your J2SE to choose some of them. +If you are using Sun's J2SE, J2SE 1.4.2 or later is required. +And then, J2SE 1.4.2(or later) does not support aes256 by the default, +because of 'import control restrictions of some countries'. +We have confirmed that by applying + "Java Cryptography Extension (JCE) + Unlimited Strength Jurisdiction Policy Files 1.4.2" +on + http://java.sun.com/j2se/1.4.2/download.html#docs +we can enjoy 'aes256-cbc,aes256-ctr'. + + +Stream Forwarding +================= +JSch has a unique functionality, Stream Forwarding. +Stream Forwarding allows you to plug Java I/O streams directly into a remote TCP +port without assigning and opening a local TCP port. +In port forwarding, as with the -L option of ssh command, you have to assign +and open a local TCP port and that port is also accessible by crackers +on localhost. In some case, that local TCP port may be plugged to a +secret port via SSH session. +A sample program, /example/StreamForwarding.java , demonstrates +this functionality. + + +Generating Authentication Keys +============================== +JSch allows you to generate DSA and RSA key pairs, which are in OpenSSH format. +Please refer to 'examples/KeyGen.java'. + + +Packet Compression +================== +According to the draft from IETF sesch working group, the packet +compression can be applied to each data stream directions; from sshd +server to ssh client and from ssh client to sshd server. So, jsch +allows you to choose which data stream direction will be compressed or not. +For example, in X11 forwarding session, the packet compression for data +stream from sshd to ssh client will save the network traffic, but +usually the traffic from ssh client to sshd is light, so by omitting +the compression for this direction, you may be able to save some CPU time. +Please refer to a sample program 'examples/Compression.java'. + + +Property +======== +By setting properties, you can control the behavior of jsch. +Here is an example of enabling the packet compression, + + Session session=jsch.getSession(user, host, 22); + java.util.Properties config=new java.util.Properties(); + config.put("compression.s2c", "zlib,none"); + config.put("compression.c2s", "zlib,none"); + session.setConfig(config); + session.connect(); + +Current release has supported following properties, +* compression.s2c: zlib, none + default: none + Specifies whether to use compression for the data stream + from sshd to jsch. If "zlib,none" is given and the remote sshd does + not allow the packet compression, compression will not be done. +* compression.c2s: zlib, none + default: none + Specifies whether to use compression for the data stream + from jsch to sshd. +* StrictHostKeyChecking: ask | yes | no + default: ask + If this property is set to ``yes'', jsch will never automatically add + host keys to the $HOME/.ssh/known_hosts file, and refuses to connect + to hosts whose host key has changed. This property forces the user + to manually add all new hosts. If this property is set to ``no'', + jsch will automatically add new host keys to the user known hosts + files. If this property is set to ``ask'', new host keys will be + added to the user known host files only after the user has confirmed + that is what they really want to do, and jsch will refuse to connect + to hosts whose host key has changed. + + +TODO +==== +* re-implementation with java.nio. +* replacing cipher, hash by JCE with pure Java code. +* SSH File Transfer Protocol version 4. +* error handling. + + +Copyrights & Disclaimers +======================== +JSch is copyrighted by ymnk, JCraft,Inc. and is licensed through BSD style license. +Read the LICENSE.txt file for the complete license. + + +Credits and Acknowledgments +============================ +JSch has been developed by ymnk@jcraft.com and it can not be hacked +without several help. +* First of all, we want to thank JCE team at Sun Microsystems. + For long time, we had planed to implement SSH2 in pure Java, + but we had hesitated to do because tons of work must be done for + implementing ciphers, hashes, etc., from the scratch. + Thanks to newly added functionalities to J2SE 1.4.0, we could + start this project. +* We appreciate the OpenSSH project. + The options '-ddd' of sshd, '---vvv' of ssh and the compile options + '-DPACKET_DEBUG', '-DDEBUG_KEXDH' and '-DDEBUG_KEX' were very + useful in debugging JSch. +* We appreciate IETF sesch working group and SSH Communications Security Corp. + Without the standardization of the protocol, we could not get the + chance to implement JSch. +* We appreciate Seigo Haruyama(http://www.unixuser.org/~haruyama/), + who are interpreting drafts of SSH2 protocol in Japanese. + His works were very useful for us to understand the technical terms + in our native language. +* We also appreciate SourceForge.net's awesome service to the + Open Source Community. + + +If you have any comments, suggestions and questions, write us +at jsch@jcraft.com + + +``SSH is a registered trademark and Secure Shell is a trademark of +SSH Communications Security Corp (www.ssh.com)''. diff --git a/build.bat b/build.bat new file mode 100644 index 00000000..91d56dc7 --- /dev/null +++ b/build.bat @@ -0,0 +1,17 @@ +@echo off + +echo. +echo JSch Build System +echo ----------------- + +set OLD_ANT_HOME=%ANT_HOME% +set ANT_HOME=tools + +set OLD_CLASSPATH=%CLASSPATH% + +%ANT_HOME%\bin\ant.bat -emacs %1 %2 %3 %4 %5 %6 %7 %8 +goto cleanup + +:cleanup +set ANT_HOME=%OLD_ANT_HOME% +set CLASSPATH=%OLD_CLASSPATH% diff --git a/build.sh b/build.sh new file mode 100644 index 00000000..9435a67e --- /dev/null +++ b/build.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +echo +echo "JSch Build System" +echo "-----------------" + +export OLD_ANT_HOME=$ANT_HOME +ANT_HOME=./tools + +export OLD_CLASSPATH=$CLASSPATH + + + +export CLASSPATH + +chmod u+x ${ANT_HOME}/bin/antRun +chmod u+x ${ANT_HOME}/bin/ant + +export PROPOSAL="" + + +${ANT_HOME}/bin/ant -emacs $@ + +export CLASSPATH=$OLD_CLASSPATH +export ANT_HOME=$OLD_ANT_HOME diff --git a/build.xml b/build.xml new file mode 100644 index 00000000..ae9e76ee --- /dev/null +++ b/build.xml @@ -0,0 +1,97 @@ + + +JSch is a pure Java implementation of SSH2. JSch allows you to connect to an +sshd server and use port forwarding, X11 forwarding, file transfer, etc., and +you can integrate its functionality into your own Java programs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/AES.java b/examples/AES.java new file mode 100644 index 00000000..0b3e80ea --- /dev/null +++ b/examples/AES.java @@ -0,0 +1,149 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/** + * This program will demonstrate how to use "aes128-cbc". + * + */ +import com.jcraft.jsch.*; +import java.awt.*; +import javax.swing.*; + +public class AES{ + public static void main(String[] arg){ + + try{ + JSch jsch=new JSch(); + + //jsch.setKnownHosts("/home/foo/.ssh/known_hosts"); + + String host=null; + if(arg.length>0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + //session.setPassword("your password"); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.setConfig("cipher.s2c", "aes128-cbc,3des-cbc,blowfish-cbc"); + session.setConfig("cipher.c2s", "aes128-cbc,3des-cbc,blowfish-cbc"); + session.setConfig("CheckCiphers", "aes128-cbc"); + + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ + return false; + } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.setConfig("compression.s2c", "zlib@openssh.com,zlib,none"); + session.setConfig("compression.c2s", "zlib@openssh.com,zlib,none"); + session.setConfig("compression_level", "9"); + + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + String foo=JOptionPane.showInputDialog("Enter remote port number", + "8888"); + rport=Integer.parseInt(foo); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.connect(); + + //session.setPortForwardingR(rport, Parrot.class.getName()); + session.setPortForwardingR(rport, "Daemon$Parrot"); + System.out.println(host+":"+rport+" <--> "+"Parrot"); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class Parrot implements ForwardedTCPIPDaemon{ + ChannelForwardedTCPIP channel; + Object[] arg; + InputStream in; + OutputStream out; + + public void setChannel(ChannelForwardedTCPIP c, InputStream in, OutputStream out){ + this.channel=c; + this.in=in; + this.out=out; + } + public void setArg(Object[] arg){this.arg=arg;} + public void run(){ + try{ + byte[] buf=new byte[1024]; + System.out.println("remote port: "+channel.getRemotePort()); + System.out.println("remote host: "+channel.getSession().getHost()); + while(true){ + int i=in.read(buf, 0, buf.length); + if(i<=0)break; + out.write(buf, 0, i); + out.flush(); + if(buf[0]=='.')break; + } + } + catch(JSchException e){ + System.out.println("session is down."); + } + catch(IOException e){ + } + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result=JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + /* + String xhost="127.0.0.1"; + int xport=0; + String display=JOptionPane.showInputDialog("Enter display name", + xhost+":"+xport); + xhost=display.substring(0, display.indexOf(':')); + xport=Integer.parseInt(display.substring(display.indexOf(':')+1)); + session.setX11Host(xhost); + session.setX11Port(xport+6000); + */ + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + String command=JOptionPane.showInputDialog("Enter command", + "set|grep SSH"); + + Channel channel=session.openChannel("exec"); + ((ChannelExec)channel).setCommand(command); + + // X Forwarding + // channel.setXForwarding(true); + + //channel.setInputStream(System.in); + channel.setInputStream(null); + + //channel.setOutputStream(System.out); + + //FileOutputStream fos=new FileOutputStream("/tmp/stderr"); + //((ChannelExec)channel).setErrStream(fos); + ((ChannelExec)channel).setErrStream(System.err); + + InputStream in=channel.getInputStream(); + + channel.connect(); + + byte[] tmp=new byte[1024]; + while(true){ + while(in.available()>0){ + int i=in.read(tmp, 0, 1024); + if(i<0)break; + System.out.print(new String(tmp, 0, i)); + } + if(channel.isClosed()){ + if(in.available()>0) continue; + System.out.println("exit-status: "+channel.getExitStatus()); + break; + } + try{Thread.sleep(1000);}catch(Exception ee){} + } + channel.disconnect(); + session.disconnect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ + return false; + } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + /* + // In adding to known_hosts file, host names will be hashed. + session.setConfig("HashKnownHosts", "yes"); + */ + + session.connect(); + + { + HostKey hk=session.getHostKey(); + System.out.println("HostKey: "+ + hk.getHost()+" "+ + hk.getType()+" "+ + hk.getFingerPrint(jsch)); + } + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyLogger implements com.jcraft.jsch.Logger { + static java.util.Hashtable name=new java.util.Hashtable(); + static{ + name.put(new Integer(DEBUG), "DEBUG: "); + name.put(new Integer(INFO), "INFO: "); + name.put(new Integer(WARN), "WARN: "); + name.put(new Integer(ERROR), "ERROR: "); + name.put(new Integer(FATAL), "FATAL: "); + } + public boolean isEnabled(int level){ + return true; + } + public void log(int level, String message){ + System.err.print(name.get(new Integer(level))); + System.err.println(message); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result=JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ + return false; + } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + String config = + "Port 22\n"+ + "\n"+ + "Host foo\n"+ + " User "+user+"\n"+ + " Hostname "+host+"\n"+ + "Host *\n"+ + " ConnectTime 30000\n"+ + " PreferredAuthentications keyboard-interactive,password,publickey\n"+ + " #ForwardAgent yes\n"+ + " #StrictHostKeyChecking no\n"+ + " #IdentityFile ~/.ssh/id_rsa\n"+ + " #UserKnownHostsFile ~/.ssh/known_hosts"; + + System.out.println("Generated configurations:"); + System.out.println(config); + + ConfigRepository configRepository = + com.jcraft.jsch.OpenSSHConfig.parse(config); + //com.jcraft.jsch.OpenSSHConfig.parseFile("~/.ssh/config"); + + jsch.setConfigRepository(configRepository); + + // "foo" is from "Host foo" in the above config. + Session session=jsch.getSession("foo"); + + String passwd = JOptionPane.showInputDialog("Enter password"); + session.setPassword(passwd); + + UserInfo ui = new MyUserInfo(){ + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + public boolean promptYesNo(String message){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + message, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + // If password is not given before the invocation of Session#connect(), + // implement also following methods, + // * UserInfo#getPassword(), + // * UserInfo#promptPassword(String message) and + // * UIKeyboardInteractive#promptKeyboardInteractive() + + }; + + session.setUserInfo(ui); + + session.connect(); // making a connection with timeout as defined above. + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + /* + // a hack for MS-DOS prompt on Windows. + channel.setInputStream(new FilterInputStream(System.in){ + public int read(byte[] b, int off, int len)throws IOException{ + return in.read(b, off, (len>1024?1024:len)); + } + }); + */ + + channel.setOutputStream(System.out); + + /* + // Choose the pty-type "vt102". + ((ChannelShell)channel).setPtyType("vt102"); + */ + + /* + // Set environment variable "LANG" as "ja_JP.eucJP". + ((ChannelShell)channel).setEnv("LANG", "ja_JP.eucJP"); + */ + + //channel.connect(); + channel.connect(3*1000); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static abstract class MyUserInfo + implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return null; } + public boolean promptYesNo(String str){ return false; } + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return false; } + public boolean promptPassword(String message){ return false; } + public void showMessage(String message){ } + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + return null; + } + } +} diff --git a/examples/PortForwardingL.java b/examples/PortForwardingL.java new file mode 100644 index 00000000..a34f238e --- /dev/null +++ b/examples/PortForwardingL.java @@ -0,0 +1,155 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/** + * This program will demonstrate the port forwarding like option -L of + * ssh command; the given port on the local host will be forwarded to + * the given remote host and port on the remote side. + * $ CLASSPATH=.:../build javac PortForwardingL.java + * $ CLASSPATH=.:../build java PortForwardingL + * You will be asked username, hostname, port:host:hostport and passwd. + * If everything works fine, you will get the shell prompt. + * Try the port on localhost. + * + */ +import com.jcraft.jsch.*; +import java.awt.*; +import javax.swing.*; + +public class PortForwardingL{ + public static void main(String[] arg){ + + int lport; + String rhost; + int rport; + + try{ + JSch jsch=new JSch(); + + String host=null; + if(arg.length>0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + String foo=JOptionPane.showInputDialog("Enter -L port:host:hostport", + "port:host:hostport"); + lport=Integer.parseInt(foo.substring(0, foo.indexOf(':'))); + foo=foo.substring(foo.indexOf(':')+1); + rhost=foo.substring(0, foo.indexOf(':')); + rport=Integer.parseInt(foo.substring(foo.indexOf(':')+1)); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.connect(); + + //Channel channel=session.openChannel("shell"); + //channel.connect(); + + int assinged_port=session.setPortForwardingL(lport, rhost, rport); + System.out.println("localhost:"+assinged_port+" -> "+rhost+":"+rport); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + String foo=JOptionPane.showInputDialog("Enter -R port:host:hostport", + "port:host:hostport"); + rport=Integer.parseInt(foo.substring(0, foo.indexOf(':'))); + foo=foo.substring(foo.indexOf(':')+1); + lhost=foo.substring(0, foo.indexOf(':')); + lport=Integer.parseInt(foo.substring(foo.indexOf(':')+1)); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.connect(); + + // Channel channel=session.openChannel("shell"); + // channel.connect(); + + session.setPortForwardingR(rport, lhost, lport); + + System.out.println(host+":"+rport+" -> "+lhost+":"+lport); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i'. + 'help' command will show available command. + In current implementation, the destination path for 'get' and 'put' + commands must be a file, not a directory. + +- KnownHosts.java + This program will demonstrate the 'known_hosts' file handling. + $ CLASSPATH=.:../build javac KnownHosts.java + $ CLASSPATH=.:../build java KnownHosts + You will be asked username, hostname, a path for 'known_hosts' and passwd. + If everything works fine, you will get the shell prompt. + In current implementation, jsch only reads 'known_hosts' for checking + and does not modify it. + +- UserAuthKI.java + This program will demonstrate the keyboard-interactive authentication. + $ CLASSPATH=.:../build javac UserAuthKI.java + $ CLASSPATH=.:../build java UserAuthKI + If the remote sshd supports keyboard-interactive authentication, + you will be prompted. + +- KeyGen.java + This progam will demonstrate the DSA keypair generation. + $ CLASSPATH=.:../build javac KeyGen.java + $ CLASSPATH=.:../build java KeyGen rsa output_keyfile comment +or + $ CLASSPATH=.:../build java KeyGen dsa output_keyfile comment + You will be asked a passphrase for output_keyfile. + If everything works fine, you will get the DSA or RSA keypair, + output_keyfile and output_keyfile+".pub". + The private key and public key are in the OpenSSH format. + +- ChangePassphrase.java + This program will demonstrate to change the passphrase for a + private key file instead of creating a new private key. + $ CLASSPATH=.:../build javac ChangePassphrase.java + $ CLASSPATH=.:../build java ChangePassphrase private-key + A passphrase will be prompted if the given private-key has been + encrypted. After successfully loading the content of the + private-key, the new passphrase will be prompted and the given + private-key will be re-encrypted with that new passphrase. + +- AES.java + This program will demonstrate how to use "aes128-cbc". + +- Daemon.java + This program will demonstrate how to provide a network service like + inetd by using remote port-forwarding functionality. + +- Logger.java + This program will demonstrate how to enable logging mechanism and + get logging messages. + +- Subsystem.java + This program will demonstrate how to use the Subsystem channel. + +- Sudo.java + This program will demonstrate how to exec 'sudo' on the remote. + +- ScpToNoneCipher.java + This program will demonstrate how to enable none cipher. + +- JumpHosts.java + This program will demonstrate SSH through jump hosts. + +- OpenSSHConfig.java + This program will demonstrate how OpenSSH's config is supported. diff --git a/examples/ScpFrom.java b/examples/ScpFrom.java new file mode 100644 index 00000000..0d633aee --- /dev/null +++ b/examples/ScpFrom.java @@ -0,0 +1,249 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/** + * This program will demonstrate the file transfer from remote to local + * $ CLASSPATH=.:../build javac ScpFrom.java + * $ CLASSPATH=.:../build java ScpFrom user@remotehost:file1 file2 + * You will be asked passwd. + * If everything works fine, a file 'file1' on 'remotehost' will copied to + * local 'file1'. + * + */ +import com.jcraft.jsch.*; +import java.awt.*; +import javax.swing.*; +import java.io.*; + +public class ScpFrom{ + public static void main(String[] arg){ + if(arg.length!=2){ + System.err.println("usage: java ScpFrom user@remotehost:file1 file2"); + System.exit(-1); + } + + FileOutputStream fos=null; + try{ + + String user=arg[0].substring(0, arg[0].indexOf('@')); + arg[0]=arg[0].substring(arg[0].indexOf('@')+1); + String host=arg[0].substring(0, arg[0].indexOf(':')); + String rfile=arg[0].substring(arg[0].indexOf(':')+1); + String lfile=arg[1]; + + String prefix=null; + if(new File(lfile).isDirectory()){ + prefix=lfile+File.separator; + } + + JSch jsch=new JSch(); + Session session=jsch.getSession(user, host, 22); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + // exec 'scp -f rfile' remotely + rfile=rfile.replace("'", "'\"'\"'"); + rfile="'"+rfile+"'"; + String command="scp -f "+rfile; + Channel channel=session.openChannel("exec"); + ((ChannelExec)channel).setCommand(command); + + // get I/O streams for remote scp + OutputStream out=channel.getOutputStream(); + InputStream in=channel.getInputStream(); + + channel.connect(); + + byte[] buf=new byte[1024]; + + // send '\0' + buf[0]=0; out.write(buf, 0, 1); out.flush(); + + while(true){ + int c=checkAck(in); + if(c!='C'){ + break; + } + + // read '0644 ' + in.read(buf, 0, 5); + + long filesize=0L; + while(true){ + if(in.read(buf, 0, 1)<0){ + // error + break; + } + if(buf[0]==' ')break; + filesize=filesize*10L+(long)(buf[0]-'0'); + } + + String file=null; + for(int i=0;;i++){ + in.read(buf, i, 1); + if(buf[i]==(byte)0x0a){ + file=new String(buf, 0, i); + break; + } + } + + //System.out.println("filesize="+filesize+", file="+file); + + // send '\0' + buf[0]=0; out.write(buf, 0, 1); out.flush(); + + // read a content of lfile + fos=new FileOutputStream(prefix==null ? lfile : prefix+file); + int foo; + while(true){ + if(buf.length0){ + command+=lfile.substring(lfile.lastIndexOf('/')+1); + } + else{ + command+=lfile; + } + command+="\n"; + out.write(command.getBytes()); out.flush(); + if(checkAck(in)!=0){ + System.exit(0); + } + + // send a content of lfile + fis=new FileInputStream(lfile); + byte[] buf=new byte[1024]; + while(true){ + int len=fis.read(buf, 0, buf.length); + if(len<=0) break; + out.write(buf, 0, len); //out.flush(); + } + fis.close(); + fis=null; + // send '\0' + buf[0]=0; out.write(buf, 0, 1); out.flush(); + if(checkAck(in)!=0){ + System.exit(0); + } + out.close(); + + channel.disconnect(); + session.disconnect(); + + System.exit(0); + } + catch(Exception e){ + System.out.println(e); + try{if(fis!=null)fis.close();}catch(Exception ee){} + } + } + + static int checkAck(InputStream in) throws IOException{ + int b=in.read(); + // b may be 0 for success, + // 1 for error, + // 2 for fatal error, + // -1 + if(b==0) return b; + if(b==-1) return b; + + if(b==1 || b==2){ + StringBuffer sb=new StringBuffer(); + int c; + do { + c=in.read(); + sb.append((char)c); + } + while(c!='\n'); + if(b==1){ // error + System.out.print(sb.toString()); + } + if(b==2){ // fatal error + System.out.print(sb.toString()); + } + } + return b; + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + command+=lfile.substring(lfile.lastIndexOf('/')+1); + } + else{ + command+=lfile; + } + command+="\n"; + out.write(command.getBytes()); out.flush(); + + if(checkAck(in)!=0){ + System.exit(0); + } + + // send a content of lfile + fis=new FileInputStream(lfile); + byte[] buf=new byte[1024]; + while(true){ + int len=fis.read(buf, 0, buf.length); + if(len<=0) break; + out.write(buf, 0, len); out.flush(); + } + fis.close(); + fis=null; + + // send '\0' + buf[0]=0; out.write(buf, 0, 1); out.flush(); + + if(checkAck(in)!=0){ + System.exit(0); + } + + session.disconnect(); + + System.exit(0); + } + catch(Exception e){ + System.out.println(e); + try{if(fis!=null)fis.close();}catch(Exception ee){} + } + } + + static int checkAck(InputStream in) throws IOException{ + int b=in.read(); + // b may be 0 for success, + // 1 for error, + // 2 for fatal error, + // -1 + if(b==0) return b; + if(b==-1) return b; + + if(b==1 || b==2){ + StringBuffer sb=new StringBuffer(); + int c; + do { + c=in.read(); + sb.append((char)c); + } + while(c!='\n'); + if(b==1){ // error + System.out.print(sb.toString()); + } + if(b==2){ // fatal error + System.out.print(sb.toString()); + } + } + return b; + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i'. + * 'help' command will show available command. + * In current implementation, the destination path for 'get' and 'put' + * commands must be a file, not a directory. + * + */ +import com.jcraft.jsch.*; +import java.awt.*; +import javax.swing.*; + +public class Sftp{ + public static void main(String[] arg){ + + try{ + JSch jsch=new JSch(); + + String host=null; + if(arg.length>0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + int port=22; + + Session session=jsch.getSession(user, host, port); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.connect(); + + Channel channel=session.openChannel("sftp"); + channel.connect(); + ChannelSftp c=(ChannelSftp)channel; + + java.io.InputStream in=System.in; + java.io.PrintStream out=System.out; + + java.util.Vector cmds=new java.util.Vector(); + byte[] buf=new byte[1024]; + int i; + String str; + int level=0; + + while(true){ + out.print("sftp> "); + cmds.removeAllElements(); + i=in.read(buf, 0, 1024); + if(i<=0)break; + + i--; + if(i>0 && buf[i-1]==0x0d)i--; + //str=new String(buf, 0, i); + //System.out.println("|"+str+"|"); + int s=0; + for(int ii=0; ii0){ cmds.addElement(new String(buf, s, ii-s)); } + while(ii'7'){foo=-1; break;} + foo<<=3; + foo|=(k-'0'); + } + if(foo==-1)continue; + } + else{ + try{foo=Integer.parseInt((String)cmds.elementAt(1));} + catch(Exception e){continue;} + } + try{ + if(cmd.equals("chgrp")){ c.chgrp(foo, path); } + else if(cmd.equals("chown")){ c.chown(foo, path); } + else if(cmd.equals("chmod")){ c.chmod(foo, path); } + } + catch(SftpException e){ + System.out.println(e.toString()); + } + continue; + } + if(cmd.equals("pwd") || cmd.equals("lpwd")){ + str=(cmd.equals("pwd")?"Remote":"Local"); + str+=" working directory: "; + if(cmd.equals("pwd")) str+=c.pwd(); + else str+=c.lpwd(); + out.println(str); + continue; + } + if(cmd.equals("ls") || cmd.equals("dir")){ + String path="."; + if(cmds.size()==2) path=(String)cmds.elementAt(1); + try{ + java.util.Vector vv=c.ls(path); + if(vv!=null){ + for(int ii=0; ii2) continue; + String p1 = cmds.size()==1 ? ".": (String)cmds.elementAt(1); + SftpStatVFS stat = c.statVFS(p1); + + long size = stat.getSize(); + long used = stat.getUsed(); + long avail = stat.getAvailForNonRoot(); + long root_avail = stat.getAvail(); + long capacity = stat.getCapacity(); + + System.out.println("Size: "+size); + System.out.println("Used: "+used); + System.out.println("Avail: "+avail); + System.out.println("(root): "+root_avail); + System.out.println("%Capacity: "+capacity); + + continue; + } + if(cmd.equals("stat") || cmd.equals("lstat")){ + if(cmds.size()!=2) continue; + String p1=(String)cmds.elementAt(1); + SftpATTRS attrs=null; + try{ + if(cmd.equals("stat")) attrs=c.stat(p1); + else attrs=c.lstat(p1); + } + catch(SftpException e){ + System.out.println(e.toString()); + } + if(attrs!=null){ + out.println(attrs); + } + else{ + } + continue; + } + if(cmd.equals("readlink")){ + if(cmds.size()!=2) continue; + String p1=(String)cmds.elementAt(1); + String filename=null; + try{ + filename=c.readlink(p1); + out.println(filename); + } + catch(SftpException e){ + System.out.println(e.toString()); + } + continue; + } + if(cmd.equals("realpath")){ + if(cmds.size()!=2) continue; + String p1=(String)cmds.elementAt(1); + String filename=null; + try{ + filename=c.realpath(p1); + out.println(filename); + } + catch(SftpException e){ + System.out.println(e.toString()); + } + continue; + } + if(cmd.equals("version")){ + out.println("SFTP protocol version "+c.version()); + continue; + } + if(cmd.equals("help") || cmd.equals("?")){ + out.println(help); + continue; + } + out.println("unimplemented command: "+cmd); + } + session.disconnect(); + } + catch(Exception e){ + System.out.println(e); + } + System.exit(0); + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i=this.count*100/max){ return true; } + percent=this.count*100/max; + + monitor.setNote("Completed "+this.count+"("+percent+"%) out of "+max+"."); + monitor.setProgress((int)this.count); + + return !(monitor.isCanceled()); + } + public void end(){ + monitor.close(); + } + } + + private static String help = +" Available commands:\n"+ +" * means unimplemented command.\n"+ +"cd path Change remote directory to 'path'\n"+ +"lcd path Change local directory to 'path'\n"+ +"chgrp grp path Change group of file 'path' to 'grp'\n"+ +"chmod mode path Change permissions of file 'path' to 'mode'\n"+ +"chown own path Change owner of file 'path' to 'own'\n"+ +"df [path] Display statistics for current directory or\n"+ +" filesystem containing 'path'\n"+ +"help Display this help text\n"+ +"get remote-path [local-path] Download file\n"+ +"get-resume remote-path [local-path] Resume to download file.\n"+ +"get-append remote-path [local-path] Append remote file to local file\n"+ +"hardlink oldpath newpath Hardlink remote file\n"+ +"*lls [ls-options [path]] Display local directory listing\n"+ +"ln oldpath newpath Symlink remote file\n"+ +"*lmkdir path Create local directory\n"+ +"lpwd Print local working directory\n"+ +"ls [path] Display remote directory listing\n"+ +"*lumask umask Set local umask to 'umask'\n"+ +"mkdir path Create remote directory\n"+ +"put local-path [remote-path] Upload file\n"+ +"put-resume local-path [remote-path] Resume to upload file\n"+ +"put-append local-path [remote-path] Append local file to remote file.\n"+ +"pwd Display remote working directory\n"+ +"stat path Display info about path\n"+ +"exit Quit sftp\n"+ +"quit Quit sftp\n"+ +"rename oldpath newpath Rename remote file\n"+ +"rmdir path Remove remote directory\n"+ +"rm path Delete remote file\n"+ +"symlink oldpath newpath Symlink remote file\n"+ +"readlink path Check the target of a symbolic link\n"+ +"realpath path Canonicalize the path\n"+ +"rekey Key re-exchanging\n"+ +"compression level Packet compression will be enabled\n"+ +"version Show SFTP version\n"+ +"? Synonym for help"; +} diff --git a/examples/Shell.java b/examples/Shell.java new file mode 100644 index 00000000..425cd59c --- /dev/null +++ b/examples/Shell.java @@ -0,0 +1,123 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/** + * This program enables you to connect to sshd server and get the shell prompt. + * $ CLASSPATH=.:../build javac Shell.java + * $ CLASSPATH=.:../build java Shell + * You will be asked username, hostname and passwd. + * If everything works fine, you will get the shell prompt. Output may + * be ugly because of lacks of terminal-emulation, but you can issue commands. + * + */ +import com.jcraft.jsch.*; +import java.awt.*; +import javax.swing.*; + +public class Shell{ + public static void main(String[] arg){ + + try{ + JSch jsch=new JSch(); + + //jsch.setKnownHosts("/home/foo/.ssh/known_hosts"); + + String host=null; + if(arg.length>0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + String passwd = JOptionPane.showInputDialog("Enter password"); + session.setPassword(passwd); + + UserInfo ui = new MyUserInfo(){ + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + public boolean promptYesNo(String message){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + message, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + // If password is not given before the invocation of Session#connect(), + // implement also following methods, + // * UserInfo#getPassword(), + // * UserInfo#promptPassword(String message) and + // * UIKeyboardInteractive#promptKeyboardInteractive() + + }; + + session.setUserInfo(ui); + + // It must not be recommended, but if you want to skip host-key check, + // invoke following, + // session.setConfig("StrictHostKeyChecking", "no"); + + //session.connect(); + session.connect(30000); // making a connection with timeout. + + Channel channel=session.openChannel("shell"); + + // Enable agent-forwarding. + //((ChannelShell)channel).setAgentForwarding(true); + + channel.setInputStream(System.in); + /* + // a hack for MS-DOS prompt on Windows. + channel.setInputStream(new FilterInputStream(System.in){ + public int read(byte[] b, int off, int len)throws IOException{ + return in.read(b, off, (len>1024?1024:len)); + } + }); + */ + + channel.setOutputStream(System.out); + + /* + // Choose the pty-type "vt102". + ((ChannelShell)channel).setPtyType("vt102"); + */ + + /* + // Set environment variable "LANG" as "ja_JP.eucJP". + ((ChannelShell)channel).setEnv("LANG", "ja_JP.eucJP"); + */ + + //channel.connect(); + channel.connect(3*1000); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static abstract class MyUserInfo + implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return null; } + public boolean promptYesNo(String str){ return false; } + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return false; } + public boolean promptPassword(String message){ return false; } + public void showMessage(String message){ } + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + return null; + } + } +} diff --git a/examples/StreamForwarding.java b/examples/StreamForwarding.java new file mode 100644 index 00000000..4a13939a --- /dev/null +++ b/examples/StreamForwarding.java @@ -0,0 +1,155 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/** + * This program will demonstrate the stream forwarding. The given Java + * I/O streams will be forwared to the given remote host and port on + * the remote side. It is simmilar to the -L option of ssh command, + * but you don't have to assign and open a local tcp port. + * $ CLASSPATH=.:../build javac StreamForwarding.java + * $ CLASSPATH=.:../build java StreamForwarding + * You will be asked username, hostname, host:hostport and passwd. + * If everything works fine, System.in and System.out streams will be + * forwared to remote port and you can send messages from command line. + * + */ +import com.jcraft.jsch.*; +import java.awt.*; +import javax.swing.*; + +public class StreamForwarding{ + public static void main(String[] arg){ + int port; + + try{ + JSch jsch=new JSch(); + + String host=null; + if(arg.length>0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + String foo=JOptionPane.showInputDialog("Enter host and port", + "host:port"); + host=foo.substring(0, foo.indexOf(':')); + port=Integer.parseInt(foo.substring(foo.indexOf(':')+1)); + + System.out.println("System.{in,out} will be forwarded to "+ + host+":"+port+"."); + Channel channel = session.getStreamForwarder(host, port); + // InputStream in = channel.getInputStream(); + // OutpuStream out = channel.getOutputStream(); + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + channel.connect(1000); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + String subsystem=JOptionPane.showInputDialog("Enter subsystem name", ""); + + Channel channel=session.openChannel("subsystem"); + ((ChannelSubsystem)channel).setSubsystem(subsystem); + ((ChannelSubsystem)channel).setPty(true); + + channel.setInputStream(System.in); + ((ChannelSubsystem)channel).setErrStream(System.err); + channel.setOutputStream(System.out); + channel.connect(); + + /* + channel.setInputStream(System.in); + ((ChannelSubsystem)channel).setErrStream(System.err); + InputStream in = channel.getInputStream(); + channel.connect(); + + byte[] tmp=new byte[1024]; + while(true){ + while(in.available()>0){ + int i=in.read(tmp, 0, 1024); + if(i<0)break; + System.out.print(new String(tmp, 0, i)); + } + if(channel.isClosed()){ + System.out.println("exit-status: "+channel.getExitStatus()); + break; + } + try{Thread.sleep(1000);}catch(Exception ee){} + } + channel.disconnect(); + session.disconnect(); + */ + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ + return false; + } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + String command=JOptionPane.showInputDialog("Enter command, execed with sudo", + "printenv SUDO_USER"); + + String sudo_pass=null; + { + JTextField passwordField=(JTextField)new JPasswordField(8); + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, + ob, + "Enter password for sudo", + JOptionPane.OK_CANCEL_OPTION); + if(result!=JOptionPane.OK_OPTION){ + System.exit(-1); + } + sudo_pass=passwordField.getText(); + } + + Channel channel=session.openChannel("exec"); + + // man sudo + // -S The -S (stdin) option causes sudo to read the password from the + // standard input instead of the terminal device. + // -p The -p (prompt) option allows you to override the default + // password prompt and use a custom one. + ((ChannelExec)channel).setCommand("sudo -S -p '' "+command); + + + InputStream in=channel.getInputStream(); + OutputStream out=channel.getOutputStream(); + ((ChannelExec)channel).setErrStream(System.err); + + channel.connect(); + + out.write((sudo_pass+"\n").getBytes()); + out.flush(); + + byte[] tmp=new byte[1024]; + while(true){ + while(in.available()>0){ + int i=in.read(tmp, 0, 1024); + if(i<0)break; + System.out.print(new String(tmp, 0, i)); + } + if(channel.isClosed()){ + System.out.println("exit-status: "+channel.getExitStatus()); + break; + } + try{Thread.sleep(1000);}catch(Exception ee){} + } + channel.disconnect(); + session.disconnect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ + return false; + } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + // username and passphrase will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return false; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ + return false; + } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ +/* +//System.out.println("promptKeyboardInteractive"); +System.out.println("destination: "+destination); +System.out.println("name: "+name); +System.out.println("instruction: "+instruction); +System.out.println("prompt.length: "+prompt.length); +System.out.println("prompt: "+prompt[0]); +*/ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + // username and passphrase will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return null; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passphrase; + JTextField passphraseField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return passphrase; } + public boolean promptPassphrase(String message){ + Object[] ob={passphraseField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passphrase=passphraseField.getText(); + return true; + } + else{ return false; } + } + public boolean promptPassword(String message){ return true; } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + String proxy=JOptionPane.showInputDialog("Enter proxy server", + "hostname:port"); + proxy_host=proxy.substring(0, proxy.indexOf(':')); + proxy_port=Integer.parseInt(proxy.substring(proxy.indexOf(':')+1)); + + session.setProxy(new ProxyHTTP(proxy_host, proxy_port)); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + String proxy=JOptionPane.showInputDialog("Enter proxy server", + "hostname:port"); + proxy_host=proxy.substring(0, proxy.indexOf(':')); + proxy_port=Integer.parseInt(proxy.substring(proxy.indexOf(':')+1)); + + session.setProxy(new ProxySOCKS5(proxy_host, proxy_port)); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i0){ + host=arg[0]; + } + else{ + host=JOptionPane.showInputDialog("Enter username@hostname", + System.getProperty("user.name")+ + "@localhost"); + } + String user=host.substring(0, host.indexOf('@')); + host=host.substring(host.indexOf('@')+1); + + Session session=jsch.getSession(user, host, 22); + + String display=JOptionPane.showInputDialog("Please enter display name", + xhost+":"+xport); + xhost=display.substring(0, display.indexOf(':')); + xport=Integer.parseInt(display.substring(display.indexOf(':')+1)); + + session.setX11Host(xhost); + session.setX11Port(xport+6000); + + // username and password will be given via UserInfo interface. + UserInfo ui=new MyUserInfo(); + session.setUserInfo(ui); + session.connect(); + + Channel channel=session.openChannel("shell"); + + channel.setXForwarding(true); + + channel.setInputStream(System.in); + channel.setOutputStream(System.out); + + channel.connect(); + } + catch(Exception e){ + System.out.println(e); + } + } + + public static class MyUserInfo implements UserInfo, UIKeyboardInteractive{ + public String getPassword(){ return passwd; } + public boolean promptYesNo(String str){ + Object[] options={ "yes", "no" }; + int foo=JOptionPane.showOptionDialog(null, + str, + "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, + null, options, options[0]); + return foo==0; + } + + String passwd; + JTextField passwordField=(JTextField)new JPasswordField(20); + + public String getPassphrase(){ return null; } + public boolean promptPassphrase(String message){ return true; } + public boolean promptPassword(String message){ + Object[] ob={passwordField}; + int result= + JOptionPane.showConfirmDialog(null, ob, message, + JOptionPane.OK_CANCEL_OPTION); + if(result==JOptionPane.OK_OPTION){ + passwd=passwordField.getText(); + return true; + } + else{ return false; } + } + public void showMessage(String message){ + JOptionPane.showMessageDialog(null, message); + } + final GridBagConstraints gbc = + new GridBagConstraints(0,0,1,1,1,1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0,0,0,0),0,0); + private Container panel; + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo){ + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + + gbc.gridwidth = GridBagConstraints.RELATIVE; + + JTextField[] texts=new JTextField[prompt.length]; + for(int i=0; i + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..5e5fed66 --- /dev/null +++ b/pom.xml @@ -0,0 +1,113 @@ + + 4.0.0 + com.jcraft + jsch + jar + 0.1.55 + JSch + http://www.jcraft.com/jsch/ + JSch is a pure Java implementation of SSH2 + + JCraft,Inc. + http://www.jcraft.com/ + + + scm:git:http://git.jcraft.com/jsch.git + scm:git:http://git.jcraft.com/jsch.git + http://git.jcraft.com/jsch.git + + + + ymnk + Atsuhiko Yamanaka + ymnk at jcraft D0t com + http://github.com/ymnk + JCraft,Inc. + http://www.jcraft.com/ + + architect + developer + + +9 + + + + + Revised BSD + http://www.jcraft.com/jsch/LICENSE.txt + + + + + com.jcraft + jzlib + 1.0.7 + true + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + + + + org.apache.maven.wagon + wagon-ssh-external + 1.0-alpha-5 + + + + + + org.sonatype.oss + oss-parent + 6 + + \ No newline at end of file diff --git a/src/main/java/com/jcraft/jsch/Buffer.java b/src/main/java/com/jcraft/jsch/Buffer.java new file mode 100644 index 00000000..ad1b2c08 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/Buffer.java @@ -0,0 +1,297 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class Buffer{ + final byte[] tmp=new byte[4]; + byte[] buffer; + int index; + int s; + public Buffer(int size){ + buffer=new byte[size]; + index=0; + s=0; + } + public Buffer(byte[] buffer){ + this.buffer=buffer; + index=0; + s=0; + } + public Buffer(){ this(1024*10*2); } + public void putByte(byte foo){ + buffer[index++]=foo; + } + public void putByte(byte[] foo) { + putByte(foo, 0, foo.length); + } + public void putByte(byte[] foo, int begin, int length) { + System.arraycopy(foo, begin, buffer, index, length); + index+=length; + } + public void putString(byte[] foo){ + putString(foo, 0, foo.length); + } + public void putString(byte[] foo, int begin, int length) { + putInt(length); + putByte(foo, begin, length); + } + public void putInt(int val) { + tmp[0]=(byte)(val >>> 24); + tmp[1]=(byte)(val >>> 16); + tmp[2]=(byte)(val >>> 8); + tmp[3]=(byte)(val); + System.arraycopy(tmp, 0, buffer, index, 4); + index+=4; + } + public void putLong(long val) { + tmp[0]=(byte)(val >>> 56); + tmp[1]=(byte)(val >>> 48); + tmp[2]=(byte)(val >>> 40); + tmp[3]=(byte)(val >>> 32); + System.arraycopy(tmp, 0, buffer, index, 4); + tmp[0]=(byte)(val >>> 24); + tmp[1]=(byte)(val >>> 16); + tmp[2]=(byte)(val >>> 8); + tmp[3]=(byte)(val); + System.arraycopy(tmp, 0, buffer, index+4, 4); + index+=8; + } + void skip(int n) { + index+=n; + } + void putPad(int n) { + while(n>0){ + buffer[index++]=(byte)0; + n--; + } + } + public void putMPInt(byte[] foo){ + int i=foo.length; + if((foo[0]&0x80)!=0){ + i++; + putInt(i); + putByte((byte)0); + } + else{ + putInt(i); + } + putByte(foo); + } + public int getLength(){ + return index-s; + } + public int getOffSet(){ + return s; + } + public void setOffSet(int s){ + this.s=s; + } + public long getLong(){ + long foo = getInt()&0xffffffffL; + foo = ((foo<<32)) | (getInt()&0xffffffffL); + return foo; + } + public int getInt(){ + int foo = getShort(); + foo = ((foo<<16)&0xffff0000) | (getShort()&0xffff); + return foo; + } + public long getUInt(){ + long foo = 0L; + long bar = 0L; + foo = getByte(); + foo = ((foo<<8)&0xff00)|(getByte()&0xff); + bar = getByte(); + bar = ((bar<<8)&0xff00)|(getByte()&0xff); + foo = ((foo<<16)&0xffff0000) | (bar&0xffff); + return foo; + } + int getShort() { + int foo = getByte(); + foo = ((foo<<8)&0xff00)|(getByte()&0xff); + return foo; + } + public int getByte() { + return (buffer[s++]&0xff); + } + public void getByte(byte[] foo) { + getByte(foo, 0, foo.length); + } + void getByte(byte[] foo, int start, int len) { + System.arraycopy(buffer, s, foo, start, len); + s+=len; + } + public int getByte(int len) { + int foo=s; + s+=len; + return foo; + } + public byte[] getMPInt() { + int i=getInt(); // uint32 + if(i<0 || // bigger than 0x7fffffff + i>8*1024){ + // TODO: an exception should be thrown. + i = 8*1024; // the session will be broken, but working around OOME. + } + byte[] foo=new byte[i]; + getByte(foo, 0, i); + return foo; + } + public byte[] getMPIntBits() { + int bits=getInt(); + int bytes=(bits+7)/8; + byte[] foo=new byte[bytes]; + getByte(foo, 0, bytes); + if((foo[0]&0x80)!=0){ + byte[] bar=new byte[foo.length+1]; + bar[0]=0; // ?? + System.arraycopy(foo, 0, bar, 1, foo.length); + foo=bar; + } + return foo; + } + public byte[] getString() { + int i = getInt(); // uint32 + if(i<0 || // bigger than 0x7fffffff + i>256*1024){ + // TODO: an exception should be thrown. + i = 256*1024; // the session will be broken, but working around OOME. + } + byte[] foo=new byte[i]; + getByte(foo, 0, i); + return foo; + } + byte[] getString(int[]start, int[]len) { + int i=getInt(); + start[0]=getByte(i); + len[0]=i; + return buffer; + } + public void reset(){ + index=0; + s=0; + } + public void shift(){ + if(s==0)return; + System.arraycopy(buffer, s, buffer, 0, index-s); + index=index-s; + s=0; + } + void rewind(){ + s=0; + } + + byte getCommand(){ + return buffer[5]; + } + + void checkFreeSize(int n){ + int size = index+n+Session.buffer_margin; + if(buffer.length>>4)&0xf]); + System.err.print(chars[foo&0xf]); + if(i%16==15){ + System.err.println(""); + continue; + } + if(i>0 && i%2==1){ + System.err.print(" "); + } + } + System.err.println(""); + } + static void dump(byte[] b){ + dump(b, 0, b.length); + } + static void dump(byte[] b, int s, int l){ + for(int i=s; i0) + notifyAll(); + } + int getRecipient(){ + return recipient; + } + + void init() throws JSchException { + } + + public void connect() throws JSchException{ + connect(0); + } + + public void connect(int connectTimeout) throws JSchException{ + this.connectTimeout=connectTimeout; + try{ + sendChannelOpen(); + start(); + } + catch(Exception e){ + connected=false; + disconnect(); + if(e instanceof JSchException) + throw (JSchException)e; + throw new JSchException(e.toString(), e); + } + } + + public void setXForwarding(boolean foo){ + } + + public void start() throws JSchException{} + + public boolean isEOF() {return eof_remote;} + + void getData(Buffer buf){ + setRecipient(buf.getInt()); + setRemoteWindowSize(buf.getUInt()); + setRemotePacketSize(buf.getInt()); + } + + public void setInputStream(InputStream in){ + io.setInputStream(in, false); + } + public void setInputStream(InputStream in, boolean dontclose){ + io.setInputStream(in, dontclose); + } + public void setOutputStream(OutputStream out){ + io.setOutputStream(out, false); + } + public void setOutputStream(OutputStream out, boolean dontclose){ + io.setOutputStream(out, dontclose); + } + public void setExtOutputStream(OutputStream out){ + io.setExtOutputStream(out, false); + } + public void setExtOutputStream(OutputStream out, boolean dontclose){ + io.setExtOutputStream(out, dontclose); + } + public InputStream getInputStream() throws IOException { + int max_input_buffer_size = 32*1024; + try { + max_input_buffer_size = + Integer.parseInt(getSession().getConfig("max_input_buffer_size")); + } + catch(Exception e){} + PipedInputStream in = + new MyPipedInputStream( + 32*1024, // this value should be customizable. + max_input_buffer_size + ); + boolean resizable = 32*10240){ + int _l=l; + if(l>_bufl-(14+dataLen)-Session.buffer_margin){ + _l=_bufl-(14+dataLen)-Session.buffer_margin; + } + + if(_l<=0){ + flush(); + continue; + } + + System.arraycopy(buf, s, _buf, 14+dataLen, _l); + dataLen+=_l; + s+=_l; + l-=_l; + } + } + + public void flush() throws java.io.IOException{ + if(closed){ + throw new java.io.IOException("Already closed"); + } + if(dataLen==0) + return; + packet.reset(); + buffer.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buffer.putInt(recipient); + buffer.putInt(dataLen); + buffer.skip(dataLen); + try{ + int foo=dataLen; + dataLen=0; + synchronized(channel){ + if(!channel.close) + getSession().write(packet, channel, foo); + } + } + catch(Exception e){ + close(); + throw new java.io.IOException(e.toString()); + } + + } + public void close() throws java.io.IOException{ + if(packet==null){ + try{ + init(); + } + catch(java.io.IOException e){ + // close should be finished silently. + return; + } + } + if(closed){ + return; + } + if(dataLen>0){ + flush(); + } + channel.eof(); + closed=true; + } + }; + return out; + } + + class MyPipedInputStream extends PipedInputStream{ + private int BUFFER_SIZE = 1024; + private int max_buffer_size = BUFFER_SIZE; + MyPipedInputStream() throws IOException{ super(); } + MyPipedInputStream(int size) throws IOException{ + super(); + buffer=new byte[size]; + BUFFER_SIZE = size; + max_buffer_size = size; + } + MyPipedInputStream(int size, int max_buffer_size) throws IOException{ + this(size); + this.max_buffer_size = max_buffer_size; + } + MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); } + MyPipedInputStream(PipedOutputStream out, int size) throws IOException{ + super(out); + buffer=new byte[size]; + BUFFER_SIZE=size; + } + + /* + * TODO: We should have our own Piped[I/O]Stream implementation. + * Before accepting data, JDK's PipedInputStream will check the existence of + * reader thread, and if it is not alive, the stream will be closed. + * That behavior may cause the problem if multiple threads make access to it. + */ + public synchronized void updateReadSide() throws IOException { + if(available() != 0){ // not empty + return; + } + in = 0; + out = 0; + buffer[in++] = 0; + read(); + } + + private int freeSpace(){ + int size = 0; + if(out < in) { + size = buffer.length-in; + } + else if(in < out){ + if(in == -1) size = buffer.length; + else size = out - in; + } + return size; + } + synchronized void checkSpace(int len) throws IOException { + int size = freeSpace(); + if(size max_buffer_size){ + foo = max_buffer_size; + } + if((foo - datasize) < len) return; + + byte[] tmp = new byte[foo]; + if(out < in) { + System.arraycopy(buffer, 0, tmp, 0, buffer.length); + } + else if(in < out){ + if(in == -1) { + } + else { + System.arraycopy(buffer, 0, tmp, 0, in); + System.arraycopy(buffer, out, + tmp, tmp.length-(buffer.length-out), + (buffer.length-out)); + out = tmp.length-(buffer.length-out); + } + } + else if(in == out){ + System.arraycopy(buffer, 0, tmp, 0, buffer.length); + in=buffer.length; + } + buffer=tmp; + } + else if(buffer.length == size && size > BUFFER_SIZE) { + int i = size/2; + if(i0) + notifyAll(); + } + void setRemotePacketSize(int foo){ this.rmpsize=foo; } + + public void run(){ + } + + void write(byte[] foo) throws IOException { + write(foo, 0, foo.length); + } + void write(byte[] foo, int s, int l) throws IOException { + try{ + io.put(foo, s, l); + }catch(NullPointerException e){} + } + void write_ext(byte[] foo, int s, int l) throws IOException { + try{ + io.put_ext(foo, s, l); + }catch(NullPointerException e){} + } + + void eof_remote(){ + eof_remote=true; + try{ + io.out_close(); + } + catch(NullPointerException e){} + } + + void eof(){ + if(eof_local)return; + eof_local=true; + + int i = getRecipient(); + if(i == -1) return; + + try{ + Buffer buf=new Buffer(100); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF); + buf.putInt(i); + synchronized(this){ + if(!close) + getSession().write(packet); + } + } + catch(Exception e){ + //System.err.println("Channel.eof"); + //e.printStackTrace(); + } + /* + if(!isConnected()){ disconnect(); } + */ + } + + /* + http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt + +5.3 Closing a Channel + When a party will no longer send more data to a channel, it SHOULD + send SSH_MSG_CHANNEL_EOF. + + byte SSH_MSG_CHANNEL_EOF + uint32 recipient_channel + + No explicit response is sent to this message. However, the + application may send EOF to whatever is at the other end of the + channel. Note that the channel remains open after this message, and + more data may still be sent in the other direction. This message + does not consume window space and can be sent even if no window space + is available. + + When either party wishes to terminate the channel, it sends + SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST + send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this + message for the channel. The channel is considered closed for a + party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and + the party may then reuse the channel number. A party MAY send + SSH_MSG_CHANNEL_CLOSE without having sent or received + SSH_MSG_CHANNEL_EOF. + + byte SSH_MSG_CHANNEL_CLOSE + uint32 recipient_channel + + This message does not consume window space and can be sent even if no + window space is available. + + It is recommended that any data sent before this message is delivered + to the actual destination, if possible. + */ + + void close(){ + if(close)return; + close=true; + eof_local=eof_remote=true; + + int i = getRecipient(); + if(i == -1) return; + + try{ + Buffer buf=new Buffer(100); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE); + buf.putInt(i); + synchronized(this){ + getSession().write(packet); + } + } + catch(Exception e){ + //e.printStackTrace(); + } + } + public boolean isClosed(){ + return close; + } + static void disconnect(Session session){ + Channel[] channels=null; + int count=0; + synchronized(pool){ + channels=new Channel[pool.size()]; + for(int i=0; i0){ + if(timeout>0L){ + if((System.currentTimeMillis()-start)>timeout){ + retry=0; + continue; + } + } + try{ + long t = timeout==0L ? 10L : timeout; + this.notifyme=1; + wait(t); + } + catch(java.lang.InterruptedException e){ + } + finally{ + this.notifyme=0; + } + retry--; + } + } + if(!_session.isConnected()){ + throw new JSchException("session is down"); + } + if(this.getRecipient()==-1){ // timeout + throw new JSchException("channel is not opened."); + } + if(this.open_confirmation==false){ // SSH_MSG_CHANNEL_OPEN_FAILURE + throw new JSchException("channel is not opened."); + } + connected=true; + } +} diff --git a/src/main/java/com/jcraft/jsch/ChannelAgentForwarding.java b/src/main/java/com/jcraft/jsch/ChannelAgentForwarding.java new file mode 100644 index 00000000..3c1672b1 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ChannelAgentForwarding.java @@ -0,0 +1,266 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; +import java.util.Vector; + +class ChannelAgentForwarding extends Channel{ + + static private final int LOCAL_WINDOW_SIZE_MAX=0x20000; + static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000; + + private final byte SSH_AGENTC_REQUEST_RSA_IDENTITIES = 1; + private final byte SSH_AGENT_RSA_IDENTITIES_ANSWER = 2; + private final byte SSH_AGENTC_RSA_CHALLENGE = 3; + private final byte SSH_AGENT_RSA_RESPONSE = 4; + private final byte SSH_AGENT_FAILURE = 5; + private final byte SSH_AGENT_SUCCESS = 6; + private final byte SSH_AGENTC_ADD_RSA_IDENTITY = 7; + private final byte SSH_AGENTC_REMOVE_RSA_IDENTITY = 8; + private final byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9; + + private final byte SSH2_AGENTC_REQUEST_IDENTITIES=11; + private final byte SSH2_AGENT_IDENTITIES_ANSWER=12; + private final byte SSH2_AGENTC_SIGN_REQUEST=13; + private final byte SSH2_AGENT_SIGN_RESPONSE=14; + private final byte SSH2_AGENTC_ADD_IDENTITY=17; + private final byte SSH2_AGENTC_REMOVE_IDENTITY=18; + private final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES=19; + private final byte SSH2_AGENT_FAILURE=30; + + boolean init=true; + + private Buffer rbuf=null; + private Buffer wbuf=null; + private Packet packet=null; + private Buffer mbuf=null; + + ChannelAgentForwarding(){ + super(); + + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + + type=Util.str2byte("auth-agent@openssh.com"); + rbuf=new Buffer(); + rbuf.reset(); + //wbuf=new Buffer(rmpsize); + //packet=new Packet(wbuf); + mbuf=new Buffer(); + connected=true; + } + + public void run(){ + try{ + sendOpenConfirmation(); + } + catch(Exception e){ + close=true; + disconnect(); + } + } + + void write(byte[] foo, int s, int l) throws java.io.IOException { + + if(packet==null){ + wbuf=new Buffer(rmpsize); + packet=new Packet(wbuf); + } + + rbuf.shift(); + if(rbuf.buffer.lengthrbuf.getLength()){ + rbuf.s-=4; + return; + } + + int typ=rbuf.getByte(); + + Session _session=null; + try{ + _session=getSession(); + } + catch(JSchException e){ + throw new java.io.IOException(e.toString()); + } + + IdentityRepository irepo = _session.getIdentityRepository(); + UserInfo userinfo=_session.getUserInfo(); + + mbuf.reset(); + + if(typ==SSH2_AGENTC_REQUEST_IDENTITIES){ + mbuf.putByte(SSH2_AGENT_IDENTITIES_ANSWER); + Vector identities = irepo.getIdentities(); + synchronized(identities){ + int count=0; + for(int i=0; iname and value are needed to be passed + * to the remote in your favorite encoding, + * use {@link #setEnv(byte[], byte[])}. + * Refer to RFC4254 6.4 Environment Variable Passing. + * + * @param name A name for environment variable. + * @param value A value for environment variable. + */ + public void setEnv(String name, String value){ + setEnv(Util.str2byte(name), Util.str2byte(value)); + } + + /** + * Set the environment variable. + * Refer to RFC4254 6.4 Environment Variable Passing. + * + * @param name A name of environment variable. + * @param value A value of environment variable. + * @see #setEnv(String, String) + */ + public void setEnv(byte[] name, byte[] value){ + synchronized(this){ + getEnv().put(name, value); + } + } + + private Hashtable getEnv(){ + if(env==null) + env=new Hashtable(); + return env; + } + + /** + * Allocate a Pseudo-Terminal. + * Refer to RFC4254 6.2. Requesting a Pseudo-Terminal. + * + * @param enable + */ + public void setPty(boolean enable){ + pty=enable; + } + + /** + * Set the terminal mode. + * + * @param terminal_mode + */ + public void setTerminalMode(byte[] terminal_mode){ + this.terminal_mode=terminal_mode; + } + + /** + * Change the window dimension interactively. + * Refer to RFC4254 6.7. Window Dimension Change Message. + * + * @param col terminal width, columns + * @param row terminal height, rows + * @param wp terminal width, pixels + * @param hp terminal height, pixels + */ + public void setPtySize(int col, int row, int wp, int hp){ + setPtyType(this.ttype, col, row, wp, hp); + if(!pty || !isConnected()){ + return; + } + try{ + RequestWindowChange request=new RequestWindowChange(); + request.setSize(col, row, wp, hp); + request.request(getSession(), this); + } + catch(Exception e){ + //System.err.println("ChannelSessio.setPtySize: "+e); + } + } + + /** + * Set the terminal type. + * This method is not effective after Channel#connect(). + * + * @param ttype terminal type(for example, "vt100") + * @see #setPtyType(String, int, int, int, int) + */ + public void setPtyType(String ttype){ + setPtyType(ttype, 80, 24, 640, 480); + } + + /** + * Set the terminal type. + * This method is not effective after Channel#connect(). + * + * @param ttype terminal type(for example, "vt100") + * @param col terminal width, columns + * @param row terminal height, rows + * @param wp terminal width, pixels + * @param hp terminal height, pixels + */ + public void setPtyType(String ttype, int col, int row, int wp, int hp){ + this.ttype=ttype; + this.tcol=col; + this.trow=row; + this.twp=wp; + this.thp=hp; + } + + protected void sendRequests() throws Exception{ + Session _session=getSession(); + Request request; + if(agent_forwarding){ + request=new RequestAgentForwarding(); + request.request(_session, this); + } + + if(xforwading){ + request=new RequestX11(); + request.request(_session, this); + } + + if(pty){ + request=new RequestPtyReq(); + ((RequestPtyReq)request).setTType(ttype); + ((RequestPtyReq)request).setTSize(tcol, trow, twp, thp); + if(terminal_mode!=null){ + ((RequestPtyReq)request).setTerminalMode(terminal_mode); + } + request.request(_session, this); + } + + if(env!=null){ + for(Enumeration _env=env.keys(); _env.hasMoreElements();){ + Object name=_env.nextElement(); + Object value=env.get(name); + request=new RequestEnv(); + ((RequestEnv)request).setEnv(toByteArray(name), + toByteArray(value)); + request.request(_session, this); + } + } + } + + private byte[] toByteArray(Object o){ + if(o instanceof String){ + return Util.str2byte((String)o); + } + return (byte[])o; + } + + public void run(){ + //System.err.println(this+":run >"); + + Buffer buf=new Buffer(rmpsize); + Packet packet=new Packet(buf); + int i=-1; + try{ + while(isConnected() && + thread!=null && + io!=null && + io.in!=null){ + i=io.in.read(buf.buffer, + 14, + buf.buffer.length-14 + -Session.buffer_margin + ); + if(i==0)continue; + if(i==-1){ + eof(); + break; + } + if(close)break; + //System.out.println("write: "+i); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + getSession().write(packet, this, i); + } + } + catch(Exception e){ + //System.err.println("# ChannelExec.run"); + //e.printStackTrace(); + } + Thread _thread=thread; + if(_thread!=null){ + synchronized(_thread){ _thread.notifyAll(); } + } + thread=null; + //System.err.println(this+":run <"); + } +} diff --git a/src/main/java/com/jcraft/jsch/ChannelSftp.java b/src/main/java/com/jcraft/jsch/ChannelSftp.java new file mode 100644 index 00000000..f76d1d5d --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ChannelSftp.java @@ -0,0 +1,3055 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +import java.util.Vector; + +public class ChannelSftp extends ChannelSession{ + + static private final int LOCAL_MAXIMUM_PACKET_SIZE=32*1024; + static private final int LOCAL_WINDOW_SIZE_MAX=(64*LOCAL_MAXIMUM_PACKET_SIZE); + + private static final byte SSH_FXP_INIT= 1; + private static final byte SSH_FXP_VERSION= 2; + private static final byte SSH_FXP_OPEN= 3; + private static final byte SSH_FXP_CLOSE= 4; + private static final byte SSH_FXP_READ= 5; + private static final byte SSH_FXP_WRITE= 6; + private static final byte SSH_FXP_LSTAT= 7; + private static final byte SSH_FXP_FSTAT= 8; + private static final byte SSH_FXP_SETSTAT= 9; + private static final byte SSH_FXP_FSETSTAT= 10; + private static final byte SSH_FXP_OPENDIR= 11; + private static final byte SSH_FXP_READDIR= 12; + private static final byte SSH_FXP_REMOVE= 13; + private static final byte SSH_FXP_MKDIR= 14; + private static final byte SSH_FXP_RMDIR= 15; + private static final byte SSH_FXP_REALPATH= 16; + private static final byte SSH_FXP_STAT= 17; + private static final byte SSH_FXP_RENAME= 18; + private static final byte SSH_FXP_READLINK= 19; + private static final byte SSH_FXP_SYMLINK= 20; + private static final byte SSH_FXP_STATUS= 101; + private static final byte SSH_FXP_HANDLE= 102; + private static final byte SSH_FXP_DATA= 103; + private static final byte SSH_FXP_NAME= 104; + private static final byte SSH_FXP_ATTRS= 105; + private static final byte SSH_FXP_EXTENDED= (byte)200; + private static final byte SSH_FXP_EXTENDED_REPLY= (byte)201; + + // pflags + private static final int SSH_FXF_READ= 0x00000001; + private static final int SSH_FXF_WRITE= 0x00000002; + private static final int SSH_FXF_APPEND= 0x00000004; + private static final int SSH_FXF_CREAT= 0x00000008; + private static final int SSH_FXF_TRUNC= 0x00000010; + private static final int SSH_FXF_EXCL= 0x00000020; + + private static final int SSH_FILEXFER_ATTR_SIZE= 0x00000001; + private static final int SSH_FILEXFER_ATTR_UIDGID= 0x00000002; + private static final int SSH_FILEXFER_ATTR_PERMISSIONS= 0x00000004; + private static final int SSH_FILEXFER_ATTR_ACMODTIME= 0x00000008; + private static final int SSH_FILEXFER_ATTR_EXTENDED= 0x80000000; + + public static final int SSH_FX_OK= 0; + public static final int SSH_FX_EOF= 1; + public static final int SSH_FX_NO_SUCH_FILE= 2; + public static final int SSH_FX_PERMISSION_DENIED= 3; + public static final int SSH_FX_FAILURE= 4; + public static final int SSH_FX_BAD_MESSAGE= 5; + public static final int SSH_FX_NO_CONNECTION= 6; + public static final int SSH_FX_CONNECTION_LOST= 7; + public static final int SSH_FX_OP_UNSUPPORTED= 8; +/* + SSH_FX_OK + Indicates successful completion of the operation. + SSH_FX_EOF + indicates end-of-file condition; for SSH_FX_READ it means that no + more data is available in the file, and for SSH_FX_READDIR it + indicates that no more files are contained in the directory. + SSH_FX_NO_SUCH_FILE + is returned when a reference is made to a file which should exist + but doesn't. + SSH_FX_PERMISSION_DENIED + is returned when the authenticated user does not have sufficient + permissions to perform the operation. + SSH_FX_FAILURE + is a generic catch-all error message; it should be returned if an + error occurs for which there is no more specific error code + defined. + SSH_FX_BAD_MESSAGE + may be returned if a badly formatted packet or protocol + incompatibility is detected. + SSH_FX_NO_CONNECTION + is a pseudo-error which indicates that the client has no + connection to the server (it can only be generated locally by the + client, and MUST NOT be returned by servers). + SSH_FX_CONNECTION_LOST + is a pseudo-error which indicates that the connection to the + server has been lost (it can only be generated locally by the + client, and MUST NOT be returned by servers). + SSH_FX_OP_UNSUPPORTED + indicates that an attempt was made to perform an operation which + is not supported for the server (it may be generated locally by + the client if e.g. the version number exchange indicates that a + required feature is not supported by the server, or it may be + returned by the server if the server does not implement an + operation). +*/ + private static final int MAX_MSG_LENGTH = 256* 1024; + + public static final int OVERWRITE=0; + public static final int RESUME=1; + public static final int APPEND=2; + + private boolean interactive=false; + private int seq=1; + private int[] ackid=new int[1]; + + private Buffer buf; + private Packet packet; + + // The followings will be used in file uploading. + private Buffer obuf; + private Packet opacket; + + private int client_version=3; + private int server_version=3; + private String version=String.valueOf(client_version); + + private java.util.Hashtable extensions=null; + private InputStream io_in=null; + + private boolean extension_posix_rename = false; + private boolean extension_statvfs = false; + // private boolean extension_fstatvfs = false; + private boolean extension_hardlink = false; + +/* +10. Changes from previous protocol versions + The SSH File Transfer Protocol has changed over time, before it's + standardization. The following is a description of the incompatible + changes between different versions. +10.1 Changes between versions 3 and 2 + o The SSH_FXP_READLINK and SSH_FXP_SYMLINK messages were added. + o The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages were added. + o The SSH_FXP_STATUS message was changed to include fields `error + message' and `language tag'. +10.2 Changes between versions 2 and 1 + o The SSH_FXP_RENAME message was added. +10.3 Changes between versions 1 and 0 + o Implementation changes, no actual protocol changes. +*/ + + private static final String file_separator=java.io.File.separator; + private static final char file_separatorc=java.io.File.separatorChar; + private static boolean fs_is_bs=(byte)java.io.File.separatorChar == '\\'; + + private String cwd; + private String home; + private String lcwd; + + private static final String UTF8="UTF-8"; + private String fEncoding=UTF8; + private boolean fEncoding_is_utf8=true; + + private RequestQueue rq = new RequestQueue(16); + + /** + * Specify how many requests may be sent at any one time. + * Increasing this value may slightly improve file transfer speed but will + * increase memory usage. The default is 16 requests. + * + * @param bulk_requests how many requests may be outstanding at any one time. + */ + public void setBulkRequests(int bulk_requests) throws JSchException { + if(bulk_requests>0) + rq = new RequestQueue(bulk_requests); + else + throw new JSchException("setBulkRequests: "+ + bulk_requests+" must be greater than 0."); + } + + /** + * This method will return the value how many requests may be + * sent at any one time. + * + * @return how many requests may be sent at any one time. + */ + public int getBulkRequests(){ + return rq.size(); + } + + public ChannelSftp(){ + super(); + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + } + + void init(){ + } + + public void start() throws JSchException{ + try{ + + PipedOutputStream pos=new PipedOutputStream(); + io.setOutputStream(pos); + PipedInputStream pis=new MyPipedInputStream(pos, rmpsize); + io.setInputStream(pis); + + io_in=io.in; + + if(io_in==null){ + throw new JSchException("channel is down"); + } + + Request request=new RequestSftp(); + request.request(getSession(), this); + + /* + System.err.println("lmpsize: "+lmpsize); + System.err.println("lwsize: "+lwsize); + System.err.println("rmpsize: "+rmpsize); + System.err.println("rwsize: "+rwsize); + */ + + buf=new Buffer(lmpsize); + packet=new Packet(buf); + + obuf=new Buffer(rmpsize); + opacket=new Packet(obuf); + + int i=0; + int length; + int type; + byte[] str; + + // send SSH_FXP_INIT + sendINIT(); + + // receive SSH_FXP_VERSION + Header header=new Header(); + header=header(buf, header); + length=header.length; + if(length > MAX_MSG_LENGTH){ + throw new SftpException(SSH_FX_FAILURE, + "Received message is too long: " + length); + } + type=header.type; // 2 -> SSH_FXP_VERSION + server_version=header.rid; + //System.err.println("SFTP protocol server-version="+server_version); + extensions=new java.util.Hashtable(); + if(length>0){ + // extension data + fill(buf, length); + byte[] extension_name=null; + byte[] extension_data=null; + while(length>0){ + extension_name=buf.getString(); + length-=(4+extension_name.length); + extension_data=buf.getString(); + length-=(4+extension_data.length); + extensions.put(Util.byte2str(extension_name), + Util.byte2str(extension_data)); + } + } + + if(extensions.get("posix-rename@openssh.com")!=null && + extensions.get("posix-rename@openssh.com").equals("1")){ + extension_posix_rename = true; + } + + if(extensions.get("statvfs@openssh.com")!=null && + extensions.get("statvfs@openssh.com").equals("2")){ + extension_statvfs = true; + } + + /* + if(extensions.get("fstatvfs@openssh.com")!=null && + extensions.get("fstatvfs@openssh.com").equals("2")){ + extension_fstatvfs = true; + } + */ + + if(extensions.get("hardlink@openssh.com")!=null && + extensions.get("hardlink@openssh.com").equals("1")){ + extension_hardlink = true; + } + + lcwd=new File(".").getCanonicalPath(); + } + catch(Exception e){ + //System.err.println(e); + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + public void quit(){ disconnect();} + public void exit(){ disconnect();} + public void lcd(String path) throws SftpException{ + path=localAbsolutePath(path); + if((new File(path)).isDirectory()){ + try{ + path=(new File(path)).getCanonicalPath(); + } + catch(Exception e){} + lcwd=path; + return; + } + throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory"); + } + + public void cd(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + path=isUnique(path); + + byte[] str=_realpath(path); + SftpATTRS attr=_stat(str); + + if((attr.getFlags()&SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS)==0){ + throw new SftpException(SSH_FX_FAILURE, + "Can't change directory: "+path); + } + if(!attr.isDir()){ + throw new SftpException(SSH_FX_FAILURE, + "Can't change directory: "+path); + } + + setCwd(Util.byte2str(str, fEncoding)); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void put(String src, String dst) throws SftpException{ + put(src, dst, null, OVERWRITE); + } + public void put(String src, String dst, int mode) throws SftpException{ + put(src, dst, null, mode); + } + public void put(String src, String dst, + SftpProgressMonitor monitor) throws SftpException{ + put(src, dst, monitor, OVERWRITE); + } + + /** + * Sends data from src file to dst file. + * The mode should be OVERWRITE, + * RESUME or APPEND. + * + * @param src source file + * @param dst destination file + * @param monitor progress monitor + * @param mode how data should be added to dst + */ + public void put(String src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=localAbsolutePath(src); + dst=remoteAbsolutePath(dst); + + Vector v=glob_remote(dst); + int vsize=v.size(); + if(vsize!=1){ + if(vsize==0){ + if(isPattern(dst)) + throw new SftpException(SSH_FX_FAILURE, dst); + else + dst=Util.unquote(dst); + } + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } + else{ + dst=(String)(v.elementAt(0)); + } + + boolean isRemoteDir=isRemoteDir(dst); + + v=glob_local(src); + vsize=v.size(); + + StringBuffer dstsb=null; + if(isRemoteDir){ + if(!dst.endsWith("/")){ + dst+="/"; + } + dstsb=new StringBuffer(dst); + } + else if(vsize>1){ + throw new SftpException(SSH_FX_FAILURE, + "Copying multiple files, but the destination is missing or a file."); + } + + for(int j=0; ji) + i=ii; + } + if(i==-1) dstsb.append(_src); + else dstsb.append(_src.substring(i + 1)); + _dst=dstsb.toString(); + dstsb.delete(dst.length(), _dst.length()); + } + else{ + _dst=dst; + } + //System.err.println("_dst "+_dst); + + long size_of_dst=0; + if(mode==RESUME){ + try{ + SftpATTRS attr=_stat(_dst); + size_of_dst=attr.getSize(); + } + catch(Exception eee){ + //System.err.println(eee); + } + long size_of_src=new File(_src).length(); + if(size_of_srcsrc to dst file. + * The mode should be OVERWRITE, + * RESUME or APPEND. + * + * @param src input stream + * @param dst destination file + * @param monitor progress monitor + * @param mode how data should be added to dst + */ + public void put(InputStream src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + dst=remoteAbsolutePath(dst); + + Vector v=glob_remote(dst); + int vsize=v.size(); + if(vsize!=1){ + if(vsize==0){ + if(isPattern(dst)) + throw new SftpException(SSH_FX_FAILURE, dst); + else + dst=Util.unquote(dst); + } + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } + else{ + dst=(String)(v.elementAt(0)); + } + + if(monitor!=null){ + monitor.init(SftpProgressMonitor.PUT, + "-", dst, + SftpProgressMonitor.UNKNOWN_SIZE); + } + + _put(src, dst, monitor, mode); + } + catch(Exception e){ + if(e instanceof SftpException) { + if(((SftpException)e).id == SSH_FX_FAILURE && + isRemoteDir(dst)) { + throw new SftpException(SSH_FX_FAILURE, dst+" is a directory"); + } + throw (SftpException)e; + } + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, e.toString()); + } + } + + public void _put(InputStream src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + byte[] dstb=Util.str2byte(dst, fEncoding); + long skip=0; + if(mode==RESUME || mode==APPEND){ + try{ + SftpATTRS attr=_stat(dstb); + skip=attr.getSize(); + } + catch(Exception eee){ + //System.err.println(eee); + } + } + if(mode==RESUME && skip>0){ + long skipped=src.skip(skip); + if(skipped0){ + int sent=sendWRITE(handle, _offset[0], d, s, _len); + writecount++; + _offset[0]+=sent; + s+=sent; + _len-=sent; + if((seq-1)==startid || + io_in.available()>=1024){ + while(io_in.available()>0){ + if(checkStatus(ackid, header)){ + _ackid=ackid[0]; + if(startid>_ackid || _ackid>seq-1){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + ackcount++; + } + else{ + break; + } + } + } + } + if(monitor!=null && !monitor.count(len)){ + close(); + throw new IOException("canceled"); + } + } + catch(IOException e){ throw e; } + catch(Exception e){ throw new IOException(e.toString()); } + } + + byte[] _data=new byte[1]; + public void write(int foo) throws java.io.IOException{ + _data[0]=(byte)foo; + write(_data, 0, 1); + } + + public void flush() throws java.io.IOException{ + + if(isClosed){ + throw new IOException("stream already closed"); + } + + if(!init){ + try{ + while(writecount>ackcount){ + if(!checkStatus(null, header)){ + break; + } + ackcount++; + } + } + catch(SftpException e){ + throw new IOException(e.toString()); + } + } + } + + public void close() throws java.io.IOException{ + if(isClosed){ + return; + } + flush(); + if(monitor!=null)monitor.end(); + try{ _sendCLOSE(handle, header); } + catch(IOException e){ throw e; } + catch(Exception e){ + throw new IOException(e.toString()); + } + isClosed=true; + } + }; + return out; + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public void get(String src, String dst) throws SftpException{ + get(src, dst, null, OVERWRITE); + } + public void get(String src, String dst, + SftpProgressMonitor monitor) throws SftpException{ + get(src, dst, monitor, OVERWRITE); + } + public void get(String src, String dst, + SftpProgressMonitor monitor, int mode) throws SftpException{ + // System.out.println("get: "+src+" "+dst); + + boolean _dstExist = false; + String _dst=null; + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=remoteAbsolutePath(src); + dst=localAbsolutePath(dst); + + Vector v=glob_remote(src); + int vsize=v.size(); + if(vsize==0){ + throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such file"); + } + + File dstFile=new File(dst); + boolean isDstDir=dstFile.isDirectory(); + StringBuffer dstsb=null; + if(isDstDir){ + if(!dst.endsWith(file_separator)){ + dst+=file_separator; + } + dstsb=new StringBuffer(dst); + } + else if(vsize>1){ + throw new SftpException(SSH_FX_FAILURE, + "Copying multiple files, but destination is missing or a file."); + } + + for(int j=0; jdstc.length() && + _dstc.substring(0, dstc.length()+1).equals(dstc+file_separator))){ + throw new SftpException(SSH_FX_FAILURE, + "writing to an unexpected file "+_src); + } + } + dstsb.delete(dst.length(), _dst.length()); + } + else{ + _dst=dst; + } + + File _dstFile=new File(_dst); + if(mode==RESUME){ + long size_of_src=attr.getSize(); + long size_of_dst=_dstFile.length(); + if(size_of_dst>size_of_src){ + throw new SftpException(SSH_FX_FAILURE, + "failed to resume for "+_dst); + } + if(size_of_dst==size_of_src){ + return; + } + } + + if(monitor!=null){ + monitor.init(SftpProgressMonitor.GET, _src, _dst, attr.getSize()); + if(mode==RESUME){ + monitor.count(_dstFile.length()); + } + } + + FileOutputStream fos=null; + _dstExist = _dstFile.exists(); + try{ + if(mode==OVERWRITE){ + fos=new FileOutputStream(_dst); + } + else{ + fos=new FileOutputStream(_dst, true); // append + } + // System.err.println("_get: "+_src+", "+_dst); + _get(_src, fos, monitor, mode, new File(_dst).length()); + } + finally{ + if(fos!=null){ + fos.close(); + } + } + } + } + catch(Exception e){ + if(!_dstExist && _dst!=null){ + File _dstFile = new File(_dst); + if(_dstFile.exists() && _dstFile.length()==0){ + _dstFile.delete(); + } + } + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + public void get(String src, OutputStream dst) throws SftpException{ + get(src, dst, null, OVERWRITE, 0); + } + public void get(String src, OutputStream dst, + SftpProgressMonitor monitor) throws SftpException{ + get(src, dst, monitor, OVERWRITE, 0); + } + public void get(String src, OutputStream dst, + SftpProgressMonitor monitor, int mode, long skip) throws SftpException{ +//System.err.println("get: "+src+", "+dst); + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=remoteAbsolutePath(src); + src=isUnique(src); + + if(monitor!=null){ + SftpATTRS attr=_stat(src); + monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); + if(mode==RESUME){ + monitor.count(skip); + } + } + _get(src, dst, monitor, mode, skip); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + private void _get(String src, OutputStream dst, + SftpProgressMonitor monitor, int mode, long skip) throws SftpException{ + //System.err.println("_get: "+src+", "+dst); + + byte[] srcb=Util.str2byte(src, fEncoding); + try{ + sendOPENR(srcb); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + + byte[] handle=buf.getString(); // filename + + long offset=0; + if(mode==RESUME){ + offset+=skip; + } + + int request_max=1; + rq.init(); + long request_offset=offset; + + int request_len = buf.buffer.length-13; + if(server_version==0){ request_len=1024; } + + loop: + while(true){ + + while(rq.count() < request_max){ + sendREAD(handle, request_offset, request_len, rq); + request_offset += request_len; + } + + header=header(buf, header); + length=header.length; + type=header.type; + + RequestQueue.Request rr = null; + try{ + rr = rq.get(header.rid); + } + catch(RequestQueue.OutOfOrderException e){ + request_offset = e.offset; + skip(header.length); + rq.cancel(header, buf); + continue; + } + + if(type==SSH_FXP_STATUS){ + fill(buf, length); + int i=buf.getInt(); + if(i==SSH_FX_EOF){ + break loop; + } + throwStatusError(buf, i); + } + + if(type!=SSH_FXP_DATA){ + break loop; + } + + buf.rewind(); + fill(buf.buffer, 0, 4); length-=4; + int length_of_data = buf.getInt(); // length of data + + /** + Since sftp protocol version 6, "end-of-file" has been defined, + + byte SSH_FXP_DATA + uint32 request-id + string data + bool end-of-file [optional] + + but some sftpd server will send such a field in the sftp protocol 3 ;-( + */ + int optional_data = length - length_of_data; + + int foo = length_of_data; + while(foo>0){ + int bar=foo; + if(bar>buf.buffer.length){ + bar=buf.buffer.length; + } + int data_len = io_in.read(buf.buffer, 0, bar); + if(data_len<0){ + break loop; + } + + dst.write(buf.buffer, 0, data_len); + + offset+=data_len; + foo-=data_len; + + if(monitor!=null){ + if(!monitor.count(data_len)){ + skip(foo); + if(optional_data>0){ + skip(optional_data); + } + break loop; + } + } + + } + //System.err.println("length: "+length); // length should be 0 + + if(optional_data>0){ + skip(optional_data); + } + + if(length_of_data=rrq.length) tail -= rrq.length; + rrq[tail].id=id; + rrq[tail].offset=offset; + rrq[tail].length=length; + count++; + } + + Request get(int id) throws OutOfOrderException, SftpException { + count -= 1; + int i = head; + head++; + if(head==rrq.length) head=0; + if(rrq[i].id != id){ + long offset = getOffset(); + boolean find = false; + for(int j = 0; jrrq[i].offset) + result=rrq[i].offset; + } + + return result; + } + } + + public InputStream get(String src) throws SftpException{ + return get(src, null, 0L); + } + public InputStream get(String src, SftpProgressMonitor monitor) throws SftpException{ + return get(src, monitor, 0L); + } + + /** + * @deprecated This method will be deleted in the future. + */ + public InputStream get(String src, int mode) throws SftpException{ + return get(src, null, 0L); + } + /** + * @deprecated This method will be deleted in the future. + */ + public InputStream get(String src, final SftpProgressMonitor monitor, final int mode) throws SftpException{ + return get(src, monitor, 0L); + } + public InputStream get(String src, final SftpProgressMonitor monitor, final long skip) throws SftpException{ + + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + src=remoteAbsolutePath(src); + src=isUnique(src); + + byte[] srcb=Util.str2byte(src, fEncoding); + + SftpATTRS attr=_stat(srcb); + if(monitor!=null){ + monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); + } + + sendOPENR(srcb); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + + final byte[] handle=buf.getString(); // handle + + rq.init(); + + java.io.InputStream in=new java.io.InputStream(){ + long offset=skip; + boolean closed=false; + int rest_length=0; + byte[] _data=new byte[1]; + byte[] rest_byte=new byte[1024]; + Header header=new Header(); + int request_max=1; + long request_offset=offset; + + public int read() throws java.io.IOException{ + if(closed)return -1; + int i=read(_data, 0, 1); + if (i==-1) { return -1; } + else { + return _data[0]&0xff; + } + } + public int read(byte[] d) throws java.io.IOException{ + if(closed)return -1; + return read(d, 0, d.length); + } + public int read(byte[] d, int s, int len) throws java.io.IOException{ + if(closed)return -1; + if(d==null){throw new NullPointerException();} + if(s<0 || len <0 || s+len>d.length){ + throw new IndexOutOfBoundsException(); + } + if(len==0){ return 0; } + + if(rest_length>0){ + int foo=rest_length; + if(foo>len) foo=len; + System.arraycopy(rest_byte, 0, d, s, foo); + if(foo!=rest_length){ + System.arraycopy(rest_byte, foo, + rest_byte, 0, rest_length-foo); + } + + if(monitor!=null){ + if(!monitor.count(foo)){ + close(); + return -1; + } + } + + rest_length-=foo; + return foo; + } + + if(buf.buffer.length-131024){ + len=1024; + } + + if(rq.count()==0 + || true // working around slow transfer speed for + // some sftp servers including Titan FTP. + ) { + int request_len = buf.buffer.length-13; + if(server_version==0){ request_len=1024; } + + while(rq.count() < request_max){ + try{ + sendREAD(handle, request_offset, request_len, rq); + } + catch(Exception e){ throw new IOException("error"); } + request_offset += request_len; + } + } + + header=header(buf, header); + rest_length=header.length; + int type=header.type; + int id=header.rid; + + RequestQueue.Request rr = null; + try{ + rr = rq.get(header.rid); + } + catch(RequestQueue.OutOfOrderException e){ + request_offset = e.offset; + skip(header.length); + rq.cancel(header, buf); + return 0; + } + catch(SftpException e){ + throw new IOException("error: "+e.toString()); + } + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_DATA){ + throw new IOException("error"); + } + if(type==SSH_FXP_STATUS){ + fill(buf, rest_length); + int i=buf.getInt(); + rest_length=0; + if(i==SSH_FX_EOF){ + close(); + return -1; + } + //throwStatusError(buf, i); + throw new IOException("error"); + } + + buf.rewind(); + fill(buf.buffer, 0, 4); + int length_of_data = buf.getInt(); rest_length-=4; + + /** + Since sftp protocol version 6, "end-of-file" has been defined, + + byte SSH_FXP_DATA + uint32 request-id + string data + bool end-of-file [optional] + + but some sftpd server will send such a field in the sftp protocol 3 ;-( + */ + int optional_data = rest_length - length_of_data; + + offset += length_of_data; + int foo = length_of_data; + if(foo>0){ + int bar=foo; + if(bar>len){ + bar=len; + } + int i=io_in.read(d, s, bar); + if(i<0){ + return -1; + } + foo-=i; + rest_length=foo; + + if(foo>0){ + if(rest_byte.length0){ + j=io_in.read(rest_byte, _s, _len); + if(j<=0)break; + _s+=j; + _len-=j; + } + } + + if(optional_data>0){ + io_in.skip(optional_data); + } + + if(length_of_datapath. + * Each files and directories will be passed to + * LsEntrySelector#select(LsEntry) method, and if that method + * returns LsEntrySelector#BREAK, the operation will be + * canceled immediately. + * + * @see ChannelSftp.LsEntrySelector + * @since 0.1.47 + */ + public void ls(String path, LsEntrySelector selector) throws SftpException{ + //System.out.println("ls: "+path); + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + byte[] pattern=null; + java.util.Vector v=new java.util.Vector(); + + int foo=path.lastIndexOf('/'); + String dir=path.substring(0, ((foo==0)?1:foo)); + String _pattern=path.substring(foo+1); + dir=Util.unquote(dir); + + // If pattern has included '*' or '?', we need to convert + // to UTF-8 string before globbing. + byte[][] _pattern_utf8=new byte[1][]; + boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8); + + if(pattern_has_wildcard){ + pattern=_pattern_utf8[0]; + } + else{ + String upath=Util.unquote(path); + //SftpATTRS attr=_lstat(upath); + SftpATTRS attr=_stat(upath); + if(attr.isDir()){ + pattern=null; + dir=upath; + } + else{ + /* + // If we can generage longname by ourself, + // we don't have to use openDIR. + String filename=Util.unquote(_pattern); + String longname=... + v.addElement(new LsEntry(filename, longname, attr)); + return v; + */ + + if(fEncoding_is_utf8){ + pattern=_pattern_utf8[0]; + pattern=Util.unquote(pattern); + } + else{ + _pattern=Util.unquote(_pattern); + pattern=Util.str2byte(_pattern, fEncoding); + } + + } + } + + sendOPENDIR(Util.str2byte(dir, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + int i=buf.getInt(); + throwStatusError(buf, i); + } + + int cancel = LsEntrySelector.CONTINUE; + byte[] handle=buf.getString(); // handle + + while(cancel==LsEntrySelector.CONTINUE){ + + sendREADDIR(handle); + + header=header(buf, header); + length=header.length; + type=header.type; + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_STATUS){ + fill(buf, length); + int i=buf.getInt(); + if(i==SSH_FX_EOF) + break; + throwStatusError(buf, i); + } + + buf.rewind(); + fill(buf.buffer, 0, 4); length-=4; + int count=buf.getInt(); + + byte[] str; + int flags; + + buf.reset(); + while(count>0){ + if(length>0){ + buf.shift(); + int j=(buf.buffer.length>(buf.index+length)) ? + length : + (buf.buffer.length-buf.index); + int i=fill(buf.buffer, buf.index, j); + buf.index+=i; + length-=i; + } + byte[] filename=buf.getString(); + byte[] longname=null; + if(server_version<=3){ + longname=buf.getString(); + } + SftpATTRS attrs=SftpATTRS.getATTR(buf); + + if(cancel==LsEntrySelector.BREAK){ + count--; + continue; + } + + boolean find=false; + String f=null; + if(pattern==null){ + find=true; + } + else if(!pattern_has_wildcard){ + find=Util.array_equals(pattern, filename); + } + else{ + byte[] _filename=filename; + if(!fEncoding_is_utf8){ + f=Util.byte2str(_filename, fEncoding); + _filename=Util.str2byte(f, UTF8); + } + find=Util.glob(pattern, _filename); + } + + if(find){ + if(f==null){ + f=Util.byte2str(filename, fEncoding); + } + String l=null; + if(longname==null){ + // TODO: we need to generate long name from attrs + // for the sftp protocol 4(and later). + l=attrs.toString()+" "+f; + } + else{ + l=Util.byte2str(longname, fEncoding); + } + + cancel = selector.select(new LsEntry(f, l, attrs)); + } + + count--; + } + } + _sendCLOSE(handle, header); + + /* + if(v.size()==1 && pattern_has_wildcard){ + LsEntry le=(LsEntry)v.elementAt(0); + if(le.getAttrs().isDir()){ + String f=le.getFilename(); + if(isPattern(f)){ + f=Util.quote(f); + } + if(!dir.endsWith("/")){ + dir+="/"; + } + v=null; + return ls(dir+f); + } + } + */ + + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public String readlink(String path) throws SftpException{ + try{ + if(server_version<3){ + throw new SftpException(SSH_FX_OP_UNSUPPORTED, + "The remote sshd is too old to support symlink operation."); + } + + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + path=isUnique(path); + + sendREADLINK(Util.str2byte(path, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + if(type==SSH_FXP_NAME){ + int count=buf.getInt(); // count + byte[] filename=null; + for(int i=0; i=2){ + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } + if(vsize==1){ + newpath=(String)(v.elementAt(0)); + } + else{ // vsize==0 + if(isPattern(newpath)) + throw new SftpException(SSH_FX_FAILURE, newpath); + newpath=Util.unquote(newpath); + } + + sendRENAME(Util.str2byte(oldpath, fEncoding), + Util.str2byte(newpath, fEncoding)); + + Header header=new Header(); + header=header(buf, header); + int length=header.length; + int type=header.type; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i=buf.getInt(); + if(i==SSH_FX_OK) return; + throwStatusError(buf, i); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + public void rm(String path) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + + Header header=new Header(); + + for(int j=0; j0){ + str=buf.getString(); // absolute path; + if(server_version<=3){ + byte[] lname=buf.getString(); // long filename + } + SftpATTRS attr=SftpATTRS.getATTR(buf); // dummy attribute + } + return str; + } + + public void setStat(String path, SftpATTRS attr) throws SftpException{ + try{ + ((MyPipedInputStream)io_in).updateReadSide(); + + path=remoteAbsolutePath(path); + + Vector v=glob_remote(path); + int vsize=v.size(); + for(int j=0; j0){ + i=io_in.read(buf, s, l); + if(i<=0){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + s+=i; + l-=i; + } + } + + private boolean checkStatus(int[] ackid, Header header) throws IOException, SftpException{ + header=header(buf, header); + int length=header.length; + int type=header.type; + if(ackid!=null) + ackid[0]=header.rid; + + fill(buf, length); + + if(type!=SSH_FXP_STATUS){ + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i=buf.getInt(); + if(i!=SSH_FX_OK){ + throwStatusError(buf, i); + } + return true; + } + private boolean _sendCLOSE(byte[] handle, Header header) throws Exception{ + sendCLOSE(handle); + return checkStatus(null, header); + } + + private void sendINIT() throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_INIT, 5); + buf.putInt(3); // version 3 + getSession().write(packet, this, 5+4); + } + + private void sendREALPATH(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_REALPATH, path); + } + private void sendSTAT(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_STAT, path); + } + private void sendSTATVFS(byte[] path) throws Exception{ + sendPacketPath((byte)0, path, "statvfs@openssh.com"); + } + /* + private void sendFSTATVFS(byte[] handle) throws Exception{ + sendPacketPath((byte)0, handle, "fstatvfs@openssh.com"); + } + */ + private void sendLSTAT(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_LSTAT, path); + } + private void sendFSTAT(byte[] handle) throws Exception{ + sendPacketPath(SSH_FXP_FSTAT, handle); + } + private void sendSETSTAT(byte[] path, SftpATTRS attr) throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_SETSTAT, 9+path.length+attr.length()); + buf.putInt(seq++); + buf.putString(path); // path + attr.dump(buf); + getSession().write(packet, this, 9+path.length+attr.length()+4); + } + private void sendREMOVE(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_REMOVE, path); + } + private void sendMKDIR(byte[] path, SftpATTRS attr) throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_MKDIR, 9+path.length+(attr!=null?attr.length():4)); + buf.putInt(seq++); + buf.putString(path); // path + if(attr!=null) attr.dump(buf); + else buf.putInt(0); + getSession().write(packet, this, 9+path.length+(attr!=null?attr.length():4)+4); + } + private void sendRMDIR(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_RMDIR, path); + } + private void sendSYMLINK(byte[] p1, byte[] p2) throws Exception{ + sendPacketPath(SSH_FXP_SYMLINK, p1, p2); + } + private void sendHARDLINK(byte[] p1, byte[] p2) throws Exception{ + sendPacketPath((byte)0, p1, p2, "hardlink@openssh.com"); + } + private void sendREADLINK(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_READLINK, path); + } + private void sendOPENDIR(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_OPENDIR, path); + } + private void sendREADDIR(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_READDIR, path); + } + private void sendRENAME(byte[] p1, byte[] p2) throws Exception{ + sendPacketPath(SSH_FXP_RENAME, p1, p2, + extension_posix_rename ? "posix-rename@openssh.com" : null); + } + private void sendCLOSE(byte[] path) throws Exception{ + sendPacketPath(SSH_FXP_CLOSE, path); + } + private void sendOPENR(byte[] path) throws Exception{ + sendOPEN(path, SSH_FXF_READ); + } + private void sendOPENW(byte[] path) throws Exception{ + sendOPEN(path, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_TRUNC); + } + private void sendOPENA(byte[] path) throws Exception{ + sendOPEN(path, SSH_FXF_WRITE|/*SSH_FXF_APPEND|*/SSH_FXF_CREAT); + } + private void sendOPEN(byte[] path, int mode) throws Exception{ + packet.reset(); + putHEAD(SSH_FXP_OPEN, 17+path.length); + buf.putInt(seq++); + buf.putString(path); + buf.putInt(mode); + buf.putInt(0); // attrs + getSession().write(packet, this, 17+path.length+4); + } + private void sendPacketPath(byte fxp, byte[] path) throws Exception{ + sendPacketPath(fxp, path, (String)null); + } + private void sendPacketPath(byte fxp, byte[] path, String extension) throws Exception{ + packet.reset(); + int len = 9+path.length; + if(extension == null) { + putHEAD(fxp, len); + buf.putInt(seq++); + } + else { + len+=(4+extension.length()); + putHEAD(SSH_FXP_EXTENDED, len); + buf.putInt(seq++); + buf.putString(Util.str2byte(extension)); + } + buf.putString(path); // path + getSession().write(packet, this, len+4); + } + + private void sendPacketPath(byte fxp, byte[] p1, byte[] p2) throws Exception{ + sendPacketPath(fxp, p1, p2, null); + } + private void sendPacketPath(byte fxp, byte[] p1, byte[] p2, String extension) throws Exception{ + packet.reset(); + int len = 13+p1.length+p2.length; + if(extension==null){ + putHEAD(fxp, len); + buf.putInt(seq++); + } + else { + len+=(4+extension.length()); + putHEAD(SSH_FXP_EXTENDED, len); + buf.putInt(seq++); + buf.putString(Util.str2byte(extension)); + } + buf.putString(p1); + buf.putString(p2); + getSession().write(packet, this, len+4); + } + + private int sendWRITE(byte[] handle, long offset, + byte[] data, int start, int length) throws Exception{ + int _length=length; + opacket.reset(); + if(obuf.buffer.length0){ + if(length>0){ + buf.shift(); + int j=(buf.buffer.length>(buf.index+length)) ? length : (buf.buffer.length-buf.index); + i=io_in.read(buf.buffer, buf.index, j); + if(i<=0)break; + buf.index+=i; + length-=i; + } + + byte[] filename=buf.getString(); + //System.err.println("filename: "+new String(filename)); + if(server_version<=3){ + str=buf.getString(); // longname + } + SftpATTRS attrs=SftpATTRS.getATTR(buf); + + byte[] _filename=filename; + String f=null; + boolean found=false; + + if(!fEncoding_is_utf8){ + f=Util.byte2str(filename, fEncoding); + _filename=Util.str2byte(f, UTF8); + } + found=Util.glob(pattern, _filename); + + if(found){ + if(f==null){ + f=Util.byte2str(filename, fEncoding); + } + if(pdir==null){ + pdir=dir; + if(!pdir.endsWith("/")){ + pdir+="/"; + } + } + v.addElement(pdir+f); + } + count--; + } + } + if(_sendCLOSE(handle, header)) + return v; + return null; + } + + private boolean isPattern(byte[] path){ + int length=path.length; + int i=0; + while(i=0){ + if(path[i]!='*' && path[i]!='?'){ + i--; + continue; + } + if(!fs_is_bs && + i>0 && path[i-1]=='\\'){ + i--; + if(i>0 && path[i-1]=='\\'){ + i--; + i--; + continue; + } + } + break; + } + + if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;} + + while(i>=0){ + if(path[i]==file_separatorc || + (fs_is_bs && path[i]=='/')){ // On Windows, '/' is also the separator. + break; + } + i--; + } + + if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;} + + byte[] dir; + if(i==0){dir=new byte[]{(byte)file_separatorc};} + else{ + dir=new byte[i]; + System.arraycopy(path, 0, dir, 0, i); + } + + byte[] pattern=new byte[path.length-i-1]; + System.arraycopy(path, i+1, pattern, 0, pattern.length); + +//System.err.println("dir: "+new String(dir)+" pattern: "+new String(pattern)); + try{ + String[] children=(new File(Util.byte2str(dir, UTF8))).list(); + String pdir=Util.byte2str(dir)+file_separator; + for(int j=0; j=3 && // WindRiver's sftp will send invalid + buf.getLength()>=4){ // SSH_FXP_STATUS packet. + byte[] str=buf.getString(); + //byte[] tag=buf.getString(); + throw new SftpException(i, Util.byte2str(str, UTF8)); + } + else{ + throw new SftpException(i, "Failure"); + } + } + + private static boolean isLocalAbsolutePath(String path){ + return (new File(path)).isAbsolute(); + } + + public void disconnect(){ + super.disconnect(); + } + + private boolean isPattern(String path, byte[][] utf8){ + byte[] _path=Util.str2byte(path, UTF8); + if(utf8!=null) + utf8[0]=_path; + return isPattern(_path); + } + + private boolean isPattern(String path){ + return isPattern(path, null); + } + + private void fill(Buffer buf, int len) throws IOException{ + buf.reset(); + fill(buf.buffer, 0, len); + buf.skip(len); + } + + private int fill(byte[] buf, int s, int len) throws IOException{ + int i=0; + int foo=s; + while(len>0){ + i=io_in.read(buf, s, len); + if(i<=0){ + throw new IOException("inputstream is closed"); + //return (s-foo)==0 ? i : s-foo; + } + s+=i; + len-=i; + } + return s-foo; + } + private void skip(long foo) throws IOException{ + while(foo>0){ + long bar=io_in.skip(foo); + if(bar<=0) + break; + foo-=bar; + } + } + + class Header{ + int length; + int type; + int rid; + } + private Header header(Buffer buf, Header header) throws IOException{ + buf.rewind(); + int i=fill(buf.buffer, 0, 9); + header.length=buf.getInt()-5; + header.type=buf.getByte()&0xff; + header.rid=buf.getInt(); + return header; + } + + private String remoteAbsolutePath(String path) throws SftpException{ + if(path.charAt(0)=='/') return path; + String cwd=getCwd(); +// if(cwd.equals(getHome())) return path; + if(cwd.endsWith("/")) return cwd+path; + return cwd+"/"+path; + } + + private String localAbsolutePath(String path){ + if(isLocalAbsolutePath(path)) return path; + if(lcwd.endsWith(file_separator)) return lcwd+path; + return lcwd+file_separator+path; + } + + /** + * This method will check if the given string can be expanded to the + * unique string. If it can be expanded to mutiple files, SftpException + * will be thrown. + * @return the returned string is unquoted. + */ + private String isUnique(String path) throws SftpException, Exception{ + Vector v=glob_remote(path); + if(v.size()!=1){ + throw new SftpException(SSH_FX_FAILURE, path+" is not unique: "+v.toString()); + } + return (String)(v.elementAt(0)); + } + + public int getServerVersion() throws SftpException{ + if(!isConnected()){ + throw new SftpException(SSH_FX_FAILURE, "The channel is not connected."); + } + return server_version; + } + + public void setFilenameEncoding(String encoding) throws SftpException{ + int sversion=getServerVersion(); + if(3 <= sversion && sversion <= 5 && + !encoding.equals(UTF8)){ + throw new SftpException(SSH_FX_FAILURE, + "The encoding can not be changed for this sftp server."); + } + if(encoding.equals(UTF8)){ + encoding=UTF8; + } + fEncoding=encoding; + fEncoding_is_utf8=fEncoding.equals(UTF8); + } + + public String getExtension(String key){ + if(extensions==null) + return null; + return (String)extensions.get(key); + } + + public String realpath(String path) throws SftpException{ + try{ + byte[] _path=_realpath(remoteAbsolutePath(path)); + return Util.byte2str(_path, fEncoding); + } + catch(Exception e){ + if(e instanceof SftpException) throw (SftpException)e; + if(e instanceof Throwable) + throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); + throw new SftpException(SSH_FX_FAILURE, ""); + } + } + + public class LsEntry implements Comparable{ + private String filename; + private String longname; + private SftpATTRS attrs; + LsEntry(String filename, String longname, SftpATTRS attrs){ + setFilename(filename); + setLongname(longname); + setAttrs(attrs); + } + public String getFilename(){return filename;}; + void setFilename(String filename){this.filename = filename;}; + public String getLongname(){return longname;}; + void setLongname(String longname){this.longname = longname;}; + public SftpATTRS getAttrs(){return attrs;}; + void setAttrs(SftpATTRS attrs) {this.attrs = attrs;}; + public String toString(){ return longname; } + public int compareTo(Object o) throws ClassCastException{ + if(o instanceof LsEntry){ + return filename.compareTo(((LsEntry)o).getFilename()); + } + throw new ClassCastException("a decendent of LsEntry must be given."); + } + } + + /** + * This interface will be passed as an argument for ls method. + * + * @see ChannelSftp.LsEntry + * @see #ls(String, ChannelSftp.LsEntrySelector) + * @since 0.1.47 + */ + public interface LsEntrySelector { + public final int CONTINUE = 0; + public final int BREAK = 1; + + /** + *

The select method will be invoked in ls + * method for each file entry. If this method returns BREAK, + * ls will be canceled. + * + * @param entry one of entry from ls + * @return if BREAK is returned, the 'ls' operation will be canceled. + */ + public int select(LsEntry entry); + } +} diff --git a/src/main/java/com/jcraft/jsch/ChannelShell.java b/src/main/java/com/jcraft/jsch/ChannelShell.java new file mode 100644 index 00000000..5113c503 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ChannelShell.java @@ -0,0 +1,70 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.*; + +public class ChannelShell extends ChannelSession{ + + ChannelShell(){ + super(); + pty=true; + } + + public void start() throws JSchException{ + Session _session=getSession(); + try{ + sendRequests(); + + Request request=new RequestShell(); + request.request(_session, this); + } + catch(Exception e){ + if(e instanceof JSchException) throw (JSchException)e; + if(e instanceof Throwable) + throw new JSchException("ChannelShell", (Throwable)e); + throw new JSchException("ChannelShell"); + } + + if(io.in!=null){ + thread=new Thread(this); + thread.setName("Shell for "+_session.host); + if(_session.daemon_thread){ + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } +} diff --git a/src/main/java/com/jcraft/jsch/ChannelSubsystem.java b/src/main/java/com/jcraft/jsch/ChannelSubsystem.java new file mode 100644 index 00000000..568b4910 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ChannelSubsystem.java @@ -0,0 +1,83 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class ChannelSubsystem extends ChannelSession{ + boolean xforwading=false; + boolean pty=false; + boolean want_reply=true; + String subsystem=""; + public void setXForwarding(boolean foo){ xforwading=foo; } + public void setPty(boolean foo){ pty=foo; } + public void setWantReply(boolean foo){ want_reply=foo; } + public void setSubsystem(String foo){ subsystem=foo; } + public void start() throws JSchException{ + Session _session=getSession(); + try{ + Request request; + if(xforwading){ + request=new RequestX11(); + request.request(_session, this); + } + if(pty){ + request=new RequestPtyReq(); + request.request(_session, this); + } + request=new RequestSubsystem(); + ((RequestSubsystem)request).request(_session, this, subsystem, want_reply); + } + catch(Exception e){ + if(e instanceof JSchException){ throw (JSchException)e; } + if(e instanceof Throwable) + throw new JSchException("ChannelSubsystem", (Throwable)e); + throw new JSchException("ChannelSubsystem"); + } + if(io.in!=null){ + thread=new Thread(this); + thread.setName("Subsystem for "+_session.host); + if(_session.daemon_thread){ + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } + + public void setErrStream(java.io.OutputStream out){ + setExtOutputStream(out); + } + public java.io.InputStream getErrStream() throws java.io.IOException { + return getExtInputStream(); + } +} diff --git a/src/main/java/com/jcraft/jsch/ChannelX11.java b/src/main/java/com/jcraft/jsch/ChannelX11.java new file mode 100644 index 00000000..5be9049f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ChannelX11.java @@ -0,0 +1,273 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; + +class ChannelX11 extends Channel{ + + static private final int LOCAL_WINDOW_SIZE_MAX=0x20000; + static private final int LOCAL_MAXIMUM_PACKET_SIZE=0x4000; + + static private final int TIMEOUT=10*1000; + + private static String host="127.0.0.1"; + private static int port=6000; + + private boolean init=true; + + static byte[] cookie=null; + private static byte[] cookie_hex=null; + + private static java.util.Hashtable faked_cookie_pool=new java.util.Hashtable(); + private static java.util.Hashtable faked_cookie_hex_pool=new java.util.Hashtable(); + + private static byte[] table={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39, + 0x61,0x62,0x63,0x64,0x65,0x66}; + + private Socket socket = null; + + static int revtable(byte foo){ + for(int i=0; i>>4)&0xf]; + bar[2*i+1]=table[(foo[i])&0xf]; + } + faked_cookie_hex_pool.put(session, bar); + foo=bar; + } + return foo; + } + } + + static void removeFakedCookie(Session session){ + synchronized(faked_cookie_hex_pool){ + faked_cookie_hex_pool.remove(session); + faked_cookie_pool.remove(session); + } + } + + ChannelX11(){ + super(); + + setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX); + setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX); + setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE); + + type=Util.str2byte("x11"); + + connected=true; + /* + try{ + socket=Util.createSocket(host, port, TIMEOUT); + socket.setTcpNoDelay(true); + io=new IO(); + io.setInputStream(socket.getInputStream()); + io.setOutputStream(socket.getOutputStream()); + } + catch(Exception e){ + //System.err.println(e); + } + */ + } + + public void run(){ + + try{ + socket=Util.createSocket(host, port, TIMEOUT); + socket.setTcpNoDelay(true); + io=new IO(); + io.setInputStream(socket.getInputStream()); + io.setOutputStream(socket.getOutputStream()); + sendOpenConfirmation(); + } + catch(Exception e){ + sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); + close=true; + disconnect(); + return; + } + + thread=Thread.currentThread(); + Buffer buf=new Buffer(rmpsize); + Packet packet=new Packet(buf); + int i=0; + try{ + while(thread!=null && + io!=null && + io.in!=null){ + i=io.in.read(buf.buffer, + 14, + buf.buffer.length-14-Session.buffer_margin); + if(i<=0){ + eof(); + break; + } + if(close)break; + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + getSession().write(packet, this, i); + } + } + catch(Exception e){ + //System.err.println(e); + } + disconnect(); + } + + private byte[] cache=new byte[0]; + private byte[] addCache(byte[] foo, int s, int l){ + byte[] bar=new byte[cache.length+l]; + System.arraycopy(foo, s, bar, cache.length, l); + if(cache.length>0) + System.arraycopy(cache, 0, bar, 0, cache.length); + cache=bar; + return cache; + } + + void write(byte[] foo, int s, int l) throws java.io.IOException { + //if(eof_local)return; + + if(init){ + + Session _session=null; + try{ + _session=getSession(); + } + catch(JSchException e){ + throw new java.io.IOException(e.toString()); + } + + foo=addCache(foo, s, l); + s=0; + l=foo.length; + + if(l<9) + return; + + int plen=(foo[s+6]&0xff)*256+(foo[s+7]&0xff); + int dlen=(foo[s+8]&0xff)*256+(foo[s+9]&0xff); + + if((foo[s]&0xff)==0x42){ + } + else if((foo[s]&0xff)==0x6c){ + plen=((plen>>>8)&0xff)|((plen<<8)&0xff00); + dlen=((dlen>>>8)&0xff)|((dlen<<8)&0xff00); + } + else{ + // ?? + } + + if(l<12+plen+((-plen)&3)+dlen) + return; + + byte[] bar=new byte[dlen]; + System.arraycopy(foo, s+12+plen+((-plen)&3), bar, 0, dlen); + byte[] faked_cookie=null; + + synchronized(faked_cookie_pool){ + faked_cookie=(byte[])faked_cookie_pool.get(_session); + } + + /* +System.err.print("faked_cookie: "); +for(int i=0; i "); //dump(H, 0, H.length); + + i=0; + j=0; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + String alg=Util.byte2str(K_S, i, j); + i+=j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state=STATE_END; + return result; + } + return false; + } + + public int getState(){return state; } +} diff --git a/src/main/java/com/jcraft/jsch/DHG14.java b/src/main/java/com/jcraft/jsch/DHG14.java new file mode 100644 index 00000000..47cb5e85 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/DHG14.java @@ -0,0 +1,215 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class DHG14 extends KeyExchange{ + + static final byte[] g={ 2 }; + static final byte[] p={ +(byte)0x00, +(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF, +(byte)0xC9,(byte)0x0F,(byte)0xDA,(byte)0xA2,(byte)0x21,(byte)0x68,(byte)0xC2,(byte)0x34, +(byte)0xC4,(byte)0xC6,(byte)0x62,(byte)0x8B,(byte)0x80,(byte)0xDC,(byte)0x1C,(byte)0xD1, +(byte)0x29,(byte)0x02,(byte)0x4E,(byte)0x08,(byte)0x8A,(byte)0x67,(byte)0xCC,(byte)0x74, +(byte)0x02,(byte)0x0B,(byte)0xBE,(byte)0xA6,(byte)0x3B,(byte)0x13,(byte)0x9B,(byte)0x22, +(byte)0x51,(byte)0x4A,(byte)0x08,(byte)0x79,(byte)0x8E,(byte)0x34,(byte)0x04,(byte)0xDD, +(byte)0xEF,(byte)0x95,(byte)0x19,(byte)0xB3,(byte)0xCD,(byte)0x3A,(byte)0x43,(byte)0x1B, +(byte)0x30,(byte)0x2B,(byte)0x0A,(byte)0x6D,(byte)0xF2,(byte)0x5F,(byte)0x14,(byte)0x37, +(byte)0x4F,(byte)0xE1,(byte)0x35,(byte)0x6D,(byte)0x6D,(byte)0x51,(byte)0xC2,(byte)0x45, +(byte)0xE4,(byte)0x85,(byte)0xB5,(byte)0x76,(byte)0x62,(byte)0x5E,(byte)0x7E,(byte)0xC6, +(byte)0xF4,(byte)0x4C,(byte)0x42,(byte)0xE9,(byte)0xA6,(byte)0x37,(byte)0xED,(byte)0x6B, +(byte)0x0B,(byte)0xFF,(byte)0x5C,(byte)0xB6,(byte)0xF4,(byte)0x06,(byte)0xB7,(byte)0xED, +(byte)0xEE,(byte)0x38,(byte)0x6B,(byte)0xFB,(byte)0x5A,(byte)0x89,(byte)0x9F,(byte)0xA5, +(byte)0xAE,(byte)0x9F,(byte)0x24,(byte)0x11,(byte)0x7C,(byte)0x4B,(byte)0x1F,(byte)0xE6, +(byte)0x49,(byte)0x28,(byte)0x66,(byte)0x51,(byte)0xEC,(byte)0xE4,(byte)0x5B,(byte)0x3D, +(byte)0xC2,(byte)0x00,(byte)0x7C,(byte)0xB8,(byte)0xA1,(byte)0x63,(byte)0xBF,(byte)0x05, +(byte)0x98,(byte)0xDA,(byte)0x48,(byte)0x36,(byte)0x1C,(byte)0x55,(byte)0xD3,(byte)0x9A, +(byte)0x69,(byte)0x16,(byte)0x3F,(byte)0xA8,(byte)0xFD,(byte)0x24,(byte)0xCF,(byte)0x5F, +(byte)0x83,(byte)0x65,(byte)0x5D,(byte)0x23,(byte)0xDC,(byte)0xA3,(byte)0xAD,(byte)0x96, +(byte)0x1C,(byte)0x62,(byte)0xF3,(byte)0x56,(byte)0x20,(byte)0x85,(byte)0x52,(byte)0xBB, +(byte)0x9E,(byte)0xD5,(byte)0x29,(byte)0x07,(byte)0x70,(byte)0x96,(byte)0x96,(byte)0x6D, +(byte)0x67,(byte)0x0C,(byte)0x35,(byte)0x4E,(byte)0x4A,(byte)0xBC,(byte)0x98,(byte)0x04, +(byte)0xF1,(byte)0x74,(byte)0x6C,(byte)0x08,(byte)0xCA,(byte)0x18,(byte)0x21,(byte)0x7C, +(byte)0x32,(byte)0x90,(byte)0x5E,(byte)0x46,(byte)0x2E,(byte)0x36,(byte)0xCE,(byte)0x3B, +(byte)0xE3,(byte)0x9E,(byte)0x77,(byte)0x2C,(byte)0x18,(byte)0x0E,(byte)0x86,(byte)0x03, +(byte)0x9B,(byte)0x27,(byte)0x83,(byte)0xA2,(byte)0xEC,(byte)0x07,(byte)0xA2,(byte)0x8F, +(byte)0xB5,(byte)0xC5,(byte)0x5D,(byte)0xF0,(byte)0x6F,(byte)0x4C,(byte)0x52,(byte)0xC9, +(byte)0xDE,(byte)0x2B,(byte)0xCB,(byte)0xF6,(byte)0x95,(byte)0x58,(byte)0x17,(byte)0x18, +(byte)0x39,(byte)0x95,(byte)0x49,(byte)0x7C,(byte)0xEA,(byte)0x95,(byte)0x6A,(byte)0xE5, +(byte)0x15,(byte)0xD2,(byte)0x26,(byte)0x18,(byte)0x98,(byte)0xFA,(byte)0x05,(byte)0x10, +(byte)0x15,(byte)0x72,(byte)0x8E,(byte)0x5A,(byte)0x8A,(byte)0xAC,(byte)0xAA,(byte)0x68, +(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF +}; + + private static final int SSH_MSG_KEXDH_INIT= 30; + private static final int SSH_MSG_KEXDH_REPLY= 31; + + private int state; + + DH dh; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + byte[] e; + + private Buffer buf; + private Packet packet; + + public void init(Session session, + byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{ + this.session=session; + this.V_S=V_S; + this.V_C=V_C; + this.I_S=I_S; + this.I_C=I_C; + + try{ + Class c=Class.forName(session.getConfig("sha-1")); + sha=(HASH)(c.newInstance()); + sha.init(); + } + catch(Exception e){ + System.err.println(e); + } + + buf=new Buffer(); + packet=new Packet(buf); + + try{ + Class c=Class.forName(session.getConfig("dh")); + dh=(DH)(c.newInstance()); + dh.init(); + } + catch(Exception e){ + //System.err.println(e); + throw e; + } + + dh.setP(p); + dh.setG(g); + // The client responds with: + // byte SSH_MSG_KEXDH_INIT(30) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e=dh.getE(); + packet.reset(); + buf.putByte((byte)SSH_MSG_KEXDH_INIT); + buf.putMPInt(e); + + if(V_S==null){ // This is a really ugly hack for Session.checkKexes ;-( + return; + } + + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEXDH_INIT sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEXDH_REPLY"); + } + + state=SSH_MSG_KEXDH_REPLY; + } + + public boolean next(Buffer _buf) throws Exception{ + int i,j; + + switch(state){ + case SSH_MSG_KEXDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEXDH_REPLY(31) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j=_buf.getInt(); + j=_buf.getByte(); + j=_buf.getByte(); + if(j!=31){ + System.err.println("type: must be 31 "+j); + return false; + } + + K_S=_buf.getString(); + + byte[] f=_buf.getMPInt(); + byte[] sig_of_H=_buf.getString(); + + dh.setF(f); + + dh.checkRange(); + + K=normalize(dh.getK()); + + //The hash H is computed as the HASH hash of the concatenation of the + //following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + buf.reset(); + buf.putString(V_C); buf.putString(V_S); + buf.putString(I_C); buf.putString(I_S); + buf.putString(K_S); + buf.putMPInt(e); buf.putMPInt(f); + buf.putMPInt(K); + byte[] foo=new byte[buf.getLength()]; + buf.getByte(foo); + sha.update(foo, 0, foo.length); + H=sha.digest(); + //System.err.print("H -> "); //dump(H, 0, H.length); + + i=0; + j=0; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + String alg=Util.byte2str(K_S, i, j); + i+=j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state=STATE_END; + return result; + } + return false; + } + + public int getState(){return state; } +} diff --git a/src/main/java/com/jcraft/jsch/DHGEX.java b/src/main/java/com/jcraft/jsch/DHGEX.java new file mode 100644 index 00000000..f975e5f1 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/DHGEX.java @@ -0,0 +1,245 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class DHGEX extends KeyExchange{ + + private static final int SSH_MSG_KEX_DH_GEX_GROUP= 31; + private static final int SSH_MSG_KEX_DH_GEX_INIT= 32; + private static final int SSH_MSG_KEX_DH_GEX_REPLY= 33; + private static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34; + + static int min=1024; + static int preferred=1024; + int max=1024; + + private int state; + + DH dh; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + private Buffer buf; + private Packet packet; + + private byte[] p; + private byte[] g; + private byte[] e; + + protected String hash="sha-1"; + + public void init(Session session, + byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{ + this.session=session; + this.V_S=V_S; + this.V_C=V_C; + this.I_S=I_S; + this.I_C=I_C; + + try{ + Class c=Class.forName(session.getConfig(hash)); + sha=(HASH)(c.newInstance()); + sha.init(); + } + catch(Exception e){ + System.err.println(e); + } + + buf=new Buffer(); + packet=new Packet(buf); + + try{ + Class c=Class.forName(session.getConfig("dh")); + // Since JDK8, SunJCE has lifted the keysize restrictions + // from 1024 to 2048 for DH. + preferred = max = check2048(c, max); + dh=(com.jcraft.jsch.DH)(c.newInstance()); + dh.init(); + } + catch(Exception e){ + throw e; + } + + packet.reset(); + buf.putByte((byte)SSH_MSG_KEX_DH_GEX_REQUEST); + buf.putInt(min); + buf.putInt(preferred); + buf.putInt(max); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEX_DH_GEX_REQUEST("+min+"<"+preferred+"<"+max+") sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEX_DH_GEX_GROUP"); + } + + state=SSH_MSG_KEX_DH_GEX_GROUP; + } + + public boolean next(Buffer _buf) throws Exception{ + int i,j; + switch(state){ + case SSH_MSG_KEX_DH_GEX_GROUP: + // byte SSH_MSG_KEX_DH_GEX_GROUP(31) + // mpint p, safe prime + // mpint g, generator for subgroup in GF (p) + _buf.getInt(); + _buf.getByte(); + j=_buf.getByte(); + if(j!=SSH_MSG_KEX_DH_GEX_GROUP){ + System.err.println("type: must be SSH_MSG_KEX_DH_GEX_GROUP "+j); + return false; + } + + p=_buf.getMPInt(); + g=_buf.getMPInt(); + + dh.setP(p); + dh.setG(g); + // The client responds with: + // byte SSH_MSG_KEX_DH_GEX_INIT(32) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e=dh.getE(); + + packet.reset(); + buf.putByte((byte)SSH_MSG_KEX_DH_GEX_INIT); + buf.putMPInt(e); + session.write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEX_DH_GEX_INIT sent"); + JSch.getLogger().log(Logger.INFO, + "expecting SSH_MSG_KEX_DH_GEX_REPLY"); + } + + state=SSH_MSG_KEX_DH_GEX_REPLY; + return true; + //break; + + case SSH_MSG_KEX_DH_GEX_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_DH_GEX_REPLY(33) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j=_buf.getInt(); + j=_buf.getByte(); + j=_buf.getByte(); + if(j!=SSH_MSG_KEX_DH_GEX_REPLY){ + System.err.println("type: must be SSH_MSG_KEX_DH_GEX_REPLY "+j); + return false; + } + + K_S=_buf.getString(); + + byte[] f=_buf.getMPInt(); + byte[] sig_of_H=_buf.getString(); + + dh.setF(f); + + dh.checkRange(); + + K=normalize(dh.getK()); + + //The hash H is computed as the HASH hash of the concatenation of the + //following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // uint32 min, minimal size in bits of an acceptable group + // uint32 n, preferred size in bits of the group the server should send + // uint32 max, maximal size in bits of an acceptable group + // mpint p, safe prime + // mpint g, generator for subgroup + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + + buf.reset(); + buf.putString(V_C); buf.putString(V_S); + buf.putString(I_C); buf.putString(I_S); + buf.putString(K_S); + buf.putInt(min); buf.putInt(preferred); buf.putInt(max); + buf.putMPInt(p); buf.putMPInt(g); buf.putMPInt(e); buf.putMPInt(f); + buf.putMPInt(K); + + byte[] foo=new byte[buf.getLength()]; + buf.getByte(foo); + sha.update(foo, 0, foo.length); + + H=sha.digest(); + + // System.err.print("H -> "); dump(H, 0, H.length); + + i=0; + j=0; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + String alg=Util.byte2str(K_S, i, j); + i+=j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state=STATE_END; + return result; + } + return false; + } + + public int getState(){return state; } + + protected int check2048(Class c, int _max) throws Exception { + DH dh=(com.jcraft.jsch.DH)(c.newInstance()); + dh.init(); + byte[] foo = new byte[257]; + foo[1]=(byte)0xdd; + foo[256]=0x73; + dh.setP(foo); + byte[] bar = {(byte)0x02}; + dh.setG(bar); + try { + dh.getE(); + _max=2048; + } + catch(Exception e){ } + return _max; + } +} diff --git a/src/main/java/com/jcraft/jsch/DHGEX256.java b/src/main/java/com/jcraft/jsch/DHGEX256.java new file mode 100644 index 00000000..6627aaa1 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/DHGEX256.java @@ -0,0 +1,36 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class DHGEX256 extends DHGEX { + DHGEX256(){ + hash="sha-256"; + } +} diff --git a/src/main/java/com/jcraft/jsch/ECDH.java b/src/main/java/com/jcraft/jsch/ECDH.java new file mode 100644 index 00000000..b92c31e5 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ECDH.java @@ -0,0 +1,37 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface ECDH { + void init(int size) throws Exception; + byte[] getSecret(byte[] r, byte[] s) throws Exception; + byte[] getQ() throws Exception; + boolean validate(byte[] r, byte[] s) throws Exception; +} diff --git a/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java b/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java new file mode 100644 index 00000000..2318c6c8 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java @@ -0,0 +1,36 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; +import java.io.*; + +public interface ForwardedTCPIPDaemon extends Runnable{ + void setChannel(ChannelForwardedTCPIP channel, InputStream in, OutputStream out); + void setArg(Object[] arg); +} diff --git a/src/main/java/com/jcraft/jsch/GSSContext.java b/src/main/java/com/jcraft/jsch/GSSContext.java new file mode 100644 index 00000000..1c3864a5 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/GSSContext.java @@ -0,0 +1,38 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2004-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface GSSContext{ + public void create(String user, String host) throws JSchException; + public boolean isEstablished(); + public byte[] init(byte[] token, int s, int l) throws JSchException; + public byte[] getMIC(byte[] message, int s, int l); + public void dispose(); +} diff --git a/src/main/java/com/jcraft/jsch/HASH.java b/src/main/java/com/jcraft/jsch/HASH.java new file mode 100644 index 00000000..bea8aa48 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/HASH.java @@ -0,0 +1,37 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface HASH{ + void init() throws Exception; + int getBlockSize(); + void update(byte[] foo, int start, int len) throws Exception; + byte[] digest() throws Exception; +} diff --git a/src/main/java/com/jcraft/jsch/HostKey.java b/src/main/java/com/jcraft/jsch/HostKey.java new file mode 100644 index 00000000..c90a6fd8 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/HostKey.java @@ -0,0 +1,141 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class HostKey{ + + private static final byte[][] names = { + Util.str2byte("ssh-dss"), + Util.str2byte("ssh-rsa"), + Util.str2byte("ecdsa-sha2-nistp256"), + Util.str2byte("ecdsa-sha2-nistp384"), + Util.str2byte("ecdsa-sha2-nistp521") + }; + + protected static final int GUESS=0; + public static final int SSHDSS=1; + public static final int SSHRSA=2; + public static final int ECDSA256=3; + public static final int ECDSA384=4; + public static final int ECDSA521=5; + static final int UNKNOWN=6; + + protected String marker; + protected String host; + protected int type; + protected byte[] key; + protected String comment; + + public HostKey(String host, byte[] key) throws JSchException { + this(host, GUESS, key); + } + + public HostKey(String host, int type, byte[] key) throws JSchException { + this(host, type, key, null); + } + public HostKey(String host, int type, byte[] key, String comment) throws JSchException { + this("", host, type, key, comment); + } + public HostKey(String marker, String host, int type, byte[] key, String comment) throws JSchException { + this.marker=marker; + this.host=host; + if(type==GUESS){ + if(key[8]=='d'){ this.type=SSHDSS; } + else if(key[8]=='r'){ this.type=SSHRSA; } + else if(key[8]=='a' && key[20]=='2'){ this.type=ECDSA256; } + else if(key[8]=='a' && key[20]=='3'){ this.type=ECDSA384; } + else if(key[8]=='a' && key[20]=='5'){ this.type=ECDSA521; } + else { throw new JSchException("invalid key type");} + } + else{ + this.type=type; + } + this.key=key; + this.comment=comment; + } + + public String getHost(){ return host; } + public String getType(){ + if(type==SSHDSS || + type==SSHRSA || + type==ECDSA256 || + type==ECDSA384 || + type==ECDSA521){ + return Util.byte2str(names[type-1]); + } + return "UNKNOWN"; + } + protected static int name2type(String name){ + for(int i = 0; i < names.length; i++){ + if(Util.byte2str(names[i]).equals(name)){ + return i + 1; + } + } + return UNKNOWN; + } + public String getKey(){ + return Util.byte2str(Util.toBase64(key, 0, key.length)); + } + public String getFingerPrint(JSch jsch){ + HASH hash=null; + try{ + Class c=Class.forName(jsch.getConfig("md5")); + hash=(HASH)(c.newInstance()); + } + catch(Exception e){ System.err.println("getFingerPrint: "+e); } + return Util.getFingerPrint(hash, key); + } + public String getComment(){ return comment; } + public String getMarker(){ return marker; } + + boolean isMatched(String _host){ + return isIncluded(_host); + } + + private boolean isIncluded(String _host){ + int i=0; + String hosts=this.host; + int hostslen=hosts.length(); + int hostlen=_host.length(); + int j; + while(ihost is included with the key. + * + * @return #NOT_INCLUDED, #OK or #CHANGED + * @see #NOT_INCLUDED + * @see #OK + * @see #CHANGED + */ + int check(String host, byte[] key); + + /** + * Adds a host key hostkey + * + * @param hostkey a host key to be added + * @param ui a user interface for showing messages or promping inputs. + * @see UserInfo + */ + void add(HostKey hostkey, UserInfo ui); + + /** + * Removes a host key if there exists mached key with + * host, type. + * + * @see #remove(String host, String type, byte[] key) + */ + void remove(String host, String type); + + /** + * Removes a host key if there exists a matched key with + * host, type and key. + */ + void remove(String host, String type, byte[] key); + + /** + * Returns id of this repository. + * + * @return identity in String + */ + String getKnownHostsRepositoryID(); + + /** + * Retuns a list for host keys managed in this repository. + * + * @see #getHostKey(String host, String type) + */ + HostKey[] getHostKey(); + + /** + * Retuns a list for host keys managed in this repository. + * + * @param host a hostname used in searching host keys. + * If null is given, every host key will be listed. + * @param type a key type used in searching host keys, + * and it should be "ssh-dss" or "ssh-rsa". + * If null is given, a key type type will not be ignored. + */ + HostKey[] getHostKey(String host, String type); +} diff --git a/src/main/java/com/jcraft/jsch/IO.java b/src/main/java/com/jcraft/jsch/IO.java new file mode 100644 index 00000000..0be3bf0f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/IO.java @@ -0,0 +1,132 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +public class IO{ + InputStream in; + OutputStream out; + OutputStream out_ext; + + private boolean in_dontclose=false; + private boolean out_dontclose=false; + private boolean out_ext_dontclose=false; + + void setOutputStream(OutputStream out){ this.out=out; } + void setOutputStream(OutputStream out, boolean dontclose){ + this.out_dontclose=dontclose; + setOutputStream(out); + } + void setExtOutputStream(OutputStream out){ this.out_ext=out; } + void setExtOutputStream(OutputStream out, boolean dontclose){ + this.out_ext_dontclose=dontclose; + setExtOutputStream(out); + } + void setInputStream(InputStream in){ this.in=in; } + void setInputStream(InputStream in, boolean dontclose){ + this.in_dontclose=dontclose; + setInputStream(in); + } + + public void put(Packet p) throws IOException, java.net.SocketException { + out.write(p.buffer.buffer, 0, p.buffer.index); + out.flush(); + } + void put(byte[] array, int begin, int length) throws IOException { + out.write(array, begin, length); + out.flush(); + } + void put_ext(byte[] array, int begin, int length) throws IOException { + out_ext.write(array, begin, length); + out_ext.flush(); + } + + int getByte() throws IOException { + return in.read(); + } + + void getByte(byte[] array) throws IOException { + getByte(array, 0, array.length); + } + + void getByte(byte[] array, int begin, int length) throws IOException { + do{ + int completed = in.read(array, begin, length); + if(completed<0){ + throw new IOException("End of IO Stream Read"); + } + begin+=completed; + length-=completed; + } + while (length>0); + } + + void out_close(){ + try{ + if(out!=null && !out_dontclose) out.close(); + out=null; + } + catch(Exception ee){} + } + + public void close(){ + try{ + if(in!=null && !in_dontclose) in.close(); + in=null; + } + catch(Exception ee){} + + out_close(); + + try{ + if(out_ext!=null && !out_ext_dontclose) out_ext.close(); + out_ext=null; + } + catch(Exception ee){} + } + + /* + public void finalize() throws Throwable{ + try{ + if(in!=null) in.close(); + } + catch(Exception ee){} + try{ + if(out!=null) out.close(); + } + catch(Exception ee){} + try{ + if(out_ext!=null) out_ext.close(); + } + catch(Exception ee){} + } + */ +} diff --git a/src/main/java/com/jcraft/jsch/Identity.java b/src/main/java/com/jcraft/jsch/Identity.java new file mode 100644 index 00000000..4b2c1aa5 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/Identity.java @@ -0,0 +1,83 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface Identity{ + + /** + * Decrypts this identity with the specified pass-phrase. + * @param passphrase the pass-phrase for this identity. + * @return true if the decryption is succeeded + * or this identity is not cyphered. + */ + public boolean setPassphrase(byte[] passphrase) throws JSchException; + + /** + * Returns the public-key blob. + * @return the public-key blob + */ + public byte[] getPublicKeyBlob(); + + /** + * Signs on data with this identity, and returns the result. + * @param data data to be signed + * @return the signature + */ + public byte[] getSignature(byte[] data); + + /** + * @deprecated The decryption should be done automatically in #setPassphase(byte[] passphrase) + * @see #setPassphrase(byte[] passphrase) + */ + public boolean decrypt(); + + /** + * Returns the name of the key algorithm. + * @return "ssh-rsa" or "ssh-dss" + */ + public String getAlgName(); + + /** + * Returns the name of this identity. + * It will be useful to identify this object in the {@link IdentityRepository}. + */ + public String getName(); + + /** + * Returns true if this identity is cyphered. + * @return true if this identity is cyphered. + */ + public boolean isEncrypted(); + + /** + * Disposes internally allocated data, like byte array for the private key. + */ + public void clear(); +} diff --git a/src/main/java/com/jcraft/jsch/IdentityFile.java b/src/main/java/com/jcraft/jsch/IdentityFile.java new file mode 100644 index 00000000..fa41225e --- /dev/null +++ b/src/main/java/com/jcraft/jsch/IdentityFile.java @@ -0,0 +1,136 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +class IdentityFile implements Identity{ + private JSch jsch; + private KeyPair kpair; + private String identity; + + static IdentityFile newInstance(String prvfile, String pubfile, JSch jsch) throws JSchException{ + KeyPair kpair = KeyPair.load(jsch, prvfile, pubfile); + return new IdentityFile(jsch, prvfile, kpair); + } + + static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, JSch jsch) throws JSchException{ + + KeyPair kpair = KeyPair.load(jsch, prvkey, pubkey); + return new IdentityFile(jsch, name, kpair); + } + + private IdentityFile(JSch jsch, String name, KeyPair kpair) throws JSchException{ + this.jsch = jsch; + this.identity = name; + this.kpair = kpair; + } + + /** + * Decrypts this identity with the specified pass-phrase. + * @param passphrase the pass-phrase for this identity. + * @return true if the decryption is succeeded + * or this identity is not cyphered. + */ + public boolean setPassphrase(byte[] passphrase) throws JSchException{ + return kpair.decrypt(passphrase); + } + + /** + * Returns the public-key blob. + * @return the public-key blob + */ + public byte[] getPublicKeyBlob(){ + return kpair.getPublicKeyBlob(); + } + + /** + * Signs on data with this identity, and returns the result. + * @param data data to be signed + * @return the signature + */ + public byte[] getSignature(byte[] data){ + return kpair.getSignature(data); + } + + /** + * @deprecated This method should not be invoked. + * @see #setPassphrase(byte[] passphrase) + */ + public boolean decrypt(){ + throw new RuntimeException("not implemented"); + } + + /** + * Returns the name of the key algorithm. + * @return "ssh-rsa" or "ssh-dss" + */ + public String getAlgName(){ + byte[] name = kpair.getKeyTypeName(); + try { + return new String(name, "UTF-8"); + } + catch (UnsupportedEncodingException e){ + return new String(name); + } + } + + /** + * Returns the name of this identity. + * It will be useful to identify this object in the {@link IdentityRepository}. + */ + public String getName(){ + return identity; + } + + /** + * Returns true if this identity is cyphered. + * @return true if this identity is cyphered. + */ + public boolean isEncrypted(){ + return kpair.isEncrypted(); + } + + /** + * Disposes internally allocated data, like byte array for the private key. + */ + public void clear(){ + kpair.dispose(); + kpair = null; + } + + /** + * Returns an instance of {@link KeyPair} used in this {@link Identity}. + * @return an instance of {@link KeyPair} used in this {@link Identity}. + */ + public KeyPair getKeyPair(){ + return kpair; + } +} diff --git a/src/main/java/com/jcraft/jsch/IdentityRepository.java b/src/main/java/com/jcraft/jsch/IdentityRepository.java new file mode 100644 index 00000000..d4f3f228 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/IdentityRepository.java @@ -0,0 +1,115 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.Vector; + +public interface IdentityRepository { + public static final int UNAVAILABLE=0; + public static final int NOTRUNNING=1; + public static final int RUNNING=2; + public String getName(); + public int getStatus(); + public Vector getIdentities(); + public boolean add(byte[] identity); + public boolean remove(byte[] blob); + public void removeAll(); + + /** + * JSch will accept ciphered keys, but some implementations of + * IdentityRepository can not. For example, IdentityRepository for + * ssh-agent and pageant only accept plain keys. The following class has + * been introduced to cache ciphered keys for them, and pass them + * whenever they are de-ciphered. + */ + static class Wrapper implements IdentityRepository { + private IdentityRepository ir; + private Vector cache = new Vector(); + private boolean keep_in_cache = false; + Wrapper(IdentityRepository ir){ + this(ir, false); + } + Wrapper(IdentityRepository ir, boolean keep_in_cache){ + this.ir = ir; + this.keep_in_cache = keep_in_cache; + } + public String getName() { + return ir.getName(); + } + public int getStatus() { + return ir.getStatus(); + } + public boolean add(byte[] identity) { + return ir.add(identity); + } + public boolean remove(byte[] blob) { + return ir.remove(blob); + } + public void removeAll() { + cache.removeAllElements(); + ir.removeAll(); + } + public Vector getIdentities() { + Vector result = new Vector(); + for(int i = 0; i< cache.size(); i++){ + Identity identity = (Identity)(cache.elementAt(i)); + result.add(identity); + } + Vector tmp = ir.getIdentities(); + for(int i = 0; i< tmp.size(); i++){ + result.add(tmp.elementAt(i)); + } + return result; + } + void add(Identity identity) { + if(!keep_in_cache && + !identity.isEncrypted() && (identity instanceof IdentityFile)) { + try { + ir.add(((IdentityFile)identity).getKeyPair().forSSHAgent()); + } + catch(JSchException e){ + // an exception will not be thrown. + } + } + else + cache.addElement(identity); + } + void check() { + if(cache.size() > 0){ + Object[] identities = cache.toArray(); + for(int i = 0; i < identities.length; i++){ + Identity identity = (Identity)(identities[i]); + cache.removeElement(identity); + add(identity); + } + } + } + } +} diff --git a/src/main/java/com/jcraft/jsch/JSch.java b/src/main/java/com/jcraft/jsch/JSch.java new file mode 100644 index 00000000..b93c2885 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/JSch.java @@ -0,0 +1,590 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.InputStream; +import java.util.Vector; + +public class JSch{ + /** + * The version number. + */ + public static final String VERSION = "0.1.54"; + + static java.util.Hashtable config=new java.util.Hashtable(); + static{ + config.put("kex", "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1"); + config.put("server_host_key", "ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521"); + config.put("cipher.s2c", + "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc"); + config.put("cipher.c2s", + "aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc"); + + config.put("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96"); + config.put("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96"); + config.put("compression.s2c", "none"); + config.put("compression.c2s", "none"); + + config.put("lang.s2c", ""); + config.put("lang.c2s", ""); + + config.put("compression_level", "6"); + + config.put("diffie-hellman-group-exchange-sha1", + "com.jcraft.jsch.DHGEX"); + config.put("diffie-hellman-group1-sha1", + "com.jcraft.jsch.DHG1"); + config.put("diffie-hellman-group14-sha1", + "com.jcraft.jsch.DHG14"); // available since JDK8. + config.put("diffie-hellman-group-exchange-sha256", + "com.jcraft.jsch.DHGEX256"); // available since JDK1.4.2. + // On JDK8, 2048bits will be used. + config.put("ecdsa-sha2-nistp256", "com.jcraft.jsch.jce.SignatureECDSA256"); + config.put("ecdsa-sha2-nistp384", "com.jcraft.jsch.jce.SignatureECDSA384"); + config.put("ecdsa-sha2-nistp521", "com.jcraft.jsch.jce.SignatureECDSA521"); + + config.put("ecdh-sha2-nistp256", "com.jcraft.jsch.DHEC256"); + config.put("ecdh-sha2-nistp384", "com.jcraft.jsch.DHEC384"); + config.put("ecdh-sha2-nistp521", "com.jcraft.jsch.DHEC521"); + + config.put("ecdh-sha2-nistp", "com.jcraft.jsch.jce.ECDHN"); + + config.put("dh", "com.jcraft.jsch.jce.DH"); + config.put("3des-cbc", "com.jcraft.jsch.jce.TripleDESCBC"); + config.put("blowfish-cbc", "com.jcraft.jsch.jce.BlowfishCBC"); + config.put("hmac-sha1", "com.jcraft.jsch.jce.HMACSHA1"); + config.put("hmac-sha1-96", "com.jcraft.jsch.jce.HMACSHA196"); + config.put("hmac-sha2-256", "com.jcraft.jsch.jce.HMACSHA256"); + // The "hmac-sha2-512" will require the key-length 2048 for DH, + // but Sun's JCE has not allowed to use such a long key. + //config.put("hmac-sha2-512", "com.jcraft.jsch.jce.HMACSHA512"); + config.put("hmac-md5", "com.jcraft.jsch.jce.HMACMD5"); + config.put("hmac-md5-96", "com.jcraft.jsch.jce.HMACMD596"); + config.put("sha-1", "com.jcraft.jsch.jce.SHA1"); + config.put("sha-256", "com.jcraft.jsch.jce.SHA256"); + config.put("sha-384", "com.jcraft.jsch.jce.SHA384"); + config.put("sha-512", "com.jcraft.jsch.jce.SHA512"); + config.put("md5", "com.jcraft.jsch.jce.MD5"); + config.put("signature.dss", "com.jcraft.jsch.jce.SignatureDSA"); + config.put("signature.rsa", "com.jcraft.jsch.jce.SignatureRSA"); + config.put("keypairgen.dsa", "com.jcraft.jsch.jce.KeyPairGenDSA"); + config.put("keypairgen.rsa", "com.jcraft.jsch.jce.KeyPairGenRSA"); + config.put("keypairgen.ecdsa", "com.jcraft.jsch.jce.KeyPairGenECDSA"); + config.put("random", "com.jcraft.jsch.jce.Random"); + + config.put("none", "com.jcraft.jsch.CipherNone"); + + config.put("aes128-cbc", "com.jcraft.jsch.jce.AES128CBC"); + config.put("aes192-cbc", "com.jcraft.jsch.jce.AES192CBC"); + config.put("aes256-cbc", "com.jcraft.jsch.jce.AES256CBC"); + + config.put("aes128-ctr", "com.jcraft.jsch.jce.AES128CTR"); + config.put("aes192-ctr", "com.jcraft.jsch.jce.AES192CTR"); + config.put("aes256-ctr", "com.jcraft.jsch.jce.AES256CTR"); + config.put("3des-ctr", "com.jcraft.jsch.jce.TripleDESCTR"); + config.put("arcfour", "com.jcraft.jsch.jce.ARCFOUR"); + config.put("arcfour128", "com.jcraft.jsch.jce.ARCFOUR128"); + config.put("arcfour256", "com.jcraft.jsch.jce.ARCFOUR256"); + + config.put("userauth.none", "com.jcraft.jsch.UserAuthNone"); + config.put("userauth.password", "com.jcraft.jsch.UserAuthPassword"); + config.put("userauth.keyboard-interactive", "com.jcraft.jsch.UserAuthKeyboardInteractive"); + config.put("userauth.publickey", "com.jcraft.jsch.UserAuthPublicKey"); + config.put("userauth.gssapi-with-mic", "com.jcraft.jsch.UserAuthGSSAPIWithMIC"); + config.put("gssapi-with-mic.krb5", "com.jcraft.jsch.jgss.GSSContextKrb5"); + + config.put("zlib", "com.jcraft.jsch.jcraft.Compression"); + config.put("zlib@openssh.com", "com.jcraft.jsch.jcraft.Compression"); + + config.put("pbkdf", "com.jcraft.jsch.jce.PBKDF"); + + config.put("StrictHostKeyChecking", "ask"); + config.put("HashKnownHosts", "no"); + + config.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password"); + + config.put("CheckCiphers", "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256"); + config.put("CheckKexes", "diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521"); + config.put("CheckSignatures", "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521"); + + config.put("MaxAuthTries", "6"); + config.put("ClearAllForwardings", "no"); + } + + private java.util.Vector sessionPool = new java.util.Vector(); + + private IdentityRepository defaultIdentityRepository = + new LocalIdentityRepository(this); + + private IdentityRepository identityRepository = defaultIdentityRepository; + + private ConfigRepository configRepository = null; + + /** + * Sets the identityRepository, which will be referred + * in the public key authentication. + * + * @param identityRepository if null is given, + * the default repository, which usually refers to ~/.ssh/, will be used. + * + * @see #getIdentityRepository() + */ + public synchronized void setIdentityRepository(IdentityRepository identityRepository){ + if(identityRepository == null){ + this.identityRepository = defaultIdentityRepository; + } + else{ + this.identityRepository = identityRepository; + } + } + + public synchronized IdentityRepository getIdentityRepository(){ + return this.identityRepository; + } + + public ConfigRepository getConfigRepository() { + return this.configRepository; + } + + public void setConfigRepository(ConfigRepository configRepository) { + this.configRepository = configRepository; + } + + private HostKeyRepository known_hosts=null; + + private static final Logger DEVNULL=new Logger(){ + public boolean isEnabled(int level){return false;} + public void log(int level, String message){} + }; + static Logger logger=DEVNULL; + + public JSch(){ + /* + // The JCE of Sun's Java5 on Mac OS X has the resource leak bug + // in calculating HMAC, so we need to use our own implementations. + try{ + String osname=(String)(System.getProperties().get("os.name")); + if(osname!=null && osname.equals("Mac OS X")){ + config.put("hmac-sha1", "com.jcraft.jsch.jcraft.HMACSHA1"); + config.put("hmac-md5", "com.jcraft.jsch.jcraft.HMACMD5"); + config.put("hmac-md5-96", "com.jcraft.jsch.jcraft.HMACMD596"); + config.put("hmac-sha1-96", "com.jcraft.jsch.jcraft.HMACSHA196"); + } + } + catch(Exception e){ + } + */ + } + + /** + * Instantiates the Session object with + * host. The user name and port number will be retrieved from + * ConfigRepository. If user name is not given, + * the system property "user.name" will be referred. + * + * @param host hostname + * + * @throws JSchException + * if username or host are invalid. + * + * @return the instance of Session class. + * + * @see #getSession(String username, String host, int port) + * @see com.jcraft.jsch.Session + * @see com.jcraft.jsch.ConfigRepository + */ + public Session getSession(String host) + throws JSchException { + return getSession(null, host, 22); + } + + /** + * Instantiates the Session object with + * username and host. + * The TCP port 22 will be used in making the connection. + * Note that the TCP connection must not be established + * until Session#connect(). + * + * @param username user name + * @param host hostname + * + * @throws JSchException + * if username or host are invalid. + * + * @return the instance of Session class. + * + * @see #getSession(String username, String host, int port) + * @see com.jcraft.jsch.Session + */ + public Session getSession(String username, String host) + throws JSchException { + return getSession(username, host, 22); + } + + /** + * Instantiates the Session object with given + * username, host and port. + * Note that the TCP connection must not be established + * until Session#connect(). + * + * @param username user name + * @param host hostname + * @param port port number + * + * @throws JSchException + * if username or host are invalid. + * + * @return the instance of Session class. + * + * @see #getSession(String username, String host, int port) + * @see com.jcraft.jsch.Session + */ + public Session getSession(String username, String host, int port) throws JSchException { + if(host==null){ + throw new JSchException("host must not be null."); + } + Session s = new Session(this, username, host, port); + return s; + } + + protected void addSession(Session session){ + synchronized(sessionPool){ + sessionPool.addElement(session); + } + } + + protected boolean removeSession(Session session){ + synchronized(sessionPool){ + return sessionPool.remove(session); + } + } + + /** + * Sets the hostkey repository. + * + * @param hkrepo + * + * @see com.jcraft.jsch.HostKeyRepository + * @see com.jcraft.jsch.KnownHosts + */ + public void setHostKeyRepository(HostKeyRepository hkrepo){ + known_hosts=hkrepo; + } + + /** + * Sets the instance of KnownHosts, which refers + * to filename. + * + * @param filename filename of known_hosts file. + * + * @throws JSchException + * if the given filename is invalid. + * + * @see com.jcraft.jsch.KnownHosts + */ + public void setKnownHosts(String filename) throws JSchException{ + if(known_hosts==null) known_hosts=new KnownHosts(this); + if(known_hosts instanceof KnownHosts){ + synchronized(known_hosts){ + ((KnownHosts)known_hosts).setKnownHosts(filename); + } + } + } + + /** + * Sets the instance of KnownHosts generated with + * stream. + * + * @param stream the instance of InputStream from known_hosts file. + * + * @throws JSchException + * if an I/O error occurs. + * + * @see com.jcraft.jsch.KnownHosts + */ + public void setKnownHosts(InputStream stream) throws JSchException{ + if(known_hosts==null) known_hosts=new KnownHosts(this); + if(known_hosts instanceof KnownHosts){ + synchronized(known_hosts){ + ((KnownHosts)known_hosts).setKnownHosts(stream); + } + } + } + + /** + * Returns the current hostkey repository. + * By the default, this method will the instance of KnownHosts. + * + * @return current hostkey repository. + * + * @see com.jcraft.jsch.HostKeyRepository + * @see com.jcraft.jsch.KnownHosts + */ + public HostKeyRepository getHostKeyRepository(){ + if(known_hosts==null) known_hosts=new KnownHosts(this); + return known_hosts; + } + + /** + * Sets the private key, which will be referred in + * the public key authentication. + * + * @param prvkey filename of the private key. + * + * @throws JSchException if prvkey is invalid. + * + * @see #addIdentity(String prvkey, String passphrase) + */ + public void addIdentity(String prvkey) throws JSchException{ + addIdentity(prvkey, (byte[])null); + } + + /** + * Sets the private key, which will be referred in + * the public key authentication. + * Before registering it into identityRepository, + * it will be deciphered with passphrase. + * + * @param prvkey filename of the private key. + * @param passphrase passphrase for prvkey. + * + * @throws JSchException if passphrase is not right. + * + * @see #addIdentity(String prvkey, byte[] passphrase) + */ + public void addIdentity(String prvkey, String passphrase) throws JSchException{ + byte[] _passphrase=null; + if(passphrase!=null){ + _passphrase=Util.str2byte(passphrase); + } + addIdentity(prvkey, _passphrase); + if(_passphrase!=null) + Util.bzero(_passphrase); + } + + /** + * Sets the private key, which will be referred in + * the public key authentication. + * Before registering it into identityRepository, + * it will be deciphered with passphrase. + * + * @param prvkey filename of the private key. + * @param passphrase passphrase for prvkey. + * + * @throws JSchException if passphrase is not right. + * + * @see #addIdentity(String prvkey, String pubkey, byte[] passphrase) + */ + public void addIdentity(String prvkey, byte[] passphrase) throws JSchException{ + Identity identity=IdentityFile.newInstance(prvkey, null, this); + addIdentity(identity, passphrase); + } + + /** + * Sets the private key, which will be referred in + * the public key authentication. + * Before registering it into identityRepository, + * it will be deciphered with passphrase. + * + * @param prvkey filename of the private key. + * @param pubkey filename of the public key. + * @param passphrase passphrase for prvkey. + * + * @throws JSchException if passphrase is not right. + */ + public void addIdentity(String prvkey, String pubkey, byte[] passphrase) throws JSchException{ + Identity identity=IdentityFile.newInstance(prvkey, pubkey, this); + addIdentity(identity, passphrase); + } + + /** + * Sets the private key, which will be referred in + * the public key authentication. + * Before registering it into identityRepository, + * it will be deciphered with passphrase. + * + * @param name name of the identity to be used to + retrieve it in the identityRepository. + * @param prvkey private key in byte array. + * @param pubkey public key in byte array. + * @param passphrase passphrase for prvkey. + * + */ + public void addIdentity(String name, byte[]prvkey, byte[]pubkey, byte[] passphrase) throws JSchException{ + Identity identity=IdentityFile.newInstance(name, prvkey, pubkey, this); + addIdentity(identity, passphrase); + } + + /** + * Sets the private key, which will be referred in + * the public key authentication. + * Before registering it into identityRepository, + * it will be deciphered with passphrase. + * + * @param identity private key. + * @param passphrase passphrase for identity. + * + * @throws JSchException if passphrase is not right. + */ + public void addIdentity(Identity identity, byte[] passphrase) throws JSchException{ + if(passphrase!=null){ + try{ + byte[] goo=new byte[passphrase.length]; + System.arraycopy(passphrase, 0, goo, 0, passphrase.length); + passphrase=goo; + identity.setPassphrase(passphrase); + } + finally{ + Util.bzero(passphrase); + } + } + + if(identityRepository instanceof LocalIdentityRepository){ + ((LocalIdentityRepository)identityRepository).add(identity); + } + else if(identity instanceof IdentityFile && !identity.isEncrypted()) { + identityRepository.add(((IdentityFile)identity).getKeyPair().forSSHAgent()); + } + else { + synchronized(this){ + if(!(identityRepository instanceof IdentityRepository.Wrapper)){ + setIdentityRepository(new IdentityRepository.Wrapper(identityRepository)); + } + } + ((IdentityRepository.Wrapper)identityRepository).add(identity); + } + } + + /** + * @deprecated use #removeIdentity(Identity identity) + */ + public void removeIdentity(String name) throws JSchException{ + Vector identities = identityRepository.getIdentities(); + for(int i=0; iidentity is invalid. + */ + public void removeIdentity(Identity identity) throws JSchException{ + identityRepository.remove(identity.getPublicKeyBlob()); + } + + /** + * Lists names of identities included in the identityRepository. + * + * @return names of identities + * + * @throws JSchException if identityReposory has problems. + */ + public Vector getIdentityNames() throws JSchException{ + Vector foo=new Vector(); + Vector identities = identityRepository.getIdentities(); + for(int i=0; iclient"+ + " "+guess[PROPOSAL_ENC_ALGS_STOC]+ + " "+guess[PROPOSAL_MAC_ALGS_STOC]+ + " "+guess[PROPOSAL_COMP_ALGS_STOC]); + JSch.getLogger().log(Logger.INFO, + "kex: client->server"+ + " "+guess[PROPOSAL_ENC_ALGS_CTOS]+ + " "+guess[PROPOSAL_MAC_ALGS_CTOS]+ + " "+guess[PROPOSAL_COMP_ALGS_CTOS]); + } + + return guess; + } + + public String getFingerPrint(){ + HASH hash=null; + try{ + Class c=Class.forName(session.getConfig("md5")); + hash=(HASH)(c.newInstance()); + } + catch(Exception e){ System.err.println("getFingerPrint: "+e); } + return Util.getFingerPrint(hash, getHostKey()); + } + byte[] getK(){ return K; } + byte[] getH(){ return H; } + HASH getHash(){ return sha; } + byte[] getHostKey(){ return K_S; } + + /* + * It seems JCE included in Oracle's Java7u6(and later) has suddenly changed + * its behavior. The secrete generated by KeyAgreement#generateSecret() + * may start with 0, even if it is a positive value. + */ + protected byte[] normalize(byte[] secret) { + if(secret.length > 1 && + secret[0] == 0 && (secret[1]&0x80) == 0) { + byte[] tmp=new byte[secret.length-1]; + System.arraycopy(secret, 1, tmp, 0, tmp.length); + return normalize(tmp); + } + else { + return secret; + } + } + + protected boolean verify(String alg, byte[] K_S, int index, + byte[] sig_of_H) throws Exception { + int i,j; + + i=index; + boolean result=false; + + if(alg.equals("ssh-rsa")){ + byte[] tmp; + byte[] ee; + byte[] n; + + type=RSA; + key_alg_name=alg; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + ee=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + n=tmp; + + SignatureRSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.rsa")); + sig=(SignatureRSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + sig.setPubKey(ee, n); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_rsa_verify: signature "+result); + } + } + else if(alg.equals("ssh-dss")){ + byte[] q=null; + byte[] tmp; + byte[] p; + byte[] g; + byte[] f; + + type=DSS; + key_alg_name=alg; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + p=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + q=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + g=tmp; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + f=tmp; + + SignatureDSA sig=null; + try{ + Class c=Class.forName(session.getConfig("signature.dss")); + sig=(SignatureDSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + sig.setPubKey(f, p, q, g); + sig.update(H); + result=sig.verify(sig_of_H); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "ssh_dss_verify: signature "+result); + } + } + else if(alg.equals("ecdsa-sha2-nistp256") || + alg.equals("ecdsa-sha2-nistp384") || + alg.equals("ecdsa-sha2-nistp521")) { + byte[] tmp; + byte[] r; + byte[] s; + + // RFC 5656, + type=ECDSA; + key_alg_name=alg; + + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j; + j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)| + ((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff); + i++; + tmp=new byte[(j-1)/2]; + System.arraycopy(K_S, i, tmp, 0, tmp.length); i+=(j-1)/2; + r=tmp; + tmp=new byte[(j-1)/2]; + System.arraycopy(K_S, i, tmp, 0, tmp.length); i+=(j-1)/2; + s=tmp; + + SignatureECDSA sig=null; + try{ + Class c=Class.forName(session.getConfig(alg)); + sig=(SignatureECDSA)(c.newInstance()); + sig.init(); + } + catch(Exception e){ + System.err.println(e); + } + + sig.setPubKey(r, s); + + sig.update(H); + + result=sig.verify(sig_of_H); + } + else{ + System.err.println("unknown alg"); + } + + return result; + } + +} diff --git a/src/main/java/com/jcraft/jsch/KeyPair.java b/src/main/java/com/jcraft/jsch/KeyPair.java new file mode 100644 index 00000000..76b396ee --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KeyPair.java @@ -0,0 +1,1255 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.File; +import java.io.IOException; + +public abstract class KeyPair{ + public static final int ERROR=0; + public static final int DSA=1; + public static final int RSA=2; + public static final int ECDSA=3; + public static final int UNKNOWN=4; + + static final int VENDOR_OPENSSH=0; + static final int VENDOR_FSECURE=1; + static final int VENDOR_PUTTY=2; + static final int VENDOR_PKCS8=3; + + int vendor=VENDOR_OPENSSH; + + private static final byte[] cr=Util.str2byte("\n"); + + public static KeyPair genKeyPair(JSch jsch, int type) throws JSchException{ + return genKeyPair(jsch, type, 1024); + } + public static KeyPair genKeyPair(JSch jsch, int type, int key_size) throws JSchException{ + KeyPair kpair=null; + if(type==DSA){ kpair=new KeyPairDSA(jsch); } + else if(type==RSA){ kpair=new KeyPairRSA(jsch); } + else if(type==ECDSA){ kpair=new KeyPairECDSA(jsch); } + if(kpair!=null){ + kpair.generate(key_size); + } + return kpair; + } + + abstract void generate(int key_size) throws JSchException; + + abstract byte[] getBegin(); + abstract byte[] getEnd(); + abstract int getKeySize(); + + public abstract byte[] getSignature(byte[] data); + public abstract Signature getVerifier(); + + public abstract byte[] forSSHAgent() throws JSchException; + + public String getPublicKeyComment(){ + return publicKeyComment; + } + + public void setPublicKeyComment(String publicKeyComment){ + this.publicKeyComment = publicKeyComment; + } + + protected String publicKeyComment = "no comment"; + + JSch jsch=null; + private Cipher cipher; + private HASH hash; + private Random random; + + private byte[] passphrase; + + public KeyPair(JSch jsch){ + this.jsch=jsch; + } + + static byte[][] header={Util.str2byte("Proc-Type: 4,ENCRYPTED"), + Util.str2byte("DEK-Info: DES-EDE3-CBC,")}; + + abstract byte[] getPrivateKey(); + + /** + * Writes the plain private key to the given output stream. + * @param out output stream + * @see #writePrivateKey(java.io.OutputStream out, byte[] passphrase) + */ + public void writePrivateKey(java.io.OutputStream out){ + this.writePrivateKey(out, null); + } + + /** + * Writes the cyphered private key to the given output stream. + * @param out output stream + * @param passphrase a passphrase to encrypt the private key + */ + public void writePrivateKey(java.io.OutputStream out, byte[] passphrase){ + if(passphrase == null) + passphrase = this.passphrase; + + byte[] plain=getPrivateKey(); + byte[][] _iv=new byte[1][]; + byte[] encoded=encrypt(plain, _iv, passphrase); + if(encoded!=plain) + Util.bzero(plain); + byte[] iv=_iv[0]; + byte[] prv=Util.toBase64(encoded, 0, encoded.length); + + try{ + out.write(getBegin()); out.write(cr); + if(passphrase!=null){ + out.write(header[0]); out.write(cr); + out.write(header[1]); + for(int i=0; i>>4)&0x0f))); + out.write(b2a((byte)(iv[i]&0x0f))); + } + out.write(cr); + out.write(cr); + } + int i=0; + while(i0){ + len>>>=8; + i++; + } + return i; + } + + int writeLength(byte[] data, int index, int len){ + int i=countLength(len)-1; + if(i==0){ + data[index++]=(byte)len; + return index; + } + data[index++]=(byte)(0x80|i); + int j=index+i; + while(i>0){ + data[index+i-1]=(byte)(len&0xff); + len>>>=8; + i--; + } + return j; + } + + private Random genRandom(){ + if(random==null){ + try{ + Class c=Class.forName(jsch.getConfig("random")); + random=(Random)(c.newInstance()); + } + catch(Exception e){ System.err.println("connect: random "+e); } + } + return random; + } + + private HASH genHash(){ + try{ + Class c=Class.forName(jsch.getConfig("md5")); + hash=(HASH)(c.newInstance()); + hash.init(); + } + catch(Exception e){ + } + return hash; + } + private Cipher genCipher(){ + try{ + Class c; + c=Class.forName(jsch.getConfig("3des-cbc")); + cipher=(Cipher)(c.newInstance()); + } + catch(Exception e){ + } + return cipher; + } + + /* + hash is MD5 + h(0) <- hash(passphrase, iv); + h(n) <- hash(h(n-1), passphrase, iv); + key <- (h(0),...,h(n))[0,..,key.length]; + */ + synchronized byte[] genKey(byte[] passphrase, byte[] iv){ + if(cipher==null) cipher=genCipher(); + if(hash==null) hash=genHash(); + + byte[] key=new byte[cipher.getBlockSize()]; + int hsize=hash.getBlockSize(); + byte[] hn=new byte[key.length/hsize*hsize+ + (key.length%hsize==0?0:hsize)]; + try{ + byte[] tmp=null; + if(vendor==VENDOR_OPENSSH){ + for(int index=0; index+hsize<=hn.length;){ + if(tmp!=null){ hash.update(tmp, 0, tmp.length); } + hash.update(passphrase, 0, passphrase.length); + hash.update(iv, 0, iv.length > 8 ? 8: iv.length); + tmp=hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index+=tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } + else if(vendor==VENDOR_FSECURE){ + for(int index=0; index+hsize<=hn.length;){ + if(tmp!=null){ hash.update(tmp, 0, tmp.length); } + hash.update(passphrase, 0, passphrase.length); + tmp=hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index+=tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } + else if(vendor==VENDOR_PUTTY){ + Class c=Class.forName((String)jsch.getConfig("sha-1")); + HASH sha1=(HASH)(c.newInstance()); + tmp = new byte[4]; + key = new byte[20*2]; + for(int i = 0; i < 2; i++){ + sha1.init(); + tmp[3]=(byte)i; + sha1.update(tmp, 0, tmp.length); + sha1.update(passphrase, 0, passphrase.length); + System.arraycopy(sha1.digest(), 0, key, i*20, 20); + } + } + } + catch(Exception e){ + System.err.println(e); + } + return key; + } + + /** + * @deprecated use #writePrivateKey(java.io.OutputStream out, byte[] passphrase) + */ + public void setPassphrase(String passphrase){ + if(passphrase==null || passphrase.length()==0){ + setPassphrase((byte[])null); + } + else{ + setPassphrase(Util.str2byte(passphrase)); + } + } + + /** + * @deprecated use #writePrivateKey(String name, byte[] passphrase) + */ + public void setPassphrase(byte[] passphrase){ + if(passphrase!=null && passphrase.length==0) + passphrase=null; + this.passphrase=passphrase; + } + + protected boolean encrypted=false; + protected byte[] data=null; + private byte[] iv=null; + private byte[] publickeyblob=null; + + public boolean isEncrypted(){ return encrypted; } + public boolean decrypt(String _passphrase){ + if(_passphrase==null || _passphrase.length()==0){ + return !encrypted; + } + return decrypt(Util.str2byte(_passphrase)); + } + public boolean decrypt(byte[] _passphrase){ + + if(!encrypted){ + return true; + } + if(_passphrase==null){ + return !encrypted; + } + byte[] bar=new byte[_passphrase.length]; + System.arraycopy(_passphrase, 0, bar, 0, bar.length); + _passphrase=bar; + byte[] foo=decrypt(data, _passphrase, iv); + Util.bzero(_passphrase); + if(parse(foo)){ + encrypted=false; + } + return !encrypted; + } + + public static KeyPair load(JSch jsch, String prvkey) throws JSchException{ + String pubkey=prvkey+".pub"; + if(!new File(pubkey).exists()){ + pubkey=null; + } + return load(jsch, prvkey, pubkey); + } + public static KeyPair load(JSch jsch, String prvfile, String pubfile) throws JSchException{ + + byte[] prvkey=null; + byte[] pubkey=null; + + try{ + prvkey = Util.fromFile(prvfile); + } + catch(IOException e){ + throw new JSchException(e.toString(), (Throwable)e); + } + + String _pubfile=pubfile; + if(pubfile==null){ + _pubfile=prvfile+".pub"; + } + + try{ + pubkey = Util.fromFile(_pubfile); + } + catch(IOException e){ + if(pubfile!=null){ + throw new JSchException(e.toString(), (Throwable)e); + } + } + + try { + return load(jsch, prvkey, pubkey); + } + finally { + Util.bzero(prvkey); + } + } + + public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchException{ + + byte[] iv=new byte[8]; // 8 + boolean encrypted=true; + byte[] data=null; + + byte[] publickeyblob=null; + + int type=ERROR; + int vendor=VENDOR_OPENSSH; + String publicKeyComment = ""; + Cipher cipher=null; + + // prvkey from "ssh-add" command on the remote. + if(pubkey==null && + prvkey!=null && + (prvkey.length>11 && + prvkey[0]==0 && prvkey[1]==0 && prvkey[2]==0 && + (prvkey[3]==7 || prvkey[3]==19))){ + + Buffer buf=new Buffer(prvkey); + buf.skip(prvkey.length); // for using Buffer#available() + String _type = new String(buf.getString()); // ssh-rsa or ssh-dss + buf.rewind(); + + KeyPair kpair=null; + if(_type.equals("ssh-rsa")){ + kpair=KeyPairRSA.fromSSHAgent(jsch, buf); + } + else if(_type.equals("ssh-dss")){ + kpair=KeyPairDSA.fromSSHAgent(jsch, buf); + } + else if(_type.equals("ecdsa-sha2-nistp256") || + _type.equals("ecdsa-sha2-nistp384") || + _type.equals("ecdsa-sha2-nistp521")){ + kpair=KeyPairECDSA.fromSSHAgent(jsch, buf); + } + else{ + throw new JSchException("privatekey: invalid key "+new String(prvkey, 4, 7)); + } + return kpair; + } + + try{ + byte[] buf=prvkey; + + if(buf!=null){ + KeyPair ppk = loadPPK(jsch, buf); + if(ppk !=null) + return ppk; + } + + int len = (buf!=null ? buf.length : 0); + int i=0; + + // skip garbage lines. + while(i= len) + throw new JSchException("invalid privatekey: "+prvkey); + if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; } + else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; } + else if(buf[i]=='E'&& buf[i+1]=='C'){ type=ECDSA; } + else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){ // FSecure + type=UNKNOWN; + vendor=VENDOR_FSECURE; + } + else if(i+6 < len && + buf[i]=='P' && buf[i+1]=='R' && + buf[i+2]=='I' && buf[i+3]=='V' && + buf[i+4]=='A' && buf[i+5]=='T' && buf[i+6]=='E'){ + type=UNKNOWN; + vendor=VENDOR_PKCS8; + encrypted=false; + i+=3; + } + else if(i+8 < len && + buf[i]=='E' && buf[i+1]=='N' && + buf[i+2]=='C' && buf[i+3]=='R' && + buf[i+4]=='Y' && buf[i+5]=='P' && buf[i+6]=='T' && + buf[i+7]=='E' && buf[i+8]=='D'){ + type=UNKNOWN; + vendor=VENDOR_PKCS8; + i+=5; + } + else{ + throw new JSchException("invalid privatekey: "+prvkey); + } + i+=3; + continue; + } + if(buf[i]=='A'&& i+7 0) + data=Util.fromBase64(_buf, start, i-start); + + Util.bzero(_buf); + } + + if(data!=null && + data.length>4 && // FSecure + data[0]==(byte)0x3f && + data[1]==(byte)0x6f && + data[2]==(byte)0xf9 && + data[3]==(byte)0xeb){ + + Buffer _buf=new Buffer(data); + _buf.getInt(); // 0x3f6ff9be + _buf.getInt(); + byte[]_type=_buf.getString(); + //System.err.println("type: "+new String(_type)); + String _cipher=Util.byte2str(_buf.getString()); + //System.err.println("cipher: "+_cipher); + if(_cipher.equals("3des-cbc")){ + _buf.getInt(); + byte[] foo=new byte[data.length-_buf.getOffSet()]; + _buf.getByte(foo); + data=foo; + encrypted=true; + throw new JSchException("unknown privatekey format: "+prvkey); + } + else if(_cipher.equals("none")){ + _buf.getInt(); + _buf.getInt(); + + encrypted=false; + + byte[] foo=new byte[data.length-_buf.getOffSet()]; + _buf.getByte(foo); + data=foo; + } + } + + if(pubkey!=null){ + try{ + buf=pubkey; + len=buf.length; + if(buf.length>4 && // FSecure's public key + buf[0]=='-' && buf[1]=='-' && buf[2]=='-' && buf[3]=='-'){ + + boolean valid=true; + i=0; + do{i++;}while(buf.length>i && buf[i]!=0x0a); + if(buf.length<=i) {valid=false;} + + while(valid){ + if(buf[i]==0x0a){ + boolean inheader=false; + for(int j=i+1; j7){ + if(buf[4]=='d'){ type=DSA; } + else if(buf[4]=='r'){ type=RSA; } + } + i=0; + while(i0 && buf[i-1]==0x0d) i--; + if(start7){ + type=ECDSA; + } + i=0; + while(i0 && buf[i-1]==0x0d) i--; + if(start0){ + while(buf.length > i){ + if(buf[i++] == 0x0d){ + if(data == null){ + data = new byte[i - index - 1]; + System.arraycopy(buf, index, data, 0, i - index - 1); + } + else { + byte[] tmp = new byte[data.length + i - index - 1]; + System.arraycopy(data, 0, tmp, 0, data.length); + System.arraycopy(buf, index, tmp, data.length, i - index -1); + for(int j = 0; j < data.length; j++) data[j] = 0; // clear + data = tmp; + } + break; + } + } + if(buf[i]==0x0a) + i++; + index=i; + } + + if(data != null) + buffer.index = index; + + return data; + } + + private static boolean parseHeader(Buffer buffer, java.util.Hashtable v){ + byte[] buf = buffer.buffer; + int index = buffer.index; + String key = null; + String value = null; + for(int i = index; i < buf.length; i++){ + if(buf[i] == 0x0d){ + break; + } + if(buf[i] == ':'){ + key = new String(buf, index, i - index); + i++; + if(i < buf.length && buf[i] == ' '){ + i++; + } + index = i; + break; + } + } + + if(key == null) + return false; + + for(int i = index; i < buf.length; i++){ + if(buf[i] == 0x0d){ + value = new String(buf, index, i - index); + i++; + if(i < buf.length && buf[i] == 0x0a){ + i++; + } + index = i; + break; + } + } + + if(value != null){ + v.put(key, value); + buffer.index = index; + } + + return (key != null && value != null); + } + + void copy(KeyPair kpair){ + this.publickeyblob=kpair.publickeyblob; + this.vendor=kpair.vendor; + this.publicKeyComment=kpair.publicKeyComment; + this.cipher=kpair.cipher; + } + + class ASN1Exception extends Exception { + } + + class ASN1 { + byte[] buf; + int start; + int length; + ASN1(byte[] buf) throws ASN1Exception { + this(buf, 0, buf.length); + } + ASN1(byte[] buf, int start, int length) throws ASN1Exception { + this.buf = buf; + this.start = start; + this.length = length; + if(start+length>buf.length) + throw new ASN1Exception(); + } + int getType() { + return buf[start]&0xff; + } + boolean isSEQUENCE() { + return getType()==(0x30&0xff); + } + boolean isINTEGER() { + return getType()==(0x02&0xff); + } + boolean isOBJECT() { + return getType()==(0x06&0xff); + } + boolean isOCTETSTRING() { + return getType()==(0x04&0xff); + } + private int getLength(int[] indexp) { + int index=indexp[0]; + int length=buf[index++]&0xff; + if((length&0x80)!=0) { + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(buf[index++]&0xff); } + } + indexp[0]=index; + return length; + } + byte[] getContent() { + int[] indexp=new int[1]; + indexp[0]=start+1; + int length = getLength(indexp); + int index=indexp[0]; + byte[] tmp = new byte[length]; + System.arraycopy(buf, index, tmp, 0, tmp.length); + return tmp; + } + ASN1[] getContents() throws ASN1Exception { + int typ = buf[start]; + int[] indexp=new int[1]; + indexp[0]=start+1; + int length = getLength(indexp); + if(typ == 0x05){ + return new ASN1[0]; + } + int index=indexp[0]; + java.util.Vector values = new java.util.Vector(); + while(length>0) { + index++; length--; + int tmp=index; + indexp[0]=index; + int l=getLength(indexp); + index=indexp[0]; + length-=(index-tmp); + values.addElement(new ASN1(buf, tmp-1, 1+(index-tmp)+l)); + index+=l; + length-=l; + } + ASN1[] result = new ASN1[values.size()]; + for(int i = 0; i 0){ length=(length<<8)+(plain[index++]&0xff); } + } + + if(plain[index]!=0x02)return false; + index++; // INTEGER + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + P_array=new byte[length]; + System.arraycopy(plain, index, P_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + Q_array=new byte[length]; + System.arraycopy(plain, index, Q_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + G_array=new byte[length]; + System.arraycopy(plain, index, G_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + pub_array=new byte[length]; + System.arraycopy(plain, index, pub_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + prv_array=new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + index+=length; + + if(P_array!=null) + key_size = (new java.math.BigInteger(P_array)).bitLength(); + } + catch(Exception e){ + //System.err.println(e); + //e.printStackTrace(); + return false; + } + return true; + } + + public byte[] getPublicKeyBlob(){ + byte[] foo=super.getPublicKeyBlob(); + if(foo!=null) return foo; + + if(P_array==null) return null; + byte[][] tmp = new byte[5][]; + tmp[0] = sshdss; + tmp[1] = P_array; + tmp[2] = Q_array; + tmp[3] = G_array; + tmp[4] = pub_array; + return Buffer.fromBytes(tmp).buffer; + } + + private static final byte[] sshdss=Util.str2byte("ssh-dss"); + byte[] getKeyTypeName(){return sshdss;} + public int getKeyType(){return DSA;} + + public int getKeySize(){ + return key_size; + } + + public byte[] getSignature(byte[] data){ + try{ + Class c=Class.forName((String)jsch.getConfig("signature.dss")); + SignatureDSA dsa=(SignatureDSA)(c.newInstance()); + dsa.init(); + dsa.setPrvKey(prv_array, P_array, Q_array, G_array); + + dsa.update(data); + byte[] sig = dsa.sign(); + byte[][] tmp = new byte[2][]; + tmp[0] = sshdss; + tmp[1] = sig; + return Buffer.fromBytes(tmp).buffer; + } + catch(Exception e){ + //System.err.println("e "+e); + } + return null; + } + + public Signature getVerifier(){ + try{ + Class c=Class.forName((String)jsch.getConfig("signature.dss")); + SignatureDSA dsa=(SignatureDSA)(c.newInstance()); + dsa.init(); + + if(pub_array == null && P_array == null && getPublicKeyBlob()!=null){ + Buffer buf = new Buffer(getPublicKeyBlob()); + buf.getString(); + P_array = buf.getString(); + Q_array = buf.getString(); + G_array = buf.getString(); + pub_array = buf.getString(); + } + + dsa.setPubKey(pub_array, P_array, Q_array, G_array); + return dsa; + } + catch(Exception e){ + //System.err.println("e "+e); + } + return null; + } + + static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(7, "invalid key format"); + + byte[] P_array = tmp[1]; + byte[] Q_array = tmp[2]; + byte[] G_array = tmp[3]; + byte[] pub_array = tmp[4]; + byte[] prv_array = tmp[5]; + KeyPairDSA kpair = new KeyPairDSA(jsch, + P_array, Q_array, G_array, + pub_array, prv_array); + kpair.publicKeyComment = new String(tmp[6]); + kpair.vendor=VENDOR_OPENSSH; + return kpair; + } + + public byte[] forSSHAgent() throws JSchException { + if(isEncrypted()){ + throw new JSchException("key is encrypted."); + } + Buffer buf = new Buffer(); + buf.putString(sshdss); + buf.putString(P_array); + buf.putString(Q_array); + buf.putString(G_array); + buf.putString(pub_array); + buf.putString(prv_array); + buf.putString(Util.str2byte(publicKeyComment)); + byte[] result = new byte[buf.getLength()]; + buf.getByte(result, 0, result.length); + return result; + } + + public void dispose(){ + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/src/main/java/com/jcraft/jsch/KeyPairECDSA.java b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java new file mode 100644 index 00000000..e00c3ca0 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java @@ -0,0 +1,408 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class KeyPairECDSA extends KeyPair{ + + private static byte[][] oids = { + {(byte)0x06, (byte)0x08, (byte)0x2a, (byte)0x86, (byte)0x48, // 256 + (byte)0xce, (byte)0x3d, (byte)0x03, (byte)0x01, (byte)0x07}, + {(byte)0x06, (byte)0x05, (byte)0x2b, (byte)0x81, (byte)0x04, // 384 + (byte)0x00, (byte)0x22}, + {(byte)0x06, (byte)0x05, (byte)0x2b, (byte)0x81, (byte)0x04, //521 + (byte)0x00, (byte)0x23}, + }; + + private static String[] names = { + "nistp256", "nistp384", "nistp521" + }; + + private byte[] name=Util.str2byte(names[0]); + private byte[] r_array; + private byte[] s_array; + private byte[] prv_array; + + private int key_size=256; + + public KeyPairECDSA(JSch jsch){ + this(jsch, null, null, null, null); + } + + public KeyPairECDSA(JSch jsch , byte[] pubkey){ + this(jsch, null, null, null, null); + + if(pubkey!=null){ + byte[] name = new byte[8]; + System.arraycopy(pubkey, 11, name, 0, 8); + if(Util.array_equals(name, Util.str2byte("nistp384"))){ + key_size=384; + this.name=name; + } + if(Util.array_equals(name, Util.str2byte("nistp521"))){ + key_size=521; + this.name=name; + } + } + } + + public KeyPairECDSA(JSch jsch, + byte[] name, + byte[] r_array, + byte[] s_array, + byte[] prv_array){ + super(jsch); + if(name!=null) + this.name = name; + this.r_array = r_array; + this.s_array = s_array; + this.prv_array = prv_array; + if(prv_array!=null) + key_size = prv_array.length>=64 ? 521 : + (prv_array.length>=48 ? 384 : 256); + } + + void generate(int key_size) throws JSchException{ + this.key_size=key_size; + try{ + Class c=Class.forName(jsch.getConfig("keypairgen.ecdsa")); + KeyPairGenECDSA keypairgen=(KeyPairGenECDSA)(c.newInstance()); + keypairgen.init(key_size); + prv_array=keypairgen.getD(); + r_array=keypairgen.getR(); + s_array=keypairgen.getS(); + name=Util.str2byte(names[prv_array.length>=64 ? 2 : + (prv_array.length>=48 ? 1 : 0)]); + keypairgen=null; + } + catch(Exception e){ + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + private static final byte[] begin = + Util.str2byte("-----BEGIN EC PRIVATE KEY-----"); + private static final byte[] end = + Util.str2byte("-----END EC PRIVATE KEY-----"); + + byte[] getBegin(){ return begin; } + byte[] getEnd(){ return end; } + + byte[] getPrivateKey(){ + + byte[] tmp = new byte[1]; tmp[0]=1; + + byte[] oid = oids[ + (r_array.length>=64) ? 2 : + ((r_array.length>=48) ? 1 : 0) + ]; + + byte[] point = toPoint(r_array, s_array); + + int bar = ((point.length+1)&0x80)==0 ? 3 : 4; + byte[] foo = new byte[point.length+bar]; + System.arraycopy(point, 0, foo, bar, point.length); + foo[0]=0x03; // BITSTRING + if(bar==3){ + foo[1]=(byte)(point.length+1); + } + else { + foo[1]=(byte)0x81; + foo[2]=(byte)(point.length+1); + } + point = foo; + + int content= + 1+countLength(tmp.length) + tmp.length + + 1+countLength(prv_array.length) + prv_array.length + + 1+countLength(oid.length) + oid.length + + 1+countLength(point.length) + point.length; + + int total= + 1+countLength(content)+content; // SEQUENCE + + byte[] plain=new byte[total]; + int index=0; + index=writeSEQUENCE(plain, index, content); + index=writeINTEGER(plain, index, tmp); + index=writeOCTETSTRING(plain, index, prv_array); + index=writeDATA(plain, (byte)0xa0, index, oid); + index=writeDATA(plain, (byte)0xa1, index, point); + + return plain; + } + + boolean parse(byte[] plain){ + try{ + + if(vendor==VENDOR_FSECURE){ + /* + if(plain[0]!=0x30){ // FSecure + return true; + } + return false; + */ + return false; + } + else if(vendor==VENDOR_PUTTY){ + /* + Buffer buf=new Buffer(plain); + buf.skip(plain.length); + + try { + byte[][] tmp = buf.getBytes(1, ""); + prv_array = tmp[0]; + } + catch(JSchException e){ + return false; + } + + return true; + */ + return false; + } + + int index=0; + int length=0; + + if(plain[index]!=0x30)return false; + index++; // SEQUENCE + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + if(plain[index]!=0x02)return false; + index++; // INTEGER + + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + index+=length; + index++; // 0x04 + + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + prv_array=new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + + index+=length; + + index++; // 0xa0 + + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + byte[] oid_array=new byte[length]; + System.arraycopy(plain, index, oid_array, 0, length); + index+=length; + + for(int i = 0; i0){ length=(length<<8)+(plain[index++]&0xff); } + } + + byte[] Q_array=new byte[length]; + System.arraycopy(plain, index, Q_array, 0, length); + index+=length; + + byte[][] tmp = fromPoint(Q_array); + r_array = tmp[0]; + s_array = tmp[1]; + + if(prv_array!=null) + key_size = prv_array.length>=64 ? 521 : + (prv_array.length>=48 ? 384 : 256); + } + catch(Exception e){ + //System.err.println(e); + //e.printStackTrace(); + return false; + } + return true; + } + + public byte[] getPublicKeyBlob(){ + byte[] foo = super.getPublicKeyBlob(); + + if(foo!=null) return foo; + + if(r_array==null) return null; + + byte[][] tmp = new byte[3][]; + tmp[0] = Util.str2byte("ecdsa-sha2-"+new String(name)); + tmp[1] = name; + tmp[2] = new byte[1+r_array.length+s_array.length]; + tmp[2][0] = 4; // POINT_CONVERSION_UNCOMPRESSED + System.arraycopy(r_array, 0, tmp[2], 1, r_array.length); + System.arraycopy(s_array, 0, tmp[2], 1+r_array.length, s_array.length); + + return Buffer.fromBytes(tmp).buffer; + } + + byte[] getKeyTypeName(){ + return Util.str2byte("ecdsa-sha2-"+new String(name)); + } + public int getKeyType(){ + return ECDSA; + } + public int getKeySize(){ + return key_size; + } + + public byte[] getSignature(byte[] data){ + try{ + Class c=Class.forName((String)jsch.getConfig("ecdsa-sha2-"+new String(name))); + SignatureECDSA ecdsa=(SignatureECDSA)(c.newInstance()); + ecdsa.init(); + ecdsa.setPrvKey(prv_array); + + ecdsa.update(data); + byte[] sig = ecdsa.sign(); + + byte[][] tmp = new byte[2][]; + tmp[0] = Util.str2byte("ecdsa-sha2-"+new String(name)); + tmp[1] = sig; + return Buffer.fromBytes(tmp).buffer; + } + catch(Exception e){ + //System.err.println("e "+e); + } + return null; + } + + public Signature getVerifier(){ + try{ + Class c=Class.forName((String)jsch.getConfig("ecdsa-sha2-"+new String(name))); + final SignatureECDSA ecdsa=(SignatureECDSA)(c.newInstance()); + ecdsa.init(); + + if(r_array == null && s_array == null && getPublicKeyBlob()!=null){ + Buffer buf = new Buffer(getPublicKeyBlob()); + buf.getString(); // ecdsa-sha2-nistp256 + buf.getString(); // nistp256 + byte[][] tmp = fromPoint(buf.getString()); + r_array = tmp[0]; + s_array = tmp[1]; + } + ecdsa.setPubKey(r_array, s_array); + return ecdsa; + } + catch(Exception e){ + //System.err.println("e "+e); + } + return null; + } + + static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(5, "invalid key format"); + + byte[] name = tmp[1]; // nistp256 + byte[][] foo = fromPoint(tmp[2]); + byte[] r_array = foo[0]; + byte[] s_array = foo[1]; + + byte[] prv_array = tmp[3]; + KeyPairECDSA kpair = new KeyPairECDSA(jsch, + name, + r_array, s_array, + prv_array); + kpair.publicKeyComment = new String(tmp[4]); + kpair.vendor=VENDOR_OPENSSH; + return kpair; + } + + public byte[] forSSHAgent() throws JSchException { + if(isEncrypted()){ + throw new JSchException("key is encrypted."); + } + Buffer buf = new Buffer(); + buf.putString(Util.str2byte("ecdsa-sha2-"+new String(name))); + buf.putString(name); + buf.putString(toPoint(r_array, s_array)); + buf.putString(prv_array); + buf.putString(Util.str2byte(publicKeyComment)); + byte[] result = new byte[buf.getLength()]; + buf.getByte(result, 0, result.length); + return result; + } + + static byte[] toPoint(byte[] r_array, byte[] s_array) { + byte[] tmp = new byte[1+r_array.length+s_array.length]; + tmp[0]=0x04; + System.arraycopy(r_array, 0, tmp, 1, r_array.length); + System.arraycopy(s_array, 0, tmp, 1+r_array.length, s_array.length); + return tmp; + } + + static byte[][] fromPoint(byte[] point) { + int i = 0; + while(point[i]!=4) i++; + i++; + byte[][] tmp = new byte[2][]; + byte[] r_array = new byte[(point.length-i)/2]; + byte[] s_array = new byte[(point.length-i)/2]; + // point[0] == 0x04 == POINT_CONVERSION_UNCOMPRESSED + System.arraycopy(point, i, r_array, 0, r_array.length); + System.arraycopy(point, i+r_array.length, s_array, 0, s_array.length); + tmp[0] = r_array; + tmp[1] = s_array; + + return tmp; + } + + public void dispose(){ + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java b/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java new file mode 100644 index 00000000..2c7ee097 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface KeyPairGenDSA{ + void init(int key_size) throws Exception; + byte[] getX(); + byte[] getY(); + byte[] getP(); + byte[] getQ(); + byte[] getG(); +} diff --git a/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java b/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java new file mode 100644 index 00000000..18369af5 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java @@ -0,0 +1,37 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface KeyPairGenECDSA{ + void init(int key_size) throws Exception; + byte[] getD(); + byte[] getR(); + byte[] getS(); +} diff --git a/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java b/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java new file mode 100644 index 00000000..78334f5a --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java @@ -0,0 +1,43 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface KeyPairGenRSA{ + void init(int key_size) throws Exception; + byte[] getD(); + byte[] getE(); + byte[] getN(); + + byte[] getC(); + byte[] getEP(); + byte[] getEQ(); + byte[] getP(); + byte[] getQ(); +} diff --git a/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java new file mode 100644 index 00000000..33f4b6f6 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java @@ -0,0 +1,363 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.Vector; +import java.math.BigInteger; + +public class KeyPairPKCS8 extends KeyPair { + private static final byte[] rsaEncryption = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, + (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01 + }; + + private static final byte[] dsaEncryption = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0xce, + (byte)0x38, (byte)0x04, (byte)0x1 + }; + + private static final byte[] pbes2 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x0d + }; + + private static final byte[] pbkdf2 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x0c + }; + + private static final byte[] aes128cbc = { + (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, + (byte)0x03, (byte)0x04, (byte)0x01, (byte)0x02 + }; + + private static final byte[] aes192cbc = { + (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, + (byte)0x03, (byte)0x04, (byte)0x01, (byte)0x16 + }; + + private static final byte[] aes256cbc = { + (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, + (byte)0x03, (byte)0x04, (byte)0x01, (byte)0x2a + }; + + private static final byte[] pbeWithMD5AndDESCBC = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x03 + }; + + private KeyPair kpair = null; + + public KeyPairPKCS8(JSch jsch){ + super(jsch); + } + + void generate(int key_size) throws JSchException{ + } + + private static final byte[] begin=Util.str2byte("-----BEGIN DSA PRIVATE KEY-----"); + private static final byte[] end=Util.str2byte("-----END DSA PRIVATE KEY-----"); + + byte[] getBegin(){ return begin; } + byte[] getEnd(){ return end; } + + byte[] getPrivateKey(){ + return null; + } + + boolean parse(byte[] plain){ + + /* from RFC5208 + PrivateKeyInfo ::= SEQUENCE { + version Version, + privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + privateKey PrivateKey, + attributes [0] IMPLICIT Attributes OPTIONAL + } + Version ::= INTEGER + PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + PrivateKey ::= OCTET STRING + Attributes ::= SET OF Attribute + } + */ + + try{ + Vector values = new Vector(); + + ASN1[] contents = null; + ASN1 asn1 = new ASN1(plain); + contents = asn1.getContents(); + + ASN1 privateKeyAlgorithm = contents[1]; + ASN1 privateKey = contents[2]; + + contents = privateKeyAlgorithm.getContents(); + byte[] privateKeyAlgorithmID = contents[0].getContent(); + contents = contents[1].getContents(); + if(contents.length>0){ + for(int i = 0; i < contents.length; i++){ + values.addElement(contents[i].getContent()); + } + } + + byte[] _data = privateKey.getContent(); + + KeyPair _kpair = null; + if(Util.array_equals(privateKeyAlgorithmID, rsaEncryption)){ + _kpair = new KeyPairRSA(jsch); + _kpair.copy(this); + if(_kpair.parse(_data)){ + kpair = _kpair; + } + } + else if(Util.array_equals(privateKeyAlgorithmID, dsaEncryption)){ + asn1 = new ASN1(_data); + if(values.size() == 0) { // embedded DSA parameters format + /* + SEQUENCE + SEQUENCE + INTEGER // P_array + INTEGER // Q_array + INTEGER // G_array + INTEGER // prv_array + */ + contents = asn1.getContents(); + byte[] bar = contents[1].getContent(); + contents = contents[0].getContents(); + for(int i = 0; i < contents.length; i++){ + values.addElement(contents[i].getContent()); + } + values.addElement(bar); + } + else { + /* + INTEGER // prv_array + */ + values.addElement(asn1.getContent()); + } + + byte[] P_array = (byte[])values.elementAt(0); + byte[] Q_array = (byte[])values.elementAt(1); + byte[] G_array = (byte[])values.elementAt(2); + byte[] prv_array = (byte[])values.elementAt(3); + // Y = g^X mode p + byte[] pub_array = + (new BigInteger(G_array)). + modPow(new BigInteger(prv_array), new BigInteger(P_array)). + toByteArray(); + + KeyPairDSA _key = new KeyPairDSA(jsch, + P_array, Q_array, G_array, + pub_array, prv_array); + plain = _key.getPrivateKey(); + + _kpair = new KeyPairDSA(jsch); + _kpair.copy(this); + if(_kpair.parse(plain)){ + kpair = _kpair; + } + } + } + catch(ASN1Exception e){ + return false; + } + catch(Exception e){ + //System.err.println(e); + return false; + } + return kpair != null; + } + + public byte[] getPublicKeyBlob(){ + return kpair.getPublicKeyBlob(); + } + + byte[] getKeyTypeName(){ return kpair.getKeyTypeName();} + public int getKeyType(){return kpair.getKeyType();} + + public int getKeySize(){ + return kpair.getKeySize(); + } + + public byte[] getSignature(byte[] data){ + return kpair.getSignature(data); + } + + public Signature getVerifier(){ + return kpair.getVerifier(); + } + + public byte[] forSSHAgent() throws JSchException { + return kpair.forSSHAgent(); + } + + public boolean decrypt(byte[] _passphrase){ + if(!isEncrypted()){ + return true; + } + if(_passphrase==null){ + return !isEncrypted(); + } + + /* + SEQUENCE + SEQUENCE + OBJECT :PBES2 + SEQUENCE + SEQUENCE + OBJECT :PBKDF2 + SEQUENCE + OCTET STRING [HEX DUMP]:E4E24ADC9C00BD4D + INTEGER :0800 + SEQUENCE + OBJECT :aes-128-cbc + OCTET STRING [HEX DUMP]:5B66E6B3BF03944C92317BC370CC3AD0 + OCTET STRING [HEX DUMP]: + +or + + SEQUENCE + SEQUENCE + OBJECT :pbeWithMD5AndDES-CBC + SEQUENCE + OCTET STRING [HEX DUMP]:DBF75ECB69E3C0FC + INTEGER :0800 + OCTET STRING [HEX DUMP] + */ + + try{ + + ASN1[] contents = null; + ASN1 asn1 = new ASN1(data); + + contents = asn1.getContents(); + + byte[] _data = contents[1].getContent(); + + ASN1 pbes = contents[0]; + contents = pbes.getContents(); + byte[] pbesid = contents[0].getContent(); + ASN1 pbesparam = contents[1]; + + byte[] salt = null; + int iterations = 0; + byte[] iv = null; + byte[] encryptfuncid = null; + + if(Util.array_equals(pbesid, pbes2)){ + contents = pbesparam.getContents(); + ASN1 pbkdf = contents[0]; + ASN1 encryptfunc = contents[1]; + contents = pbkdf.getContents(); + byte[] pbkdfid = contents[0].getContent(); + ASN1 pbkdffunc = contents[1]; + contents = pbkdffunc.getContents(); + salt = contents[0].getContent(); + iterations = + Integer.parseInt((new BigInteger(contents[1].getContent())).toString()); + + contents = encryptfunc.getContents(); + encryptfuncid = contents[0].getContent(); + iv = contents[1].getContent(); + } + else if(Util.array_equals(pbesid, pbeWithMD5AndDESCBC)){ + // not supported + return false; + } + else { + return false; + } + + Cipher cipher=getCipher(encryptfuncid); + if(cipher==null) return false; + + byte[] key=null; + try{ + Class c=Class.forName((String)jsch.getConfig("pbkdf")); + PBKDF tmp=(PBKDF)(c.newInstance()); + key = tmp.getKey(_passphrase, salt, iterations, cipher.getBlockSize()); + } + catch(Exception ee){ + } + + if(key==null){ + return false; + } + + cipher.init(Cipher.DECRYPT_MODE, key, iv); + Util.bzero(key); + byte[] plain=new byte[_data.length]; + cipher.update(_data, 0, _data.length, plain, 0); + if(parse(plain)){ + encrypted=false; + return true; + } + } + catch(ASN1Exception e){ + // System.err.println(e); + } + catch(Exception e){ + // System.err.println(e); + } + + return false; + } + + Cipher getCipher(byte[] id){ + Cipher cipher=null; + String name = null; + try{ + if(Util.array_equals(id, aes128cbc)){ + name="aes128-cbc"; + } + else if(Util.array_equals(id, aes192cbc)){ + name="aes192-cbc"; + } + else if(Util.array_equals(id, aes256cbc)){ + name="aes256-cbc"; + } + Class c=Class.forName((String)jsch.getConfig(name)); + cipher=(Cipher)(c.newInstance()); + } + catch(Exception e){ + if(JSch.getLogger().isEnabled(Logger.FATAL)){ + String message=""; + if(name==null){ + message="unknown oid: "+Util.toHex(id); + } + else { + message="function "+name+" is not supported"; + } + JSch.getLogger().log(Logger.FATAL, "PKCS8: "+message); + } + } + return cipher; + } +} diff --git a/src/main/java/com/jcraft/jsch/KeyPairRSA.java b/src/main/java/com/jcraft/jsch/KeyPairRSA.java new file mode 100644 index 00000000..eb0d35b6 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KeyPairRSA.java @@ -0,0 +1,418 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.math.BigInteger; + +public class KeyPairRSA extends KeyPair{ + private byte[] n_array; // modulus p multiply q + private byte[] pub_array; // e + private byte[] prv_array; // d e^-1 mod (p-1)(q-1) + + private byte[] p_array; // prime p + private byte[] q_array; // prime q + private byte[] ep_array; // prime exponent p dmp1 == prv mod (p-1) + private byte[] eq_array; // prime exponent q dmq1 == prv mod (q-1) + private byte[] c_array; // coefficient iqmp == modinv(q, p) == q^-1 mod p + + private int key_size=1024; + + public KeyPairRSA(JSch jsch){ + this(jsch, null, null, null); + } + + public KeyPairRSA(JSch jsch, + byte[] n_array, + byte[] pub_array, + byte[] prv_array){ + super(jsch); + this.n_array = n_array; + this.pub_array = pub_array; + this.prv_array = prv_array; + if(n_array!=null){ + key_size = (new java.math.BigInteger(n_array)).bitLength(); + } + } + + void generate(int key_size) throws JSchException{ + this.key_size=key_size; + try{ + Class c=Class.forName(jsch.getConfig("keypairgen.rsa")); + KeyPairGenRSA keypairgen=(KeyPairGenRSA)(c.newInstance()); + keypairgen.init(key_size); + pub_array=keypairgen.getE(); + prv_array=keypairgen.getD(); + n_array=keypairgen.getN(); + + p_array=keypairgen.getP(); + q_array=keypairgen.getQ(); + ep_array=keypairgen.getEP(); + eq_array=keypairgen.getEQ(); + c_array=keypairgen.getC(); + + keypairgen=null; + } + catch(Exception e){ + //System.err.println("KeyPairRSA: "+e); + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + + private static final byte[] begin=Util.str2byte("-----BEGIN RSA PRIVATE KEY-----"); + private static final byte[] end=Util.str2byte("-----END RSA PRIVATE KEY-----"); + + byte[] getBegin(){ return begin; } + byte[] getEnd(){ return end; } + + byte[] getPrivateKey(){ + int content= + 1+countLength(1) + 1 + // INTEGER + 1+countLength(n_array.length) + n_array.length + // INTEGER N + 1+countLength(pub_array.length) + pub_array.length + // INTEGER pub + 1+countLength(prv_array.length) + prv_array.length+ // INTEGER prv + 1+countLength(p_array.length) + p_array.length+ // INTEGER p + 1+countLength(q_array.length) + q_array.length+ // INTEGER q + 1+countLength(ep_array.length) + ep_array.length+ // INTEGER ep + 1+countLength(eq_array.length) + eq_array.length+ // INTEGER eq + 1+countLength(c_array.length) + c_array.length; // INTEGER c + + int total= + 1+countLength(content)+content; // SEQUENCE + + byte[] plain=new byte[total]; + int index=0; + index=writeSEQUENCE(plain, index, content); + index=writeINTEGER(plain, index, new byte[1]); // 0 + index=writeINTEGER(plain, index, n_array); + index=writeINTEGER(plain, index, pub_array); + index=writeINTEGER(plain, index, prv_array); + index=writeINTEGER(plain, index, p_array); + index=writeINTEGER(plain, index, q_array); + index=writeINTEGER(plain, index, ep_array); + index=writeINTEGER(plain, index, eq_array); + index=writeINTEGER(plain, index, c_array); + return plain; + } + + boolean parse(byte [] plain){ + + try{ + int index=0; + int length=0; + + if(vendor==VENDOR_PUTTY){ + Buffer buf = new Buffer(plain); + buf.skip(plain.length); + + try { + byte[][] tmp = buf.getBytes(4, ""); + prv_array = tmp[0]; + p_array = tmp[1]; + q_array = tmp[2]; + c_array = tmp[3]; + } + catch(JSchException e){ + return false; + } + + getEPArray(); + getEQArray(); + + return true; + } + + if(vendor==VENDOR_FSECURE){ + if(plain[index]!=0x30){ // FSecure + Buffer buf=new Buffer(plain); + pub_array=buf.getMPIntBits(); + prv_array=buf.getMPIntBits(); + n_array=buf.getMPIntBits(); + byte[] u_array=buf.getMPIntBits(); + p_array=buf.getMPIntBits(); + q_array=buf.getMPIntBits(); + if(n_array!=null){ + key_size = (new java.math.BigInteger(n_array)).bitLength(); + } + + getEPArray(); + getEQArray(); + getCArray(); + + return true; + } + return false; + } + + /* + Key must be in the following ASN.1 DER encoding, + RSAPrivateKey ::= SEQUENCE { + version Version, + modulus INTEGER, -- n + publicExponent INTEGER, -- e + privateExponent INTEGER, -- d + prime1 INTEGER, -- p + prime2 INTEGER, -- q + exponent1 INTEGER, -- d mod (p-1) + exponent2 INTEGER, -- d mod (q-1) + coefficient INTEGER, -- (inverse of q) mod p + otherPrimeInfos OtherPrimeInfos OPTIONAL + } + */ + + index++; // SEQUENCE + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + + if(plain[index]!=0x02)return false; + index++; // INTEGER + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + n_array=new byte[length]; + System.arraycopy(plain, index, n_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + pub_array=new byte[length]; + System.arraycopy(plain, index, pub_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + prv_array=new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + p_array=new byte[length]; + System.arraycopy(plain, index, p_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + q_array=new byte[length]; + System.arraycopy(plain, index, q_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + ep_array=new byte[length]; + System.arraycopy(plain, index, ep_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + eq_array=new byte[length]; + System.arraycopy(plain, index, eq_array, 0, length); + index+=length; + + index++; + length=plain[index++]&0xff; + if((length&0x80)!=0){ + int foo=length&0x7f; length=0; + while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); } + } + c_array=new byte[length]; + System.arraycopy(plain, index, c_array, 0, length); + index+=length; + + if(n_array!=null){ + key_size = (new java.math.BigInteger(n_array)).bitLength(); + } + + } + catch(Exception e){ + //System.err.println(e); + return false; + } + return true; + } + + public byte[] getPublicKeyBlob(){ + byte[] foo=super.getPublicKeyBlob(); + if(foo!=null) return foo; + + if(pub_array==null) return null; + byte[][] tmp = new byte[3][]; + tmp[0] = sshrsa; + tmp[1] = pub_array; + tmp[2] = n_array; + return Buffer.fromBytes(tmp).buffer; + } + + private static final byte[] sshrsa=Util.str2byte("ssh-rsa"); + byte[] getKeyTypeName(){return sshrsa;} + public int getKeyType(){return RSA;} + + public int getKeySize(){ + return key_size; + } + + public byte[] getSignature(byte[] data){ + try{ + Class c=Class.forName((String)jsch.getConfig("signature.rsa")); + SignatureRSA rsa=(SignatureRSA)(c.newInstance()); + rsa.init(); + rsa.setPrvKey(prv_array, n_array); + + rsa.update(data); + byte[] sig = rsa.sign(); + byte[][] tmp = new byte[2][]; + tmp[0] = sshrsa; + tmp[1] = sig; + return Buffer.fromBytes(tmp).buffer; + } + catch(Exception e){ + } + return null; + } + + public Signature getVerifier(){ + try{ + Class c=Class.forName((String)jsch.getConfig("signature.rsa")); + SignatureRSA rsa=(SignatureRSA)(c.newInstance()); + rsa.init(); + + if(pub_array == null && n_array == null && getPublicKeyBlob()!=null){ + Buffer buf = new Buffer(getPublicKeyBlob()); + buf.getString(); + pub_array = buf.getString(); + n_array = buf.getString(); + } + + rsa.setPubKey(pub_array, n_array); + return rsa; + } + catch(Exception e){ + } + return null; + } + + static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(8, "invalid key format"); + + byte[] n_array = tmp[1]; + byte[] pub_array = tmp[2]; + byte[] prv_array = tmp[3]; + KeyPairRSA kpair = new KeyPairRSA(jsch, n_array, pub_array, prv_array); + kpair.c_array = tmp[4]; // iqmp + kpair.p_array = tmp[5]; + kpair.q_array = tmp[6]; + kpair.publicKeyComment = new String(tmp[7]); + kpair.vendor=VENDOR_OPENSSH; + return kpair; + } + + public byte[] forSSHAgent() throws JSchException { + if(isEncrypted()){ + throw new JSchException("key is encrypted."); + } + Buffer buf = new Buffer(); + buf.putString(sshrsa); + buf.putString(n_array); + buf.putString(pub_array); + buf.putString(prv_array); + buf.putString(getCArray()); + buf.putString(p_array); + buf.putString(q_array); + buf.putString(Util.str2byte(publicKeyComment)); + byte[] result = new byte[buf.getLength()]; + buf.getByte(result, 0, result.length); + return result; + } + + private byte[] getEPArray(){ + if(ep_array==null){ + ep_array=(new BigInteger(prv_array)).mod(new BigInteger(p_array).subtract(BigInteger.ONE)).toByteArray(); + } + return ep_array; + } + + private byte[] getEQArray(){ + if(eq_array==null){ + eq_array=(new BigInteger(prv_array)).mod(new BigInteger(q_array).subtract(BigInteger.ONE)).toByteArray(); + } + return eq_array; + } + + private byte[] getCArray(){ + if(c_array==null){ + c_array=(new BigInteger(q_array)).modInverse(new BigInteger(p_array)).toByteArray(); + } + return c_array; + } + + public void dispose(){ + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/src/main/java/com/jcraft/jsch/KnownHosts.java b/src/main/java/com/jcraft/jsch/KnownHosts.java new file mode 100644 index 00000000..f6b7049b --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KnownHosts.java @@ -0,0 +1,589 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; + +public +class KnownHosts implements HostKeyRepository{ + private static final String _known_hosts="known_hosts"; + + private JSch jsch=null; + private String known_hosts=null; + private java.util.Vector pool=null; + + private MAC hmacsha1=null; + + KnownHosts(JSch jsch){ + super(); + this.jsch=jsch; + this.hmacsha1 = getHMACSHA1(); + pool=new java.util.Vector(); + } + + void setKnownHosts(String filename) throws JSchException{ + try{ + known_hosts = filename; + FileInputStream fis=new FileInputStream(Util.checkTilde(filename)); + setKnownHosts(fis); + } + catch(FileNotFoundException e){ + // The non-existing file should be allowed. + } + } + void setKnownHosts(InputStream input) throws JSchException{ + pool.removeAllElements(); + StringBuffer sb=new StringBuffer(); + byte i; + int j; + boolean error=false; + try{ + InputStream fis=input; + String host; + String key=null; + int type; + byte[] buf=new byte[1024]; + int bufl=0; +loop: + while(true){ + bufl=0; + while(true){ + j=fis.read(); + if(j==-1){ + if(bufl==0){ break loop; } + else{ break; } + } + if(j==0x0d){ continue; } + if(j==0x0a){ break; } + if(buf.length<=bufl){ + if(bufl>1024*10) break; // too long... + byte[] newbuf=new byte[buf.length*2]; + System.arraycopy(buf, 0, newbuf, 0, buf.length); + buf=newbuf; + } + buf[bufl++]=(byte)j; + } + + j=0; + while(j=bufl){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + sb.setLength(0); + while(j=bufl || host.length()==0){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + while(j=bufl || host.length()==0){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + while(j=bufl){ + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + while(j1 + ){ + return check(host.substring(1, host.indexOf("]:")), key); + } + + return result; + } + + public void add(HostKey hostkey, UserInfo userinfo){ + int type=hostkey.type; + String host=hostkey.getHost(); + byte[] key=hostkey.key; + + HostKey hk=null; + synchronized(pool){ + for(int i=0; i1){ + HostKey[] tmp = + getHostKey(host.substring(1, host.indexOf("]:")), type); + if(tmp.length > 0){ + HostKey[] bar = new HostKey[foo.length + tmp.length]; + System.arraycopy(foo, 0, bar, 0, foo.length); + System.arraycopy(tmp, 0, bar, foo.length, tmp.length); + foo = bar; + } + } + return foo; + } + } + public void remove(String host, String type){ + remove(host, type, null); + } + public void remove(String host, String type, byte[] key){ + boolean sync=false; + synchronized(pool){ + for(int i=0; i0){ + String data=this.host.substring(HASH_MAGIC.length()); + String _salt=data.substring(0, data.indexOf(HASH_DELIM)); + String _hash=data.substring(data.indexOf(HASH_DELIM)+1); + salt=Util.fromBase64(Util.str2byte(_salt), 0, _salt.length()); + hash=Util.fromBase64(Util.str2byte(_hash), 0, _hash.length()); + if(salt.length!=20 || // block size of hmac-sha1 + hash.length!=20){ + salt=null; + hash=null; + return; + } + hashed=true; + } + } + + boolean isMatched(String _host){ + if(!hashed){ + return super.isMatched(_host); + } + MAC macsha1=getHMACSHA1(); + try{ + synchronized(macsha1){ + macsha1.init(salt); + byte[] foo=Util.str2byte(_host); + macsha1.update(foo, 0, foo.length); + byte[] bar=new byte[macsha1.getBlockSize()]; + macsha1.doFinal(bar, 0); + return Util.array_equals(hash, bar); + } + } + catch(Exception e){ + System.out.println(e); + } + return false; + } + + boolean isHashed(){ + return hashed; + } + + void hash(){ + if(hashed) + return; + MAC macsha1=getHMACSHA1(); + if(salt==null){ + Random random=Session.random; + synchronized(random){ + salt=new byte[macsha1.getBlockSize()]; + random.fill(salt, 0, salt.length); + } + } + try{ + synchronized(macsha1){ + macsha1.init(salt); + byte[] foo=Util.str2byte(host); + macsha1.update(foo, 0, foo.length); + hash=new byte[macsha1.getBlockSize()]; + macsha1.doFinal(hash, 0); + } + } + catch(Exception e){ + } + host=HASH_MAGIC+Util.byte2str(Util.toBase64(salt, 0, salt.length))+ + HASH_DELIM+Util.byte2str(Util.toBase64(hash, 0, hash.length)); + hashed=true; + } + } +} diff --git a/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java b/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java new file mode 100644 index 00000000..227995c4 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java @@ -0,0 +1,151 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.Vector; + +class LocalIdentityRepository implements IdentityRepository { + private static final String name = "Local Identity Repository"; + + private Vector identities = new Vector(); + private JSch jsch; + + LocalIdentityRepository(JSch jsch){ + this.jsch = jsch; + } + + public String getName(){ + return name; + } + + public int getStatus(){ + return RUNNING; + } + + public synchronized Vector getIdentities() { + removeDupulicates(); + Vector v = new Vector(); + for(int i=0; i + *

  • Host
  • + *
  • User
  • + *
  • Hostname
  • + *
  • Port
  • + *
  • PreferredAuthentications
  • + *
  • IdentityFile
  • + *
  • NumberOfPasswordPrompts
  • + *
  • ConnectTimeout
  • + *
  • HostKeyAlias
  • + *
  • UserKnownHostsFile
  • + *
  • KexAlgorithms
  • + *
  • HostKeyAlgorithms
  • + *
  • Ciphers
  • + *
  • Macs
  • + *
  • Compression
  • + *
  • CompressionLevel
  • + *
  • ForwardAgent
  • + *
  • RequestTTY
  • + *
  • ServerAliveInterval
  • + *
  • LocalForward
  • + *
  • RemoteForward
  • + *
  • ClearAllForwardings
  • + * + * + * @see ConfigRepository + */ +public class OpenSSHConfig implements ConfigRepository { + + /** + * Parses the given string, and returns an instance of ConfigRepository. + * + * @param conf string, which includes OpenSSH's config + * @return an instanceof OpenSSHConfig + */ + public static OpenSSHConfig parse(String conf) throws IOException { + Reader r = new StringReader(conf); + try { + return new OpenSSHConfig(r); + } + finally { + r.close(); + } + } + + /** + * Parses the given file, and returns an instance of ConfigRepository. + * + * @param file OpenSSH's config file + * @return an instanceof OpenSSHConfig + */ + public static OpenSSHConfig parseFile(String file) throws IOException { + Reader r = new FileReader(Util.checkTilde(file)); + try { + return new OpenSSHConfig(r); + } + finally { + r.close(); + } + } + + OpenSSHConfig(Reader r) throws IOException { + _parse(r); + } + + private final Hashtable config = new Hashtable(); + private final Vector hosts = new Vector(); + + private void _parse(Reader r) throws IOException { + BufferedReader br = new BufferedReader(r); + + String host = ""; + Vector/**/ kv = new Vector(); + String l = null; + + while((l = br.readLine()) != null){ + l = l.trim(); + if(l.length() == 0 || l.startsWith("#")) + continue; + + String[] key_value = l.split("[= \t]", 2); + for(int i = 0; i < key_value.length; i++) + key_value[i] = key_value[i].trim(); + + if(key_value.length <= 1) + continue; + + if(key_value[0].equals("Host")){ + config.put(host, kv); + hosts.addElement(host); + host = key_value[1]; + kv = new Vector(); + } + else { + kv.addElement(key_value); + } + } + config.put(host, kv); + hosts.addElement(host); + } + + public Config getConfig(String host) { + return new MyConfig(host); + } + + private static final Hashtable keymap = new Hashtable(); + static { + keymap.put("kex", "KexAlgorithms"); + keymap.put("server_host_key", "HostKeyAlgorithms"); + keymap.put("cipher.c2s", "Ciphers"); + keymap.put("cipher.s2c", "Ciphers"); + keymap.put("mac.c2s", "Macs"); + keymap.put("mac.s2c", "Macs"); + keymap.put("compression.s2c", "Compression"); + keymap.put("compression.c2s", "Compression"); + keymap.put("compression_level", "CompressionLevel"); + keymap.put("MaxAuthTries", "NumberOfPasswordPrompts"); + } + + class MyConfig implements Config { + + private String host; + private Vector _configs = new Vector(); + + MyConfig(String host){ + this.host = host; + + _configs.addElement(config.get("")); + + byte[] _host = Util.str2byte(host); + if(hosts.size() > 1){ + for(int i = 1; i < hosts.size(); i++){ + String patterns[] = ((String)hosts.elementAt(i)).split("[ \t]"); + for(int j = 0; j < patterns.length; j++){ + boolean negate = false; + String foo = patterns[j].trim(); + if(foo.startsWith("!")){ + negate = true; + foo = foo.substring(1).trim(); + } + if(Util.glob(Util.str2byte(foo), _host)){ + if(!negate){ + _configs.addElement(config.get((String)hosts.elementAt(i))); + } + } + else if(negate){ + _configs.addElement(config.get((String)hosts.elementAt(i))); + } + } + } + } + } + + private String find(String key) { + if(keymap.get(key)!=null) { + key = (String)keymap.get(key); + } + key = key.toUpperCase(); + String value = null; + for(int i = 0; i < _configs.size(); i++) { + Vector v = (Vector)_configs.elementAt(i); + for(int j = 0; j < v.size(); j++) { + String[] kv = (String[])v.elementAt(j); + if(kv[0].toUpperCase().equals(key)) { + value = kv[1]; + break; + } + } + if(value != null) + break; + } + // TODO: The following change should be applied, + // but it is breaking changes. + // The consensus is required to enable it. + /* + if(value!=null && + (key.equals("SERVERALIVEINTERVAL") || + key.equals("CONNECTTIMEOUT"))){ + try { + int timeout = Integer.parseInt(value); + value = Integer.toString(timeout*1000); + } catch (NumberFormatException e) { + } + } + */ + return value; + } + + private String[] multiFind(String key) { + key = key.toUpperCase(); + Vector value = new Vector(); + for(int i = 0; i < _configs.size(); i++) { + Vector v = (Vector)_configs.elementAt(i); + for(int j = 0; j < v.size(); j++) { + String[] kv = (String[])v.elementAt(j); + if(kv[0].toUpperCase().equals(key)) { + String foo = kv[1]; + if(foo != null) { + value.remove(foo); + value.addElement(foo); + } + } + } + } + String[] result = new String[value.size()]; + value.toArray(result); + return result; + } + + public String getHostname(){ return find("Hostname"); } + public String getUser(){ return find("User"); } + public int getPort(){ + String foo = find("Port"); + int port = -1; + try { + port = Integer.parseInt(foo); + } + catch(NumberFormatException e){ + // wrong format + } + return port; + } + public String getValue(String key){ + if(key.equals("compression.s2c") || + key.equals("compression.c2s")) { + String foo = find(key); + if(foo == null || foo.equals("no")) + return "none,zlib@openssh.com,zlib"; + return "zlib@openssh.com,zlib,none"; + } + return find(key); + } + public String[] getValues(String key){ return multiFind(key); } + } +} diff --git a/src/main/java/com/jcraft/jsch/PBKDF.java b/src/main/java/com/jcraft/jsch/PBKDF.java new file mode 100644 index 00000000..44619a37 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/PBKDF.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface PBKDF { + byte[] getKey(byte[] pass, byte[] salt, int iteration, int size); +} diff --git a/src/main/java/com/jcraft/jsch/Packet.java b/src/main/java/com/jcraft/jsch/Packet.java new file mode 100644 index 00000000..0a9a5c13 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/Packet.java @@ -0,0 +1,115 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class Packet{ + + private static Random random=null; + static void setRandom(Random foo){ random=foo;} + + Buffer buffer; + byte[] ba4=new byte[4]; + public Packet(Buffer buffer){ + this.buffer=buffer; + } + public void reset(){ + buffer.index=5; + } + void padding(int bsize){ + int len=buffer.index; + int pad=(-len)&(bsize-1); + if(pad>>24); + ba4[1]=(byte)(len>>>16); + ba4[2]=(byte)(len>>>8); + ba4[3]=(byte)(len); + System.arraycopy(ba4, 0, buffer.buffer, 0, 4); + buffer.buffer[4]=(byte)pad; + synchronized(random){ + random.fill(buffer.buffer, buffer.index, pad); + } + buffer.skip(pad); + //buffer.putPad(pad); +/* +for(int i=0; i0){ + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + out.write(Util.str2byte("CONNECT "+host+":"+port+" HTTP/1.0\r\n")); + + if(user!=null && passwd!=null){ + byte[] code=Util.str2byte(user+":"+passwd); + code=Util.toBase64(code, 0, code.length); + out.write(Util.str2byte("Proxy-Authorization: Basic ")); + out.write(code); + out.write(Util.str2byte("\r\n")); + } + + out.write(Util.str2byte("\r\n")); + out.flush(); + + int foo=0; + + StringBuffer sb=new StringBuffer(); + while(foo>=0){ + foo=in.read(); if(foo!=13){sb.append((char)foo); continue;} + foo=in.read(); if(foo!=10){continue;} + break; + } + if(foo<0){ + throw new IOException(); + } + + String response=sb.toString(); + String reason="Unknow reason"; + int code=-1; + try{ + foo=response.indexOf(' '); + int bar=response.indexOf(' ', foo+1); + code=Integer.parseInt(response.substring(foo+1, bar)); + reason=response.substring(bar+1); + } + catch(Exception e){ + } + if(code!=200){ + throw new IOException("proxy error: "+reason); + } + + /* + while(foo>=0){ + foo=in.read(); if(foo!=13) continue; + foo=in.read(); if(foo!=10) continue; + foo=in.read(); if(foo!=13) continue; + foo=in.read(); if(foo!=10) continue; + break; + } + */ + + int count=0; + while(true){ + count=0; + while(foo>=0){ + foo=in.read(); if(foo!=13){count++; continue;} + foo=in.read(); if(foo!=10){continue;} + break; + } + if(foo<0){ + throw new IOException(); + } + if(count==0)break; + } + } + catch(RuntimeException e){ + throw e; + } + catch(Exception e){ + try{ if(socket!=null)socket.close(); } + catch(Exception eee){ + } + String message="ProxyHTTP: "+e.toString(); + if(e instanceof Throwable) + throw new JSchException(message, (Throwable)e); + throw new JSchException(message); + } + } + public InputStream getInputStream(){ return in; } + public OutputStream getOutputStream(){ return out; } + public Socket getSocket(){ return socket; } + public void close(){ + try{ + if(in!=null)in.close(); + if(out!=null)out.close(); + if(socket!=null)socket.close(); + } + catch(Exception e){ + } + in=null; + out=null; + socket=null; + } + public static int getDefaultPort(){ + return DEFAULTPORT; + } +} diff --git a/src/main/java/com/jcraft/jsch/ProxySOCKS4.java b/src/main/java/com/jcraft/jsch/ProxySOCKS4.java new file mode 100644 index 00000000..76dcb04f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ProxySOCKS4.java @@ -0,0 +1,212 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + This file depends on following documents, + - SOCKS: A protocol for TCP proxy across firewalls, Ying-Da Lee + http://www.socks.nec.com/protocol/socks4.protocol + */ + +package com.jcraft.jsch; + +import java.io.*; +import java.net.*; + +public class ProxySOCKS4 implements Proxy{ + private static int DEFAULTPORT=1080; + private String proxy_host; + private int proxy_port; + private InputStream in; + private OutputStream out; + private Socket socket; + private String user; + private String passwd; + + public ProxySOCKS4(String proxy_host){ + int port=DEFAULTPORT; + String host=proxy_host; + if(proxy_host.indexOf(':')!=-1){ + try{ + host=proxy_host.substring(0, proxy_host.indexOf(':')); + port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1)); + } + catch(Exception e){ + } + } + this.proxy_host=host; + this.proxy_port=port; + } + public ProxySOCKS4(String proxy_host, int proxy_port){ + this.proxy_host=proxy_host; + this.proxy_port=proxy_port; + } + public void setUserPasswd(String user, String passwd){ + this.user=user; + this.passwd=passwd; + } + public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{ + try{ + if(socket_factory==null){ + socket=Util.createSocket(proxy_host, proxy_port, timeout); + //socket=new Socket(proxy_host, proxy_port); + in=socket.getInputStream(); + out=socket.getOutputStream(); + } + else{ + socket=socket_factory.createSocket(proxy_host, proxy_port); + in=socket_factory.getInputStream(socket); + out=socket_factory.getOutputStream(socket); + } + if(timeout>0){ + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + byte[] buf=new byte[1024]; + int index=0; + +/* + 1) CONNECT + + The client connects to the SOCKS server and sends a CONNECT request when + it wants to establish a connection to an application server. The client + includes in the request packet the IP address and the port number of the + destination host, and userid, in the following format. + + +----+----+----+----+----+----+----+----+----+----+....+----+ + | VN | CD | DSTPORT | DSTIP | USERID |NULL| + +----+----+----+----+----+----+----+----+----+----+....+----+ + # of bytes: 1 1 2 4 variable 1 + + VN is the SOCKS protocol version number and should be 4. CD is the + SOCKS command code and should be 1 for CONNECT request. NULL is a byte + of all zero bits. +*/ + + index=0; + buf[index++]=4; + buf[index++]=1; + + buf[index++]=(byte)(port>>>8); + buf[index++]=(byte)(port&0xff); + + try{ + InetAddress addr=InetAddress.getByName(host); + byte[] byteAddress = addr.getAddress(); + for (int i = 0; i < byteAddress.length; i++) { + buf[index++]=byteAddress[i]; + } + } + catch(UnknownHostException uhe){ + throw new JSchException("ProxySOCKS4: "+uhe.toString(), uhe); + } + + if(user!=null){ + System.arraycopy(Util.str2byte(user), 0, buf, index, user.length()); + index+=user.length(); + } + buf[index++]=0; + out.write(buf, 0, index); + +/* + The SOCKS server checks to see whether such a request should be granted + based on any combination of source IP address, destination IP address, + destination port number, the userid, and information it may obtain by + consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS + server makes a connection to the specified port of the destination host. + A reply packet is sent to the client when this connection is established, + or when the request is rejected or the operation fails. + + +----+----+----+----+----+----+----+----+ + | VN | CD | DSTPORT | DSTIP | + +----+----+----+----+----+----+----+----+ + # of bytes: 1 1 2 4 + + VN is the version of the reply code and should be 0. CD is the result + code with one of the following values: + + 90: request granted + 91: request rejected or failed + 92: request rejected becasue SOCKS server cannot connect to + identd on the client + 93: request rejected because the client program and identd + report different user-ids + + The remaining fields are ignored. +*/ + + int len=8; + int s=0; + while(s0){ + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + byte[] buf=new byte[1024]; + int index=0; + +/* + +----+----------+----------+ + |VER | NMETHODS | METHODS | + +----+----------+----------+ + | 1 | 1 | 1 to 255 | + +----+----------+----------+ + + The VER field is set to X'05' for this version of the protocol. The + NMETHODS field contains the number of method identifier octets that + appear in the METHODS field. + + The values currently defined for METHOD are: + + o X'00' NO AUTHENTICATION REQUIRED + o X'01' GSSAPI + o X'02' USERNAME/PASSWORD + o X'03' to X'7F' IANA ASSIGNED + o X'80' to X'FE' RESERVED FOR PRIVATE METHODS + o X'FF' NO ACCEPTABLE METHODS +*/ + + buf[index++]=5; + + buf[index++]=2; + buf[index++]=0; // NO AUTHENTICATION REQUIRED + buf[index++]=2; // USERNAME/PASSWORD + + out.write(buf, 0, index); + +/* + The server selects from one of the methods given in METHODS, and + sends a METHOD selection message: + + +----+--------+ + |VER | METHOD | + +----+--------+ + | 1 | 1 | + +----+--------+ +*/ + //in.read(buf, 0, 2); + fill(in, buf, 2); + + boolean check=false; + switch((buf[1])&0xff){ + case 0: // NO AUTHENTICATION REQUIRED + check=true; + break; + case 2: // USERNAME/PASSWORD + if(user==null || passwd==null)break; + +/* + Once the SOCKS V5 server has started, and the client has selected the + Username/Password Authentication protocol, the Username/Password + subnegotiation begins. This begins with the client producing a + Username/Password request: + + +----+------+----------+------+----------+ + |VER | ULEN | UNAME | PLEN | PASSWD | + +----+------+----------+------+----------+ + | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + +----+------+----------+------+----------+ + + The VER field contains the current version of the subnegotiation, + which is X'01'. The ULEN field contains the length of the UNAME field + that follows. The UNAME field contains the username as known to the + source operating system. The PLEN field contains the length of the + PASSWD field that follows. The PASSWD field contains the password + association with the given UNAME. +*/ + index=0; + buf[index++]=1; + buf[index++]=(byte)(user.length()); + System.arraycopy(Util.str2byte(user), 0, buf, index, user.length()); + index+=user.length(); + buf[index++]=(byte)(passwd.length()); + System.arraycopy(Util.str2byte(passwd), 0, buf, index, passwd.length()); + index+=passwd.length(); + + out.write(buf, 0, index); + +/* + The server verifies the supplied UNAME and PASSWD, and sends the + following response: + + +----+--------+ + |VER | STATUS | + +----+--------+ + | 1 | 1 | + +----+--------+ + + A STATUS field of X'00' indicates success. If the server returns a + `failure' (STATUS value other than X'00') status, it MUST close the + connection. +*/ + //in.read(buf, 0, 2); + fill(in, buf, 2); + if(buf[1]==0) + check=true; + break; + default: + } + + if(!check){ + try{ socket.close(); } + catch(Exception eee){ + } + throw new JSchException("fail in SOCKS5 proxy"); + } + +/* + The SOCKS request is formed as follows: + + +----+-----+-------+------+----------+----------+ + |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + Where: + + o VER protocol version: X'05' + o CMD + o CONNECT X'01' + o BIND X'02' + o UDP ASSOCIATE X'03' + o RSV RESERVED + o ATYP address type of following address + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o DST.ADDR desired destination address + o DST.PORT desired destination port in network octet + order +*/ + + index=0; + buf[index++]=5; + buf[index++]=1; // CONNECT + buf[index++]=0; + + byte[] hostb=Util.str2byte(host); + int len=hostb.length; + buf[index++]=3; // DOMAINNAME + buf[index++]=(byte)(len); + System.arraycopy(hostb, 0, buf, index, len); + index+=len; + buf[index++]=(byte)(port>>>8); + buf[index++]=(byte)(port&0xff); + + out.write(buf, 0, index); + +/* + The SOCKS request information is sent by the client as soon as it has + established a connection to the SOCKS server, and completed the + authentication negotiations. The server evaluates the request, and + returns a reply formed as follows: + + +----+-----+-------+------+----------+----------+ + |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + Where: + + o VER protocol version: X'05' + o REP Reply field: + o X'00' succeeded + o X'01' general SOCKS server failure + o X'02' connection not allowed by ruleset + o X'03' Network unreachable + o X'04' Host unreachable + o X'05' Connection refused + o X'06' TTL expired + o X'07' Command not supported + o X'08' Address type not supported + o X'09' to X'FF' unassigned + o RSV RESERVED + o ATYP address type of following address + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o BND.ADDR server bound address + o BND.PORT server bound port in network octet order +*/ + + //in.read(buf, 0, 4); + fill(in, buf, 4); + + if(buf[1]!=0){ + try{ socket.close(); } + catch(Exception eee){ + } + throw new JSchException("ProxySOCKS5: server returns "+buf[1]); + } + + switch(buf[3]&0xff){ + case 1: + //in.read(buf, 0, 6); + fill(in, buf, 6); + break; + case 3: + //in.read(buf, 0, 1); + fill(in, buf, 1); + //in.read(buf, 0, buf[0]+2); + fill(in, buf, (buf[0]&0xff)+2); + break; + case 4: + //in.read(buf, 0, 18); + fill(in, buf, 18); + break; + default: + } + } + catch(RuntimeException e){ + throw e; + } + catch(Exception e){ + try{ if(socket!=null)socket.close(); } + catch(Exception eee){ + } + String message="ProxySOCKS5: "+e.toString(); + if(e instanceof Throwable) + throw new JSchException(message, (Throwable)e); + throw new JSchException(message); + } + } + public InputStream getInputStream(){ return in; } + public OutputStream getOutputStream(){ return out; } + public Socket getSocket(){ return socket; } + public void close(){ + try{ + if(in!=null)in.close(); + if(out!=null)out.close(); + if(socket!=null)socket.close(); + } + catch(Exception e){ + } + in=null; + out=null; + socket=null; + } + public static int getDefaultPort(){ + return DEFAULTPORT; + } + private void fill(InputStream in, byte[] buf, int len) throws JSchException, IOException{ + int s=0; + while(s0){ + setReply(true); + } + } + boolean waitForReply(){ return reply; } + void setReply(boolean reply){ this.reply=reply; } + void write(Packet packet) throws Exception{ + if(reply){ + channel.reply=-1; + } + session.write(packet); + if(reply){ + long start=System.currentTimeMillis(); + long timeout=channel.connectTimeout; + while(channel.isConnected() && channel.reply==-1){ + try{Thread.sleep(10);} + catch(Exception ee){ + } + if(timeout>0L && + (System.currentTimeMillis()-start)>timeout){ + channel.reply=0; + throw new JSchException("channel request: timeout"); + } + } + + if(channel.reply==0){ + throw new JSchException("failed to send channel request"); + } + } + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestAgentForwarding.java b/src/main/java/com/jcraft/jsch/RequestAgentForwarding.java new file mode 100644 index 00000000..9528c19c --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestAgentForwarding.java @@ -0,0 +1,53 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestAgentForwarding extends Request{ + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + setReply(false); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "auth-agent-req@openssh.com" + // boolean want reply // 0 + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("auth-agent-req@openssh.com")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + write(packet); + session.agent_forwarding=true; + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestEnv.java b/src/main/java/com/jcraft/jsch/RequestEnv.java new file mode 100644 index 00000000..afc4d702 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestEnv.java @@ -0,0 +1,54 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestEnv extends Request{ + byte[] name=new byte[0]; + byte[] value=new byte[0]; + void setEnv(byte[] name, byte[] value){ + this.name=name; + this.value=value; + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("env")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(name); + buf.putString(value); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestExec.java b/src/main/java/com/jcraft/jsch/RequestExec.java new file mode 100644 index 00000000..44e41a7d --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestExec.java @@ -0,0 +1,58 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestExec extends Request{ + private byte[] command=new byte[0]; + RequestExec(byte[] command){ + this.command=command; + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // send + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "exec" + // boolean want reply // 0 + // string command + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("exec")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.checkFreeSize(4+command.length); + buf.putString(command); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestPtyReq.java b/src/main/java/com/jcraft/jsch/RequestPtyReq.java new file mode 100644 index 00000000..63b3125b --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestPtyReq.java @@ -0,0 +1,78 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestPtyReq extends Request{ + private String ttype="vt100"; + private int tcol=80; + private int trow=24; + private int twp=640; + private int thp=480; + + private byte[] terminal_mode=Util.empty; + + void setCode(String cookie){ + } + + void setTType(String ttype){ + this.ttype=ttype; + } + + void setTerminalMode(byte[] terminal_mode){ + this.terminal_mode=terminal_mode; + } + + void setTSize(int tcol, int trow, int twp, int thp){ + this.tcol=tcol; + this.trow=trow; + this.twp=twp; + this.thp=thp; + } + + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("pty-req")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(ttype)); + buf.putInt(tcol); + buf.putInt(trow); + buf.putInt(twp); + buf.putInt(thp); + buf.putString(terminal_mode); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestSftp.java b/src/main/java/com/jcraft/jsch/RequestSftp.java new file mode 100644 index 00000000..3ad5166b --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestSftp.java @@ -0,0 +1,49 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class RequestSftp extends Request{ + RequestSftp(){ + setReply(true); + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("subsystem")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte("sftp")); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestShell.java b/src/main/java/com/jcraft/jsch/RequestShell.java new file mode 100644 index 00000000..12ef31bf --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestShell.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestShell extends Request{ + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // send + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "shell" + // boolean want reply // 0 + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("shell")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestSignal.java b/src/main/java/com/jcraft/jsch/RequestSignal.java new file mode 100644 index 00000000..a5146712 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestSignal.java @@ -0,0 +1,49 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestSignal extends Request{ + private String signal="KILL"; + public void setSignal(String foo){ signal=foo; } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("signal")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(signal)); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestSubsystem.java b/src/main/java/com/jcraft/jsch/RequestSubsystem.java new file mode 100644 index 00000000..a3edeae4 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestSubsystem.java @@ -0,0 +1,53 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public class RequestSubsystem extends Request{ + private String subsystem=null; + public void request(Session session, Channel channel, String subsystem, boolean want_reply) throws Exception{ + setReply(want_reply); + this.subsystem=subsystem; + this.request(session, channel); + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + packet.reset(); + buf.putByte((byte)Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("subsystem")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(subsystem)); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestWindowChange.java b/src/main/java/com/jcraft/jsch/RequestWindowChange.java new file mode 100644 index 00000000..d465f40c --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestWindowChange.java @@ -0,0 +1,68 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestWindowChange extends Request{ + int width_columns=80; + int height_rows=24; + int width_pixels=640; + int height_pixels=480; + void setSize(int col, int row, int wp, int hp){ + this.width_columns=col; + this.height_rows=row; + this.width_pixels=wp; + this.height_pixels=hp; + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + //byte SSH_MSG_CHANNEL_REQUEST + //uint32 recipient_channel + //string "window-change" + //boolean FALSE + //uint32 terminal width, columns + //uint32 terminal height, rows + //uint32 terminal width, pixels + //uint32 terminal height, pixels + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("window-change")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putInt(width_columns); + buf.putInt(height_rows); + buf.putInt(width_pixels); + buf.putInt(height_pixels); + write(packet); + } +} diff --git a/src/main/java/com/jcraft/jsch/RequestX11.java b/src/main/java/com/jcraft/jsch/RequestX11.java new file mode 100644 index 00000000..3c87179a --- /dev/null +++ b/src/main/java/com/jcraft/jsch/RequestX11.java @@ -0,0 +1,63 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +class RequestX11 extends Request{ + public void setCookie(String cookie){ + ChannelX11.cookie=Util.str2byte(cookie); + } + public void request(Session session, Channel channel) throws Exception{ + super.request(session, channel); + + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "x11-req" + // boolean want reply // 0 + // boolean single connection + // string x11 authentication protocol // "MIT-MAGIC-COOKIE-1". + // string x11 authentication cookie + // uint32 x11 screen number + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("x11-req")); + buf.putByte((byte)(waitForReply() ? 1 : 0)); + buf.putByte((byte)0); + buf.putString(Util.str2byte("MIT-MAGIC-COOKIE-1")); + buf.putString(ChannelX11.getFakedCookie(session)); + buf.putInt(0); + write(packet); + + session.x11_forwarding=true; + } +} diff --git a/src/main/java/com/jcraft/jsch/ServerSocketFactory.java b/src/main/java/com/jcraft/jsch/ServerSocketFactory.java new file mode 100644 index 00000000..7c0467ad --- /dev/null +++ b/src/main/java/com/jcraft/jsch/ServerSocketFactory.java @@ -0,0 +1,37 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.net.*; +import java.io.*; + +public interface ServerSocketFactory{ + public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException; +} diff --git a/src/main/java/com/jcraft/jsch/Session.java b/src/main/java/com/jcraft/jsch/Session.java new file mode 100644 index 00000000..9f56c735 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/Session.java @@ -0,0 +1,2840 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.io.*; +import java.net.*; +import java.util.Vector; + +public class Session implements Runnable{ + + // http://ietf.org/internet-drafts/draft-ietf-secsh-assignednumbers-01.txt + static final int SSH_MSG_DISCONNECT= 1; + static final int SSH_MSG_IGNORE= 2; + static final int SSH_MSG_UNIMPLEMENTED= 3; + static final int SSH_MSG_DEBUG= 4; + static final int SSH_MSG_SERVICE_REQUEST= 5; + static final int SSH_MSG_SERVICE_ACCEPT= 6; + static final int SSH_MSG_KEXINIT= 20; + static final int SSH_MSG_NEWKEYS= 21; + static final int SSH_MSG_KEXDH_INIT= 30; + static final int SSH_MSG_KEXDH_REPLY= 31; + static final int SSH_MSG_KEX_DH_GEX_GROUP= 31; + static final int SSH_MSG_KEX_DH_GEX_INIT= 32; + static final int SSH_MSG_KEX_DH_GEX_REPLY= 33; + static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34; + static final int SSH_MSG_GLOBAL_REQUEST= 80; + static final int SSH_MSG_REQUEST_SUCCESS= 81; + static final int SSH_MSG_REQUEST_FAILURE= 82; + static final int SSH_MSG_CHANNEL_OPEN= 90; + static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION= 91; + static final int SSH_MSG_CHANNEL_OPEN_FAILURE= 92; + static final int SSH_MSG_CHANNEL_WINDOW_ADJUST= 93; + static final int SSH_MSG_CHANNEL_DATA= 94; + static final int SSH_MSG_CHANNEL_EXTENDED_DATA= 95; + static final int SSH_MSG_CHANNEL_EOF= 96; + static final int SSH_MSG_CHANNEL_CLOSE= 97; + static final int SSH_MSG_CHANNEL_REQUEST= 98; + static final int SSH_MSG_CHANNEL_SUCCESS= 99; + static final int SSH_MSG_CHANNEL_FAILURE= 100; + + private static final int PACKET_MAX_SIZE = 256 * 1024; + + private byte[] V_S; // server version + private byte[] V_C=Util.str2byte("SSH-2.0-JSCH-"+JSch.VERSION); // client version + + private byte[] I_C; // the payload of the client's SSH_MSG_KEXINIT + private byte[] I_S; // the payload of the server's SSH_MSG_KEXINIT + private byte[] K_S; // the host key + + private byte[] session_id; + + private byte[] IVc2s; + private byte[] IVs2c; + private byte[] Ec2s; + private byte[] Es2c; + private byte[] MACc2s; + private byte[] MACs2c; + + private int seqi=0; + private int seqo=0; + + String[] guess=null; + private Cipher s2ccipher; + private Cipher c2scipher; + private MAC s2cmac; + private MAC c2smac; + //private byte[] mac_buf; + private byte[] s2cmac_result1; + private byte[] s2cmac_result2; + + private Compression deflater; + private Compression inflater; + + private IO io; + private Socket socket; + private int timeout=0; + + private volatile boolean isConnected=false; + + private boolean isAuthed=false; + + private Thread connectThread=null; + private Object lock=new Object(); + + boolean x11_forwarding=false; + boolean agent_forwarding=false; + + InputStream in=null; + OutputStream out=null; + + static Random random; + + Buffer buf; + Packet packet; + + SocketFactory socket_factory=null; + + static final int buffer_margin = 32 + // maximum padding length + 64 + // maximum mac length + 32; // margin for deflater; deflater may inflate data + + private java.util.Hashtable config=null; + + private Proxy proxy=null; + private UserInfo userinfo; + + private String hostKeyAlias=null; + private int serverAliveInterval=0; + private int serverAliveCountMax=1; + + private IdentityRepository identityRepository = null; + private HostKeyRepository hostkeyRepository = null; + + protected boolean daemon_thread=false; + + private long kex_start_time=0L; + + int max_auth_tries = 6; + int auth_failures = 0; + + String host="127.0.0.1"; + String org_host="127.0.0.1"; + int port=22; + + String username=null; + byte[] password=null; + + JSch jsch; + + Session(JSch jsch, String username, String host, int port) throws JSchException{ + super(); + this.jsch=jsch; + buf=new Buffer(); + packet=new Packet(buf); + this.username = username; + this.org_host = this.host = host; + this.port = port; + + applyConfig(); + + if(this.username==null) { + try { + this.username=(String)(System.getProperties().get("user.name")); + } + catch(SecurityException e){ + // ignore e + } + } + + if(this.username==null) { + throw new JSchException("username is not given."); + } + } + + public void connect() throws JSchException{ + connect(timeout); + } + + public void connect(int connectTimeout) throws JSchException{ + if(isConnected){ + throw new JSchException("session is already connected"); + } + + io=new IO(); + if(random==null){ + try{ + Class c=Class.forName(getConfig("random")); + random=(Random)(c.newInstance()); + } + catch(Exception e){ + throw new JSchException(e.toString(), e); + } + } + Packet.setRandom(random); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Connecting to "+host+" port "+port); + } + + try { + int i, j; + + if(proxy==null){ + InputStream in; + OutputStream out; + if(socket_factory==null){ + socket=Util.createSocket(host, port, connectTimeout); + in=socket.getInputStream(); + out=socket.getOutputStream(); + } + else{ + socket=socket_factory.createSocket(host, port); + in=socket_factory.getInputStream(socket); + out=socket_factory.getOutputStream(socket); + } + //if(timeout>0){ socket.setSoTimeout(timeout); } + socket.setTcpNoDelay(true); + io.setInputStream(in); + io.setOutputStream(out); + } + else{ + synchronized(proxy){ + proxy.connect(socket_factory, host, port, connectTimeout); + io.setInputStream(proxy.getInputStream()); + io.setOutputStream(proxy.getOutputStream()); + socket=proxy.getSocket(); + } + } + + if(connectTimeout>0 && socket!=null){ + socket.setSoTimeout(connectTimeout); + } + + isConnected=true; + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Connection established"); + } + + jsch.addSession(this); + + { + // Some Cisco devices will miss to read '\n' if it is sent separately. + byte[] foo=new byte[V_C.length+1]; + System.arraycopy(V_C, 0, foo, 0, V_C.length); + foo[foo.length-1]=(byte)'\n'; + io.put(foo, 0, foo.length); + } + + while(true){ + i=0; + j=0; + while(i0 && buf.buffer[i-1]==13){ // 0x0d + i--; + } + } + + if(i<=3 || + ((i!=buf.buffer.length) && + (buf.buffer[0]!='S'||buf.buffer[1]!='S'|| + buf.buffer[2]!='H'||buf.buffer[3]!='-'))){ + // It must not start with 'SSH-' + //System.err.println(new String(buf.buffer, 0, i); + continue; + } + + if(i==buf.buffer.length || + i<7 || // SSH-1.99 or SSH-2.0 + (buf.buffer[4]=='1' && buf.buffer[6]!='9') // SSH-1.5 + ){ + throw new JSchException("invalid server's version string"); + } + break; + } + + V_S=new byte[i]; System.arraycopy(buf.buffer, 0, V_S, 0, i); + //System.err.println("V_S: ("+i+") ["+new String(V_S)+"]"); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Remote version string: "+Util.byte2str(V_S)); + JSch.getLogger().log(Logger.INFO, + "Local version string: "+Util.byte2str(V_C)); + } + + send_kexinit(); + + buf=read(buf); + if(buf.getCommand()!=SSH_MSG_KEXINIT){ + in_kex=false; + throw new JSchException("invalid protocol: "+buf.getCommand()); + } + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEXINIT received"); + } + + KeyExchange kex=receive_kexinit(buf); + + while(true){ + buf=read(buf); + if(kex.getState()==buf.getCommand()){ + kex_start_time=System.currentTimeMillis(); + boolean result=kex.next(buf); + if(!result){ + //System.err.println("verify: "+result); + in_kex=false; + throw new JSchException("verify: "+result); + } + } + else{ + in_kex=false; + throw new JSchException("invalid protocol(kex): "+buf.getCommand()); + } + if(kex.getState()==KeyExchange.STATE_END){ + break; + } + } + + try{ + long tmp=System.currentTimeMillis(); + in_prompt = true; + checkHost(host, port, kex); + in_prompt = false; + kex_start_time+=(System.currentTimeMillis()-tmp); + } + catch(JSchException ee){ + in_kex=false; + in_prompt = false; + throw ee; + } + + send_newkeys(); + + // receive SSH_MSG_NEWKEYS(21) + buf=read(buf); + //System.err.println("read: 21 ? "+buf.getCommand()); + if(buf.getCommand()==SSH_MSG_NEWKEYS){ + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_NEWKEYS received"); + } + + receive_newkeys(buf, kex); + } + else{ + in_kex=false; + throw new JSchException("invalid protocol(newkyes): "+buf.getCommand()); + } + + try{ + String s = getConfig("MaxAuthTries"); + if(s!=null){ + max_auth_tries = Integer.parseInt(s); + } + } + catch(NumberFormatException e){ + throw new JSchException("MaxAuthTries: "+getConfig("MaxAuthTries"), e); + } + + boolean auth=false; + boolean auth_cancel=false; + + UserAuth ua=null; + try{ + Class c=Class.forName(getConfig("userauth.none")); + ua=(UserAuth)(c.newInstance()); + } + catch(Exception e){ + throw new JSchException(e.toString(), e); + } + + auth=ua.start(this); + + String cmethods=getConfig("PreferredAuthentications"); + + String[] cmethoda=Util.split(cmethods, ","); + + String smethods=null; + if(!auth){ + smethods=((UserAuthNone)ua).getMethods(); + if(smethods!=null){ + smethods=smethods.toLowerCase(); + } + else{ + // methods: publickey,password,keyboard-interactive + //smethods="publickey,password,keyboard-interactive"; + smethods=cmethods; + } + } + + String[] smethoda=Util.split(smethods, ","); + + int methodi=0; + + loop: + while(true){ + + while(!auth && + cmethoda!=null && methodi= max_auth_tries){ + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Login trials exceeds "+max_auth_tries); + } + } + if(auth_cancel) + throw new JSchException("Auth cancel"); + throw new JSchException("Auth fail"); + } + + if(socket!=null && (connectTimeout>0 || timeout>0)){ + socket.setSoTimeout(timeout); + } + + isAuthed=true; + + synchronized(lock){ + if(isConnected){ + connectThread=new Thread(this); + connectThread.setName("Connect thread "+host+" session"); + if(daemon_thread){ + connectThread.setDaemon(daemon_thread); + } + connectThread.start(); + + requestPortForwarding(); + } + else{ + // The session has been already down and + // we don't have to start new thread. + } + } + } + catch(Exception e) { + in_kex=false; + try{ + if(isConnected){ + String message = e.toString(); + packet.reset(); + buf.checkFreeSize(1+4*3+message.length()+2+buffer_margin); + buf.putByte((byte)SSH_MSG_DISCONNECT); + buf.putInt(3); + buf.putString(Util.str2byte(message)); + buf.putString(Util.str2byte("en")); + write(packet); + } + } + catch(Exception ee){} + try{ disconnect(); } catch(Exception ee){ } + isConnected=false; + //e.printStackTrace(); + if(e instanceof RuntimeException) throw (RuntimeException)e; + if(e instanceof JSchException) throw (JSchException)e; + throw new JSchException("Session.connect: "+e); + } + finally{ + Util.bzero(this.password); + this.password=null; + } + } + + private KeyExchange receive_kexinit(Buffer buf) throws Exception { + int j=buf.getInt(); + if(j!=buf.getLength()){ // packet was compressed and + buf.getByte(); // j is the size of deflated packet. + I_S=new byte[buf.index-5]; + } + else{ + I_S=new byte[j-1-buf.getByte()]; + } + System.arraycopy(buf.buffer, buf.s, I_S, 0, I_S.length); + + if(!in_kex){ // We are in rekeying activated by the remote! + send_kexinit(); + } + + guess=KeyExchange.guess(I_S, I_C); + if(guess==null){ + throw new JSchException("Algorithm negotiation fail"); + } + + if(!isAuthed && + (guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS].equals("none") || + (guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC].equals("none")))){ + throw new JSchException("NONE Cipher should not be chosen before authentification is successed."); + } + + KeyExchange kex=null; + try{ + Class c=Class.forName(getConfig(guess[KeyExchange.PROPOSAL_KEX_ALGS])); + kex=(KeyExchange)(c.newInstance()); + } + catch(Exception e){ + throw new JSchException(e.toString(), e); + } + + kex.init(this, V_S, V_C, I_S, I_C); + return kex; + } + + private volatile boolean in_kex=false; + private volatile boolean in_prompt=false; + public void rekey() throws Exception { + send_kexinit(); + } + private void send_kexinit() throws Exception { + if(in_kex) + return; + + String cipherc2s=getConfig("cipher.c2s"); + String ciphers2c=getConfig("cipher.s2c"); + + String[] not_available_ciphers=checkCiphers(getConfig("CheckCiphers")); + if(not_available_ciphers!=null && not_available_ciphers.length>0){ + cipherc2s=Util.diffString(cipherc2s, not_available_ciphers); + ciphers2c=Util.diffString(ciphers2c, not_available_ciphers); + if(cipherc2s==null || ciphers2c==null){ + throw new JSchException("There are not any available ciphers."); + } + } + + String kex=getConfig("kex"); + String[] not_available_kexes=checkKexes(getConfig("CheckKexes")); + if(not_available_kexes!=null && not_available_kexes.length>0){ + kex=Util.diffString(kex, not_available_kexes); + if(kex==null){ + throw new JSchException("There are not any available kexes."); + } + } + + String server_host_key = getConfig("server_host_key"); + String[] not_available_shks = + checkSignatures(getConfig("CheckSignatures")); + if(not_available_shks!=null && not_available_shks.length>0){ + server_host_key=Util.diffString(server_host_key, not_available_shks); + if(server_host_key==null){ + throw new JSchException("There are not any available sig algorithm."); + } + } + + in_kex=true; + kex_start_time=System.currentTimeMillis(); + + // byte SSH_MSG_KEXINIT(20) + // byte[16] cookie (random bytes) + // string kex_algorithms + // string server_host_key_algorithms + // string encryption_algorithms_client_to_server + // string encryption_algorithms_server_to_client + // string mac_algorithms_client_to_server + // string mac_algorithms_server_to_client + // string compression_algorithms_client_to_server + // string compression_algorithms_server_to_client + // string languages_client_to_server + // string languages_server_to_client + Buffer buf = new Buffer(); // send_kexinit may be invoked + Packet packet = new Packet(buf); // by user thread. + packet.reset(); + buf.putByte((byte) SSH_MSG_KEXINIT); + synchronized(random){ + random.fill(buf.buffer, buf.index, 16); buf.skip(16); + } + buf.putString(Util.str2byte(kex)); + buf.putString(Util.str2byte(server_host_key)); + buf.putString(Util.str2byte(cipherc2s)); + buf.putString(Util.str2byte(ciphers2c)); + buf.putString(Util.str2byte(getConfig("mac.c2s"))); + buf.putString(Util.str2byte(getConfig("mac.s2c"))); + buf.putString(Util.str2byte(getConfig("compression.c2s"))); + buf.putString(Util.str2byte(getConfig("compression.s2c"))); + buf.putString(Util.str2byte(getConfig("lang.c2s"))); + buf.putString(Util.str2byte(getConfig("lang.s2c"))); + buf.putByte((byte)0); + buf.putInt(0); + + buf.setOffSet(5); + I_C=new byte[buf.getLength()]; + buf.getByte(I_C); + + write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_KEXINIT sent"); + } + } + + private void send_newkeys() throws Exception { + // send SSH_MSG_NEWKEYS(21) + packet.reset(); + buf.putByte((byte)SSH_MSG_NEWKEYS); + write(packet); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "SSH_MSG_NEWKEYS sent"); + } + } + + private void checkHost(String chost, int port, KeyExchange kex) throws JSchException { + String shkc=getConfig("StrictHostKeyChecking"); + + if(hostKeyAlias!=null){ + chost=hostKeyAlias; + } + + //System.err.println("shkc: "+shkc); + + byte[] K_S=kex.getHostKey(); + String key_type=kex.getKeyType(); + String key_fprint=kex.getFingerPrint(); + + if(hostKeyAlias==null && port!=22){ + chost=("["+chost+"]:"+port); + } + + HostKeyRepository hkr=getHostKeyRepository(); + + String hkh=getConfig("HashKnownHosts"); + if(hkh.equals("yes") && (hkr instanceof KnownHosts)){ + hostkey=((KnownHosts)hkr).createHashedHostKey(chost, K_S); + } + else{ + hostkey=new HostKey(chost, K_S); + } + + int i=0; + synchronized(hkr){ + i=hkr.check(chost, K_S); + } + + boolean insert=false; + if((shkc.equals("ask") || shkc.equals("yes")) && + i==HostKeyRepository.CHANGED){ + String file=null; + synchronized(hkr){ + file=hkr.getKnownHostsRepositoryID(); + } + if(file==null){file="known_hosts";} + + boolean b=false; + + if(userinfo!=null){ + String message= +"WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n"+ +"IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n"+ +"Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n"+ +"It is also possible that the "+key_type+" host key has just been changed.\n"+ +"The fingerprint for the "+key_type+" key sent by the remote host "+chost+" is\n"+ +key_fprint+".\n"+ +"Please contact your system administrator.\n"+ +"Add correct host key in "+file+" to get rid of this message."; + + if(shkc.equals("ask")){ + b=userinfo.promptYesNo(message+ + "\nDo you want to delete the old key and insert the new key?"); + } + else{ // shkc.equals("yes") + userinfo.showMessage(message); + } + } + + if(!b){ + throw new JSchException("HostKey has been changed: "+chost); + } + + synchronized(hkr){ + hkr.remove(chost, + kex.getKeyAlgorithName(), + null); + insert=true; + } + } + + if((shkc.equals("ask") || shkc.equals("yes")) && + (i!=HostKeyRepository.OK) && !insert){ + if(shkc.equals("yes")){ + throw new JSchException("reject HostKey: "+host); + } + //System.err.println("finger-print: "+key_fprint); + if(userinfo!=null){ + boolean foo=userinfo.promptYesNo( +"The authenticity of host '"+host+"' can't be established.\n"+ +key_type+" key fingerprint is "+key_fprint+".\n"+ +"Are you sure you want to continue connecting?" + ); + if(!foo){ + throw new JSchException("reject HostKey: "+host); + } + insert=true; + } + else{ + if(i==HostKeyRepository.NOT_INCLUDED) + throw new JSchException("UnknownHostKey: "+host+". "+key_type+" key fingerprint is "+key_fprint); + else + throw new JSchException("HostKey has been changed: "+host); + } + } + + if(shkc.equals("no") && + HostKeyRepository.NOT_INCLUDED==i){ + insert=true; + } + + if(i==HostKeyRepository.OK){ + HostKey[] keys = + hkr.getHostKey(chost, kex.getKeyAlgorithName()); + String _key= Util.byte2str(Util.toBase64(K_S, 0, K_S.length)); + for(int j=0; j< keys.length; j++){ + if(keys[i].getKey().equals(_key) && + keys[j].getMarker().equals("@revoked")){ + if(userinfo!=null){ + userinfo.showMessage( +"The "+ key_type +" host key for "+ host +" is marked as revoked.\n"+ +"This could mean that a stolen key is being used to "+ +"impersonate this host."); + } + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Host '"+host+"' has provided revoked key."); + } + throw new JSchException("revoked HostKey: "+host); + } + } + } + + if(i==HostKeyRepository.OK && + JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Host '"+host+"' is known and matches the "+key_type+" host key"); + } + + if(insert && + JSch.getLogger().isEnabled(Logger.WARN)){ + JSch.getLogger().log(Logger.WARN, + "Permanently added '"+host+"' ("+key_type+") to the list of known hosts."); + } + + if(insert){ + synchronized(hkr){ + hkr.add(hostkey, userinfo); + } + } + } + +//public void start(){ (new Thread(this)).start(); } + + public Channel openChannel(String type) throws JSchException{ + if(!isConnected){ + throw new JSchException("session is down"); + } + try{ + Channel channel=Channel.getChannel(type); + addChannel(channel); + channel.init(); + if(channel instanceof ChannelSession){ + applyConfigChannel((ChannelSession)channel); + } + return channel; + } + catch(Exception e){ + //e.printStackTrace(); + } + return null; + } + + // encode will bin invoked in write with synchronization. + public void encode(Packet packet) throws Exception{ +//System.err.println("encode: "+packet.buffer.getCommand()); +//System.err.println(" "+packet.buffer.index); +//if(packet.buffer.getCommand()==96){ +//Thread.dumpStack(); +//} + if(deflater!=null){ + compress_len[0]=packet.buffer.index; + packet.buffer.buffer=deflater.compress(packet.buffer.buffer, + 5, compress_len); + packet.buffer.index=compress_len[0]; + } + if(c2scipher!=null){ + //packet.padding(c2scipher.getIVSize()); + packet.padding(c2scipher_size); + int pad=packet.buffer.buffer[4]; + synchronized(random){ + random.fill(packet.buffer.buffer, packet.buffer.index-pad, pad); + } + } + else{ + packet.padding(8); + } + + if(c2smac!=null){ + c2smac.update(seqo); + c2smac.update(packet.buffer.buffer, 0, packet.buffer.index); + c2smac.doFinal(packet.buffer.buffer, packet.buffer.index); + } + if(c2scipher!=null){ + byte[] buf=packet.buffer.buffer; + c2scipher.update(buf, 0, packet.buffer.index, buf, 0); + } + if(c2smac!=null){ + packet.buffer.skip(c2smac.getBlockSize()); + } + } + + int[] uncompress_len=new int[1]; + int[] compress_len=new int[1]; + + private int s2ccipher_size=8; + private int c2scipher_size=8; + public Buffer read(Buffer buf) throws Exception{ + int j=0; + while(true){ + buf.reset(); + io.getByte(buf.buffer, buf.index, s2ccipher_size); + buf.index+=s2ccipher_size; + if(s2ccipher!=null){ + s2ccipher.update(buf.buffer, 0, s2ccipher_size, buf.buffer, 0); + } + j=((buf.buffer[0]<<24)&0xff000000)| + ((buf.buffer[1]<<16)&0x00ff0000)| + ((buf.buffer[2]<< 8)&0x0000ff00)| + ((buf.buffer[3] )&0x000000ff); + // RFC 4253 6.1. Maximum Packet Length + if(j<5 || j>PACKET_MAX_SIZE){ + start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE); + } + int need = j+4-s2ccipher_size; + //if(need<0){ + // throw new IOException("invalid data"); + //} + if((buf.index+need)>buf.buffer.length){ + byte[] foo=new byte[buf.index+need]; + System.arraycopy(buf.buffer, 0, foo, 0, buf.index); + buf.buffer=foo; + } + + if((need%s2ccipher_size)!=0){ + String message="Bad packet length "+need; + if(JSch.getLogger().isEnabled(Logger.FATAL)){ + JSch.getLogger().log(Logger.FATAL, message); + } + start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-s2ccipher_size); + } + + if(need>0){ + io.getByte(buf.buffer, buf.index, need); buf.index+=(need); + if(s2ccipher!=null){ + s2ccipher.update(buf.buffer, s2ccipher_size, need, buf.buffer, s2ccipher_size); + } + } + + if(s2cmac!=null){ + s2cmac.update(seqi); + s2cmac.update(buf.buffer, 0, buf.index); + + s2cmac.doFinal(s2cmac_result1, 0); + io.getByte(s2cmac_result2, 0, s2cmac_result2.length); + if(!java.util.Arrays.equals(s2cmac_result1, s2cmac_result2)){ + if(need > PACKET_MAX_SIZE){ + throw new IOException("MAC Error"); + } + start_discard(buf, s2ccipher, s2cmac, j, PACKET_MAX_SIZE-need); + continue; + } + } + + seqi++; + + if(inflater!=null){ + //inflater.uncompress(buf); + int pad=buf.buffer[4]; + uncompress_len[0]=buf.index-5-pad; + byte[] foo=inflater.uncompress(buf.buffer, 5, uncompress_len); + if(foo!=null){ + buf.buffer=foo; + buf.index=5+uncompress_len[0]; + } + else{ + System.err.println("fail in inflater"); + break; + } + } + + int type=buf.getCommand()&0xff; + //System.err.println("read: "+type); + if(type==SSH_MSG_DISCONNECT){ + buf.rewind(); + buf.getInt();buf.getShort(); + int reason_code=buf.getInt(); + byte[] description=buf.getString(); + byte[] language_tag=buf.getString(); + throw new JSchException("SSH_MSG_DISCONNECT: "+ + reason_code+ + " "+Util.byte2str(description)+ + " "+Util.byte2str(language_tag)); + //break; + } + else if(type==SSH_MSG_IGNORE){ + } + else if(type==SSH_MSG_UNIMPLEMENTED){ + buf.rewind(); + buf.getInt();buf.getShort(); + int reason_id=buf.getInt(); + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "Received SSH_MSG_UNIMPLEMENTED for "+reason_id); + } + } + else if(type==SSH_MSG_DEBUG){ + buf.rewind(); + buf.getInt();buf.getShort(); +/* + byte always_display=(byte)buf.getByte(); + byte[] message=buf.getString(); + byte[] language_tag=buf.getString(); + System.err.println("SSH_MSG_DEBUG:"+ + " "+Util.byte2str(message)+ + " "+Util.byte2str(language_tag)); +*/ + } + else if(type==SSH_MSG_CHANNEL_WINDOW_ADJUST){ + buf.rewind(); + buf.getInt();buf.getShort(); + Channel c=Channel.getChannel(buf.getInt(), this); + if(c==null){ + } + else{ + c.addRemoteWindowSize(buf.getUInt()); + } + } + else if(type==UserAuth.SSH_MSG_USERAUTH_SUCCESS){ + isAuthed=true; + if(inflater==null && deflater==null){ + String method; + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; + initDeflater(method); + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; + initInflater(method); + } + break; + } + else{ + break; + } + } + buf.rewind(); + return buf; + } + + private void start_discard(Buffer buf, Cipher cipher, MAC mac, + int packet_length, int discard) throws JSchException, IOException{ + MAC discard_mac = null; + + if(!cipher.isCBC()){ + throw new JSchException("Packet corrupt"); + } + + if(packet_length!=PACKET_MAX_SIZE && mac != null){ + discard_mac = mac; + } + + discard -= buf.index; + + while(discard>0){ + buf.reset(); + int len = discard>buf.buffer.length ? buf.buffer.length : discard; + io.getByte(buf.buffer, 0, len); + if(discard_mac!=null){ + discard_mac.update(buf.buffer, 0, len); + } + discard -= len; + } + + if(discard_mac!=null){ + discard_mac.doFinal(buf.buffer, 0); + } + + throw new JSchException("Packet corrupt"); + } + + byte[] getSessionId(){ + return session_id; + } + + private void receive_newkeys(Buffer buf, KeyExchange kex) throws Exception { + updateKeys(kex); + in_kex=false; + } + private void updateKeys(KeyExchange kex) throws Exception{ + byte[] K=kex.getK(); + byte[] H=kex.getH(); + HASH hash=kex.getHash(); + + if(session_id==null){ + session_id=new byte[H.length]; + System.arraycopy(H, 0, session_id, 0, H.length); + } + + /* + Initial IV client to server: HASH (K || H || "A" || session_id) + Initial IV server to client: HASH (K || H || "B" || session_id) + Encryption key client to server: HASH (K || H || "C" || session_id) + Encryption key server to client: HASH (K || H || "D" || session_id) + Integrity key client to server: HASH (K || H || "E" || session_id) + Integrity key server to client: HASH (K || H || "F" || session_id) + */ + + buf.reset(); + buf.putMPInt(K); + buf.putByte(H); + buf.putByte((byte)0x41); + buf.putByte(session_id); + hash.update(buf.buffer, 0, buf.index); + IVc2s=hash.digest(); + + int j=buf.index-session_id.length-1; + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + IVs2c=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + Ec2s=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + Es2c=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + MACc2s=hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + MACs2c=hash.digest(); + + try{ + Class c; + String method; + + method=guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC]; + c=Class.forName(getConfig(method)); + s2ccipher=(Cipher)(c.newInstance()); + while(s2ccipher.getBlockSize()>Es2c.length){ + buf.reset(); + buf.putMPInt(K); + buf.putByte(H); + buf.putByte(Es2c); + hash.update(buf.buffer, 0, buf.index); + byte[] foo=hash.digest(); + byte[] bar=new byte[Es2c.length+foo.length]; + System.arraycopy(Es2c, 0, bar, 0, Es2c.length); + System.arraycopy(foo, 0, bar, Es2c.length, foo.length); + Es2c=bar; + } + s2ccipher.init(Cipher.DECRYPT_MODE, Es2c, IVs2c); + s2ccipher_size=s2ccipher.getIVSize(); + + method=guess[KeyExchange.PROPOSAL_MAC_ALGS_STOC]; + c=Class.forName(getConfig(method)); + s2cmac=(MAC)(c.newInstance()); + MACs2c = expandKey(buf, K, H, MACs2c, hash, s2cmac.getBlockSize()); + s2cmac.init(MACs2c); + //mac_buf=new byte[s2cmac.getBlockSize()]; + s2cmac_result1=new byte[s2cmac.getBlockSize()]; + s2cmac_result2=new byte[s2cmac.getBlockSize()]; + + method=guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS]; + c=Class.forName(getConfig(method)); + c2scipher=(Cipher)(c.newInstance()); + while(c2scipher.getBlockSize()>Ec2s.length){ + buf.reset(); + buf.putMPInt(K); + buf.putByte(H); + buf.putByte(Ec2s); + hash.update(buf.buffer, 0, buf.index); + byte[] foo=hash.digest(); + byte[] bar=new byte[Ec2s.length+foo.length]; + System.arraycopy(Ec2s, 0, bar, 0, Ec2s.length); + System.arraycopy(foo, 0, bar, Ec2s.length, foo.length); + Ec2s=bar; + } + c2scipher.init(Cipher.ENCRYPT_MODE, Ec2s, IVc2s); + c2scipher_size=c2scipher.getIVSize(); + + method=guess[KeyExchange.PROPOSAL_MAC_ALGS_CTOS]; + c=Class.forName(getConfig(method)); + c2smac=(MAC)(c.newInstance()); + MACc2s = expandKey(buf, K, H, MACc2s, hash, c2smac.getBlockSize()); + c2smac.init(MACc2s); + + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; + initDeflater(method); + + method=guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; + initInflater(method); + } + catch(Exception e){ + if(e instanceof JSchException) + throw e; + throw new JSchException(e.toString(), e); + //System.err.println("updatekeys: "+e); + } + } + + + /* + * RFC 4253 7.2. Output from Key Exchange + * If the key length needed is longer than the output of the HASH, the + * key is extended by computing HASH of the concatenation of K and H and + * the entire key so far, and appending the resulting bytes (as many as + * HASH generates) to the key. This process is repeated until enough + * key material is available; the key is taken from the beginning of + * this value. In other words: + * K1 = HASH(K || H || X || session_id) (X is e.g., "A") + * K2 = HASH(K || H || K1) + * K3 = HASH(K || H || K1 || K2) + * ... + * key = K1 || K2 || K3 || ... + */ + private byte[] expandKey(Buffer buf, byte[] K, byte[] H, byte[] key, + HASH hash, int required_length) throws Exception { + byte[] result = key; + int size = hash.getBlockSize(); + while(result.length < required_length){ + buf.reset(); + buf.putMPInt(K); + buf.putByte(H); + buf.putByte(result); + hash.update(buf.buffer, 0, buf.index); + byte[] tmp = new byte[result.length+size]; + System.arraycopy(result, 0, tmp, 0, result.length); + System.arraycopy(hash.digest(), 0, tmp, result.length, size); + Util.bzero(result); + result = tmp; + } + return result; + } + + /*public*/ /*synchronized*/ void write(Packet packet, Channel c, int length) throws Exception{ + long t = getTimeout(); + while(true){ + if(in_kex){ + if(t>0L && (System.currentTimeMillis()-kex_start_time)>t){ + throw new JSchException("timeout in waiting for rekeying process."); + } + try{Thread.sleep(10);} + catch(java.lang.InterruptedException e){}; + continue; + } + synchronized(c){ + + if(c.rwsize=length){ + c.rwsize-=length; + break; + } + + } + if(c.close || !c.isConnected()){ + throw new IOException("channel is broken"); + } + + boolean sendit=false; + int s=0; + byte command=0; + int recipient=-1; + synchronized(c){ + if(c.rwsize>0){ + long len=c.rwsize; + if(len>length){ + len=length; + } + if(len!=length){ + s=packet.shift((int)len, + (c2scipher!=null ? c2scipher_size : 8), + (c2smac!=null ? c2smac.getBlockSize() : 0)); + } + command=packet.buffer.getCommand(); + recipient=c.getRecipient(); + length-=len; + c.rwsize-=len; + sendit=true; + } + } + if(sendit){ + _write(packet); + if(length==0){ + return; + } + packet.unshift(command, recipient, s, length); + } + + synchronized(c){ + if(in_kex){ + continue; + } + if(c.rwsize>=length){ + c.rwsize-=length; + break; + } + + //try{ + //System.out.println("1wait: "+c.rwsize); + // c.notifyme++; + // c.wait(100); + //} + //catch(java.lang.InterruptedException e){ + //} + //finally{ + // c.notifyme--; + //} + } + } + _write(packet); + } + + public void write(Packet packet) throws Exception{ + // System.err.println("in_kex="+in_kex+" "+(packet.buffer.getCommand())); + long t = getTimeout(); + while(in_kex){ + if(t>0L && + (System.currentTimeMillis()-kex_start_time)>t && + !in_prompt + ){ + throw new JSchException("timeout in waiting for rekeying process."); + } + byte command=packet.buffer.getCommand(); + //System.err.println("command: "+command); + if(command==SSH_MSG_KEXINIT || + command==SSH_MSG_NEWKEYS || + command==SSH_MSG_KEXDH_INIT || + command==SSH_MSG_KEXDH_REPLY || + command==SSH_MSG_KEX_DH_GEX_GROUP || + command==SSH_MSG_KEX_DH_GEX_INIT || + command==SSH_MSG_KEX_DH_GEX_REPLY || + command==SSH_MSG_KEX_DH_GEX_REQUEST || + command==SSH_MSG_DISCONNECT){ + break; + } + try{Thread.sleep(10);} + catch(java.lang.InterruptedException e){}; + } + _write(packet); + } + + private void _write(Packet packet) throws Exception{ + synchronized(lock){ + encode(packet); + if(io!=null){ + io.put(packet); + seqo++; + } + } + } + + Runnable thread; + public void run(){ + thread=this; + + byte[] foo; + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + int i=0; + Channel channel; + int[] start=new int[1]; + int[] length=new int[1]; + KeyExchange kex=null; + + int stimeout=0; + try{ + while(isConnected && + thread!=null){ + try{ + buf=read(buf); + stimeout=0; + } + catch(InterruptedIOException/*SocketTimeoutException*/ ee){ + if(!in_kex && stimeoutlport is 0, the tcp port will be allocated. + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @return an allocated local TCP port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout) + */ + public int setPortForwardingL(int lport, String host, int rport) throws JSchException{ + return setPortForwardingL("127.0.0.1", lport, host, rport); + } + + /** + * Registers the local port forwarding. If bind_address is an empty string + * or '*', the port should be available from all interfaces. + * If bind_address is "localhost" or + * null, the listening port will be bound for local use only. + * If lport is 0, the tcp port will be allocated. + * @param bind_address bind address for local port forwarding + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @return an allocated local TCP port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout) + */ + public int setPortForwardingL(String bind_address, int lport, String host, int rport) throws JSchException{ + return setPortForwardingL(bind_address, lport, host, rport, null); + } + + /** + * Registers the local port forwarding. + * If bind_address is an empty string or "*", + * the port should be available from all interfaces. + * If bind_address is "localhost" or + * null, the listening port will be bound for local use only. + * If lport is 0, the tcp port will be allocated. + * @param bind_address bind address for local port forwarding + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @param ssf socket factory + * @return an allocated local TCP port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout) + */ + public int setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf) throws JSchException{ + return setPortForwardingL(bind_address, lport, host, rport, ssf, 0); + } + + /** + * Registers the local port forwarding. + * If bind_address is an empty string + * or "*", the port should be available from all interfaces. + * If bind_address is "localhost" or + * null, the listening port will be bound for local use only. + * If lport is 0, the tcp port will be allocated. + * @param bind_address bind address for local port forwarding + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @param ssf socket factory + * @param connectTimeout timeout for establishing port connection + * @return an allocated local TCP port number + */ + public int setPortForwardingL(String bind_address, int lport, String host, int rport, ServerSocketFactory ssf, int connectTimeout) throws JSchException{ + PortWatcher pw=PortWatcher.addPort(this, bind_address, lport, host, rport, ssf); + pw.setConnectTimeout(connectTimeout); + Thread tmp=new Thread(pw); + tmp.setName("PortWatcher Thread for "+host); + if(daemon_thread){ + tmp.setDaemon(daemon_thread); + } + tmp.start(); + return pw.lport; + } + + /** + * Cancels the local port forwarding assigned + * at local TCP port lport on loopback interface. + * + * @param lport local TCP port + */ + public void delPortForwardingL(int lport) throws JSchException{ + delPortForwardingL("127.0.0.1", lport); + } + + /** + * Cancels the local port forwarding assigned + * at local TCP port lport on bind_address interface. + * + * @param bind_address bind_address of network interfaces + * @param lport local TCP port + */ + public void delPortForwardingL(String bind_address, int lport) throws JSchException{ + PortWatcher.delPort(this, bind_address, lport); + } + + /** + * Lists the registered local port forwarding. + * + * @return a list of "lport:host:hostport" + */ + public String[] getPortForwardingL() throws JSchException{ + return PortWatcher.getPortForwarding(this); + } + + /** + * Registers the remote port forwarding for the loopback interface + * of the remote. + * + * @param rport remote port + * @param host host address + * @param lport local port + * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) + */ + public void setPortForwardingR(int rport, String host, int lport) throws JSchException{ + setPortForwardingR(null, rport, host, lport, (SocketFactory)null); + } + + /** + * Registers the remote port forwarding. + * If bind_address is an empty string or "*", + * the port should be available from all interfaces. + * If bind_address is "localhost" or is not given, + * the listening port will be bound for local use only. + * Note that if GatewayPorts is "no" on the + * remote, "localhost" is always used as a bind_address. + * + * @param bind_address bind address + * @param rport remote port + * @param host host address + * @param lport local port + * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) + */ + public void setPortForwardingR(String bind_address, int rport, String host, int lport) throws JSchException{ + setPortForwardingR(bind_address, rport, host, lport, (SocketFactory)null); + } + + /** + * Registers the remote port forwarding for the loopback interface + * of the remote. + * + * @param rport remote port + * @param host host address + * @param lport local port + * @param sf socket factory + * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) + */ + public void setPortForwardingR(int rport, String host, int lport, SocketFactory sf) throws JSchException{ + setPortForwardingR(null, rport, host, lport, sf); + } + + // TODO: This method should return the integer value as the assigned port. + /** + * Registers the remote port forwarding. + * If bind_address is an empty string or "*", + * the port should be available from all interfaces. + * If bind_address is "localhost" or is not given, + * the listening port will be bound for local use only. + * Note that if GatewayPorts is "no" on the + * remote, "localhost" is always used as a bind_address. + * If rport is 0, the TCP port will be allocated on the remote. + * + * @param bind_address bind address + * @param rport remote port + * @param host host address + * @param lport local port + * @param sf socket factory + */ + public void setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory sf) throws JSchException{ + int allocated=_setPortForwardingR(bind_address, rport); + ChannelForwardedTCPIP.addPort(this, bind_address, + rport, allocated, host, lport, sf); + } + + /** + * Registers the remote port forwarding for the loopback interface + * of the remote. + * The TCP connection to rport on the remote will be + * forwarded to an instance of the class daemon. + * The class specified by daemon must implement + * ForwardedTCPIPDaemon. + * + * @param rport remote port + * @param daemon class name, which implements "ForwardedTCPIPDaemon" + * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) + */ + public void setPortForwardingR(int rport, String daemon) throws JSchException{ + setPortForwardingR(null, rport, daemon, null); + } + + /** + * Registers the remote port forwarding for the loopback interface + * of the remote. + * The TCP connection to rport on the remote will be + * forwarded to an instance of the class daemon with + * the argument arg. + * The class specified by daemon must implement ForwardedTCPIPDaemon. + * + * @param rport remote port + * @param daemon class name, which implements "ForwardedTCPIPDaemon" + * @param arg arguments for "daemon" + * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) + */ + public void setPortForwardingR(int rport, String daemon, Object[] arg) throws JSchException{ + setPortForwardingR(null, rport, daemon, arg); + } + + /** + * Registers the remote port forwarding. + * If bind_address is an empty string + * or "*", the port should be available from all interfaces. + * If bind_address is "localhost" or is not given, + * the listening port will be bound for local use only. + * Note that if GatewayPorts is "no" on the + * remote, "localhost" is always used as a bind_address. + * The TCP connection to rport on the remote will be + * forwarded to an instance of the class daemon with the + * argument arg. + * The class specified by daemon must implement ForwardedTCPIPDaemon. + * + * @param bind_address bind address + * @param rport remote port + * @param daemon class name, which implements "ForwardedTCPIPDaemon" + * @param arg arguments for "daemon" + * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) + */ + public void setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) throws JSchException{ + int allocated = _setPortForwardingR(bind_address, rport); + ChannelForwardedTCPIP.addPort(this, bind_address, + rport, allocated, daemon, arg); + } + + /** + * Lists the registered remote port forwarding. + * + * @return a list of "rport:host:hostport" + */ + public String[] getPortForwardingR() throws JSchException{ + return ChannelForwardedTCPIP.getPortForwarding(this); + } + + private class Forwarding { + String bind_address = null; + int port = -1; + String host = null; + int hostport = -1; + } + + /** + * The given argument may be "[bind_address:]port:host:hostport" or + * "[bind_address:]port host:hostport", which is from LocalForward command of + * ~/.ssh/config . + */ + private Forwarding parseForwarding(String conf) throws JSchException { + String[] tmp = conf.split(" "); + if(tmp.length>1){ // "[bind_address:]port host:hostport" + Vector foo = new Vector(); + for(int i=0; ibind_address is an empty string or "*", + * the port should be available from all interfaces. + * If bind_address is "localhost" or is not given, + * the listening port will be bound for local use only. + * + * @param conf configuration of local port forwarding + * @return an assigned port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport) + */ + public int setPortForwardingL(String conf) throws JSchException { + Forwarding f = parseForwarding(conf); + return setPortForwardingL(f.bind_address, f.port, f.host, f.hostport); + } + + /** + * Registers the remote port forwarding. The argument should be + * in the format like "[bind_address:]port:host:hostport". If the + * bind_address is not given, the default is to only bind to loopback + * addresses. If the bind_address is "*" or an empty string, + * then the forwarding is requested to listen on all interfaces. + * Note that if GatewayPorts is "no" on the remote, + * "localhost" is always used for bind_address. + * If the specified remote is "0", + * the TCP port will be allocated on the remote. + * + * @param conf configuration of remote port forwarding + * @return an allocated TCP port on the remote. + * @see #setPortForwardingR(String bind_address, int rport, String host, int rport) + */ + public int setPortForwardingR(String conf) throws JSchException { + Forwarding f = parseForwarding(conf); + int allocated = _setPortForwardingR(f.bind_address, f.port); + ChannelForwardedTCPIP.addPort(this, f.bind_address, + f.port, allocated, f.host, f.hostport, null); + return allocated; + } + + /** + * Instantiates an instance of stream-forwarder to host:port. + * Set I/O stream to the given channel, and then invoke Channel#connect() method. + * + * @param host remote host, which the given stream will be plugged to. + * @param port remote port, which the given stream will be plugged to. + */ + public Channel getStreamForwarder(String host, int port) throws JSchException { + ChannelDirectTCPIP channel = new ChannelDirectTCPIP(); + channel.init(); + this.addChannel(channel); + channel.setHost(host); + channel.setPort(port); + return channel; + } + + private class GlobalRequestReply{ + private Thread thread=null; + private int reply=-1; + private int port=0; + void setThread(Thread thread){ + this.thread=thread; + this.reply=-1; + } + Thread getThread(){ return thread; } + void setReply(int reply){ this.reply=reply; } + int getReply(){ return this.reply; } + int getPort(){ return this.port; } + void setPort(int port){ this.port=port; } + } + private GlobalRequestReply grr=new GlobalRequestReply(); + private int _setPortForwardingR(String bind_address, int rport) throws JSchException{ + synchronized(grr){ + Buffer buf=new Buffer(100); // ?? + Packet packet=new Packet(buf); + + String address_to_bind=ChannelForwardedTCPIP.normalize(bind_address); + + grr.setThread(Thread.currentThread()); + grr.setPort(rport); + + try{ + // byte SSH_MSG_GLOBAL_REQUEST 80 + // string "tcpip-forward" + // boolean want_reply + // string address_to_bind + // uint32 port number to bind + packet.reset(); + buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST); + buf.putString(Util.str2byte("tcpip-forward")); + buf.putByte((byte)1); + buf.putString(Util.str2byte(address_to_bind)); + buf.putInt(rport); + write(packet); + } + catch(Exception e){ + grr.setThread(null); + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + + int count = 0; + int reply = grr.getReply(); + while(count < 10 && reply == -1){ + try{ Thread.sleep(1000); } + catch(Exception e){ + } + count++; + reply = grr.getReply(); + } + grr.setThread(null); + if(reply != 1){ + throw new JSchException("remote port forwarding failed for listen port "+rport); + } + rport=grr.getPort(); + } + return rport; + } + + /** + * Cancels the remote port forwarding assigned at remote TCP port rport. + * + * @param rport remote TCP port + */ + public void delPortForwardingR(int rport) throws JSchException{ + this.delPortForwardingR(null, rport); + } + + /** + * Cancels the remote port forwarding assigned at + * remote TCP port rport bound on the interface at + * bind_address. + * + * @param bind_address bind address of the interface on the remote + * @param rport remote TCP port + */ + public void delPortForwardingR(String bind_address, int rport) throws JSchException{ + ChannelForwardedTCPIP.delPort(this, bind_address, rport); + } + + private void initDeflater(String method) throws JSchException{ + if(method.equals("none")){ + deflater=null; + return; + } + String foo=getConfig(method); + if(foo!=null){ + if(method.equals("zlib") || + (isAuthed && method.equals("zlib@openssh.com"))){ + try{ + Class c=Class.forName(foo); + deflater=(Compression)(c.newInstance()); + int level=6; + try{ level=Integer.parseInt(getConfig("compression_level"));} + catch(Exception ee){ } + deflater.init(Compression.DEFLATER, level); + } + catch(NoClassDefFoundError ee){ + throw new JSchException(ee.toString(), ee); + } + catch(Exception ee){ + throw new JSchException(ee.toString(), ee); + //System.err.println(foo+" isn't accessible."); + } + } + } + } + private void initInflater(String method) throws JSchException{ + if(method.equals("none")){ + inflater=null; + return; + } + String foo=getConfig(method); + if(foo!=null){ + if(method.equals("zlib") || + (isAuthed && method.equals("zlib@openssh.com"))){ + try{ + Class c=Class.forName(foo); + inflater=(Compression)(c.newInstance()); + inflater.init(Compression.INFLATER, 0); + } + catch(Exception ee){ + throw new JSchException(ee.toString(), ee); + //System.err.println(foo+" isn't accessible."); + } + } + } + } + + void addChannel(Channel channel){ + channel.setSession(this); + } + + public void setProxy(Proxy proxy){ this.proxy=proxy; } + public void setHost(String host){ this.host=host; } + public void setPort(int port){ this.port=port; } + void setUserName(String username){ this.username=username; } + public void setUserInfo(UserInfo userinfo){ this.userinfo=userinfo; } + public UserInfo getUserInfo(){ return userinfo; } + public void setInputStream(InputStream in){ this.in=in; } + public void setOutputStream(OutputStream out){ this.out=out; } + public void setX11Host(String host){ ChannelX11.setHost(host); } + public void setX11Port(int port){ ChannelX11.setPort(port); } + public void setX11Cookie(String cookie){ ChannelX11.setCookie(cookie); } + public void setPassword(String password){ + if(password!=null) + this.password=Util.str2byte(password); + } + public void setPassword(byte[] password){ + if(password!=null){ + this.password=new byte[password.length]; + System.arraycopy(password, 0, this.password, 0, password.length); + } + } + + public void setConfig(java.util.Properties newconf){ + setConfig((java.util.Hashtable)newconf); + } + + public void setConfig(java.util.Hashtable newconf){ + synchronized(lock){ + if(config==null) + config=new java.util.Hashtable(); + for(java.util.Enumeration e=newconf.keys() ; e.hasMoreElements() ;) { + String key=(String)(e.nextElement()); + config.put(key, (String)(newconf.get(key))); + } + } + } + + public void setConfig(String key, String value){ + synchronized(lock){ + if(config==null){ + config=new java.util.Hashtable(); + } + config.put(key, value); + } + } + + public String getConfig(String key){ + Object foo=null; + if(config!=null){ + foo=config.get(key); + if(foo instanceof String) return (String)foo; + } + foo=jsch.getConfig(key); + if(foo instanceof String) return (String)foo; + return null; + } + + public void setSocketFactory(SocketFactory sfactory){ + socket_factory=sfactory; + } + public boolean isConnected(){ return isConnected; } + public int getTimeout(){ return timeout; } + public void setTimeout(int timeout) throws JSchException { + if(socket==null){ + if(timeout<0){ + throw new JSchException("invalid timeout value"); + } + this.timeout=timeout; + return; + } + try{ + socket.setSoTimeout(timeout); + this.timeout=timeout; + } + catch(Exception e){ + if(e instanceof Throwable) + throw new JSchException(e.toString(), (Throwable)e); + throw new JSchException(e.toString()); + } + } + public String getServerVersion(){ + return Util.byte2str(V_S); + } + public String getClientVersion(){ + return Util.byte2str(V_C); + } + public void setClientVersion(String cv){ + V_C=Util.str2byte(cv); + } + + public void sendIgnore() throws Exception{ + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)SSH_MSG_IGNORE); + write(packet); + } + + private static final byte[] keepalivemsg=Util.str2byte("keepalive@jcraft.com"); + public void sendKeepAliveMsg() throws Exception{ + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)SSH_MSG_GLOBAL_REQUEST); + buf.putString(keepalivemsg); + buf.putByte((byte)1); + write(packet); + } + + private static final byte[] nomoresessions=Util.str2byte("no-more-sessions@openssh.com"); + public void noMoreSessionChannels() throws Exception{ + Buffer buf=new Buffer(); + Packet packet=new Packet(buf); + packet.reset(); + buf.putByte((byte)SSH_MSG_GLOBAL_REQUEST); + buf.putString(nomoresessions); + buf.putByte((byte)0); + write(packet); + } + + private HostKey hostkey=null; + public HostKey getHostKey(){ return hostkey; } + public String getHost(){return host;} + public String getUserName(){return username;} + public int getPort(){return port;} + public void setHostKeyAlias(String hostKeyAlias){ + this.hostKeyAlias=hostKeyAlias; + } + public String getHostKeyAlias(){ + return hostKeyAlias; + } + + /** + * Sets the interval to send a keep-alive message. If zero is + * specified, any keep-alive message must not be sent. The default interval + * is zero. + * + * @param interval the specified interval, in milliseconds. + * @see #getServerAliveInterval() + */ + public void setServerAliveInterval(int interval) throws JSchException { + setTimeout(interval); + this.serverAliveInterval=interval; + } + + /** + * Returns setting for the interval to send a keep-alive message. + * + * @see #setServerAliveInterval(int) + */ + public int getServerAliveInterval(){ + return this.serverAliveInterval; + } + + /** + * Sets the number of keep-alive messages which may be sent without + * receiving any messages back from the server. If this threshold is + * reached while keep-alive messages are being sent, the connection will + * be disconnected. The default value is one. + * + * @param count the specified count + * @see #getServerAliveCountMax() + */ + public void setServerAliveCountMax(int count){ + this.serverAliveCountMax=count; + } + + /** + * Returns setting for the threshold to send keep-alive messages. + * + * @see #setServerAliveCountMax(int) + */ + public int getServerAliveCountMax(){ + return this.serverAliveCountMax; + } + + public void setDaemonThread(boolean enable){ + this.daemon_thread=enable; + } + + private String[] checkCiphers(String ciphers){ + if(ciphers==null || ciphers.length()==0) + return null; + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + JSch.getLogger().log(Logger.INFO, + "CheckCiphers: "+ciphers); + } + + String cipherc2s=getConfig("cipher.c2s"); + String ciphers2c=getConfig("cipher.s2c"); + + Vector result=new Vector(); + String[] _ciphers=Util.split(ciphers, ","); + for(int i=0; i<_ciphers.length; i++){ + String cipher=_ciphers[i]; + if(ciphers2c.indexOf(cipher) == -1 && cipherc2s.indexOf(cipher) == -1) + continue; + if(!checkCipher(getConfig(cipher))){ + result.addElement(cipher); + } + } + if(result.size()==0) + return null; + String[] foo=new String[result.size()]; + System.arraycopy(result.toArray(), 0, foo, 0, result.size()); + + if(JSch.getLogger().isEnabled(Logger.INFO)){ + for(int i=0; inull. + * + * @param identityRepository + * @see #getIdentityRepository() + */ + public void setIdentityRepository(IdentityRepository identityRepository){ + this.identityRepository = identityRepository; + } + + /** + * Gets the identityRepository. + * If this.identityRepository is null, + * JSch#getIdentityRepository() will be invoked. + * + * @see JSch#getIdentityRepository() + */ + IdentityRepository getIdentityRepository(){ + if(identityRepository == null) + return jsch.getIdentityRepository(); + return identityRepository; + } + + /** + * Sets the hostkeyRepository, which will be referred in checking host keys. + * + * @param hostkeyRepository + * @see #getHostKeyRepository() + */ + public void setHostKeyRepository(HostKeyRepository hostkeyRepository){ + this.hostkeyRepository = hostkeyRepository; + } + + /** + * Gets the hostkeyRepository. + * If this.hostkeyRepository is null, + * JSch#getHostKeyRepository() will be invoked. + * + * @see JSch#getHostKeyRepository() + */ + public HostKeyRepository getHostKeyRepository(){ + if(hostkeyRepository == null) + return jsch.getHostKeyRepository(); + return hostkeyRepository; + } + + /* + // setProxyCommand("ssh -l user2 host2 -o 'ProxyCommand ssh user1@host1 nc host2 22' nc %h %p") + public void setProxyCommand(String command){ + setProxy(new ProxyCommand(command)); + } + + class ProxyCommand implements Proxy { + String command; + Process p = null; + InputStream in = null; + OutputStream out = null; + ProxyCommand(String command){ + this.command = command; + } + public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws Exception { + String _command = command.replace("%h", host); + _command = _command.replace("%p", new Integer(port).toString()); + p = Runtime.getRuntime().exec(_command); + in = p.getInputStream(); + out = p.getOutputStream(); + } + public Socket getSocket() { return null; } + public InputStream getInputStream() { return in; } + public OutputStream getOutputStream() { return out; } + public void close() { + try{ + if(p!=null){ + p.getErrorStream().close(); + p.getOutputStream().close(); + p.getInputStream().close(); + p.destroy(); + p=null; + } + } + catch(IOException e){ + } + } + } + */ + + private void applyConfig() throws JSchException { + ConfigRepository configRepository = jsch.getConfigRepository(); + if(configRepository == null){ + return; + } + + ConfigRepository.Config config = + configRepository.getConfig(org_host); + + String value = null; + + if(username==null){ + value = config.getUser(); + if(value != null) + username = value; + } + + value = config.getHostname(); + if(value != null) + host = value; + + int port = config.getPort(); + if(port != -1) + this.port = port; + + checkConfig(config, "kex"); + checkConfig(config, "server_host_key"); + + checkConfig(config, "cipher.c2s"); + checkConfig(config, "cipher.s2c"); + checkConfig(config, "mac.c2s"); + checkConfig(config, "mac.s2c"); + checkConfig(config, "compression.c2s"); + checkConfig(config, "compression.s2c"); + checkConfig(config, "compression_level"); + + checkConfig(config, "StrictHostKeyChecking"); + checkConfig(config, "HashKnownHosts"); + checkConfig(config, "PreferredAuthentications"); + checkConfig(config, "MaxAuthTries"); + checkConfig(config, "ClearAllForwardings"); + + value = config.getValue("HostKeyAlias"); + if(value != null) + this.setHostKeyAlias(value); + + value = config.getValue("UserKnownHostsFile"); + if(value != null) { + KnownHosts kh = new KnownHosts(jsch); + kh.setKnownHosts(value); + this.setHostKeyRepository(kh); + } + + String[] values = config.getValues("IdentityFile"); + if(values != null) { + String[] global = + configRepository.getConfig("").getValues("IdentityFile"); + if(global != null){ + for(int i = 0; i < global.length; i++){ + jsch.addIdentity(global[i]); + } + } + else { + global = new String[0]; + } + if(values.length - global.length > 0){ + IdentityRepository.Wrapper ir = + new IdentityRepository.Wrapper(jsch.getIdentityRepository(), true); + for(int i = 0; i < values.length; i++){ + String ifile = values[i]; + for(int j = 0; j < global.length; j++){ + if(!ifile.equals(global[j])) + continue; + ifile = null; + break; + } + if(ifile == null) + continue; + Identity identity = + IdentityFile.newInstance(ifile, null, jsch); + ir.add(identity); + } + this.setIdentityRepository(ir); + } + } + + value = config.getValue("ServerAliveInterval"); + if(value != null) { + try { + this.setServerAliveInterval(Integer.parseInt(value)); + } + catch(NumberFormatException e){ + } + } + + value = config.getValue("ConnectTimeout"); + if(value != null) { + try { + setTimeout(Integer.parseInt(value)); + } + catch(NumberFormatException e){ + } + } + + value = config.getValue("MaxAuthTries"); + if(value != null) { + setConfig("MaxAuthTries", value); + } + + value = config.getValue("ClearAllForwardings"); + if(value != null) { + setConfig("ClearAllForwardings", value); + } + + } + + private void applyConfigChannel(ChannelSession channel) throws JSchException { + ConfigRepository configRepository = jsch.getConfigRepository(); + if(configRepository == null){ + return; + } + + ConfigRepository.Config config = + configRepository.getConfig(org_host); + + String value = null; + + value = config.getValue("ForwardAgent"); + if(value != null){ + channel.setAgentForwarding(value.equals("yes")); + } + + value = config.getValue("RequestTTY"); + if(value != null){ + channel.setPty(value.equals("yes")); + } + } + + private void requestPortForwarding() throws JSchException { + + if(getConfig("ClearAllForwardings").equals("yes")) + return; + + ConfigRepository configRepository = jsch.getConfigRepository(); + if(configRepository == null){ + return; + } + + ConfigRepository.Config config = + configRepository.getConfig(org_host); + + String[] values = config.getValues("LocalForward"); + if(values != null){ + for(int i = 0; i < values.length; i++) { + setPortForwardingL(values[i]); + } + } + + values = config.getValues("RemoteForward"); + if(values != null){ + for(int i = 0; i < values.length; i++) { + setPortForwardingR(values[i]); + } + } + } + + private void checkConfig(ConfigRepository.Config config, String key){ + String value = config.getValue(key); + if(value != null) + this.setConfig(key, value); + } +} diff --git a/src/main/java/com/jcraft/jsch/SftpATTRS.java b/src/main/java/com/jcraft/jsch/SftpATTRS.java new file mode 100644 index 00000000..a871427e --- /dev/null +++ b/src/main/java/com/jcraft/jsch/SftpATTRS.java @@ -0,0 +1,294 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/* + uint32 flags + uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE + uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID + uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS + uint32 atime present only if flag SSH_FILEXFER_ACMODTIME + uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME + uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED + string extended_type + string extended_data + ... more extended data (extended_type - extended_data pairs), + so that number of pairs equals extended_count +*/ +public class SftpATTRS { + + static final int S_ISUID = 04000; // set user ID on execution + static final int S_ISGID = 02000; // set group ID on execution + static final int S_ISVTX = 01000; // sticky bit ****** NOT DOCUMENTED ***** + + static final int S_IRUSR = 00400; // read by owner + static final int S_IWUSR = 00200; // write by owner + static final int S_IXUSR = 00100; // execute/search by owner + static final int S_IREAD = 00400; // read by owner + static final int S_IWRITE= 00200; // write by owner + static final int S_IEXEC = 00100; // execute/search by owner + + static final int S_IRGRP = 00040; // read by group + static final int S_IWGRP = 00020; // write by group + static final int S_IXGRP = 00010; // execute/search by group + + static final int S_IROTH = 00004; // read by others + static final int S_IWOTH = 00002; // write by others + static final int S_IXOTH = 00001; // execute/search by others + + private static final int pmask = 0xFFF; + + public String getPermissionsString() { + StringBuffer buf = new StringBuffer(10); + + if(isDir()) buf.append('d'); + else if(isLink()) buf.append('l'); + else buf.append('-'); + + if((permissions & S_IRUSR)!=0) buf.append('r'); + else buf.append('-'); + + if((permissions & S_IWUSR)!=0) buf.append('w'); + else buf.append('-'); + + if((permissions & S_ISUID)!=0) buf.append('s'); + else if ((permissions & S_IXUSR)!=0) buf.append('x'); + else buf.append('-'); + + if((permissions & S_IRGRP)!=0) buf.append('r'); + else buf.append('-'); + + if((permissions & S_IWGRP)!=0) buf.append('w'); + else buf.append('-'); + + if((permissions & S_ISGID)!=0) buf.append('s'); + else if((permissions & S_IXGRP)!=0) buf.append('x'); + else buf.append('-'); + + if((permissions & S_IROTH) != 0) buf.append('r'); + else buf.append('-'); + + if((permissions & S_IWOTH) != 0) buf.append('w'); + else buf.append('-'); + + if((permissions & S_IXOTH) != 0) buf.append('x'); + else buf.append('-'); + return (buf.toString()); + } + + public String getAtimeString(){ + Date date= new Date(((long)atime)*1000L); + return (date.toString()); + } + + public String getMtimeString(){ + Date date= new Date(((long)mtime)*1000L); + return (date.toString()); + } + + public static final int SSH_FILEXFER_ATTR_SIZE= 0x00000001; + public static final int SSH_FILEXFER_ATTR_UIDGID= 0x00000002; + public static final int SSH_FILEXFER_ATTR_PERMISSIONS= 0x00000004; + public static final int SSH_FILEXFER_ATTR_ACMODTIME= 0x00000008; + public static final int SSH_FILEXFER_ATTR_EXTENDED= 0x80000000; + + static final int S_IFMT=0xf000; + static final int S_IFIFO=0x1000; + static final int S_IFCHR=0x2000; + static final int S_IFDIR=0x4000; + static final int S_IFBLK=0x6000; + static final int S_IFREG=0x8000; + static final int S_IFLNK=0xa000; + static final int S_IFSOCK=0xc000; + + int flags=0; + long size; + int uid; + int gid; + int permissions; + int atime; + int mtime; + String[] extended=null; + + private SftpATTRS(){ + } + + static SftpATTRS getATTR(Buffer buf){ + SftpATTRS attr=new SftpATTRS(); + attr.flags=buf.getInt(); + if((attr.flags&SSH_FILEXFER_ATTR_SIZE)!=0){ attr.size=buf.getLong(); } + if((attr.flags&SSH_FILEXFER_ATTR_UIDGID)!=0){ + attr.uid=buf.getInt(); attr.gid=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0){ + attr.permissions=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ + attr.atime=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0){ + attr.mtime=buf.getInt(); + } + if((attr.flags&SSH_FILEXFER_ATTR_EXTENDED)!=0){ + int count=buf.getInt(); + if(count>0){ + attr.extended=new String[count*2]; + for(int i=0; i0){ + for(int i=0; i0){ + for(int i=0; i= session.max_auth_tries){ + return false; + } + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name (ISO-10646 UTF-8, as defined in [RFC-2279]) + // string service name (US-ASCII) "ssh-userauth" ? "ssh-connection" + // string "keyboard-interactive" (US-ASCII) + // string language tag (as defined in [RFC-3066]) + // string submethods (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + //buf.putString("ssh-userauth".getBytes()); + buf.putString(Util.str2byte("keyboard-interactive")); + buf.putString(Util.empty); + buf.putString(Util.empty); + session.write(packet); + + boolean firsttime=true; + loop: + while(true){ + buf=session.read(buf); + int command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop; + } + if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); +// System.err.println(new String(foo)+ +// " partial_success:"+(partial_success!=0)); + + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + + if(firsttime){ + return false; + //throw new JSchException("USERAUTH KI is not supported"); + //cancel=true; // ?? + } + session.auth_failures++; + break; + } + if(command==SSH_MSG_USERAUTH_INFO_REQUEST){ + firsttime=false; + buf.getInt(); buf.getByte(); buf.getByte(); + String name=Util.byte2str(buf.getString()); + String instruction=Util.byte2str(buf.getString()); + String languate_tag=Util.byte2str(buf.getString()); + int num=buf.getInt(); + String[] prompt=new String[num]; + boolean[] echo=new boolean[num]; + for(int i=0; i= 0){ + response=new byte[1][]; + response[0]=password; + password=null; + } + else if(num>0 + ||(name.length()>0 || instruction.length()>0) + ){ + if(userinfo!=null){ + UIKeyboardInteractive kbi=(UIKeyboardInteractive)userinfo; + String[] _response=kbi.promptKeyboardInteractive(dest, + name, + instruction, + prompt, + echo); + if(_response!=null){ + response=new byte[_response.length][]; + for(int i=0; i<_response.length; i++){ + response[i]=Util.str2byte(_response[i]); + } + } + } + } + + // byte SSH_MSG_USERAUTH_INFO_RESPONSE(61) + // int num-responses + // string response[1] (ISO-10646 UTF-8) + // ... + // string response[num-responses] (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_INFO_RESPONSE); + if(num>0 && + (response==null || // cancel + num!=response.length)){ + + if(response==null){ + // working around the bug in OpenSSH ;-< + buf.putInt(num); + for(int i=0; i= session.max_auth_tries){ + return false; + } + + if(password==null){ + if(userinfo==null){ + //throw new JSchException("USERAUTH fail"); + return false; + } + if(!userinfo.promptPassword("Password for "+dest)){ + throw new JSchAuthCancelException("password"); + //break; + } + + String _password=userinfo.getPassword(); + if(_password==null){ + throw new JSchAuthCancelException("password"); + //break; + } + password=Util.str2byte(_password); + } + + byte[] _username=null; + _username=Util.str2byte(username); + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "password" + // boolen FALSE + // string plaintext password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("password")); + buf.putByte((byte)0); + buf.putString(password); + session.write(packet); + + loop: + while(true){ + buf=session.read(buf); + int command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop; + } + if(command==SSH_MSG_USERAUTH_PASSWD_CHANGEREQ){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] instruction=buf.getString(); + byte[] tag=buf.getString(); + if(userinfo==null || + !(userinfo instanceof UIKeyboardInteractive)){ + if(userinfo!=null){ + userinfo.showMessage("Password must be changed."); + } + return false; + } + + UIKeyboardInteractive kbi=(UIKeyboardInteractive)userinfo; + String[] response; + String name="Password Change Required"; + String[] prompt={"New Password: "}; + boolean[] echo={false}; + response=kbi.promptKeyboardInteractive(dest, + name, + Util.byte2str(instruction), + prompt, + echo); + if(response==null){ + throw new JSchAuthCancelException("password"); + } + + byte[] newpassword=Util.str2byte(response[0]); + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "password" + // boolen TRUE + // string plaintext old password (ISO-10646 UTF-8) + // string plaintext new password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("password")); + buf.putByte((byte)1); + buf.putString(password); + buf.putString(newpassword); + Util.bzero(newpassword); + response=null; + session.write(packet); + continue loop; + } + if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); + //System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + session.auth_failures++; + break; + } + else{ + //System.err.println("USERAUTH fail ("+buf.getCommand()+")"); +// throw new JSchException("USERAUTH fail ("+buf.getCommand()+")"); + return false; + } + } + + if(password!=null){ + Util.bzero(password); + password=null; + } + + } + + } + finally{ + if(password!=null){ + Util.bzero(password); + password=null; + } + } + + //throw new JSchException("USERAUTH fail"); + //return false; + } +} diff --git a/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java new file mode 100644 index 00000000..2f2ab4a6 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java @@ -0,0 +1,234 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +import java.util.Vector; + +class UserAuthPublicKey extends UserAuth{ + + public boolean start(Session session) throws Exception{ + super.start(session); + + Vector identities=session.getIdentityRepository().getIdentities(); + + byte[] passphrase=null; + byte[] _username=null; + + int command; + + synchronized(identities){ + if(identities.size()<=0){ + return false; + } + + _username=Util.str2byte(username); + + for(int i=0; i= session.max_auth_tries){ + return false; + } + + Identity identity=(Identity)(identities.elementAt(i)); + byte[] pubkeyblob=identity.getPublicKeyBlob(); + + if(pubkeyblob!=null){ + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "publickey" + // boolen FALSE + // string public key algorithm name + // string public key blob + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("publickey")); + buf.putByte((byte)0); + buf.putString(Util.str2byte(identity.getAlgName())); + buf.putString(pubkeyblob); + session.write(packet); + + loop1: + while(true){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_PK_OK){ + break; + } + else if(command==SSH_MSG_USERAUTH_FAILURE){ + break; + } + else if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop1; + } + else{ + //System.err.println("USERAUTH fail ("+command+")"); + //throw new JSchException("USERAUTH fail ("+command+")"); + break; + } + } + + if(command!=SSH_MSG_USERAUTH_PK_OK){ + continue; + } + } + +//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted()); + + int count=5; + while(true){ + if((identity.isEncrypted() && passphrase==null)){ + if(userinfo==null) throw new JSchException("USERAUTH fail"); + if(identity.isEncrypted() && + !userinfo.promptPassphrase("Passphrase for "+identity.getName())){ + throw new JSchAuthCancelException("publickey"); + //throw new JSchException("USERAUTH cancel"); + //break; + } + String _passphrase=userinfo.getPassphrase(); + if(_passphrase!=null){ + passphrase=Util.str2byte(_passphrase); + } + } + + if(!identity.isEncrypted() || passphrase!=null){ + if(identity.setPassphrase(passphrase)){ + if(passphrase!=null && + (session.getIdentityRepository() instanceof IdentityRepository.Wrapper)){ + ((IdentityRepository.Wrapper)session.getIdentityRepository()).check(); + } + break; + } + } + Util.bzero(passphrase); + passphrase=null; + count--; + if(count==0)break; + } + + Util.bzero(passphrase); + passphrase=null; +//System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted()); + + if(identity.isEncrypted()) continue; + if(pubkeyblob==null) pubkeyblob=identity.getPublicKeyBlob(); + +//System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob); + + if(pubkeyblob==null) continue; + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "publickey" + // boolen TRUE + // string public key algorithm name + // string public key blob + // string signature + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("publickey")); + buf.putByte((byte)1); + buf.putString(Util.str2byte(identity.getAlgName())); + buf.putString(pubkeyblob); + +// byte[] tmp=new byte[buf.index-5]; +// System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length); +// buf.putString(signature); + + byte[] sid=session.getSessionId(); + int sidlen=sid.length; + byte[] tmp=new byte[4+sidlen+buf.index-5]; + tmp[0]=(byte)(sidlen>>>24); + tmp[1]=(byte)(sidlen>>>16); + tmp[2]=(byte)(sidlen>>>8); + tmp[3]=(byte)(sidlen); + System.arraycopy(sid, 0, tmp, 4, sidlen); + System.arraycopy(buf.buffer, 5, tmp, 4+sidlen, buf.index-5); + byte[] signature=identity.getSignature(tmp); + if(signature==null){ // for example, too long key length. + break; + } + buf.putString(signature); + session.write(packet); + + loop2: + while(true){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_SUCCESS){ + return true; + } + else if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop2; + } + else if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); + //System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + session.auth_failures++; + break; + } + //System.err.println("USERAUTH fail ("+command+")"); + //throw new JSchException("USERAUTH fail ("+command+")"); + break; + } + } + } + return false; + } +} diff --git a/src/main/java/com/jcraft/jsch/UserInfo.java b/src/main/java/com/jcraft/jsch/UserInfo.java new file mode 100644 index 00000000..73fe5612 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/UserInfo.java @@ -0,0 +1,39 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface UserInfo{ + String getPassphrase(); + String getPassword(); + boolean promptPassword(String message); + boolean promptPassphrase(String message); + boolean promptYesNo(String message); + void showMessage(String message); +} diff --git a/src/main/java/com/jcraft/jsch/Util.java b/src/main/java/com/jcraft/jsch/Util.java new file mode 100644 index 00000000..ef0700c2 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/Util.java @@ -0,0 +1,526 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; +import java.net.Socket; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +class Util{ + + private static final byte[] b64 =Util.str2byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="); + private static byte val(byte foo){ + if(foo == '=') return 0; + for(int j=0; j>>4)); + if(buf[i+2]==(byte)'='){ j++; break;} + foo[j+1]=(byte)(((val(buf[i+1])&0x0f)<<4)|((val(buf[i+2])&0x3c)>>>2)); + if(buf[i+3]==(byte)'='){ j+=2; break;} + foo[j+2]=(byte)(((val(buf[i+2])&0x03)<<6)|(val(buf[i+3])&0x3f)); + j+=3; + } + byte[] bar=new byte[j]; + System.arraycopy(foo, 0, bar, 0, j); + return bar; + } + catch(ArrayIndexOutOfBoundsException e) { + throw new JSchException("fromBase64: invalid base64 data", e); + } + } + static byte[] toBase64(byte[] buf, int start, int length){ + + byte[] tmp=new byte[length*2]; + int i,j,k; + + int foo=(length/3)*3+start; + i=0; + for(j=start; j>>2)&0x3f; + tmp[i++]=b64[k]; + k=(buf[j]&0x03)<<4|(buf[j+1]>>>4)&0x0f; + tmp[i++]=b64[k]; + k=(buf[j+1]&0x0f)<<2|(buf[j+2]>>>6)&0x03; + tmp[i++]=b64[k]; + k=buf[j+2]&0x3f; + tmp[i++]=b64[k]; + } + + foo=(start+length)-foo; + if(foo==1){ + k=(buf[j]>>>2)&0x3f; + tmp[i++]=b64[k]; + k=((buf[j]&0x03)<<4)&0x3f; + tmp[i++]=b64[k]; + tmp[i++]=(byte)'='; + tmp[i++]=(byte)'='; + } + else if(foo==2){ + k=(buf[j]>>>2)&0x3f; + tmp[i++]=b64[k]; + k=(buf[j]&0x03)<<4|(buf[j+1]>>>4)&0x0f; + tmp[i++]=b64[k]; + k=((buf[j+1]&0x0f)<<2)&0x3f; + tmp[i++]=b64[k]; + tmp[i++]=(byte)'='; + } + byte[] bar=new byte[i]; + System.arraycopy(tmp, 0, bar, 0, i); + return bar; + +// return sun.misc.BASE64Encoder().encode(buf); + } + + static String[] split(String foo, String split){ + if(foo==null) + return null; + byte[] buf=Util.str2byte(foo); + java.util.Vector bar=new java.util.Vector(); + int start=0; + int index; + while(true){ + index=foo.indexOf(split, start); + if(index>=0){ + bar.addElement(Util.byte2str(buf, start, index-start)); + start=index+1; + continue; + } + bar.addElement(Util.byte2str(buf, start, buf.length-start)); + break; + } + String[] result=new String[bar.size()]; + for(int i=0; i0 && name[0]=='.'){ + if(pattern.length>0 && pattern[0]=='.'){ + if(pattern.length==2 && pattern[1]=='*') return true; + return glob(pattern, pattern_index+1, name, name_index+1); + } + return false; + } + return glob(pattern, pattern_index, name, name_index); + } + static private boolean glob(byte[] pattern, int pattern_index, + byte[] name, int name_index){ + //System.err.println("glob: "+new String(pattern)+", "+pattern_index+" "+new String(name)+", "+name_index); + + int patternlen=pattern.length; + if(patternlen==0) + return false; + + int namelen=name.length; + int i=pattern_index; + int j=name_index; + + while(i>>4)&0xf]); + sb.append(chars[(bar)&0xf]); + if(i+1>>8); + bar[j++]=(byte)foo[i]; + } + } + return bar; + } + */ + static void bzero(byte[] foo){ + if(foo==null) + return; + for(int i=0; iivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + + public boolean isCBC(){return true; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/AES128CTR.java b/src/main/java/com/jcraft/jsch/jce/AES128CTR.java new file mode 100644 index 00000000..a88b2d5c --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/AES128CTR.java @@ -0,0 +1,75 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES128CTR implements Cipher{ + private static final int ivsize=16; + private static final int bsize=16; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + + public boolean isCBC(){return false; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/AES192CBC.java b/src/main/java/com/jcraft/jsch/jce/AES192CBC.java new file mode 100644 index 00000000..e67535bf --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/AES192CBC.java @@ -0,0 +1,73 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES192CBC implements Cipher{ + private static final int ivsize=16; + private static final int bsize=24; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/AES192CTR.java b/src/main/java/com/jcraft/jsch/jce/AES192CTR.java new file mode 100644 index 00000000..e99735e3 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/AES192CTR.java @@ -0,0 +1,73 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES192CTR implements Cipher{ + private static final int ivsize=16; + private static final int bsize=24; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/AES256CBC.java b/src/main/java/com/jcraft/jsch/jce/AES256CBC.java new file mode 100644 index 00000000..cb6ac972 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/AES256CBC.java @@ -0,0 +1,73 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES256CBC implements Cipher{ + private static final int ivsize=16; + private static final int bsize=32; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CBC/"+pad); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/AES256CTR.java b/src/main/java/com/jcraft/jsch/jce/AES256CTR.java new file mode 100644 index 00000000..2437703f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/AES256CTR.java @@ -0,0 +1,73 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.spec.*; + +public class AES256CTR implements Cipher{ + private static final int ivsize=16; + private static final int bsize=32; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec keyspec=new SecretKeySpec(key, "AES"); + cipher=javax.crypto.Cipher.getInstance("AES/CTR/"+pad); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java b/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java new file mode 100644 index 00000000..dbe6bf9a --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java @@ -0,0 +1,70 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class ARCFOUR implements Cipher{ + private static final int ivsize=8; + private static final int bsize=16; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + byte[] tmp; + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + cipher=javax.crypto.Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java b/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java new file mode 100644 index 00000000..b77b0c1f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java @@ -0,0 +1,73 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class ARCFOUR128 implements Cipher{ + private static final int ivsize=8; + private static final int bsize=16; + private static final int skip=1536; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + byte[] tmp; + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + cipher=javax.crypto.Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key); + } + byte[] foo=new byte[1]; + for(int i=0; ibsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + cipher=javax.crypto.Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key); + } + byte[] foo=new byte[1]; + for(int i=0; iivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + try{ + SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish"); + cipher=javax.crypto.Cipher.getInstance("Blowfish/CBC/"+pad); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/DH.java b/src/main/java/com/jcraft/jsch/jce/DH.java new file mode 100644 index 00000000..b8a4506f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/DH.java @@ -0,0 +1,100 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import javax.crypto.*; +import javax.crypto.spec.*; +import com.jcraft.jsch.JSchException; + +public class DH implements com.jcraft.jsch.DH{ + BigInteger p; + BigInteger g; + BigInteger e; // my public key + byte[] e_array; + BigInteger f; // your public key + BigInteger K; // shared secret key + byte[] K_array; + + private KeyPairGenerator myKpairGen; + private KeyAgreement myKeyAgree; + public void init() throws Exception{ + myKpairGen=KeyPairGenerator.getInstance("DH"); + myKeyAgree=KeyAgreement.getInstance("DH"); + } + public byte[] getE() throws Exception{ + if(e==null){ + DHParameterSpec dhSkipParamSpec=new DHParameterSpec(p, g); + myKpairGen.initialize(dhSkipParamSpec); + KeyPair myKpair=myKpairGen.generateKeyPair(); + myKeyAgree.init(myKpair.getPrivate()); + e=((javax.crypto.interfaces.DHPublicKey)(myKpair.getPublic())).getY(); + e_array=e.toByteArray(); + } + return e_array; + } + public byte[] getK() throws Exception{ + if(K==null){ + KeyFactory myKeyFac=KeyFactory.getInstance("DH"); + DHPublicKeySpec keySpec=new DHPublicKeySpec(f, p, g); + PublicKey yourPubKey=myKeyFac.generatePublic(keySpec); + myKeyAgree.doPhase(yourPubKey, true); + byte[] mySharedSecret=myKeyAgree.generateSecret(); + K=new BigInteger(1, mySharedSecret); + K_array=K.toByteArray(); + K_array=mySharedSecret; + } + return K_array; + } + public void setP(byte[] p){ setP(new BigInteger(1, p)); } + public void setG(byte[] g){ setG(new BigInteger(1, g)); } + public void setF(byte[] f){ setF(new BigInteger(1, f)); } + void setP(BigInteger p){this.p=p;} + void setG(BigInteger g){this.g=g;} + void setF(BigInteger f){this.f=f;} + + // e, f must be in [1, p-1]. + public void checkRange() throws Exception { + /* + checkRange(e); + checkRange(f); + */ + } + + private void checkRange(BigInteger tmp) throws Exception { + BigInteger one = BigInteger.ONE; + BigInteger p_1 = p.subtract(one); + // !(1bsize){ + byte[] tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, bsize); + key = tmp; + } + SecretKeySpec skey = new SecretKeySpec(key, algorithm); + mac = Mac.getInstance(algorithm); + mac.init(skey); + } + + private final byte[] tmp = new byte[4]; + public void update(int i){ + tmp[0] = (byte)(i>>>24); + tmp[1] = (byte)(i>>>16); + tmp[2] = (byte)(i>>>8); + tmp[3] = (byte)i; + update(tmp, 0, 4); + } + + public void update(byte foo[], int s, int l){ + mac.update(foo, s, l); + } + + public void doFinal(byte[] buf, int offset){ + try{ + mac.doFinal(buf, offset); + } + catch(ShortBufferException e){ + System.err.println(e); + } + } + + public String getName(){ + return name; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/HMACMD5.java b/src/main/java/com/jcraft/jsch/jce/HMACMD5.java new file mode 100644 index 00000000..ca02c1cb --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/HMACMD5.java @@ -0,0 +1,42 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.MAC; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class HMACMD5 extends HMAC { + public HMACMD5(){ + name = "hmac-md5"; + bsize = 16; + algorithm = "HmacMD5"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/HMACMD596.java b/src/main/java/com/jcraft/jsch/jce/HMACMD596.java new file mode 100644 index 00000000..039c3712 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/HMACMD596.java @@ -0,0 +1,46 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +public class HMACMD596 extends HMACMD5 { + public HMACMD596(){ + name="hmac-md5-96"; + } + + public int getBlockSize(){ + return 12; + }; + + private final byte[] _buf16 = new byte[16]; + public void doFinal(byte[] buf, int offset){ + super.doFinal(_buf16, 0); + System.arraycopy(_buf16, 0, buf, offset, 12); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/HMACSHA1.java b/src/main/java/com/jcraft/jsch/jce/HMACSHA1.java new file mode 100644 index 00000000..eda37275 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/HMACSHA1.java @@ -0,0 +1,38 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +public class HMACSHA1 extends HMAC { + public HMACSHA1(){ + name = "hmac-sha1"; + bsize = 20; + algorithm = "HmacSHA1"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/HMACSHA196.java b/src/main/java/com/jcraft/jsch/jce/HMACSHA196.java new file mode 100644 index 00000000..247ecb2d --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/HMACSHA196.java @@ -0,0 +1,47 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +public class HMACSHA196 extends HMACSHA1 { + + public HMACSHA196(){ + name = "hmac-sha1-96"; + } + + public int getBlockSize(){ + return 12; + }; + + private final byte[] _buf20 = new byte[20]; + public void doFinal(byte[] buf, int offset){ + super.doFinal(_buf20, 0); + System.arraycopy(_buf20, 0, buf, offset, 12); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/HMACSHA256.java b/src/main/java/com/jcraft/jsch/jce/HMACSHA256.java new file mode 100644 index 00000000..0517438f --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/HMACSHA256.java @@ -0,0 +1,38 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +public class HMACSHA256 extends HMAC { + public HMACSHA256(){ + name = "hmac-sha2-256"; + bsize = 32; + algorithm = "HmacSHA256"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/HMACSHA512.java b/src/main/java/com/jcraft/jsch/jce/HMACSHA512.java new file mode 100644 index 00000000..be0bc90e --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/HMACSHA512.java @@ -0,0 +1,38 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +public class HMACSHA512 extends HMAC { + public HMACSHA512(){ + name = "hmac-sha2-512"; + bsize = 64; + algorithm = "HmacSHA512"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java b/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java new file mode 100644 index 00000000..d36848d7 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java @@ -0,0 +1,62 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.security.*; +import java.security.interfaces.*; + +public class KeyPairGenDSA implements com.jcraft.jsch.KeyPairGenDSA{ + byte[] x; // private + byte[] y; // public + byte[] p; + byte[] q; + byte[] g; + + public void init(int key_size) throws Exception{ + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); + keyGen.initialize(key_size, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + PublicKey pubKey=pair.getPublic(); + PrivateKey prvKey=pair.getPrivate(); + + x=((DSAPrivateKey)prvKey).getX().toByteArray(); + y=((DSAPublicKey)pubKey).getY().toByteArray(); + + DSAParams params=((DSAKey)prvKey).getParams(); + p=params.getP().toByteArray(); + q=params.getQ().toByteArray(); + g=params.getG().toByteArray(); + } + public byte[] getX(){return x;} + public byte[] getY(){return y;} + public byte[] getP(){return p;} + public byte[] getQ(){return q;} + public byte[] getG(){return g;} +} diff --git a/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java b/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java new file mode 100644 index 00000000..82d62a14 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java @@ -0,0 +1,96 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; +import com.jcraft.jsch.JSchException; + +public class KeyPairGenECDSA implements com.jcraft.jsch.KeyPairGenECDSA { + byte[] d; + byte[] r; + byte[] s; + ECPublicKey pubKey; + ECPrivateKey prvKey; + ECParameterSpec params; + public void init(int key_size) throws Exception { + String name=null; + if(key_size==256) name="secp256r1"; + else if(key_size==384) name="secp384r1"; + else if(key_size==521) name="secp521r1"; + else throw new JSchException("unsupported key size: "+key_size); + + for(int i = 0; i<1000; i++) { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); + ECGenParameterSpec ecsp = new ECGenParameterSpec(name); + kpg.initialize(ecsp); + KeyPair kp = kpg.genKeyPair(); + prvKey = (ECPrivateKey)kp.getPrivate(); + pubKey = (ECPublicKey)kp.getPublic(); + params=pubKey.getParams(); + d=((ECPrivateKey)prvKey).getS().toByteArray(); + ECPoint w = pubKey.getW(); + r = w.getAffineX().toByteArray(); + s = w.getAffineY().toByteArray(); + + if(r.length!=s.length) continue; + if(key_size==256 && r.length==32) break; + if(key_size==384 && r.length==48) break; + if(key_size==521 && r.length==66) break; + } + if(d.lengthtmp.length){ tmp=new byte[len]; } + random.nextBytes(tmp); + System.arraycopy(tmp, 0, foo, start, len); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SHA1.java b/src/main/java/com/jcraft/jsch/jce/SHA1.java new file mode 100644 index 00000000..0d3997c2 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SHA1.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; + +import java.security.*; + +public class SHA1 implements HASH{ + MessageDigest md; + public int getBlockSize(){return 20;} + public void init() throws Exception{ + try{ md=MessageDigest.getInstance("SHA-1"); } + catch(Exception e){ + System.err.println(e); + } + } + public void update(byte[] foo, int start, int len) throws Exception{ + md.update(foo, start, len); + } + public byte[] digest() throws Exception{ + return md.digest(); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SHA256.java b/src/main/java/com/jcraft/jsch/jce/SHA256.java new file mode 100644 index 00000000..f2f90948 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SHA256.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; + +import java.security.*; + +public class SHA256 implements HASH { + MessageDigest md; + public int getBlockSize(){return 32;} + public void init() throws Exception { + try{ md=MessageDigest.getInstance("SHA-256"); } + catch(Exception e){ + System.err.println(e); + } + } + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + public byte[] digest() throws Exception { + return md.digest(); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SHA384.java b/src/main/java/com/jcraft/jsch/jce/SHA384.java new file mode 100644 index 00000000..976f9cc3 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SHA384.java @@ -0,0 +1,49 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.security.*; + +public class SHA384 implements com.jcraft.jsch.HASH { + MessageDigest md; + public int getBlockSize(){return 48;} + public void init() throws Exception { + try{ md=MessageDigest.getInstance("SHA-384"); } + catch(Exception e){ + System.err.println(e); + } + } + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + public byte[] digest() throws Exception { + return md.digest(); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SHA512.java b/src/main/java/com/jcraft/jsch/jce/SHA512.java new file mode 100644 index 00000000..4c6b1949 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SHA512.java @@ -0,0 +1,49 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.security.*; + +public class SHA512 implements com.jcraft.jsch.HASH { + MessageDigest md; + public int getBlockSize(){return 64;} + public void init() throws Exception { + try{ md=MessageDigest.getInstance("SHA-512"); } + catch(Exception e){ + System.err.println(e); + } + } + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + public byte[] digest() throws Exception { + return md.digest(); + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java b/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java new file mode 100644 index 00000000..94ba7dea --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java @@ -0,0 +1,157 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; +import com.jcraft.jsch.Buffer; + +public class SignatureDSA implements com.jcraft.jsch.SignatureDSA{ + + java.security.Signature signature; + KeyFactory keyFactory; + + public void init() throws Exception{ + signature=java.security.Signature.getInstance("SHA1withDSA"); + keyFactory=KeyFactory.getInstance("DSA"); + } + public void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception{ + DSAPublicKeySpec dsaPubKeySpec = + new DSAPublicKeySpec(new BigInteger(y), + new BigInteger(p), + new BigInteger(q), + new BigInteger(g)); + PublicKey pubKey=keyFactory.generatePublic(dsaPubKeySpec); + signature.initVerify(pubKey); + } + public void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception{ + DSAPrivateKeySpec dsaPrivKeySpec = + new DSAPrivateKeySpec(new BigInteger(x), + new BigInteger(p), + new BigInteger(q), + new BigInteger(g)); + PrivateKey prvKey = keyFactory.generatePrivate(dsaPrivKeySpec); + signature.initSign(prvKey); + } + public byte[] sign() throws Exception{ + byte[] sig=signature.sign(); +/* +System.err.print("sign["+sig.length+"] "); +for(int i=0; i 1 && + secret[0] == 0 && (secret[1]&0x80) == 0) { + byte[] tmp=new byte[secret.length-1]; + System.arraycopy(secret, 1, tmp, 0, tmp.length); + return normalize(tmp); + } + else { + return secret; + } + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureECDSA256.java b/src/main/java/com/jcraft/jsch/jce/SignatureECDSA256.java new file mode 100644 index 00000000..3d36c841 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SignatureECDSA256.java @@ -0,0 +1,41 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; +import com.jcraft.jsch.Buffer; + +public class SignatureECDSA256 extends SignatureECDSAN { + String getName() { + return "ecdsa-sha2-nistp256"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureECDSA384.java b/src/main/java/com/jcraft/jsch/jce/SignatureECDSA384.java new file mode 100644 index 00000000..8055d447 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SignatureECDSA384.java @@ -0,0 +1,41 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; +import com.jcraft.jsch.Buffer; + +public class SignatureECDSA384 extends SignatureECDSAN { + String getName() { + return "ecdsa-sha2-nistp384"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureECDSA521.java b/src/main/java/com/jcraft/jsch/jce/SignatureECDSA521.java new file mode 100644 index 00000000..7cb03c02 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SignatureECDSA521.java @@ -0,0 +1,41 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; +import com.jcraft.jsch.Buffer; + +public class SignatureECDSA521 extends SignatureECDSAN { + String getName() { + return "ecdsa-sha2-nistp521"; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java b/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java new file mode 100644 index 00000000..8037eb20 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java @@ -0,0 +1,192 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.*; +import com.jcraft.jsch.Buffer; + +public abstract class SignatureECDSAN implements com.jcraft.jsch.SignatureECDSA { + + Signature signature; + KeyFactory keyFactory; + + abstract String getName(); + + public void init() throws Exception{ + String name = getName(); + String foo="SHA256withECDSA"; + if(name.equals("ecdsa-sha2-nistp384")) foo="SHA384withECDSA"; + else if(name.equals("ecdsa-sha2-nistp521")) foo="SHA512withECDSA"; + signature=java.security.Signature.getInstance(foo); + keyFactory=KeyFactory.getInstance("EC"); + } + + public void setPubKey(byte[] r, byte[] s) throws Exception{ + + // r and s must be unsigned values. + r=insert0(r); + s=insert0(s); + + String name="secp256r1"; + if(r.length>=64) name="secp521r1"; + else if(r.length>=48) name="secp384r1"; + + AlgorithmParameters param = AlgorithmParameters.getInstance("EC"); + param.init(new ECGenParameterSpec(name)); + ECParameterSpec ecparam = + (ECParameterSpec)param.getParameterSpec(ECParameterSpec.class); + ECPoint w = new ECPoint(new BigInteger(1, r), new BigInteger(1, s)); + PublicKey pubKey = + keyFactory.generatePublic(new ECPublicKeySpec(w, ecparam)); + signature.initVerify(pubKey); + } + + public void setPrvKey(byte[] d) throws Exception{ + + // d must be unsigned value. + d=insert0(d); + + String name="secp256r1"; + if(d.length>=64) name="secp521r1"; + else if(d.length>=48) name="secp384r1"; + + AlgorithmParameters param = AlgorithmParameters.getInstance("EC"); + param.init(new ECGenParameterSpec(name)); + ECParameterSpec ecparam = + (ECParameterSpec)param.getParameterSpec(ECParameterSpec.class); + BigInteger _d = new BigInteger(1, d); + PrivateKey prvKey = + keyFactory.generatePrivate(new ECPrivateKeySpec(_d, ecparam)); + signature.initSign(prvKey); + } + public byte[] sign() throws Exception{ + byte[] sig=signature.sign(); + + // It seems that the output from SunEC is in ASN.1, + // so we have to convert it. + if(sig[0]==0x30 && // in ASN.1 + ((sig[1]+2 == sig.length) || + ((sig[1]&0x80)!=0 && (sig[2]&0xff)+3==sig.length))){// 2bytes for len + + int index=3; + if((sig[1]&0x80)!=0 && (sig[2]&0xff)+3==sig.length) + index=4; + + byte[] r = new byte[sig[index]]; + byte[] s = new byte[sig[index+2+sig[index]]]; + System.arraycopy(sig, index+1, r, 0, r.length); + System.arraycopy(sig, index+3+sig[index], s, 0, s.length); + + r = chop0(r); + s = chop0(s); + + Buffer buf = new Buffer(); + buf.putMPInt(r); + buf.putMPInt(s); + + sig=new byte[buf.getLength()]; + buf.setOffSet(0); + buf.getByte(sig); + } + + return sig; + } + public void update(byte[] foo) throws Exception{ + signature.update(foo); + } + public boolean verify(byte[] sig) throws Exception{ + + // It seems that SunEC expects ASN.1 data, + // so we have to convert it. + if(!(sig[0]==0x30 && // not in ASN.1 + ((sig[1]+2 == sig.length) || + ((sig[1]&0x80)!=0 && (sig[2]&0xff)+3==sig.length)))) { + Buffer b = new Buffer(sig); + + b.getString(); // ecdsa-sha2-nistp256 + b.getInt(); + + byte[] r = b.getMPInt(); + byte[] s = b.getMPInt(); + + r=insert0(r); + s=insert0(s); + + byte[] asn1 = null; + if(r.length<64){ + asn1 = new byte[6+r.length+s.length]; + asn1[0] = (byte)0x30; + asn1[1] = (byte)(4+r.length+s.length); + asn1[2] = (byte)0x02; + asn1[3] = (byte)r.length; + System.arraycopy(r, 0, asn1, 4, r.length); + asn1[r.length+4] = (byte)0x02; + asn1[r.length+5] = (byte)s.length; + System.arraycopy(s, 0, asn1, (6+r.length), s.length); + } + else { + asn1 = new byte[6+r.length+s.length+1]; + asn1[0] = (byte)0x30; + asn1[1] = (byte)0x81; + asn1[2] = (byte)(4+r.length+s.length); + asn1[3] = (byte)0x02; + asn1[4] = (byte)r.length; + System.arraycopy(r, 0, asn1, 5, r.length); + asn1[r.length+5] = (byte)0x02; + asn1[r.length+6] = (byte)s.length; + System.arraycopy(s, 0, asn1, (7+r.length), s.length); + } + sig=asn1; + } + + return signature.verify(sig); + } + + private byte[] insert0(byte[] buf){ + if ((buf[0] & 0x80) == 0) return buf; + byte[] tmp = new byte[buf.length+1]; + System.arraycopy(buf, 0, tmp, 1, buf.length); + bzero(buf); + return tmp; + } + private byte[] chop0(byte[] buf){ + if(buf[0]!=0) return buf; + byte[] tmp = new byte[buf.length-1]; + System.arraycopy(buf, 1, tmp, 0, tmp.length); + bzero(buf); + return tmp; + } + + private void bzero(byte[] buf){ + for(int i = 0; iivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + cipher=javax.crypto.Cipher.getInstance("DESede/CBC/"+pad); +/* + // The following code does not work on IBM's JDK 1.4.1 + SecretKeySpec skeySpec = new SecretKeySpec(key, "DESede"); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); +*/ + DESedeKeySpec keyspec=new DESedeKeySpec(key); + SecretKeyFactory keyfactory=SecretKeyFactory.getInstance("DESede"); + SecretKey _key=keyfactory.generateSecret(keyspec); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return true; } +} diff --git a/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java b/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java new file mode 100644 index 00000000..f8a98071 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java @@ -0,0 +1,86 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Cipher; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class TripleDESCTR implements Cipher{ + private static final int ivsize=8; + private static final int bsize=24; + private javax.crypto.Cipher cipher; + public int getIVSize(){return ivsize;} + public int getBlockSize(){return bsize;} + public void init(int mode, byte[] key, byte[] iv) throws Exception{ + String pad="NoPadding"; + //if(padding) pad="PKCS5Padding"; + byte[] tmp; + if(iv.length>ivsize){ + tmp=new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv=tmp; + } + if(key.length>bsize){ + tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key=tmp; + } + + try{ + cipher=javax.crypto.Cipher.getInstance("DESede/CTR/"+pad); +/* + // The following code does not work on IBM's JDK 1.4.1 + SecretKeySpec skeySpec = new SecretKeySpec(key, "DESede"); + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); +*/ + DESedeKeySpec keyspec=new DESedeKeySpec(key); + SecretKeyFactory keyfactory=SecretKeyFactory.getInstance("DESede"); + SecretKey _key=keyfactory.generateSecret(keyspec); + synchronized(javax.crypto.Cipher.class){ + cipher.init((mode==ENCRYPT_MODE? + javax.crypto.Cipher.ENCRYPT_MODE: + javax.crypto.Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); + } + } + catch(Exception e){ + cipher=null; + throw e; + } + } + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception{ + cipher.update(foo, s1, len, bar, s2); + } + public boolean isCBC(){return false; } +} diff --git a/src/main/java/com/jcraft/jsch/jcraft/Compression.java b/src/main/java/com/jcraft/jsch/jcraft/Compression.java new file mode 100644 index 00000000..a973fb9a --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jcraft/Compression.java @@ -0,0 +1,140 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; +import com.jcraft.jzlib.*; +import com.jcraft.jsch.*; + +public class Compression implements com.jcraft.jsch.Compression { + static private final int BUF_SIZE=4096; + private final int buffer_margin=32+20; // AES256 + HMACSHA1 + private int type; + private ZStream stream; + private byte[] tmpbuf=new byte[BUF_SIZE]; + + public Compression(){ + stream=new ZStream(); + } + + public void init(int type, int level){ + if(type==DEFLATER){ + stream.deflateInit(level); + this.type=DEFLATER; + } + else if(type==INFLATER){ + stream.inflateInit(); + inflated_buf=new byte[BUF_SIZE]; + this.type=INFLATER; + } + } + + private byte[] inflated_buf; + + public byte[] compress(byte[] buf, int start, int[] len){ + stream.next_in=buf; + stream.next_in_index=start; + stream.avail_in=len[0]-start; + int status; + int outputlen=start; + byte[] outputbuf=buf; + int tmp=0; + + do{ + stream.next_out=tmpbuf; + stream.next_out_index=0; + stream.avail_out=BUF_SIZE; + status=stream.deflate(JZlib.Z_PARTIAL_FLUSH); + switch(status){ + case JZlib.Z_OK: + tmp=BUF_SIZE-stream.avail_out; + if(outputbuf.lengthbuffer.length-start){ + byte[] foo=new byte[inflated_end+start]; + System.arraycopy(buffer, 0, foo, 0, start); + System.arraycopy(inflated_buf, 0, foo, start, inflated_end); + buffer=foo; + } + else{ + System.arraycopy(inflated_buf, 0, buffer, start, inflated_end); + } + length[0]=inflated_end; + return buffer; + default: + System.err.println("uncompress: inflate returnd "+status); + return null; + } + } + } +} diff --git a/src/main/java/com/jcraft/jsch/jcraft/HMAC.java b/src/main/java/com/jcraft/jsch/jcraft/HMAC.java new file mode 100644 index 00000000..f5c5e14c --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jcraft/HMAC.java @@ -0,0 +1,108 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import java.security.*; + +class HMAC{ + + /* + * Refer to RFC2104. + * + * H(K XOR opad, H(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + private static final int B=64; + private byte[] k_ipad=null; + private byte[] k_opad=null; + + private MessageDigest md=null; + + private int bsize=0; + + protected void setH(MessageDigest md){ + this.md=md; + bsize=md.getDigestLength(); + } + + public int getBlockSize(){return bsize;}; + public void init(byte[] key) throws Exception{ + md.reset(); + if(key.length>bsize){ + byte[] tmp=new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, bsize); + key=tmp; + } + + /* if key is longer than B bytes reset it to key=MD5(key) */ + if(key.length>B){ + md.update(key, 0, key.length); + key=md.digest(); + } + + k_ipad=new byte[B]; + System.arraycopy(key, 0, k_ipad, 0, key.length); + k_opad=new byte[B]; + System.arraycopy(key, 0, k_opad, 0, key.length); + + /* XOR key with ipad and opad values */ + for(int i=0; i>>24); + tmp[1]=(byte)(i>>>16); + tmp[2]=(byte)(i>>>8); + tmp[3]=(byte)i; + update(tmp, 0, 4); + } + + public void update(byte foo[], int s, int l){ + md.update(foo, s, l); + } + + public void doFinal(byte[] buf, int offset){ + byte[] result=md.digest(); + md.update(k_opad, 0, B); + md.update(result, 0, bsize); + try{md.digest(buf, offset, bsize);}catch(Exception e){} + md.update(k_ipad, 0, B); + } +} diff --git a/src/main/java/com/jcraft/jsch/jcraft/HMACMD5.java b/src/main/java/com/jcraft/jsch/jcraft/HMACMD5.java new file mode 100644 index 00000000..174a126e --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jcraft/HMACMD5.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; +import java.security.*; + +public class HMACMD5 extends HMAC implements MAC{ + private static final String name="hmac-md5"; + + public HMACMD5(){ + super(); + MessageDigest md=null; + try{ md=MessageDigest.getInstance("MD5"); } + catch(Exception e){ + System.err.println(e); + } + setH(md); + } + + public String getName(){ + return name; + } +} diff --git a/src/main/java/com/jcraft/jsch/jcraft/HMACMD596.java b/src/main/java/com/jcraft/jsch/jcraft/HMACMD596.java new file mode 100644 index 00000000..5fcc225e --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jcraft/HMACMD596.java @@ -0,0 +1,50 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; + +public class HMACMD596 extends HMACMD5{ + + private static final String name="hmac-md5-96"; + private static final int BSIZE=12; + + public int getBlockSize(){return BSIZE;}; + + private final byte[] _buf16=new byte[16]; + public void doFinal(byte[] buf, int offset){ + super.doFinal(_buf16, 0); + System.arraycopy(_buf16, 0, buf, offset, BSIZE); + } + + public String getName(){ + return name; + } +} diff --git a/src/main/java/com/jcraft/jsch/jcraft/HMACSHA1.java b/src/main/java/com/jcraft/jsch/jcraft/HMACSHA1.java new file mode 100644 index 00000000..d750ed7a --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jcraft/HMACSHA1.java @@ -0,0 +1,51 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; +import java.security.*; + +public class HMACSHA1 extends HMAC implements MAC{ + private static final String name="hmac-sha1"; + + public HMACSHA1(){ + super(); + MessageDigest md=null; + try{ md=MessageDigest.getInstance("SHA-1"); } + catch(Exception e){ + System.err.println(e); + } + setH(md); + } + + public String getName(){ + return name; + } +} diff --git a/src/main/java/com/jcraft/jsch/jcraft/HMACSHA196.java b/src/main/java/com/jcraft/jsch/jcraft/HMACSHA196.java new file mode 100644 index 00000000..20d961b1 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jcraft/HMACSHA196.java @@ -0,0 +1,50 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jcraft; + +import com.jcraft.jsch.MAC; + +public class HMACSHA196 extends HMACSHA1{ + + private static final String name="hmac-sha1-96"; + private static final int BSIZE=12; + + public int getBlockSize(){return BSIZE;}; + + private final byte[] _buf16=new byte[20]; + public void doFinal(byte[] buf, int offset){ + super.doFinal(_buf16, 0); + System.arraycopy(_buf16, 0, buf, offset, BSIZE); + } + + public String getName(){ + return name; + } +} diff --git a/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java b/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java new file mode 100644 index 00000000..3b39b847 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java @@ -0,0 +1,177 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jgss; + +import com.jcraft.jsch.JSchException; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.MessageProp; +import org.ietf.jgss.Oid; + +public class GSSContextKrb5 implements com.jcraft.jsch.GSSContext{ + + private static final String pUseSubjectCredsOnly = + "javax.security.auth.useSubjectCredsOnly"; + private static String useSubjectCredsOnly = + getSystemProperty(pUseSubjectCredsOnly); + + private GSSContext context=null; + public void create(String user, String host) throws JSchException{ + try{ + // RFC 1964 + Oid krb5=new Oid("1.2.840.113554.1.2.2"); + // Kerberos Principal Name Form + Oid principalName=new Oid("1.2.840.113554.1.2.2.1"); + + GSSManager mgr=GSSManager.getInstance(); + + GSSCredential crd=null; + /* + try{ + GSSName _user=mgr.createName(user, principalName); + crd=mgr.createCredential(_user, + GSSCredential.DEFAULT_LIFETIME, + krb5, + GSSCredential.INITIATE_ONLY); + } + catch(GSSException crdex){ + } + */ + + String cname=host; + try{ + cname=InetAddress.getByName(cname).getCanonicalHostName(); + } + catch(UnknownHostException e){ + } + GSSName _host=mgr.createName("host/"+cname, principalName); + + context=mgr.createContext(_host, + krb5, + crd, + GSSContext.DEFAULT_LIFETIME); + + // RFC4462 3.4. GSS-API Session + // + // When calling GSS_Init_sec_context(), the client MUST set + // integ_req_flag to "true" to request that per-message integrity + // protection be supported for this context. In addition, + // deleg_req_flag MAY be set to "true" to request access delegation, if + // requested by the user. + // + // Since the user authentication process by its nature authenticates + // only the client, the setting of mutual_req_flag is not needed for + // this process. This flag SHOULD be set to "false". + + // TODO: OpenSSH's sshd does accepts 'false' for mutual_req_flag + //context.requestMutualAuth(false); + context.requestMutualAuth(true); + context.requestConf(true); + context.requestInteg(true); // for MIC + context.requestCredDeleg(true); + context.requestAnonymity(false); + + return; + } + catch(GSSException ex){ + throw new JSchException(ex.toString()); + } + } + + public boolean isEstablished(){ + return context.isEstablished(); + } + + public byte[] init(byte[] token, int s, int l) throws JSchException { + try{ + // Without setting "javax.security.auth.useSubjectCredsOnly" to "false", + // Sun's JVM for Un*x will show messages to stderr in + // processing context.initSecContext(). + // This hack is not thread safe ;-<. + // If that property is explicitly given as "true" or "false", + // this hack must not be invoked. + if(useSubjectCredsOnly==null){ + setSystemProperty(pUseSubjectCredsOnly, "false"); + } + return context.initSecContext(token, 0, l); + } + catch(GSSException ex){ + throw new JSchException(ex.toString()); + } + catch(java.lang.SecurityException ex){ + throw new JSchException(ex.toString()); + } + finally{ + if(useSubjectCredsOnly==null){ + // By the default, it must be "true". + setSystemProperty(pUseSubjectCredsOnly, "true"); + } + } + } + + public byte[] getMIC(byte[] message, int s, int l){ + try{ + MessageProp prop = new MessageProp(0, true); + return context.getMIC(message, s, l, prop); + } + catch(GSSException ex){ + return null; + } + } + + public void dispose(){ + try{ + context.dispose(); + } + catch(GSSException ex){ + } + } + + private static String getSystemProperty(String key){ + try{ return System.getProperty(key); } + catch(Exception e){ + // We are not allowed to get the System properties. + return null; + } + } + + private static void setSystemProperty(String key, String value){ + try{ System.setProperty(key, value); } + catch(Exception e){ + // We are not allowed to set the System properties. + } + } +} diff --git a/tools/bin/ant b/tools/bin/ant new file mode 100644 index 00000000..365f9004 --- /dev/null +++ b/tools/bin/ant @@ -0,0 +1,134 @@ +#! /bin/sh + +if [ -f $HOME/.antrc ] ; then + . $HOME/.antrc +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +case "`uname`" in + CYGWIN*) cygwin=true ;; + Darwin*) darwin=true ;; +esac + +if [ -z "$ANT_HOME" ] ; then + # try to find ANT + if [ -d /opt/ant ] ; then + ANT_HOME=/opt/ant + fi + + if [ -d ${HOME}/opt/ant ] ; then + ANT_HOME=${HOME}/opt/ant + fi + + ## resolve links - $0 may be a link to ant's home + PRG=$0 + progname=`basename $0` + + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '.*/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname $PRG`/$link" + fi + done + + ANT_HOME=`dirname "$PRG"`/.. + +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$ANT_HOME" ] && + ANT_HOME=`cygpath --unix "$ANT_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + else + JAVACMD=java + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." + echo " We cannot execute $JAVACMD" + exit +fi + +if [ -n "$CLASSPATH" ] ; then + LOCALCLASSPATH=$CLASSPATH +fi + +# add in the dependency .jar files +DIRLIBS=${ANT_HOME}/lib/*.jar +for i in ${DIRLIBS} +do + # if the directory is empty, then it will return the input string + # this is stupid, so case for it + if [ "$i" != "${DIRLIBS}" ] ; then + if [ -z "$LOCALCLASSPATH" ] ; then + LOCALCLASSPATH=$i + else + LOCALCLASSPATH="$i":$LOCALCLASSPATH + fi + fi +done + +if [ -n "$JAVA_HOME" ] ; then + if [ -f "$JAVA_HOME/lib/tools.jar" ] ; then + LOCALCLASSPATH=$LOCALCLASSPATH:$JAVA_HOME/lib/tools.jar + fi + + if [ -f "$JAVA_HOME/lib/classes.zip" ] ; then + LOCALCLASSPATH=$LOCALCLASSPATH:$JAVA_HOME/lib/classes.zip + fi + + # OSX hack to make Ant work with jikes + if $darwin ; then + OSXHACK="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes" + if [ -d ${OSXHACK} ] ; then + for i in ${OSXHACK}/*.jar + do + JIKESPATH=$JIKESPATH:$i + done + fi + fi + +else + echo "Warning: JAVA_HOME environment variable is not set." + echo " If build fails because sun.* classes could not be found" + echo " you will need to set the JAVA_HOME environment variable" + echo " to the installation directory of java." +fi + +# supply JIKESPATH to Ant as jikes.class.path +if [ -n "$JIKESPATH" ] ; then + if [ -n "$ANT_OPTS" ] ; then + ANT_OPTS="$ANT_OPTS -Djikes.class.path=$JIKESPATH" + else + ANT_OPTS=-Djikes.class.path=$JIKESPATH + fi +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + ANT_HOME=`cygpath --path --windows "$ANT_HOME"` + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + LOCALCLASSPATH=`cygpath --path --windows "$LOCALCLASSPATH"` +fi + +$JAVACMD -classpath "$LOCALCLASSPATH" -Dant.home="${ANT_HOME}" $ANT_OPTS org.apache.tools.ant.Main "$@" diff --git a/tools/bin/ant.bat b/tools/bin/ant.bat new file mode 100644 index 00000000..81a2835b --- /dev/null +++ b/tools/bin/ant.bat @@ -0,0 +1,110 @@ +@echo off + +if exist "%HOME%\antrc_pre.bat" call "%HOME%\antrc_pre.bat" + +if not "%OS%"=="Windows_NT" goto win9xStart +:winNTStart +@setlocal + +rem %~dp0 is name of current script under NT +set DEFAULT_ANT_HOME=%~dp0 + +rem : operator works similar to make : operator +set DEFAULT_ANT_HOME=%DEFAULT_ANT_HOME%\.. + +if "%ANT_HOME%"=="" set ANT_HOME=%DEFAULT_ANT_HOME% +set DEFAULT_ANT_HOME= + +rem Need to check if we are using the 4NT shell... +if "%eval[2+2]" == "4" goto setup4NT + +rem On NT/2K grab all arguments at once +set ANT_CMD_LINE_ARGS=%* +goto doneStart + +:setup4NT +set ANT_CMD_LINE_ARGS=%$ +goto doneStart + +:win9xStart +rem Slurp the command line arguments. This loop allows for an unlimited number of +rem agruments (up to the command line limit, anyway). + +set ANT_CMD_LINE_ARGS= + +:setupArgs +if %1a==a goto doneStart +set ANT_CMD_LINE_ARGS=%ANT_CMD_LINE_ARGS% %1 +shift +goto setupArgs + +:doneStart +rem This label provides a place for the argument list loop to break out +rem and for NT handling to skip to. + +rem find ANT_HOME +if not "%ANT_HOME%"=="" goto checkJava + +rem check for ant in Program Files on system drive +if not exist "%SystemDrive%\Program Files\ant" goto checkSystemDrive +set ANT_HOME=%SystemDrive%\Program Files\ant +goto checkJava + +:checkSystemDrive +rem check for ant in root directory of system drive +if not exist %SystemDrive%\ant\nul goto checkCDrive +set ANT_HOME=%SystemDrive%\ant +goto checkJava + +:checkCDrive +rem check for ant in C:\ant for Win9X users +if not exist C:\ant\nul goto noAntHome +set ANT_HOME=C:\ant +goto checkJava + +:noAntHome +echo ANT_HOME is not set and ant could not be located. Please set ANT_HOME. +goto end + +:checkJava +set _JAVACMD=%JAVACMD% +set LOCALCLASSPATH=%CLASSPATH% +for %%i in ("%ANT_HOME%\lib\*.jar") do call "%ANT_HOME%\bin\lcp.bat" %%i + +if "%JAVA_HOME%" == "" goto noJavaHome +if "%_JAVACMD%" == "" set _JAVACMD=%JAVA_HOME%\bin\java +if exist "%JAVA_HOME%\lib\tools.jar" call "%ANT_HOME%\bin\lcp.bat" %JAVA_HOME%\lib\tools.jar +if exist "%JAVA_HOME%\lib\classes.zip" call "%ANT_HOME%\bin\lcp.bat" %JAVA_HOME%\lib\classes.zip +goto checkJikes + +:noJavaHome +if "%_JAVACMD%" == "" set _JAVACMD=java +echo. +echo Warning: JAVA_HOME environment variable is not set. +echo If build fails because sun.* classes could not be found +echo you will need to set the JAVA_HOME environment variable +echo to the installation directory of java. +echo. + +:checkJikes +if not "%JIKESPATH%" == "" goto runAntWithJikes + +:runAnt +"%_JAVACMD%" -classpath "%LOCALCLASSPATH%" -Dant.home="%ANT_HOME%" %ANT_OPTS% org.apache.tools.ant.Main %ANT_CMD_LINE_ARGS% +goto end + +:runAntWithJikes +"%_JAVACMD%" -classpath "%LOCALCLASSPATH%" -Dant.home="%ANT_HOME%" -Djikes.class.path="%JIKESPATH%" %ANT_OPTS% org.apache.tools.ant.Main %ANT_CMD_LINE_ARGS% + +:end +set LOCALCLASSPATH= +set _JAVACMD= +set ANT_CMD_LINE_ARGS= + +if not "%OS%"=="Windows_NT" goto mainEnd +:winNTend +@endlocal + +:mainEnd +if exist "%HOME%\antrc_post.bat" call "%HOME%\antrc_post.bat" + diff --git a/tools/bin/antRun b/tools/bin/antRun new file mode 100644 index 00000000..f0a18f16 --- /dev/null +++ b/tools/bin/antRun @@ -0,0 +1,9 @@ +#! /bin/sh + +# Args: DIR command +cd "$1" +CMD="$2" +shift +shift + +exec $CMD "$@" diff --git a/tools/bin/antRun.bat b/tools/bin/antRun.bat new file mode 100644 index 00000000..91689326 --- /dev/null +++ b/tools/bin/antRun.bat @@ -0,0 +1,20 @@ +@echo off + +rem Change drive and directory to %1 (Win9X only for NT/2K use "cd /d") +cd %1 +%1\ +set ANT_RUN_CMD=%2 +shift +shift + +set PARAMS= +:loop +if ""%1 == "" goto runCommand +set PARAMS=%PARAMS% %1 +shift +goto loop + +:runCommand +rem echo %ANT_RUN_CMD% %PARAMS% +%ANT_RUN_CMD% %PARAMS% + diff --git a/tools/bin/lcp.bat b/tools/bin/lcp.bat new file mode 100644 index 00000000..2c7e2760 --- /dev/null +++ b/tools/bin/lcp.bat @@ -0,0 +1,9 @@ +set _CLASSPATHCOMPONENT=%1 +:argCheck +if %2a==a goto gotAllArgs +shift +set _CLASSPATHCOMPONENT=%_CLASSPATHCOMPONENT% %1 +goto argCheck +:gotAllArgs +set LOCALCLASSPATH=%_CLASSPATHCOMPONENT%;%LOCALCLASSPATH% + diff --git a/tools/bin/runant.pl b/tools/bin/runant.pl new file mode 100644 index 00000000..a2cc523e --- /dev/null +++ b/tools/bin/runant.pl @@ -0,0 +1,131 @@ +#!/usr/bin/perl +####################################################################### +# +# runant.pl +# +# wrapper script for invoking ant in a platform with Perl installed +# this may include cgi-bin invocation, which is considered somewhat daft. +# (slo: that should be a separate file which can be derived from this +# and returns the XML formatted output) +# +# the code is not totally portable due to classpath and directory splitting +# issues. oops. (NB, use File::Spec::Functions will help and the code is +# structured for the catfile() call, but because of perl version funnies +# the code is not included. +# +# created: 2000-8-24 +# last modified: 2000-8-24 +# author: Steve Loughran steve_l@sourceforge.net +####################################################################### +# +# Assumptions: +# +# - the "java" executable/script is on the command path +# - ANT_HOME has been set +# - target platform uses ":" as classpath separator or perl indicates it is dos/win32 +# - target platform uses "/" as directory separator. + +#be fussy about variables +use strict; + +#platform specifics (disabled) +#use File::Spec::Functions; + +#turn warnings on during dev; generates a few spurious uninitialised var access warnings +#use warnings; + +#and set $debug to 1 to turn on trace info +my $debug=0; + +####################################################################### +# +# check to make sure environment is setup +# + +my $HOME = $ENV{ANT_HOME}; +if ($HOME eq "") + { + die "\n\nANT_HOME *MUST* be set!\n\n"; + } + +my $JAVACMD = $ENV{JAVACMD}; +$JAVACMD = "java" if $JAVACMD eq ""; + +#ISSUE: what java wants to split up classpath varies from platform to platform +#and perl is not too hot at hinting which box it is on. +#here I assume ":" 'cept on win32 and dos. Add extra tests here as needed. +my $s=":"; +if(($^O eq "MSWin32") || ($^O eq "dos")) + { + $s=";"; + } + +#build up standard classpath +my $localpath=$ENV{CLASSPATH}; +if ($localpath eq "") + { + print "warning: no initial classpath\n" if ($debug); + $localpath=""; + } + +#add jar files. I am sure there is a perl one liner to do this. +my $jarpattern="$HOME/lib/*.jar"; +my @jarfiles =glob($jarpattern); +print "jarfiles=@jarfiles\n" if ($debug); +my $jar; +foreach $jar (@jarfiles ) + { + $localpath.="$s$jar"; + } + +#if Java home is defined, look for tools.jar & classes.zip and add to classpath +my $JAVA_HOME = $ENV{JAVA_HOME}; +if ($JAVA_HOME ne "") + { + my $tools="$JAVA_HOME/lib/tools.jar"; + if (-e "$tools") + { + $localpath .= "$s$tools"; + } + my $classes="$JAVA_HOME/lib/classes.zip"; + if (-e $classes) + { + $localpath .= "$s$classes"; + } + } +else + { + print "\n\nWarning: JAVA_HOME environment variable is not set.\n". + "If the build fails because sun.* classes could not be found\n". + "you will need to set the JAVA_HOME environment variable\n". + "to the installation directory of java\n"; + } + +#jikes +my @ANT_OPTS=split $ENV{ANT_OPTS}; +if($ENV{JIKESPATH} ne "") + { + push @ANT_OPTS, "-Djikes.class.path=$ENV{JIKESPATH}"; + } + +#construct arguments to java + +my @ARGS; +push @ARGS, "-classpath", "$localpath", "-Dant.home=$HOME"; +push @ARGS, @ANT_OPTS; +push @ARGS, "org.apache.tools.ant.Main"; +push @ARGS, @ARGV; + +print "\n $JAVACMD @ARGS\n\n" if ($debug); + +my $returnValue = system $JAVACMD, @ARGS; +if ($returnValue eq 0) + { + exit 0; + } +else + { + # only 0 and 1 are widely recognized as exit values + # so change the exit value to 1 + exit 1; + } diff --git a/tools/bin/runant.py b/tools/bin/runant.py new file mode 100644 index 00000000..d5e200ca --- /dev/null +++ b/tools/bin/runant.py @@ -0,0 +1,96 @@ +#!/usr/bin/python +""" + + runant.py + + This script is a translation of the runant.pl written by Steve Loughran. + It runs ant with/out arguments, it should be quite portable (thanks to + the python os library) + This script has been tested with Python2.0/Win2K + + created: 2001-04-11 + author: Pierre Dittgen pierre.dittgen@criltelecom.com + + Assumptions: + + - the "java" executable/script is on the command path + - ANT_HOME has been set +""" +import os, os.path, string, sys + +# Change it to 1 to get extra debug information +debug = 0 + +####################################################################### +# +# check to make sure environment is setup +# +if not os.environ.has_key('ANT_HOME'): + print '\n\nANT_HOME *MUST* be set!\n\n' + sys.exit(1) +else: + ANT_HOME = os.environ['ANT_HOME'] + +if not os.environ.has_key('JAVACMD'): + JAVACMD = 'java' +else: + JAVACMD = os.environ['JAVACMD'] + +# Sets the separator char for CLASSPATH +SEPARATOR = ':' +if os.name == 'dos' or os.name == 'nt': + SEPARATOR = ';' + +# Build up standard classpath +localpath = '' +if os.environ.has_key('CLASSPATH'): + localpath = os.environ['CLASSPATH'] +else: + if debug: + print 'Warning: no initial classpath\n' + +# Add jar files +LIBDIR = os.path.join(ANT_HOME, 'lib') +jarfiles = [] +for file in os.listdir(LIBDIR): + if file[-4:] == '.jar': + jarfiles.append(os.path.join(LIBDIR,file)) +if debug: + print 'Jar files:' + for jar in jarfiles: + print jar +localpath = localpath + SEPARATOR + string.join(jarfiles, SEPARATOR) + +# If JAVA_HOME is defined, look for tools.jar & classes.zip +# and add to classpath +if os.environ.has_key('JAVA_HOME') and os.environ['JAVA_HOME'] != '': + JAVA_HOME = os.environ['JAVA_HOME'] + TOOLS = os.path.join(JAVA_HOME, os.path.join('lib', 'tools.jar')) + if os.path.exists(TOOLS): + localpath = localpath + SEPARATOR + TOOLS + CLASSES = os.path.join(JAVA_HOME, os.path.join('lib', 'classes.zip')) + if os.path.exists(CLASSES): + localpath = localpath + SEPARATOR + CLASSES +else: + print '\n\nWarning: JAVA_HOME environment variable is not set.\n', \ + 'If the build fails because sun.* classes could not be found\n', \ + 'you will need to set the JAVA_HOME environment variable\n', \ + 'to the installation directory of java\n' + +# Jikes +ANT_OPTS = [] +if os.environ.has_key('ANT_OPTS'): + ANT_OPTS = string.split(os.environ['ANT_OPTS']) +if os.environ.has_key('JIKESPATH'): + ANT_OPTS.append('-Djikes.class.path=' + os.environ['JIKESPATH']) + +# Builds the commandline +cmdline = '%s -classpath %s -Dant.home=%s %s org.apache.tools.ant.Main %s' \ + % (JAVACMD, localpath, ANT_HOME, string.join(ANT_OPTS,' '), \ + string.join(sys.argv[1:], ' ')) + +if debug: + print '\n%s\n\n' % (cmdline) + +# Run the biniou! +os.system(cmdline)