diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index a12d42b2c6..063323e707 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -157,6 +157,7 @@ set(app_android_SRCS src/uuid.cc) set(app_ios_SRCS src/app_ios.mm + src/util_apple.mm src/util_ios.mm src/invites/ios/invites_receiver_internal_ios.mm src/invites/ios/invites_ios_startup.mm @@ -220,7 +221,8 @@ else() src/secure/user_secure_darwin_internal.mm src/filesystem_apple.mm src/locale_apple.mm - src/uuid_ios_darwin.mm) + src/uuid_ios_darwin.mm + src/util_apple.mm) else() # Linux requires libsecret. pkg_check_modules(LIBSECRET libsecret-1) diff --git a/app/src/app_desktop.cc b/app/src/app_desktop.cc index ad0de8ed92..0276c163c9 100644 --- a/app/src/app_desktop.cc +++ b/app/src/app_desktop.cc @@ -29,6 +29,7 @@ #include "app/src/include/firebase/internal/common.h" #include "app/src/include/firebase/version.h" #include "app/src/log.h" +#include "app/src/semaphore.h" #include "app/src/util.h" namespace firebase { @@ -131,6 +132,9 @@ App* App::Create(const AppOptions& options, const char* name) { // NOLINT return app; } LogDebug("Creating Firebase App %s for %s", name, kFirebaseVersionString); + LogDebug("Validating semaphore creation."); + { firebase::Semaphore sem_test(0); } + AppOptions options_with_defaults = options; if (options_with_defaults.PopulateRequiredWithDefaults()) { app = new App(); diff --git a/app/src/semaphore.h b/app/src/semaphore.h index 316373d8a0..83a28468a0 100644 --- a/app/src/semaphore.h +++ b/app/src/semaphore.h @@ -19,6 +19,7 @@ #include +#include "app/src/assert.h" #include "app/src/include/firebase/internal/platform.h" #include "app/src/time.h" @@ -39,6 +40,9 @@ #if FIREBASE_PLATFORM_OSX || FIREBASE_PLATFORM_IOS || FIREBASE_PLATFORM_TVOS #include "app/src/include/firebase/internal/mutex.h" #include "app/src/pthread_condvar.h" +#include "app/src/util_apple.h" + +#define FIREBASE_SEMAPHORE_DEFAULT_PREFIX "/firebase-" #endif // FIREBASE_PLATFORM_OSX || FIREBASE_PLATFORM_IOS || // FIREBASE_PLATFORM_TVOS @@ -50,29 +54,51 @@ class Semaphore { #if FIREBASE_PLATFORM_OSX || FIREBASE_PLATFORM_IOS || FIREBASE_PLATFORM_TVOS // MacOS requires named semaphores, and does not support unnamed. // Generate a unique string for the semaphore name: - static const char kPrefix[] = "/firebase-"; - // String length of the name prefix. - static const int kPprefixLen = sizeof(kPrefix); + // String length of the pointer, when printed to a string. static const int kPointerStringLength = 16; // Buffer size. the +1 is for the null terminator. - static const int kBufferSize = kPprefixLen + kPointerStringLength + 1; + static const int kBufferSize = kPointerStringLength + 1; char buffer[kBufferSize]; - snprintf(buffer, kBufferSize, "%s%016llx", kPrefix, + snprintf(buffer, kBufferSize, "%016llx", static_cast( // NOLINT reinterpret_cast(this))); +#if FIREBASE_PLATFORM_OSX + // Append custom semaphore names, if present, to support macOS Sandbox + // mode. + std::string semaphore_name = util::GetSandboxModeSemaphorePrefix(); + if (semaphore_name.empty()) { + semaphore_name = FIREBASE_SEMAPHORE_DEFAULT_PREFIX; + } +#else + std::string semaphore_name = FIREBASE_SEMAPHORE_DEFAULT_PREFIX; +#endif // FIREBASE_PLATFORM_OSX - semaphore_ = sem_open(buffer, + semaphore_name.append(buffer); + semaphore_ = sem_open(semaphore_name.c_str(), O_CREAT | O_EXCL, // Create if it doesn't exist. S_IRUSR | S_IWUSR, // Only the owner can read/write. initial_count); + + // Check for errors. +#if FIREBASE_PLATFORM_OSX + FIREBASE_ASSERT_MESSAGE( + semaphore_ != SEM_FAILED, + "Firebase failed to create semaphore. If running in sandbox mode be " + "sure to configure FBAppGroupEntitlementName in your app's " + "Info.plist."); +#endif // FIREBASE_PLATFORM_OSX + + assert(semaphore_ != SEM_FAILED); + assert(semaphore_ != nullptr); + // Remove the semaphore from the system registry, so other processes can't // grab it. (These are designed to just be passed around internally by // pointer, like unnamed semaphores. Mac OSX targets don't support unnamed // semaphores though, so we have to use named, and just treat them like // unnamed. - bool success = (sem_unlink(buffer) == 0); + bool success = (sem_unlink(semaphore_name.c_str()) == 0); assert(success); (void)success; #elif !FIREBASE_PLATFORM_WINDOWS @@ -81,13 +107,14 @@ class Semaphore { bool success = sem_init(semaphore_, 0, initial_count) == 0; assert(success); (void)success; + assert(semaphore_ != nullptr); #else semaphore_ = CreateSemaphore(nullptr, // default security attributes initial_count, // initial count LONG_MAX, // maximum count nullptr); // unnamed semaphore -#endif assert(semaphore_ != nullptr); +#endif } ~Semaphore() { diff --git a/app/src/util_apple.h b/app/src/util_apple.h new file mode 100644 index 0000000000..2485c3621d --- /dev/null +++ b/app/src/util_apple.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed 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. + */ + +#ifndef FIREBASE_APP_SRC_UTIL_APPLE_H_ +#define FIREBASE_APP_SRC_UTIL_APPLE_H_ + +#include + +namespace firebase { +namespace util { + +// Attempts to query the custom semaphore prefix from the application's +// Info.plist file. Returns an empty string if a custom semahpore prefix +// wasn't conifgured. +std::string GetSandboxModeSemaphorePrefix(); + +} // namespace util +} // namespace firebase + +#endif // FIREBASE_APP_SRC_UTIL_APPLE_H_ diff --git a/app/src/util_apple.mm b/app/src/util_apple.mm new file mode 100644 index 0000000000..0d226e43f6 --- /dev/null +++ b/app/src/util_apple.mm @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed 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. + */ + +#include "app/src/util_apple.h" + +#import + +namespace firebase { +namespace util { + +std::string GetSandboxModeSemaphorePrefix() { + NSBundle* mainBundle = [NSBundle mainBundle]; + if (mainBundle != nil) { + NSDictionary* dictionary = [mainBundle infoDictionary]; + if (dictionary != nil) { + NSString* customPrefix = [dictionary valueForKey:@"FBAppGroupEntitlementName"]; + if (customPrefix != nil) { + return std::string(customPrefix.UTF8String).append("/"); + } + } + } + return std::string(); +} + +} // namespace util +} // namespace firebase diff --git a/release_build_files/readme.md b/release_build_files/readme.md index 73ffad6a0b..d4ed89a831 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -645,6 +645,12 @@ code. ### Upcoming Release - Changes - Analytics: Add `analytics::SetConsent()` API. + - General (macOS): In order to support sandbox mode, apps can define a + key/value pair for FBAppGroupEntitlementName in Info.plist. The value + associated with this key will be used to prefix semaphore names + created internally by the Firebase C++ SDK so that they conform with + [macOS sandbox + requirements](https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24). ### 10.3.0 - Changes