diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7e85a2..27b3f3e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,14 +10,12 @@ jobs: #This check is case insensitive if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')" outputs: - artifact-version: ${{ steps.setversion.outputs.version }} - env: - BUILD_VERSION: SNAPSHOT + artifactVersion: ${{ steps.setversion.outputs.version }} steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 14 + java-version: 17 - uses: actions/cache@v1 with: path: ~/.m2/repository @@ -27,18 +25,17 @@ jobs: - name: Ensure to use tagged version run: mvn versions:set --file ./pom.xml -DnewVersion=${GITHUB_REF##*/} # use shell parameter expansion to strip of 'refs/tags' if: startsWith(github.ref, 'refs/tags/') - - name: Export the project version to the job environment and fix it as an ouput of this job + - name: Output project version id: setversion run: | - v=$(mvn help:evaluate "-Dexpression=project.version" -q -DforceStdout) - echo "::set-env name=BUILD_VERSION::${v}" - echo "::set-output name=version::${v}" + BUILD_VERSION=$(mvn help:evaluate "-Dexpression=project.version" -q -DforceStdout) + echo "::set-output name=version::${BUILD_VERSION}" - name: Build and Test run: mvn -B install - - name: Upload snapshot artifact cryptomator-cli-${{ env.BUILD_VERSION }}.jar + - name: Upload artifact cryptomator-cli-${{ steps.setversion.outputs.version }}.jar uses: actions/upload-artifact@v2 with: - name: cryptomator-cli-${{ env.BUILD_VERSION }}.jar + name: cryptomator-cli-${{ steps.setversion.outputs.version }}.jar path: target/cryptomator-cli-*.jar release: @@ -50,7 +47,7 @@ jobs: - name: Download cryptomator-cli.jar uses: actions/download-artifact@v1 with: - name: cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar + name: cryptomator-cli-${{ needs.build.outputs.artifactVersion }}.jar path: . - name: Create Release id: create_release @@ -64,12 +61,12 @@ jobs: :construction: Work in Progress draft: true prerelease: false - - name: Upload cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar to GitHub Releases + - name: Upload cryptomator-cli-${{ needs.build.outputs.artifactVersion }}.jar to GitHub Releases uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar - asset_name: cryptomator-cli-${{ needs.build.outputs.artifact-version }}.jar + asset_path: cryptomator-cli-${{ needs.build.outputs.artifactVersion }}.jar + asset_name: cryptomator-cli-${{ needs.build.outputs.artifactVersion }}.jar asset_content_type: application/jar diff --git a/README.md b/README.md index e6dd37a..232ae86 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,4 @@ This project is a fork of https://github.com/cryptomator/cli and currently, it a lead to data loss. 5. A saner API when unlocking a single vault and the example is: -```java -jar cryptomator-cli.jar --vault $VAULT_PATH --fusemount $MOUNT_PATH --mountFlags $FUSE_OPTIONS``` +```java -jar cryptomator-cli.jar --vault $VAULT_PATH --fusemount $MOUNT_PATH --mountFlags $FUSE_OPTIONS``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6b4d59e..2efc757 100644 --- a/pom.xml +++ b/pom.xml @@ -2,19 +2,19 @@ 4.0.0 org.cryptomator cli - 0.5.0-SNAPSHOT + 0.5.1 Cryptomator CLI Command line program to access encrypted files via WebDAV. https://github.com/cryptomator/cli - 1.9.10 - 1.0.11 - 1.4 - 1.2.3 - 1.2.4 + 2.3.1 + 1.2.6 + 1.5.0 + 1.2.9 + 1.3.3 - 11 + 17 UTF-8 diff --git a/src/main/java/org/cryptomator/cli/CryptomatorCli.java b/src/main/java/org/cryptomator/cli/CryptomatorCli.java index 5b2df06..2e1440b 100644 --- a/src/main/java/org/cryptomator/cli/CryptomatorCli.java +++ b/src/main/java/org/cryptomator/cli/CryptomatorCli.java @@ -17,13 +17,17 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Optional; import java.util.Set; +import com.google.common.base.Preconditions; import org.apache.commons.cli.ParseException; import org.cryptomator.cryptofs.CryptoFileSystemProperties; import org.cryptomator.cryptofs.CryptoFileSystemProvider; +import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +59,9 @@ public class CryptomatorCli { private static final Logger LOG = LoggerFactory.getLogger(CryptomatorCli.class); + private static final byte[] PEPPER = new byte[0]; + private static final String SCHEME = "masterkeyfile"; + public static void main(String[] rawArgs) throws IOException { try { if (rawArgs.length==1 && strEq(rawArgs[0], "--version")) { @@ -102,12 +109,27 @@ private static void startup(Args args) throws IOException { Optional server = initWebDavServer(args); ArrayList mounts = new ArrayList<>(); ArrayList mInfo = new ArrayList<>(); + + SecureRandom secureRandom; + try { + secureRandom = SecureRandom.getInstanceStrong(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("A strong algorithm must exist in every Java platform.", e); + } + MasterkeyFileAccess masterkeyFileAccess = new MasterkeyFileAccess(PEPPER, secureRandom); + for (String vaultName : args.getVaultNames()) { Path vaultPath = Paths.get(args.getVaultPath(vaultName)); LOG.info("Unlocking vault \"{}\" located at {}", vaultName, vaultPath); String vaultPassword = args.getPasswordStrategy(vaultName).password(); CryptoFileSystemProperties properties = CryptoFileSystemProperties.cryptoFileSystemProperties() - .withPassphrase(vaultPassword).build(); + .withKeyLoader(keyId -> { + Preconditions.checkArgument(SCHEME.equalsIgnoreCase(keyId.getScheme()), "Only supports keys with scheme " + SCHEME); + Path keyFilePath = vaultPath.resolve(keyId.getSchemeSpecificPart()); + return masterkeyFileAccess.load(keyFilePath, vaultPassword); + }) + .build(); + Path vaultRoot = CryptoFileSystemProvider.newFileSystem(vaultPath, properties).getPath("/"); Path fuseMountPoint = args.getFuseMountPoint(vaultName); @@ -177,16 +199,34 @@ private static boolean hasActiveMount(ArrayList localList) { } private static void listenForUnMountEvents(ArrayList mounts) { - while (true){ - if (hasActiveMount(mounts)){ + while (true) { + if (hasActiveMount(mounts)) { sleep(2); - }else{ + } else { LOG.info("All vaults are locked, exiting"); break; } } } + private static void waitForShutdown(Runnable runnable) { + Runtime.getRuntime().addShutdownHook(new Thread(runnable)); + LOG.info("Press Ctrl+C to terminate."); + + // Block the main thread infinitely as otherwise when using + // Fuse mounts the application quits immediately. + try { + Object mainThreadBlockLock = new Object(); + synchronized (mainThreadBlockLock) { + while (true) { + mainThreadBlockLock.wait(); + } + } + } catch (Exception e) { + LOG.error("Main thread blocking failed."); + } + } + private static void sleep(int interval) { try { Object mainThreadBlockLock = new Object(); diff --git a/src/main/java/org/cryptomator/cli/frontend/FuseMount.java b/src/main/java/org/cryptomator/cli/frontend/FuseMount.java index 46f10bb..34b6d8c 100644 --- a/src/main/java/org/cryptomator/cli/frontend/FuseMount.java +++ b/src/main/java/org/cryptomator/cli/frontend/FuseMount.java @@ -4,8 +4,8 @@ import java.util.Arrays; import java.util.ArrayList; -import org.cryptomator.frontend.fuse.mount.CommandFailedException; import org.cryptomator.frontend.fuse.mount.EnvironmentVariables; +import org.cryptomator.frontend.fuse.mount.FuseMountException; import org.cryptomator.frontend.fuse.mount.FuseMountFactory; import org.cryptomator.frontend.fuse.mount.Mount; import org.cryptomator.frontend.fuse.mount.Mounter; @@ -40,28 +40,32 @@ public boolean mount() { try { Mounter mounter = FuseMountFactory.getMounter(); - EnvironmentVariables envVars ; - + String[] parsedMountFlags; if (mountFlags != null) { - ArrayList defaultMountFlags = new ArrayList(Arrays.asList(mounter.defaultMountFlags())); + ArrayList defaultMountFlags = new ArrayList(Arrays.asList(mounter.defaultMountFlags())); for (String it : mountFlags.split(",")) { - String m = it.replace(' ','='); - m = m.replace(encodeCharacterToString('='), "="); - defaultMountFlags.add("-o"+m); + defaultMountFlags.add( + "-o"+it + .replace(' ','=') + .replace(encodeCharacterToString('='), "=") + ); } - String[] newMountFlags = defaultMountFlags.toArray(new String[defaultMountFlags.size()]); - envVars = EnvironmentVariables.create().withFlags(newMountFlags) - .withMountPoint(mountPoint).build(); + parsedMountFlags = defaultMountFlags.toArray(new String[defaultMountFlags.size()]); }else{ - envVars = EnvironmentVariables.create().withFlags(mounter.defaultMountFlags()) - .withMountPoint(mountPoint).build(); + parsedMountFlags = mounter.defaultMountFlags(); } + EnvironmentVariables envVars = EnvironmentVariables.create() // + .withFlags(parsedMountFlags) // + .withFileNameTranscoder(mounter.defaultFileNameTranscoder()) // + .withMountPoint(mountPoint).build(); + + mnt = mounter.mount(vaultRoot, envVars); LOG.info("Mounted to {}", mountPoint); LOG.info("Run fusermount -u \"{}\" to unmount", mountPoint); - } catch (CommandFailedException e) { + } catch (FuseMountException e) { LOG.error("Can't mount: {}, error: {}", mountPoint, e.getMessage()); return false; } @@ -72,7 +76,7 @@ public void unmount() { try { mnt.unmount(); LOG.info("Unmounted {}", mountPoint); - } catch (CommandFailedException e) { + } catch (FuseMountException e) { LOG.error("Can't unmount gracefully: {}. Force unmount.", e.getMessage()); forceUnmount(); } @@ -82,7 +86,7 @@ private void forceUnmount() { try { mnt.unmountForced(); LOG.info("Unmounted {}", mountPoint); - } catch (CommandFailedException e) { + } catch (FuseMountException e) { LOG.error("Force unmount failed: {}", e.getMessage()); } }