Exemple #1
0
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;
}
Exemple #2
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;
}