Allow to use "isu list" by non-privileged user 63/316863/8 accepted/tizen_unified_dev accepted/tizen/unified/20240911.161949 accepted/tizen/unified/dev/20240913.055527 accepted/tizen/unified/x/20240912.013819
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Wed, 28 Aug 2024 12:33:35 +0000 (14:33 +0200)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Mon, 9 Sep 2024 14:24:55 +0000 (16:24 +0200)
Previously, libisu loaded isu package information on the system by
referencing the file system, reading data directly from
/opt/isu/$PKG_NAME/isu.cfg. However, the latter is often only
accessible from the "system" user, thus preventing non-privileged
users such as “owner” from obtaining package information via this
route.

The proper way is to put isud in charge and return package list and
single package information via DBus, as isud is a privileged process
and has full access to the configuration files of isu services.

This commit introduces two new DBus calls in isud:
- List: returns a list of isu packages, allowing "isu list" to be
called by an unprivileged user such as "owner",
- PkgInfo: returns information such as name, version, list of systemd
   services associated with a given isu package.

Change-Id: Ia07f218492692098303d9f4a4428fa32d5fb5017
Signed-off-by: Mateusz Moscicki <m.moscicki2@samsung.com>
packaging/isu.spec
src/isud/isud.c
src/isud/isud.conf
src/libisu/libisu-internal.c
src/libisu/libisu-internal.h
src/libisu/libisu.c

index 2ea50c00aff0c07ee395866e370d1c1158e48dc3..78c51523dbe7b04f44e4c55e9d62cd9374be0f57 100644 (file)
@@ -1,8 +1,8 @@
-%define libisu_version 0.0.1
+%define libisu_version 0.0.2
 %define integration_tests_dir /usr/libexec/isu/
 Summary: Individual Service Upgrade support
 Name:    isu
-Version: 9.0.5
+Version: 9.0.6
 Release: 1
 Source0: %{name}-%{version}.tar.gz
 License: MIT
index 4a6bb33572f0285972df4cce6944924b4a7fd498..80fdf29ea5fc7b5b978675e0774556fb034508a8 100644 (file)
@@ -38,6 +38,8 @@ enum Action {
     InstallDir,
     Uninstall,
     GetVersion,
+    List,
+    PkgInfo,
 };
 
 static GMainLoop *loop;
@@ -64,6 +66,17 @@ static const gchar introspection_xml[] =
 "   <arg type='s' name='version' direction='out'/>"
 "   <arg type='i' name='result' direction='out'/>"
 "  </method>"
+"  <method name='list'>"
+"   <arg type='as' name='result' direction='out'/>"
+"   <arg type='i' name='result' direction='out'/>"
+"  </method>"
+"  <method name='pkg_info'>"
+"   <arg type='s' name='pkg_name' direction='in'/>"
+"   <arg type='s' name='name' direction='out'/>"
+"   <arg type='s' name='version' direction='out'/>"
+"   <arg type='as' name='services' direction='out'/>"
+"   <arg type='i' name='result' direction='out'/>"
+"  </method>"
 " </interface>"
 "</node>";
 
@@ -110,6 +123,10 @@ static char* action2str(enum Action action) {
             return "uninstall";
         case GetVersion:
             return "get_version";
+        case List:
+            return "list";
+        case PkgInfo:
+            return "list";
     }
     return "unknown action";
 }
@@ -119,9 +136,14 @@ static void call_handler(GDBusMethodInvocation *invocation, enum Action action)
     GDBusMessage *incoming_message = g_dbus_method_invocation_get_message(invocation);
     GVariant *body = g_dbus_message_get_body(incoming_message);
     gchar *data;
-    g_variant_get(body, "(s)", &data);
+    if (action == List) {
+        data = "";
+    } else {
+        g_variant_get(body, "(s)", &data);
+    }
     int res;
     gchar *version = NULL;
+    struct _isu_pkg_list *list;
     switch (action) {
         case Install:
             res = isu_install_internal(data);
@@ -135,6 +157,9 @@ static void call_handler(GDBusMethodInvocation *invocation, enum Action action)
         case GetVersion:
             res = isu_get_version_internal(data, &version);
             break;
+        case List:
+            res = isu_list_init_internal(&list);
+            break;
         default:
             res = -1;
     }
@@ -154,6 +179,21 @@ static void call_handler(GDBusMethodInvocation *invocation, enum Action action)
                                                   g_variant_new("(si)", version, res));
             g_free(version);
             break;
+        case List:
+        {
+            GVariantBuilder builder;
+            g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
+            struct _isu_pkg_list_element *element = list->head;
+            while (element != NULL) {
+                g_variant_builder_add(&builder, "s", element->name);
+                element = element->next;
+            }
+
+            GVariant *payload = g_variant_new("(asi)", &builder, res);
+            g_dbus_method_invocation_return_value(invocation, payload);
+            isu_list_free(list);
+            break;
+        }
         default:
             g_dbus_method_invocation_return_value(invocation,
                                                   g_variant_new("(i)", res));
@@ -180,6 +220,44 @@ static void get_version_handler(GDBusMethodInvocation *invocation)
     call_handler(invocation, GetVersion);
 }
 
+static void list_handler(GDBusMethodInvocation *invocation)
+{
+    call_handler(invocation, List);
+}
+
+static void get_pkg_info_handler(GDBusMethodInvocation *invocation)
+{
+    int res = ISU_RES_OK;
+    GDBusMessage *incoming_message = g_dbus_method_invocation_get_message(invocation);
+    GVariant *body = g_dbus_message_get_body(incoming_message);
+    gchar *pkg_name;
+    g_variant_get(body, "(s)", &pkg_name);
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path), "%s/%s/isu.cfg", ISU_PKG_PATH, pkg_name);
+    g_free(pkg_name);
+    struct _isu_pkg_info *pkg_info = get_pkg_info(path);
+
+    GVariantBuilder builder;
+    g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
+
+    char *name = "";
+    char *version = "";
+    if (pkg_info == NULL) {
+        res = ISU_RES_ERR_INTERNAL;
+    } else {
+        name = pkg_info->name;
+        version = pkg_info->version;
+        for (size_t i = 0; i < pkg_info->service_files_len; i++) {
+            g_variant_builder_add(&builder, "s", pkg_info->service_files[i]);
+        }
+    }
+    GVariant *payload = g_variant_new("(ssasi)", name, version, &builder, res);
+    g_dbus_method_invocation_return_value(invocation, payload);
+    if (pkg_info != NULL) {
+        isu_pkg_info_free(pkg_info);
+    }
+}
+
 static void method_call_handler(GDBusConnection *conn,
                             const gchar *sender,
                             const gchar *object_path,
@@ -199,6 +277,10 @@ static void method_call_handler(GDBusConnection *conn,
         uninstall_handler(invocation);
     } else if (g_strcmp0(method_name, "get_version") == 0) {
         get_version_handler(invocation);
+    } else if (g_strcmp0(method_name, "list") == 0) {
+        list_handler(invocation);
+    } else if (g_strcmp0(method_name, "pkg_info") == 0) {
+        get_pkg_info_handler(invocation);
     } else {
         SLOGE("Unsupported method: %s", method_name);
     }
index a86c1bf48543f1fe1b10f693ff3b8bfba8835d32..c4b82aeaccb57bf94527a4443c31042b5763677b 100644 (file)
                       send_interface="org.tizen.system.isu"/>
        </policy>
 
-       <policy user="owner">
-               <allow send_destination="org.tizen.system.isu"
-                      send_interface="org.tizen.system.isu"/>
-       </policy>
-
        <policy context="default">
                <deny own="org.tizen.system.isu"/>
                <deny send_destination="org.tizen.system.isu"/>
+               <check send_destination="org.tizen.system.isu"
+                      privilege="http://tizen.org/privilege/isu.admin"/>
        </policy>
 </busconfig>
index 524390084ae1af1685bb0c50d19bf2a8983d71ad..28d77d1f6e3fa0b8a9c06900da00a4890dcb134d 100644 (file)
@@ -160,10 +160,9 @@ exit:
     return result;
 }
 
-isu_result isu_dbus_call(const char *method, const char *parameter, char **outparam)
-{
+static isu_result isu_dbus_make_call(const char *method, GVariant *input_param, GVariant **body) {
     GError *error = NULL;
-    int res;
+    int res = ISU_RES_OK;
     GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
     if (conn == NULL) {
         SLOGE("isu_%s: g_bus_get_sync error: %s", method, error ? error->message : "");
@@ -182,7 +181,7 @@ isu_result isu_dbus_call(const char *method, const char *parameter, char **outpa
         return ISU_RES_ERR_INTERNAL;
     }
 
-    g_dbus_message_set_body(msg, g_variant_new("(s)", parameter));
+    g_dbus_message_set_body(msg, input_param);
 
     GDBusMessage *reply = g_dbus_connection_send_message_with_reply_sync(
                                 conn,
@@ -207,12 +206,27 @@ isu_result isu_dbus_call(const char *method, const char *parameter, char **outpa
         goto exit;
     }
 
-    GVariant *body = g_dbus_message_get_body(reply);
-    if (body == NULL) {
+    *body = g_dbus_message_get_body(reply);
+    if (*body == NULL) {
         SLOGE("isu_%s: get message body error", method);
         res = ISU_RES_ERR_INTERNAL;
         goto exit;
     }
+    *body = g_variant_ref(*body);
+exit:
+    g_object_unref(msg);
+    g_object_unref(reply);
+    g_object_unref(conn);
+    return res;
+}
+
+isu_result isu_dbus_call(const char *method, const char *parameter, char **outparam)
+{
+    GVariant *body = NULL;
+    isu_result res = isu_dbus_make_call(method, g_variant_new("(s)", parameter), &body);
+    if (res != ISU_RES_OK) {
+        goto exit;
+    }
 
     if (strcmp(method, "get_version") == 0) {
         gchar *version;
@@ -229,9 +243,110 @@ isu_result isu_dbus_call(const char *method, const char *parameter, char **outpa
         g_variant_get(body, "(i)", &res);
     }
 exit:
-    g_object_unref(msg);
-    g_object_unref(reply);
-    g_object_unref(conn);
+    if (body != NULL) {
+        g_variant_unref(body);
+    }
+    return res;
+}
+
+isu_result isu_dbus_list_call(struct _isu_pkg_list **list) {
+    assert(list);
+    GVariant *body = NULL;
+    const gchar **str_array = NULL;
+    isu_result res = isu_dbus_make_call("list", NULL, &body);
+    if (res != ISU_RES_OK) {
+        goto exit;
+    }
+
+    GVariant *array;
+    g_variant_get(body, "(@asi)", &array, &res);
+
+    gsize len;
+    str_array = g_variant_get_strv(array, &len);
+    g_variant_unref(array);
+
+    *list = malloc(sizeof(**list));
+    if (*list == NULL) {
+        SLOGE("Memory allocation error (%d): %m", errno);
+        res = ISU_RES_ERR_INTERNAL;
+        goto exit;
+    }
+    (*list)->head = NULL;
+    (*list)->cur = NULL;
+
+    struct _isu_pkg_list_element *prev = NULL;
+    for (size_t i = 0; i < len; i++) {
+        struct _isu_pkg_list_element *element = malloc(sizeof(*element));
+        if (element == NULL) {
+            SLOGE("Memory allocation error (%d): %m");
+            isu_list_free(*list);
+            *list = NULL;
+            res = ISU_RES_ERR_INTERNAL;
+            goto exit;
+        }
+        element->name = strdup(str_array[i]);
+        element->next = NULL;
+        if (prev == NULL) {
+            (*list)->head = element;
+            (*list)->cur = element;
+        } else {
+            prev->next = element;
+        }
+        prev = element;
+    }
+exit:
+    if (body != NULL) {
+        g_variant_unref(body);
+    }
+    if (str_array != NULL) {
+        g_free(str_array);
+    }
+    return res;
+}
+
+isu_result isu_pkg_info_call(char *pkg_name, struct _isu_pkg_info **pkg_info)
+{
+    assert(pkg_name);
+    assert(pkg_info);
+    GVariant *body = NULL;
+    const gchar **service_array = NULL;
+    isu_result res = isu_dbus_make_call("pkg_info", g_variant_new("(s)", pkg_name), &body);
+    if (res != ISU_RES_OK) {
+        goto exit;
+    }
+    GVariant *array;
+    gchar *name;
+    gchar *version;
+    g_variant_get(body, "(ss@asi)", &name, &version, &array, &res);
+
+    gsize len;
+    service_array = g_variant_get_strv(array, &len);
+    struct _isu_pkg_info *_pkg_info = malloc(sizeof(*_pkg_info));
+    if (_pkg_info == NULL) {
+        res = ISU_RES_ERR_INTERNAL;
+        goto exit;
+    }
+    _pkg_info->name = name;
+    _pkg_info->version = version;
+    _pkg_info->service_files_len = len;
+    _pkg_info->service_files = malloc(len*sizeof(*_pkg_info->service_files));
+    if (_pkg_info->service_files == NULL) {
+        free(_pkg_info);
+        res = ISU_RES_ERR_INTERNAL;
+        goto exit;
+    }
+    for (size_t i = 0; i < len; i++) {
+        _pkg_info->service_files[i] = strdup(service_array[i]);
+    }
+
+    *pkg_info = _pkg_info;
+exit:
+    if (body != NULL) {
+        g_variant_unref(body);
+    }
+    if (service_array != NULL) {
+        g_free(service_array);
+    }
     return res;
 }
 
@@ -774,3 +889,72 @@ isu_result isu_get_version_internal(const char *pkgId, char **version)
 
     return *version ? ISU_RES_OK : ISU_RES_ERR_NOT_EXIST;
 }
+
+bool is_valid_isu_pkg(const char *path)
+{
+    assert(path);
+
+    struct stat statbuff;
+    char pathbuff[PATH_MAX+8];
+    snprintf(pathbuff, sizeof(pathbuff), "%s/%s", path, ISU_CFG);
+
+    return stat(pathbuff, &statbuff) == 0;
+}
+
+isu_result isu_list_init_internal(struct _isu_pkg_list **pkg_list)
+{
+    assert(pkg_list);
+
+    RET_IF_FEATURE_NOT_SUPPORTED(ISU_RES_ERR_FEATURE);
+    *pkg_list = malloc(sizeof(struct _isu_pkg_list));
+    if (*pkg_list == NULL) {
+        SLOGE("Memory allocation error (%d): %m\n", errno);
+        return ISU_RES_ERR_INTERNAL;
+    }
+
+    (*pkg_list)->head = NULL;
+    (*pkg_list)->cur = NULL;
+    DIR *dir = opendir(ISU_PKG_PATH);
+    if (dir == NULL) {
+        SLOGE("Open dir %s error (%d): %m\n", ISU_PKG_PATH, errno);
+        isu_list_free(*pkg_list);
+        *pkg_list = NULL;
+        return ISU_RES_ERR_INTERNAL;
+    }
+
+    struct _isu_pkg_list_element *prev = NULL;
+    struct dirent *entry;
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_type != DT_DIR) continue;
+
+        char pathbuff[PATH_MAX];
+        snprintf(pathbuff, sizeof(pathbuff), "%s/%s", ISU_PKG_PATH, entry->d_name);
+        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+            continue;
+
+        if (is_valid_isu_pkg(pathbuff)) {
+            struct _isu_pkg_list_element *element = malloc(sizeof(struct _isu_pkg_list_element));
+            if (element == NULL) {
+                SLOGE("Memory allocation error (%d): %m\n", errno);
+                isu_list_free(*pkg_list);
+                closedir(dir);
+                return ISU_RES_ERR_INTERNAL;
+            }
+            element->name = strdup(entry->d_name);
+            element->next = NULL;
+            if (prev == NULL) {
+                (*pkg_list)->head = element;
+                (*pkg_list)->cur = element;
+            } else {
+                prev->next = element;
+            }
+            prev = element;
+        } else {
+            SLOGW("Invalid ISU package directory: %s\n", pathbuff);
+        }
+    }
+
+    closedir(dir);
+
+    return ISU_RES_OK;
+}
index e0dfdf10fb9fc53a239876d0156b913857585294..f4bdda630cd045607d6cba5ab5b991f303b479a0 100644 (file)
@@ -64,6 +64,7 @@ struct _isu_pkg_info {
     char **service_files;
 };
 
+bool is_valid_isu_pkg(const char *path);
 isu_result isu_install_dir(const char *path);
 isu_result isu_install_dir_internal(const char *path);
 isu_result isu_install_internal(const char *path);
@@ -72,4 +73,7 @@ isu_result isu_get_version_internal(const char *pkgId, char **version);
 isu_file_res is_isu_file(const char *path, pid_t pid, char **source_path);
 struct _isu_pkg_info* get_pkg_info(const char *isu_cfg_path);
 isu_result isu_dbus_call(const char *method, const char *parameter, char **outparam);
+isu_result isu_dbus_list_call(struct _isu_pkg_list **list);
+isu_result isu_pkg_info_call(char *pkg_name, struct _isu_pkg_info **pkg_info);
 isu_result remove_dir_contents(const char *path);
+isu_result isu_list_init_internal(struct _isu_pkg_list **list);
index efdfe6417af9af73dca0e75763d3bd10aa7358f7..a2847dabdbe81830766a679e34edbb687bc4baa7 100644 (file)
 #include "libisu-internal.h"
 
 
-static bool is_valid_isu_pkg(const char *path)
-{
-    assert(path);
-
-    struct stat statbuff;
-    char pathbuff[PATH_MAX+8];
-    snprintf(pathbuff, sizeof(pathbuff), "%s/%s", path, ISU_CFG);
-
-    return stat(pathbuff, &statbuff) == 0;
-}
-
 /*
  * Public API Functions
  */
 
 isu_pkg_list isu_list_init()
 {
-    RET_IF_FEATURE_NOT_SUPPORTED(NULL);
-    struct _isu_pkg_list *pkg_list = malloc(sizeof(struct _isu_pkg_list));
-    if (pkg_list == NULL) {
-        SLOGE("Memory allocation error (%d): %m\n", errno);
-        return NULL;
-    }
-
-    pkg_list->head = NULL;
-    pkg_list->cur = NULL;
-    DIR *dir = opendir(ISU_PKG_PATH);
-    if (dir == NULL) {
-        SLOGE("Open dir %s error (%d): %m\n", ISU_PKG_PATH, errno);
-        isu_list_free(pkg_list);
-        return NULL;
-    }
-
-    struct _isu_pkg_list_element *prev = NULL;
-    struct dirent *entry;
-    while ((entry = readdir(dir)) != NULL) {
-        if (entry->d_type != DT_DIR) continue;
-
-        char pathbuff[PATH_MAX];
-        snprintf(pathbuff, sizeof(pathbuff), "%s/%s", ISU_PKG_PATH, entry->d_name);
-        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
-            continue;
-
-        if (is_valid_isu_pkg(pathbuff)) {
-            struct _isu_pkg_list_element *element = malloc(sizeof(struct _isu_pkg_list_element));
-            if (element == NULL) {
-                SLOGE("Memory allocation error (%d): %m\n", errno);
-                isu_list_free(pkg_list);
-                closedir(dir);
-                return NULL;
-            }
-            element->name = strdup(entry->d_name);
-            element->next = NULL;
-            if (prev == NULL) {
-                pkg_list->head = element;
-                pkg_list->cur = element;
-            } else {
-                prev->next = element;
-            }
-            prev = element;
-        } else {
-            SLOGW("Invalid ISU package directory: %s\n", pathbuff);
+    struct _isu_pkg_list *list = NULL;
+    if (isu_dbus_list_call(&list) != ISU_RES_OK) {
+        if (list != NULL) {
+            isu_list_free(list);
         }
+        return NULL;
     }
 
-    closedir(dir);
-
-    return pkg_list;
+    return list;
 }
 
 isu_result isu_list_free(isu_pkg_list pkg_list)
@@ -136,12 +83,16 @@ isu_pkg_info isu_list_next(isu_pkg_list pkg_list)
     if (pkg_list_i->cur == NULL)
         return NULL;
 
-    char isu_cfg_path[PATH_MAX];
-    snprintf(isu_cfg_path, sizeof(isu_cfg_path), "%s/%s/%s", ISU_PKG_PATH, pkg_list_i->cur->name, ISU_CFG);
-    isu_pkg_info pkg_info = get_pkg_info(isu_cfg_path);
-
+    struct _isu_pkg_info *_pkg_info = NULL;
+    isu_result res = isu_pkg_info_call(pkg_list_i->cur->name, &_pkg_info);
+    if (res != ISU_RES_OK) {
+        if (_pkg_info != NULL) {
+            free(_pkg_info);
+        }
+        return NULL;
+    }
     pkg_list_i->cur = pkg_list_i->cur->next;
-    return pkg_info;
+    return (isu_pkg_info)_pkg_info;
 }
 
 isu_result isu_pkg_get_name(isu_pkg_info pkg_info, char *name, size_t len)