xmlrpc.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # defusedxml
  2. #
  3. # Copyright (c) 2013 by Christian Heimes <christian@python.org>
  4. # Licensed to PSF under a Contributor Agreement.
  5. # See http://www.python.org/psf/license for licensing details.
  6. """Defused xmlrpclib
  7. Also defuses gzip bomb
  8. """
  9. from __future__ import print_function, absolute_import
  10. import io
  11. from .common import (DTDForbidden, EntitiesForbidden,
  12. ExternalReferenceForbidden, PY3, PY31, PY26)
  13. if PY3:
  14. __origin__ = "xmlrpc.client"
  15. from xmlrpc.client import ExpatParser
  16. from xmlrpc import client as xmlrpc_client
  17. from xmlrpc import server as xmlrpc_server
  18. if not PY31:
  19. from xmlrpc.client import gzip_decode as _orig_gzip_decode
  20. from xmlrpc.client import GzipDecodedResponse as _OrigGzipDecodedResponse
  21. else:
  22. __origin__ = "xmlrpclib"
  23. from xmlrpclib import ExpatParser
  24. import xmlrpclib as xmlrpc_client
  25. xmlrpc_server = None
  26. if not PY26:
  27. from xmlrpclib import gzip_decode as _orig_gzip_decode
  28. from xmlrpclib import GzipDecodedResponse as _OrigGzipDecodedResponse
  29. try:
  30. import gzip
  31. except ImportError:
  32. gzip = None
  33. # Limit maximum request size to prevent resource exhaustion DoS
  34. # Also used to limit maximum amount of gzip decoded data in order to prevent
  35. # decompression bombs
  36. # A value of -1 or smaller disables the limit
  37. MAX_DATA = 30 * 1024 * 1024 # 30 MB
  38. def defused_gzip_decode(data, limit=None):
  39. """gzip encoded data -> unencoded data
  40. Decode data using the gzip content encoding as described in RFC 1952
  41. """
  42. if not gzip:
  43. raise NotImplementedError
  44. if limit is None:
  45. limit = MAX_DATA
  46. f = io.BytesIO(data)
  47. gzf = gzip.GzipFile(mode="rb", fileobj=f)
  48. try:
  49. if limit < 0: # no limit
  50. decoded = gzf.read()
  51. else:
  52. decoded = gzf.read(limit + 1)
  53. except IOError:
  54. raise ValueError("invalid data")
  55. f.close()
  56. gzf.close()
  57. if limit >= 0 and len(decoded) > limit:
  58. raise ValueError("max gzipped payload length exceeded")
  59. return decoded
  60. class DefusedGzipDecodedResponse(gzip.GzipFile if gzip else object):
  61. """a file-like object to decode a response encoded with the gzip
  62. method, as described in RFC 1952.
  63. """
  64. def __init__(self, response, limit=None):
  65. #response doesn't support tell() and read(), required by
  66. #GzipFile
  67. if not gzip:
  68. raise NotImplementedError
  69. self.limit = limit = limit if limit is not None else MAX_DATA
  70. if limit < 0: # no limit
  71. data = response.read()
  72. self.readlength = None
  73. else:
  74. data = response.read(limit + 1)
  75. self.readlength = 0
  76. if limit >= 0 and len(data) > limit:
  77. raise ValueError("max payload length exceeded")
  78. self.stringio = io.BytesIO(data)
  79. gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
  80. def read(self, n):
  81. if self.limit >= 0:
  82. left = self.limit - self.readlength
  83. n = min(n, left + 1)
  84. data = gzip.GzipFile.read(self, n)
  85. self.readlength += len(data)
  86. if self.readlength > self.limit:
  87. raise ValueError("max payload length exceeded")
  88. return data
  89. else:
  90. return gzip.GzipFile.read(self, n)
  91. def close(self):
  92. gzip.GzipFile.close(self)
  93. self.stringio.close()
  94. class DefusedExpatParser(ExpatParser):
  95. def __init__(self, target, forbid_dtd=False, forbid_entities=True,
  96. forbid_external=True):
  97. ExpatParser.__init__(self, target)
  98. self.forbid_dtd = forbid_dtd
  99. self.forbid_entities = forbid_entities
  100. self.forbid_external = forbid_external
  101. parser = self._parser
  102. if self.forbid_dtd:
  103. parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl
  104. if self.forbid_entities:
  105. parser.EntityDeclHandler = self.defused_entity_decl
  106. parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl
  107. if self.forbid_external:
  108. parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler
  109. def defused_start_doctype_decl(self, name, sysid, pubid,
  110. has_internal_subset):
  111. raise DTDForbidden(name, sysid, pubid)
  112. def defused_entity_decl(self, name, is_parameter_entity, value, base,
  113. sysid, pubid, notation_name):
  114. raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)
  115. def defused_unparsed_entity_decl(self, name, base, sysid, pubid,
  116. notation_name):
  117. # expat 1.2
  118. raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)
  119. def defused_external_entity_ref_handler(self, context, base, sysid,
  120. pubid):
  121. raise ExternalReferenceForbidden(context, base, sysid, pubid)
  122. def monkey_patch():
  123. xmlrpc_client.FastParser = DefusedExpatParser
  124. if PY26 or PY31:
  125. # Python 2.6 and 3.1 have no gzip support in xmlrpc
  126. return
  127. xmlrpc_client.GzipDecodedResponse = DefusedGzipDecodedResponse
  128. xmlrpc_client.gzip_decode = defused_gzip_decode
  129. if xmlrpc_server:
  130. xmlrpc_server.gzip_decode = defused_gzip_decode
  131. def unmonkey_patch():
  132. xmlrpc_client.FastParser = None
  133. if PY26 or PY31:
  134. return
  135. xmlrpc_client.GzipDecodedResponse = _OrigGzipDecodedResponse
  136. xmlrpc_client.gzip_decode = _orig_gzip_decode
  137. if xmlrpc_server:
  138. xmlrpc_server.gzip_decode = _orig_gzip_decode