Lazarus

Programming => Operating Systems => Linux => Topic started by: devEric69 on October 21, 2020, 12:16:30 pm

Title: [Solved] Cannot open the default browser under Linux
Post by: devEric69 on October 21, 2020, 12:16:30 pm
Hello,

I'm trying to launch my default browser under Linux. When Icode OpenURL('https://www.noip.com/');, nothing happens.
But, if I launch the command xdg-open https://www.noip.com/ & in the terminal, Firefox opens - without problem - on the requested page \ URL.
So, i've then tested:

Code: Pascal  [Select][+][-]
  1. procedure TWebUtils.OpenURLinDefaultBrowser;
  2. var
  3.   oProcess: TProcess;
  4.   sCmd: string;
  5.   i, j, k: integer;
  6. begin
  7.   oProcess:= TProcess.Create(nil);
  8.   try
  9.     oProcess.Options:= [poWaitOnExit, poNoConsole];
  10.     sCmd:= '/usr/bin/xdg-open https://www.noip.com/ &';
  11.     oProcess.CommandLine:= sCmd;
  12.     oProcess.Execute;
  13.     i:= oProcess.ExitStatus;    // returns 1
  14.     j:= oProcess.ExitCode;      // returns 0
  15.     k:= oProcess.ProcessID;     // <== returns 36500, but 'ps -ef', in a terminal, doesn't report the existence of such a process??! Htop neither.
  16.     oProcess.CloseOutput;
  17.   finally
  18.     oProcess.Free;
  19.   end;
  20. end;  

I've found that xdg-open has a practice verbose debugging mode...:
Code: Bash  [Select][+][-]
  1. bash -x /usr/bin/xdg-open https://www.noip.com/
  2. + check_common_commands https://www.noip.com/
  3. + '[' 1 -gt 0 ']'
  4. + parm=https://www.noip.com/
  5. + shift
  6. + case "$parm" in
  7. + '[' 0 -gt 0 ']'
  8. + '[' -z '' ']'
  9. + unset XDG_UTILS_DEBUG_LEVEL
  10. + '[' 0 -lt 1 ']'
  11. + xdg_redirect_output=' > /dev/null 2> /dev/null'
  12. + '[' xhttps://www.noip.com/ '!=' x ']'
  13. + url=
  14. + '[' 1 -gt 0 ']'
  15. + parm=https://www.noip.com/
  16. + shift
  17. + case "$parm" in
  18. + '[' -n '' ']'
  19. + url=https://www.noip.com/
  20. + '[' 0 -gt 0 ']'
  21. + '[' -z https://www.noip.com/ ']'
  22. + detectDE
  23. + unset GREP_OPTIONS
  24. + '[' -n ubuntu:GNOME ']'
  25. + case "${XDG_CURRENT_DESKTOP}" in
  26. + '[' x = x ']'
  27. + '[' x '!=' x ']'
  28. + '[' xthis-is-deprecated '!=' x ']'
  29. + DE=gnome
  30. + '[' xgnome = x ']'
  31. + '[' xgnome = x ']'
  32. + '[' xgnome = xgnome ']'
  33. + which gnome-default-applications-properties
  34. + DE=gnome3
  35. + '[' -f /run/user/1000/flatpak-info ']'
  36. + '[' xgnome3 = x ']'
  37. + DEBUG 2 'Selected DE gnome3'
  38. + '[' -z '' ']'
  39. + return 0
  40. + case "${BROWSER}" in
  41. + case "$DE" in
  42. + open_gnome3 https://www.noip.com/
  43. + gio help open
  44. + gio open https://www.noip.com/
  45. + '[' 0 -eq 0 ']'
  46. + exit_success
  47. + '[' 0 -gt 0 ']'
  48. + exit 0
...but I can't run it from a TProcess instance either :( .

Does anyone have an idea, a track, to understand why I can't do the same thing with TProcess?
Title: Re: Cannot open the default browser under Linux
Post by: Cyrax on October 21, 2020, 01:09:12 pm
Rather than using TProcess.CommandLine directly, you should use TProcess.Parameters. Also do not use "&", only a shell program understands its internally.
Title: Re: Cannot open the default browser under Linux
Post by: trev on October 21, 2020, 01:11:40 pm
I'm trying to launch my default browser under Linux. When Icode OpenURL('https://www.noip.com/');, nothing happens.

This works for me in Ubuntu 18.04 LTS and now 20.04 LTS:

Code: Pascal  [Select][+][-]
  1. procedure TForm2_About.LicLabel2Click(Sender: TObject);
  2. begin
  3.   OpenURL('https://unlicense.org/');
  4. end;
  5.  

firing up Firefox and taking me to unlicense.org.
Title: Re: Cannot open the default browser under Linux
Post by: marcov on October 21, 2020, 01:50:12 pm
Your way of execution assumes that xdg-open is a binary, and not a script.

Title: Re: Cannot open the default browser under Linux
Post by: devEric69 on October 21, 2020, 03:32:04 pm
Quote
This works for me in Ubuntu 18.04 LTS and now 20.04 LTS

Okay, thank you. I'm investigating on my computer...
Title: Re: Cannot open the default browser under Linux
Post by: dogriz on October 22, 2020, 07:54:49 am
Your way of execution assumes that xdg-open is a binary, and not a script.
Why does that matter?
Title: Re: Cannot open the default browser under Linux
Post by: PascalDragon on October 22, 2020, 09:20:23 am
Your way of execution assumes that xdg-open is a binary, and not a script.
Why does that matter?

Scripts are executed by a shell. It is the one that interprets the shebang (#!) at the start to determine the correct interpreter.
TProcess however uses fork/exec and thus the kernel to start binaries and the kernel itself does not handle scripts. Thus you need to start a shell and pass it the command to ensure that it is executed.

Note: in theory it would be possible to set up the Linux kernel to handle this using the binfmt_misc (https://en.wikipedia.org/wiki/Binfmt_misc) functionality, but that is not done by default, thus you can't rely on it.
Title: Re: Cannot open the default browser under Linux
Post by: MarkMLl on October 22, 2020, 10:02:26 am
Scripts are executed by a shell. It is the one that interprets the shebang (#!) at the start to determine the correct interpreter.
TProcess however uses fork/exec and thus the kernel to start binaries and the kernel itself does not handle scripts. Thus you need to start a shell and pass it the command to ensure that it is executed.

The kernel most definitely used to, and I have no reason to believe that this has changed: i.e. the shebang is specifically processed by code in the Linux kernel rather than relying on a shell, which is different from some other unix implementations.

However I had an odd situation a couple of weeks ago (please note that I'm working from memory here) where I found that RunCommand() etc. wouldn't execute a script under certain conditions (I think it was while being debugged), but the program concerned is doing some very odd things with POSIX capabilities etc.:

Code: [Select]
(* If the file is executable then assume that this is a script or program that  *)
(* checks the content of the file of interest, and if it fails to return a zero *)
(* result return false. Otherwise assume that the name is that of the file of   *)
(* interest and return false if the age is greater than was specified on the    *)
(* command line. ExecuteProcess() might misbehave under gdbserver, if in doubt  *)
(* use "old school" debugging techniques.                                       *)

    if fpStat(mon[i].name, buff) <> 0 then (* Probably same as FileExists() above *)
      exit(false);
    if buff.mode and &001 = &001 then begin (* Executable by "other"            *)
      try
        age := ExecuteProcess(mon[i].name, IntToStr(mon[i].mins))
      except
        age := $ffff
      end;
      if age <> 0 then                  (* Visible for debugging                *)
        exit(false)
      else
        smallest := 2                   (* See comment on result/sideffect below *)
    end else begin

(* There's a misnomer here that always catches me out: this returns a timestamp *)
(* rather than an age (relative timestamp).                                     *)

      age := FileAge(mon[i].name);      (* Visible for debugging                *)
      scratch := DateTimeToStr(FileDateTodateTime(age));
      age := Round((UTC_Now() - FileDateTodateTime(age)) * MinsPerDay);
      if age > mon[i].mins then
        exit(false)
    end
  end;

The parameter mon[ i].name is *directly* from the command line, and in this particular case is normally a Perl script. Works just fine.

Code: [Select]
#!/usr/bin/perl

$MAINLOG = '/var/log/exim4/mainlog';
$MAINLOG1 = $MAINLOG . '.1';            # Optional "last" logfile
$LIMIT = 90;
...

This has definitely been the case since approximately the 2.2 kernel, which I dived into because somebody (on a private conferencing system) had made the same assertion in a somewhat less pleasant way. I don't know what the original rationale was but I'd speculate that somebody wanted something like to be able to write init in either Perl or as a binary. However these days it makes since since it ensures that POSIX capabilities and other EAs relating to e.g. SELinux can be applied strictly to a single script, rather than having a specially tailored shell which could be abused.

MarkMLl
Title: Re: Cannot open the default browser under Linux
Post by: PascalDragon on October 22, 2020, 01:36:20 pm
Scripts are executed by a shell. It is the one that interprets the shebang (#!) at the start to determine the correct interpreter.
TProcess however uses fork/exec and thus the kernel to start binaries and the kernel itself does not handle scripts. Thus you need to start a shell and pass it the command to ensure that it is executed.

The kernel most definitely used to, and I have no reason to believe that this has changed: i.e. the shebang is specifically processed by code in the Linux kernel rather than relying on a shell, which is different from some other unix implementations.

Indeed you're right: The Linux kernel initializes (https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/fs/binfmt_script.c) a binfmt loader for script files by default.

This might not work on every *nix system however as POSIX states this about the exec* (https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html) family of calls:

Quote
Historically, there have been two ways that implementations can exec shell scripts.

One common historical implementation is that the execl(), execv(), execle(), and execve() functions return an [ENOEXEC] error for any file not recognizable as executable, including a shell script. When the execlp() and execvp() functions encounter such a file, they assume the file to be a shell script and invoke a known command interpreter to interpret such files. This is now required by POSIX.1-2017. These implementations of execvp() and execlp() only give the [ENOEXEC] error in the rare case of a problem with the command interpreter's executable file. Because of these implementations, the [ENOEXEC] error is not mentioned for execlp() or execvp(), although implementations can still give it.

Another way that some historical implementations handle shell scripts is by recognizing the first two bytes of the file as the character string "#!" and using the remainder of the first line of the file as the name of the command interpreter to execute.

TProcess uses either execve or execv, but never exec*p, thus it's not cross platform to assume that TProcess can indeed start a shell script.
Title: Re: Cannot open the default browser under Linux
Post by: MarkMLl on October 22, 2020, 01:52:56 pm
This might not work on every *nix system however as POSIX states this about the exec* (https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html) family of calls:

Very much agreed. I'm not really in a position to look at e.g. Solaris at the moment, much as I'd like to.

I suspect though that things like POSIX capabilities would be a major incentive for a kernel to handle the shebang etc. internally, since generally speaking anything that moves a file to a new inode will- by design- lose the EAs that implement them hence the capabilities will need to be reapplied by a privileged user. I don't know whether an equivalently-robust alternative could be implemented if the kernel didn't at least read the EAs of the file to be executed.

I'm not sure which way this argues, but I spotted something on StackOverflow recently where it was being asked why one of the major distreaux (possibly Red Hat) mandated that shebang lines like

#!/bin/sh -x

should be avoided, instead using

#!/bin/sh

set -x


and so on. My assumption is that that's in recognition of the fact that it's normally the kernel that parses the shebang and that the parser might have limitations e.g. in the line length it's prepared to accept.

MarkMLl

Title: Re: Cannot open the default browser under Linux
Post by: devEric69 on October 22, 2020, 09:36:25 pm
I finally found the reason, which is due to the rights policy under Linux. I run a Lazarus as 'root', to open Firefox in a 'user01' session:

Code: Text  [Select][+][-]
  1. Initialization output:
  2. Running Firefox as root in a regular user's session is not supported.  ($XAUTHORITY is /run/user/1000/gdm/Xauthority which is owned by user01.)
  3. Running Firefox as root in a regular user's session is not supported.  ($XAUTHORITY is /run/user/1000/gdm/Xauthority which is owned by user01.)
  4. Running Firefox as root in a regular user's session is not supported.  ($XAUTHORITY is /run/user/1000/gdm/Xauthority which is owned by user01.)
  5. /usr/bin/xdg-open: 869: iceweasel: not found
  6. /usr/bin/xdg-open: 869: seamonkey: not found
  7. /usr/bin/xdg-open: 869: mozilla: not found
  8. /usr/bin/xdg-open: 869: epiphany: not found
  9. /usr/bin/xdg-open: 869: konqueror: not found
  10. /usr/bin/xdg-open: 869: chromium: not found
  11. /usr/bin/xdg-open: 869: chromium-browser: not found
  12. /usr/bin/xdg-open: 869: google-chrome: not found
  13. /usr/bin/xdg-open: 869: www-browser: not found
  14. /usr/bin/xdg-open: 869: links2: not found
  15. /usr/bin/xdg-open: 869: elinks: not found
  16. /usr/bin/xdg-open: 869: links: not found
  17. /usr/bin/xdg-open: 869: lynx: not found
  18. /usr/bin/xdg-open: 869: w3m: not found
  19. xdg-open: no method available for opening 'https://unlicense.org/''

In such a case, is there a way to code, with Lazarus, in the program, a workaround to this kind of problem :-\ ?
Title: Re: Cannot open the default browser under Linux
Post by: Bart on October 22, 2020, 09:50:36 pm
I finally found the reason, which is due to the rights policy under Linux. I run a Lazarus as 'root',

Why?
I can't think of a reason why you should do that.

Bart
Title: Re: Cannot open the default browser under Linux
Post by: devEric69 on October 22, 2020, 09:58:57 pm
Indeed. No serious or legitimate reason  ;D .

But out of curiosity, there would be an imaginable and codable "strategy" :D ?
Title: Re: Cannot open the default browser under Linux
Post by: MarkMLl on October 22, 2020, 10:21:49 pm
In such a case, is there a way to code, with Lazarus, in the program, a workaround to this kind of problem :-\ ?

Don't run as root, you're a disaster waiting to happen. xdgopen is quite correctly set up to not run something as complex as a browser with elevated rights, and you should be applying the same philosophy to the Lazarus IDE.

If your program needs a specific right over and above those held by ordinary users, then you should be using POSIX capabilities. And what's more if at all possible you should be exploiting the extra capability and then releasing it before starting the GUI part of your program, so that when complex stuff starts there's less chance of damage if something dodgy or malicious turns out to be buried in a library. **

And while gdb has problems debugging that, gdbserver works perfectly well except- as I noted earlier- when trying to run something with a shebang (see PascalDragon's comments for the probable reason).

** Same philosophy as a setuid program, which you'll find is fundamentally incompatible with some widget sets for security reasons.

MarkMLl
Title: Re: Cannot open the default browser under Linux
Post by: devEric69 on October 23, 2020, 09:46:59 am
Quote
Don't run as root, you're a disaster waiting to happen.

In a certain way, yes: indeed, I'm not exempt from forgetting code that would (conditionally) bypass the security layer during development, and that I'll forget to remove in the release >:D . And without help or leads from some other people, I am unable to find such a code - by myself - to bypass the xdg-open's security.

Quote
If your program needs a specific right over and above those held by ordinary users, then you should be using POSIX capabilities.

I am 'root' in development only. I always test afterwards, regularly, the software as 'user01'.
Now, it's sure that the POSIX software layer which ensures the security, is clearly more aware than me, of the vulnerabilities that may exist, and is more consistent with its constant 'proprietary' + 'group' + 'others' strategy rights flags, to apply to the program when it is installed. In fact, I follow a "everything must be embedded with or near the binary" installation strategy, thanks to AppImage. It's my assumption, that hard disk space is no more the limiting factor, for a client GUI application.

Quote
And while gdb has problems debugging that, gdbserver works perfectly well except - as I noted earlier - when trying to run something with a shebang (see PascalDragon's comments for the probable reason).

That's good to know. I would switch from fpDebug to gdbServer temporarily in the future, when I'll suspect a specific right problem.

Thank you - all - for your contributions. I close this thread, concerning me.
Title: Re: Cannot open the default browser under Linux
Post by: MarkMLl on October 23, 2020, 10:56:21 am
I am 'root' in development only. I always test afterwards, regularly, the software as 'user01'.

You're missing the point. The IDE is an extremely complex piece of software, which activates an enormous number of libraries handling rendering etc. as the same user with which you invoke it. It's also prepared to fire up a browser etc. for external reference material from external sites.

By running the IDE as root, you're giving software writers that you should not be trusting free rein over your development system, which you should now be considered to be compromised.

There is no reason to do this, and you shouldn't be doing it.

MarkMLl
Title: Re: [Solved] Cannot open the default browser under Linux
Post by: devEric69 on October 23, 2020, 11:46:23 am
Quote
The IDE is an extremely complex piece of software, which activates an enormous number of libraries handling rendering etc.

I'm aware of this: it's now an old and huge piece of programming, very large and too complex for a small head like me.

Quote
There is no reason to do this, and you shouldn't be doing it.

For sure: as 'root', I'm launching mountains of source code, which may contain malicious code... potentially breaking my computer. But exactly as I answer "yes" to the UAC under Windows, which asks me to take my responsibilities before launching a program that can break the Windows shell, I take these responsibilities with the shell of Nunux. So, if I run a silent hard disk formatting without a shell accountability message, I will only be able to blame myself >:( .
Title: Re: [Solved] Cannot open the default browser under Linux
Post by: MarkMLl on October 23, 2020, 12:57:51 pm
I presume that you are familiar with Ken Thompson's "Reflections on Trusting Trust" lecture.

I don't know whether anybody has considered such a thing with FPC, but there's really no defence against that sort of thing without robustly signed binaries: something that unix isn't very good at, and which could almost certainly be circumvented by the local root user.

MarkMLl
Title: Re: [Solved] Cannot open the default browser under Linux
Post by: devEric69 on October 23, 2020, 02:20:39 pm
Okay, fine: I surrender :'( .
I'll switch the launch of Lazarus, as belonging to 'user01', as soon as possible :) .
Title: Re: [Solved] Cannot open the default browser under Linux
Post by: MarkMLl on October 23, 2020, 02:47:26 pm
If you need a hand with capabilities or debugging yell. I've spent the last few weeks on something that ran as an ordinary user but had elevated capabilities so that it could create a unix domain socket in /var/run and a UDP socket below 1024, and debugging using gdbserver (which does have to run as root).

Only significant issue- as mentioned much earlier- was being unable to debug running a shell script directly.

MarkMLl
TinyPortal © 2005-2018