]>
Commit | Line | Data |
---|---|---|
e91b9f68 A |
1 | /* |
2 | * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* | |
25 | * bootstrap -- fundamental service initiator and port server | |
26 | * Mike DeMoney, NeXT, Inc. | |
27 | * Copyright, 1990. All rights reserved. | |
28 | * | |
29 | * rpc_services.c -- implementation of bootstrap rpc services | |
30 | */ | |
31 | ||
32 | #include <mach/mach.h> | |
33 | #include <mach/mach_error.h> | |
34 | #include <syslog.h> | |
35 | #include <string.h> | |
36 | ||
37 | #include <bsm/audit.h> | |
38 | #include <bsm/libbsm.h> | |
39 | ||
40 | #include "bootstrap_internal.h" | |
41 | #include "lists.h" | |
42 | #include "bootstrap.h" | |
43 | ||
44 | #ifndef ASSERT | |
45 | #define ASSERT(p) | |
46 | #endif | |
47 | ||
48 | #ifndef NULL | |
49 | #define NULL ((void *)0) | |
50 | #endif NULL | |
51 | ||
52 | #define bsstatus(servicep) \ | |
53 | (((servicep)->isActive) ? BOOTSTRAP_STATUS_ACTIVE : \ | |
54 | (((servicep)->server && (servicep)->server->servertype == DEMAND) ? \ | |
55 | BOOTSTRAP_STATUS_ON_DEMAND : BOOTSTRAP_STATUS_INACTIVE)) | |
56 | ||
57 | /* extern port_all_t backup_port; */ | |
58 | ||
59 | /* | |
60 | * kern_return_t | |
61 | * bootstrap_create_server(mach_port_t bootstrap_port, | |
62 | * cmd_t server_cmd, | |
63 | * integer_t server_uid, | |
64 | * boolean_t on_demand, | |
65 | * mach_port_t *server_portp) | |
66 | * | |
67 | * Returns send rights to server_port of service. At this point, the | |
68 | * server appears active, so nothing will try to launch it. The server_port | |
69 | * can be used to delare services associated with this server by calling | |
70 | * bootstrap_create_service() and passing server_port as the bootstrap port. | |
71 | * | |
72 | * Errors: Returns appropriate kernel errors on rpc failure. | |
73 | * Returns BOOTSTRAP_NOT_PRIVILEGED, if bootstrap port invalid. | |
74 | */ | |
75 | __private_extern__ kern_return_t | |
76 | x_bootstrap_create_server( | |
77 | mach_port_t bootstrapport, | |
78 | cmd_t server_cmd, | |
79 | uid_t server_uid, | |
80 | boolean_t on_demand, | |
81 | audit_token_t client_audit_token, | |
82 | mach_port_t *server_portp) | |
83 | { | |
84 | server_t *serverp; | |
85 | struct auditinfo audit_info; | |
86 | bootstrap_info_t *bootstrap; | |
87 | ||
88 | uid_t client_euid; | |
89 | ||
90 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
91 | syslog(LOG_DEBUG, "Server create attempt: \"%s\" bootstrap %x", | |
92 | server_cmd, bootstrapport); | |
93 | ||
94 | /* No forwarding allowed for this call - security risk (we run as root) */ | |
95 | if (!bootstrap || !active_bootstrap(bootstrap)) { | |
96 | syslog(LOG_DEBUG, "Server create: \"%s\": invalid bootstrap %x", | |
97 | server_cmd, bootstrapport); | |
98 | return BOOTSTRAP_NOT_PRIVILEGED; | |
99 | } | |
100 | ||
101 | /* get the identity of the requestor and set up audit_info of server */ | |
102 | audit_token_to_au32(client_audit_token, | |
103 | &audit_info.ai_auid, | |
104 | &client_euid, | |
105 | NULL /* egid */, | |
106 | NULL /* ruid */, | |
107 | NULL /* rgid */, | |
108 | NULL /* pid */, | |
109 | &audit_info.ai_asid, | |
110 | &audit_info.ai_termid); | |
111 | ||
112 | if (client_euid != 0 && client_euid != server_uid) { | |
113 | syslog(LOG_NOTICE, "Server create: \"%s\": insufficient privilege for specified uid (euid-%d != requested-%d)", | |
114 | server_cmd, client_euid, server_uid); | |
115 | return BOOTSTRAP_NOT_PRIVILEGED; | |
116 | } | |
117 | ||
118 | serverp = new_server( | |
119 | bootstrap, | |
120 | server_cmd, | |
121 | server_uid, | |
122 | (on_demand) ? DEMAND : RESTARTABLE, | |
123 | audit_info); | |
124 | setup_server(serverp); | |
125 | ||
126 | syslog(LOG_INFO, "New server %x in bootstrap %x: \"%s\"", | |
127 | serverp->port, bootstrapport, server_cmd); | |
128 | *server_portp = serverp->port; | |
129 | return BOOTSTRAP_SUCCESS; | |
130 | } | |
131 | ||
132 | /* | |
133 | * kern_return_t | |
134 | * bootstrap_unprivileged(mach_port_t bootstrapport, | |
135 | * mach_port_t *unprivportp) | |
136 | * | |
137 | * Given a bootstrap port, return its unprivileged equivalent. If | |
138 | * the port is already unprivileged, another reference to the same | |
139 | * port is returned. | |
140 | * | |
141 | * This is most often used by servers, which are launched with their | |
142 | * bootstrap port set to the privileged port for the server, to get | |
143 | * an unprivileged version of the same port for use by its unprivileged | |
144 | * children (or any offspring that it does not want to count as part | |
145 | * of the "server" for mach_init registration and re-launch purposes). | |
146 | */ | |
147 | __private_extern__ kern_return_t | |
148 | x_bootstrap_unprivileged( | |
149 | mach_port_t bootstrapport, | |
150 | mach_port_t *unprivportp) | |
151 | { | |
152 | bootstrap_info_t *bootstrap; | |
153 | ||
154 | syslog(LOG_DEBUG, "Get unprivileged attempt for bootstrap %x", bootstrapport); | |
155 | ||
156 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
157 | if (!bootstrap) { | |
158 | syslog(LOG_DEBUG, "Get unprivileged: invalid bootstrap %x", bootstrapport); | |
159 | return BOOTSTRAP_NOT_PRIVILEGED; | |
160 | } | |
161 | ||
162 | *unprivportp = bootstrap->bootstrap_port; | |
163 | ||
164 | syslog(LOG_DEBUG, "Get unpriv bootstrap %x returned for bootstrap %x", | |
165 | bootstrap->bootstrap_port, bootstrapport); | |
166 | return BOOTSTRAP_SUCCESS; | |
167 | } | |
168 | ||
169 | ||
170 | /* | |
171 | * kern_return_t | |
172 | * bootstrap_check_in(mach_port_t bootstrapport, | |
173 | * name_t servicename, | |
174 | * mach_port_t *serviceportp) | |
175 | * | |
176 | * Returns receive rights to service_port of service named by service_name. | |
177 | * | |
178 | * Errors: Returns appropriate kernel errors on rpc failure. | |
179 | * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. | |
180 | * Returns BOOTSTRAP_SERVICE_NOT_DECLARED, if service not declared | |
181 | * in /etc/bootstrap.conf. | |
182 | * Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been | |
183 | * registered or checked-in. | |
184 | */ | |
185 | __private_extern__ kern_return_t | |
186 | x_bootstrap_check_in( | |
187 | mach_port_t bootstrapport, | |
188 | name_t servicename, | |
189 | mach_port_t *serviceportp) | |
190 | { | |
191 | kern_return_t result; | |
192 | mach_port_t previous; | |
193 | service_t *servicep; | |
194 | server_t *serverp; | |
195 | bootstrap_info_t *bootstrap; | |
196 | ||
197 | serverp = lookup_server_by_port(bootstrapport); | |
198 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
199 | syslog(LOG_DEBUG, "Service checkin attempt for service %s bootstrap %x", | |
200 | servicename, bootstrapport); | |
201 | ||
202 | servicep = lookup_service_by_name(bootstrap, servicename); | |
203 | if (servicep == NULL || servicep->port == MACH_PORT_NULL) { | |
204 | syslog(LOG_DEBUG, "bootstrap_check_in service %s unknown%s", servicename, | |
205 | forward_ok ? " forwarding" : ""); | |
206 | return forward_ok ? | |
207 | bootstrap_check_in( | |
208 | inherited_bootstrap_port, | |
209 | servicename, | |
210 | serviceportp) : | |
211 | BOOTSTRAP_UNKNOWN_SERVICE; | |
212 | } | |
213 | if (servicep->server != NULL && servicep->server != serverp) { | |
214 | syslog(LOG_DEBUG, "bootstrap_check_in service %s not privileged", | |
215 | servicename); | |
216 | return BOOTSTRAP_NOT_PRIVILEGED; | |
217 | } | |
218 | if (!canReceive(servicep->port)) { | |
219 | ASSERT(servicep->isActive); | |
220 | syslog(LOG_DEBUG, "bootstrap_check_in service %s already active", | |
221 | servicename); | |
222 | return BOOTSTRAP_SERVICE_ACTIVE; | |
223 | } | |
224 | syslog(LOG_DEBUG, "Checkin service %s for bootstrap %x", servicename, | |
225 | bootstrap->bootstrap_port); | |
226 | ASSERT(servicep->isActive == FALSE); | |
227 | servicep->isActive = TRUE; | |
228 | ||
229 | if (servicep->server != NULL_SERVER) { | |
230 | /* registered server - service needs backup */ | |
231 | serverp->activity++; | |
232 | serverp->active_services++; | |
233 | result = mach_port_request_notification( | |
234 | mach_task_self(), | |
235 | servicep->port, | |
236 | MACH_NOTIFY_PORT_DESTROYED, | |
237 | 0, | |
238 | backup_port, | |
239 | MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
240 | &previous); | |
241 | if (result != KERN_SUCCESS) | |
242 | panic("mach_port_request_notification(): %s", mach_error_string(result)); | |
243 | } else { | |
244 | /* one time use/created service */ | |
245 | servicep->servicetype = REGISTERED; | |
246 | result = mach_port_request_notification( | |
247 | mach_task_self(), | |
248 | servicep->port, | |
249 | MACH_NOTIFY_DEAD_NAME, | |
250 | 0, | |
251 | notify_port, | |
252 | MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
253 | &previous); | |
254 | if (result != KERN_SUCCESS) | |
255 | panic("mach_port_request_notification(): %s", mach_error_string(result)); | |
256 | else if (previous != MACH_PORT_NULL) { | |
257 | syslog(LOG_DEBUG, "deallocating old notification port (%x) for checked in service %x", | |
258 | previous, servicep->port); | |
259 | result = mach_port_deallocate( | |
260 | mach_task_self(), | |
261 | previous); | |
262 | if (result != KERN_SUCCESS) | |
263 | panic("mach_port_deallocate(): %s", mach_error_string(result)); | |
264 | } | |
265 | } | |
266 | ||
267 | syslog(LOG_INFO, "Check-in service %x in bootstrap %x: %s", | |
268 | servicep->port, servicep->bootstrap->bootstrap_port, servicep->name); | |
269 | ||
270 | *serviceportp = servicep->port; | |
271 | return BOOTSTRAP_SUCCESS; | |
272 | } | |
273 | ||
274 | /* | |
275 | * kern_return_t | |
276 | * bootstrap_register(mach_port_t bootstrapport, | |
277 | * name_t servicename, | |
278 | * mach_port_t serviceport) | |
279 | * | |
280 | * Registers send rights for the port service_port for the service named by | |
281 | * service_name. Registering a declared service or registering a service for | |
282 | * which bootstrap has receive rights via a port backup notification is | |
283 | * allowed. | |
284 | * The previous service port will be deallocated. Restarting services wishing | |
285 | * to resume service for previous clients must first attempt to checkin to the | |
286 | * service. | |
287 | * | |
288 | * Errors: Returns appropriate kernel errors on rpc failure. | |
289 | * Returns BOOTSTRAP_NOT_PRIVILEGED, if request directed to | |
290 | * unprivileged bootstrap port. | |
291 | * Returns BOOTSTRAP_SERVICE_ACTIVE, if service has already been | |
292 | * register or checked-in. | |
293 | */ | |
294 | __private_extern__ kern_return_t | |
295 | x_bootstrap_register( | |
296 | mach_port_t bootstrapport, | |
297 | name_t servicename, | |
298 | mach_port_t serviceport) | |
299 | { | |
300 | kern_return_t result; | |
301 | service_t *servicep; | |
302 | server_t *serverp; | |
303 | bootstrap_info_t *bootstrap; | |
304 | mach_port_t old_port; | |
305 | ||
306 | syslog(LOG_DEBUG, "Register attempt for service %s port %x", | |
307 | servicename, serviceport); | |
308 | ||
309 | /* | |
310 | * Validate the bootstrap. | |
311 | */ | |
312 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
313 | if (!bootstrap || !active_bootstrap(bootstrap)) | |
314 | return BOOTSTRAP_NOT_PRIVILEGED; | |
315 | ||
316 | /* | |
317 | * If this bootstrap port is for a server, or it's an unprivileged | |
318 | * bootstrap can't register the port. | |
319 | */ | |
320 | serverp = lookup_server_by_port(bootstrapport); | |
321 | servicep = lookup_service_by_name(bootstrap, servicename); | |
322 | if (servicep && servicep->server && servicep->server != serverp) | |
323 | return BOOTSTRAP_NOT_PRIVILEGED; | |
324 | ||
325 | if (servicep == NULL || servicep->bootstrap != bootstrap) { | |
326 | servicep = new_service(bootstrap, | |
327 | servicename, | |
328 | serviceport, | |
329 | ACTIVE, | |
330 | REGISTERED, | |
331 | NULL_SERVER); | |
332 | syslog(LOG_DEBUG, "Registered new service %s", servicename); | |
333 | } else { | |
334 | if (servicep->isActive) { | |
335 | syslog(LOG_DEBUG, "Register: service %s already active, port %x", | |
336 | servicep->name, servicep->port); | |
337 | ASSERT(!canReceive(servicep->port)); | |
338 | return BOOTSTRAP_SERVICE_ACTIVE; | |
339 | } | |
340 | old_port = servicep->port; | |
341 | if (servicep->servicetype == DECLARED) { | |
342 | servicep->servicetype = REGISTERED; | |
343 | ||
344 | if (servicep->server) { | |
345 | ASSERT(servicep->server == serverp); | |
346 | ASSERT(active_server(serverp)); | |
347 | servicep->server = NULL_SERVER; | |
348 | serverp->activity++; | |
349 | } | |
350 | ||
351 | result = mach_port_mod_refs( | |
352 | mach_task_self(), | |
353 | old_port, | |
354 | MACH_PORT_RIGHT_RECEIVE, | |
355 | -1); | |
356 | if (result != KERN_SUCCESS) | |
357 | panic("mach_port_mod_refs(): %s", mach_error_string(result)); | |
358 | } | |
359 | result = mach_port_deallocate( | |
360 | mach_task_self(), | |
361 | old_port); | |
362 | if (result != KERN_SUCCESS) | |
363 | panic("mach_port_mod_refs(): %s", mach_error_string(result)); | |
364 | ||
365 | servicep->port = serviceport; | |
366 | servicep->isActive = TRUE; | |
367 | syslog(LOG_DEBUG, "Re-registered inactive service %x bootstrap %x: %s", | |
368 | servicep->port, servicep->bootstrap->bootstrap_port, servicename); | |
369 | } | |
370 | ||
371 | /* detect the new service port going dead */ | |
372 | result = mach_port_request_notification( | |
373 | mach_task_self(), | |
374 | serviceport, | |
375 | MACH_NOTIFY_DEAD_NAME, | |
376 | 0, | |
377 | notify_port, | |
378 | MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
379 | &old_port); | |
380 | if (result != KERN_SUCCESS) { | |
381 | syslog(LOG_DEBUG, "Can't request notification on service %x bootstrap %x: %s", | |
382 | service_port, servicep->bootstrap->bootstrap_port, "must be dead"); | |
383 | delete_service(servicep); | |
384 | return BOOTSTRAP_SUCCESS; | |
385 | } else if (old_port != MACH_PORT_NULL) { | |
386 | syslog(LOG_DEBUG, "deallocating old notification port (%x) for service %x", | |
387 | old_port, serviceport); | |
388 | result = mach_port_deallocate( | |
389 | mach_task_self(), | |
390 | old_port); | |
391 | if (result != KERN_SUCCESS) | |
392 | panic("mach_port_deallocate(): %s", mach_error_string(result)); | |
393 | } | |
394 | syslog(LOG_INFO, "Registered service %x bootstrap %x: %s", | |
395 | servicep->port, servicep->bootstrap->bootstrap_port, servicep->name); | |
396 | return BOOTSTRAP_SUCCESS; | |
397 | } | |
398 | ||
399 | /* | |
400 | * kern_return_t | |
401 | * bootstrap_look_up(mach_port_t bootstrapport, | |
402 | * name_t servicename, | |
403 | * mach_port_t *serviceportp) | |
404 | * | |
405 | * Returns send rights for the service port of the service named by | |
406 | * service_name in *service_portp. Service is not guaranteed to be active. | |
407 | * | |
408 | * Errors: Returns appropriate kernel errors on rpc failure. | |
409 | * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. | |
410 | */ | |
411 | __private_extern__ kern_return_t | |
412 | x_bootstrap_look_up( | |
413 | mach_port_t bootstrapport, | |
414 | name_t servicename, | |
415 | mach_port_t *serviceportp) | |
416 | { | |
417 | service_t *servicep; | |
418 | bootstrap_info_t *bootstrap; | |
419 | ||
420 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
421 | servicep = lookup_service_by_name(bootstrap, servicename); | |
422 | if (servicep == NULL || servicep->port == MACH_PORT_NULL) { | |
423 | if (forward_ok) { | |
424 | syslog(LOG_DEBUG, "bootstrap_look_up service %s forwarding", | |
425 | servicename); | |
426 | return bootstrap_look_up(inherited_bootstrap_port, | |
427 | servicename, | |
428 | serviceportp); | |
429 | } else { | |
430 | syslog(LOG_DEBUG, "bootstrap_look_up service %s unknown", | |
431 | servicename); | |
432 | return BOOTSTRAP_UNKNOWN_SERVICE; | |
433 | } | |
434 | } | |
435 | *serviceportp = servicep->port; | |
436 | syslog(LOG_DEBUG, "Lookup returns port %x for service %s", servicep->port, servicep->name); | |
437 | return BOOTSTRAP_SUCCESS; | |
438 | } | |
439 | ||
440 | /* | |
441 | * kern_return_t | |
442 | * bootstrap_look_up_array(mach_port_t bootstrapport, | |
443 | * name_array_t servicenames, | |
444 | * int servicenames_cnt, | |
445 | * mach_port_array_t *serviceports, | |
446 | * int *serviceports_cnt, | |
447 | * boolean_t *allservices_known) | |
448 | * | |
449 | * Returns port send rights in corresponding entries of the array service_ports | |
450 | * for all services named in the array service_names. Service_ports_cnt is | |
451 | * returned and will always equal service_names_cnt (assuming service_names_cnt | |
452 | * is greater than or equal to zero). | |
453 | * | |
454 | * Errors: Returns appropriate kernel errors on rpc failure. | |
455 | * Returns BOOTSTRAP_NO_MEMORY, if server couldn't obtain memory | |
456 | * for response. | |
457 | * Unknown service names have the corresponding service | |
458 | * port set to MACH_PORT_NULL. | |
459 | * If all services are known, all_services_known is true on | |
460 | * return, | |
461 | * if any service is unknown, it's false. | |
462 | */ | |
463 | __private_extern__ kern_return_t | |
464 | x_bootstrap_look_up_array( | |
465 | mach_port_t bootstrapport, | |
466 | name_array_t servicenames, | |
467 | unsigned int servicenames_cnt, | |
468 | mach_port_array_t *serviceportsp, | |
469 | unsigned int *serviceports_cnt, | |
470 | boolean_t *allservices_known) | |
471 | { | |
472 | unsigned int i; | |
473 | static mach_port_t service_ports[BOOTSTRAP_MAX_LOOKUP_COUNT]; | |
474 | ||
475 | if (servicenames_cnt > BOOTSTRAP_MAX_LOOKUP_COUNT) | |
476 | return BOOTSTRAP_BAD_COUNT; | |
477 | *serviceports_cnt = servicenames_cnt; | |
478 | *allservices_known = TRUE; | |
479 | for (i = 0; i < servicenames_cnt; i++) { | |
480 | if ( x_bootstrap_look_up(bootstrapport, | |
481 | servicenames[i], | |
482 | &service_ports[i]) | |
483 | != BOOTSTRAP_SUCCESS) | |
484 | { | |
485 | *allservices_known = FALSE; | |
486 | service_ports[i] = MACH_PORT_NULL; | |
487 | } | |
488 | } | |
489 | syslog(LOG_DEBUG, "bootstrap_look_up_array returns %d ports", servicenames_cnt); | |
490 | *serviceportsp = service_ports; | |
491 | return BOOTSTRAP_SUCCESS; | |
492 | } | |
493 | ||
494 | /* | |
495 | * kern_return_t | |
496 | * bootstrap_parent(mach_port_t bootstrapport, | |
497 | * mach_port_t *parentport); | |
498 | * | |
499 | * Given a bootstrap subset port, return the parent bootstrap port. | |
500 | * If the specified bootstrap port is already the root subset, | |
501 | * MACH_PORT_NULL will be returned. | |
502 | * | |
503 | * Errors: | |
504 | * Returns BOOTSTRAP_NOT_PRIVILEGED if the caller is not running | |
505 | * with an effective user id of root (as determined by the security | |
506 | * token in the message trailer). | |
507 | */ | |
508 | __private_extern__ kern_return_t | |
509 | x_bootstrap_parent( | |
510 | mach_port_t bootstrapport, | |
511 | security_token_t sectoken, | |
512 | mach_port_t *parentport) | |
513 | { | |
514 | bootstrap_info_t *bootstrap; | |
515 | ||
516 | syslog(LOG_DEBUG, "Parent attempt for bootstrap %x", bootstrapport); | |
517 | ||
518 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
519 | if (!bootstrap) { | |
520 | syslog(LOG_DEBUG, "Parent attempt for bootstrap %x: invalid bootstrap", | |
521 | bootstrapport); | |
522 | return BOOTSTRAP_NOT_PRIVILEGED; | |
523 | } | |
524 | if (sectoken.val[0]) { | |
525 | syslog(LOG_NOTICE, "Bootstrap parent for bootstrap %x: invalid security token (%d)", | |
526 | bootstrapport, sectoken.val[0]); | |
527 | return BOOTSTRAP_NOT_PRIVILEGED; | |
528 | } | |
529 | syslog(LOG_DEBUG, "Returning bootstrap parent %x for bootstrap %x", | |
530 | bootstrap->parent->bootstrap_port, bootstrapport); | |
531 | *parentport = bootstrap->parent->bootstrap_port; | |
532 | return BOOTSTRAP_SUCCESS; | |
533 | } | |
534 | ||
535 | /* | |
536 | * kern_return_t | |
537 | * bootstrap_status(mach_port_t bootstrapport, | |
538 | * name_t servicename, | |
539 | * bootstrap_status_t *serviceactive); | |
540 | * | |
541 | * Returns: service_active indicates if service is available. | |
542 | * | |
543 | * Errors: Returns appropriate kernel errors on rpc failure. | |
544 | * Returns BOOTSTRAP_UNKNOWN_SERVICE, if service does not exist. | |
545 | */ | |
546 | __private_extern__ kern_return_t | |
547 | x_bootstrap_status( | |
548 | mach_port_t bootstrapport, | |
549 | name_t servicename, | |
550 | bootstrap_status_t *serviceactivep) | |
551 | { | |
552 | service_t *servicep; | |
553 | bootstrap_info_t *bootstrap; | |
554 | ||
555 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
556 | servicep = lookup_service_by_name(bootstrap, servicename); | |
557 | if (servicep == NULL) { | |
558 | if (forward_ok) { | |
559 | syslog(LOG_DEBUG, "bootstrap_status forwarding status, server %s", | |
560 | servicename); | |
561 | return bootstrap_status(inherited_bootstrap_port, | |
562 | servicename, | |
563 | serviceactivep); | |
564 | } else { | |
565 | syslog(LOG_DEBUG, "bootstrap_status service %s unknown", | |
566 | servicename); | |
567 | return BOOTSTRAP_UNKNOWN_SERVICE; | |
568 | } | |
569 | } | |
570 | *serviceactivep = bsstatus(servicep); | |
571 | ||
572 | syslog(LOG_DEBUG, "bootstrap_status server %s %sactive", servicename, | |
573 | servicep->isActive ? "" : "in"); | |
574 | return BOOTSTRAP_SUCCESS; | |
575 | } | |
576 | ||
577 | /* | |
578 | * kern_return_t | |
579 | * bootstrap_info(mach_port_t bootstrapport, | |
580 | * name_array_t *servicenamesp, | |
581 | * int *servicenames_cnt, | |
582 | * name_array_t *servernamesp, | |
583 | * int *servernames_cnt, | |
584 | * bootstrap_status_array_t *serviceactivesp, | |
585 | * int *serviceactive_cnt); | |
586 | * | |
587 | * Returns bootstrap status for all known services. | |
588 | * | |
589 | * Errors: Returns appropriate kernel errors on rpc failure. | |
590 | */ | |
591 | __private_extern__ kern_return_t | |
592 | x_bootstrap_info( | |
593 | mach_port_t bootstrapport, | |
594 | name_array_t *servicenamesp, | |
595 | unsigned int *servicenames_cnt, | |
596 | name_array_t *servernamesp, | |
597 | unsigned int *servernames_cnt, | |
598 | bootstrap_status_array_t *serviceactivesp, | |
599 | unsigned int *serviceactives_cnt) | |
600 | { | |
601 | kern_return_t result; | |
602 | unsigned int i, cnt; | |
603 | service_t *servicep; | |
604 | server_t *serverp; | |
605 | bootstrap_info_t *bootstrap; | |
606 | name_array_t service_names; | |
607 | name_array_t server_names; | |
608 | bootstrap_status_array_t service_actives; | |
609 | ||
610 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
611 | ||
612 | for ( cnt = i = 0, servicep = services.next | |
613 | ; i < nservices | |
614 | ; servicep = servicep->next, i++) | |
615 | { | |
616 | if (lookup_service_by_name(bootstrap, servicep->name) == servicep) | |
617 | { | |
618 | cnt++; | |
619 | } | |
620 | } | |
621 | result = vm_allocate(mach_task_self(), | |
622 | (vm_address_t *)&service_names, | |
623 | cnt * sizeof(service_names[0]), | |
624 | ANYWHERE); | |
625 | if (result != KERN_SUCCESS) | |
626 | return BOOTSTRAP_NO_MEMORY; | |
627 | ||
628 | result = vm_allocate(mach_task_self(), | |
629 | (vm_address_t *)&server_names, | |
630 | cnt * sizeof(server_names[0]), | |
631 | ANYWHERE); | |
632 | if (result != KERN_SUCCESS) { | |
633 | (void)vm_deallocate(mach_task_self(), | |
634 | (vm_address_t)service_names, | |
635 | cnt * sizeof(service_names[0])); | |
636 | return BOOTSTRAP_NO_MEMORY; | |
637 | } | |
638 | result = vm_allocate(mach_task_self(), | |
639 | (vm_address_t *)&service_actives, | |
640 | cnt * sizeof(service_actives[0]), | |
641 | ANYWHERE); | |
642 | if (result != KERN_SUCCESS) { | |
643 | (void)vm_deallocate(mach_task_self(), | |
644 | (vm_address_t)service_names, | |
645 | cnt * sizeof(service_names[0])); | |
646 | (void)vm_deallocate(mach_task_self(), | |
647 | (vm_address_t)server_names, | |
648 | cnt * sizeof(server_names[0])); | |
649 | return BOOTSTRAP_NO_MEMORY; | |
650 | } | |
651 | ||
652 | for ( i = 0, servicep = services.next | |
653 | ; i < cnt | |
654 | ; servicep = servicep->next) | |
655 | { | |
656 | if ( lookup_service_by_name(bootstrap, servicep->name) | |
657 | != servicep) | |
658 | continue; | |
659 | strncpy(service_names[i], | |
660 | servicep->name, | |
661 | sizeof(service_names[0])); | |
662 | service_names[i][sizeof(service_names[0]) - 1] = '\0'; | |
663 | if (servicep->server) { | |
664 | serverp = servicep->server; | |
665 | strncpy(server_names[i], | |
666 | serverp->cmd, | |
667 | sizeof(server_names[0])); | |
668 | server_names[i][sizeof(server_names[0]) - 1] = '\0'; | |
669 | syslog(LOG_DEBUG, "bootstrap info service %s server %s %sactive", | |
670 | servicep->name, | |
671 | serverp->cmd, servicep->isActive ? "" : "in"); | |
672 | } else { | |
673 | server_names[i][0] = '\0'; | |
674 | syslog(LOG_DEBUG, "bootstrap info service %s %sactive", | |
675 | servicep->name, servicep->isActive ? "" : "in"); | |
676 | } | |
677 | service_actives[i] = bsstatus(servicep); | |
678 | i++; | |
679 | } | |
680 | *servicenamesp = service_names; | |
681 | *servernamesp = server_names; | |
682 | *serviceactivesp = service_actives; | |
683 | *servicenames_cnt = *servernames_cnt = *serviceactives_cnt = cnt; | |
684 | ||
685 | return BOOTSTRAP_SUCCESS; | |
686 | } | |
687 | ||
688 | /* | |
689 | * kern_return_t | |
690 | * bootstrap_subset(mach_port_t bootstrapport, | |
691 | * mach_port_t requestorport, | |
692 | * mach_port_t *subsetport); | |
693 | * | |
694 | * Returns a new port to use as a bootstrap port. This port behaves | |
695 | * exactly like the previous bootstrap_port, except that ports dynamically | |
696 | * registered via bootstrap_register() are available only to users of this | |
697 | * specific subset_port. Lookups on the subset_port will return ports | |
698 | * registered with this port specifically, and ports registered with | |
699 | * ancestors of this subset_port. Duplications of services already | |
700 | * registered with an ancestor port may be registered with the subset port | |
701 | * are allowed. Services already advertised may then be effectively removed | |
702 | * by registering MACH_PORT_NULL for the service. | |
703 | * When it is detected that the requestor_port is destroied the subset | |
704 | * port and all services advertized by it are destroied as well. | |
705 | * | |
706 | * Errors: Returns appropriate kernel errors on rpc failure. | |
707 | */ | |
708 | __private_extern__ kern_return_t | |
709 | x_bootstrap_subset( | |
710 | mach_port_t bootstrapport, | |
711 | mach_port_t requestorport, | |
712 | mach_port_t *subsetportp) | |
713 | { | |
714 | kern_return_t result; | |
715 | bootstrap_info_t *bootstrap; | |
716 | bootstrap_info_t *subset; | |
717 | mach_port_t new_bootstrapport; | |
718 | mach_port_t previous; | |
719 | ||
720 | syslog(LOG_DEBUG, "Subset create attempt: bootstrap %x, requestor: %x", | |
721 | bootstrapport, requestorport); | |
722 | ||
723 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
724 | if (!bootstrap || !active_bootstrap(bootstrap)) | |
725 | return BOOTSTRAP_NOT_PRIVILEGED; | |
726 | ||
727 | result = mach_port_allocate( | |
728 | mach_task_self(), | |
729 | MACH_PORT_RIGHT_RECEIVE, | |
730 | &new_bootstrapport); | |
731 | if (result != KERN_SUCCESS) | |
732 | panic("mach_port_allocate(): %s", mach_error_string(result)); | |
733 | ||
734 | result = mach_port_insert_right( | |
735 | mach_task_self(), | |
736 | new_bootstrapport, | |
737 | new_bootstrapport, | |
738 | MACH_MSG_TYPE_MAKE_SEND); | |
739 | if (result != KERN_SUCCESS) | |
740 | panic("failed to insert send right(): %s", mach_error_string(result)); | |
741 | ||
742 | result = mach_port_insert_member( | |
743 | mach_task_self(), | |
744 | new_bootstrapport, | |
745 | bootstrap_port_set); | |
746 | if (result != KERN_SUCCESS) | |
747 | panic("port_set_add(): %s", mach_error_string(result)); | |
748 | ||
749 | subset = new_bootstrap(bootstrap, new_bootstrapport, requestorport); | |
750 | ||
751 | result = mach_port_request_notification( | |
752 | mach_task_self(), | |
753 | requestorport, | |
754 | MACH_NOTIFY_DEAD_NAME, | |
755 | 0, | |
756 | notify_port, | |
757 | MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
758 | &previous); | |
759 | if (result != KERN_SUCCESS) { | |
760 | syslog(LOG_ERR, "mach_port_request_notification(): %s", mach_error_string(result)); | |
761 | mach_port_deallocate(mach_task_self(), requestorport); | |
762 | subset->requestor_port = MACH_PORT_NULL; | |
763 | deactivate_bootstrap(subset); | |
764 | } else if (previous != MACH_PORT_NULL) { | |
765 | syslog(LOG_DEBUG, "deallocating old notification port (%x) for requestor %x", | |
766 | previous, requestorport); | |
767 | result = mach_port_deallocate( | |
768 | mach_task_self(), | |
769 | previous); | |
770 | if (result != KERN_SUCCESS) | |
771 | panic("mach_port_deallocate(): %s", mach_error_string(result)); | |
772 | } | |
773 | ||
774 | syslog(LOG_INFO, "Created bootstrap subset %x parent %x requestor %x", | |
775 | new_bootstrapport, bootstrapport, requestorport); | |
776 | *subsetportp = new_bootstrapport; | |
777 | return BOOTSTRAP_SUCCESS; | |
778 | } | |
779 | ||
780 | /* | |
781 | * kern_return_t | |
782 | * bootstrap_create_service(mach_port_t bootstrapport, | |
783 | * name_t servicename, | |
784 | * mach_port_t *serviceportp) | |
785 | * | |
786 | * Creates a service named "service_name" and returns send rights to that | |
787 | * port in "service_port." The port may later be checked in as if this | |
788 | * port were configured in the bootstrap configuration file. | |
789 | * | |
790 | * Errors: Returns appropriate kernel errors on rpc failure. | |
791 | * Returns BOOTSTRAP_NAME_IN_USE, if service already exists. | |
792 | */ | |
793 | __private_extern__ kern_return_t | |
794 | x_bootstrap_create_service( | |
795 | mach_port_t bootstrapport, | |
796 | name_t servicename, | |
797 | mach_port_t *serviceportp) | |
798 | { | |
799 | server_t *serverp; | |
800 | service_t *servicep; | |
801 | bootstrap_info_t *bootstrap; | |
802 | kern_return_t result; | |
803 | ||
804 | bootstrap = lookup_bootstrap_by_port(bootstrapport); | |
805 | if (!bootstrap || !active_bootstrap(bootstrap)) | |
806 | return BOOTSTRAP_NOT_PRIVILEGED; | |
807 | ||
808 | syslog(LOG_DEBUG, "Service creation attempt for service %s bootstrap %x", | |
809 | servicename, bootstrapport); | |
810 | ||
811 | servicep = lookup_service_by_name(bootstrap, servicename); | |
812 | if (servicep) { | |
813 | syslog(LOG_DEBUG, "Service creation attempt for service %s failed, " | |
814 | "service already exists", servicename); | |
815 | return BOOTSTRAP_NAME_IN_USE; | |
816 | } | |
817 | ||
818 | serverp = lookup_server_by_port(bootstrapport); | |
819 | ||
820 | result = mach_port_allocate(mach_task_self(), | |
821 | MACH_PORT_RIGHT_RECEIVE, | |
822 | serviceportp); | |
823 | if (result != KERN_SUCCESS) | |
824 | panic("port_allocate(): %s", mach_error_string(result)); | |
825 | result = mach_port_insert_right(mach_task_self(), | |
826 | *serviceportp, | |
827 | *serviceportp, | |
828 | MACH_MSG_TYPE_MAKE_SEND); | |
829 | if (result != KERN_SUCCESS) | |
830 | panic("failed to insert send right(): %s", mach_error_string(result)); | |
831 | ||
832 | if (serverp) | |
833 | serverp->activity++; | |
834 | ||
835 | servicep = new_service(bootstrap, | |
836 | servicename, | |
837 | *serviceportp, | |
838 | !ACTIVE, | |
839 | DECLARED, | |
840 | serverp); | |
841 | ||
842 | syslog(LOG_INFO, "Created new service %x in bootstrap %x: %s", | |
843 | servicep->port, bootstrap->bootstrap_port, servicename); | |
844 | ||
845 | return BOOTSTRAP_SUCCESS; | |
846 | } |