comparison sat_pubsub/delegation.py @ 460:607616f9ef5b

backend: new `server_jid` option: Server domain must be known to validate requests, this can be done explicitely by using the `server_jid` option. If this option is not set, the server domain is found: - by using the `from` name of the initial delegation advertising message - or it fallbacks to using the part after initial `.` (`pubsub.example.org` would give `example.org`)
author Goffi <goffi@goffi.org>
date Fri, 15 Oct 2021 09:32:07 +0200
parents cebcb7f56889
children b544109ab4c4
comparison
equal deleted inserted replaced
459:cebcb7f56889 460:607616f9ef5b
53 class DelegationsHandler(XMPPHandler): 53 class DelegationsHandler(XMPPHandler):
54 _service_hacked = False 54 _service_hacked = False
55 55
56 def __init__(self): 56 def __init__(self):
57 super(DelegationsHandler, self).__init__() 57 super(DelegationsHandler, self).__init__()
58 self.backend = None
58 59
59 def _service_hack(self): 60 def _service_hack(self):
60 """Patch the request classes of services to track delegated stanzas""" 61 """Patch the request classes of services to track delegated stanzas"""
61 # XXX: we need to monkey patch to track origin of the stanza in PubSubRequest. 62 # XXX: we need to monkey patch to track origin of the stanza in PubSubRequest.
62 # As PubSubRequest from sat.tmp.wokkel.pubsub use _request_class while 63 # As PubSubRequest from sat.tmp.wokkel.pubsub use _request_class while
100 setattr(module, default_base_cls, RequestWithDelegation) 101 setattr(module, default_base_cls, RequestWithDelegation)
101 module_patched = True 102 module_patched = True
102 DelegationsHandler._service_hacked = True 103 DelegationsHandler._service_hacked = True
103 104
104 def connectionInitialized(self): 105 def connectionInitialized(self):
106 self.backend = self.parent.parent.getServiceNamed('backend')
105 if not self._service_hacked: 107 if not self._service_hacked:
106 self._service_hack() 108 self._service_hack()
107 self.xmlstream.addObserver(DELEGATION_ADV_XPATH, self.onAdvertise) 109 self.xmlstream.addObserver(DELEGATION_ADV_XPATH, self.onAdvertise)
108 self.xmlstream.addObserver(DELEGATION_FWD_XPATH, self._obsWrapper, 0, self.onForward) 110 self.xmlstream.addObserver(DELEGATION_FWD_XPATH, self._obsWrapper, 0, self.onForward)
109 self._current_iqs = {} # dict of iq being handler by delegation 111 self._current_iqs = {} # dict of iq being handler by delegation
148 self._xs_send(error_elt) 150 self._xs_send(error_elt)
149 stanza.handled = True 151 stanza.handled = True
150 152
151 def onAdvertise(self, message): 153 def onAdvertise(self, message):
152 """Manage the <message/> advertising delegations""" 154 """Manage the <message/> advertising delegations"""
155 if self.backend.config["server_jid"] is None:
156 # if server_jid is not specified in config, we use the advertising message
157 # to get it (and replace the one found from this component jid)
158 self.backend.server_jid = self.backend.config["server_jid"] = jid.JID(
159 message["from"]
160 )
161 else:
162 if jid.JID(message["from"]) != self.backend.server_jid:
163 log.err(
164 f"Delagation advertising message received from {message['from']}, "
165 f"while expected server jid is {self.backend.server_jid}, this may "
166 "be a security threat, please check your configuration and network."
167 )
168 raise error.StanzaError("not-allowed")
153 delegation_elt = next(message.elements(DELEGATION_NS, 'delegation')) 169 delegation_elt = next(message.elements(DELEGATION_NS, 'delegation'))
154 delegated = {} 170 delegated = {}
155 for delegated_elt in delegation_elt.elements(DELEGATION_NS): 171 for delegated_elt in delegation_elt.elements(DELEGATION_NS):
156 try: 172 try:
157 if delegated_elt.name != 'delegated': 173 if delegated_elt.name != 'delegated':
180 def onForward(self, iq): 196 def onForward(self, iq):
181 """Manage forwarded iq 197 """Manage forwarded iq
182 198
183 @param iq(domish.Element): full delegation stanza 199 @param iq(domish.Element): full delegation stanza
184 """ 200 """
185 201 if jid.JID(iq['from']) != self.backend.server_jid:
186 # FIXME: we use a hack supposing that our delegation come from hostname 202 log.err("SECURITY WARNING: forwarded stanza doesn't come from our server: "
187 # and we are a component named [name].hostname 203 f"{iq.toXml()}")
188 # but we need to manage properly allowed servers
189 # TODO: do proper origin security check
190 _, allowed = iq['to'].split('.', 1)
191 if jid.JID(iq['from']) != jid.JID(allowed):
192 log.msg(("SECURITY WARNING: forwarded stanza doesn't come from our server: {}"
193 .format(iq.toXml())).encode('utf-8'))
194 raise error.StanzaError('not-allowed') 204 raise error.StanzaError('not-allowed')
195 205
196 try: 206 try:
197 delegation_elt = next(iq.elements(DELEGATION_NS, 'delegation')) 207 delegation_elt = next(iq.elements(DELEGATION_NS, 'delegation'))
198 forwarded_elt = next(delegation_elt.elements(FORWARDED_NS, 'forwarded')) 208 forwarded_elt = next(delegation_elt.elements(FORWARDED_NS, 'forwarded'))
222 232
223 The same features/identities are returned for main and bare nodes 233 The same features/identities are returned for main and bare nodes
224 """ 234 """
225 if not nodeIdentifier.startswith(DELEGATION_NS): 235 if not nodeIdentifier.startswith(DELEGATION_NS):
226 return [] 236 return []
227
228 try: 237 try:
229 _, namespace = nodeIdentifier.split(DELEGATION_MAIN_SEP, 1) 238 _, namespace = nodeIdentifier.split(DELEGATION_MAIN_SEP, 1)
230 except ValueError: 239 except ValueError:
231 try: 240 try:
232 _, namespace = nodeIdentifier.split(DELEGATION_BARE_SEP, 1) 241 _, namespace = nodeIdentifier.split(DELEGATION_BARE_SEP, 1)