示例#1
0
static int EscalateTestMockHelperProcess(int stdin_fd, int stdout_fd) {
  GIOChannel *stdin_stream = g_io_channel_unix_new(stdin_fd);
  GIOChannel *stdout_stream = g_io_channel_unix_new(stdout_fd);
  GError *error = NULL;
  gboolean success = FALSE;

  g_assert(mock_helper_expected_messages);
  g_assert(mock_helper_response_messages);

  for (guint i = 0; i < g_strv_length(mock_helper_expected_messages); i++) {
    EscalateMessage *expected = NULL;
    EscalateMessage *message = NULL;
    EscalateMessage *response = NULL;

    expected = EscalateMessageLoad(mock_helper_expected_messages[i], &error);
    g_assert_no_error(error);
    g_assert(expected);

    message = EscalateMessageRead(stdin_stream, &error);
    g_assert_no_error(error);
    g_assert(message);

    if (mock_helper_response_messages[i]) {
      response = EscalateMessageLoad(mock_helper_response_messages[i], &error);
      g_assert_no_error(error);
      g_assert(response);
    }

    g_assert_cmpint(expected->type, ==, message->type);
    if (!g_variant_equal(expected->values, message->values)) {
      gchar *variant_str = g_variant_print(expected->values, FALSE);
      g_message("Expected: %s", variant_str);
      g_free(variant_str);
      variant_str = g_variant_print(message->values, FALSE);
      g_message("Got: %s", variant_str);
      g_free(variant_str);
      g_error("Message values didn't match what was expected");
    }

    if (response) {
      success = EscalateMessageWrite(response, stdout_stream, &error);
      g_assert_no_error(error);
      g_assert(success);
      EscalateMessageUnref(response);
    }

    EscalateMessageUnref(expected);
    EscalateMessageUnref(message);
  }

  g_io_channel_shutdown(stdin_stream, FALSE, NULL);
  g_io_channel_shutdown(stdout_stream, FALSE, NULL);
  g_io_channel_unref(stdin_stream);
  g_io_channel_unref(stdout_stream);
  return 0;
}
static void AssertMessage(GIOChannel *channel, const gchar *expected_str) {
  GError *error = NULL;
  EscalateMessage *expected = EscalateMessageLoad(expected_str, &error);
  g_assert_no_error(error);
  g_assert(expected);
  EscalateMessage *message = EscalateMessageRead(channel, &error);
  g_assert_no_error(error);
  g_assert(message);
  g_assert_cmpint(expected->type, ==, message->type);
  g_assert(g_variant_equal(expected->values, message->values));
  EscalateMessageUnref(expected);
  EscalateMessageUnref(message);
}
/**
 * EscalateHelperPrompt:
 * @self: #EscalateHelper instance.
 * @conv_request: Conversation message to send.
 * @conv_response: Conversation response contents received.
 *
 * Returns: PAM_SUCCESS if one message was sent and response was read, or
 * PAM_CONV_ERROR on failure. Error are logged with pam_syslog.
 */
static int EscalateHelperPrompt(EscalateHelper *self,
                                const struct pam_message *conv_request,
                                struct pam_response *conv_response) {
  EscalateMessage *request = NULL;
  GError *error = NULL;
  EscalateMessage *response = NULL;
  gchar *response_msg = NULL;
  int response_retcode = 0;
  int result = PAM_CONV_ERR;

  request = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_CONV_MESSAGE,
                               conv_request->msg_style, conv_request->msg);

  if (!EscalateMessageWrite(request, self->writer, &error)) {
    pam_syslog(self->pamh, LOG_WARNING,
               "Failed to write conversation request: %s", error->message);
    g_clear_error(&error);
    goto done;
  }

  response = EscalateHelperRecv(self, ESCALATE_MESSAGE_TYPE_CONV_RESPONSE,
                                &error);
  if (!response) {
    pam_syslog(self->pamh, LOG_WARNING,
               "Failed to read conversation response: %s", error->message);
    g_clear_error(&error);
    goto done;
  }

  EscalateMessageGetValues(response, &response_msg, &response_retcode);

  if (response_msg) {
    conv_response->resp = strdup(response_msg);
  } else {
    conv_response->resp = NULL;
  }

  conv_response->resp_retcode = response_retcode;
  result = PAM_SUCCESS;

done:
  if (request)
    EscalateMessageUnref(request);
  if (response)
    EscalateMessageUnref(response);
  g_free(response_msg);
  return result;
}
/**
 * EscalateModuleHandleNext:
 * @self: PAM module that's receiving messages from the helper process.
 * @error: (out)(allow-none): Error return location or #NULL.
 *
 * Returns: #TRUE if a message was read and handled without an error.
 */
gboolean EscalateModuleHandleNext(EscalateModule *self, GError **error) {
  EscalateMessage *message = NULL;
  gboolean result = FALSE;

  message = EscalateSubprocessRecv(self->child, error);
  if (!message)
    goto done;

  switch (EscalateMessageGetType(message)) {
    case ESCALATE_MESSAGE_TYPE_CONV_MESSAGE:
      result = EscalateModuleHandleConversation(self, message, error);
      break;
    case ESCALATE_MESSAGE_TYPE_FINISH:
      result = EscalateModuleHandleFinish(self, message, error);
      break;
    default:
      g_set_error(error, ESCALATE_MODULE_ERROR,
                  ESCALATE_MODULE_ERROR_MESSAGE_TYPE,
                  "Unexpected message type: %d",
                  EscalateMessageGetType(message));
      break;
  }

done:
  if (message)
    EscalateMessageUnref(message);
  return result;
}
/**
 * EscalateModuleStart:
 * @self: PAM module being described in the start message.
 * @action: Action being called on this module, like
 * #ESCALATE_MESSAGE_ACTION_AUTHENTICATE when pam_sm_authenticate() is being
 * used.
 * @error: (out)(allow-none): Error return location or #NULL.
 *
 * Returns: #TRUE if the message was sent.
 */
gboolean EscalateModuleStart(EscalateModule *self, EscalateMessageAction action,
                             GError **error) {
  GVariantBuilder *items = NULL;
  EscalateMessage *message = NULL;
  gboolean result = FALSE;

  items = g_variant_builder_new(G_VARIANT_TYPE("a{ims}"));
  for (guint i = 0; i < G_N_ELEMENTS(escalate_module_include_items); i++) {
    gint item = escalate_module_include_items[i];
    if (!EscalateModuleStartAddItem(self, items, item, error)) {
      goto done;
    }
  }

  // TODO(vonhollen): Include environment variables?
  message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_START, action, self->flags,
                               self->username, items);
  if (EscalateSubprocessSend(self->child, message, error))
    result = TRUE;

done:
  if (message)
    EscalateMessageUnref(message);
  g_variant_builder_unref(items);
  return result;
}
static void TestWrite(gconstpointer user_data) {
  guint index = GPOINTER_TO_UINT(user_data);
  gint fds [] = { 0, 0 };
  GIOChannel *reader = NULL;
  GIOChannel *writer = NULL;
  GThread *thread = NULL;
  EscalateMessage *message = NULL;
  GError *error = NULL;
  gboolean success = FALSE;
  gchar *result = NULL;

  g_assert(g_unix_open_pipe(fds, 0, NULL));
  reader = g_io_channel_unix_new(fds[0]);
  writer = g_io_channel_unix_new(fds[1]);

  thread = g_thread_new("Reader", (GThreadFunc) ReaderThread, reader);

  message = EscalateMessageLoad(example_messages[index], NULL);
  g_assert(message);
  success = EscalateMessageWrite(message, writer, &error);
  g_assert_no_error(error);
  g_assert(success);

  g_assert_cmpint(G_IO_STATUS_NORMAL, ==,
                  g_io_channel_shutdown(writer, TRUE, NULL));

  result = (gchar *) g_thread_join(thread);
  result[strlen(result)-1] = '\0';  // Cut off newline.
  g_assert_cmpstr(example_messages[index], ==, result);

  g_io_channel_unref(reader);
  g_io_channel_unref(writer);
  EscalateMessageUnref(message);
  g_free(result);
}
static void WriteMessage(GIOChannel *channel, const gchar *message_str) {
  GError *error = NULL;
  EscalateMessage *message = EscalateMessageLoad(message_str, &error);
  g_assert_no_error(error);
  g_assert(message);
  gboolean success = EscalateMessageWrite(message, channel, &error);
  g_assert_no_error(error);
  g_assert(success);
  EscalateMessageUnref(message);
}
static void TestDump(gconstpointer user_data) {
  guint index = GPOINTER_TO_UINT(user_data);
  GError *error = NULL;
  EscalateMessage *message = EscalateMessageLoad(example_messages[index],
                                                 &error);
  g_assert_no_error(error);
  g_assert(message);

  gchar *message_str = EscalateMessageDump(message);
  g_assert_cmpstr(example_messages[index], ==, message_str);

  g_free(message_str);
  EscalateMessageUnref(message);
}
static void TestLoad(gconstpointer user_data) {
  guint index = GPOINTER_TO_UINT(user_data);
  GError *error = NULL;
  GVariant *expected_values = CreateExampleMessageValues(index);

  EscalateMessage *message = EscalateMessageLoad(example_messages[index],
                                                 &error);
  g_assert_no_error(error);
  g_assert(message);
  g_assert_cmpint(EscalateMessageGetType(message), ==,
                  example_message_types[index]);
  g_assert(g_variant_equal(expected_values, message->values));

  g_variant_unref(expected_values);
  EscalateMessageUnref(message);
}
示例#10
0
/**
 * EscalateHelperRecv:
 * @self: #EscalateHelper instance.
 * @type: Only accept a message with this type.
 * @error: (out)(allow-none): Error return location or #NULL.
 *
 * Returns: New #EscalateMessage instance, or #NULL on error.
 */
static EscalateMessage *EscalateHelperRecv(EscalateHelper *self,
                                           EscalateMessageType type,
                                           GError **error) {
  EscalateMessage *message = EscalateMessageRead(self->reader, error);
  if (!message)
    return NULL;

  if (EscalateMessageGetType(message) == type)
    return message;

  g_set_error(error, ESCALATE_HELPER_ERROR,
              ESCALATE_HELPER_ERROR_UNEXPECTED_MESSAGE_TYPE,
              "Expected message type %d but got %d instead", type,
              EscalateMessageGetType(message));
  EscalateMessageUnref(message);
  return NULL;
}
示例#11
0
/**
 * EscalateHelperDoAction:
 * @self: #EscalateHelper instance.
 * @error: (out)(allow-none): Error return location or #NULL.
 *
 * Return: #TRUE if the action specified in the start message (just
 * pam_authenticate for now) was called successfully and the finish message was
 * sent.
 */
gboolean EscalateHelperDoAction(EscalateHelper *self, GError **error) {
  int setcred_result = PAM_SUCCESS;
  EscalateMessage *message = NULL;
  GVariantBuilder *env = NULL;
  gboolean result = FALSE;

  // Run the action specified in the start message.
  switch (self->action) {
    case ESCALATE_MESSAGE_ACTION_AUTHENTICATE:
      self->result = pam_authenticate(self->pamh, self->flags);
      if (self->result == PAM_SUCCESS || self->result == PAM_NEW_AUTHTOK_REQD) {
        // Refresh things like Kerberos credentials. This is safe to do here
        // even if the client never calls pam_setcred() because the entire auth
        // stack succeeded.
        // TODO(vonhollen): Make this configurable by pam_escalate.so.
        setcred_result = pam_setcred(self->pamh, PAM_REINITIALIZE_CRED);
        if (setcred_result != PAM_SUCCESS) {
          pam_syslog(self->pamh, LOG_NOTICE,
                     "pam_setcred() failed for user '%s': %s",
                     self->username, pam_strerror(self->pamh, setcred_result));
        }
      }
      break;
    default:
      self->result = PAM_SYSTEM_ERR;
      g_error("Unsupported action %d", self->action);
  }

  // Prevent this function from being run twice.
  self->action = ESCALATE_MESSAGE_ACTION_UNKNOWN;

  // Get the PAM environment to include in the result.
  env = EscalateUtilPamEnvToVariant(self->pamh, error);
  if (!env) {
    goto done;
  }

  // Send the final PAM result for the action and the complete environment.
  message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, self->result, env);
  result = EscalateMessageWrite(message, self->writer, error);
  EscalateMessageUnref(message);
  g_variant_builder_unref(env);

done:
  return result;
}
示例#12
0
/**
 * EscalateModuleHandleConversation:
 * @self: PAM module to forward conversation messages to.
 * @message: Conversation message from the helper process holding the same
 * values as one "struct pam_message".
 * @error: (out)(allow-none): Error return location or #NULL.
 *
 * Returns: #TRUE if a response message was sent back to the helper process.
 */
static gboolean EscalateModuleHandleConversation(EscalateModule *self,
                                                 EscalateMessage *message,
                                                 GError **error) {
  gchar *conv_message_str = NULL;
  struct pam_message conv_message = { 0, NULL };
  const struct pam_message *conv_message_array [] = { &conv_message, NULL };
  struct pam_response *conv_response = NULL;
  gint pam_status = PAM_SYSTEM_ERR;
  EscalateMessage *response = NULL;
  gboolean result = FALSE;

  EscalateMessageGetValues(message, &conv_message.msg_style, &conv_message_str);
  conv_message.msg = conv_message_str;

  // TODO(vonhollen): Support multiple requests/responses per call.
  pam_status = self->conv->conv(1, conv_message_array, &conv_response,
                                self->conv->appdata_ptr);
  if (pam_status != PAM_SUCCESS) {
    g_set_error(error, ESCALATE_MODULE_ERROR, ESCALATE_MODULE_ERROR_CONV,
                "Conversation function failed: %s",
                pam_strerror(self->pamh, pam_status));
    goto done;
  }

  response = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_CONV_RESPONSE,
                                conv_response[0].resp,
                                conv_response[0].resp_retcode);
  if (EscalateSubprocessSend(self->child, response, error))
    result = TRUE;

done:
  g_free(conv_message_str);
  if (conv_response) {
    free(conv_response[0].resp);
    free(conv_response);
  }
  if (response) {
    EscalateMessageUnref(response);
  }
  return result;
}
static void TestNew(gconstpointer user_data) {
  guint index = GPOINTER_TO_UINT(user_data);
  GVariantBuilder items;
  GVariantBuilder env;
  EscalateMessage *message = NULL;
  GVariant *expected_values = CreateExampleMessageValues(index);

  switch (index) {
    case 0:
      g_variant_builder_init(&items, G_VARIANT_TYPE_ARRAY);
      g_variant_builder_add(&items, "{ims}", 2, "testuser");
      g_variant_builder_init(&env, G_VARIANT_TYPE_ARRAY);
      g_variant_builder_add(&env, "{ss}", "PATH", "/path");
      message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_START, 1, 0,
                                   "testuser", &items, &env);
      break;
    case 1:
      message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_CONV_MESSAGE, 1,
                                   "Password: "******"testpass", 0);
      break;
    case 3:
      g_variant_builder_init(&env, G_VARIANT_TYPE_ARRAY);
      g_variant_builder_add(&env, "{ss}", "PATH", "/path");
      message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, 0, &env);
      break;
    default:
      g_error("No message available for index %d", index);
  }

  g_assert(message);
  g_assert_cmpint(EscalateMessageGetType(message), ==,
                  example_message_types[index]);
  g_assert(g_variant_equal(expected_values, message->values));

  EscalateMessageUnref(message);
  g_variant_unref(expected_values);
}
示例#14
0
/**
 * EscalateHelperDoAction:
 * @self: #EscalateHelper instance.
 * @error: (out)(allow-none): Error return location or #NULL.
 *
 * Return: #TRUE if the action specified in the start message (just
 * pam_authenticate for now) was called successfully and the finish message was
 * sent.
 */
gboolean EscalateHelperDoAction(EscalateHelper *self, GError **error) {
  EscalateMessage *message = NULL;
  gboolean success = FALSE;

  switch (self->action) {
    case ESCALATE_MESSAGE_ACTION_AUTHENTICATE:
      self->result = pam_authenticate(self->pamh, self->flags);
      break;
    default:
      self->result = PAM_SYSTEM_ERR;
      g_error("Unsupported action %d", self->action);
  }

  // Prevents this function from being run twice.
  self->action = ESCALATE_MESSAGE_ACTION_UNKNOWN;

  message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, self->result);
  success = EscalateMessageWrite(message, self->writer, error);
  EscalateMessageUnref(message);
  return success;
}
static void TestRead(gconstpointer user_data) {
  guint index = GPOINTER_TO_UINT(user_data);
  gint fds [] = { 0, 0 };
  GIOChannel *reader = NULL;
  WriterThreadContext thread_ctx = { NULL, NULL };
  GThread *thread = NULL;
  GError *error = NULL;
  EscalateMessage *message = NULL;
  GVariant *expected_values = CreateExampleMessageValues(index);

  g_assert(g_unix_open_pipe(fds, 0, NULL));
  reader = g_io_channel_unix_new(fds[0]);

  thread_ctx.channel = g_io_channel_unix_new(fds[1]);
  thread_ctx.contents = example_messages[index];
  thread = g_thread_new("Writer", (GThreadFunc) WriterThread, &thread_ctx);
  g_assert(thread);

  message = EscalateMessageRead(reader, &error);
  g_assert_no_error(error);
  g_assert(message);
  g_assert_cmpint(EscalateMessageGetType(message), ==,
                  example_message_types[index]);
  g_assert(g_variant_equal(expected_values, message->values));
  EscalateMessageUnref(message);

  message = EscalateMessageRead(reader, &error);
  g_assert_error(error, ESCALATE_MESSAGE_ERROR, ESCALATE_MESSAGE_ERROR_EOF);
  g_assert(!message);
  g_error_free(error);

  g_thread_join(thread);
  g_variant_unref(expected_values);
  g_io_channel_shutdown(reader, FALSE, NULL);
  g_io_channel_unref(reader);
  g_io_channel_unref(thread_ctx.channel);
}
static void TestGetValues(gconstpointer user_data) {
  guint index = GPOINTER_TO_UINT(user_data);
  EscalateMessage *message = NULL;
  gint action = 0;
  gint flags = 0;
  gchar *username = NULL;
  gchar *value = NULL;
  gint item_key = 0;
  gchar *item_value = NULL;
  GVariantIter *items_iter = NULL;
  gchar *env_key = NULL;
  gchar *env_value = NULL;
  GVariantIter *env_iter = NULL;

  message = EscalateMessageLoad(example_messages[index], NULL);
  g_assert(message);

  switch (index) {
    case 0:
      EscalateMessageGetValues(message, &action, &flags, &username, &items_iter,
                               &env_iter);
      g_assert_cmpint(1, ==, action);
      g_assert_cmpint(0, ==, flags);
      g_assert_cmpstr("testuser", ==, username);
      g_assert(g_variant_iter_next(items_iter, "{ims}", &item_key,
                                   &item_value));
      g_assert_cmpint(2, ==, item_key);
      g_assert_cmpstr("testuser", ==, item_value);
      g_assert(!g_variant_iter_next(items_iter, "{ims}", NULL, NULL));
      g_variant_iter_free(items_iter);
      g_assert(g_variant_iter_next(env_iter, "{ss}", &env_key, &env_value));
      g_assert_cmpstr("PATH", ==, env_key);
      g_assert_cmpstr("/path", ==, env_value);
      g_assert(!g_variant_iter_next(env_iter, "{ss}", NULL, NULL));
      g_variant_iter_free(env_iter);
      break;
    case 1:
      EscalateMessageGetValues(message, &flags, &value);
      g_assert_cmpint(1, ==, flags);
      g_assert_cmpstr("Password: "******"testpass", ==, value);
      g_assert_cmpint(0, ==, flags);
      break;
    case 3:
      EscalateMessageGetValues(message, &flags, &env_iter);
      g_assert_cmpint(0, ==, flags);
      g_assert(g_variant_iter_next(env_iter, "{ss}", &env_key, &env_value));
      g_assert_cmpstr("PATH", ==, env_key);
      g_assert_cmpstr("/path", ==, env_value);
      g_assert(!g_variant_iter_next(env_iter, "{ss}", NULL, NULL));
      g_variant_iter_free(env_iter);
      break;
    default:
      g_error("No message available for index %d", index);
  }

  g_free(username);
  g_free(value);
  g_free(item_value);
  g_free(env_key);
  g_free(env_value);
  EscalateMessageUnref(message);
}
示例#17
0
/**
 * EscalateHelperHandleStart:
 * @self: #EscalateHelper instance.
 * @error: (out)(allow-none): Error return location or #NULL.
 *
 * Return: #TRUE if pam_start() was called and it's OK to call
 * EscalateHelperDoAction(). #FALSE if there was an error and the finish message
 * was sent.
 */
gboolean EscalateHelperHandleStart(EscalateHelper *self, GError **error) {
  EscalateMessage *message = NULL;
  EscalateMessage *response = NULL;
  GVariantIter *items = NULL;
  GVariantIter *env = NULL;
  int pam_status = PAM_SYSTEM_ERR;
  int item_type = -1;
  const gchar *item_value = NULL;
  gboolean result = FALSE;

  // Support EscalateHelperHandleStart() being called multiple times.
  self->action = ESCALATE_MESSAGE_ACTION_UNKNOWN;
  self->flags = 0;
  g_free(self->username);
  self->username = NULL;
  self->result = PAM_SYSTEM_ERR;

  message = EscalateHelperRecv(self, ESCALATE_MESSAGE_TYPE_START, error);
  if (!message)
    goto done;

  EscalateMessageGetValues(message, &self->action, &self->flags,
                           &self->username, &items, &env);

  if (!EscalateHelperIsUserAllowed(self, error))
    goto done;

  // TODO(vonhollen): Safely allow calls to multiple services.
  pam_status = pam_start(ESCALATE_SERVICE_NAME, self->username, &self->conv,
                         &self->pamh);
  if (pam_status != PAM_SUCCESS) {
    g_set_error(error, ESCALATE_HELPER_ERROR,
                ESCALATE_HELPER_ERROR_START_FAILED,
                "Failed to start PAM session: %s",
                pam_strerror(self->pamh, pam_status));
    goto done;
  }

  while (g_variant_iter_loop(items, "{im&s}", &item_type, &item_value)) {
    if (!EscalateHelperIsSafeItem(item_type)) {
      g_set_error(error, ESCALATE_HELPER_ERROR,
                  ESCALATE_HELPER_ERROR_UNSUPPORTED_ITEM,
                  "Item type %d is not supported", item_type);
      goto done;
    }
    pam_status = pam_set_item(self->pamh, item_type, item_value);
    if (pam_status != PAM_SUCCESS) {
      g_set_error(error, ESCALATE_HELPER_ERROR,
                  ESCALATE_HELPER_ERROR_SET_ITEM_FAILED,
                  "Failed to set item type %d to '%s'", item_type, item_value);
      goto done;
    }
  }

  result = EscalateUtilPamEnvFromVariant(self->pamh, env, error);

done:
  if (!result) {
    response = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, PAM_SYSTEM_ERR,
                                  NULL);
    EscalateMessageWrite(response, self->writer, NULL);
    EscalateMessageUnref(response);
  }

  if (items)
    g_variant_iter_free(items);
  if (env)
    g_variant_iter_free(env);
  if (message)
    EscalateMessageUnref(message);
  return result;
}