After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 136571 - Problems running as untrusted client
Problems running as untrusted client
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Widget: Other
2.2.x
Other Linux
: Normal normal
: ---
Assigned To: gtk-bugs
gtk-bugs
: 155053 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2004-03-08 17:24 UTC by Mike Paul
Modified: 2011-02-04 16:10 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
gdk-ssh-X.patch (4.61 KB, patch)
2006-03-31 03:19 UTC, Ed Catmur
none Details | Review
gdk-gtk-trusted.patch (16.30 KB, patch)
2006-05-07 01:24 UTC, Ed Catmur
none Details | Review
gdk-gtk-trusted.patch (16.27 KB, patch)
2006-05-18 06:11 UTC, Ed Catmur
none Details | Review
gdk-gtk-trusted.patch (16.47 KB, patch)
2006-05-21 05:55 UTC, Ed Catmur
none Details | Review
gdk-gtk-trusted.patch (13.93 KB, patch)
2006-05-21 16:38 UTC, Ed Catmur
none Details | Review
gdk-gtk-trusted.patch (13.60 KB, patch)
2006-05-21 16:39 UTC, Ed Catmur
none Details | Review
gdk-gtk-trusted.patch (18.05 KB, patch)
2006-05-24 17:41 UTC, Ed Catmur
none Details | Review

Description Mike Paul 2004-03-08 17:24:45 UTC
When GTK connects to an X11 display using an "untrusted" xauth key, it has
severe problems, such as crashing with BadAtom and BadWindow errors, along
with less-severe problems such as failing to use the user's chosen theme,
and falling back to the default instead.  OpenSSH now creates untrusted
xauth keys by default, so anyone trying to run a GTK program through an
SSH-tunneled X11 connection will experience these problems unless they know
to use OpenSSH's new "-Y" option instead of "-X".
Comment 1 Owen Taylor 2004-03-08 18:00:51 UTC
What's an untrusted xauth key? Is this related
to the X security extension? On what operating system?
Comment 2 Owen Taylor 2004-03-08 18:45:35 UTC
I think in any case, you need to give us exact details of
what XErrors are occuring (backtraces when the application
is run with the --sync option)

Some things like getting the default theme are pretty
much inevitable ... the security mechanism aren't letting
GTK+ look at the other clients properties. (This possibly
could be fixed by adding code to gnome-settings-daemon
to export the information to "untrusted clients" but
I don't really know if that's possible.)
Comment 3 Manish Singh 2004-03-08 19:55:31 UTC
Hmm, reading
http://davinci01.man.ac.uk/aix433/x11/specs/pdf/security.htm, there
are possible issues with keyboard grabs too.
Comment 4 Manish Singh 2004-03-08 23:52:41 UTC
I've tried it out for myself with ssh 3.8, XFree 4.3, on Linux.

Any window using GTK_WIN_POS_CENTER will cause a problem, since
XQueryPointer on the root window is disallowed (in the above url, it
says that an exception might be warranted for the root window since
emacs needs this too).
Comment 5 Elijah Newren 2004-06-19 18:43:30 UTC
Mass changing gtk+ bugs with target milestone of 2.4.2 to target 2.4.4, as
Matthias said he was trying to do himself on IRC and was asking for help with. 
If you see this message, it means I was successful at fixing the borken-ness in
bugzilla :)  Sorry for the spam; just query on this message and delete all
emails you get with this message, since there will probably be a lot.
Comment 6 Owen Taylor 2004-10-12 15:13:01 UTC
*** Bug 155053 has been marked as a duplicate of this bug. ***
Comment 7 Matthias Clasen 2004-10-13 02:41:37 UTC
If we can figure out a way to find out if we are trusted, it might be a good
idea to emit a warning if we are not.
Comment 8 Matthias Clasen 2004-10-13 22:18:27 UTC
As owen points out, it might be worth to first try and identify all places where
gdk issues requests on other clients resources, and add traps around those.
Comment 9 Manish Singh 2004-10-13 22:28:29 UTC
Well, perhaps the Security Extension spec should be revisited, it's rather dated
and doesn't seem like the most perfect thing (see comment about root window
exception above)
Comment 10 Owen Taylor 2004-10-13 22:55:39 UTC
Turns out that things *mostly* actually don't work that badly. 

Problems I ran into with a quick try with gtk-demo:

 - XQueryPointer fails on the root window, causing menus, 
   gdk_window_at_pointer(), etc, etc, to die.

 - The keyboard can't be grabbed, causing problems for menus,
   drag-and-drop, etc. We typically grab pointer and keyboard
   and fail when we can't grab either.

 - GtkEntry seems to have a bug when selection retrieval from
   the clipboard fails and spews:
 
(lt-gtk-demo:28248): Gtk-CRITICAL **: IA__gtk_entry_set_text: assertion `text !=
NULL' failed

 - Starting a failed drag produces:

(lt-gtk-demo:28248): Gtk-CRITICAL **: IA__gtk_drag_set_icon_default: assertion
`GDK_IS_DRAG_CONTEXT (context)' failed

 - XSETTINGS doesn't work. (Not much we can do about that)

The XQueryPointer problem is maybe not that hard to work around - error
trapping the XQueryPointer on the root window is free and it's not
too hard to come up with a "only touching our windows" equivalent.

The keyboard grab is a pain-and-a-half, but one possibility is just to
accept keyboard grab failures ... people won't be able to cancel menus
with Esc.

The other two problems are just fixable bugs.
Comment 11 Ed Catmur 2006-03-31 03:19:43 UTC
Created attachment 62433 [details] [review]
gdk-ssh-X.patch

This patch is intended to fix XQueryPointer calls so that gdk doesn't die.

_gdk_windowing_get_pointer(): XQueryPointer dies on the root window. To work around this I create a 1x1 InputOnly window, XQueryPointer on that, and then destroy it. Brain-dead but it works.

_gdk_windowing_window_get_pointer(): XQueryPointer dies if the window isn't ours. I'd be surprised if it was possible to call this with any foreign window other than the root window (since gdk_window_foreign_new() returns NULL when running untrusted), but it's as well to be able to cope. Workaround is to get the pointer coords from _gdk_windowing_get_pointer() and subtract the window origin.

_gdk_windowing_window_at_pointer(): loop through all GDK-aware toplevels on all screens in the hope we hit one containing the pointer; then use that as the basis of the current XQueryPointer child recursion. Special case: we might be over an area of a toplevel without a child (e.g. the background of a GtkDialog); to detect such instances I create a 1x1 InputOnly window under the pointer and test to see if XQueryPointer on the toplevel picks it up (which it will iff that toplevel is the topmost window under the pointer).

Note that none of the added code is hit unless XQueryPointer raises an X error, so (I hope) the normal GDK code paths will be unaffected.

I've tested this patch reasonably thoroughly (the gedit Python Console has been a godsend, thanks Raphael and Adam!); I'll try to answer any queries anyone has.

Also, of course, this doesn't fix any other of the issues Owen raised (notably, context menus don't work). My motivation is to be able to use gvim untrusted without crashing.
Comment 12 Matthias Clasen 2006-03-31 15:42:48 UTC
Looks good in principle. I wonder if we should create that 1x1 window 
and keep it instead of recreating it all the time. Maybe its not worth it;
what do you think ?
Comment 13 Ed Catmur 2006-04-13 21:01:32 UTC
Sorry for the delay.

Keeping the 1x1 window around would reduce roundtripping; if we were going to do that it would make sense to check at GDK initialisation whether we're running untrusted and if so use the workarounds. 

This would also help with pointer grabs (simplest would be to just return TRUE, but we could also provide the information to the client).
Comment 14 Owen Taylor 2006-04-13 21:33:00 UTC
Window creation is not a round trip. 

(Yes, really! The trick is that the client allocates the window IDs)
Comment 15 Ed Catmur 2006-04-14 07:54:31 UTC
Ooh, sneaky.

Checking whether we're untrusted at startup would still be useful, though: leaving out the gdk_error_trap_{push,pop} will eliminate an XSync. 

For the grab issue, returning TRUE would be evil; how about providing gdk_display_is_pointer_grab_possible() and gdk_display_is_keyboard_grab_possible() for GtkMenu etc. to use?

Uh - the X SECURITY specs don't seem to indicate a way for a client to find out whether its cookie is trusted; we'd need a call guaranteed to succeed if trusted and fail if untrusted, maybe XQueryPointer on root?
Comment 16 Ed Catmur 2006-05-07 01:24:45 UTC
Created attachment 64946 [details] [review]
gdk-gtk-trusted.patch

Update to the above patch.

This patch adds a trusted_client boolean member to the GdkDisplayX11 struct, which is set in gdk_display_open() if a XQueryPointer on the root window succeeds without error. As stated above this is believed to be an adequate method to detect whether the client is running trusted.

For pointer detection etc. the same workarounds as in the previous patch are used, but the trusted_client member is used instead of trapping for error. This eliminates an XSync; trusted_client test is wrapped in G_LIKELY to make performance impact minimal.

Pointer grabs: in gdk_pointer_grab(), if the client is untrusted _gdk_input_grab_pointer is allowed to return AlreadyGrabbed and the client will try to grab the main pointer anyway. It appears untrusted clients are allowed to execute a pointer grab on their own windows (with some exceptions), but not XGrabDevice. 

Keyboard grabs: untrusted clients are not allowed keyboard grabs. However, much of the functionality of a keyboard grab is given by Gdk rewriting keyboard events to the client to the grabbing widget, so the keyboard grab is pretended to succeed. (Is this acceptable?)

Menus: the occasions when an untrusted client cannot make a pointer grab are when the window is InputOnly, or is not yet mapped, returning GrabNotViewable. (I can't see where this is specified in the X SECURITY specs; this is observed behaviour with the X.Org server, version 1.0.2). gtk_menu_popup responds to this by using an InputOutput, mapped, offscreen, override-redirect transfer window. As scaffolding, popup_grab_on_window takes a return location argument for the grab status of the failed grab, and menu_grab_transfer_window_get takes a gboolean argument for whether to create an InputOnly or InputOutput window.

Drag-and-drop: again, the IPC widget has to be InputOutput and mapped for a pointer grab to work. gtk_drag_ipc_widget_make_viewable() throws away the GdkWindow of the GtkInvisible IPC widget and replaces it with an InputOutput window. This is used if the pointer grab returns GrabNotViewable. After this, the creation of a window cache fails because of the use of XQueryTree; untrusted clients use gdk_screen_get_toplevel_windows() instead.

With the above patch, gtk-demo works completely (including drag-and-drop); menus work (including context menus called up by right-click and the Menu key); applications don't crash.

Any comments, questions, etc.
Comment 17 Ed Catmur 2006-05-18 06:11:07 UTC
Created attachment 65748 [details] [review]
gdk-gtk-trusted.patch

Updated for 2.9.1.
Comment 18 Matthias Clasen 2006-05-18 17:12:24 UTC
> Keyboard grabs: untrusted clients are not allowed keyboard grabs. However, 
> much of the functionality of a keyboard grab is given by Gdk rewriting 
> keyboard events to the client to the grabbing widget, so the keyboard grab 
> is pretended to succeed. (Is this acceptable?)

This looks slightly odd to me. Can it not lead to problems if code assumes that
after grabbing the keyboard, it gets all key events ? Anyway, it may be the best
we can do. Maybe we should emit a warning when running untrusted that certain
things may not work ?

> gtk_menu_popup responds to
> this by using an InputOutput, mapped, offscreen, override-redirect transfer
> window.

Would it not be simpler to always use an InputOutput window then ?

> again, the IPC widget has to be InputOutput and mapped for a
> pointer grab to work.

Same question as for menus here. Maybe GtkInvisible should simply use an 
InputOutput window ?



Comment 19 Ed Catmur 2006-05-18 23:30:40 UTC
> Can it not lead to problems if code assumes that after grabbing the keyboard, 
> it gets all key events ? 

Yes. But what clients perform a keyboard grab without input focus? Window managers? A11y clients? Keyloggers? Not the sort of thing you'd want to run as an untrusted client.

> Maybe we should emit a warning when running untrusted that certain things may
> not work ?

    if (G_UNLIKELY (gdk_error_trap_pop () == BadWindow)) {
      g_warning ("Connection to display %s appears to be untrusted. Pointer grabs and inter-client communication may not work as expected", display_name);
      display_x11->trusted_client = FALSE;
    }

Something like that?

> Would it not be simpler to always use an InputOutput window then ?

Although creating a window does not involve the server, mapping it involves a round trip and uses server resources, so is less efficient. My intention with this patch is to have minimal impact on efficiency in the usual (trusted client) case.
Comment 20 Matthias Clasen 2006-05-19 15:00:21 UTC
>> Can it not lead to problems if code assumes that after grabbing the keyboard, 
>> it gets all key events ? 
>
>Yes. But what clients perform a keyboard grab without input focus? Window
>managers? A11y clients? Keyloggers? Not the sort of thing you'd want to run as
>an untrusted client.

I guess I was thinking about PointerRoot focus, which is pretty irrelevant.

>> Maybe we should emit a warning when running untrusted that certain things may
>> not work ?
>
>    if (G_UNLIKELY (gdk_error_trap_pop () == BadWindow)) {
>      g_warning ("Connection to display %s appears to be untrusted. Pointer
>grabs and inter-client communication may not work as expected", display_name);
>.      display_x11->trusted_client = FALSE;
>    }
>
>Something like that?

Yes, but mention keyboard grabs as well.

> > Would it not be simpler to always use an InputOutput window then ?
>
>Although creating a window does not involve the server, mapping it involves a
>round trip and uses server resources, so is less efficient. My intention with
>this patch is to have minimal impact on efficiency in the usual (trusted
>client) case.

Fair enough.
Comment 21 Ed Catmur 2006-05-21 05:55:06 UTC
Created attachment 65928 [details] [review]
gdk-gtk-trusted.patch

With warning message using gdk_display_get_name (display). Example:

(gtk-demo:28602): Gdk-WARNING **: Connection to display localhost:10.0 appears to be untrusted. Pointer and keyboard grabs and inter-client communication may not work as expected.

I'm not happy with the gtk_drag_ipc_widget_make_viewable() hack; I'm working on that right now.
Comment 22 Ed Catmur 2006-05-21 06:10:00 UTC
Oh-kaaay. It seems there's an xorg bug (or at least a misfeature):

https://bugs.freedesktop.org/show_bug.cgi?id=6988

If a client is untrusted (in the X SECURITY sense) and tries to create an
InputOnly window with a root window as the parent, the server refuses to map it,
silently returning success:

#ifdef XCSECURITY
    /*  don't let an untrusted client map a child-of-trusted-window, InputOnly
     *  window; too easy to steal device input
     */
    if ( (client->trustLevel != XSecurityClientTrusted) &&
	 (pWin->drawable.class == InputOnly) &&
	 (wClient(pWin->parent)->trustLevel == XSecurityClientTrusted) )
	 return Success;
#endif	

The root window, natch, is trusted, so our ipc widget (and lots of other invisible widgets used similarly) doesn't get mapped, so pointer grabs fail with GrabNotViewable.

I've put a patch up at the Xorg bug, but even if it gets accepted it'll be years till the fix gets rolled out anywhere near complete coverage. So it would seem a workaround is in order. Idea: in gdk_window_new, if the class is InputOnly and the parent is a root window, change it to InputOutput and squeal. Working on it.
Comment 23 Ed Catmur 2006-05-21 16:38:13 UTC
Created attachment 65949 [details] [review]
gdk-gtk-trusted.patch

Changes: when an untrusted client creates a toplevel InputOnly window, gdk_window_new changes it to InputOutput and squeals:

(gtk-demo:2825): Gdk-WARNING **: Coercing GDK_INPUT_ONLY toplevel window to GDK_INPUT_OUTPUT to work around bug in Xorg server

This means the hackish code in gtkmenu.c and gtkdnd.c can be removed entirely.

Unfortunately this introduces another bug (it's always the way...); GtkColorSelection reparents the GdkWindow of a GtkInvisible to one of its own windows. Because the GtkInvisible is mapped first, the GtkInvisible's GdkWindow is created as a child of the root window and so is coerced to an InputOutput window. As a result, the GtkColorSelection has a black square at its origin. (This only happens to untrusted clients, of course.)

The fix I use is to have GtkColorSelection use gtk_widget_set_parent_window before the GtkInvisible is mapped. This requires GtkInvisible to respect the GdkWindow set by gtk_widget_set_parent_window, which in turn requires gtk_widget_get_parent_window to return the window set by gtk_widget_set_parent_window even if the widget does not have a parent. I feel the change to gtk_widget_get_parent_window is an actual bugfix (one expects gtk_widget_get_parent_window to return the value set by gtk_widget_set_parent_window) and the change to GtkInvisible makes sense and is highly unlikely to impact on existing code (the change is that if gtk_widget_set_parent_window is set on the GtkInvisible before it is mapped, the GdkWindow will be created as a child of that window instead of the root window).

Also in GtkColorSelection, the eyedropper is fixed so that if getting the color under the pointer by screenshooting the root window fails, it tries to get the color from its own windows.
Comment 24 Ed Catmur 2006-05-21 16:39:59 UTC
Created attachment 65950 [details] [review]
gdk-gtk-trusted.patch

(trivial fix)
Comment 25 Matthias Clasen 2006-05-22 12:44:50 UTC
would it be easier to just use negative coord in GtkColorSel when reparenting
the invisible ?

      gdk_window_reparent (priv->dropper_grab_widget->window, 
			   GTK_WIDGET (colorsel)->window, -100, -100);
Comment 26 Ed Catmur 2006-05-22 16:27:09 UTC
Oh yeah, that works; I tried that first. It just didn't feel like the right solution.
Comment 27 Matthias Clasen 2006-05-23 14:05:38 UTC
I agree, and the gtk_widget_get_parent_window change indeed looks like a bug fix.

If you don't have any further improvements on the patch, I'd like to get this
committed soon.
Comment 28 Ed Catmur 2006-05-24 17:41:41 UTC
Created attachment 66142 [details] [review]
gdk-gtk-trusted.patch

Some more changes, sorry ;)

Fix crashes in: gdk_x11_screen_get_window_manager_name, gdk_screen_get_active_window, gdk_notify_startup_complete, gdk_window_unstick

Details:
* gdk/x11/gdkdisplay-x11.c: bail in gdk_notify_startup_complete if untrusted
* gdk/x11/gdkwindow-x11.c: fold together common code from gdk_x11_window_move_to_current_desktop and gdk_window_unstick, using the former's guard to prevent crash
* gdk/x11/gdkevents-x11.c: place guards in fetch_net_wm_check_window, gdk_x11_screen_get_window_manager_name, gdk_x11_screen_supports_net_wm_hint to prevent errors caused by XGetWindowProperty on root

Also, in gdk/x11/gdkdnd-x11.c, Motif Dnd is impossible for untrusted clients, so don't even try.

I discovered the first of these problems in random testing, then tried to do an audit of the code for potentially fatal X calls. I think I've done an OK job; I don't have anything more to add to the patch.
Comment 29 Matthias Clasen 2006-05-25 05:31:22 UTC
Fixed in the development version. The fix will be available in the next major release. Thank you for your bug report.


2006-05-25  Matthias Clasen  <mclasen@redhat.com>

	Make GTK+ work as an untrusted X client. (#136571,
	Ed Catmur)