e - add polkit module to add an auth agent into e

new feature - polkit auth agent support partly in core (need to have
the pam setuid root auth tool respond via dbus) and partly a module
(the agent dbus protocol handling and setup as well as auth gui). this
took me a while even with all the docs to work out how polkit works...
it was really fussy and its data structs are an extra pain in the butt
to craft with eldbus, but i managed it. not everything is supported
but the core basics are there and this can be built on.

right now the gui is really basic, but does the job.
This commit is contained in:
Carsten Haitzler 2019-10-08 01:29:24 +01:00
parent c7d0d6e6c9
commit 5f4697fd7d
19 changed files with 1022 additions and 34 deletions

View File

@ -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";

View File

@ -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 {

View File

@ -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 {

View File

@ -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,

View File

@ -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;
}

View File

@ -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)

View File

@ -15,6 +15,10 @@
#include <alloca.h>
#endif
#include <Eina.h>
#include <Ecore.h>
#include <Eldbus.h>
#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;
}

View File

@ -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)

View File

@ -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!!!!!

View File

@ -753,6 +753,7 @@ _e_module_whitelist_check(void)
"ibox",
"layout",
"lokker",
"polkit",
"luncher",
"mixer",
"msgbus",

View File

@ -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

View File

@ -65,6 +65,7 @@ mods = [
# also standard modules with no icon or desktop file
'xwayland',
'lokker',
'polkit',
'wl_x11',
'wl_wl',
'wl_buffer',

View File

@ -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);
}

Binary file not shown.

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,6 @@
src = files(
'e_mod_main.c',
'polkit.c',
'auth_ui.c',
'e_mod_main.h'
)

View File

@ -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

523
src/modules/polkit/polkit.c Normal file
View File

@ -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;
}