Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieve a not-empty Application ID for un-sandboxed clients that use cgroups #581

Closed

Conversation

carlocastoldi
Copy link
Contributor

@carlocastoldi carlocastoldi commented May 3, 2021

Following #579, I present a simple but yet effective solution to retrieve the Application ID from PID's cgroup.
The usage of cgroups to manage processes by DEs is an innovative approach pushed forward by both KDE & GNOME, and uses the application's ID to distinguish the various resources.

I thus intend to check if the cgroup of the client is compliant to the standard and, if it is, use the contained ID. Otherwise proceed with how we have always done: assigning "" as app_id.

You can test this patch by running my demo with an appropriate .desktop file or with one of the two following command:

systemd-run --user --scope -u app-com.myapp.InhibitorTest-`uuidgen | sed s/-//g`.scope   ./xdg-portal-inhibitor-test-gtk4
systemd-run --user -u app-com.myapp.InhibitorTest@`uuidgen | sed s/-//g`.service   ./xdg-portal-inhibitor-test-gtk4

Currently missing

Documentation update. I'd write two lines in Chapter 1. Common Conventions informing that a non-empty app_id is passed down only if the launcher is compliant with the standard: https://systemd.io/DESKTOP_ENVIRONMENTS/

@swick
Copy link
Contributor

swick commented May 4, 2021

This is a good change IMO. However some code doesn't use XdpAppInfo's XDP_APP_INFO_KIND_HOST to determine that the client is non-sandboxed but instead just checks the app_id for NULL. Non-sandboxed clients for which the app id can be determined then get the same treatment as sandboxed clients.

@carlocastoldi
Copy link
Contributor Author

However some code doesn't use XdpAppInfo's XDP_APP_INFO_KIND_HOST to determine that the client is non-sandboxed but instead just checks the app_id for NULL.

In the current implementation when would the app_id be NULL? As of now shouldn't it be set to "" for host applications?

To avoid changing the code without being sure, I post here the potential tests that perhaps could look at XdpAppInfo's kind:

if (strcmp (app_id, "") == 0)

if (app_id == NULL || app_id[0] == '\0')

if (app_id == NULL)

if (g_str_equal (app_id, ""))
(doubt)
(doubt. There is a multitude of similar tests)
if (g_str_equal (app_id, ""))
(doubt)

Additionally there are many checks on app_id in document-portal/document-portal.c, but I am not sure if they are actually testing if the client is an host or just that if the app_id is empty/NULL (similarly to src/wallpaper.c above).

@carlocastoldi
Copy link
Contributor Author

Non-sandboxed clients for which the app id can be determined then get the same treatment as sandboxed clients.

Where could this not be desirable?

@Erick555
Copy link

Erick555 commented May 4, 2021

I guess in case of document portal trying unsandboxed apps to use it isn't desirable although the portal will fail anyway as it won't be able to determine app file permissions which is unfortunately also true for sandboxed but non-flatpak/snap apps.

src/xdp-utils.c Outdated Show resolved Hide resolved
src/xdp-utils.c Outdated Show resolved Hide resolved
src/xdp-utils.c Outdated Show resolved Hide resolved
src/xdp-utils.c Outdated Show resolved Hide resolved
src/xdp-utils.c Outdated Show resolved Hide resolved
@matthiasclasen
Copy link
Contributor

However some code doesn't use XdpAppInfo's XDP_APP_INFO_KIND_HOST to determine that the client is non-sandboxed but instead just checks the app_id for NULL.

In the current implementation when would the app_id be NULL? As of now shouldn't it be set to "" for host applications?

To avoid changing the code without being sure, I post here the potential tests that perhaps could look at XdpAppInfo's kind:

if (strcmp (app_id, "") == 0)

We should pass down the appinfo here.

if (app_id == NULL || app_id[0] == '\0')

if (app_id == NULL)

if (g_str_equal (app_id, ""))

(doubt)

Here too.


(doubt. There is a multitude of similar tests)

if (g_str_equal (app_id, ""))

(doubt)

Not sure it makes a difference here, but we have the app_info right there.

@carlocastoldi carlocastoldi force-pushed the app-id-for-host-portals branch 3 times, most recently from 22c58df to 2fc9546 Compare June 28, 2021 15:43
@carlocastoldi
Copy link
Contributor Author

@matthiasclasen: "We should pass down the appinfo here."

I was trying to do so, bu is there a simple way to retrieve XdgAppInfo from XdgDomain or from xdp_fuse_lookup()? If yes, can I change the domain struct so that it has an appinfo instead of the app_id, keeping the same semantic (i.e., NULL for root, by-app, non-app id)?

Problem is that I don't think I am able to retrieve the AppInfo from there. I can see where the app_id is coming from, but atm I think I lack the knowledge in order to get something else.
I think I need to modify the XdpDomain because, by trying to pass down the appinfo, I followed the following path:

P.S. similarly here:

inode = ensure_by_app_inode (parent, name);

@carlocastoldi carlocastoldi changed the title [WIP] Retrieve a not-empty Application ID for un-sendboxed clients that use cgroups Retrieve a not-empty Application ID for un-sendboxed clients that use cgroups Nov 26, 2021
@mwleeds mwleeds changed the title Retrieve a not-empty Application ID for un-sendboxed clients that use cgroups Retrieve a not-empty Application ID for un-sandboxed clients that use cgroups Feb 17, 2022
@@ -27,6 +27,7 @@ CLEANFILES += $(dbus_service_DATA)
CLEANFILES += $(systemduserunit_DATA)
EXTRA_DIST += $(service_in_files)

CFLAGS = -lsystemd
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps make it a compile-time optional, enabled by default?

char *unit, **dash_splits, **at_splits, **app_id;
int res, len;

res = sd_pid_get_user_unit(pid, &unit);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit: missing space before (

Comment on lines +179 to +183
/*
* the session might not be managed by systemd
* or there could be error fetching own systemd units
* or the unit might not be started by the the desktop environment (e.g. it's a script run from terminal)
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this comment is necessary - probably better to mention these cases in the commit message?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a helpful comment to me

* or there could be error fetching own systemd units
* or the unit might not be started by the the desktop environment (e.g. it's a script run from terminal)
*/
if (res == -ENODATA || res < 0 || !g_str_has_prefix (unit, "app-"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

g_str_has_prefix is not NULL-safe, so !unit || !g_str_has_prefix (...)

Comment on lines +205 to +206
g_strfreev(at_splits);
g_strfreev (dash_splits);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove these calls by declaring these variables as g_auto(GStrv) at_splits = NULL and g_auto(GStrv) dash_splits = NULL

Comment on lines +199 to +202
else {
at_splits = g_strsplit (dash_splits[len-1], "@", 2);
app_id = &at_splits[0];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to check if (g_str_has_suffix (unit, ".service")) here, and let the else condition set the app id to an empty string and return

Copy link
Collaborator

@smcv smcv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @matthiasclasen that using XdpAppInfo everywhere is a prerequisite for doing this.

I think if someone wants to move this forward (@GeorgesStavracas? @mwleeds?) then the way to do it would be:

  • first, go through all the places that use an app ID for decisions, and make sure they get an XdpAppInfo instead, with kind == XDP_APP_INFO_KIND_HOST instead of g_str_equal (app_id, "") as the way to tell that the caller is un-sandboxed;
  • then, reapply changes similar to these on top of that refactoring

@@ -27,6 +27,7 @@ CLEANFILES += $(dbus_service_DATA)
CLEANFILES += $(systemduserunit_DATA)
EXTRA_DIST += $(service_in_files)

CFLAGS = -lsystemd
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should become a check for libsystemd in the build system (Flatpak has a suitable check in its configure.ac), keeping libsystemd optional. If compiled without libsystemd support, the answer should always be "I don't know what app this is".

if ((*str)[i] == '\\' && (*str)[i+1] == 'x')
{
hex[2] = (*str)[i+2];
hex[3] = (*str)[i+3];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If hex[2] is \0 then this will be out of bounds.

Comment on lines +165 to +168
char **splits = g_strsplit(*str, "\\x\\", -1);
g_free (*str);
*str = g_strjoinv ("", splits);
g_strfreev (splits);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After going to heroic lengths to do the decoding in-place with no extra allocations, this does a lot of malloc/free cycles at the end...

I think a better approach to this would be to take a const char * argument and iterate through it, appending characters to a GString buffer, returning a newly allocated string with g_string_free (gstr, FALSE).

Comment on lines +193 to +194
* app[-<launcher>]-<ApplicationID>-<RANDOM>.scope
* app[-<launcher>]-<ApplicationID>-autostart.service -> no longer true since systemd v248
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of relying on the part after the app-ID to not contain any additional - (e.g. app-gnome-com.example.Foobar-123-456.service would break this logic), it might make more sense to assume that the <launcher> does not contain dots (in practice it's something like gnome, flatpak or steam, which doesn't), so that the logic could be:

  • remove trailing .service, .slice or .scope
  • remove an @ and anything after it, if present
  • split into dash-separated components
  • if second component contains at least two dots, it's the app ID
  • else if third component contains at least two dots, it's the app ID

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I came up with a regex that seems to work for this: "^app-(?:[[:alnum:]]+\\-)?(.+?\\..+?\\..+?)(?:[@\\-][0-9]*)?(?:-autostart)?(?:\\.scope|\\.service|\\.slice)$"

But unfortunately the unit returned by systemd for gnome-software (which runs as /usr/bin/gnome-software --gapplication-service) is app-gnome-gnome\x2dsoftware\x2dservice-9502.scope (where 9502 is the pid in my case) so parsing that in any way is not going to get us the correct app ID of org.gnome.Software.

char *unit, **dash_splits, **at_splits, **app_id;
int res, len;

res = sd_pid_get_user_unit(pid, &unit);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unit is leaked, at the moment.

@mwleeds
Copy link
Contributor

mwleeds commented Feb 28, 2022

However some code doesn't use XdpAppInfo's XDP_APP_INFO_KIND_HOST to determine that the client is non-sandboxed but instead just checks the app_id for NULL.

In the current implementation when would the app_id be NULL? As of now shouldn't it be set to "" for host applications?
To avoid changing the code without being sure, I post here the potential tests that perhaps could look at XdpAppInfo's kind:

if (strcmp (app_id, "") == 0)

We should pass down the appinfo here.

Seems to me we need two variants of document_entry_get_permissions(), one that operates on a XdpAppInfo and one on an app id, because we only have the app id available for the app being granted permissions in portal_grant_permissions() (see target_app_id) not an XdpAppInfo. Conceptually though I'm not sure when one sandboxed app would be granting another sandboxed app permissions to a document (or maybe it's intended for use by unsandboxed processes).

@mwleeds
Copy link
Contributor

mwleeds commented Feb 28, 2022

@matthiasclasen: "We should pass down the appinfo here."

I was trying to do so, bu is there a simple way to retrieve XdgAppInfo from XdgDomain or from xdp_fuse_lookup()? If yes, can I change the domain struct so that it has an appinfo instead of the app_id, keeping the same semantic (i.e., NULL for root, by-app, non-app id)?

Problem is that I don't think I am able to retrieve the AppInfo from there. I can see where the app_id is coming from, but atm I think I lack the knowledge in order to get something else. I think I need to modify the XdpDomain because, by trying to pass down the appinfo, I followed the following path:

P.S. similarly here:

inode = ensure_by_app_inode (parent, name);

Looks like there is no need to change document-portal-fuse.c to use XdpAppInfo because it's not making any decisions based on xdp_app_info_get_id returning NULL or "". It is only ever working with valid non-empty app IDs for XDP_DOMAIN_BY_APP and XDP_DOMAIN_APP, and for XDP_DOMAIN_ROOT the access is not dependent on app ID.

We can test the document portal with these patches (when they're ready) just to be sure, but I think it would be a bit pointless since the document portal doesn't use the fuse filesystem to expose files to unsandboxed callers, and passes them the host path directly instead.

@Erick555
Copy link

Erick555 commented Feb 28, 2022

it would be a bit pointless since the document portal doesn't use the fuse filesystem to expose files to unsandboxed callers, and passes them the host path directly instead.

There may be sandboxed but not recognized as such callers though. I wonder if there could be a opt-in switch to force document portal to use fuse filesystem for those.

mwleeds added a commit that referenced this pull request Mar 1, 2022
As discussed in #581, we need to change how we handle
xdp_app_info_get_id() returning an empty string. Currently, this only
happens when the calling pid is unsandboxed (not Flatpak or Snap) so it
has so far been safe to assume that xdp_app_info_get_id() will return
the empty string if and only if xdp_app_info_is_host() returns TRUE.
However since we are about to add support for setting the app ID in
XdpAppInfo even for unsandboxed callers, we need to:
(a) Only use xdp_app_info_is_host() if the intention is to give
unsandboxed processes access to something, and
(b) Only check if the app ID is the empty string if we're doing so to
handle that case gracefully, e.g. by leaving out the app ID from a
user-facing message.

This commit does entail a slight change in behavior: for those portals
that use the app ID in the permission store even if the app ID is the
empty string, such as the wallpaper portal, the behavior before this
commit is that all unsandboxed apps share the same permission since the
empty string is used as the key. After this commit, only unsandboxed
callers for whom an app ID could not be determined share the same
permission (in other words granting a permission for one grants it for
all). I don't know if storing the empty string as the app ID in the
permission store is actually a good idea, but doing so is not a new
change in this commit, so if we want to change that we can do it
separately.
@mwleeds
Copy link
Contributor

mwleeds commented Mar 1, 2022

Preparatory PR for this is here: #718

@mwleeds
Copy link
Contributor

mwleeds commented Mar 1, 2022

it would be a bit pointless since the document portal doesn't use the fuse filesystem to expose files to unsandboxed callers, and passes them the host path directly instead.

There may be sandboxed but not recognized as such callers though. I wonder if there could be a opt-in switch to force document portal to use fuse filesystem for those.

In a sense there already is a switch in that you can create /.flatpak-info in the filesystem of a process and it will be treated as sandboxed, which WebKitGTK already does, but it's a bit of a hack. Alternatively the document portal could check if the calling process has access to a file. It already does that for sandboxed callers using /proc/self/fd. Probably best to open a separate issue to discuss this if you want, since it would be an independent change from the ones here.

mwleeds added a commit that referenced this pull request Mar 1, 2022
As discussed in #581, we need to change how we handle
xdp_app_info_get_id() returning an empty string. Currently, this only
happens when the calling pid is unsandboxed (not Flatpak or Snap) so it
has so far been safe to assume that xdp_app_info_get_id() will return
the empty string if and only if xdp_app_info_is_host() returns TRUE.
However since we are about to add support for setting the app ID in
XdpAppInfo even for unsandboxed callers, we need to:
(a) Only use xdp_app_info_is_host() if the intention is to give
unsandboxed processes access to something, and
(b) Only check if the app ID is the empty string if we're doing so to
handle that case gracefully, e.g. by leaving out the app ID from a
user-facing message.

This commit does entail a slight change in behavior: for those portals
that use the app ID in the permission store even if the app ID is the
empty string, such as the wallpaper portal, the behavior before this
commit is that all unsandboxed apps share the same permission since the
empty string is used as the key. After this commit, only unsandboxed
callers for whom an app ID could not be determined share the same
permission (in other words granting a permission for one grants it for
all). I don't know if storing the empty string as the app ID in the
permission store is actually a good idea, but doing so is not a new
change in this commit, so if we want to change that we can do it
separately.
@Erick555
Copy link

Erick555 commented Mar 1, 2022

The /.flatpak-info or GTK_USE_PORTAL=1 won't cut it for document portal because the portal assumes sandbox==flatpak and consults flatpak metadata for file permissions instead of checking if actual file exist in sandbox.

https://github.com/flatpak/xdg-desktop-portal/blob/main/document-portal/document-portal.c#L648

https://github.com/flatpak/xdg-desktop-portal/blob/main/document-portal/document-portal.c#L569

Obviously the former will always fail for non-flatpak sandbox and portal will pass the host path even if it doesn't exist in sandbox. So setting non-empty appid for document portal is pointless as you observed because it's impossible to use it anyway but I wonder if it can be made meaningful in future?

The issue for that exist: #680

@mwleeds
Copy link
Contributor

mwleeds commented Mar 2, 2022

Closing this in favor of #719 which is in better shape but also not ready to be merged.

@mwleeds mwleeds closed this Mar 2, 2022
GeorgesStavracas pushed a commit that referenced this pull request Mar 7, 2022
As discussed in #581, we need to change how we handle
xdp_app_info_get_id() returning an empty string. Currently, this only
happens when the calling pid is unsandboxed (not Flatpak or Snap) so it
has so far been safe to assume that xdp_app_info_get_id() will return
the empty string if and only if xdp_app_info_is_host() returns TRUE.
However since we are about to add support for setting the app ID in
XdpAppInfo even for unsandboxed callers, we need to:
(a) Only use xdp_app_info_is_host() if the intention is to give
unsandboxed processes access to something, and
(b) Only check if the app ID is the empty string if we're doing so to
handle that case gracefully, e.g. by leaving out the app ID from a
user-facing message.

This commit does entail a slight change in behavior: for those portals
that use the app ID in the permission store even if the app ID is the
empty string, such as the wallpaper portal, the behavior before this
commit is that all unsandboxed apps share the same permission since the
empty string is used as the key. After this commit, only unsandboxed
callers for whom an app ID could not be determined share the same
permission (in other words granting a permission for one grants it for
all). I don't know if storing the empty string as the app ID in the
permission store is actually a good idea, but doing so is not a new
change in this commit, so if we want to change that we can do it
separately.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants