@@ -1,2 +1,7 @@ | |||
*.egg-info | |||
MANIFEST.in | |||
build/ | |||
dist/ | |||
setup.cfg | |||
venv/ | |||
*.pyc |
@@ -3,6 +3,8 @@ CHANGELOG | |||
0.12.6 (unreleased) | |||
----------------------- | |||
- fix cve CVE Request ---- SOAPpy 0.12.5 Multiple Vulnerabilities -- XXE part | |||
[kiorky] | |||
- Remove dependency on fpconst. | |||
- adding maptype [Sandro Knauß] | |||
- Support / (and other reserved characters) in the password. [Ionut Turturica] | |||
@@ -0,0 +1,10 @@ | |||
include *.txt *.cfg *.rst | |||
recursive-include validate * | |||
recursive-include contrib * | |||
recursive-include src * | |||
recursive-include tests * | |||
recursive-include tools * | |||
recursive-include zope * | |||
recursive-include docs * | |||
recursive-include bid * | |||
global-exclude *pyc |
@@ -1,4 +1,5 @@ | |||
# SOAPpy modules | |||
import traceback | |||
from Config import Config | |||
from Types import * | |||
from NS import NS | |||
@@ -7,6 +8,10 @@ from Utilities import * | |||
import string | |||
import xml.sax | |||
from wstools.XMLname import fromXMLname | |||
try: | |||
from cStringIO import StringIO | |||
except ImportError: | |||
from StringIO import StringIO | |||
try: from M2Crypto import SSL | |||
except: pass | |||
@@ -93,7 +98,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
elif prefix: | |||
tag = prefix + ":" + tag | |||
return tag | |||
# Workaround two sax bugs | |||
if name[0] == None and name[1][0] == ' ': | |||
name = (None, name[1][1:]) | |||
@@ -127,7 +132,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
elif self._next == "": | |||
raise Error, "expected nothing, " \ | |||
"got `%s'" % toStr( name ) | |||
if len(self._stack) == 2: | |||
rules = self._rules | |||
@@ -275,7 +280,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
null = 1 | |||
# check for nil=1, but watch out for string values | |||
try: | |||
try: | |||
null = int(null) | |||
except ValueError, e: | |||
if not e[0].startswith("invalid literal for int()"): | |||
@@ -312,7 +317,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
#print "cur.kind=", cur.kind | |||
#print "cur.rules=", cur.rules | |||
#print "\n" | |||
if cur.rules != None: | |||
rule = cur.rules | |||
@@ -374,7 +379,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
# print "ns:", ns | |||
# print "attrs:", attrs | |||
# print "kind:", kind | |||
if kind == None: | |||
# If the current item's container is an array, it will | |||
@@ -863,7 +868,7 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
# print " attrs=", attrs | |||
# print " t[0]=", t[0] | |||
# print " t[1]=", t[1] | |||
# print " in?", t[0] in NS.EXSD_L | |||
if t[0] in NS.EXSD_L: | |||
@@ -933,11 +938,11 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
elif d == 0: | |||
if type(self.zerofloatre) == StringType: | |||
self.zerofloatre = re.compile(self.zerofloatre) | |||
if self.zerofloatre.search(s): | |||
raise UnderflowError, "invalid %s: %s" % (t[1], s) | |||
return d | |||
if t[1] in ("dateTime", "date", "timeInstant", "time"): | |||
return self.convertDateTime(d, t[1]) | |||
if t[1] == "decimal": | |||
@@ -1031,14 +1036,17 @@ class SOAPParser(xml.sax.handler.ContentHandler): | |||
################################################################################ | |||
# call to SOAPParser that keeps all of the info | |||
################################################################################ | |||
def _parseSOAP(xml_str, rules = None): | |||
try: | |||
from cStringIO import StringIO | |||
except ImportError: | |||
from StringIO import StringIO | |||
class EmptyEntityResolver(xml.sax.handler.EntityResolver): | |||
def resolveEntity(self, publicId, systemId): | |||
return StringIO("<?xml version='1.0' encoding='UTF-8'?>") | |||
def _parseSOAP(xml_str, rules = None, ignore_ext=None): | |||
if ignore_ext is None: | |||
ignore_ext = False | |||
parser = xml.sax.make_parser() | |||
t = SOAPParser(rules = rules) | |||
t = SOAPParser(rules=rules) | |||
parser.setContentHandler(t) | |||
e = xml.sax.handler.ErrorHandler() | |||
parser.setErrorHandler(e) | |||
@@ -1046,15 +1054,19 @@ def _parseSOAP(xml_str, rules = None): | |||
inpsrc = xml.sax.xmlreader.InputSource() | |||
inpsrc.setByteStream(StringIO(xml_str)) | |||
# disable by default entity loading on posted content | |||
if ignore_ext: | |||
parser.setEntityResolver(EmptyEntityResolver()) | |||
# turn on namespace mangeling | |||
parser.setFeature(xml.sax.handler.feature_namespaces,1) | |||
parser.setFeature(xml.sax.handler.feature_namespaces, 1) | |||
try: | |||
parser.parse(inpsrc) | |||
except xml.sax.SAXParseException, e: | |||
parser._parser = None | |||
print traceback.format_exc() | |||
raise e | |||
return t | |||
################################################################################ | |||
@@ -1068,9 +1080,9 @@ def parseSOAP(xml_str, attrs = 0): | |||
return t.body | |||
def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None): | |||
def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None, ignore_ext=None): | |||
t = _parseSOAP(xml_str, rules = rules) | |||
t = _parseSOAP(xml_str, rules = rules, ignore_ext=ignore_ext) | |||
p = t.body[0] | |||
# Empty string, for RPC this translates into a void | |||
@@ -1080,7 +1092,7 @@ def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None): | |||
if k[0] != "_": | |||
name = k | |||
p = structType(name) | |||
if header or body or attrs: | |||
ret = (p,) | |||
if header : ret += (t.header,) | |||
@@ -188,8 +188,9 @@ class SOAPServerBase: | |||
if namespace[0] == ":": namespace = namespace[1:] | |||
del self.objmap[namespace] | |||
class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
ignore_ext = True | |||
def version_string(self): | |||
return '<a href="http://pywebsvcs.sf.net">' + \ | |||
'SOAPpy ' + __version__ + '</a> (Python ' + \ | |||
@@ -204,7 +205,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
def do_POST(self): | |||
global _contexts | |||
status = 500 | |||
try: | |||
if self.server.config.dumpHeadersIn: | |||
@@ -226,7 +227,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
debugFooter(s) | |||
(r, header, body, attrs) = \ | |||
parseSOAPRPC(data, header = 1, body = 1, attrs = 1) | |||
parseSOAPRPC(data, header = 1, body = 1, attrs = 1, ignore_ext=self.ignore_ext) | |||
method = r._name | |||
args = r._aslist() | |||
@@ -252,8 +253,8 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
ordered_args = {} | |||
named_args = {} | |||
if Config.specialArgs: | |||
if Config.specialArgs: | |||
for (k,v) in kw.items(): | |||
if k[0]=="v": | |||
@@ -271,13 +272,13 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
# if r._ns is specified use it, if not check for | |||
# a path, if it's specified convert it and use it as the | |||
# namespace. If both are specified, use r._ns. | |||
ns = r._ns | |||
if len(self.path) > 1 and not ns: | |||
ns = self.path.replace("/", ":") | |||
if ns[0] == ":": ns = ns[1:] | |||
# authorization method | |||
a = None | |||
@@ -291,9 +292,9 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
#print '<-> Argument Matching Yielded:' | |||
#print '<-> Ordered Arguments:' + str(ordered_args) | |||
#print '<-> Named Arguments :' + str(named_args) | |||
resp = "" | |||
# For fault messages | |||
if ns: | |||
nsmethod = "%s:%s" % (ns, method) | |||
@@ -318,7 +319,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
# there are none, because the split will return | |||
# [method] | |||
f = self.server.objmap[ns] | |||
# Look for the authorization method | |||
if self.server.config.authMethod != None: | |||
authmethod = self.server.config.authMethod | |||
@@ -359,7 +360,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
if "SOAPAction".lower() not in self.headers.keys() or \ | |||
self.headers["SOAPAction"] == "\"\"": | |||
self.headers["SOAPAction"] = method | |||
thread_id = thread.get_ident() | |||
_contexts[thread_id] = SOAPContext(header, body, | |||
attrs, data, | |||
@@ -374,11 +375,11 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
raise faultType("%s:Server" % NS.ENV_T, | |||
"Authorization failed.", | |||
"%s" % nsmethod) | |||
# If it's wrapped, some special action may be needed | |||
if isinstance(f, MethodSig): | |||
c = None | |||
if f.context: # retrieve context object | |||
c = _contexts[thread_id] | |||
@@ -389,9 +390,9 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
elif f.keywords: | |||
# This is lame, but have to de-unicode | |||
# keywords | |||
strkw = {} | |||
for (k, v) in kw.items(): | |||
strkw[str(k)] = v | |||
if c: | |||
@@ -408,7 +409,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
else: | |||
fr = apply(f, args, {}) | |||
if type(fr) == type(self) and \ | |||
isinstance(fr, voidType): | |||
resp = buildSOAP(kw = {'%sResponse' % method: fr}, | |||
@@ -423,7 +424,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
# Clean up _contexts | |||
if _contexts.has_key(thread_id): | |||
del _contexts[thread_id] | |||
except Exception, e: | |||
import traceback | |||
info = sys.exc_info() | |||
@@ -558,7 +559,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
self.connection.shutdown(1) | |||
def do_GET(self): | |||
#print 'command ', self.command | |||
#print 'path ', self.path | |||
#print 'request_version', self.request_version | |||
@@ -567,7 +568,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
#print ' maintype', self.headers.maintype | |||
#print ' subtype ', self.headers.subtype | |||
#print ' params ', self.headers.plist | |||
path = self.path.lower() | |||
if path.endswith('wsdl'): | |||
method = 'wsdl' | |||
@@ -575,13 +576,13 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
if self.server.funcmap.has_key(namespace) \ | |||
and self.server.funcmap[namespace].has_key(method): | |||
function = self.server.funcmap[namespace][method] | |||
else: | |||
else: | |||
if namespace in self.server.objmap.keys(): | |||
function = self.server.objmap[namespace] | |||
l = method.split(".") | |||
for i in l: | |||
function = getattr(function, i) | |||
if function: | |||
self.send_response(200) | |||
self.send_header("Content-type", 'text/plain') | |||
@@ -589,7 +590,7 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
response = apply(function, ()) | |||
self.wfile.write(str(response)) | |||
return | |||
# return error | |||
self.send_response(200) | |||
self.send_header("Content-type", 'text/html') | |||
@@ -614,13 +615,17 @@ class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
</body>''') | |||
def log_message(self, format, *args): | |||
if self.server.log: | |||
BaseHTTPServer.BaseHTTPRequestHandler.\ | |||
log_message (self, format, *args) | |||
class SOAPInsecureRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |||
'''Request handler that does load POSTed doctypes''' | |||
ignore_ext = False | |||
class SOAPServer(SOAPServerBase, SocketServer.TCPServer): | |||
@@ -679,19 +684,19 @@ class ThreadingSOAPServer(SOAPServerBase, SocketServer.ThreadingTCPServer): | |||
if hasattr(socket, "AF_UNIX"): | |||
class SOAPUnixSocketServer(SOAPServerBase, SocketServer.UnixStreamServer): | |||
def __init__(self, addr = 8000, | |||
RequestHandler = SOAPRequestHandler, log = 0, encoding = 'UTF-8', | |||
config = Config, namespace = None, ssl_context = None): | |||
# Test the encoding, raising an exception if it's not known | |||
if encoding != None: | |||
''.encode(encoding) | |||
if ssl_context != None and not config.SSLserver: | |||
raise AttributeError, \ | |||
"SSL server not supported by this Python installation" | |||
self.namespace = namespace | |||
self.objmap = {} | |||
self.funcmap = {} | |||
@@ -699,8 +704,12 @@ if hasattr(socket, "AF_UNIX"): | |||
self.encoding = encoding | |||
self.config = config | |||
self.log = log | |||
self.allow_reuse_address= 1 | |||
SocketServer.UnixStreamServer.__init__(self, str(addr), RequestHandler) | |||
@@ -0,0 +1,11 @@ | |||
#!/usr/bin/env python | |||
# -*- coding: utf-8 -*- | |||
__docformat__ = 'restructuredtext en' | |||
#!/usr/bin/env python | |||
# coding:utf-8 | |||
from SOAPpy import SOAPProxy | |||
server = SOAPProxy("http://localhost:8080/") | |||
print server.echo("Hello world") | |||
# vim:set et sts=4 ts=4 tw=80: |
@@ -0,0 +1,13 @@ | |||
#!/usr/bin/env python | |||
# -*- coding: utf-8 -*- | |||
__docformat__ = 'restructuredtext en' | |||
#!/usr/bin/env python | |||
# encoding:utf-8 | |||
from SOAPpy import SOAPServer | |||
def echo(s): | |||
return s # repeats a string twice | |||
server = SOAPServer(("0.0.0.0", 8080)) | |||
server.registerFunction(echo) | |||
server.serve_forever() | |||
# vim:set et sts=4 ts=4 tw=80: |
@@ -0,0 +1,21 @@ | |||
POST / HTTP/1.0 | |||
Host: localhost:8080 | |||
User-agent: SOAPpy 0.12.0 (pywebsvcs.sf.net) | |||
Content-type: text/xml; charset="UTF-8" | |||
Content-length: 10000000 | |||
SOAPAction: "echo" | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE v1 [ <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> | |||
<SOAP-ENV:Envelope | |||
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" | |||
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" | |||
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" | |||
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" | |||
xmlns:xsd="http://www.w3.org/1999/XMLSchema" > | |||
<SOAP-ENV:Body> | |||
<echo SOAP-ENC:root="1"> | |||
<v1 xsi:type="xsd:string">&xxe; aaa</v1> | |||
</echo> | |||
</SOAP-ENV:Body> | |||
</SOAP-ENV:Envelope> |
@@ -0,0 +1,32 @@ | |||
POST / HTTP/1.0 | |||
Host: localhost:8080 | |||
User-agent: SOAPpy 0.12.0 (pywebsvcs.sf.net) | |||
Content-type: text/xml; charset="UTF-8" | |||
Content-length: 10000000 | |||
SOAPAction: "echo" | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE v1 [ | |||
<!ENTITY lol "lol"> | |||
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> | |||
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> | |||
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> | |||
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> | |||
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> | |||
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> | |||
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> | |||
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> | |||
]> | |||
<SOAP-ENV:Envelope | |||
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" | |||
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" | |||
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" | |||
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" | |||
xmlns:xsd="http://www.w3.org/1999/XMLSchema" | |||
> | |||
<SOAP-ENV:Body> | |||
<echo SOAP-ENC:root="1"> | |||
<v1 xsi:type="xsd:string">&lol9;</v1> | |||
</echo> | |||
</SOAP-ENV:Body> | |||
</SOAP-ENV:Envelope> |
@@ -0,0 +1,21 @@ | |||
POST / HTTP/1.0 | |||
Host: localhost:8080 | |||
User-agent: SOAPpy 0.12.0 (pywebsvcs.sf.net) | |||
Content-type: text/xml; charset="UTF-8" | |||
Content-length: 484 | |||
SOAPAction: "echo" | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<SOAP-ENV:Envelope | |||
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" | |||
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" | |||
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" | |||
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" | |||
xmlns:xsd="http://www.w3.org/1999/XMLSchema" | |||
> | |||
<SOAP-ENV:Body> | |||
<echo SOAP-ENC:root="1"> | |||
<v1 xsi:type="xsd:string">Hello world</v1> | |||
</echo> | |||
</SOAP-ENV:Body> | |||
</SOAP-ENV:Envelope> |