1

I have a python script that is being run on some Macs by my MDM tool. This means that the script is being run as root. There is a part of the script I need to run as the currently logged in local user on the account. I found this article below for using Popen to do this:

Run child processes as a different user from a long-running process

However, I am getting an error when I attempt to use this method on any pre macOS 10.13 computers. These are still modern OS versions such as 10.12 and 10.11. I have not been able to track this error down. Please see the code below.

Note: There are likely some extra import statements as this is pulled from a larger script. This snippet should work as-is.

#!/usr/bin/python

import subprocess
import platform
import os
import pwd
import sys
import hashlib
import plistlib
import time
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
from distutils.version import StrictVersion as SV

def getLoggedInUserUID():
    userUID = SCDynamicStoreCopyConsoleUser(None, None, None)[1]
    return userUID

def getLoggedInUsername():
    username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]
    return username

def getLoggedInUserGID():
    username = getLoggedInUsername()
    pwRecord = pwd.getpwnam(username)
    userGID = pwRecord.pw_gid
    return userGID

def getLoggedInUserHomeDir():
    username = getLoggedInUsername()
    pwRecord = pwd.getpwnam(username)
    homeDir = pwRecord.pw_dir
    return homeDir

def demote():
    def result():
        os.setgid(getLoggedInUserGID())
        os.setuid(getLoggedInUserUID())
    return result

def setupEnvironment():
    environment = os.environ.copy()
    environment['HOME'] = str(getLoggedInUserHomeDir())
    environment['LOGNAME'] = str(getLoggedInUsername())
    environment['PWD'] = str(getLoggedInUserHomeDir())
    environment['USER'] = str(getLoggedInUsername())
    return environment

def launchCommand():
    command = ['echo', 'whoami']
    process = subprocess.Popen(command,
                               stdout=subprocess.PIPE,
                               preexec_fn=demote(),
                               cwd=str(getLoggedInUserHomeDir()),
                               env=setupEnvironment())

def main():
    launchCommand()

if __name__== "__main__":
    main()

The error that I get is:

Traceback (most recent call last):
  File "testScript.py", line 60, in <module>
    main()
  File "testScript.py", line 57, in main
    launchCommand()
  File "testScript.py", line 54, in launchCommand
    env=setupEnvironment())
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1335, in _execute_child
    raise child_exception
KeyError: 'getpwnam(): name not found: '

It looks like it is missing some key value but I cannot for the like of me figure out what it is. Any help in tracking this down so I can run the command as the logged in user would help greatly.

Thanks in Advance, Ed

Edward S.
  • 331
  • 2
  • 15
  • use the sudo command in the Terminal app to execute commands as a different user – stovfl Sep 17 '18 at 14:47
  • Not a Mac person, so cannot tell you why, or how to do it differently, but `getLoggedInUsername` (i.e. `SCDynamicStoreCopyConsoleUser...`) returned an empty string. You'd get the same error if you called `pwd.getpwnam('')`. So some interface must have changed between OS versions, I'd play with the call a bit to see what it returns on different versions of the OS or perhaps try to check out docs for documented changes first. – Ondrej K. Sep 17 '18 at 15:08

1 Answers1

0

The way that I did in my small mdm managed environment is like this:

I developed small windowless LoginItem helperApp that starts for every open user session on the mac and listens for custom distributed system notification. It also has a function for executing terminal commands without showing terminal window (You can find examples for this on stackowerflow). I transmit to all apps currently running on the system two params in the notification: an username and a terminal command string. All of the users running instances get the notification, than they check the username for if they run in that user and the one that does - executes the command in that users name.

Try this if it fits your requirement.