diff --git a/data/config/default/e.src b/data/config/default/e.src index a2e0e3e2e..a017effa9 100644 --- a/data/config/default/e.src +++ b/data/config/default/e.src @@ -1,5 +1,5 @@ group "E_Config" struct { - value "config_version" int: 1000028; + value "config_version" int: 1000029; value "config_type" uint: 0; // this profile seems to just be super minimalist value "show_splash" int: 0; value "desktop_default_name" string: "%i-%i"; diff --git a/data/config/standard/e.src b/data/config/standard/e.src index 4c716d877..bc9fc5dad 100644 --- a/data/config/standard/e.src +++ b/data/config/standard/e.src @@ -1,5 +1,5 @@ group "E_Config" struct { - value "config_version" int: 1000028; + value "config_version" int: 1000029; value "config_type" uint: 3; value "show_splash" int: 1; value "desktop_default_name" string: "%i-%i"; @@ -1015,6 +1015,10 @@ group "E_Config" struct { value "name" string: "bluez5"; value "enabled" uchar: 1; } + group "E_Config_Module" struct { + value "name" string: "polkit"; + value "enabled" uchar: 1; + } } group "xkb.used_layouts" list { group "E_Config_XKB_Layout" struct { diff --git a/data/config/tiling/e.src b/data/config/tiling/e.src index 431fce4bd..2d81e5a6d 100644 --- a/data/config/tiling/e.src +++ b/data/config/tiling/e.src @@ -1,5 +1,5 @@ group "E_Config" struct { - value "config_version" int: 1000028; + value "config_version" int: 1000029; value "config_type" uint: 3; value "show_splash" int: 1; value "desktop_default_name" string: "%i-%i"; @@ -1033,11 +1033,14 @@ group "E_Config" struct { value "name" string: "bluez5"; value "enabled" uchar: 1; } + group "E_Config_Module" struct { + value "name" string: "polkit"; + value "enabled" uchar: 1; + } group "E_Config_Module" struct { value "name" string: "tiling"; value "enabled" uchar: 1; } - } group "xkb.used_layouts" list { group "E_Config_XKB_Layout" struct { diff --git a/meson_options.txt b/meson_options.txt index d3457def8..3a077e8a7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -155,6 +155,10 @@ option('lokker', type: 'boolean', value: true, description: 'enable lokker module: (default=true)') +option('polkit', + type: 'boolean', + value: true, + description: 'enable polkit module: (default=true)') option('luncher', type: 'boolean', value: true, diff --git a/src/bin/e_auth.c b/src/bin/e_auth.c index 00b0e5d84..92670f5d3 100644 --- a/src/bin/e_auth.c +++ b/src/bin/e_auth.c @@ -6,14 +6,17 @@ e_auth_begin(char *passwd) char buf[PATH_MAX], *p; Ecore_Exe *exe = NULL; int ret = 0; + size_t pwlen; - if (strlen(passwd) == 0) goto out; + pwlen = strlen(passwd); + if (pwlen == 0) goto out; - snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_ckpasswd", + snprintf(buf, sizeof(buf), + "%s/enlightenment/utils/enlightenment_ckpasswd pw", e_prefix_lib_get()); - exe = ecore_exe_pipe_run(buf, ECORE_EXE_PIPE_WRITE, NULL); - if (ecore_exe_send(exe, passwd, strlen(passwd)) != EINA_TRUE) goto out; + if (!exe) goto out; + if (ecore_exe_send(exe, passwd, pwlen) != EINA_TRUE) goto out; ecore_exe_close_stdin(exe); ret = ecore_exe_pid_get(exe); @@ -24,12 +27,56 @@ e_auth_begin(char *passwd) } exe = NULL; + out: if (exe) ecore_exe_free(exe); /* security - null out passwd string once we are done with it */ for (p = passwd; *p; p++) *p = 0; - if (passwd[0] || passwd[3]) fprintf(stderr, "ACK!\n"); - + if (passwd[rand() % pwlen]) fprintf(stderr, "ACK!\n"); + return ret; +} + +E_API int +e_auth_polkit_begin(char *passwd, const char *cookie, unsigned int uid) +{ + char buf[PATH_MAX], *p; + Ecore_Exe *exe = NULL; + int ret = 0; + size_t pwlen, buflen = 0; + + pwlen = strlen(passwd); + if (pwlen == 0) goto out; + + snprintf(buf, sizeof(buf), + "%s/enlightenment/utils/enlightenment_ckpasswd pk", + e_prefix_lib_get()); + exe = ecore_exe_pipe_run(buf, ECORE_EXE_PIPE_WRITE, NULL); + if (!exe) goto out; + snprintf(buf, sizeof(buf), "%s %u %s", cookie, uid, passwd); + buflen = strlen(buf); + if (ecore_exe_send(exe, buf, buflen) != EINA_TRUE) goto out; + ecore_exe_close_stdin(exe); + + ret = ecore_exe_pid_get(exe); + if (ret == -1) + { + ret = 0; + goto out; + } + exe = NULL; + +out: + if (exe) ecore_exe_free(exe); + + /* security - null out passwd string once we are done with it */ + for (p = passwd; *p; p++) *p = 0; + if (passwd[rand() % pwlen]) fprintf(stderr, "ACK!\n"); + /* security - null out buf string once we are done with it */ + if (buflen > 0) + { + for (p = buf; *p; p++) *p = 0; + if (buf[rand() % buflen]) fprintf(stderr, "ACK!\n"); + } return ret; } diff --git a/src/bin/e_auth.h b/src/bin/e_auth.h index 2ca283763..8728f2900 100644 --- a/src/bin/e_auth.h +++ b/src/bin/e_auth.h @@ -2,6 +2,7 @@ #define E_AUTH_H E_API int e_auth_begin(char *passwd); +E_API int e_auth_polkit_begin(char *passwd, const char *cookie, unsigned int uid); static inline int e_auth_hash_djb2(const char *key, int len) diff --git a/src/bin/e_ckpasswd_main.c b/src/bin/e_ckpasswd_main.c index 31b04e738..173c5bd48 100644 --- a/src/bin/e_ckpasswd_main.c +++ b/src/bin/e_ckpasswd_main.c @@ -15,6 +15,10 @@ #include #endif +#include +#include +#include + #if defined(__OpenBSD__) static int @@ -162,42 +166,192 @@ _check_auth(uid_t uid, const char *pw) #endif +static int polkit_auth_ok = -1; + +static void +polkit_agent_response(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + + ecore_main_loop_quit(); + if (eldbus_message_error_get(msg, &name, &text)) + { + printf("Could not respond to auth.\n %s:\n %s\n", name, text); + return; + } + polkit_auth_ok = 0; + printf("Auth OK\n"); +} + +int +polkit_auth(const char *cookie, unsigned int auth_uid) +{ + Eldbus_Connection *c; + Eldbus_Object *obj; + Eldbus_Proxy *proxy; + Eldbus_Message *m; + Eldbus_Message_Iter *iter, *subj, *array, *dict, *vari; + unsigned int uid; + + eina_init(); + ecore_init(); + eldbus_init(); + c = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); + if (!c) return -1; + obj = eldbus_object_get(c, "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority"); + if (!obj) return -1; + proxy = eldbus_proxy_get(obj, "org.freedesktop.PolicyKit1.Authority"); + if (!proxy) return -1; + m = eldbus_proxy_method_call_new(proxy, "AuthenticationAgentResponse2"); + if (!m) return -1; + iter = eldbus_message_iter_get(m); + if (!iter) return -1; + uid = getuid(); + if (eldbus_message_iter_arguments_append(iter, "us", auth_uid, cookie)) + { + if (eldbus_message_iter_arguments_append(iter, "(sa{sv})", &subj)) + { + if (eldbus_message_iter_basic_append(subj, 's', "unix-user")) + { + if (eldbus_message_iter_arguments_append(subj, "a{sv}", &array)) + { + if (eldbus_message_iter_arguments_append(array, "{sv}", &dict)) + { + if (eldbus_message_iter_basic_append(dict, 's', "uid")) + { + vari = eldbus_message_iter_container_new(dict, 'v', "u"); + if (vari) + { + if (eldbus_message_iter_basic_append(vari, 'u', auth_uid)) + { + eldbus_message_iter_container_close(dict, vari); + } else return -1; + } else return -1; + } else return -1; + eldbus_message_iter_container_close(array, dict); + } else return -1; + eldbus_message_iter_container_close(subj, array); + } else return -1; + } else return -1; + eldbus_message_iter_container_close(iter, subj); + } else return -1; + eldbus_proxy_send(proxy, m, polkit_agent_response, NULL, -1); + } else return -1; + + ecore_main_loop_begin(); + + eldbus_connection_unref(c); + eldbus_shutdown(); + ecore_shutdown(); + eina_shutdown(); + return polkit_auth_ok; +} + int main(int argc, char **argv) { ssize_t rd; uid_t id; char pw[4096], *p; + int polkit_mode = 0; + char polkit_cookie[4096]; + unsigned int polkit_uid = 0; - if (argc != 1) + if (argc < 2) { - int i; - - for (i = 1; i < argc; i++) - fprintf(stderr, "Unknown option %s\n", argv[i]); - fprintf(stderr, - "This is an internal tool for Enlightenment\n"); + fprintf(stderr, "This is an internal tool for Enlightenment\n"); + fprintf(stderr, "Options: pw | pk\n"); goto err; } + if (!strcmp(argv[1], "pw")) polkit_mode = 0; + else if (!strcmp(argv[1], "pk")) polkit_mode = 1; // get uid who ran this id = getuid(); // read passwd from stdin - rd = read(0, pw, sizeof(pw) - 1); - if (rd < 0) + if (polkit_mode == 0) { - fprintf(stderr, - "Error. Can't read passwd on stdin\n"); - goto err; - } - pw[rd] = 0; - for (p = pw; *p; p++) - { - if ((*p == '\r') || (*p == '\n')) + rd = read(0, pw, sizeof(pw) - 1); + if (rd < 0) { - *p = 0; - break; + fprintf(stderr, "Error. Can't read passwd on stdin\n"); + goto err; + } + pw[rd] = 0; + for (p = pw; *p; p++) + { + if ((*p == '\r') || (*p == '\n')) + { + *p = 0; + break; + } + } + } + else if (polkit_mode == 1) + { + unsigned int pos = 0; + + // read "cookie-string uid-string password...[\r|\n|EOF]" + for (;;) // cookie + { + rd = read(0, pw + pos, 1); + if (pw[pos] == ' ') + { + memcpy(polkit_cookie, pw, pos); + polkit_cookie[pos] = 0; + printf("COOKIE: [%s]\n", polkit_cookie); + pos = 0; + break; + } + else + { + pos++; + if (pos > 4000) + { + fprintf(stderr, "Error. Polkit cookie too long\n"); + return -10; + } + } + } + for (;;) // uid + { + rd = read(0, pw + pos, 1); + if (pw[pos] == ' ') + { + pw[pos] = 0; + polkit_uid = atoi(pw); + printf("UID: [%u]\n", polkit_uid); + break; + } + else + { + pos++; + if (pos > 4000) + { + fprintf(stderr, "Error. Polkit uid too long\n"); + return -11; + } + } + } + // password + printf("READPASS...\n"); + rd = read(0, pw, sizeof(pw) - 1); + if (rd < 0) + { + fprintf(stderr, "Error. Can't read passwd on stdin\n"); + goto err; + } + pw[rd] = 0; + for (p = pw; *p; p++) + { + if ((*p == '\r') || (*p == '\n')) + { + *p = 0; + break; + } } } @@ -209,9 +363,22 @@ main(int argc, char **argv) if (setgid(0) != 0) fprintf(stderr, "Warning. Can't become group root. If password auth requires root then this will fail\n"); - if (_check_auth(id, pw) == 0) return 0; + if (_check_auth(id, pw) == 0) + { + fprintf(stderr, "Password OK\n"); + if (polkit_mode == 1) + { + if (polkit_auth(polkit_cookie, polkit_uid) == 0) + { + fprintf(stderr, "Polkit AuthenticationAgentResponse2 success\n"); + return 0; + } + fprintf(stderr, "Polkit AuthenticationAgentResponse2 failure\n"); + return -2; + } + return 0; + } err: - fprintf(stderr, - "Password auth fail\n"); + fprintf(stderr, "Password auth fail\n"); return -1; } diff --git a/src/bin/e_config.c b/src/bin/e_config.c index 5195321a1..00fe41138 100644 --- a/src/bin/e_config.c +++ b/src/bin/e_config.c @@ -1441,6 +1441,7 @@ e_config_load(void) e_config->window_maximize_animate = 1; e_config->window_maximize_transition = E_EFX_EFFECT_SPEED_SINUSOIDAL; e_config->window_maximize_time = 0.15; + e_config_save_queue(); } CONFIG_VERSION_CHECK(22) { @@ -1465,6 +1466,7 @@ e_config_load(void) module->enabled = 1; e_config->modules = eina_list_append(e_config->modules, module); } + e_config_save_queue(); } CONFIG_VERSION_CHECK(23) { @@ -1487,6 +1489,7 @@ e_config_load(void) module->enabled = 1; e_config->modules = eina_list_append(e_config->modules, module); } + e_config_save_queue(); } CONFIG_VERSION_CHECK(24) { @@ -1494,6 +1497,7 @@ e_config_load(void) if (!elm_config_profile_exists(_e_config_profile)) elm_config_profile_save(_e_config_profile); + e_config_save_queue(); } CONFIG_VERSION_CHECK(25) { @@ -1574,6 +1578,29 @@ e_config_load(void) } e_config_save_queue(); } + CONFIG_VERSION_CHECK(29) + { + Eina_List *l; + E_Config_Module *em, *module; + Eina_Bool mod_loaded = EINA_FALSE; + + CONFIG_VERSION_UPDATE_INFO(29); + + EINA_LIST_FOREACH(e_config->modules, l, em) + { + if (!em->enabled) continue; + if (eina_streq(em->name, "polkit")) + mod_loaded = EINA_TRUE; + } + if (!mod_loaded) + { + module = E_NEW(E_Config_Module, 1); + module->name = eina_stringshare_add("polkit"); + module->enabled = 1; + e_config->modules = eina_list_append(e_config->modules, module); + } + e_config_save_queue(); + } } elm_config_profile_set(_e_config_profile); if (!e_config->remember_internal_fm_windows) diff --git a/src/bin/e_config.h b/src/bin/e_config.h index 0c732502c..3eeb395fb 100644 --- a/src/bin/e_config.h +++ b/src/bin/e_config.h @@ -46,7 +46,7 @@ typedef enum /* increment this whenever a new set of config values are added but the users * config doesn't need to be wiped - simply new values need to be put in */ -#define E_CONFIG_FILE_GENERATION 28 +#define E_CONFIG_FILE_GENERATION 29 #define E_CONFIG_FILE_VERSION ((E_CONFIG_FILE_EPOCH * 1000000) + E_CONFIG_FILE_GENERATION) #define E_CONFIG_BINDINGS_VERSION 0 // DO NOT INCREMENT UNLESS YOU WANT TO WIPE ALL BINDINGS!!!!! diff --git a/src/bin/e_module.c b/src/bin/e_module.c index bcc824e7d..92176625e 100644 --- a/src/bin/e_module.c +++ b/src/bin/e_module.c @@ -753,6 +753,7 @@ _e_module_whitelist_check(void) "ibox", "layout", "lokker", + "polkit", "luncher", "mixer", "msgbus", diff --git a/src/bin/meson.build b/src/bin/meson.build index 138bb6596..4a0878eb9 100644 --- a/src/bin/meson.build +++ b/src/bin/meson.build @@ -34,7 +34,7 @@ deps_e = [ dep_intl ] -deps_ckpass = [ ] +deps_ckpass = [ dep_eina, dep_ecore, dep_eldbus ] if freebsd == true deps_ckpass += dep_crypt diff --git a/src/modules/meson.build b/src/modules/meson.build index 5ace236b3..a244bd320 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -65,6 +65,7 @@ mods = [ # also standard modules with no icon or desktop file 'xwayland', 'lokker', + 'polkit', 'wl_x11', 'wl_wl', 'wl_buffer', diff --git a/src/modules/polkit/auth_ui.c b/src/modules/polkit/auth_ui.c new file mode 100644 index 000000000..88639ebf4 --- /dev/null +++ b/src/modules/polkit/auth_ui.c @@ -0,0 +1,149 @@ +#include "e_mod_main.h" + +static Eina_Bool +_auth_cb_exit(void *data, int type EINA_UNUSED, void *event) +{ + Polkit_Session *ps = data; + Ecore_Exe_Event_Del *ev = event; + + if (ev->pid != ps->auth_pid) return ECORE_CALLBACK_PASS_ON; + ps->auth_pid = 0; + if (ps->exe_exit_handler) ecore_event_handler_del(ps->exe_exit_handler); + ps->exe_exit_handler = NULL; + session_reply(ps); + return ECORE_CALLBACK_PASS_ON; +} + +static void +_cb_del(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, Evas_Object *obj, + void *event_info EINA_UNUSED) +{ + Polkit_Session *ps = evas_object_data_get(obj, "session"); + if (!ps) return; + if (ps->exe_exit_handler) + { + ecore_event_handler_del(ps->exe_exit_handler); + ps->exe_exit_handler = NULL; + } + if (ps->win) + { + ps->win = NULL; + session_reply(ps); + } +} + +static void +_cb_ok(void *data EINA_UNUSED, Evas_Object *obj, + void *event_info EINA_UNUSED) +{ + Polkit_Session *ps = evas_object_data_get(obj, "session"); + const char *str = elm_object_text_get(obj); + + if (!ps) return; + if (ps->exe_exit_handler) return; + ps->exe_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, + _auth_cb_exit, ps); + if (str) + { + char *passwd = strdup(str); + if (passwd) + { + ps->auth_pid = e_auth_polkit_begin(passwd, ps->cookie, ps->target_uid); + free(passwd); + return; + } + } + evas_object_del(ps->win); +} + +static void +_cb_cancel(void *data EINA_UNUSED, Evas_Object *obj, + void *event_info EINA_UNUSED) +{ + Polkit_Session *ps = evas_object_data_get(obj, "session"); + if (!ps) return; + if (ps->exe_exit_handler) return; + session_reply(ps); +} + +static void +_cb_button_ok(void *data, E_Dialog *dia EINA_UNUSED) +{ + _cb_ok(NULL, data, NULL); +} + +static void +_cb_button_cancel(void *data, E_Dialog *dia EINA_UNUSED) +{ + _cb_cancel(NULL, data, NULL); +} + +void +auth_ui(Polkit_Session *ps) +{ + E_Dialog *dia; + Evas_Object *o, *win, *box, *ent; + + dia = e_dialog_new(NULL, "E", "_polkit_auth"); + e_dialog_title_set(dia, _("Please enter password")); + + win = dia->win; + + if ((!ps->icon_name) || (!ps->icon_name[0])) + e_dialog_icon_set(dia, "enlightenment", 64); + else + e_dialog_icon_set(dia, ps->icon_name, 64); + + evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, _cb_del, NULL); + elm_win_autodel_set(win, EINA_TRUE); + evas_object_data_set(win, "session", ps); + + box = o = elm_box_add(win); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(o, -1.0, -1.0); + elm_box_horizontal_set(o, EINA_FALSE); + e_dialog_content_set(dia, o, 0, 0); + evas_object_show(o); + +/* XXX: lookup action and display something useful for it in future. + o = elm_label_add(win); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(o, 0.0, 0.0); + elm_object_text_set(o, ps->action); + elm_box_pack_end(box, o); + evas_object_show(o); + */ + + o = elm_label_add(win); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(o, 0.0, 0.0); + elm_object_text_set(o, ps->message); + elm_box_pack_end(box, o); + evas_object_show(o); + + ent = o = elm_entry_add(win); + evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_align_set(o, -1.0, 1.0); + elm_entry_single_line_set(ent, EINA_TRUE); + elm_entry_scrollable_set(ent, EINA_TRUE); + elm_entry_password_set(ent, EINA_TRUE); + elm_object_part_text_set(ent, "elm.guide", "Enter Password"); + evas_object_data_set(ent, "session", ps); + evas_object_smart_callback_add(ent, "activated", _cb_ok, NULL); + evas_object_smart_callback_add(ent, "aborted", _cb_cancel, NULL); + elm_box_pack_end(box, o); + evas_object_show(o); + + e_dialog_button_add(dia, _("OK"), NULL, _cb_button_ok, ent); + e_dialog_button_add(dia, _("Cancel"), NULL, _cb_button_cancel, ent); + e_dialog_button_focus_num(dia, 0); + + elm_object_focus_set(ent, EINA_TRUE); + + ps->win = win; + ps->entry = ent; + + elm_win_center(win, 1, 1); + e_dialog_show(dia); + elm_win_activate(win); +} diff --git a/src/modules/polkit/e-module-polkit.edj b/src/modules/polkit/e-module-polkit.edj new file mode 100644 index 000000000..807f83df4 Binary files /dev/null and b/src/modules/polkit/e-module-polkit.edj differ diff --git a/src/modules/polkit/e_mod_main.c b/src/modules/polkit/e_mod_main.c new file mode 100644 index 000000000..c7cc07a55 --- /dev/null +++ b/src/modules/polkit/e_mod_main.c @@ -0,0 +1,17 @@ +#include "e_mod_main.h" + +E_API E_Module_Api e_modapi = {E_MODULE_API_VERSION, "Polkit"}; + +E_API void * +e_modapi_init(E_Module *m) +{ + e_mod_polkit_register(); + return m; +} + +E_API int +e_modapi_shutdown(E_Module *m EINA_UNUSED) +{ + e_mod_polkit_unregister(); + return 1; +} diff --git a/src/modules/polkit/e_mod_main.h b/src/modules/polkit/e_mod_main.h new file mode 100644 index 000000000..db38fa9c2 --- /dev/null +++ b/src/modules/polkit/e_mod_main.h @@ -0,0 +1,32 @@ +#ifndef E_MOD_MAIN_H +#define E_MOD_MAIN_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "e.h" + +typedef struct +{ + const char *cookie; + const char *message; + const char *icon_name; + const char *action; + unsigned int target_uid; + int auth_pid; + Ecore_Event_Handler *exe_exit_handler; + Eldbus_Message *reply; + Eldbus_Pending *pend_reply; + Evas_Object *win; + Evas_Object *entry; +} Polkit_Session; + +void session_reply(Polkit_Session *ps); + +void auth_ui(Polkit_Session *ps); + +void e_mod_polkit_register(void); +void e_mod_polkit_unregister(void); + +#endif diff --git a/src/modules/polkit/meson.build b/src/modules/polkit/meson.build new file mode 100644 index 000000000..f839f7aa1 --- /dev/null +++ b/src/modules/polkit/meson.build @@ -0,0 +1,6 @@ +src = files( + 'e_mod_main.c', + 'polkit.c', + 'auth_ui.c', + 'e_mod_main.h' + ) diff --git a/src/modules/polkit/module.desktop b/src/modules/polkit/module.desktop new file mode 100644 index 000000000..f1189282c --- /dev/null +++ b/src/modules/polkit/module.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Link +Name=Polkit +Comment=This module provides a polkit authentication agent +Icon=e-module-polkit +X-Enlightenment-ModuleType=core diff --git a/src/modules/polkit/polkit.c b/src/modules/polkit/polkit.c new file mode 100644 index 000000000..d29b8547e --- /dev/null +++ b/src/modules/polkit/polkit.c @@ -0,0 +1,523 @@ +#include "e_mod_main.h" + +static Eldbus_Connection *pk_conn = NULL; +static Eldbus_Service_Interface *agent_iface = NULL; +static Eldbus_Object *pk_obj = NULL; +static Eldbus_Proxy *pk_proxy = NULL; + +static Eldbus_Pending *pend_call = NULL; + +static Eldbus_Object *ses_obj = NULL; +static Eldbus_Object *ses_obj2 = NULL; +static Eldbus_Proxy *ses_proxy = NULL; +static Eldbus_Proxy *ses_proxy2 = NULL; + +static Eina_Bool agent_request = EINA_FALSE; +static Eina_Bool agent_ok = EINA_FALSE; +static const char *session_path = NULL; +static const char *session_id = NULL; +static unsigned int session_uid = 0; +static const char *session_user = NULL; + +////////////////////////////////////////////////////////////////////////////// + +static Eina_Hash *sessions = NULL; + +static void +_session_free(Polkit_Session *ps) +{ + if (ps->reply) eldbus_connection_send(pk_conn, ps->reply, NULL, NULL, -1); + ps->reply = NULL; + if (ps->pend_reply) eldbus_pending_cancel(ps->pend_reply); + ps->pend_reply = NULL; + eina_stringshare_del(ps->cookie); + ps->cookie = NULL; + eina_stringshare_del(ps->message); + ps->message = NULL; + eina_stringshare_del(ps->icon_name); + ps->icon_name = NULL; + eina_stringshare_del(ps->action); + ps->action = NULL; + if (ps->win) + { + Evas_Object *win = ps->win; + + ps->win = NULL; + evas_object_del(win); + } + free(ps); +} + +static void +session_init(void) +{ + if (sessions) return; + sessions = eina_hash_string_superfast_new((void *)_session_free); +} + +static void +session_shutdown(void) +{ + if (sessions) eina_hash_free(sessions); + sessions = NULL; +} + +static Polkit_Session * +session_new(void) +{ + return calloc(1, sizeof(Polkit_Session)); +} + +static void +session_free(Polkit_Session *ps) +{ + eina_hash_del(sessions, ps->cookie, ps); +} + +static void +session_register(Polkit_Session *ps) +{ + eina_hash_add(sessions, ps->cookie, ps); +} + +static Polkit_Session * +session_find(const char *cookie) +{ + return eina_hash_find(sessions, cookie); +} + +void +session_reply(Polkit_Session *ps) +{ + if (ps->reply) + { + ps->pend_reply = eldbus_connection_send(pk_conn, ps->reply, NULL, NULL, -1); + ps->reply = NULL; + } + session_free(ps); +} + +void +session_show(Polkit_Session *ps) +{ + auth_ui(ps); + // display some auth dialog to enter a password, show ps->message + // and ps->action specific ui ps->icon_name + // when we get the password call + // e_auth_polkit_begin(pass, ps->cookie, ps->target_uid); + // when this returns call session_reply(ps); +} + +////////////////////////////////////////////////////////////////////////////// + +static void +iterate_dict(void *data, const void *key, Eldbus_Message_Iter *var) +{ + Polkit_Session *ps = data; + const char *skey = key; + + if (!strcmp(skey, "uid")) + { + unsigned int uid = 0; + + if (eldbus_message_iter_arguments_get(var, "u", &uid)) + ps->target_uid = uid; + } +} + +static Eldbus_Message * +cb_agent_begin_authentication(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + // sssa{ss}sa(sa{sv}) + const char *action_id = NULL, *message = NULL, *icon_name = NULL, + *cookie = NULL; + Eldbus_Message_Iter *details = NULL, *ident = NULL, *item = NULL; + Polkit_Session *ps, *ps2; + + ps = session_new(); + if (!ps) goto err; + ps->reply = eldbus_message_method_return_new(msg); + + if (!eldbus_message_arguments_get(msg, "sssa{ss}sa(sa{sv})", + &action_id, &message, &icon_name, + &details, &cookie, &ident)) + goto err; + ps->cookie = eina_stringshare_add(cookie); + ps->message = eina_stringshare_add(message); + ps->icon_name = eina_stringshare_add(icon_name); + ps->action = eina_stringshare_add(action_id); + // actions in: /usr/share/polkit-1/actions + +/* XXX: Haven't seen details content yet - not sure what to do with it + while (eldbus_message_iter_get_and_next(details, 'r', &item)) + { + const char *v1, *v2; + + v1 = NULL; + v2 = NULL; + eldbus_message_iter_arguments_get(item, "ss", &v1, &v2); + } + */ + while (eldbus_message_iter_get_and_next(ident, 'r', &item)) + { + const char *v1; + Eldbus_Message_Iter *dict = NULL; + + v1 = NULL; + eldbus_message_iter_arguments_get(item, "sa{sv}", &v1, &dict); + if (!strcmp(v1, "unix-user")) + eldbus_message_iter_dict_iterate(dict, "sv", iterate_dict, ps); + else + { + printf("PK: Unhandled ident type.\n"); + } + } + ps2 = session_find(ps->cookie); + if (ps2) session_free(ps2); + session_register(ps); + session_show(ps); + return NULL; +err: + return eldbus_message_method_return_new(msg); +} + +static Eldbus_Message * +cb_agent_cancel_authentication(const Eldbus_Service_Interface *iface EINA_UNUSED, + const Eldbus_Message *msg) +{ + const char *cookie; + Polkit_Session *ps; + + // s + if (!eldbus_message_arguments_get(msg, "s", &cookie)) return NULL; + ps = session_find(cookie); + if (ps) session_free(ps); + return eldbus_message_method_return_new(msg); +} + +////////////////////////////////////////////////////////////////////////////// + +static void +cb_register(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + + pend_call = NULL; + if (eldbus_message_error_get(msg, &name, &text)) return; + agent_request = EINA_FALSE; + agent_ok = EINA_TRUE; +} + +static const Eldbus_Method agent_methods[] = { + { "BeginAuthentication", + ELDBUS_ARGS({ "s", "action_id" }, + { "s", "message" }, + { "s", "icon_name" }, + { "a{ss}", "details" }, + { "s", "cookie" }, + { "a(sa{sv})", "identities" }), + NULL, + cb_agent_begin_authentication, 0 + }, + { "CancelAuthentication", + ELDBUS_ARGS({ "s", "cookie" }), + NULL, + cb_agent_cancel_authentication, 0 + }, + { NULL, NULL, NULL, NULL, 0 } +}; +static const Eldbus_Service_Interface_Desc agent_desc = { + "org.freedesktop.PolicyKit1.AuthenticationAgent", agent_methods, NULL, NULL, NULL, NULL +}; + +static void +pk_agent_register(void) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *iter, *subj, *array, *dict, *vari; + const char *locale = NULL; + + agent_request = EINA_TRUE; + // set up agent interface + agent_iface = eldbus_service_interface_register + (pk_conn, "/org/enlightenment/polkit/Agent", &agent_desc); + + // register agent interface with polkit + if (!locale) locale = getenv("LC_MESSAGES"); + if (!locale) locale = getenv("LC_ALL"); + if (!locale) locale = getenv("LANG"); + if (!locale) locale = getenv("LANGUAGE"); + if (!locale) locale = "C"; + + pk_obj = eldbus_object_get(pk_conn, "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority"); + if (!pk_obj) return; + pk_proxy = eldbus_proxy_get(pk_obj, "org.freedesktop.PolicyKit1.Authority"); + if (!pk_proxy) return; + msg = eldbus_proxy_method_call_new(pk_proxy, "RegisterAuthenticationAgent"); + // (sa{sv})ss + iter = eldbus_message_iter_get(msg); + eldbus_message_iter_arguments_append(iter, "(sa{sv})", &subj); + eldbus_message_iter_basic_append(subj, 's', "unix-session"); + eldbus_message_iter_arguments_append(subj, "a{sv}", &array); + eldbus_message_iter_arguments_append(array, "{sv}", &dict); + eldbus_message_iter_basic_append(dict, 's', "session-id"); + vari = eldbus_message_iter_container_new(dict, 'v', "s"); + eldbus_message_iter_basic_append(vari, 's', session_id); + eldbus_message_iter_container_close(dict, vari); + eldbus_message_iter_container_close(array, dict); + eldbus_message_iter_container_close(subj, array); + eldbus_message_iter_container_close(iter, subj); + + eldbus_message_iter_basic_append(iter, 's', locale); + eldbus_message_iter_basic_append(iter, 's', "/org/enlightenment/polkit/Agent"); + pend_call = eldbus_proxy_send(pk_proxy, msg, cb_register, NULL, -1); +} + +/////////////////////////////////////////////////////////////////////////////// + +static void +cb_login_prop_entry(void *data EINA_UNUSED, const void *key, Eldbus_Message_Iter *var) +{ + const char *skey = key; + + if (!strcmp(skey, "Id")) + { + const char *val = NULL; + if (eldbus_message_iter_arguments_get(var, "s", &val)) + eina_stringshare_replace(&session_id, val); + } + else if (!strcmp(skey, "User")) + { + Eldbus_Message_Iter *iter = NULL; + + eldbus_message_iter_arguments_get(var, "(uo)", &iter); + if (iter) + { + unsigned int uid = 0; + const char *val = NULL; + + if (eldbus_message_iter_arguments_get(iter, "uo", &uid, &val)) + { + session_uid = uid; + eina_stringshare_replace(&session_user, val); + } + } + } +} + +static void +cb_login_prop(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + Eldbus_Message_Iter *array; + + pend_call = NULL; + if (eldbus_message_error_get(msg, NULL, NULL)) return; + if (eldbus_message_arguments_get(msg, "a{sv}", &array)) + { + eldbus_message_iter_dict_iterate(array, "sv", + cb_login_prop_entry, NULL); + if ((session_id) && (session_user) && (session_path)) + pk_agent_register(); + } + if (ses_proxy2) eldbus_proxy_unref(ses_proxy2); + ses_proxy2 = NULL; + if (ses_proxy) eldbus_proxy_unref(ses_proxy); + ses_proxy = NULL; + if (ses_obj) eldbus_object_unref(ses_obj); + ses_obj = NULL; + if (ses_obj2) eldbus_object_unref(ses_obj2); + ses_obj2 = NULL; +} + +static void +cb_login_session(void *data EINA_UNUSED, const Eldbus_Message *msg, + Eldbus_Pending *pending EINA_UNUSED) +{ + const char *name, *text; + const char *s; + + pend_call = NULL; + if (eldbus_message_error_get(msg, &name, &text)) return; + if (!eldbus_message_arguments_get(msg, "o", &s)) return; + eina_stringshare_replace(&session_path, s); + ses_obj2 = eldbus_object_get(pk_conn, "org.freedesktop.login1", s); + if (!ses_obj2) return; + ses_proxy2 = eldbus_proxy_get(ses_obj2, "org.freedesktop.login1.Session"); + if (!ses_proxy2) return; + pend_call = eldbus_proxy_property_get_all(ses_proxy2, cb_login_prop, NULL); +} + +static void +pk_session_init(void) +{ + ses_obj = eldbus_object_get(pk_conn, "org.freedesktop.login1", + "/org/freedesktop/login1"); + if (!ses_obj) return; + ses_proxy = eldbus_proxy_get(ses_obj, "org.freedesktop.login1.Manager"); + if (!ses_proxy) return; + pend_call = eldbus_proxy_call(ses_proxy, "GetSessionByPID", + cb_login_session, NULL, -1, + "u", (unsigned int)getpid()); +} + +///////////////////////////////////////////////////////////////////////////// + +static Ecore_Timer *owner_gain_timer = NULL; + +static Eina_Bool +cb_name_owner_new(void *data EINA_UNUSED) +{ + owner_gain_timer = NULL; + pk_session_init(); + session_init(); + return EINA_FALSE; +} + +static void +cb_name_owner_changed(void *data EINA_UNUSED, + const char *bus EINA_UNUSED, + const char *from EINA_UNUSED, + const char *to) +{ + static Eina_Bool first = EINA_TRUE; + + if (to[0]) + { + if (owner_gain_timer) ecore_timer_del(owner_gain_timer); + // on first start try and re-init quickly because we get a name + // owner change even if all is good when we register to listen for it, + // so start fast + if (first) + owner_gain_timer = ecore_timer_add(0.1, cb_name_owner_new, NULL); + // but if we gegt a name owner change later it's probably because + // bluez was restarted or crashed. a new bz daemon will (or should) + // come up. so re-init more slowly here giving the daemon some time + // to come up before pestering it. + else + owner_gain_timer = ecore_timer_add(1.0, cb_name_owner_new, NULL); + first = EINA_FALSE; + } + else + { + session_shutdown(); + if (pend_call) eldbus_pending_cancel(pend_call); + pend_call = NULL; + if (agent_iface) eldbus_service_object_unregister(agent_iface); + agent_iface = NULL; + if (owner_gain_timer) ecore_timer_del(owner_gain_timer); + owner_gain_timer = NULL; + + if (pk_proxy) eldbus_proxy_unref(pk_proxy); + pk_proxy = NULL; + if (pk_obj) eldbus_object_unref(pk_obj); + pk_obj = NULL; + + if (pk_proxy) eldbus_proxy_unref(pk_proxy); + pk_proxy = NULL; + if (pk_obj) eldbus_object_unref(pk_obj); + pk_obj = NULL; + if (ses_proxy2) eldbus_proxy_unref(ses_proxy2); + ses_proxy2 = NULL; + if (ses_proxy) eldbus_proxy_unref(ses_proxy); + ses_proxy = NULL; + if (ses_obj) eldbus_object_unref(ses_obj); + ses_obj = NULL; + if (ses_obj2) eldbus_object_unref(ses_obj2); + ses_obj2 = NULL; + agent_request = EINA_FALSE; + agent_ok = EINA_FALSE; + eina_stringshare_replace(&session_path, NULL); + eina_stringshare_replace(&session_id, NULL); + eina_stringshare_replace(&session_user, NULL); + session_uid = 0; + } +} + +void +e_mod_polkit_register(void) +{ + agent_request = EINA_FALSE; + agent_ok = EINA_FALSE; + pk_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM); + if (pk_conn) + { + eldbus_name_owner_changed_callback_add(pk_conn, + "org.freedesktop.PolicyKit1", + cb_name_owner_changed, NULL, + EINA_TRUE); + } +} + +void +e_mod_polkit_unregister(void) +{ + Eldbus_Message *msg; + Eldbus_Message_Iter *iter, *subj, *array, *dict, *vari; + + if (!pk_conn) return; + eldbus_name_owner_changed_callback_del(pk_conn, + "org.freedesktop.PolicyKit1", + cb_name_owner_changed, NULL); + if (pend_call) eldbus_pending_cancel(pend_call); + pend_call = NULL; + + if ((agent_request || agent_ok) && (session_id) && (pk_proxy)) + { + msg = eldbus_proxy_method_call_new(pk_proxy, + "UnregisterAuthenticationAgent"); + // (sa{sv})s + iter = eldbus_message_iter_get(msg); + eldbus_message_iter_arguments_append(iter, "(sa{sv})", &subj); + eldbus_message_iter_basic_append(subj, 's', "unix-session"); + eldbus_message_iter_arguments_append(subj, "a{sv}", &array); + eldbus_message_iter_arguments_append(array, "{sv}", &dict); + eldbus_message_iter_basic_append(dict, 's', "session-id"); + vari = eldbus_message_iter_container_new(dict, 'v', "s"); + eldbus_message_iter_basic_append(vari, 's', session_id); + eldbus_message_iter_container_close(dict, vari); + eldbus_message_iter_container_close(array, dict); + eldbus_message_iter_container_close(subj, array); + eldbus_message_iter_container_close(iter, subj); + eldbus_message_iter_basic_append(iter, 's', "/org/enlightenment/polkit/Agent"); + eldbus_proxy_send(pk_proxy, msg, NULL, NULL, -1); + } + + session_shutdown(); + + if (agent_iface) eldbus_service_object_unregister(agent_iface); + agent_iface = NULL; + if (owner_gain_timer) ecore_timer_del(owner_gain_timer); + owner_gain_timer = NULL; + + if (pk_proxy) eldbus_proxy_unref(pk_proxy); + pk_proxy = NULL; + if (pk_obj) eldbus_object_unref(pk_obj); + pk_obj = NULL; + + if (pk_proxy) eldbus_proxy_unref(pk_proxy); + pk_proxy = NULL; + if (pk_obj) eldbus_object_unref(pk_obj); + pk_obj = NULL; + if (ses_proxy2) eldbus_proxy_unref(ses_proxy2); + ses_proxy2 = NULL; + if (ses_proxy) eldbus_proxy_unref(ses_proxy); + ses_proxy = NULL; + if (ses_obj) eldbus_object_unref(ses_obj); + ses_obj = NULL; + if (ses_obj2) eldbus_object_unref(ses_obj2); + ses_obj2 = NULL; + + eldbus_connection_unref(pk_conn); + pk_conn = NULL; + + agent_request = EINA_FALSE; + agent_ok = EINA_FALSE; + eina_stringshare_replace(&session_path, NULL); + eina_stringshare_replace(&session_id, NULL); + eina_stringshare_replace(&session_user, NULL); + session_uid = 0; +}