diff --git a/configure.ac b/configure.ac
index aff6ce1..b824b7b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,6 +49,14 @@ AC_ARG_ENABLE([mer_ssu], AS_HELP_STRING([--enable-mer-ssu], [Enable MER SSU @<:@
esac],[mer_ssu=false])
AM_CONDITIONAL([USE_MER_SSU], [test x$mer_ssu = xtrue])
+AC_ARG_ENABLE([sailfish_access_control], AS_HELP_STRING([--enable-sailfish-access-control], [Enable Sailfish Access Control @<:@default=false@:>@]),
+ [case "${enableval}" in
+ yes) sailfish_access_control=true ; CFLAGS="-DSAILFISH_ACCESS_CONTROL $CFLAGS" ;;
+ no) sailfish_access_control=false ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-sailfish-access-control]) ;;
+ esac],[sailfish_access_control=false])
+AM_CONDITIONAL([SAILFISH_ACCESS_CONTROL], [test x$sailfish_access_control = xtrue])
+
AC_ARG_ENABLE([app_sync], AS_HELP_STRING([--enable-app-sync], [Enable application syncing @<:@default=true@:>@]),
[case "${enableval}" in
yes) app_sync=true ; CFLAGS="-DAPP_SYNC $CFLAGS" ;;
@@ -100,6 +108,7 @@ PKG_CHECK_MODULES([USB_MODED], [
ssu-sysinfo
libsystemd
dsme
+ sailfishaccesscontrol
])
AC_SUBST(USB_MODED_LIBS)
diff --git a/rpm/usb-moded.spec b/rpm/usb-moded.spec
index c15cf10..a97c0c1 100644
--- a/rpm/usb-moded.spec
+++ b/rpm/usb-moded.spec
@@ -17,6 +17,7 @@ BuildRequires: doxygen
BuildRequires: pkgconfig(libsystemd)
BuildRequires: pkgconfig(ssu-sysinfo)
BuildRequires: pkgconfig(dsme) >= 0.65.0
+BuildRequires: pkgconfig(sailfishaccesscontrol)
Requires: lsof
Requires: usb-moded-configs
@@ -327,7 +328,7 @@ when the UI fails.
%build
test -e Makefile || (%autogen)
-test -e Makefile || (%configure --enable-app-sync --enable-meegodevlock --enable-debug --enable-connman --enable-systemd --enable-mer-ssu)
+test -e Makefile || (%configure --enable-app-sync --enable-meegodevlock --enable-debug --enable-connman --enable-systemd --enable-mer-ssu --enable-sailfish-access-control)
make all doc %{?_smp_mflags}
%install
diff --git a/src/Makefile.am b/src/Makefile.am
index 451a6ce..a9dfdb7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -80,6 +80,11 @@ usb_moded_SOURCES += \
usb_moded-appsync-dbus-private.h
endif
+if SAILFISH_ACCESS_CONTROL
+usb_moded_CPPFLAGS += `pkg-config --cflags sailfishaccesscontrol`
+usb_moded_LDFLAGS += `pkg-config --libs sailfishaccesscontrol`
+endif
+
usb_moded_util_CPPFLAGS = \
$(USB_MODED_CFLAGS)
diff --git a/src/com.meego.usb_moded.xml b/src/com.meego.usb_moded.xml
index 32d04e4..513e375 100644
--- a/src/com.meego.usb_moded.xml
+++ b/src/com.meego.usb_moded.xml
@@ -36,6 +36,9 @@
+
+
+
diff --git a/src/usb_moded-common.c b/src/usb_moded-common.c
index f7e487f..a433a7a 100644
--- a/src/usb_moded-common.c
+++ b/src/usb_moded-common.c
@@ -61,7 +61,7 @@ bool common_msleep_ (const char *file, int line, co
static bool common_mode_in_list (const char *mode, char *const *modes);
bool common_modename_is_internal (const char *modename);
int common_valid_mode (const char *mode);
-gchar *common_get_mode_list (mode_list_type_t type);
+gchar *common_get_mode_list (mode_list_type_t type, uid_t uid);
/* ========================================================================= *
* Functions
@@ -205,7 +205,7 @@ void common_send_supported_modes_signal(void)
{
LOG_REGISTER_CONTEXT;
- gchar *mode_list = common_get_mode_list(SUPPORTED_MODES_LIST);
+ gchar *mode_list = common_get_mode_list(SUPPORTED_MODES_LIST, 0);
umdbus_send_supported_modes_signal(mode_list);
g_free(mode_list);
}
@@ -216,7 +216,7 @@ void common_send_available_modes_signal(void)
{
LOG_REGISTER_CONTEXT;
- gchar *mode_list = common_get_mode_list(AVAILABLE_MODES_LIST);
+ gchar *mode_list = common_get_mode_list(AVAILABLE_MODES_LIST, 0);
umdbus_send_available_modes_signal(mode_list);
g_free(mode_list);
}
@@ -503,10 +503,12 @@ int common_valid_mode(const char *mode)
/** make a list of all available usb modes
*
* @param type The type of list to return. Supported or available.
+ * @param uid Uid of the process requesting the information;
+ * this is used to limit allowed modes, 0 returns all
*
* @return a comma-separated list of modes (MODE_ASK not included as it is not a real mode)
*/
-gchar *common_get_mode_list(mode_list_type_t type)
+gchar *common_get_mode_list(mode_list_type_t type, uid_t uid)
{
LOG_REGISTER_CONTEXT;
@@ -544,6 +546,10 @@ gchar *common_get_mode_list(mode_list_type_t type)
{
modedata_t *data = iter->data;
+ /* skip dynamic modes that are not allowed */
+ if (!usbmoded_is_mode_permitted(data->mode_name, uid))
+ continue;
+
/* skip items in the hidden list */
if (common_mode_in_list(data->mode_name, hidden_modes_array))
continue;
diff --git a/src/usb_moded-common.h b/src/usb_moded-common.h
index 74ea5f4..8f4e7f7 100644
--- a/src/usb_moded-common.h
+++ b/src/usb_moded-common.h
@@ -61,7 +61,7 @@ waitres_t common_wait (unsigned tot_ms, bool (*ready_c
bool common_msleep_ (const char *file, int line, const char *func, unsigned msec);
bool common_modename_is_internal (const char *modename);
int common_valid_mode (const char *mode);
-gchar *common_get_mode_list (mode_list_type_t type);
+gchar *common_get_mode_list (mode_list_type_t type, uid_t uid);
/* ========================================================================= *
* Macros
diff --git a/src/usb_moded-control.c b/src/usb_moded-control.c
index 1f5d900..88ad301 100644
--- a/src/usb_moded-control.c
+++ b/src/usb_moded-control.c
@@ -58,6 +58,7 @@ void control_set_cable_state (cable_state_t cable_state);
cable_state_t control_get_cable_state (void);
void control_clear_cable_state (void);
bool control_get_connection_state (void);
+void control_set_last_seen_user (uid_t uid);
/* ========================================================================= *
* Data
@@ -89,6 +90,12 @@ static char *control_internal_mode = NULL;
*/
static cable_state_t control_cable_state = CABLE_STATE_UNKNOWN;
+/** Last user seen
+ *
+ * Defaults to invalid user which has no rights.
+ */
+static uid_t control_last_seen_user = (uid_t)-1;
+
/* ========================================================================= *
* Functions
* ========================================================================= */
@@ -337,12 +344,17 @@ void control_select_usb_mode(void)
/* If there is only one allowed mode, use it without
* going through ask-mode */
if( !strcmp(MODE_ASK, mode_to_set) ) {
- // FIXME free() vs g_free() conflict
- gchar *available = common_get_mode_list(AVAILABLE_MODES_LIST);
- if( *available && !strchr(available, ',') ) {
- free(mode_to_set), mode_to_set = available, available = 0;
+ if( control_last_seen_user == (uid_t)-1 ) {
+ /* Use charging only if no user has been seen */
+ free(mode_to_set), mode_to_set = 0;
+ } else {
+ // FIXME free() vs g_free() conflict
+ gchar *available = common_get_mode_list(AVAILABLE_MODES_LIST, control_last_seen_user);
+ if( *available && !strchr(available, ',') ) {
+ free(mode_to_set), mode_to_set = available, available = 0;
+ }
+ g_free(available);
}
- g_free(available);
}
if( mode_to_set && usbmoded_can_export() ) {
@@ -431,3 +443,12 @@ bool control_get_connection_state(void)
}
return connected;
}
+
+/** Set the last seen user
+ *
+ * @param uid of last seen user, controls implicitly set modes
+ */
+void control_set_last_seen_user(uid_t uid)
+{
+ control_last_seen_user = uid;
+}
diff --git a/src/usb_moded-control.h b/src/usb_moded-control.h
index 4e50ccf..2d33a16 100644
--- a/src/usb_moded-control.h
+++ b/src/usb_moded-control.h
@@ -49,5 +49,6 @@ void control_set_cable_state (cable_state_t cable_state);
cable_state_t control_get_cable_state (void);
void control_clear_cable_state (void);
bool control_get_connection_state (void);
+void control_set_last_seen_user (uid_t uid);
#endif /* USB_MODED_CONTROL_H_ */
diff --git a/src/usb_moded-dbus-private.h b/src/usb_moded-dbus-private.h
index 0184083..89001c1 100644
--- a/src/usb_moded-dbus-private.h
+++ b/src/usb_moded-dbus-private.h
@@ -42,6 +42,9 @@
/** Logical name for org.freedesktop.DBus.NameOwnerChanged signal */
# define DBUS_NAME_OWNER_CHANGED_SIG "NameOwnerChanged"
+/** Logical name for org.freedesktop.DBus.GetNameOwner method */
+# define DBUS_GET_CONNECTION_PID_REQ "GetConnectionUnixProcessID"
+
/* ========================================================================= *
* Types
* ========================================================================= */
diff --git a/src/usb_moded-dbus.c b/src/usb_moded-dbus.c
index 9111608..24c27b7 100644
--- a/src/usb_moded-dbus.c
+++ b/src/usb_moded-dbus.c
@@ -40,6 +40,7 @@
#include
#include
+#include
#include
@@ -84,6 +85,7 @@ int umdbus_send_hidden_modes_signal (const char *hidde
int umdbus_send_whitelisted_modes_signal(const char *whitelist);
static void umdbus_get_name_owner_cb (DBusPendingCall *pc, void *aptr);
gboolean umdbus_get_name_owner_async (const char *name, usb_moded_get_name_owner_fn cb, DBusPendingCall **ppc);
+uid_t umdbus_get_sender_uid (const char *sender);
/* ========================================================================= *
* Data
@@ -161,6 +163,9 @@ static const char umdbus_introspect_usbmoded[] =
" \n"
" \n"
" \n"
+" \n"
+" \n"
+" \n"
" \n"
" \n"
" \n"
@@ -284,6 +289,7 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB
const char *member = dbus_message_get_member(msg);
const char *object = dbus_message_get_path(msg);
int type = dbus_message_get_type(msg);
+ const char *sender = dbus_message_get_sender(msg);
(void)user_data;
@@ -340,11 +346,17 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB
const char *mode = control_get_external_mode();
char *use = 0;
DBusError err = DBUS_ERROR_INIT;
+ uid_t uid = umdbus_get_sender_uid(sender);
if(!dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &use, DBUS_TYPE_INVALID)) {
log_err("parse error: %s: %s", err.name, err.message);
reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, member);
}
+ else if( !usbmoded_is_mode_permitted(use, uid) ) {
+ /* Insufficient permissions */
+ log_warning("Mode '%s' is not allowed for uid %d", use, uid);
+ reply = dbus_message_new_error(msg, DBUS_ERROR_ACCESS_DENIED, member);
+ }
else if( control_get_cable_state() != CABLE_STATE_PC_CONNECTED ) {
/* Mode change makes no sence unless we have a PC connection */
log_warning("Mode '%s' requested while not connected to pc", use);
@@ -401,6 +413,11 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB
if(!dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &config, DBUS_TYPE_INVALID))
reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, member);
+#ifdef SAILFISH_ACCESS_CONTROL
+ /* do not let non-owner user hide modes */
+ else if (!sailfish_access_control_hasgroup(umdbus_get_sender_uid(sender), "sailfish-system"))
+ reply = dbus_message_new_error(msg, DBUS_ERROR_ACCESS_DENIED, member);
+#endif
else
{
/* error checking is done when setting configuration */
@@ -422,6 +439,11 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB
if(!dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &config, DBUS_TYPE_INVALID))
reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, member);
+#ifdef SAILFISH_ACCESS_CONTROL
+ /* do not let non-owner user unhide modes */
+ else if (!sailfish_access_control_hasgroup(umdbus_get_sender_uid(sender), "sailfish-system"))
+ reply = dbus_message_new_error(msg, DBUS_ERROR_ACCESS_DENIED, member);
+#endif
else
{
/* error checking is done when setting configuration */
@@ -500,7 +522,7 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB
}
else if(!strcmp(member, USB_MODE_LIST))
{
- gchar *mode_list = common_get_mode_list(SUPPORTED_MODES_LIST);
+ gchar *mode_list = common_get_mode_list(SUPPORTED_MODES_LIST, 0);
if((reply = dbus_message_new_method_return(msg)))
dbus_message_append_args (reply, DBUS_TYPE_STRING, (const char *) &mode_list, DBUS_TYPE_INVALID);
@@ -508,12 +530,23 @@ static DBusHandlerResult umdbus_msg_handler(DBusConnection *const connection, DB
}
else if(!strcmp(member, USB_MODE_AVAILABLE_MODES_GET))
{
- gchar *mode_list = common_get_mode_list(AVAILABLE_MODES_LIST);
+ gchar *mode_list = common_get_mode_list(AVAILABLE_MODES_LIST, 0);
if((reply = dbus_message_new_method_return(msg)))
dbus_message_append_args (reply, DBUS_TYPE_STRING, (const char *) &mode_list, DBUS_TYPE_INVALID);
g_free(mode_list);
}
+ else if(!strcmp(member, USB_MODE_AVAILABLE_MODES_FOR_USER))
+ {
+ uid_t uid = umdbus_get_sender_uid(sender);
+ gchar *mode_list = common_get_mode_list(AVAILABLE_MODES_LIST, uid);
+
+ if((reply = dbus_message_new_method_return(msg)))
+ dbus_message_append_args (reply, DBUS_TYPE_STRING, (const char *) &mode_list, DBUS_TYPE_INVALID);
+ g_free(mode_list);
+
+ control_set_last_seen_user(uid);
+ }
else if(!strcmp(member, USB_MODE_RESCUE_OFF))
{
usbmoded_set_rescue_mode(false);
@@ -1323,3 +1356,71 @@ gboolean umdbus_get_name_owner_async(const char *name,
return ack;
}
+
+/**
+ * Get uid of sender from D-Bus. This makes a synchronous D-Bus call
+ *
+ * @param name Name of sender from DBusMessage
+ * @return Uid of the sender
+ */
+uid_t umdbus_get_sender_uid(const char *name)
+{
+ LOG_REGISTER_CONTEXT;
+
+ pid_t pid = (pid_t)-1;
+ uid_t uid = (uid_t)-1;
+ DBusMessage *req = 0;
+ DBusMessage *rsp = 0;
+ DBusError err = DBUS_ERROR_INIT;
+ char path[256];
+ struct stat st;
+
+ if(!umdbus_connection)
+ goto EXIT;
+
+ req = dbus_message_new_method_call(DBUS_INTERFACE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ DBUS_GET_CONNECTION_PID_REQ);
+ if( !req ) {
+ log_err("could not create method call message");
+ goto EXIT;
+ }
+
+ if( !dbus_message_append_args(req,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID) ) {
+ log_err("could not add method call parameters");
+ goto EXIT;
+ }
+
+ /* Synchronous D-Bus call */
+ rsp = dbus_connection_send_with_reply_and_block(umdbus_connection, req, -1, &err);
+
+ if( !rsp && dbus_error_is_set(&err) ) {
+ log_err("could not get sender pid for %s: %s: %s", name, err.name, err.message);
+ goto EXIT;
+ }
+
+ if( !dbus_message_get_args(rsp, &err,
+ DBUS_TYPE_UINT32, &pid,
+ DBUS_TYPE_INVALID) ) {
+ log_err("parse error: %s: %s", err.name, err.message);
+ goto EXIT;
+ }
+
+ snprintf(path, sizeof path, "/proc/%d", (int)pid);
+ memset(&st, 0, sizeof st);
+ if( stat(path, &st) != -1 ) {
+ uid = st.st_uid;
+ }
+
+EXIT:
+
+ if( req ) dbus_message_unref(req);
+ if( rsp ) dbus_message_unref(rsp);
+
+ dbus_error_free(&err);
+
+ return uid;
+}
diff --git a/src/usb_moded-dbus.h b/src/usb_moded-dbus.h
index d03e37e..0e0fd8c 100644
--- a/src/usb_moded-dbus.h
+++ b/src/usb_moded-dbus.h
@@ -59,23 +59,24 @@
# define USB_MODE_TARGET_CONFIG_SIGNAL_NAME "sig_usb_taget_mode_config_ind"
/* supported methods */
-# define USB_MODE_STATE_REQUEST "mode_request" /* returns the current mode */
-# define USB_MODE_TARGET_STATE_GET "get_target_state" /* returns the target mode */
-# define USB_MODE_RESCUE_OFF "rescue_off" /* turns rescue mode off so normal mode selection is restored */
-# define USB_MODE_CONFIG_GET "get_config" /* returns the mode set in the config */
-# define USB_MODE_LIST "get_modes" /* returns a comma-separated list of supported modes for ui's */
-# define USB_MODE_HIDE "hide_mode" /* hide a mode */
-# define USB_MODE_UNHIDE "unhide_mode" /* unhide a mode */
-# define USB_MODE_HIDDEN_GET "get_hidden" /* return the hidden modes */
-# define USB_MODE_STATE_SET "set_mode" /* set a mode (only works when connected) */
-# define USB_MODE_CONFIG_SET "set_config" /* set the mode that needs to be activated in the config file */
-# define USB_MODE_NETWORK_SET "net_config" /* set the network config in the config file */
-# define USB_MODE_NETWORK_GET "get_net_config" /* get the network config from the config file */
-# define USB_MODE_WHITELISTED_MODES_GET "get_whitelisted_modes" /* get the list of whitelisted modes */
-# define USB_MODE_WHITELISTED_MODES_SET "set_whitelisted_modes" /* set the list of whitelisted modes */
-# define USB_MODE_WHITELISTED_SET "set_whitelisted" /* sets whether an specific mode is in the whitelist */
-# define USB_MODE_AVAILABLE_MODES_GET "get_available_modes" /* returns a comma separated list of modes which are currently available for selection */
-# define USB_MODE_TARGET_CONFIG_GET "get_target_mode_config" /* returns current target mode configuration */
+# define USB_MODE_STATE_REQUEST "mode_request" /* returns the current mode */
+# define USB_MODE_TARGET_STATE_GET "get_target_state" /* returns the target mode */
+# define USB_MODE_RESCUE_OFF "rescue_off" /* turns rescue mode off so normal mode selection is restored */
+# define USB_MODE_CONFIG_GET "get_config" /* returns the mode set in the config */
+# define USB_MODE_LIST "get_modes" /* returns a comma-separated list of supported modes for ui's */
+# define USB_MODE_HIDE "hide_mode" /* hide a mode */
+# define USB_MODE_UNHIDE "unhide_mode" /* unhide a mode */
+# define USB_MODE_HIDDEN_GET "get_hidden" /* return the hidden modes */
+# define USB_MODE_STATE_SET "set_mode" /* set a mode (only works when connected) */
+# define USB_MODE_CONFIG_SET "set_config" /* set the mode that needs to be activated in the config file */
+# define USB_MODE_NETWORK_SET "net_config" /* set the network config in the config file */
+# define USB_MODE_NETWORK_GET "get_net_config" /* get the network config from the config file */
+# define USB_MODE_WHITELISTED_MODES_GET "get_whitelisted_modes" /* get the list of whitelisted modes */
+# define USB_MODE_WHITELISTED_MODES_SET "set_whitelisted_modes" /* set the list of whitelisted modes */
+# define USB_MODE_WHITELISTED_SET "set_whitelisted" /* sets whether an specific mode is in the whitelist */
+# define USB_MODE_AVAILABLE_MODES_GET "get_available_modes" /* returns a comma separated list of modes which are currently available for selection */
+# define USB_MODE_AVAILABLE_MODES_FOR_USER "get_available_modes_for_user" /* returns a comma separated list of modes which are currently available and permitted for user to select */
+# define USB_MODE_TARGET_CONFIG_GET "get_target_mode_config" /* returns current target mode configuration */
/**
* (Transient) states reported by "sig_usb_state_ind" that are not modes.
diff --git a/src/usb_moded.c b/src/usb_moded.c
index cb230b1..3be3a1f 100644
--- a/src/usb_moded.c
+++ b/src/usb_moded.c
@@ -65,6 +65,10 @@
#include
#include
+#ifdef SAILFISH_ACCESS_CONTROL
+# include
+#endif
+
#ifdef SYSTEMD
# include
#endif
@@ -116,6 +120,7 @@ bool usbmoded_get_rescue_mode (void);
void usbmoded_set_rescue_mode (bool rescue_mode);
bool usbmoded_get_diag_mode (void);
void usbmoded_set_diag_mode (bool diag_mode);
+bool usbmoded_is_mode_permitted (const char *modename, uid_t uid);
void usbmoded_set_cable_connection_delay(int delay_ms);
int usbmoded_get_cable_connection_delay(void);
static gboolean usbmoded_allow_suspend_timer_cb (gpointer aptr);
@@ -340,6 +345,41 @@ void usbmoded_set_diag_mode(bool diag_mode)
}
}
+/* ------------------------------------------------------------------------- *
+ * ACCESS_CHECKS
+ * ------------------------------------------------------------------------- */
+
+bool usbmoded_is_mode_permitted(const char *modename, uid_t uid)
+{
+#ifdef SAILFISH_ACCESS_CONTROL
+ LOG_REGISTER_CONTEXT;
+
+ bool allowed = true;
+ modedata_t *data = 0;
+
+ /* all modes are allowed for root */
+ if( uid == 0 )
+ goto EXIT;
+
+ /* non-dynamic modes are allowed for all */
+ if( !(data = usbmoded_dup_modedata(modename)) )
+ goto EXIT;
+
+ /* dynamic modes are allowed for device owner and denied for others */
+ allowed = sailfish_access_control_hasgroup(uid, "sailfish-system");
+
+EXIT:
+
+ modedata_free(data);
+
+ return allowed;
+
+#else
+ return true;
+
+#endif
+}
+
/* ------------------------------------------------------------------------- *
* CABLE_CONNECT_DELAY
* ------------------------------------------------------------------------- */
diff --git a/src/usb_moded.h b/src/usb_moded.h
index a713870..ec9b79f 100644
--- a/src/usb_moded.h
+++ b/src/usb_moded.h
@@ -75,6 +75,7 @@ bool usbmoded_get_rescue_mode (void);
void usbmoded_set_rescue_mode (bool rescue_mode);
bool usbmoded_get_diag_mode (void);
void usbmoded_set_diag_mode (bool diag_mode);
+bool usbmoded_is_mode_permitted (const char *modename, uid_t uid);
void usbmoded_set_cable_connection_delay(int delay_ms);
int usbmoded_get_cable_connection_delay(void);
void usbmoded_allow_suspend (void);