/usr/lib/python2.7/dist-packages/Pyro4/utils/flame.py is in python2-pyro4 4.53-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | """
Pyro FLAME: Foreign Location Automatic Module Exposer.
Easy but potentially very dangerous way of exposing remote modules and builtins.
Flame requires the pickle serializer to be used.
Pyro - Python Remote Objects. Copyright by Irmen de Jong (irmen@razorvine.net).
"""
import sys
import types
import code
import os
import stat
import Pyro4.core
import Pyro4.util
import Pyro4.constants
import Pyro4.errors
try:
import importlib
except ImportError:
importlib = None
try:
import builtins
except ImportError:
import __builtin__ as builtins
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
__all__ = ["connect", "start", "createModule", "Flame"]
# Exec is a statement in Py2, a function in Py3
# Workaround as written by Ned Batchelder on his blog.
if sys.version_info > (3, 0):
def exec_function(source, filename, global_map):
source = fixExecSourceNewlines(source)
exec(compile(source, filename, "exec"), global_map)
else:
# OK, this is pretty gross. In Py2, exec was a statement, but that will
# be a syntax error if we try to put it in a Py3 file, even if it isn't
# executed. So hide it inside an evaluated string literal instead.
eval(compile("""\
def exec_function(source, filename, global_map):
source=fixExecSourceNewlines(source)
exec compile(source, filename, "exec") in global_map
""", "<exec_function>", "exec"))
def fixExecSourceNewlines(source):
if sys.version_info < (2, 7) or sys.version_info[:2] in ((3, 0), (3, 1)):
# for python versions prior to 2.7 (and 3.0/3.1), compile is kinda picky.
# it needs unix type newlines and a trailing newline to work correctly.
source = source.replace("\r\n", "\n")
source = source.rstrip() + "\n"
# remove trailing whitespace that might cause IndentationErrors
source = source.rstrip()
return source
class FlameModule(object):
"""Proxy to a remote module."""
def __init__(self, flameserver, module):
# store a proxy to the flameserver regardless of autoproxy setting
self.flameserver = Pyro4.core.Proxy(flameserver._pyroDaemon.uriFor(flameserver))
self.module = module
def __getattr__(self, item):
if item in ("__getnewargs__", "__getnewargs_ex__", "__getinitargs__"):
raise AttributeError(item)
return Pyro4.core._RemoteMethod(self.__invoke, "%s.%s" % (self.module, item), 0)
def __getstate__(self):
return self.__dict__
def __setstate__(self, args):
self.__dict__ = args
def __invoke(self, module, args, kwargs):
return self.flameserver.invokeModule(module, args, kwargs)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.flameserver._pyroRelease()
def __repr__(self):
return "<%s.%s at 0x%x; module '%s' at %s>" % (self.__class__.__module__, self.__class__.__name__,
id(self), self.module, self.flameserver._pyroUri.location)
class FlameBuiltin(object):
"""Proxy to a remote builtin function."""
def __init__(self, flameserver, builtin):
# store a proxy to the flameserver regardless of autoproxy setting
self.flameserver = Pyro4.core.Proxy(flameserver._pyroDaemon.uriFor(flameserver))
self.builtin = builtin
def __call__(self, *args, **kwargs):
return self.flameserver.invokeBuiltin(self.builtin, args, kwargs)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.flameserver._pyroRelease()
def __repr__(self):
return "<%s.%s at 0x%x; builtin '%s' at %s>" % (self.__class__.__module__, self.__class__.__name__,
id(self), self.builtin, self.flameserver._pyroUri.location)
class RemoteInteractiveConsole(object):
"""Proxy to a remote interactive console."""
class LineSendingConsole(code.InteractiveConsole):
"""makes sure the lines are sent to the remote console"""
def __init__(self, remoteconsole):
code.InteractiveConsole.__init__(self, filename="<remoteconsole>")
self.remoteconsole = remoteconsole
def push(self, line):
output, more = self.remoteconsole.push_and_get_output(line)
if output:
sys.stdout.write(output)
return more
def __init__(self, remoteconsoleuri):
# store a proxy to the console regardless of autoproxy setting
self.remoteconsole = Pyro4.core.Proxy(remoteconsoleuri)
def interact(self):
console = self.LineSendingConsole(self.remoteconsole)
console.interact(banner=self.remoteconsole.get_banner())
print("(Remote session ended)")
def close(self):
self.remoteconsole.terminate()
self.remoteconsole._pyroRelease()
def terminate(self):
self.close()
def __repr__(self):
return "<%s.%s at 0x%x; for %s>" % (self.__class__.__module__, self.__class__.__name__,
id(self), self.remoteconsole._pyroUri.location)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
@Pyro4.expose
class InteractiveConsole(code.InteractiveConsole):
"""Interactive console wrapper that saves output written to stdout so it can be returned as value"""
def push_and_get_output(self, line):
output, more = "", False
stdout_save = sys.stdout
try:
sys.stdout = StringIO()
more = self.push(line)
output = sys.stdout.getvalue()
sys.stdout.close()
finally:
sys.stdout = stdout_save
return output, more
def get_banner(self):
return self.banner # custom banner string, set by Pyro daemon
def write(self, data):
sys.stdout.write(data) # stdout instead of stderr
def terminate(self):
self._pyroDaemon.unregister(self)
self.resetbuffer()
@Pyro4.expose
class Flame(object):
"""
The actual FLAME server logic.
Usually created by using :py:meth:`Pyro4.core.Daemon.startFlame`.
Be *very* cautious before starting this: it allows the clients full access to everything on your system.
"""
def __init__(self):
if set(Pyro4.config.SERIALIZERS_ACCEPTED) != {"pickle"}:
raise RuntimeError("flame requires the pickle serializer exclusively")
def module(self, name):
"""import a module on the server given by the module name and returns a proxy to it"""
if importlib:
importlib.import_module(name)
else:
__import__(name)
return FlameModule(self, name)
def builtin(self, name):
"""returns a proxy to the given builtin on the server"""
return FlameBuiltin(self, name)
def execute(self, code):
"""execute a piece of code"""
exec_function(code, "<remote-code>", globals())
def evaluate(self, expression):
"""evaluate an expression and return its result"""
return eval(expression)
def sendmodule(self, modulename, modulesource):
"""
Send the source of a module to the server and make the server load it.
Note that you still have to actually ``import`` it on the server to access it.
Sending a module again will replace the previous one with the new.
"""
createModule(modulename, modulesource)
def getmodule(self, modulename):
"""obtain the source code from a module on the server"""
import inspect
module = __import__(modulename, globals={}, locals={})
return inspect.getsource(module)
def sendfile(self, filename, filedata):
"""store a new file on the server"""
with open(filename, "wb") as targetfile:
os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR) # readable/writable by owner only
targetfile.write(filedata)
def getfile(self, filename):
"""read any accessible file from the server"""
with open(filename, "rb") as diskfile:
return diskfile.read()
def console(self):
"""get a proxy for a remote interactive console session"""
console = InteractiveConsole(filename="<remoteconsole>")
uri = self._pyroDaemon.register(console)
console.banner = "Python %s on %s\n(Remote console on %s)" % (sys.version, sys.platform, uri.location)
return RemoteInteractiveConsole(uri)
@Pyro4.expose
def invokeBuiltin(self, builtin, args, kwargs):
return getattr(builtins, builtin)(*args, **kwargs)
@Pyro4.expose
def invokeModule(self, dottedname, args, kwargs):
# dottedname is something like "os.path.walk" so strip off the module name
modulename, dottedname = dottedname.split('.', 1)
module = sys.modules[modulename]
# Look up the actual method to call.
# Because Flame already opens all doors, security wise, we allow ourselves to
# look up a dotted name via object traversal. The security implication of that
# is overshadowed by the security implications of enabling Flame in the first place.
# We also don't check for access to 'private' methods. Same reasons.
method = module
for attr in dottedname.split('.'):
method = getattr(method, attr)
return method(*args, **kwargs)
def createModule(name, source, filename="<dynamic-module>", namespace=None):
"""
Utility function to create a new module with the given name (dotted notation allowed), directly from the source string.
Adds it to sys.modules, and returns the new module object.
If you provide a namespace dict (such as ``globals()``), it will import the module into that namespace too.
"""
path = ""
components = name.split('.')
module = types.ModuleType("pyro-flame-module-context")
for component in components:
# build the module hierarchy.
path += '.' + component
real_path = path[1:]
if real_path in sys.modules:
# use already loaded modules instead of overwriting them
module = sys.modules[real_path]
else:
setattr(module, component, types.ModuleType(real_path))
module = getattr(module, component)
sys.modules[real_path] = module
exec_function(source, filename, module.__dict__)
if namespace is not None:
namespace[components[0]] = __import__(name)
return module
def start(daemon):
"""
Create and register a Flame server in the given daemon.
Be *very* cautious before starting this: it allows the clients full access to everything on your system.
"""
if Pyro4.config.FLAME_ENABLED:
if set(Pyro4.config.SERIALIZERS_ACCEPTED) != {"pickle"}:
raise Pyro4.errors.SerializeError("Flame requires the pickle serializer exclusively")
return daemon.register(Flame(), Pyro4.constants.FLAME_NAME)
else:
raise Pyro4.errors.SecurityError("Flame is disabled in the server configuration")
def connect(location):
"""
Connect to a Flame server on the given location, for instance localhost:9999 or ./u:unixsock
This is just a convenience function to creates an appropriate Pyro proxy.
"""
if Pyro4.config.SERIALIZER != "pickle":
raise Pyro4.errors.SerializeError("Flame requires the pickle serializer")
proxy = Pyro4.core.Proxy("PYRO:%s@%s" % (Pyro4.constants.FLAME_NAME, location))
proxy._pyroBind()
return proxy
|