Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QStandardPaths corresponding to a different user on Linux
QtWS25 Last Chance

QStandardPaths corresponding to a different user on Linux

Scheduled Pinned Locked Moved Solved General and Desktop
linuxqstandardpaths
15 Posts 5 Posters 3.3k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    mbrochmann
    wrote on 25 Jun 2018, 14:29 last edited by mbrochmann
    #1

    I would like to be able to retrieve locations in Linux via QStandardPaths that would correspond to a different user than the one that is running the program.

    For example, I want to run the program as root, where QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation ) returns /root/.local/share, but I want to retrieve the corresponding location for user01, which would be /home/user01/.local/share.

    I attempted to do this simply by calling seteuid(uid) and setuid(uid), and then using QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation ), however it still returns the location corresponding to root, and not user01. I have also done "chmod ug+s" on the executable and the library it calls (in which the actual call to QStandardPaths is made).

    I suspect that changing the uid or the euid does not work because QStandardPaths appears to use translation files which are presumably loaded when the QCoreApplication is initialized. If this is the case, is there a way to identify and reload the translation files used by QStandardPaths that correspond to the intended user?

    If this is not the case - what do I need to do in addition to/instead of setuid/seteuid to get QStandardPaths to behave as if I were the intended user?

    P 1 Reply Last reply 26 Jun 2018, 08:09
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 25 Jun 2018, 20:04 last edited by
      #2

      Hi and welcome to devnet,

      What happens if you use sudo when running your application ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      1
      • M mbrochmann
        25 Jun 2018, 14:29

        I would like to be able to retrieve locations in Linux via QStandardPaths that would correspond to a different user than the one that is running the program.

        For example, I want to run the program as root, where QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation ) returns /root/.local/share, but I want to retrieve the corresponding location for user01, which would be /home/user01/.local/share.

        I attempted to do this simply by calling seteuid(uid) and setuid(uid), and then using QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation ), however it still returns the location corresponding to root, and not user01. I have also done "chmod ug+s" on the executable and the library it calls (in which the actual call to QStandardPaths is made).

        I suspect that changing the uid or the euid does not work because QStandardPaths appears to use translation files which are presumably loaded when the QCoreApplication is initialized. If this is the case, is there a way to identify and reload the translation files used by QStandardPaths that correspond to the intended user?

        If this is not the case - what do I need to do in addition to/instead of setuid/seteuid to get QStandardPaths to behave as if I were the intended user?

        P Offline
        P Offline
        Paul Colby
        wrote on 26 Jun 2018, 08:09 last edited by
        #3

        Hi @mbrochmann,

        using QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation)

        In that case, on Linux, the writableLocation first looks for a XDG_CACHE_HOME environment variable, then falls back to QDir::homePath() (qstandardpaths_unix.cpp line 96). And QDir::homePath() docs say:

        Under non-Windows operating systems the HOME environment variable is used if it exists, otherwise the path returned by the rootPath().

        So try setting either XDG_CACHE_HOME or `HOME to test that's the issue.

        What happens if you use sudo when running your application ?

        You'll probably want to use sudo's -i option, if indeed the detection is environment-variable based. eg:

        root@paul-XPS-13-9343:~# env | grep 'XDG_CACHE_HOME\|HOME'
        HOME=/root
        root@paul-XPS-13-9343:~# sudo -u paul env | grep 'XDG_CACHE_HOME\|HOME'
        HOME=/root
        root@paul-XPS-13-9343:~# sudo -u paul -i env | grep 'XDG_CACHE_HOME\|HOME'
        HOME=/home/paul
        root@paul-XPS-13-9343:~# 
        

        Cheers.

        M 1 Reply Last reply 26 Jun 2018, 14:28
        3
        • P Paul Colby
          26 Jun 2018, 08:09

          Hi @mbrochmann,

          using QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation)

          In that case, on Linux, the writableLocation first looks for a XDG_CACHE_HOME environment variable, then falls back to QDir::homePath() (qstandardpaths_unix.cpp line 96). And QDir::homePath() docs say:

          Under non-Windows operating systems the HOME environment variable is used if it exists, otherwise the path returned by the rootPath().

          So try setting either XDG_CACHE_HOME or `HOME to test that's the issue.

          What happens if you use sudo when running your application ?

          You'll probably want to use sudo's -i option, if indeed the detection is environment-variable based. eg:

          root@paul-XPS-13-9343:~# env | grep 'XDG_CACHE_HOME\|HOME'
          HOME=/root
          root@paul-XPS-13-9343:~# sudo -u paul env | grep 'XDG_CACHE_HOME\|HOME'
          HOME=/root
          root@paul-XPS-13-9343:~# sudo -u paul -i env | grep 'XDG_CACHE_HOME\|HOME'
          HOME=/home/paul
          root@paul-XPS-13-9343:~# 
          

          Cheers.

          M Offline
          M Offline
          mbrochmann
          wrote on 26 Jun 2018, 14:28 last edited by
          #4

          @Paul-Colby @SGaist , thank you for the advice to use sudo and the pointer to the source code.

          I do not have an XDG_CACHE_HOME environment variable, so HOME is what is being used.

          I discovered that "sudo -u user01 -i env | grep HOME" returns:

          HOME=/home/user01

          whereas "sudo -u user01 -i echo $HOME" returns:

          /root

          (and indeed, getenv("HOME") inside the program, after switching uid/euid, also returns /root)

          So, it is not a Qt specific issue after all. Thanks for helping me figure this out!

          J 1 Reply Last reply 27 Jun 2018, 08:04
          0
          • M mbrochmann
            26 Jun 2018, 14:28

            @Paul-Colby @SGaist , thank you for the advice to use sudo and the pointer to the source code.

            I do not have an XDG_CACHE_HOME environment variable, so HOME is what is being used.

            I discovered that "sudo -u user01 -i env | grep HOME" returns:

            HOME=/home/user01

            whereas "sudo -u user01 -i echo $HOME" returns:

            /root

            (and indeed, getenv("HOME") inside the program, after switching uid/euid, also returns /root)

            So, it is not a Qt specific issue after all. Thanks for helping me figure this out!

            J Online
            J Online
            JonB
            wrote on 27 Jun 2018, 08:04 last edited by
            #5

            @mbrochmann
            Your issue seems to be to do with fetching the value of environment variables to return certain values. Let's be clear: setuid/seteuid, or chmod ug+s, affect permissions but do nothing toward altering the environment under which a program runs.

            I don't think you'll be able to retrieve the user-specific paths/variables for another user without logging in (including via sudo) as that user to allow the environment to be set up appropriately.

            P 1 Reply Last reply 27 Jun 2018, 10:28
            1
            • J JonB
              27 Jun 2018, 08:04

              @mbrochmann
              Your issue seems to be to do with fetching the value of environment variables to return certain values. Let's be clear: setuid/seteuid, or chmod ug+s, affect permissions but do nothing toward altering the environment under which a program runs.

              I don't think you'll be able to retrieve the user-specific paths/variables for another user without logging in (including via sudo) as that user to allow the environment to be set up appropriately.

              P Offline
              P Offline
              Paul Colby
              wrote on 27 Jun 2018, 10:28 last edited by
              #6

              @mbrochmann said in QStandardPaths corresponding to a different user on Linux:

              whereas "sudo -u user01 -i echo $HOME" returns:
              /root

              This is because the $HOME is being expanded before sudo is run. Compare, for example:

              root@paul-XPS-13-9343:~# sudo -u paul -i echo $HOME
              /root
              root@paul-XPS-13-9343:~# sudo -u paul -i echo "$HOME"
              /root
              root@paul-XPS-13-9343:~# sudo -u paul -i echo '$HOME'
              /home/paul
              root@paul-XPS-13-9343:~# 
              

              @JonB said:

              I don't think you'll be able to retrieve the user-specific paths/variables for another user without logging in (including via sudo) as that user to allow the environment to be set up appropriately.

              You can get the path other ways, and then either use the path directly, or set the environment manually. For example, if you can't control the code, but you know that it uses QStandardPath, you can use shell expansion, across users, eg:

              paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ ./foo 
              "/home/paul/.local/share"
              paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ HOME=~alice ./foo 
              "/home/alice/.local/share"
              

              (here my foo application just does: qDebug() << QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation);)

              You can also use getent, such as:

              paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ HOME=`getent passwd alice | cut -d: -f6` ./foo 
              "/home/alice/.local/share"
              

              If you can control the code, then you can either execute getent or parse /etc/passwd directly (for legacy reasons this is always world-readable), then you can set the environment from within your code. eg:

              qDebug() << "before" << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
              
              QFile file("/etc/passwd");
              file.open(QFile::ReadOnly);
              while (file.isOpen() && !file.atEnd()) {
                  auto entry = file.readLine().split(':');
                  if ((entry.size() > 5) && (entry.at(0) == "alice")) {
                      qputenv("HOME", entry.at(5));
                      file.close(); // Just to shortcut the loop.
                  }
              }
              
              qDebug() << "after" << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
              

              Which outputs (for me):

              before "/home/paul/.local/share"
              after "/home/alice/.local/share"
              

              Of course, none of this is portable to other platforms.

              Cheers.

              J M 2 Replies Last reply 27 Jun 2018, 10:49
              2
              • P Paul Colby
                27 Jun 2018, 10:28

                @mbrochmann said in QStandardPaths corresponding to a different user on Linux:

                whereas "sudo -u user01 -i echo $HOME" returns:
                /root

                This is because the $HOME is being expanded before sudo is run. Compare, for example:

                root@paul-XPS-13-9343:~# sudo -u paul -i echo $HOME
                /root
                root@paul-XPS-13-9343:~# sudo -u paul -i echo "$HOME"
                /root
                root@paul-XPS-13-9343:~# sudo -u paul -i echo '$HOME'
                /home/paul
                root@paul-XPS-13-9343:~# 
                

                @JonB said:

                I don't think you'll be able to retrieve the user-specific paths/variables for another user without logging in (including via sudo) as that user to allow the environment to be set up appropriately.

                You can get the path other ways, and then either use the path directly, or set the environment manually. For example, if you can't control the code, but you know that it uses QStandardPath, you can use shell expansion, across users, eg:

                paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ ./foo 
                "/home/paul/.local/share"
                paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ HOME=~alice ./foo 
                "/home/alice/.local/share"
                

                (here my foo application just does: qDebug() << QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation);)

                You can also use getent, such as:

                paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ HOME=`getent passwd alice | cut -d: -f6` ./foo 
                "/home/alice/.local/share"
                

                If you can control the code, then you can either execute getent or parse /etc/passwd directly (for legacy reasons this is always world-readable), then you can set the environment from within your code. eg:

                qDebug() << "before" << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
                
                QFile file("/etc/passwd");
                file.open(QFile::ReadOnly);
                while (file.isOpen() && !file.atEnd()) {
                    auto entry = file.readLine().split(':');
                    if ((entry.size() > 5) && (entry.at(0) == "alice")) {
                        qputenv("HOME", entry.at(5));
                        file.close(); // Just to shortcut the loop.
                    }
                }
                
                qDebug() << "after" << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
                

                Which outputs (for me):

                before "/home/paul/.local/share"
                after "/home/alice/.local/share"
                

                Of course, none of this is portable to other platforms.

                Cheers.

                J Online
                J Online
                JonB
                wrote on 27 Jun 2018, 10:49 last edited by
                #7

                @Paul-Colby
                You cannot get the QStandardPaths for another user.

                If I understand you rightly, you are saying that you know for example to parse /etc/passwd for stuff which you will use to do some work. But that only works if you know just what the code for QStandardPaths is doing, presumably by examining the source code for each "variable" you might be interested in. Personally I would not want to do that.

                P 1 Reply Last reply 27 Jun 2018, 11:07
                0
                • J JonB
                  27 Jun 2018, 10:49

                  @Paul-Colby
                  You cannot get the QStandardPaths for another user.

                  If I understand you rightly, you are saying that you know for example to parse /etc/passwd for stuff which you will use to do some work. But that only works if you know just what the code for QStandardPaths is doing, presumably by examining the source code for each "variable" you might be interested in. Personally I would not want to do that.

                  P Offline
                  P Offline
                  Paul Colby
                  wrote on 27 Jun 2018, 11:07 last edited by
                  #8

                  @JonB said in QStandardPaths corresponding to a different user on Linux:

                  You cannot get the QStandardPaths for another user.

                  You can on most Linuxes, which is the platform the subject specifically asked for. It's not portable, and like you I would try to avoid it, but if its solves the OP's need, and the limitations are understood, then it might be their best, if not only option.

                  Cheers.

                  M 1 Reply Last reply 27 Jun 2018, 19:12
                  0
                  • P Paul Colby
                    27 Jun 2018, 10:28

                    @mbrochmann said in QStandardPaths corresponding to a different user on Linux:

                    whereas "sudo -u user01 -i echo $HOME" returns:
                    /root

                    This is because the $HOME is being expanded before sudo is run. Compare, for example:

                    root@paul-XPS-13-9343:~# sudo -u paul -i echo $HOME
                    /root
                    root@paul-XPS-13-9343:~# sudo -u paul -i echo "$HOME"
                    /root
                    root@paul-XPS-13-9343:~# sudo -u paul -i echo '$HOME'
                    /home/paul
                    root@paul-XPS-13-9343:~# 
                    

                    @JonB said:

                    I don't think you'll be able to retrieve the user-specific paths/variables for another user without logging in (including via sudo) as that user to allow the environment to be set up appropriately.

                    You can get the path other ways, and then either use the path directly, or set the environment manually. For example, if you can't control the code, but you know that it uses QStandardPath, you can use shell expansion, across users, eg:

                    paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ ./foo 
                    "/home/paul/.local/share"
                    paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ HOME=~alice ./foo 
                    "/home/alice/.local/share"
                    

                    (here my foo application just does: qDebug() << QStandardPaths::writableLocation( QStandardPaths::GenericDataLocation);)

                    You can also use getent, such as:

                    paul@paul-XPS-13-9343:~/src/extern/forum-qt-io$ HOME=`getent passwd alice | cut -d: -f6` ./foo 
                    "/home/alice/.local/share"
                    

                    If you can control the code, then you can either execute getent or parse /etc/passwd directly (for legacy reasons this is always world-readable), then you can set the environment from within your code. eg:

                    qDebug() << "before" << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
                    
                    QFile file("/etc/passwd");
                    file.open(QFile::ReadOnly);
                    while (file.isOpen() && !file.atEnd()) {
                        auto entry = file.readLine().split(':');
                        if ((entry.size() > 5) && (entry.at(0) == "alice")) {
                            qputenv("HOME", entry.at(5));
                            file.close(); // Just to shortcut the loop.
                        }
                    }
                    
                    qDebug() << "after" << QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
                    

                    Which outputs (for me):

                    before "/home/paul/.local/share"
                    after "/home/alice/.local/share"
                    

                    Of course, none of this is portable to other platforms.

                    Cheers.

                    M Offline
                    M Offline
                    mbrochmann
                    wrote on 27 Jun 2018, 16:47 last edited by
                    #9

                    @Paul-Colby said in QStandardPaths corresponding to a different user on Linux:

                    This is because the $HOME is being expanded before sudo is run. Compare, for example:
                    root@paul-XPS-13-9343:~# sudo -u paul -i echo $HOME
                    /root
                    root@paul-XPS-13-9343:~# sudo -u paul -i echo "$HOME"
                    /root
                    root@paul-XPS-13-9343:~# sudo -u paul -i echo '$HOME'
                    /home/paul

                    I had not considered that, thanks for the explanation!

                    Nevertheless, as @JonB pointed out, seteuid won't result in getenv returning what I want.

                    I decided to go with

                    getpwuid( geteuid() )->pw_dir;

                    Short/sweet, gets me what I want. :)

                    I really feel like QStandardPaths should consider the euid on Linux, but I might be biased by my current coding goals. :)

                    1 Reply Last reply
                    0
                    • P Paul Colby
                      27 Jun 2018, 11:07

                      @JonB said in QStandardPaths corresponding to a different user on Linux:

                      You cannot get the QStandardPaths for another user.

                      You can on most Linuxes, which is the platform the subject specifically asked for. It's not portable, and like you I would try to avoid it, but if its solves the OP's need, and the limitations are understood, then it might be their best, if not only option.

                      Cheers.

                      M Offline
                      M Offline
                      mbrochmann
                      wrote on 27 Jun 2018, 19:12 last edited by
                      #10

                      "You cannot get the QStandardPaths for another user."

                      It may be worth pointing out that on Windows this can be done using "ImpersonateLoggedOnUser" and associated functions.

                      J 1 Reply Last reply 27 Jun 2018, 19:28
                      0
                      • M mbrochmann
                        27 Jun 2018, 19:12

                        "You cannot get the QStandardPaths for another user."

                        It may be worth pointing out that on Windows this can be done using "ImpersonateLoggedOnUser" and associated functions.

                        J Online
                        J Online
                        JonB
                        wrote on 27 Jun 2018, 19:28 last edited by JonB
                        #11

                        @mbrochmann
                        To the best of my knowledge, to use ImpersonateLoggedOnUser you need a handle to an impersonation access token (https://msdn.microsoft.com/en-us/library/windows/desktop/aa378612(v=vs.85).aspx), and that can only be obtained from LogonUser or similar. Which requires the user's password. Unless you know of another relevant way, which I'd be very interested to learn of?

                        I don't see how this would work in the context the OP is asking about, and the Linux setuid approach. He needs some way of discovering other users' paths without them having to log on or give him their password!

                        M 1 Reply Last reply 27 Jun 2018, 20:16
                        0
                        • J JonB
                          27 Jun 2018, 19:28

                          @mbrochmann
                          To the best of my knowledge, to use ImpersonateLoggedOnUser you need a handle to an impersonation access token (https://msdn.microsoft.com/en-us/library/windows/desktop/aa378612(v=vs.85).aspx), and that can only be obtained from LogonUser or similar. Which requires the user's password. Unless you know of another relevant way, which I'd be very interested to learn of?

                          I don't see how this would work in the context the OP is asking about, and the Linux setuid approach. He needs some way of discovering other users' paths without them having to log on or give him their password!

                          M Offline
                          M Offline
                          mbrochmann
                          wrote on 27 Jun 2018, 20:16 last edited by
                          #12

                          @JonB

                          Yes, this only works if the caller has sufficient privilege to get a process handle (via OpenProcess) with impersonation privileges - but no explicit password needs to be passed in this case.

                          It's probably overkill for most situations, but I just wanted to point out that if this has been done, QStandardPaths will behave as if in the context of the impersonated user.

                          J 1 Reply Last reply 27 Jun 2018, 20:32
                          0
                          • M mbrochmann
                            27 Jun 2018, 20:16

                            @JonB

                            Yes, this only works if the caller has sufficient privilege to get a process handle (via OpenProcess) with impersonation privileges - but no explicit password needs to be passed in this case.

                            It's probably overkill for most situations, but I just wanted to point out that if this has been done, QStandardPaths will behave as if in the context of the impersonated user.

                            J Online
                            J Online
                            JonB
                            wrote on 27 Jun 2018, 20:32 last edited by JonB
                            #13

                            @mbrochmann
                            Yes --- but just so I understand, if you don't mind --- the OpenProcess for obtaining the handle can only be of a process already running by the user you want to impersonate. That user had to log in, or supply his password, to get to that state in the first place. That's all you can do under Windows, right? The OP wants to be able to impersonate some random, non-logged-in user for this purpose, which you can do with Linux's setuid(uid), but you cannot in Windows, is that right?

                            M 1 Reply Last reply 27 Jun 2018, 20:39
                            0
                            • J JonB
                              27 Jun 2018, 20:32

                              @mbrochmann
                              Yes --- but just so I understand, if you don't mind --- the OpenProcess for obtaining the handle can only be of a process already running by the user you want to impersonate. That user had to log in, or supply his password, to get to that state in the first place. That's all you can do under Windows, right? The OP wants to be able to impersonate some random, non-logged-in user for this purpose, which you can do with Linux's setuid(uid), but you cannot in Windows, is that right?

                              M Offline
                              M Offline
                              mbrochmann
                              wrote on 27 Jun 2018, 20:39 last edited by
                              #14

                              @JonB

                              Yes, the impersonated user must be logged in and running a process - I am the original poster, and it is indeed a very specific situation.

                              There is probably a much easier way to get a user's home directory on Windows, but we were already impersonating the user for other reasons. :)

                              1 Reply Last reply
                              0
                              • T Offline
                                T Offline
                                Taytoo
                                wrote on 31 Mar 2021, 21:15 last edited by
                                #15

                                @mbrochmann Did you ever figure out a solution to this?

                                1 Reply Last reply
                                0

                                • Login

                                • Login or register to search.
                                • First post
                                  Last post
                                0
                                • Categories
                                • Recent
                                • Tags
                                • Popular
                                • Users
                                • Groups
                                • Search
                                • Get Qt Extensions
                                • Unsolved