Skip to content

Commit

Permalink
Merge pull request #16230 from iterate-ch/bugfix/GH-16031
Browse files Browse the repository at this point in the history
Use IOKit for receipt validation. Drop usage of exit(173) no longer supported.
  • Loading branch information
dkocher authored Sep 9, 2024
2 parents 8e4c1fe + 0ca43e7 commit 3749300
Show file tree
Hide file tree
Showing 22 changed files with 414 additions and 199 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ public interface _Class extends ObjCClass {
*/
public abstract boolean loadAndReturnError(ObjCObjectByReference error1);

/**
* Use this app bundle property to locate the app receipt if it's present; this property is nil if the receipt isn’t present.
*
* @return The file URL for the bundle’s App Store receipt.
*/
public abstract NSURL appStoreReceiptURL();

/**
* Original signature : <code>String* bundlePath)</code><br>
* <i>native declaration : :45</i>
Expand Down
40 changes: 18 additions & 22 deletions cli/src/main/java/ch/cyberduck/cli/TerminalHelpPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public static void print(final Options options, final HelpFormatter formatter) {
formatter.setSyntaxPrefix("Usage:");
final Preferences preferences = PreferencesFactory.get();
final StringBuilder builder = new StringBuilder()
.append("Default protocols")
.append(StringUtils.LF);
.append("Default protocols")
.append(StringUtils.LF);

final ProtocolFactory protocols = ProtocolFactory.get();
for(Protocol p : protocols.find(new DefaultProtocolPredicate(EnumSet.of(Protocol.Type.ftp, Protocol.Type.sftp, Protocol.Type.dav, Protocol.Type.smb)))) {
Expand All @@ -75,32 +75,28 @@ public static void print(final Options options, final HelpFormatter formatter) {
}
builder.append(StringUtils.LF);
builder.append(String.format("Third party connection profiles. Install additional connection profiles in %s",
LocalFactory.get(SupportDirectoryFinderFactory.get().find(),
PreferencesFactory.get().getProperty("profiles.folder.name")).getAbbreviatedPath()));
LocalFactory.get(SupportDirectoryFinderFactory.get().find(),
PreferencesFactory.get().getProperty("profiles.folder.name")).getAbbreviatedPath()));
builder.append(StringUtils.LF);
for(Protocol p : protocols.find(new ProfileProtocolPredicate())) {
append(p, builder);
}
final StringBuilder header = new StringBuilder(StringUtils.LF)
.append("\t")
.append("URLs must be fully qualified. Paths can either denote "
+ "a remote file (ftps://[email protected]/resource) or folder (ftps://[email protected]/directory/) "
+ "with a trailing slash. You can reference files relative to your home directory with /~ (ftps://[email protected]/~/).")
.append(StringUtils.LF)
.append(builder)
.append(StringUtils.LF)
.append(StringUtils.LF);
.append("\t")
.append("URLs must be fully qualified. Paths can either denote "
+ "a remote file (ftps://[email protected]/resource) or folder (ftps://[email protected]/directory/) "
+ "with a trailing slash. You can reference files relative to your home directory with /~ (ftps://[email protected]/~/).")
.append(StringUtils.LF)
.append(builder)
.append(StringUtils.LF)
.append(StringUtils.LF);
final StringBuilder footer = new StringBuilder(StringUtils.LF);
footer.append(String.format("Cyberduck is libre software licenced under the GPL. For general help about using Cyberduck, please refer to %s and the wiki at %s. For bug reports or feature requests open a ticket at %s.",
preferences.getProperty("website.cli"), preferences.getProperty("website.help"), MessageFormat.format(preferences.getProperty("website.bug"), preferences.getProperty("application.version"))));
preferences.getProperty("website.cli"), preferences.getProperty("website.help"), MessageFormat.format(preferences.getProperty("website.bug"), preferences.getProperty("application.version"))));
final License l = LicenseFactory.find();
footer.append(StringUtils.LF);
if(l.verify(new DisabledLicenseVerifierCallback())) {
footer.append(l.toString());
}
else {
footer.append("Not registered. Purchase a donation key to support the development of this software.");
}
footer.append(l.getName());
footer.append(l);
formatter.printHelp("duck [options...]", header.toString(), options, footer.toString());
}

Expand Down Expand Up @@ -132,14 +128,14 @@ private static void append(final Protocol protocol, final StringBuilder builder)
url = String.format(format, getScheme(protocol));

builder
.append(String.format("%s %s", StringUtils.leftPad(protocol.getDescription(), 50), url))
.append(StringUtils.LF);
.append(String.format("%s %s", StringUtils.leftPad(protocol.getDescription(), 50), url))
.append(StringUtils.LF);
}

protected static String getScheme(final Protocol protocol) {
if(new BundledProtocolPredicate().test(protocol)) {
for(String scheme :
protocol.getSchemes()) {
protocol.getSchemes()) {
// Return first custom scheme registered
return scheme;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* GNU General Public License for more details.
*/

import ch.cyberduck.binding.foundation.NSBundle;
import ch.cyberduck.core.ApplescriptTerminalService;
import ch.cyberduck.core.Factory;
import ch.cyberduck.core.IOKitSleepPreventer;
Expand Down Expand Up @@ -46,6 +47,7 @@
import ch.cyberduck.core.proxy.SystemSettingsProxyConfiguration;
import ch.cyberduck.core.quicklook.QuartzQuickLook;
import ch.cyberduck.core.resources.NSImageIconCache;
import ch.cyberduck.core.socket.IOKitHardwareAddress;
import ch.cyberduck.core.sparkle.Sandbox;
import ch.cyberduck.core.threading.AutoreleaseActionOperationBatcher;
import ch.cyberduck.core.urlhandler.LaunchServicesSchemeHandler;
Expand Down Expand Up @@ -118,12 +120,13 @@ protected void setFactories() {
}
this.setDefault("factory.urlfilewriter.class", WeblocFileWriter.class.getName());
this.setDefault("factory.quicklook.class", QuartzQuickLook.class.getName());
this.setDefault("factory.hardwareaddress.class", IOKitHardwareAddress.class.getName());
}

@Override
protected void setDefaults() {
super.setDefaults();
if(null == this.getDefault("SUExpectsDSASignature")) {
if(null != NSBundle.mainBundle().appStoreReceiptURL()) {
this.setDefault("factory.licensefactory.class", ReceiptFactory.class.getName());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ch.cyberduck.core.socket;

/*
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.UnsupportedException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.sun.jna.platform.mac.CoreFoundation;
import com.sun.jna.platform.mac.IOKit;
import com.sun.jna.platform.mac.IOKitUtil;

public class IOKitHardwareAddress implements HardwareAddress {
private static final Logger log = LogManager.getLogger(IOKitHardwareAddress.class);

// #define kIOMACAddress "IOMACAddress"
private static final CoreFoundation.CFStringRef kIOMACAddress = CoreFoundation.CFStringRef.createCFString("IOMACAddress");
// #define kIOServicePlane "IOService"
private final String kIOServicePlane = "IOService";

private final int kIORegistryIterateRecursively = 0x00000001;
private final int kIORegistryIterateParents = 0x00000002;

@Override
public byte[] getAddress() throws BackgroundException {
final IOKit.IOService en0 = IOKitUtil.getMatchingService(IOKitUtil.getBSDNameMatchingDict("en0"));

This comment has been minimized.

Copy link
@xhruso00

xhruso00 Sep 9, 2024

This wrong assumption. iMacs during Airplay 2 session will shift en0 to en1
IdeasOnCanvas/AppReceiptValidator#83 (comment)

if(null == en0) {
// Interface is not found when link is down #fail
log.warn("No network interface en0");
throw new UnsupportedException("No network interface en0");
}
final CoreFoundation.CFTypeRef property = IOKit.INSTANCE.IORegistryEntrySearchCFProperty(en0, kIOServicePlane, kIOMACAddress,
CoreFoundation.INSTANCE.CFAllocatorGetDefault(), kIORegistryIterateRecursively | kIORegistryIterateParents);
if(null == property) {
log.error("Cannot determine MAC address");
throw new UnsupportedException("No hardware address for network interface en0");
}
final CoreFoundation.CFDataRef dataRef = new CoreFoundation.CFDataRef(property.getPointer());
return dataRef.getBytePtr().getByteArray(0, dataRef.getLength());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ch.cyberduck.core.socket;

/*
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class IOKitHardwareAddressTest {

@Test
public void getAddress() throws Exception {
final byte[] address = new IOKitHardwareAddress().getAddress();
assertNotNull(address);
assertEquals(6, address.length);
}
}
30 changes: 30 additions & 0 deletions core/dylib/src/test/java/foundation/NSBundleTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package foundation;

/*
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

import ch.cyberduck.binding.foundation.NSBundle;

import org.junit.Test;

import static org.junit.Assert.assertNull;

public class NSBundleTest {

@Test
public void testAppStoreReceiptURL() {
assertNull(NSBundle.mainBundle().appStoreReceiptURL());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ch.cyberduck.core.aquaticprime;

/*
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

public class DelegatingLicenseVerifierCallback implements LicenseVerifierCallback {

private final LicenseVerifierCallback[] proxies;

public DelegatingLicenseVerifierCallback(final LicenseVerifierCallback... proxies) {
this.proxies = proxies;
}

@Override
public void failure(final InvalidLicenseException failure) {
for(LicenseVerifierCallback proxy : proxies) {
proxy.failure(failure);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,15 @@
* [email protected]
*/

import ch.cyberduck.core.Filter;
import ch.cyberduck.core.Local;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.preferences.ApplicationResourcesFinderFactory;
import ch.cyberduck.core.preferences.SupportDirectoryFinderFactory;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

public class DonationKeyFactory extends LicenseFactory {
private static final Logger log = LogManager.getLogger(DonationKeyFactory.class);
Expand All @@ -52,26 +48,6 @@ public List<License> open() throws AccessDeniedException {
if(log.isInfoEnabled()) {
log.info("No donation key found");
}
final Pattern pattern = Pattern.compile(".*\\.cyberduckreceipt");
// No key found. Look for receipt in sandboxed application container
for(Local file : SupportDirectoryFinderFactory.get().find().list().filter(new Filter<Local>() {
@Override
public boolean accept(final Local file) {
return "cyberduckreceipt".equals(Path.getExtension(file.getName()));
}

@Override
public Pattern toPattern() {
return pattern;
}
})) {
final ReceiptVerifier verifier = new ReceiptVerifier(file);
if(verifier.verify(new DisabledLicenseVerifierCallback())) {
keys.add(new Receipt(file, verifier.getGuid()));
}
}
}
if(keys.isEmpty()) {
final Local bundle = ApplicationResourcesFinderFactory.get().find();
if(bundle.exists()) {
for(Local key : bundle.list().filter(new LicenseFilter())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ch.cyberduck.core.aquaticprime;

/*
* Copyright (c) 2002-2024 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

import ch.cyberduck.core.Factory;

public class ExitCodeLicenseVerifierCallback implements LicenseVerifierCallback {

/**
* Application has determined that its receipt is invalid. Exit with a status of 173
*/
private static final int APPSTORE_VALIDATION_FAILURE = 173;

@Override
public void failure(final InvalidLicenseException failure) {
if(Factory.Platform.osversion.matches("(10|11|12|13|14)\\..*")) {
System.exit(APPSTORE_VALIDATION_FAILURE);
}
}
}
Loading

0 comments on commit 3749300

Please sign in to comment.