xref: /xnu-8796.101.5/tests/persona_adoption.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 #include <darwintest.h>
2 #include <darwintest_multiprocess.h>
3 
4 #include <bank/bank_types.h>
5 #include <libproc.h>
6 #include <mach/mach.h>
7 #include <mach/mach_voucher.h>
8 #include <mach/mach_voucher_types.h>
9 #include <os/voucher_private.h>
10 #include <sys/kauth.h>
11 #include <sys/persona.h>
12 #include <sys/proc_info.h>
13 #include <unistd.h>
14 #include <uuid/uuid.h>
15 
16 T_GLOBAL_META(
17 	T_META_NAMESPACE("xnu.persona_adoption"),
18 	T_META_CHECK_LEAKS(false),
19 	T_META_RUN_CONCURRENTLY(true),
20 	T_META_ENABLED(!TARGET_OS_WATCH) // rdar://81809878
21 	);
22 
23 static uid_t
_persona_create(int persona_type,uid_t persona_uid)24 _persona_create(int persona_type, uid_t persona_uid)
25 {
26 	struct kpersona_info pinfo = {
27 		.persona_info_version = PERSONA_INFO_V2,
28 		.persona_type = persona_type,
29 		.persona_uid = persona_uid,
30 	};
31 
32 	uuid_t uuid;
33 	uuid_generate(uuid);
34 	uuid_string_t uuid_string;
35 	uuid_unparse(uuid, uuid_string);
36 	snprintf(pinfo.persona_name, MAXLOGNAME, "persona_adoption_test.%s", uuid_string);
37 
38 	uid_t persona_id = 0;
39 	int ret = kpersona_alloc(&pinfo, &persona_id);
40 	T_WITH_ERRNO; T_ASSERT_EQ(ret, 0, NULL);
41 	T_ASSERT_GT(persona_id, 0, NULL);
42 
43 	return persona_id;
44 }
45 
46 static kern_return_t
_persona_try_adopting(uid_t persona_id)47 _persona_try_adopting(uid_t persona_id)
48 {
49 	struct proc_uniqidentifierinfo uniqidinfo;
50 	int error = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 0, &uniqidinfo, sizeof(uniqidinfo));
51 	T_ASSERT_GT(error, 0, NULL);
52 
53 	struct persona_modify_info pmi = {
54 		.persona_id = persona_id,
55 		.unique_pid = uniqidinfo.p_uniqueid,
56 	};
57 
58 	mach_voucher_t current_voucher = MACH_VOUCHER_NULL;
59 	kern_return_t kr = mach_voucher_persona_self(&current_voucher);
60 	T_ASSERT_EQ(kr, 0, NULL);
61 	T_ASSERT_NE(current_voucher, MACH_VOUCHER_NULL, NULL);
62 
63 	char voucher_buf[sizeof(mach_voucher_attr_recipe_data_t) + sizeof(pmi)];
64 
65 	mach_voucher_attr_recipe_t recipe = (mach_voucher_attr_recipe_t)&voucher_buf[0];
66 	recipe->key = MACH_VOUCHER_ATTR_KEY_BANK;
67 	recipe->command = MACH_VOUCHER_ATTR_BANK_MODIFY_PERSONA;
68 	recipe->content_size = sizeof(pmi);
69 	recipe->previous_voucher = current_voucher;
70 	memcpy(recipe->content, (void *)&pmi, sizeof(pmi));
71 
72 	mach_voucher_attr_raw_recipe_size_t recipe_size = sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size;
73 	mach_voucher_attr_raw_recipe_array_t recipes = (mach_voucher_attr_raw_recipe_array_t)&voucher_buf[0];
74 	mach_voucher_t mach_voucher = MACH_VOUCHER_NULL;
75 	kr = host_create_mach_voucher(mach_host_self(), recipes, recipe_size, &mach_voucher);
76 	if (kr != 0) {
77 		return kr;
78 	}
79 	T_ASSERT_NE(mach_voucher, MACH_VOUCHER_NULL, NULL);
80 
81 	/* Verify that persona is set on the voucher */
82 	uint32_t voucher_persona;
83 	mach_voucher_attr_content_t content_out = (mach_voucher_attr_content_t)&voucher_persona;
84 	mach_voucher_attr_content_size_t content_out_size = sizeof(voucher_persona);
85 	kr = mach_voucher_attr_command(mach_voucher, MACH_VOUCHER_ATTR_KEY_BANK, BANK_PERSONA_ID, NULL, 0, content_out, &content_out_size);
86 	if (kr != 0) {
87 		return kr;
88 	}
89 	T_ASSERT_EQ(voucher_persona, persona_id, NULL);
90 
91 	kr = thread_set_mach_voucher(mach_thread_self(), mach_voucher);
92 	return kr;
93 }
94 
95 T_DECL(persona_with_matching_uid_can_be_adopted,
96     "persona with UID matching at-spawn value can be adopted")
97 {
98 	struct kpersona_info info = {
99 		.persona_info_version = PERSONA_INFO_V2,
100 	};
101 	int error = kpersona_info(0, &info);
102 	if (error != 0) {
103 		T_SKIP("Test requrires to be running in a persona, skipping");
104 	}
105 
106 	uid_t created_persona = _persona_create(PERSONA_MANAGED, info.persona_uid);
107 	kern_return_t kr = _persona_try_adopting(created_persona);
108 	T_ASSERT_EQ(kr, 0, NULL);
109 
110 	uid_t current_persona = PERSONA_ID_NONE;
111 	T_ASSERT_EQ(kpersona_get(&current_persona), 0, NULL);
112 	T_ASSERT_EQ(current_persona, created_persona, NULL);
113 
114 	T_ASSERT_EQ(kpersona_dealloc(created_persona), 0, NULL);
115 }
116 
117 T_DECL(persona_with_mismatched_uid_cannot_be_adopted,
118     "persona with UID that doesn't match at-spawn value cannot be adopted")
119 {
120 	struct kpersona_info info = {
121 		.persona_info_version = PERSONA_INFO_V2,
122 	};
123 	int error = kpersona_info(0, &info);
124 	if (error != 0) {
125 		T_SKIP("Test requrires to be running in a persona, skipping");
126 	}
127 
128 	uid_t mismatched_uid = info.persona_uid + 1;
129 	uid_t created_persona = _persona_create(PERSONA_MANAGED, mismatched_uid);
130 	kern_return_t kr = _persona_try_adopting(created_persona);
131 	T_ASSERT_NE(kr, 0, NULL);
132 
133 	uid_t current_persona = PERSONA_ID_NONE;
134 	T_ASSERT_EQ(kpersona_get(&current_persona), 0, NULL);
135 	T_ASSERT_EQ(current_persona, info.persona_id, NULL);
136 
137 	T_ASSERT_EQ(kpersona_dealloc(created_persona), 0, NULL);
138 }
139 
140 #if !TARGET_OS_BRIDGE // PersonaEnterprise is not supported on bridgeOS
141 
142 static uid_t _helper_persona = PERSONA_ID_NONE;
143 
144 static void
_run_helper_in_persona_cleanup(void)145 _run_helper_in_persona_cleanup(void)
146 {
147 	kpersona_dealloc(_helper_persona);
148 }
149 
150 static void __attribute__((noreturn))
_run_helper_in_persona(const char * helper_name,int persona_type)151 _run_helper_in_persona(const char *helper_name, int persona_type)
152 {
153 	struct kpersona_info info = {
154 		.persona_info_version = PERSONA_INFO_V2,
155 	};
156 	int error = kpersona_info(0, &info);
157 	uid_t persona_uid = (error == 0) ? info.persona_uid : geteuid();
158 	_helper_persona = _persona_create(persona_type, persona_uid);
159 	T_ATEND(_run_helper_in_persona_cleanup);
160 
161 	xpc_object_t plist = xpc_dictionary_create_empty();
162 	xpc_dictionary_set_bool(plist, "RunAtLoad", true);
163 	xpc_dictionary_set_int64(plist, "PersonaEnterprise", _helper_persona);
164 	dt_helper_t helper = dt_launchd_helper_plist(plist, helper_name, LAUNCH_SYSTEM_DOMAIN, NULL, NULL);
165 
166 	dt_run_helpers(&helper, 1, 300);
167 }
168 
169 T_HELPER_DECL(own_persona_can_be_adopted_impl,
170     "own_persona_can_be_adopted helper spawned into persona type that prohibits adoption")
171 {
172 	struct kpersona_info info = {
173 		.persona_info_version = PERSONA_INFO_V2,
174 	};
175 	int error = kpersona_info(0, &info);
176 	T_ASSERT_EQ(error, 0, NULL);
177 
178 	kern_return_t kr = _persona_try_adopting(info.persona_id);
179 	T_ASSERT_EQ(kr, 0, NULL);
180 }
181 
182 T_DECL(own_persona_can_be_adopted,
183     "process spawned into a persona type that prohibits adoption can adopt own persona")
184 {
185 	_run_helper_in_persona("own_persona_can_be_adopted_impl", PERSONA_MANAGED);
186 }
187 
188 #endif // TARGET_OS_BRIDGE
189