X-Git-Url: https://git.saurik.com/apple/launchd.git/blobdiff_plain/ed34e3c3e5fb80e0702ac7fb92f189862089d820..5b0a4722f2ef1eac59c9650b37b29ac8b34a160e:/launchd/src/launchd_mach_ipc.c diff --git a/launchd/src/launchd_mach_ipc.c b/launchd/src/launchd_mach_ipc.c deleted file mode 100644 index 91c571b..0000000 --- a/launchd/src/launchd_mach_ipc.c +++ /dev/null @@ -1,916 +0,0 @@ -/* - * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_APACHE_LICENSE_HEADER_START@ - * - * 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. - * - * @APPLE_APACHE_LICENSE_HEADER_END@ - */ -/* - * bootstrap -- fundamental service initiator and port server - * Mike DeMoney, NeXT, Inc. - * Copyright, 1990. All rights reserved. - * - * bootstrap.c -- implementation of bootstrap main service loop - */ - -static const char *const __rcs_file_version__ = "$Revision: 1.52 $"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bootstrap_public.h" -#include "bootstrap_private.h" -#include "bootstrap.h" -#include "bootstrapServer.h" -#include "notifyServer.h" -#include "launchd_internal.h" -#include "launchd_internalServer.h" -#include "launchd.h" -#include "launchd_core_logic.h" -#include "launch_priv.h" -#include "launchd_unix_ipc.h" - -struct ldcred { - uid_t euid; - uid_t uid; - gid_t egid; - gid_t gid; - pid_t pid; - au_asid_t asid; -}; - -static au_asid_t inherited_asid = 0; - -static bool canReceive(mach_port_t); -static void init_ports(void); -static void *mport_demand_loop(void *arg); -static void audit_token_to_launchd_cred(audit_token_t au_tok, struct ldcred *ldc); - -static mach_port_t inherited_bootstrap_port = MACH_PORT_NULL; -static mach_port_t demand_port_set = MACH_PORT_NULL; -static size_t port_to_obj_size = 0; -static void **port_to_obj = NULL; -static pthread_t demand_thread; - -static bool trusted_client_check(struct jobcb *j, struct ldcred *ldc); - -struct jobcb * -job_find_by_port(mach_port_t mp) -{ - return port_to_obj[MACH_PORT_INDEX(mp)]; -} - -kern_return_t -x_handle_mport(mach_port_t junk __attribute__((unused))) -{ - mach_port_name_array_t members; - mach_msg_type_number_t membersCnt; - mach_port_status_t status; - mach_msg_type_number_t statusCnt; - struct kevent kev; - unsigned int i; - - if (!launchd_assumes(mach_port_get_set_status(mach_task_self(), demand_port_set, &members, &membersCnt) == KERN_SUCCESS)) - return 1; - - for (i = 0; i < membersCnt; i++) { - statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT; - if (mach_port_get_attributes(mach_task_self(), members[i], MACH_PORT_RECEIVE_STATUS, - (mach_port_info_t)&status, &statusCnt) != KERN_SUCCESS) - continue; - - if (status.mps_msgcount) { - EV_SET(&kev, members[i], EVFILT_MACHPORT, 0, 0, 0, job_find_by_port(members[i])); - (*((kq_callback *)kev.udata))(kev.udata, &kev); - /* the callback may have tainted our ability to continue this for loop */ - break; - } - } - - launchd_assumes(vm_deallocate(mach_task_self(), (vm_address_t)members, - (vm_size_t) membersCnt * sizeof(mach_port_name_t)) == KERN_SUCCESS); - - return 0; -} - -void -mach_init_init(mach_port_t req_port, mach_port_t checkin_port, - name_array_t l2l_names, mach_port_array_t l2l_ports, mach_msg_type_number_t l2l_cnt) -{ - mach_msg_type_number_t l2l_i; - auditinfo_t inherited_audit; - pthread_attr_t attr; - - getaudit(&inherited_audit); - inherited_asid = inherited_audit.ai_asid; - - init_ports(); - - launchd_assert((root_job = job_new_bootstrap(NULL, req_port ? req_port : mach_task_self(), checkin_port)) != NULL); - - launchd_assumes(launchd_get_bport(&inherited_bootstrap_port) == KERN_SUCCESS); - - if (getpid() != 1) - launchd_assumes(inherited_bootstrap_port != MACH_PORT_NULL); - - /* We set this explicitly as we start each child */ - launchd_assumes(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS); - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); - - launchd_assert(pthread_create(&demand_thread, &attr, mport_demand_loop, NULL) == 0); - - pthread_attr_destroy(&attr); - - /* cut off the Libc cache, we don't want to deadlock against ourself */ - bootstrap_port = MACH_PORT_NULL; - - if (l2l_names == NULL) - return; - - for (l2l_i = 0; l2l_i < l2l_cnt; l2l_i++) { - struct machservice *ms; - - if ((ms = machservice_new(root_job, l2l_names[l2l_i], l2l_ports + l2l_i))) - machservice_watch(ms); - } -} - -void mach_init_reap(void) -{ - void *status; - - launchd_assumes(mach_port_destroy(mach_task_self(), demand_port_set) == KERN_SUCCESS); - - launchd_assumes(pthread_join(demand_thread, &status) == 0); -} - -void -init_ports(void) -{ - launchd_assert((errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, - &demand_port_set)) == KERN_SUCCESS); -} - -void * -mport_demand_loop(void *arg __attribute__((unused))) -{ - mach_msg_empty_rcv_t dummy; - kern_return_t kr; - - for (;;) { - kr = mach_msg(&dummy.header, MACH_RCV_MSG|MACH_RCV_LARGE, 0, 0, demand_port_set, 0, MACH_PORT_NULL); - if (kr == MACH_RCV_PORT_CHANGED) { - break; - } else if (!launchd_assumes(kr == MACH_RCV_TOO_LARGE)) { - continue; - } - launchd_assumes(handle_mport(launchd_internal_port) == 0); - } - - return NULL; -} - -boolean_t -launchd_mach_ipc_demux(mach_msg_header_t *Request, mach_msg_header_t *Reply) -{ - if (bootstrap_server_routine(Request)) - return bootstrap_server(Request, Reply); - - return notify_server(Request, Reply); -} - -bool -canReceive(mach_port_t port) -{ - mach_port_type_t p_type; - - if (!launchd_assumes(mach_port_type(mach_task_self(), port, &p_type) == KERN_SUCCESS)) - return false; - - return ((p_type & MACH_PORT_TYPE_RECEIVE) != 0); -} - -kern_return_t -launchd_set_bport(mach_port_t name) -{ - return errno = task_set_bootstrap_port(mach_task_self(), name); -} - -kern_return_t -launchd_get_bport(mach_port_t *name) -{ - return errno = task_get_bootstrap_port(mach_task_self(), name); -} - -kern_return_t -launchd_mport_notify_req(mach_port_t name, mach_msg_id_t which) -{ - mach_port_mscount_t msgc = (which == MACH_NOTIFY_NO_SENDERS) ? 1 : 0; - mach_port_t previous, where = (which == MACH_NOTIFY_NO_SENDERS) ? name : launchd_internal_port; - - if (which == MACH_NOTIFY_NO_SENDERS) { - /* Always make sure the send count is zero, in case a receive right is reused */ - errno = mach_port_set_mscount(mach_task_self(), name, 0); - if (errno != KERN_SUCCESS) - return errno; - } - - errno = mach_port_request_notification(mach_task_self(), name, which, msgc, where, - MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); - - if (errno == 0 && previous != MACH_PORT_NULL) - launchd_assumes(launchd_mport_deallocate(previous) == KERN_SUCCESS); - - return errno; -} - -kern_return_t -launchd_mport_request_callback(mach_port_t name, void *obj, bool readmsg) -{ - size_t needed_size; - - if (!obj) - return errno = mach_port_move_member(mach_task_self(), name, MACH_PORT_NULL); - - needed_size = (MACH_PORT_INDEX(name) + 1) * sizeof(void *); - - if (needed_size > port_to_obj_size) { - if (port_to_obj == NULL) { - launchd_assumes((port_to_obj = calloc(1, needed_size * 2)) != NULL); - } else { - launchd_assumes((port_to_obj = reallocf(port_to_obj, needed_size * 2)) != NULL); - memset((uint8_t *)port_to_obj + port_to_obj_size, 0, needed_size * 2 - port_to_obj_size); - } - port_to_obj_size = needed_size * 2; - } - - port_to_obj[MACH_PORT_INDEX(name)] = obj; - - return errno = mach_port_move_member(mach_task_self(), name, readmsg ? ipc_port_set : demand_port_set); -} - -kern_return_t -launchd_mport_make_send(mach_port_t name) -{ - return errno = mach_port_insert_right(mach_task_self(), name, name, MACH_MSG_TYPE_MAKE_SEND); -} - -kern_return_t -launchd_mport_close_recv(mach_port_t name) -{ - if (launchd_assumes(port_to_obj != NULL)) { - port_to_obj[MACH_PORT_INDEX(name)] = NULL; - return errno = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE, -1); - } else { - return errno = KERN_FAILURE; - } -} - -kern_return_t -launchd_mport_create_recv(mach_port_t *name) -{ - return errno = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, name); -} - -kern_return_t -launchd_mport_deallocate(mach_port_t name) -{ - return errno = mach_port_deallocate(mach_task_self(), name); -} - -void -audit_token_to_launchd_cred(audit_token_t au_tok, struct ldcred *ldc) -{ - audit_token_to_au32(au_tok, /* audit UID */ NULL, - &ldc->euid, &ldc->egid, - &ldc->uid, &ldc->gid, &ldc->pid, - &ldc->asid, /* au_tid_t */ NULL); -} - -kern_return_t -x_bootstrap_create_server(mach_port_t bp, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, - audit_token_t au_tok, mach_port_t *server_portp) -{ - struct jobcb *js, *j = job_find_by_port(bp); - struct ldcred ldc; - - audit_token_to_launchd_cred(au_tok, &ldc); - - job_log(j, LOG_DEBUG, "Server create attempt: %s", server_cmd); - -#define LET_MERE_MORTALS_ADD_SERVERS_TO_PID1 - /* XXX - This code should go away once the per session launchd is integrated with the rest of the system */ - #ifdef LET_MERE_MORTALS_ADD_SERVERS_TO_PID1 - if (getpid() == 1) { - if (ldc.euid != 0 && ldc.euid != server_uid) { - job_log(j, LOG_WARNING, "Server create: \"%s\": Will run as UID %d, not UID %d as they told us to", - server_cmd, ldc.euid, server_uid); - server_uid = ldc.euid; - } - } else -#endif - if (!trusted_client_check(j, &ldc)) { - return BOOTSTRAP_NOT_PRIVILEGED; - } else if (server_uid != getuid()) { - job_log(j, LOG_WARNING, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d", - server_cmd, getuid(), server_uid); - server_uid = getuid(); - } - - js = job_new_via_mach_init(j, server_cmd, server_uid, on_demand); - - if (js == NULL) - return BOOTSTRAP_NO_MEMORY; - - *server_portp = job_get_bsport(js); - return BOOTSTRAP_SUCCESS; -} - -kern_return_t -x_bootstrap_getsocket(mach_port_t bp, name_t spr) -{ - if (!sockpath) { - return BOOTSTRAP_NO_MEMORY; - } else if (getpid() == 1) { - return BOOTSTRAP_NOT_PRIVILEGED; - } - - strncpy(spr, sockpath, sizeof(name_t)); - - return BOOTSTRAP_SUCCESS; -} - -kern_return_t -x_bootstrap_unprivileged(mach_port_t bp, mach_port_t *unprivportp) -{ - struct jobcb *j = job_find_by_port(bp); - - job_log(j, LOG_DEBUG, "Requested unprivileged bootstrap port"); - - j = job_get_bs(j); - - *unprivportp = job_get_bsport(j); - - return BOOTSTRAP_SUCCESS; -} - - -kern_return_t -x_bootstrap_check_in(mach_port_t bp, name_t servicename, audit_token_t au_tok, mach_port_t *serviceportp) -{ - static pid_t last_warned_pid = 0; - struct jobcb *j = job_find_by_port(bp); - struct machservice *ms; - struct ldcred ldc; - - audit_token_to_launchd_cred(au_tok, &ldc); - - trusted_client_check(j, &ldc); - - ms = job_lookup_service(j, servicename, true); - - if (ms == NULL) { - job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Unknown: %s", servicename); - return BOOTSTRAP_UNKNOWN_SERVICE; - } - if (machservice_job(ms) != j) { - if (last_warned_pid != ldc.pid) { - job_log(j, LOG_NOTICE, "Check-in of Mach service failed. PID %d is not privileged: %s", - ldc.pid, servicename); - last_warned_pid = ldc.pid; - } - return BOOTSTRAP_NOT_PRIVILEGED; - } - if (!canReceive(machservice_port(ms))) { - launchd_assumes(machservice_active(ms)); - job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Already active: %s", servicename); - return BOOTSTRAP_SERVICE_ACTIVE; - } - - machservice_watch(ms); - - job_log(j, LOG_INFO, "Check-in of service: %s", servicename); - - *serviceportp = machservice_port(ms); - return BOOTSTRAP_SUCCESS; -} - -kern_return_t -x_bootstrap_register(mach_port_t bp, audit_token_t au_tok, name_t servicename, mach_port_t serviceport) -{ - struct jobcb *j = job_find_by_port(bp); - struct machservice *ms; - struct ldcred ldc; - - audit_token_to_launchd_cred(au_tok, &ldc); - - trusted_client_check(j, &ldc); - - job_log(j, LOG_DEBUG, "Mach service registration attempt: %s", servicename); - - ms = job_lookup_service(j, servicename, false); - - if (ms) { - if (machservice_job(ms) != j) - return BOOTSTRAP_NOT_PRIVILEGED; - if (machservice_active(ms)) { - job_log(j, LOG_DEBUG, "Mach service registration failed. Already active: %s", servicename); - launchd_assumes(!canReceive(machservice_port(ms))); - return BOOTSTRAP_SERVICE_ACTIVE; - } - job_checkin(machservice_job(ms)); - machservice_delete(ms); - } - - if (serviceport != MACH_PORT_NULL) { - if ((ms = machservice_new(job_get_bs(j), servicename, &serviceport))) { - machservice_watch(ms); - } else { - return BOOTSTRAP_NO_MEMORY; - } - } - - return BOOTSTRAP_SUCCESS; -} - -kern_return_t -x_bootstrap_look_up(mach_port_t bp, audit_token_t au_tok, name_t servicename, mach_port_t *serviceportp, mach_msg_type_name_t *ptype) -{ - struct jobcb *j = job_find_by_port(bp); - struct machservice *ms; - struct ldcred ldc; - - audit_token_to_launchd_cred(au_tok, &ldc); - - trusted_client_check(j, &ldc); - - ms = job_lookup_service(j, servicename, true); - - if (ms && machservice_hidden(ms) && !job_active(machservice_job(ms))) { - ms = NULL; - } - - if (ms) { - launchd_assumes(machservice_port(ms) != MACH_PORT_NULL); - job_log(j, LOG_DEBUG, "Mach service lookup (by PID %d): %s", ldc.pid, servicename); - *serviceportp = machservice_port(ms); - *ptype = MACH_MSG_TYPE_COPY_SEND; - return BOOTSTRAP_SUCCESS; - } else if (inherited_bootstrap_port != MACH_PORT_NULL) { - job_log(j, LOG_DEBUG, "Mach service lookup (by PID %d) forwarded: %s", ldc.pid, servicename); - *ptype = MACH_MSG_TYPE_MOVE_SEND; - return bootstrap_look_up(inherited_bootstrap_port, servicename, serviceportp); - } else { - job_log(j, LOG_DEBUG, "Mach service lookup (by PID %d) failed: %s", ldc.pid, servicename); - return BOOTSTRAP_UNKNOWN_SERVICE; - } -} - -kern_return_t -x_bootstrap_parent(mach_port_t bp, mach_port_t *parentport, mach_msg_type_name_t *pptype) -{ - struct jobcb *j = job_find_by_port(bp); - - job_log(j, LOG_DEBUG, "Requested parent bootstrap port"); - - j = job_get_bs(j); - - *pptype = MACH_MSG_TYPE_MAKE_SEND; - - if (job_parent(j)) { - *parentport = job_get_bsport(job_parent(j)); - } else if (MACH_PORT_NULL == inherited_bootstrap_port) { - *parentport = job_get_bsport(j); - } else { - *pptype = MACH_MSG_TYPE_COPY_SEND; - *parentport = inherited_bootstrap_port; - } - return BOOTSTRAP_SUCCESS; -} - -static void -x_bootstrap_info_countservices(struct machservice *ms, void *context) -{ - unsigned int *cnt = context; - - (*cnt)++; -} - -struct x_bootstrap_info_copyservices_cb { - name_array_t service_names; - bootstrap_status_array_t service_actives; - mach_port_array_t ports; - unsigned int i; -}; - -static void -x_bootstrap_info_copyservices(struct machservice *ms, void *context) -{ - struct x_bootstrap_info_copyservices_cb *info_resp = context; - - strlcpy(info_resp->service_names[info_resp->i], machservice_name(ms), sizeof(info_resp->service_names[0])); - - launchd_assumes(info_resp->service_actives || info_resp->ports); - - if (info_resp->service_actives) { - info_resp->service_actives[info_resp->i] = machservice_status(ms); - } else { - info_resp->ports[info_resp->i] = machservice_port(ms); - } - info_resp->i++; -} - -kern_return_t -x_bootstrap_info(mach_port_t bp, name_array_t *servicenamesp, unsigned int *servicenames_cnt, - bootstrap_status_array_t *serviceactivesp, unsigned int *serviceactives_cnt) -{ - struct x_bootstrap_info_copyservices_cb info_resp = { NULL, NULL, NULL, 0 }; - struct jobcb *ji, *j = job_find_by_port(bp); - kern_return_t result; - unsigned int cnt = 0; - - for (ji = j; ji; ji = job_parent(ji)) - job_foreach_service(ji, x_bootstrap_info_countservices, &cnt, true); - - result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.service_names, cnt * sizeof(info_resp.service_names[0]), true); - if (!launchd_assumes(result == KERN_SUCCESS)) - goto out_bad; - - result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.service_actives, cnt * sizeof(info_resp.service_actives[0]), true); - if (!launchd_assumes(result == KERN_SUCCESS)) - goto out_bad; - - for (ji = j; ji; ji = job_parent(ji)) - job_foreach_service(ji, x_bootstrap_info_copyservices, &info_resp, true); - - launchd_assumes(info_resp.i == cnt); - - *servicenamesp = info_resp.service_names; - *serviceactivesp = info_resp.service_actives; - *servicenames_cnt = *serviceactives_cnt = cnt; - - return BOOTSTRAP_SUCCESS; - -out_bad: - if (info_resp.service_names) - vm_deallocate(mach_task_self(), (vm_address_t)info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); - - return BOOTSTRAP_NO_MEMORY; -} - -kern_return_t -x_bootstrap_transfer_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, - name_array_t *servicenamesp, unsigned int *servicenames_cnt, - mach_port_array_t *ports, unsigned int *ports_cnt) -{ - struct x_bootstrap_info_copyservices_cb info_resp = { NULL, NULL, NULL, 0 }; - struct jobcb *j = job_find_by_port(bp); - unsigned int cnt = 0; - kern_return_t result; - - if (getpid() != 1) { - job_log(j, LOG_ERR, "Only the system launchd will transfer Mach sub-bootstraps."); - return BOOTSTRAP_NOT_PRIVILEGED; - } else if (!job_parent(j)) { - job_log(j, LOG_ERR, "Root Mach bootstrap cannot be transferred."); - return BOOTSTRAP_NOT_PRIVILEGED; - } - - job_log(j, LOG_DEBUG, "Transferring sub-bootstrap to the per session launchd."); - - job_foreach_service(j, x_bootstrap_info_countservices, &cnt, false); - - result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.service_names, cnt * sizeof(info_resp.service_names[0]), true); - if (!launchd_assumes(result == KERN_SUCCESS)) - goto out_bad; - - result = vm_allocate(mach_task_self(), (vm_address_t *)&info_resp.ports, cnt * sizeof(info_resp.ports[0]), true); - if (!launchd_assumes(result == KERN_SUCCESS)) - goto out_bad; - - job_foreach_service(j, x_bootstrap_info_copyservices, &info_resp, false); - - launchd_assumes(info_resp.i == cnt); - - *servicenamesp = info_resp.service_names; - *ports = info_resp.ports; - *servicenames_cnt = *ports_cnt = cnt; - - *reqport = job_get_reqport(j); - *rcvright = job_get_bsport(j); - - launchd_assumes(launchd_mport_request_callback(*rcvright, NULL, true) == KERN_SUCCESS); - - launchd_assumes(launchd_mport_make_send(*rcvright) == KERN_SUCCESS); - - return BOOTSTRAP_SUCCESS; - -out_bad: - if (info_resp.service_names) - vm_deallocate(mach_task_self(), (vm_address_t)info_resp.service_names, cnt * sizeof(info_resp.service_names[0])); - - return BOOTSTRAP_NO_MEMORY; -} - -kern_return_t -x_bootstrap_subset(mach_port_t bp, mach_port_t requestorport, mach_port_t *subsetportp) -{ - struct jobcb *js, *j = job_find_by_port(bp); - int bsdepth = 0; - - while ((j = job_parent(j)) != NULL) - bsdepth++; - - j = job_find_by_port(bp); - - /* Since we use recursion, we need an artificial depth for subsets */ - if (bsdepth > 100) { - job_log(j, LOG_ERR, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth); - return BOOTSTRAP_NO_MEMORY; - } - - if ((js = job_new_bootstrap(j, requestorport, MACH_PORT_NULL)) == NULL) { - if (requestorport == MACH_PORT_NULL) - return BOOTSTRAP_NOT_PRIVILEGED; - return BOOTSTRAP_NO_MEMORY; - } - - *subsetportp = job_get_bsport(js); - return BOOTSTRAP_SUCCESS; -} - -kern_return_t -x_bootstrap_create_service(mach_port_t bp, name_t servicename, mach_port_t *serviceportp) -{ - struct jobcb *j = job_find_by_port(bp); - struct machservice *ms; - - if (job_prog(j)[0] == '\0') { - job_log(j, LOG_ERR, "Mach service creation requires a target server: %s", servicename); - return BOOTSTRAP_NOT_PRIVILEGED; - } - - ms = job_lookup_service(j, servicename, false); - if (ms) { - job_log(j, LOG_DEBUG, "Mach service creation attempt for failed. Already exists: %s", servicename); - return BOOTSTRAP_NAME_IN_USE; - } - - job_checkin(j); - - *serviceportp = MACH_PORT_NULL; - ms = machservice_new(j, servicename, serviceportp); - - if (!launchd_assumes(ms != NULL)) - goto out_bad; - - return BOOTSTRAP_SUCCESS; - -out_bad: - launchd_assumes(launchd_mport_close_recv(*serviceportp) == KERN_SUCCESS); - return BOOTSTRAP_NO_MEMORY; -} - -kern_return_t -x_mpm_wait(mach_port_t bp, mach_port_t srp, audit_token_t au_tok, integer_t *waitstatus) -{ - struct jobcb *j = job_find_by_port(bp); -#if 0 - struct ldcred ldc; - audit_token_to_launchd_cred(au_tok, &ldc); -#endif - return job_handle_mpm_wait(j, srp, waitstatus); -} - -kern_return_t -x_mpm_uncork_fork(mach_port_t bp, audit_token_t au_tok) -{ - struct jobcb *j = job_find_by_port(bp); - - if (!j) - return BOOTSTRAP_NOT_PRIVILEGED; - - job_uncork_fork(j); - - return 0; -} - -kern_return_t -x_mpm_spawn(mach_port_t bp, audit_token_t au_tok, - _internal_string_t charbuf, mach_msg_type_number_t charbuf_cnt, - uint32_t argc, uint32_t envc, uint64_t flags, uint16_t mig_umask, - pid_t *child_pid, mach_port_t *obsvr_port) -{ - struct jobcb *jr, *j = job_find_by_port(bp); - struct ldcred ldc; - size_t offset = 0; - char *tmpp; - const char **argv = NULL, **env = NULL; - const char *label = NULL; - const char *path = NULL; - const char *workingdir = NULL; - size_t argv_i = 0, env_i = 0; - - audit_token_to_launchd_cred(au_tok, &ldc); - -#if 0 - if (ldc.asid != inherited_asid) { - job_log(j, LOG_ERR, "Security: PID %d (ASID %d) was denied a request to spawn a process in this session (ASID %d)", - ldc.pid, ldc.asid, inherited_asid); - return BOOTSTRAP_NOT_PRIVILEGED; - } -#endif - - argv = alloca((argc + 1) * sizeof(char *)); - memset(argv, 0, (argc + 1) * sizeof(char *)); - - if (envc > 0) { - env = alloca((envc + 1) * sizeof(char *)); - memset(env, 0, (envc + 1) * sizeof(char *)); - } - - while (offset < charbuf_cnt) { - tmpp = charbuf + offset; - offset += strlen(tmpp) + 1; - if (!label) { - label = tmpp; - } else if (argc > 0) { - argv[argv_i] = tmpp; - argv_i++; - argc--; - } else if (envc > 0) { - env[env_i] = tmpp; - env_i++; - envc--; - } else if (flags & SPAWN_HAS_PATH) { - path = tmpp; - flags &= ~SPAWN_HAS_PATH; - } else if (flags & SPAWN_HAS_WDIR) { - workingdir = tmpp; - flags &= ~SPAWN_HAS_WDIR; - } - } - - jr = job_new_spawn(label, path, workingdir, argv, env, flags & SPAWN_HAS_UMASK ? &mig_umask : NULL, - flags & SPAWN_WANTS_WAIT4DEBUGGER, flags & SPAWN_WANTS_FORCE_PPC); - - if (jr == NULL) switch (errno) { - case EEXIST: - return BOOTSTRAP_NAME_IN_USE; - default: - return BOOTSTRAP_NO_MEMORY; - } - - job_log(j, LOG_INFO, "Spawned with flags:%s%s", - flags & SPAWN_WANTS_FORCE_PPC ? " ppc": "", - flags & SPAWN_WANTS_WAIT4DEBUGGER ? " stopped": ""); - - *child_pid = job_get_pid(jr); - *obsvr_port = job_get_bsport(jr); - - return BOOTSTRAP_SUCCESS; -} - -kern_return_t -do_mach_notify_port_destroyed(mach_port_t notify, mach_port_t rights) -{ - /* This message is sent to us when a receive right is returned to us. */ - - if (!job_ack_port_destruction(root_job, rights)) { - launchd_assumes(launchd_mport_close_recv(rights) == KERN_SUCCESS); - } - - return KERN_SUCCESS; -} - -kern_return_t -do_mach_notify_port_deleted(mach_port_t notify, mach_port_name_t name) -{ - /* If we deallocate/destroy/mod_ref away a port with a pending notification, - * the original notification message is replaced with this message. - * - * To quote a Mach kernel expert, "the kernel has a send-once right that has - * to be used somehow." - */ - return KERN_SUCCESS; -} - -kern_return_t -do_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscount) -{ - struct jobcb *j = job_find_by_port(notify); - - /* This message is sent to us when the last customer of one of our objects - * goes away. - */ - - if (!launchd_assumes(j != NULL)) - return KERN_FAILURE; - - job_ack_no_senders(j); - - return KERN_SUCCESS; -} - -kern_return_t -do_mach_notify_send_once(mach_port_t notify) -{ - /* - * This message is sent to us every time we close a port that we have - * outstanding Mach notification requests on. We can safely ignore - * this message. - */ - return KERN_SUCCESS; -} - -kern_return_t -do_mach_notify_dead_name(mach_port_t notify, mach_port_name_t name) -{ - /* This message is sent to us when one of our send rights no longer has - * a receiver somewhere else on the system. - */ - - if (name == inherited_bootstrap_port) { - launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS); - inherited_bootstrap_port = MACH_PORT_NULL; - } - - job_delete_anything_with_port(root_job, name); - - /* A dead-name notification about a port appears to increment the - * rights on said port. Let's deallocate it so that we don't leak - * dead-name ports. - */ - launchd_assumes(launchd_mport_deallocate(name) == KERN_SUCCESS); - - return KERN_SUCCESS; -} - -bool -trusted_client_check(struct jobcb *j, struct ldcred *ldc) -{ - static pid_t last_warned_pid = 0; - - /* In the long run, we wish to enforce the progeny rule, but for now, - * we'll let root and the user be forgiven. Once we get CoreProcesses - * to switch to using launchd rather than the WindowServer for indirect - * process invocation, we can then seriously look at cranking up the - * warning level here. - */ - - if (inherited_asid == ldc->asid) - return true; - if (progeny_check(ldc->pid)) - return true; - if (ldc->euid == geteuid()) - return true; - if (ldc->euid == 0 && ldc->uid == 0) - return true; - if (last_warned_pid == ldc->pid) - return false; - - job_log(j, LOG_NOTICE, "Security: PID %d (ASID %d) was leaked into this session (ASID %d). This will be denied in the future.", ldc->pid, ldc->asid, inherited_asid); - - last_warned_pid = ldc->pid; - - return false; -}