Skip to content

Commit 8b1dcc2

Browse files
authored
Merge pull request #1438: [Mac kext] Allow privileged system service 'amfid' to hydrate
[releases/shipped] Allow privileged system service 'amfid' to hydrate
2 parents c0d705d + ae2b1c2 commit 8b1dcc2

8 files changed

Lines changed: 57 additions & 28 deletions

File tree

ProjFS.Mac/PrjFSKext/KauthHandler.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static bool TryReadVNodeFileFlags(vnode_t vn, vfs_context_t _Nonnull context, ui
6969
KEXT_STATIC_INLINE bool FileFlagsBitIsSet(uint32_t fileFlags, uint32_t bit);
7070
KEXT_STATIC_INLINE bool TryGetFileIsFlaggedAsInRoot(vnode_t vnode, vfs_context_t _Nonnull context, bool* flaggedInRoot);
7171
KEXT_STATIC_INLINE bool ActionBitIsSet(kauth_action_t action, kauth_action_t mask);
72-
KEXT_STATIC bool CurrentProcessWasSpawnedByRegularUser();
72+
KEXT_STATIC bool CurrentProcessIsAllowedToHydrate();
7373
KEXT_STATIC bool IsFileSystemCrawler(const char* procname);
7474

7575
static void WaitForListenerCompletion();
@@ -1144,10 +1144,10 @@ static bool TryGetVirtualizationRoot(
11441144
return false;
11451145
}
11461146

1147-
if (callbackPolicy == CallbackPolicy_UserInitiatedOnly && !CurrentProcessWasSpawnedByRegularUser())
1147+
if (callbackPolicy == CallbackPolicy_UserInitiatedOnly && !CurrentProcessIsAllowedToHydrate())
11481148
{
11491149
// Prevent hydration etc. by system services
1150-
KextLog_Info("TryGetVirtualizationRoot: process %d restricted due to owner UID.", pidMakingRequest);
1150+
KextLog_Info("TryGetVirtualizationRoot: process %d is not allowed to hydrate.", pidMakingRequest);
11511151
perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_UserRestriction);
11521152

11531153
*kauthResult = KAUTH_RESULT_DENY;
@@ -1159,7 +1159,7 @@ static bool TryGetVirtualizationRoot(
11591159
return true;
11601160
}
11611161

1162-
KEXT_STATIC bool CurrentProcessWasSpawnedByRegularUser()
1162+
KEXT_STATIC bool CurrentProcessIsAllowedToHydrate()
11631163
{
11641164
bool nonServiceUser = false;
11651165

@@ -1188,7 +1188,7 @@ KEXT_STATIC bool CurrentProcessWasSpawnedByRegularUser()
11881188
process = parentProcess;
11891189
if (parentProcess == nullptr)
11901190
{
1191-
KextLog_Error("CurrentProcessIsOwnedOrWasSpawnedByRegularUser: Failed to locate ancestor process %d for current process %d\n", parentPID, proc_selfpid());
1191+
KextLog_Error("CurrentProcessIsAllowedToHydrate: Failed to locate ancestor process %d for current process %d\n", parentPID, proc_selfpid());
11921192
break;
11931193
}
11941194
}
@@ -1199,6 +1199,19 @@ KEXT_STATIC bool CurrentProcessWasSpawnedByRegularUser()
11991199
proc_rele(process);
12001200
}
12011201

1202+
if (!nonServiceUser)
1203+
{
1204+
// When amfid is invoked to check the code signature of a library which has not been hydrated,
1205+
// blocking hydration would cause the launch of an application which depends on the library to fail,
1206+
// so amfid should always be allowed to hydrate.
1207+
char buffer[MAXCOMLEN + 1] = "";
1208+
proc_selfname(buffer, sizeof(buffer));
1209+
if (0 == strcmp(buffer, "amfid"))
1210+
{
1211+
nonServiceUser = true;
1212+
}
1213+
}
1214+
12021215
return nonServiceUser;
12031216
}
12041217

ProjFS.Mac/PrjFSKext/KauthHandlerTestable.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ KEXT_STATIC bool ShouldHandleFileOpEvent(
6767
VirtualizationRootHandle* root,
6868
int* pid);
6969
KEXT_STATIC void UseMainForkIfNamedStream(vnode_t& vnode, bool& putVnodeWhenDone);
70-
KEXT_STATIC bool CurrentProcessWasSpawnedByRegularUser();
70+
KEXT_STATIC bool CurrentProcessIsAllowedToHydrate();
7171
KEXT_STATIC bool InitPendingRenames();
7272
KEXT_STATIC void CleanupPendingRenames();
7373
KEXT_STATIC void ResizePendingRenames(uint32_t newMaxPendingRenames);

ProjFS.Mac/PrjFSKextTests/HandleFileOpOperationTests.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ - (void) setUp
9595
self->otherRepoHandle = result.root;
9696

9797
MockProcess_AddContext(context, 501 /*pid*/);
98-
MockProcess_SetSelfPid(501);
98+
MockProcess_SetSelfInfo(501, "Test");
9999
MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/);
100100

101101
ProvidermessageMock_ResetResultCount();
@@ -402,7 +402,7 @@ - (void)testFileopHardlinkOtherRepoProviderPID
402402
{
403403
MockProcess_Reset();
404404
MockProcess_AddContext(context, self->dummyClientPid /*pid*/);
405-
MockProcess_SetSelfPid(self->dummyClientPid);
405+
MockProcess_SetSelfInfo(self->dummyClientPid, "Test");
406406
MockProcess_AddProcess(self->dummyClientPid /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "GVFS.Mount" /*name*/);
407407

408408
testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot;
@@ -432,7 +432,7 @@ - (void)testFileopHardlinkOtherRepoOtherProviderPID
432432
{
433433
MockProcess_Reset();
434434
MockProcess_AddContext(context, self->otherDummyClientPid /*pid*/);
435-
MockProcess_SetSelfPid(self->otherDummyClientPid);
435+
MockProcess_SetSelfInfo(self->otherDummyClientPid, "Test");
436436
MockProcess_AddProcess(self->otherDummyClientPid /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "GVFS.Mount" /*name*/);
437437

438438
testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot;

ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ - (void) setUp
100100
self->dummyRepoHandle = result.root;
101101

102102
MockProcess_AddContext(context, 501 /*pid*/);
103-
MockProcess_SetSelfPid(501);
103+
MockProcess_SetSelfInfo(501, "Test");
104104
MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/);
105105

106106
ProvidermessageMock_ResetResultCount();

ProjFS.Mac/PrjFSKextTests/KauthHandlerTests.mm

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ - (void) setUp {
2929
self->cacheWrapper.AllocateCache();
3030
context = vfs_context_create(nullptr);
3131
MockProcess_AddContext(context, 501 /*pid*/);
32-
MockProcess_SetSelfPid(501);
32+
MockProcess_SetSelfInfo(501, "Test");
3333
MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/);
3434
}
3535

@@ -247,7 +247,7 @@ - (void)testShouldHandleVnodeOpEvent {
247247
// Test with file crawler trying to populate an empty file
248248
testVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot;
249249
MockProcess_Reset();
250-
MockProcess_SetSelfPid(501);
250+
MockProcess_SetSelfInfo(501, "Test");
251251
MockProcess_AddContext(context, 501 /*pid*/);
252252
MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "mds" /*name*/);
253253
XCTAssertFalse(
@@ -265,7 +265,7 @@ - (void)testShouldHandleVnodeOpEvent {
265265

266266
// Test with finder trying to populate an empty file
267267
MockProcess_Reset();
268-
MockProcess_SetSelfPid(501);
268+
MockProcess_SetSelfInfo(501, "Test");
269269
MockProcess_AddContext(context, 501 /*pid*/);
270270
MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "Finder" /*name*/);
271271
XCTAssertTrue(
@@ -282,47 +282,55 @@ - (void)testShouldHandleVnodeOpEvent {
282282
XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER);
283283
}
284284

285-
- (void)testCurrentProcessWasSpawnedByRegularUser {
285+
- (void)testCurrentProcessIsAllowedToHydrate {
286286
// Defaults should pass for all tests
287-
XCTAssertTrue(CurrentProcessWasSpawnedByRegularUser());
287+
XCTAssertTrue(CurrentProcessIsAllowedToHydrate());
288288
MockProcess_Reset();
289289

290-
// Process is a service user and does not have a parent
290+
// Process and its parents are owned by a system user, and the executable is not a whitelisted service.
291291
MockProcess_AddContext(context, 500 /*pid*/);
292-
MockProcess_SetSelfPid(500);
292+
MockProcess_SetSelfInfo(500, "Test");
293293
MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/);
294294
MockProcess_AddProcess(500 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/);
295-
XCTAssertFalse(CurrentProcessWasSpawnedByRegularUser());
295+
XCTAssertFalse(CurrentProcessIsAllowedToHydrate());
296+
MockProcess_Reset();
297+
298+
// The service "amfid" and its parents are owned by system users, but the process name is whitelisted for hydration.
299+
MockProcess_AddContext(context, 500 /*pid*/);
300+
MockProcess_SetSelfInfo(500, "amfid");
301+
MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/);
302+
MockProcess_AddProcess(500 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/);
303+
XCTAssertTrue(CurrentProcessIsAllowedToHydrate());
296304
MockProcess_Reset();
297305

298306
// Test a process with a service UID, valid parent pid, but proc_find fails to find parent pid
299307
MockCalls::Clear();
300308
MockProcess_AddContext(context, 500 /*pid*/);
301-
MockProcess_SetSelfPid(500);
309+
MockProcess_SetSelfInfo(500, "Test");
302310
MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/);
303311
MockProcess_AddProcess(500 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/);
304-
XCTAssertFalse(CurrentProcessWasSpawnedByRegularUser());
312+
XCTAssertFalse(CurrentProcessIsAllowedToHydrate());
305313
XCTAssertTrue(MockCalls::DidCallFunction(KextMessageLogged, KEXTLOG_ERROR));
306314
MockProcess_Reset();
307315

308316
// 'sudo' scenario: Root process with non-root parent
309317
MockProcess_AddContext(context, 502 /*pid*/);
310-
MockProcess_SetSelfPid(502);
318+
MockProcess_SetSelfInfo(502, "Test");
311319
MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/);
312320
MockProcess_AddCredential(2 /*credentialId*/, 501 /*UID*/);
313321
MockProcess_AddProcess(502 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/);
314322
MockProcess_AddProcess(501 /*pid*/, 2 /*credentialId*/, 1 /*ppid*/, "test" /*name*/);
315-
XCTAssertTrue(CurrentProcessWasSpawnedByRegularUser());
323+
XCTAssertTrue(CurrentProcessIsAllowedToHydrate());
316324
MockProcess_Reset();
317325

318326
// Process and it's parent are service users
319327
MockProcess_AddContext(context, 502 /*pid*/);
320-
MockProcess_SetSelfPid(502);
328+
MockProcess_SetSelfInfo(502, "Test");
321329
MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/);
322330
MockProcess_AddCredential(2 /*credentialId*/, 2 /*UID*/);
323331
MockProcess_AddProcess(502 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/);
324332
MockProcess_AddProcess(501 /*pid*/, 2 /*credentialId*/, 1 /*ppid*/, "test" /*name*/);
325-
XCTAssertFalse(CurrentProcessWasSpawnedByRegularUser());
333+
XCTAssertFalse(CurrentProcessIsAllowedToHydrate());
326334
}
327335

328336
- (void)testUseMainForkIfNamedStream {

ProjFS.Mac/PrjFSKextTests/MockProc.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ static map<uintptr_t /*credential ID*/, int /*UID*/> s_credentialMap;
1414
static map<vfs_context_t /*context*/, int /*pid*/> s_contextMap;
1515
static map<int /*process Id*/, proc> s_processMap;
1616
static int s_selfPid;
17+
static string s_selfName;
1718
static uint16_t s_currentThreadIndex = 0;
1819
static thread s_threadPool[MockProcess_ThreadPoolSize] = {};
1920

@@ -25,9 +26,10 @@ void MockProcess_Reset()
2526
MockProcess_SetCurrentThreadIndex(0);
2627
}
2728

28-
void MockProcess_SetSelfPid(int selfPid)
29+
void MockProcess_SetSelfInfo(int selfPid, const string& selfName)
2930
{
3031
s_selfPid = selfPid;
32+
s_selfName = selfName;
3133
}
3234

3335
int proc_pid(proc_t proc)
@@ -149,6 +151,11 @@ int proc_selfpid(void)
149151
return s_selfPid;
150152
}
151153

154+
void proc_selfname(char* buf, int size)
155+
{
156+
strlcpy(buf, s_selfName.c_str(), size);
157+
}
158+
152159
void MockProcess_AddCredential(uintptr_t credentialId, uid_t UID)
153160
{
154161
s_credentialMap.insert(make_pair(credentialId, UID));

ProjFS.Mac/PrjFSKextTests/MockProc.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extern "C"
2121
int proc_rele(proc_t p);
2222
int proc_selfpid(void);
2323
kernel_thread_t current_thread(void);
24+
void proc_selfname(char* buf, int size);
2425
}
2526

2627
struct proc {
@@ -30,7 +31,7 @@ struct proc {
3031
std::string name;
3132
};
3233

33-
void MockProcess_SetSelfPid(int selfPid);
34+
void MockProcess_SetSelfInfo(int selfPid, const std::string& selfName);
3435
void MockProcess_AddCredential(uintptr_t credentialId, uid_t UID);
3536
void MockProcess_AddContext(vfs_context_t context, int pid);
3637
void MockProcess_AddProcess(int pid, uintptr_t credentialId, int ppid, std::string procName);

ProjFS.Mac/PrjFSKextTests/ShouldHandleFileOpTests.mm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ - (void) setUp {
3636
self->cacheWrapper.AllocateCache();
3737
self->context = vfs_context_create(nullptr);
3838
MockProcess_AddContext(self->context, 501 /*pid*/);
39-
MockProcess_SetSelfPid(501);
39+
MockProcess_SetSelfInfo(501, "Test");
4040
MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/);
4141

4242
self->testMount = mount::Create();
@@ -191,7 +191,7 @@ - (void)testProviderInitiatedIO {
191191
// Fail when pid matches provider pid
192192
MockProcess_Reset();
193193
MockProcess_AddContext(self->context, 0 /*pid*/);
194-
MockProcess_SetSelfPid(0);
194+
MockProcess_SetSelfInfo(0, "Test");
195195
MockProcess_AddProcess(0 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/);
196196
int pid;
197197
XCTAssertFalse(

0 commit comments

Comments
 (0)