Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ext_components/cp0_lvgl/include/cp0_lvgl_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ int cp0_sudo_run_argv_async_ex(const char *const *argv,
/* Cancellation is idempotent while a request ID remains in the bounded recent
* completion history. Returns -ENOENT only for an unknown or expired ID. */
int cp0_sudo_cancel(uint64_t request_id);
int cp0_sudo_queue_password(const char *password);
int cp0_file_read_first_line(const char *path, char *out, int out_size);
int cp0_desktop_exec_is_safe(const char *exec, char *reason, int reason_size);
int cp0_network_default_info_read(cp0_eth_info_t *info);
Expand Down
1 change: 1 addition & 0 deletions ext_components/cp0_lvgl/include/keyboard_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extern volatile uint32_t LV_EVENT_KEYBOARD;

void *keyboard_read_thread(void *argv);
int cp0_keyboard_inject(uint32_t key_code, int key_state, uint32_t mods);
int cp0_keyboard_inject_text(const char *utf8);
const char *kbd_state_name(int state);
void kbd_dump_keymap_table(void);
#ifdef __cplusplus
Expand Down
26 changes: 26 additions & 0 deletions ext_components/cp0_lvgl/src/cp0/cp0_lvgl_keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,32 @@ int cp0_keyboard_inject(uint32_t key_code, int key_state, uint32_t mods)
return 0;
}

int cp0_keyboard_inject_text(const char *utf8)
{
if (!utf8)
return -1;

const unsigned char *cursor = (const unsigned char *)utf8;
while (*cursor) {
size_t length = 1;
if ((*cursor & 0xe0) == 0xc0) length = 2;
else if ((*cursor & 0xf0) == 0xe0) length = 3;
else if ((*cursor & 0xf8) == 0xf0) length = 4;

for (size_t i = 1; i < length; ++i)
if (!cursor[i] || (cursor[i] & 0xc0) != 0x80)
return -1;

struct key_item item = {0};
item.key_state = KBD_KEY_RELEASED;
memcpy(item.utf8, cursor, length);
snprintf(item.sym_name, sizeof(item.sym_name), "RPC_TEXT");
enqueue_key(&item);
cursor += length;
}
return 0;
}

/* ============================================================
* Key repeat control
* ============================================================ */
Expand Down
40 changes: 40 additions & 0 deletions ext_components/cp0_lvgl/src/cp0/cp0_lvgl_rpc.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "cp0_lvgl.h"
#include "cp0_lvgl_app.h"

#ifndef CP0_LVGL_USE_ZMQ_RPC
#define CP0_LVGL_USE_ZMQ_RPC 0
Expand Down Expand Up @@ -47,6 +48,13 @@ const char *env_or_default(const char *name, const char *fallback)
return value && value[0] ? value : fallback;
}

void secure_clear(std::string &value)
{
volatile char *data = value.empty() ? nullptr : &value[0];
for (size_t i = 0; data && i < value.size(); ++i) data[i] = 0;
value.clear();
}

bool capture_ppm(std::vector<uint8_t> &out, std::string &error)
{
const char *device = env_or_default("APPLAUNCH_LINUX_FBDEV_DEVICE", "/dev/fb0");
Expand Down Expand Up @@ -186,6 +194,38 @@ void rpc_broker(std::shared_ptr<zmq::context_t> context)
send_text(rep, "OK key");
continue;
}
if (command == "text") {
std::string value;
std::getline(input, value);
if (!value.empty() && value.front() == ' ') value.erase(0, 1);
if (value.empty()) {
send_text(rep, "ERR usage: text <utf8>");
continue;
}
if (cp0_keyboard_inject_text(value.c_str()) != 0) {
send_text(rep, "ERR invalid utf8 text");
continue;
}
send_text(rep, "OK text");
continue;
}
if (command == "password") {
std::string value;
std::getline(input, value);
if (!value.empty() && value.front() == ' ') value.erase(0, 1);
if (value.empty()) {
send_text(rep, "ERR usage: password <value>");
continue;
}
const int result = cp0_sudo_queue_password(value.c_str());
secure_clear(value);
if (result != 0) {
send_text(rep, "ERR unable to queue password " + std::to_string(result));
continue;
}
send_text(rep, "OK password");
continue;
}
if (command == "screenshot") {
std::vector<uint8_t> ppm;
std::string error;
Expand Down
78 changes: 72 additions & 6 deletions ext_components/cp0_lvgl/src/cp0/cp0_lvgl_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ struct ExtPortGpioMap {
static constexpr const char *kHwIdPath = "/proc/cardputerzero_hw_id";
static constexpr ExtPortGpioMap kCardputerZeroGpioMap = {"/dev/gpiochip1", 3, 12, false, false};
static constexpr ExtPortGpioMap kFallbackGpioMap = {"/dev/gpiochip0", 17, 5, false, true};
static constexpr const char *kGrove5vLedPaths[] = {
"/sys/class/leds/grove_5v_out/brightness",
};
static constexpr const char *kExt5vLedPaths[] = {
"/sys/class/leds/ext_5v_out/brightness",
};

static int gpio_v2_get_value(int line_fd);

Expand Down Expand Up @@ -217,22 +223,40 @@ class SettingsSystem
int set_named_gpio(const char *name, int val)
{
std::lock_guard<std::mutex> lock(gpio_mutex_);
const ExtPortGpioMap &map = active_gpio_map_locked();
if (is_grove5v_name(name))
if (is_grove5v_name(name)) {
int ret = write_led_value(kGrove5vLedPaths, val);
if (ret != -ENOENT)
return ret;
const ExtPortGpioMap &map = active_gpio_map_locked();
return set_gpio_value(map.chip_path, map.grove5v_line, map.grove5v_active_low, "GROVE5V", val);
if (is_ext5v_name(name))
}
if (is_ext5v_name(name)) {
int ret = write_led_value(kExt5vLedPaths, val);
if (ret != -ENOENT)
return ret;
const ExtPortGpioMap &map = active_gpio_map_locked();
return set_gpio_value(map.chip_path, map.ext5v_line, map.ext5v_active_low, "EXT5V", val);
}
return -EINVAL;
}

int get_named_gpio(const char *name)
{
std::lock_guard<std::mutex> lock(gpio_mutex_);
const ExtPortGpioMap &map = active_gpio_map_locked();
if (is_grove5v_name(name))
if (is_grove5v_name(name)) {
int value = read_led_value(kGrove5vLedPaths);
if (value != -ENOENT)
return value;
const ExtPortGpioMap &map = active_gpio_map_locked();
return get_gpio_value(map.chip_path, map.grove5v_line, map.grove5v_active_low, "GROVE5V");
if (is_ext5v_name(name))
}
if (is_ext5v_name(name)) {
int value = read_led_value(kExt5vLedPaths);
if (value != -ENOENT)
return value;
const ExtPortGpioMap &map = active_gpio_map_locked();
return get_gpio_value(map.chip_path, map.ext5v_line, map.ext5v_active_low, "EXT5V");
}
return -EINVAL;
}

Expand Down Expand Up @@ -260,6 +284,48 @@ class SettingsSystem
return value < 0 ? value : (value ^ (active_low ? 1 : 0));
}

template <size_t N>
static int write_led_value(const char *const (&paths)[N], int val)
{
const char value = val ? '1' : '0';
for (const char *path : paths) {
int fd = open(path, O_WRONLY | O_CLOEXEC);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
ssize_t written = write(fd, &value, 1);
int err = written == 1 ? 0 : (written < 0 ? errno : EIO);
close(fd);
return -err;
}
return -ENOENT;
}

template <size_t N>
static int read_led_value(const char *const (&paths)[N])
{
for (const char *path : paths) {
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
char buf[16] = {};
ssize_t size = read(fd, buf, sizeof(buf) - 1);
int err = size >= 0 ? 0 : errno;
close(fd);
if (err != 0)
return -err;
if (size == 0)
return -EIO;
return std::strtol(buf, nullptr, 10) != 0 ? 1 : 0;
}
return -ENOENT;
}

static int config_get_int(const char *key, int default_val)
{
int val = default_val;
Expand Down
96 changes: 73 additions & 23 deletions ext_components/cp0_lvgl/src/cp0_sudo_async.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <cstring>
#include <iterator>
#include <memory>
#include <mutex>
#include <new>
#include <string>
#include <thread>
Expand Down Expand Up @@ -60,6 +61,9 @@ lv_group_t *g_prompt_group = nullptr;
lv_timer_t *g_timer = nullptr;
std::shared_ptr<Request> g_prompt_request;
std::string g_password;
std::mutex g_queued_password_mutex;
std::string g_queued_password;
int64_t g_queued_password_deadline_ms = 0;

int64_t now_ms()
{
Expand Down Expand Up @@ -142,6 +146,30 @@ bool authentication_error(const std::string &output)

void key_event_cb(lv_event_t *event);
void execute_actions(std::vector<Action> actions);
void submit_password();
void apply_queued_password(void *);

void clear_queued_password()
{
std::lock_guard<std::mutex> lock(g_queued_password_mutex);
secure_clear(g_queued_password);
g_queued_password_deadline_ms = 0;
}

bool take_queued_password(std::string &password)
{
std::lock_guard<std::mutex> lock(g_queued_password_mutex);
if (g_queued_password.empty()) return false;
if (g_queued_password_deadline_ms > 0 && now_ms() > g_queued_password_deadline_ms) {
secure_clear(g_queued_password);
g_queued_password_deadline_ms = 0;
return false;
}
password = std::move(g_queued_password);
g_queued_password.clear();
g_queued_password_deadline_ms = 0;
return true;
}

void destroy_prompt()
{
Expand All @@ -158,6 +186,7 @@ void destroy_prompt()
g_hint_label = nullptr;
g_prompt_request.reset();
secure_clear(g_password);
clear_queued_password();
}

void update_password_label()
Expand Down Expand Up @@ -217,6 +246,7 @@ void create_prompt(const std::shared_ptr<Request> &request)
lv_group_add_obj(g_prompt_group, g_overlay);
lv_group_set_default(g_prompt_group);
if (lv_indev_t *indev = lv_indev_get_next(nullptr)) lv_indev_set_group(indev, g_prompt_group);
lv_async_call(apply_queued_password, nullptr);
}

void show_auth_error(const std::shared_ptr<Request> &request)
Expand Down Expand Up @@ -301,41 +331,31 @@ void run_worker(std::shared_ptr<Request> request, std::string password)
if (!resolve_run_user(identity)) exit_code = -EPERM;
else {
std::string password_line = password + "\n";
int auth_timeout = request->auth_timeout_ms;
if (auth_timeout > 0)
auth_timeout = static_cast<int>(std::max<int64_t>(1, request->deadline_ms - now_ms()));
auto auth = cp0_runner::run({"sudo", "-k", "-S", "-p", "", "-v"},
&password_line, {}, &request->cancel_requested, auth_timeout,
kMaxCapturedOutputBytes, identity.uid, identity.gid, identity.name,
identity.home, identity.shell);
secure_clear(password_line);
if (auth.exit_code != 0) {
exit_code = auth.exit_code;
result = auth.exit_code == -ECANCELED ? CP0_SUDO_RESULT_CANCELLED :
auth.exit_code == -ETIMEDOUT ? CP0_SUDO_RESULT_TIMED_OUT :
authentication_error(auth.output) ? CP0_SUDO_RESULT_AUTH_FAILED :
CP0_SUDO_RESULT_EXEC_FAILED;
auto actions = g_coordinator.worker_auth_result(request->id, result, exit_code, now_ms());
secure_clear(password);
if (!actions.empty() && !post_actions(actions))
g_coordinator.requeue_actions(std::move(actions));
return;
}
std::vector<std::string> command;
if (request->use_login_shell)
command = {"sudo", "-n", "--", identity.shell, "-c", request->argv.front()};
command = {"sudo", "-k", "-S", "-p", "", "--", identity.shell, "-c",
request->argv.front()};
else {
command = {"sudo", "-n", "--"};
command = {"sudo", "-k", "-S", "-p", "", "--"};
command.insert(command.end(), request->argv.begin(), request->argv.end());
}
auto execution = cp0_runner::run(std::move(command), nullptr,
auto execution = cp0_runner::run(std::move(command), &password_line,
[request](const char *data, size_t size) { stream_output(request, data, size); },
&request->cancel_requested, request->exec_timeout_ms, kMaxCapturedOutputBytes,
identity.uid, identity.gid, identity.name, identity.home, identity.shell);
secure_clear(password_line);
exit_code = execution.exit_code;
result = exit_code == -ECANCELED ? CP0_SUDO_RESULT_CANCELLED :
exit_code == -ETIMEDOUT ? CP0_SUDO_RESULT_TIMED_OUT :
authentication_error(execution.output) ? CP0_SUDO_RESULT_AUTH_FAILED :
exit_code == 0 ? CP0_SUDO_RESULT_SUCCESS : CP0_SUDO_RESULT_EXEC_FAILED;
if (result == CP0_SUDO_RESULT_AUTH_FAILED) {
auto actions = g_coordinator.worker_auth_result(request->id, result, exit_code, now_ms());
secure_clear(password);
if (!actions.empty() && !post_actions(actions))
g_coordinator.requeue_actions(std::move(actions));
return;
}
}
#endif
secure_clear(password);
Expand Down Expand Up @@ -398,6 +418,18 @@ void submit_password()
execute_actions(g_coordinator.submit_password(g_prompt_request->id));
}

void apply_queued_password(void *)
{
if (!g_prompt_request || g_coordinator.state(g_prompt_request->id) != cp0_sudo::State::PROMPT)
return;
std::string password;
if (!take_queued_password(password)) return;
secure_clear(g_password);
g_password = std::move(password);
update_password_label();
submit_password();
}

void key_event_cb(lv_event_t *event)
{
auto *key = static_cast<key_item *>(lv_event_get_param(event));
Expand Down Expand Up @@ -516,6 +548,24 @@ extern "C" int cp0_sudo_cancel(uint64_t request_id)
return 0;
}

extern "C" int cp0_sudo_queue_password(const char *password)
{
if (!password || !password[0]) return -EINVAL;
const size_t length = std::strlen(password);
if (length > kMaxPasswordBytes) return -E2BIG;
{
std::lock_guard<std::mutex> lock(g_queued_password_mutex);
secure_clear(g_queued_password);
g_queued_password.assign(password, length);
g_queued_password_deadline_ms = now_ms() + 60000;
}
if (lv_async_call(apply_queued_password, nullptr) != LV_RESULT_OK) {
clear_queued_password();
return -EIO;
}
return 0;
}

extern "C" void init_sudo_signals(void)
{
cp0_signal_sudo_argv_async.append([](std::list<std::string> args, int auth_timeout_ms,
Expand Down
Loading
Loading