Skip to content

Commit 3095517

Browse files
authored
[macOS sandbox mode] Application group name mangling for semaphores (#1167)
When macOS Sandbox mode is enabled, macOS requires that semaphores have a name that is prefixed by the App's Group Name. If the semaphore's name doesn't match this convention then its creation fails. Unfortunately there's no official way for the SDK to query the app's group name at runtime, so we can't automatically mangle the semaphore names. Instead I've updated the SDK to use an Info.plist property named FBAppGroupEntitlementName on macOS. If that property is present then the SDK will use it's value to prefix the semaphore names. As an additional issue, the SDK attempted to detect semaphore creation errors by comparing the semaphore handle to nil. But in the case of macOS, a semaphore creation error returns SEM_FAILED which is 0xFFFFFFFFFFFFFFFF, not nil. And on Linux, sem_init returns -1. I've updated the corresponding platform implementations to detect the correct values for these errors.
1 parent c380832 commit 3095517

File tree

6 files changed

+120
-9
lines changed

6 files changed

+120
-9
lines changed

app/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ set(app_android_SRCS
157157
src/uuid.cc)
158158
set(app_ios_SRCS
159159
src/app_ios.mm
160+
src/util_apple.mm
160161
src/util_ios.mm
161162
src/invites/ios/invites_receiver_internal_ios.mm
162163
src/invites/ios/invites_ios_startup.mm
@@ -220,7 +221,8 @@ else()
220221
src/secure/user_secure_darwin_internal.mm
221222
src/filesystem_apple.mm
222223
src/locale_apple.mm
223-
src/uuid_ios_darwin.mm)
224+
src/uuid_ios_darwin.mm
225+
src/util_apple.mm)
224226
else()
225227
# Linux requires libsecret.
226228
pkg_check_modules(LIBSECRET libsecret-1)

app/src/app_desktop.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "app/src/include/firebase/internal/common.h"
3030
#include "app/src/include/firebase/version.h"
3131
#include "app/src/log.h"
32+
#include "app/src/semaphore.h"
3233
#include "app/src/util.h"
3334

3435
namespace firebase {
@@ -131,6 +132,9 @@ App* App::Create(const AppOptions& options, const char* name) { // NOLINT
131132
return app;
132133
}
133134
LogDebug("Creating Firebase App %s for %s", name, kFirebaseVersionString);
135+
LogDebug("Validating semaphore creation.");
136+
{ firebase::Semaphore sem_test(0); }
137+
134138
AppOptions options_with_defaults = options;
135139
if (options_with_defaults.PopulateRequiredWithDefaults()) {
136140
app = new App();

app/src/semaphore.h

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include <errno.h>
2121

22+
#include "app/src/assert.h"
2223
#include "app/src/include/firebase/internal/platform.h"
2324
#include "app/src/time.h"
2425

@@ -39,6 +40,9 @@
3940
#if FIREBASE_PLATFORM_OSX || FIREBASE_PLATFORM_IOS || FIREBASE_PLATFORM_TVOS
4041
#include "app/src/include/firebase/internal/mutex.h"
4142
#include "app/src/pthread_condvar.h"
43+
#include "app/src/util_apple.h"
44+
45+
#define FIREBASE_SEMAPHORE_DEFAULT_PREFIX "/firebase-"
4246
#endif // FIREBASE_PLATFORM_OSX || FIREBASE_PLATFORM_IOS ||
4347
// FIREBASE_PLATFORM_TVOS
4448

@@ -50,29 +54,51 @@ class Semaphore {
5054
#if FIREBASE_PLATFORM_OSX || FIREBASE_PLATFORM_IOS || FIREBASE_PLATFORM_TVOS
5155
// MacOS requires named semaphores, and does not support unnamed.
5256
// Generate a unique string for the semaphore name:
53-
static const char kPrefix[] = "/firebase-";
54-
// String length of the name prefix.
55-
static const int kPprefixLen = sizeof(kPrefix);
57+
5658
// String length of the pointer, when printed to a string.
5759
static const int kPointerStringLength = 16;
5860
// Buffer size. the +1 is for the null terminator.
59-
static const int kBufferSize = kPprefixLen + kPointerStringLength + 1;
61+
static const int kBufferSize = kPointerStringLength + 1;
6062

6163
char buffer[kBufferSize];
62-
snprintf(buffer, kBufferSize, "%s%016llx", kPrefix,
64+
snprintf(buffer, kBufferSize, "%016llx",
6365
static_cast<unsigned long long>( // NOLINT
6466
reinterpret_cast<intptr_t>(this)));
67+
#if FIREBASE_PLATFORM_OSX
68+
// Append custom semaphore names, if present, to support macOS Sandbox
69+
// mode.
70+
std::string semaphore_name = util::GetSandboxModeSemaphorePrefix();
71+
if (semaphore_name.empty()) {
72+
semaphore_name = FIREBASE_SEMAPHORE_DEFAULT_PREFIX;
73+
}
74+
#else
75+
std::string semaphore_name = FIREBASE_SEMAPHORE_DEFAULT_PREFIX;
76+
#endif // FIREBASE_PLATFORM_OSX
6577

66-
semaphore_ = sem_open(buffer,
78+
semaphore_name.append(buffer);
79+
semaphore_ = sem_open(semaphore_name.c_str(),
6780
O_CREAT | O_EXCL, // Create if it doesn't exist.
6881
S_IRUSR | S_IWUSR, // Only the owner can read/write.
6982
initial_count);
83+
84+
// Check for errors.
85+
#if FIREBASE_PLATFORM_OSX
86+
FIREBASE_ASSERT_MESSAGE(
87+
semaphore_ != SEM_FAILED,
88+
"Firebase failed to create semaphore. If running in sandbox mode be "
89+
"sure to configure FBAppGroupEntitlementName in your app's "
90+
"Info.plist.");
91+
#endif // FIREBASE_PLATFORM_OSX
92+
93+
assert(semaphore_ != SEM_FAILED);
94+
assert(semaphore_ != nullptr);
95+
7096
// Remove the semaphore from the system registry, so other processes can't
7197
// grab it. (These are designed to just be passed around internally by
7298
// pointer, like unnamed semaphores. Mac OSX targets don't support unnamed
7399
// semaphores though, so we have to use named, and just treat them like
74100
// unnamed.
75-
bool success = (sem_unlink(buffer) == 0);
101+
bool success = (sem_unlink(semaphore_name.c_str()) == 0);
76102
assert(success);
77103
(void)success;
78104
#elif !FIREBASE_PLATFORM_WINDOWS
@@ -81,13 +107,14 @@ class Semaphore {
81107
bool success = sem_init(semaphore_, 0, initial_count) == 0;
82108
assert(success);
83109
(void)success;
110+
assert(semaphore_ != nullptr);
84111
#else
85112
semaphore_ = CreateSemaphore(nullptr, // default security attributes
86113
initial_count, // initial count
87114
LONG_MAX, // maximum count
88115
nullptr); // unnamed semaphore
89-
#endif
90116
assert(semaphore_ != nullptr);
117+
#endif
91118
}
92119

93120
~Semaphore() {

app/src/util_apple.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef FIREBASE_APP_SRC_UTIL_APPLE_H_
18+
#define FIREBASE_APP_SRC_UTIL_APPLE_H_
19+
20+
#include <string>
21+
22+
namespace firebase {
23+
namespace util {
24+
25+
// Attempts to query the custom semaphore prefix from the application's
26+
// Info.plist file. Returns an empty string if a custom semahpore prefix
27+
// wasn't conifgured.
28+
std::string GetSandboxModeSemaphorePrefix();
29+
30+
} // namespace util
31+
} // namespace firebase
32+
33+
#endif // FIREBASE_APP_SRC_UTIL_APPLE_H_

app/src/util_apple.mm

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "app/src/util_apple.h"
18+
19+
#import <Foundation/Foundation.h>
20+
21+
namespace firebase {
22+
namespace util {
23+
24+
std::string GetSandboxModeSemaphorePrefix() {
25+
NSBundle* mainBundle = [NSBundle mainBundle];
26+
if (mainBundle != nil) {
27+
NSDictionary<NSString*, id>* dictionary = [mainBundle infoDictionary];
28+
if (dictionary != nil) {
29+
NSString* customPrefix = [dictionary valueForKey:@"FBAppGroupEntitlementName"];
30+
if (customPrefix != nil) {
31+
return std::string(customPrefix.UTF8String).append("/");
32+
}
33+
}
34+
}
35+
return std::string();
36+
}
37+
38+
} // namespace util
39+
} // namespace firebase

release_build_files/readme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,12 @@ code.
645645
### Upcoming Release
646646
- Changes
647647
- Analytics: Add `analytics::SetConsent()` API.
648+
- General (macOS): In order to support sandbox mode, apps can define a
649+
key/value pair for FBAppGroupEntitlementName in Info.plist. The value
650+
associated with this key will be used to prefix semaphore names
651+
created internally by the Firebase C++ SDK so that they conform with
652+
[macOS sandbox
653+
requirements](https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24).
648654

649655
### 10.3.0
650656
- Changes

0 commit comments

Comments
 (0)