You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/07/12 16:05:08 UTC

[commons-vfs] 01/02: SFTP: Memory leak because AbstractFileProvider#findFileSystem fails to detect equality of SFTP FileSystemOptions #272

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-vfs.git

commit 0737dc9b155b10598a1421c35bc95cccdc9f9086
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Tue Jul 12 12:03:02 2022 -0400

    SFTP: Memory leak because AbstractFileProvider#findFileSystem fails to
    detect equality of SFTP FileSystemOptions #272
    
    - Normalize File object inputs to absolute Files.
    - Add Eclipse-generated hashCode() and equals() methods.
    - Fix camel-casing of "passPhrase" -> "passphrase".
    - Make defensive copies of byte[]s.
    - Normalize Javadoc comments
---
 .../vfs2/provider/sftp/BytesIdentityInfo.java      | 218 +++++++++-------
 .../commons/vfs2/provider/sftp/IdentityInfo.java   | 285 ++++++++++++---------
 .../apache/commons/vfs2/provider/sftp/Utils.java   |  30 +++
 3 files changed, 318 insertions(+), 215 deletions(-)

diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java
index 07b1794d..419ecf1a 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/BytesIdentityInfo.java
@@ -1,92 +1,126 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.vfs2.provider.sftp;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-
-/**
- * Structure for an identity based on byte arrays.
- *
- * @since 2.4
- */
-public class BytesIdentityInfo implements IdentityProvider {
-
-    private final byte[] passPhrase;
-
-    private final byte[] privateKey;
-
-    private final byte[] publicKey;
-
-    /**
-     * Constructs an identity info with private and passphrase for the private key.
-     *
-     * @param privateKey Private key bytes
-     * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
-     */
-    public BytesIdentityInfo(final byte[] privateKey, final byte[] passPhrase) {
-        this.privateKey = privateKey;
-        this.publicKey = null;
-        this.passPhrase = passPhrase;
-    }
-
-    /**
-     * Constructs an identity info with private and public key and passphrase for the private key.
-     *
-     * @param privateKey Private key bytes
-     * @param publicKey The public key part used for connections with exchange of certificates (can be {@code null})
-     * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
-     */
-    public BytesIdentityInfo(final byte[] privateKey, final byte[] publicKey, final byte[] passPhrase) {
-        this.privateKey = privateKey;
-        this.publicKey = publicKey;
-        this.passPhrase = passPhrase;
-    }
-
-    @Override
-    public void addIdentity(final JSch jsch) throws JSchException {
-        jsch.addIdentity("PrivateKey", privateKey, publicKey, passPhrase);
-    }
-
-    /**
-     * Gets the passphrase.
-     *
-     * @return the passphrase.
-     */
-    public byte[] getPassPhrase() {
-        return passPhrase;
-    }
-
-    /**
-     * Gets the private key.
-     *
-     * @return the private key.
-     */
-    public byte[] getPrivateKeyBytes() {
-        return privateKey;
-    }
-
-    /**
-     * Gets the public key.
-     *
-     * @return the public key.
-     */
-    public byte[] getPublicKeyBytes() {
-        return publicKey;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.vfs2.provider.sftp;
+
+import java.util.Arrays;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+
+/**
+ * Structure for an identity based on byte arrays.
+ *
+ * @since 2.4
+ */
+public class BytesIdentityInfo implements IdentityProvider {
+
+    private final byte[] passphrase;
+
+    private final byte[] privateKey;
+
+    private final byte[] publicKey;
+
+    /**
+     * Constructs an identity info with private and passphrase for the private key.
+     *
+     * @param privateKey Private key bytes
+     * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+     */
+    public BytesIdentityInfo(final byte[] privateKey, final byte[] passphrase) {
+        this(privateKey, null, passphrase);
+    }
+
+    /**
+     * Constructs an identity info with private and public key and passphrase for the private key.
+     *
+     * @param privateKey Private key bytes
+     * @param publicKey The public key part used for connections with exchange of certificates (can be {@code null})
+     * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+     */
+    public BytesIdentityInfo(final byte[] privateKey, final byte[] publicKey, final byte[] passphrase) {
+        this.privateKey = Utils.clone(privateKey);
+        this.publicKey = Utils.clone(publicKey);
+        this.passphrase = Utils.clone(passphrase);
+    }
+
+    @Override
+    public void addIdentity(final JSch jsch) throws JSchException {
+        jsch.addIdentity("PrivateKey", privateKey, publicKey, passphrase);
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof BytesIdentityInfo)) {
+            return false;
+        }
+        final BytesIdentityInfo other = (BytesIdentityInfo) obj;
+        return Arrays.equals(passphrase, other.passphrase) && Arrays.equals(privateKey, other.privateKey) && Arrays.equals(publicKey, other.publicKey);
+    }
+
+    /**
+     * Gets the passphrase.
+     *
+     * @return the passphrase.
+     * @since 2.10.0
+     */
+    public byte[] getPassphrase() {
+        return Utils.clone(passphrase);
+    }
+
+    /**
+     * Gets the passphrase.
+     *
+     * @return the passphrase.
+     * @deprecated Use {@link #getPassphrase()}.
+     */
+    @Deprecated
+    public byte[] getPassPhrase() {
+        return Utils.clone(passphrase);
+    }
+
+    /**
+     * Gets the private key.
+     *
+     * @return the private key.
+     */
+    public byte[] getPrivateKeyBytes() {
+        return Utils.clone(privateKey);
+    }
+
+    /**
+     * Gets the public key.
+     *
+     * @return the public key.
+     */
+    public byte[] getPublicKeyBytes() {
+        return Utils.clone(publicKey);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(passphrase);
+        result = prime * result + Arrays.hashCode(privateKey);
+        result = prime * result + Arrays.hashCode(publicKey);
+        return result;
+    }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java
index e3319d95..266bc8fd 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/IdentityInfo.java
@@ -1,123 +1,162 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.vfs2.provider.sftp;
-
-import java.io.File;
-
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-
-/**
- * Structure for an identity based on Files.
- *
- * @since 2.1
- */
-public class IdentityInfo implements IdentityProvider {
-
-    private final byte[] passPhrase;
-    private final File privateKey;
-    private final File publicKey;
-
-    /**
-     * Constructs an identity info with private key.
-     * <p>
-     * The key is not passphrase protected.
-     * </p>
-     * <p>
-     * We use java.io.File because JSch cannot deal with VFS FileObjects.
-     * </p>
-     *
-     * @param privateKey The file with the private key
-     * @since 2.1
-     */
-    public IdentityInfo(final File privateKey) {
-        this(privateKey, null, null);
-    }
-
-    /**
-     * Constructs an identity info with private key and its passphrase.
-     * <p>
-     * We use java.io.File because JSch cannot deal with VFS FileObjects.
-     * </p>
-     *
-     * @param privateKey The file with the private key
-     * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
-     * @since 2.1
-     */
-    public IdentityInfo(final File privateKey, final byte[] passPhrase) {
-        this(privateKey, null, passPhrase);
-    }
-
-    /**
-     * Constructs an identity info with private and public key and passphrase for the private key.
-     * <p>
-     * We use java.io.File because JSch cannot deal with VFS FileObjects.
-     * </p>
-     *
-     * @param privateKey The file with the private key
-     * @param publicKey  The public key part used for connections with exchange of certificates (can be {@code null})
-     * @param passPhrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
-     * @since 2.1
-     */
-    public IdentityInfo(final File privateKey, final File publicKey, final byte[] passPhrase) {
-        this.privateKey = privateKey;
-        this.publicKey = publicKey;
-        this.passPhrase = passPhrase;
-    }
-
-    /**
-     * @since 2.4
-     */
-    @Override
-    public void addIdentity(final JSch jsch) throws JSchException {
-        jsch.addIdentity(getAbsolutePath(privateKey), getAbsolutePath(publicKey), passPhrase);
-    }
-
-    private String getAbsolutePath(final File file) {
-        return file != null ? file.getAbsolutePath() : null;
-    }
-
-    /**
-     * Get the passphrase of the private key.
-     *
-     * @return the passphrase
-     * @since 2.1
-     */
-    public byte[] getPassPhrase() {
-        return passPhrase;
-    }
-
-    /**
-     * Get the file with the private key.
-     *
-     * @return the file
-     * @since 2.1
-     */
-    public File getPrivateKey() {
-        return privateKey;
-    }
-
-    /**
-     * Get the file with the public key.
-     *
-     * @return the file
-     * @since 2.1
-     */
-    public File getPublicKey() {
-        return publicKey;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.vfs2.provider.sftp;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Objects;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+
+/**
+ * Structure for an identity based on Files.
+ *
+ * @since 2.1
+ */
+public class IdentityInfo implements IdentityProvider {
+
+    private final byte[] passphrase;
+    private final File privateKey;
+    private final File publicKey;
+
+    /**
+     * Constructs an identity info with private key.
+     * <p>
+     * The key is not passphrase protected.
+     * </p>
+     * <p>
+     * We use java.io.File because JSch cannot deal with VFS FileObjects.
+     * </p>
+     *
+     * @param privateKey The file with the private key
+     * @since 2.1
+     */
+    public IdentityInfo(final File privateKey) {
+        this(privateKey, null, null);
+    }
+
+    /**
+     * Constructs an identity info with private key and its passphrase.
+     * <p>
+     * We use java.io.File because JSch cannot deal with VFS FileObjects.
+     * </p>
+     *
+     * @param privateKey The file with the private key
+     * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+     * @since 2.1
+     */
+    public IdentityInfo(final File privateKey, final byte[] passphrase) {
+        this(privateKey, null, passphrase);
+    }
+
+    /**
+     * Constructs an identity info with private and public key and passphrase for the private key.
+     * <p>
+     * We use java.io.File because JSch cannot deal with VFS FileObjects.
+     * </p>
+     *
+     * @param privateKey The file with the private key
+     * @param publicKey  The public key part used for connections with exchange of certificates (can be {@code null})
+     * @param passphrase The passphrase to decrypt the private key (can be {@code null} if no passphrase is used)
+     * @since 2.1
+     */
+    public IdentityInfo(final File privateKey, final File publicKey, final byte[] passphrase) {
+        this.privateKey = getAbsoluteFile(privateKey);
+        this.publicKey = getAbsoluteFile(publicKey);
+        this.passphrase = Utils.clone(passphrase);
+    }
+
+    /**
+     * @since 2.4
+     */
+    @Override
+    public void addIdentity(final JSch jsch) throws JSchException {
+        jsch.addIdentity(getAbsolutePath(privateKey), getAbsolutePath(publicKey), passphrase);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof IdentityInfo)) {
+            return false;
+        }
+        IdentityInfo other = (IdentityInfo) obj;
+        return Arrays.equals(passphrase, other.passphrase) && Objects.equals(privateKey, other.privateKey) && Objects.equals(publicKey, other.publicKey);
+    }
+
+    private File getAbsoluteFile(final File privateKey) {
+        return privateKey != null ? privateKey.getAbsoluteFile() : null;
+    }
+
+    private String getAbsolutePath(final File file) {
+        return file != null ? file.getAbsolutePath() : null;
+    }
+
+    /**
+     * Gets the passphrase of the private key.
+     *
+     * @return the passphrase
+     * @since 2.10.0
+     */
+    public byte[] getPassphrase() {
+        return Utils.clone(passphrase);
+    }
+
+    /**
+     * Gets the passphrase of the private key.
+     *
+     * @return the passphrase
+     * @since 2.1
+     * @deprecated Use {@link #getPassphrase()}.
+     */
+    @Deprecated
+    public byte[] getPassPhrase() {
+        return Utils.clone(passphrase);
+    }
+
+    /**
+     * Gets the file with the private key.
+     *
+     * @return the file
+     * @since 2.1
+     */
+    public File getPrivateKey() {
+        return privateKey;
+    }
+
+    /**
+     * Gets the file with the public key.
+     *
+     * @return the file
+     * @since 2.1
+     */
+    public File getPublicKey() {
+        return publicKey;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(passphrase);
+        result = prime * result + Objects.hash(privateKey, publicKey);
+        return result;
+    }
+}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/Utils.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/Utils.java
new file mode 100644
index 00000000..d84b0ef7
--- /dev/null
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/sftp/Utils.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.vfs2.provider.sftp;
+
+final class Utils {
+
+    private Utils() {
+        // never
+    }
+
+    static byte[] clone(final byte[] array) {
+        return array != null ? array.clone() : null;
+    }
+
+}