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