3

Question:

I am trying to execute a cmd which reads from a PostgreSQL db. I am able to manually switch to root, then switch to the postgre user and access the information I desire.

The problem I have is that when I run this, it just hangs and nothing happens.

I have the root password and will need this when switching from the current user But I am not being prompted to enter it.

How can I get this not to hang and the password be prompted?

The code below only executes 'ls' for simplicity.

Code:

def popen_cmd_shell(command):
    print command
    process = subprocess.Popen(command,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT,
                               shell=True)

    proc_stdout = process.communicate()[0].strip()    
    return proc_stdout

if __name__ == '__main__':
    querylist = popen_cmd_shell('su - root; ls;')

    print querylist

Update One:

I am unable to use any library that dose not come with python 2.7 on Linux SUSE. I just need to execute a command and exit.

Update Two:

I am unable to run the script as root as I need to perform other tasks which require me not to be root.

Update Three:

As per LeBarton suggestions I have got the script to log into root, although the the ls command never gets executed as root, it gets executed as the user I originally was. When I run the command I get prompted to enter the root password and get transfered from "@host" to "host" who cannot execute any command other than exit. When I exit all the commands executed output appears.

I do not wish to store the user password in the code as LeBarton has it. How can I execute a command as root and return back and continue the rest of the script, without getting locked into the new users and needing to type 'exit'.

The "stderr=subprocess.STDOUT" seems to have been what was causing it to hang.

Code:

if __name__ == '__main__':
    def subprocess_cmd(command):
        process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True)
        proc_stdout = process.communicate()[0].strip()
        print proc_stdout

    subprocess_cmd('echo a; su - root; ls; cd;ls;')

...continue with rest of script where I execute commands as original user 

Answer:

Thanks to tripleee for his excellent answer.

I Have achieved what I set out to do with the follwoing code:

if __name__ == '__main__':
    def subprocess_cmd(command):
        process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=False)
        proc_stdout = process.communicate()[0].strip()
        print proc_stdout

    subprocess_cmd(['su','-','root','-c','su -s /bin/sh  postgres -c \'psql -U msa ..........])

I just needed to the place the command I was executing as root after -c. So it now switches to the postgres user and finds the data it needs from root returning to the normal user after.

Bawn
  • 509
  • 3
  • 13
  • 36
  • http://stackoverflow.com/questions/1770209/run-child-processes-as-different-user-from-a-long-running-process could be useful. – Ali SAID OMAR Aug 05 '15 at 13:49
  • why don't simply create the script and then run the script as root with sudo? And you don't even need root to access a DB. – KurzedMetal Aug 05 '15 at 13:49
  • 1
    (1) you should be able to access the db without switching to root. If you don't know how then ask a separate question about it specifically (2) To avoid being asked the root password, use `sudo`that you could configure to run your command as root without a password: `output = check_output(['sudo', 'get-db-data.sh'])`. On my system it is enough to drop a one-line file into `/etc/sudoers.d/` directory (3) if you need to pass a root password but you want to avoid hardcoding it in the source then you could use `root_password = getpass.getpass('root password:')` to ask for the password at runtime. – jfs Aug 15 '15 at 01:23

3 Answers3

3

You are misunderstanding what su does. su creates a privileged subprocess; it does not change the privileges of your current process. The commands after su will be executed after su finishes, with your normal privileges.

Instead, you want to pass a -c option to specify the commands you want to run with elevated privileges. (See also the su man page.)

popen_cmd_shell('su -c ls - root')

sudo was specifically designed to simplify this sort of thing, so you should probably be using that instead.

Scripted access to privileged commands is a sticky topic. One common approach is to have your command perform the privileged operation, then drop its privileges. Both from a security and a design point of view, this approach tends to simplify the overall logic. You need to make sure your privileged code is as simple and short as possible--no parsing in the privileged section, for example.

Once the privileged code is properly tested, audited, and frozen, bestowing the script with the required privileges should be a simple matter (although many organizations are paranoid, and basically unable to establish a workable policy for this sort of thing).

Regardless of which approach you take, you should definitely avoid anything with shell=True in any security-sensitive context, and instead pass any external commands as a list, not as a single string.

popen_cmd_shell(['su', '-c', 'ls', '-', 'root'])

(Maybe also rename the function, since you specifically do not want a shell. Obviously, also change the function to specify shell=False.)

Again, these security concerns hold whether you go with dropping privileges, or requesting privilege escalation via su or sudo.

tripleee
  • 175,061
  • 34
  • 275
  • 318
1

Your command is this

su - root; ls;

The shell is interpretting it as this

"su -root; ls;"

You probably don't have an executable in your with that exact name with spaces.

Try separating it into as list with

['su', '-', 'root', ';', 'ls', ';' ]

EDIT

stdout=subprocess.PIPE, is causing the program to hang. If you are trying to pass the password in, using process.communicate('root password') works.

yxre
  • 3,576
  • 21
  • 20
  • 1
    no, you should not use a list argument with `shell=True` on POSIX. no, `.communicate(input)` won't work unless `stdin=PIPE`. Also, it won't work if `su` accepts password directly from terminal (not from stdin). – jfs Aug 15 '15 at 01:26
0

Do you just want to access the PostgreSQL database? If so, you don't need to use command line at all...

The Python library psycopg2 will allow to send commands to the PostgreSQL server, more on that here: https://wiki.postgresql.org/wiki/Psycopg2_Tutorial

However I recommend an ORM such as SQLAlchemy, they make communicating with a database a trivial task.

ApolloFortyNine
  • 570
  • 2
  • 7