xmlrpc.py 5.2 KB

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