Access private properties via sandbox-safe API on Apple OSes

We detect whether or not we're running inside a sandbox and bail out if
so. We use runtime lookup of the property, so that static analysis of the
application will not mistakenly think we're using the API in sandboxed
situations.

Change-Id: I5f5c42f5a4a44b62de061d945b62ac63167ece09
Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@qt.io>
This commit is contained in:
Tor Arne Vestbø 2018-06-06 16:51:00 +02:00 committed by Jani Heikkinen
parent 67227aeffd
commit a93d29198a
4 changed files with 57 additions and 24 deletions

View File

@ -120,7 +120,7 @@ mac {
LIBS_PRIVATE += -framework Foundation LIBS_PRIVATE += -framework Foundation
osx: LIBS_PRIVATE += -framework CoreServices -framework AppKit osx: LIBS_PRIVATE += -framework CoreServices -framework AppKit -framework Security
ios|tvos { ios|tvos {
# We need UIKit for UIApplication in qeventdispatcher_cf.mm # We need UIKit for UIApplication in qeventdispatcher_cf.mm

View File

@ -193,6 +193,43 @@ AppleApplication *qt_apple_sharedApplication()
} }
#endif #endif
#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
bool qt_apple_isSandboxed()
{
static bool isSandboxed = []() {
QCFType<SecStaticCodeRef> staticCode = nullptr;
NSURL *bundleUrl = [[NSBundle mainBundle] bundleURL];
if (SecStaticCodeCreateWithPath((__bridge CFURLRef)bundleUrl,
kSecCSDefaultFlags, &staticCode) != errSecSuccess)
return false;
QCFType<SecRequirementRef> sandboxRequirement;
if (SecRequirementCreateWithString(CFSTR("entitlement[\"com.apple.security.app-sandbox\"] exists"),
kSecCSDefaultFlags, &sandboxRequirement) != errSecSuccess)
return false;
if (SecStaticCodeCheckValidityWithErrors(staticCode,
kSecCSBasicValidateOnly, sandboxRequirement, nullptr) != errSecSuccess)
return false;
return true;
}();
return isSandboxed;
}
QT_END_NAMESPACE
@implementation NSObject (QtSandboxHelpers)
- (id)qt_valueForPrivateKey:(NSString *)key
{
if (qt_apple_isSandboxed())
return nil;
return [self valueForKey:key];
}
@end
QT_BEGIN_NAMESPACE
#endif
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
/* /*
Ensure that Objective-C objects auto-released in main(), directly or indirectly, Ensure that Objective-C objects auto-released in main(), directly or indirectly,

View File

@ -160,6 +160,17 @@ QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool);
Q_CORE_EXPORT void qt_apple_check_os_version(); Q_CORE_EXPORT void qt_apple_check_os_version();
Q_CORE_EXPORT bool qt_apple_isApplicationExtension(); Q_CORE_EXPORT bool qt_apple_isApplicationExtension();
#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
Q_CORE_EXPORT bool qt_apple_isSandboxed();
# ifdef __OBJC__
QT_END_NAMESPACE
@interface NSObject (QtSandboxHelpers)
- (id)qt_valueForPrivateKey:(NSString *)key;
@end
QT_BEGIN_NAMESPACE
# endif
#endif
#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS) #if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
QT_END_NAMESPACE QT_END_NAMESPACE
# if defined(Q_OS_MACOS) # if defined(Q_OS_MACOS)

View File

@ -248,24 +248,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QVerticalSplitView);
} }
@end @end
#if !QT_CONFIG(appstore_compliant)
// This API was requested to Apple in rdar #36197888.
// We know it's safe to use up to macOS 10.13.3.
// See drawComplexControl(CC_ComboBox) for its usage.
@interface NSComboBoxCell (QtButtonCell)
@property (readonly) NSButtonCell *qt_buttonCell;
@end
@implementation NSComboBoxCell (QtButtonCell)
- (NSButtonCell *)qt_buttonCell {
return self->_buttonCell;
}
@end
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// The following constants are used for adjusting the size // The following constants are used for adjusting the size
@ -5215,11 +5197,14 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
auto *cb = static_cast<NSComboBox *>(cc); auto *cb = static_cast<NSComboBox *>(cc);
const auto frameRect = cw.adjustedControlFrame(combo->rect); const auto frameRect = cw.adjustedControlFrame(combo->rect);
cb.frame = frameRect.toCGRect(); cb.frame = frameRect.toCGRect();
#if !QT_CONFIG(appstore_compliant)
static_cast<NSComboBoxCell *>(cc.cell).qt_buttonCell.highlighted = isPressed; // This API was requested to Apple in rdar #36197888. We know it's safe to use up to macOS 10.13.3
#else if (NSButtonCell *cell = static_cast<NSButtonCell *>([cc.cell qt_valueForPrivateKey:@"_buttonCell"])) {
// TODO Render to pixmap and darken the button manually cell.highlighted = isPressed;
#endif } else {
// TODO Render to pixmap and darken the button manually
}
d->drawNSViewInRect(cb, frameRect, p, ^(CGContextRef __unused ctx, const CGRect &r) { d->drawNSViewInRect(cb, frameRect, p, ^(CGContextRef __unused ctx, const CGRect &r) {
// FIXME This is usually drawn in the control's superview, but we wouldn't get inactive look in this case // FIXME This is usually drawn in the control's superview, but we wouldn't get inactive look in this case
[cb.cell drawWithFrame:r inView:cb]; [cb.cell drawWithFrame:r inView:cb];