static int osquery_ioctl( dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { #ifdef KERNEL_TEST // Reentrant code used for testing the queue functionality. // This test-only code allows benchmarks to stress test queue handling. static unsigned int test_counter = 0; if (cmd == OSQUERY_IOCTL_TEST) { if (osquery.buffer == NULL) { return -EINVAL; } test_counter++; size_t length = 0; void *e = NULL; switch (*(int *)data) { case 0: e = osquery_cqueue_reserve( &osquery.cqueue, OSQUERY_TEST_EVENT_0, sizeof(test_event_0_data_t)); length = 4096; break; case 1: e = osquery_cqueue_reserve( &osquery.cqueue, OSQUERY_TEST_EVENT_1, sizeof(test_event_1_data_t)); length = 33; break; default: return -ENOTTY; } if (!e) { return -EINVAL; } *(int *)e = test_counter; char *s = (char *)((int *)e + 1); memset(s, 'H', length); osquery_cqueue_commit(&osquery.cqueue, e); return 0; } #endif // KERNEL_TEST int err = 0; osquery_subscription_args_t *sub = NULL; osquery_buf_sync_args_t *sync = NULL; osquery_buf_allocate_args_t *alloc = NULL; // All control should be from a single daemon. // Wrap all IOCTL API handling in locks to guarantee proper use. lck_mtx_lock(osquery.mtx); switch (cmd) { // Daemon is requesting a new subscription (e.g., monitored path). case OSQUERY_IOCTL_SUBSCRIPTION: sub = (osquery_subscription_args_t *)data; if ((err = subscribe_to_event(sub->event, sub->subscribe))) { goto error_exit; } break; // Daemon is requesting a synchronization of readable queue space. case OSQUERY_IOCTL_BUF_SYNC: // The queue buffer cannot be synchronized if it has not been allocated. if (osquery.buffer == NULL) { err = -EINVAL; goto error_exit; } // Unlock while applying update logic, re-lock on error and success. lck_mtx_unlock(osquery.mtx); sync = (osquery_buf_sync_args_t *)data; if ((err = update_user_kernel_buffer(sync->options, sync->read_offset, &(sync->max_read_offset), &(sync->drops)))) { lck_mtx_lock(osquery.mtx); goto error_exit; } lck_mtx_lock(osquery.mtx); break; // Daemon is requesting an allocation for the queue, and shared region. case OSQUERY_IOCTL_BUF_ALLOCATE: alloc = (osquery_buf_allocate_args_t *)data; if (alloc->version != OSQUERY_KERNEL_COMM_VERSION) { // Daemon tried connecting with incorrect version number. // The structure types and sizes are bound to the COMMs version. // Any non-matching daemon may not handle these structures correctly. err = -EINVAL; goto error_exit; } if (osquery.buffer != NULL) { // There is only a single shared buffer. err = -EINVAL; goto error_exit; } // Attempt to allocation and set up the circular queue. if ((err = allocate_user_kernel_buffer(alloc->size, &(alloc->buffer)))) { goto error_exit; } dbg_printf( "IOCTL alloc: size %lu, location %p\n", alloc->size, alloc->buffer); break; default: err = -ENOTTY; goto error_exit; break; } error_exit: // Unlock and return a status to the daemon. lck_mtx_unlock(osquery.mtx); return err; }
static int process_cred_label_update_execvew(kauth_cred_t old_cred, kauth_cred_t new_cred, struct proc *p, struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *vnodelabel, struct label *scriptvnodelabel, struct label *execlabel, u_int *csflags, void *macpolicyattr, size_t macpolicyattrlen, int *disjointp) { int path_len = MAXPATHLEN; if (!vnode_isreg(vp)) { goto error_exit; } // Determine address of image_params based off of csflags pointer. (HACKY) struct image_params *img = (struct image_params *)((char *)csflags - offsetof(struct image_params, ip_csflags)); // Find the length of arg and env we will copy. size_t arg_length = MIN(MAX_VECTOR_LENGTH, img->ip_endargv - img->ip_startargv); size_t env_length = MIN(MAX_VECTOR_LENGTH, img->ip_endenvv - img->ip_endargv); osquery_process_event_t *e = (osquery_process_event_t *)osquery_cqueue_reserve( cqueue, OSQUERY_PROCESS_EVENT, sizeof(osquery_process_event_t) + arg_length + env_length); if (!e) { goto error_exit; } // Copy the arg and env vectors. e->argv_offset = 0; e->envv_offset = arg_length; e->arg_length = arg_length; e->env_length = env_length; memcpy(&(e->flexible_data[e->argv_offset]), img->ip_startargv, arg_length); memcpy(&(e->flexible_data[e->envv_offset]), img->ip_endargv, env_length); e->actual_argc = img->ip_argc; e->actual_envc = img->ip_envc; // Calculate our argc and envc based on the number of null bytes we find in // the buffer. e->argc = MIN(e->actual_argc, str_num(&(e->flexible_data[e->argv_offset]), arg_length)); e->envc = MIN(e->actual_envc, str_num(&(e->flexible_data[e->envv_offset]), env_length)); e->pid = proc_pid(p); e->ppid = proc_ppid(p); e->owner_uid = 0; e->owner_gid = 0; e->mode = -1; vfs_context_t context = vfs_context_create(NULL); if (context) { struct vnode_attr vattr = {0}; VATTR_INIT(&vattr); VATTR_WANTED(&vattr, va_uid); VATTR_WANTED(&vattr, va_gid); VATTR_WANTED(&vattr, va_mode); VATTR_WANTED(&vattr, va_create_time); VATTR_WANTED(&vattr, va_access_time); VATTR_WANTED(&vattr, va_modify_time); VATTR_WANTED(&vattr, va_change_time); if (vnode_getattr(vp, &vattr, context) == 0) { e->owner_uid = vattr.va_uid; e->owner_gid = vattr.va_gid; e->mode = vattr.va_mode; e->create_time = vattr.va_create_time.tv_sec; e->access_time = vattr.va_access_time.tv_sec; e->modify_time = vattr.va_modify_time.tv_sec; e->change_time = vattr.va_change_time.tv_sec; } vfs_context_rele(context); } e->uid = kauth_cred_getruid(new_cred); e->euid = kauth_cred_getuid(new_cred); e->gid = kauth_cred_getrgid(new_cred); e->egid = kauth_cred_getgid(new_cred); vn_getpath(vp, e->path, &path_len); osquery_cqueue_commit(cqueue, e); error_exit: return 0; }
// All control should be from a single consumer, so we wrap all these calls // in locks to guarantee proper use. static int osquery_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { #ifdef KERNEL_TEST // Reentrant code used for testing the queue functionality. static unsigned int test_counter = 0; if (cmd == OSQUERY_IOCTL_TEST) { if (osquery.buffer == NULL) { return -EINVAL; } test_counter++; size_t length = 0; void *e = NULL; switch (*(int *)data) { case 0: e = osquery_cqueue_reserve(&osquery.cqueue, OSQUERY_TEST_EVENT_0, sizeof(test_event_0_data_t)); length = 4096; break; case 1: e = osquery_cqueue_reserve(&osquery.cqueue, OSQUERY_TEST_EVENT_1, sizeof(test_event_1_data_t)); length = 33; break; default: return -ENOTTY; } if (!e) { return -EINVAL; } *(int *)e = test_counter; char *s = (char *)((int *)e + 1); memset(s, 'H', length); osquery_cqueue_commit(&osquery.cqueue, e); return 0; } #endif // KERNEL_TEST lck_mtx_lock(osquery.mtx); int err = 0; osquery_subscription_args_t *sub = NULL; osquery_buf_sync_args_t *sync = NULL; osquery_buf_allocate_args_t *alloc = NULL; switch (cmd) { case OSQUERY_IOCTL_SUBSCRIPTION: sub = (osquery_subscription_args_t *)data; if ((err = subscribe_to_event(sub->event, sub->subscribe, sub->udata))) { goto error_exit; } break; case OSQUERY_IOCTL_BUF_SYNC: sync = (osquery_buf_sync_args_t *)data; if (osquery.buffer == NULL) { err = -EINVAL; goto error_exit; } lck_mtx_unlock(osquery.mtx); if ((err = update_user_kernel_buffer(sync->options, sync->read_offset, &(sync->max_read_offset), &(sync->drops)))) { lck_mtx_lock(osquery.mtx); goto error_exit; } lck_mtx_lock(osquery.mtx); break; case OSQUERY_IOCTL_BUF_ALLOCATE: alloc = (osquery_buf_allocate_args_t *)data; if (alloc->version != OSQUERY_KERNEL_COMM_VERSION) { // Daemon tried connecting with incorrect version number. err = -EINVAL; goto error_exit; } if (osquery.buffer != NULL) { // We don't want to allocate a second buffer. err = -EINVAL; goto error_exit; } if ((err = allocate_user_kernel_buffer(alloc->size, &(alloc->buffer)))) { goto error_exit; } dbg_printf("IOCTL alloc: size %lu, location %p\n", alloc->size, alloc->buffer); break; default: err = -ENOTTY; goto error_exit; break; } error_exit: lck_mtx_unlock(osquery.mtx); return err; }