Issue with Popen running as another user (macOS) - python

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

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.

Related

How to implement clear in cmd.Cmd?

I am working on a custom command line interpreter, and I want to implement the 'clear' command, just like the the one in bash shell. Is there any way to do that?
I have attempted it but I don't think it is correct at all:
#!/usr/bin/python3
import cmd
import sys
import os
class HBNBCommand(cmd.Cmd):
"""Command line interpreter"""
def do_clear(self):
"""Clear command similar to clear from shell"""
os.system('clear')
if __name__ == '__main__':
HBNBCommand().cmdloop()
When I try it, it gives me the exception:
Traceback (most recent call last):
File "/home/leuel/PycharmProjects/AirBnB_clone/./console.py", line 22, in <module>
HBNBCommand().cmdloop()
File "/usr/lib/python3.10/cmd.py", line 138, in cmdloop
stop = self.onecmd(line)
File "/usr/lib/python3.10/cmd.py", line 217, in onecmd
return func(arg)
TypeError: HBNBCommand.do_clear() takes 1 positional argument but 2 were given
When inheriting from cmd to build your custom shell, methods expect at least one argument. To quote from this resource, "The interpreter uses a loop to read all lines from its input, parse them, and then dispatch the command to an appropriate command handler. Input lines are parsed into two parts. The command, and any other text on the line."
Since do_clear has not provided for an argument, the command cannot handle any additional input, even when that input does not exist.
class HBNBCommand(cmd.Cmd):
"""Command line interpreter"""
def do_clear(self, arg):
"""Clear command similar to clear from shell"""
os.system('clear')
if __name__ == '__main__':
HBNBCommand().cmdloop()
You can also refer to the Cmd example in the Python docs: https://docs.python.org/3/library/cmd.html#cmd-example

Attaching Python debugger

I'd like to attach a Python debugger to a running process. Following this comment, I tried pdb-clone but have gotten confused. Here's the script I'm attaching to:
import os
import time
from pdb_clone import pdbhandler
pdbhandler.register()
def loop(my_pid):
print("Entering loop")
while True:
x = 'frog'
time.sleep(0.5)
print("Out of Loop")
if __name__ == '__main__':
my_pid = os.getpid()
print("pid = ", my_pid)
loop(my_pid)
If I run python3 target_code_1.py in one terminal and see PID = 95439, then in a second terminal try
sudo pdb-attach --kill --pid 95439
I get an error message (which I include below).
However, suppose I simultaneously run python3 target_code_1.py in a third terminal. I can now run sudo pdb-attach --kill --pid 95439 without error, but when I print my_pid, the value is 95440. On the other hand, if I run sudo pdb-attach --kill --pid 95440 and print my_pid, the value is 95439. (In other words, it looks like pdb-attach has swapped which thread it is attaching to.) This behavior appears to be repeatable. What is going on?
For the record, the initial error message is as follows:
sudo pdb-attach --kill --pid 95440
Traceback (most recent call last):
File "/usr/local/bin/pdb-attach", line 4, in <module>
attach.main()
File "/usr/local/lib/python3.7/site-packages/pdb_clone/attach.py", line 646, in main
attach(address)
File "/usr/local/lib/python3.7/site-packages/pdb_clone/attach.py", line 596, in attach
for count in asock.connect_retry(address, verbose):
File "/usr/local/lib/python3.7/site-packages/pdb_clone/attach.py", line 115, in connect_retry
self.connect(address)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncore.py", line 342, in connect
raise OSError(err, errorcode[err])
OSError: [Errno 22] EINVAL
FWIW, I'm running on macOS Mojave 10.14.2, Python 3.7.0, Clang 9.1.0.
(If I am solving this problem the wrong way, e.g., if there is a better Python module to use that can attach to live process, I'd be happy to use it instead.)

Python can't import WMI under special circumstance

I've created a standalone exe Windows service written in Python and built with pyInstaller. When I try to import wmi, an exception is thrown.
What's really baffling is that I can do it without a problem if running the code in a foreground exe, or a foreground python script, or a python script running as a background service via pythonservice.exe!
Why does it fail under this special circumstance of running as a service exe?
import wmi
Produces this error for me:
com_error: (-2147221020, 'Invalid syntax', None, None)
Here's the traceback:
Traceback (most recent call last):
File "<string>", line 43, in onRequest
File "C:\XXX\XXX\XXX.pyz", line 98, in XXX
File "C:\XXX\XXX\XXX.pyz", line 31, in XXX
File "C:\XXX\XXX\XXX.pyz", line 24, in XXX
File "C:\XXX\XXX\XXX.pyz", line 34, in XXX
File "C:\Program Files (x86)\PyInstaller-2.1\PyInstaller\loader\pyi_importers.py", line 270, in load_module
File "C:\XXX\XXX\out00-PYZ.pyz\wmi", line 157, in <module>
File "C:\XXX\XXX\out00-PYZ.pyz\win32com.client", line 72, in GetObject
File "C:\XXX\XXX\out00-PYZ.pyz\win32com.client", line 87, in Moniker
wmi.py line 157 has a global call to GetObject:
obj = GetObject ("winmgmts:")
win32com\client__init.py__ contains GetObject(), which ends up calling Moniker():
def GetObject(Pathname = None, Class = None, clsctx = None):
"""
Mimic VB's GetObject() function.
ob = GetObject(Class = "ProgID") or GetObject(Class = clsid) will
connect to an already running instance of the COM object.
ob = GetObject(r"c:\blah\blah\foo.xls") (aka the COM moniker syntax)
will return a ready to use Python wrapping of the required COM object.
Note: You must specifiy one or the other of these arguments. I know
this isn't pretty, but it is what VB does. Blech. If you don't
I'll throw ValueError at you. :)
This will most likely throw pythoncom.com_error if anything fails.
"""
if clsctx is None:
clsctx = pythoncom.CLSCTX_ALL
if (Pathname is None and Class is None) or \
(Pathname is not None and Class is not None):
raise ValueError("You must specify a value for Pathname or Class, but not both.")
if Class is not None:
return GetActiveObject(Class, clsctx)
else:
return Moniker(Pathname, clsctx)
The first line in Moniker(), i.e. MkParseDisplayName() is where the exception is encountered:
def Moniker(Pathname, clsctx = pythoncom.CLSCTX_ALL):
"""
Python friendly version of GetObject's moniker functionality.
"""
moniker, i, bindCtx = pythoncom.MkParseDisplayName(Pathname)
dispatch = moniker.BindToObject(bindCtx, None, pythoncom.IID_IDispatch)
return __WrapDispatch(dispatch, Pathname, clsctx=clsctx)
Note: I tried using
pythoncom.CoInitialize()
which apparently solves this import problem within a thread, but that didn't work...
I also face the same issue and I figure out this issue finally,
import pythoncom and CoInitialize pythoncom.CoInitialize (). They import wmi
import pythoncom
pythoncom.CoInitialize ()
import wmi
I tried solving this countless ways. In the end, I threw in the towel and had to just find a different means of achieving the same goals I had with wmi.
Apparently that invalid syntax error is thrown when trying to create an object with an invalid "moniker name", which can simply mean the service, application, etc. doesn't exist on the system. Under this circumstance "winmgmts" just can't be found at all it seems! And yes, I tried numerous variations on that moniker with additional specs, and I tried running the service under a different user account, etc.
Honestly I didn't dig in order to understand why this occurs.
Anyway, the below imports solved my problem - which was occurring only when ran from a Flask instance:
import os
import pythoncom
pythoncom.CoInitialize()
from win32com.client import GetObject
import wmi
The error "com_error: (-2147221020, 'Invalid syntax', None, None)" is exactly what popped up in my case so I came here after a long time of searching the web and voila:
Under this circumstance "winmgmts" just can't be found at all it
seems!
This was the correct hint for because i had just a typo , used "winmgmt:" without trailing 's'. So invalid sythax refers to the first methods parameter, not the python code itself. o_0 Unfortunately I can't find any reference which objects we can get with win32com.client.GetObject()... So if anybody has a hint to which params are "allowed" / should work, please port it here. :-)
kind regards
ChrisPHL

WLST commands in child script

Currently I am working on jython script that can execute an external jython script. The executed (external) jython script must run in the same weblogic session where my script runs in order to be able to cancel the session (in case of error) or activate it. The weblogic connection is being created in my script and the called script has to use the created (with ‘startEdit()’) session.
I found a hybrid solution but perhaps it can be done better.
The executed script in the working solution:
import wlst_operations as wl
print 'Start'
wl.cd('/')
print wl.cmo
wl.cd('/Servers/AdminServer')
print wl.cmo
wl.cd('/JDBCSystemResources/pasDataSource/JDBCResource/pasDataSource/JDBCConnectionPoolPara ms/pasDataSource')
print wl.cmo
wl.cmo.setInitialCapacity(6)
The wlst_operations jython was taken from http://www.javamonamour.org/2013/08/wlst-nameerror-cd.html.
As you can see an object like reference (‘wl.’) must be put in front of each WLST command…
The output is fine:
[MBeanServerInvocationHandler]com.bea:Name=gintdev1,Type=Domain
[MBeanServerInvocationHandler]com.bea:Name=AdminServer,Type=Server
[MBeanServerInvocationHandler]com.bea:Name=pasDataSource,Type=weblogic.j2ee.descriptor.wl.JDBCConnectionPoolParamsBean,Parent=[gintdev1]/JDBCSystemResources[pasDataSource],Path=JDBCResource[pasDataSource]/JDBCConnectionPoolParams
When I don’t use the object reference:
from wlstModule import *
print 'user defined'
cd('/')
print cmo
cd('/Servers/AdminServer')
print cmo
cd('/JDBCSystemResources/pasDataSource/JDBCResource/pasDataSource/JDBCConnectionPoolParams/pasDataSource')
print cmo
cmo.setInitialCapacity(6)
Then the output is:
[MBeanServerInvocationHandler]com.bea:Name=gintdev1,Type=Domain
[MBeanServerInvocationHandler]com.bea:Name=gintdev1,Type=Domain
[MBeanServerInvocationHandler]com.bea:Name=gintdev1,Type=Domain
Problem invoking WLST - Traceback (innermost last):
File "/tmp/lv30083/./orchestrator.py", line 83, in ?
File "/tmp/lv30083/./orchestrator.py", line 66, in main
File "/tmp/lv30083/./utils/orch_wl.py", line 55, in execute
File "user_defined_script_incorrect.py", line 11, in ?
AttributeError: setInitialCapacity
i.e. the cd commands are executed (not getting error) but it just doesn’t jump to the datasource…
My script is
import orch_logging
import sys
from wlstModule import *
class WeblogicManager(object):
def connect_to_server(self, p_ssl, p_domainName, p_userConfigFile, p_userKeyFile):
logger = orch_logging.Logger()
logger.info('Trying to connect to the node manager. domainName='+p_domainName+',userConfigFile='+p_userConfigFile+',ssl='+p_ssl+',p_userKeyFile='+p_userKeyFile)
try:
connect(domainName=p_domainName,userConfigFile=p_userConfigFile,userKeyFile=p_userKeyFile)
return True
except:
logger.error("Error while trying to connect to node manager!")
return False
def startEdit(self):
edit()
startEdit()
def activate(self):
activate()
def undo(self):
cancelEdit('y')
def disconnect(self):
disconnect()
def execute(self, path):
execfile(path)
Is there any way to use the WLST commands without using the 'wl.' reference in front of them?
Thanks,
V.
I had to modify my script. All the operations are now in one context (in the context of my script)
import sys
from wlstModule import *
from weblogic.management.scripting.utils import WLSTUtil
import sys
# needed to execute normal (without object reference) WLST commands in child scripts
origPrompt = sys.ps1
theInterpreter = WLSTUtil.ensureInterpreter();
WLSTUtil.ensureWLCtx(theInterpreter)
execfile(WLSTUtil.getWLSTScriptPath())
execfile(WLSTUtil.getOfflineWLSTScriptPath())
exec(WLSTUtil.getOfflineWLSTScriptForModule())
execfile(WLSTUtil.getWLSTCommonModulePath())
theInterpreter = None
sys.ps1 = origPrompt
modules = WLSTUtil.getWLSTModules()
for mods in modules:
execfile(mods.getAbsolutePath())
wlstPrompt = "false"
class WeblogicManager(object):
...
def execute(self, path):
execfile(path)

What permissions are required for subprocess.Popen?

The following code:
gb = self.request.form['groupby']
typ = self.request.form['type']
tbl = self.request.form['table']
primary = self.request.form.get('primary', None)
if primary is not None:
create = False
else:
create = True
mdb = tempfile.NamedTemporaryFile()
mdb.write(self.request.form['mdb'].read())
mdb.seek(0)
csv = tempfile.TemporaryFile()
conversion = subprocess.Popen(("/Users/jondoe/development/mdb-export", mdb.name, tbl,),stdout=csv)
Causes the this error when calling the last line i.e. 'conversion =' in OS X.
Traceback (innermost last):
Module ZPublisher.Publish, line 119, in publish
Module ZPublisher.mapply, line 88, in mapply
Module ZPublisher.Publish, line 42, in call_object
Module circulartriangle.mdbtoat.mdb, line 62, in __call__
Module subprocess, line 543, in __init__
Module subprocess, line 975, in _execute_child
OSError: [Errno 13] Permission denied
I've tried chmod 777 /Users/jondoe/development/mdb-export - what else might be required?
Assuming that permissions on parent folders are correct (i.e. all parent folders should have +x permission), try adding:
shell=True
to the Popen command such as:
subprocess.Popen(("/Users/jondoe/development/mdb-export", mdb.name, tbl,), stdout=csv, shell=True)
It seems the 'Permissions denied error' was orginally coming from Popen trying to execute mdb-export from the wrong location (and to compound things, with the wrong permissions).
If mdbtools is installed, the following works fine and inherits the correct permissions without the need for sudo etc.
subprocess.Popen(("mdb-export", mdb.name, tbl,),stdout=csv)
(Worth noting, I got myself into a muddle for a while, having forgotten that Popen is for opening executables, not folders or non-exectable files in folders)
Thanks for all your responses, they all made for interesting reading regardless :)
Can you feed "sudo" to subprocess? See this SO thread.
#Jon Hadley, from the interpreter:
>>> import subprocess
>>> p = subprocess.call(['sudo','/usr/bin/env'])
PASSWORD:
[snip]
USER=root
USERNAME=root
SUDO_COMMAND=/usr/bin/env
SUDO_USER=telliott99
SUDO_UID=501
SUDO_GID=20
From Terminal on OS X, I have to do sudo when I run the script:
$ sudo python test.py
then this (in test.py) gives the same output as before:
import subprocess
p = subprocess.Popen('/usr/bin/env')
Getting subprocess to directly handle the authentication from a script is probably not a good idea, since it hides the privilege escalation. But you could look at pexpect and this SO answer.
You also need to ensure read and execute permissions for the user running that code on the directories up the chain - /Users, /Users/jondoe and /Users/jondoe/development.

Categories

Resources