@@ -3272,34 +3272,48 @@ def _async_route(self, msg, in_stream=None):
32723272 ))
32733273 return
32743274
3275- # Perform source verification.
3275+ parent_stream = self ._stream_by_id .get (mitogen .parent_id )
3276+ src_stream = self ._stream_by_id .get (msg .src_id , parent_stream )
3277+
3278+ # When the ingress stream is known, verify the message was received on
3279+ # the same as the stream we would expect to receive messages from the
3280+ # src_id and auth_id. This is like Reverse Path Filtering in IP, and
3281+ # ensures messages from a privileged context cannot be spoofed by a
3282+ # child.
32763283 if in_stream :
3277- parent = self ._stream_by_id .get (mitogen .parent_id )
3278- expect = self ._stream_by_id .get (msg .auth_id , parent )
3279- if in_stream != expect :
3284+ auth_stream = self ._stream_by_id .get (msg .auth_id , parent_stream )
3285+ if in_stream != auth_stream :
32803286 LOG .error ('%r: bad auth_id: got %r via %r, not %r: %r' ,
3281- self , msg .auth_id , in_stream , expect , msg )
3287+ self , msg .auth_id , in_stream , auth_stream , msg )
32823288 return
32833289
3284- if msg .src_id != msg .auth_id :
3285- expect = self ._stream_by_id .get (msg .src_id , parent )
3286- if in_stream != expect :
3287- LOG .error ('%r: bad src_id: got %r via %r, not %r: %r' ,
3288- self , msg .src_id , in_stream , expect , msg )
3289- return
3290+ if msg .src_id != msg .auth_id and in_stream != src_stream :
3291+ LOG .error ('%r: bad src_id: got %r via %r, not %r: %r' ,
3292+ self , msg .src_id , in_stream , src_stream , msg )
3293+ return
32903294
3295+ # If the stream's MitogenProtocol has auth_id set, copy it to the
3296+ # message. This allows subtrees to become privileged by stamping a
3297+ # parent's context ID. It is used by mitogen.unix to mark client
3298+ # streams (like Ansible WorkerProcess) as having the same rights as
3299+ # the parent.
32913300 if in_stream .protocol .auth_id is not None :
32923301 msg .auth_id = in_stream .protocol .auth_id
32933302
3294- # Maintain a set of IDs the source ever communicated with.
3303+ # Record the IDs the source ever communicated with.
32953304 in_stream .protocol .egress_ids .add (msg .dst_id )
32963305
32973306 if msg .dst_id == mitogen .context_id :
32983307 return self ._invoke (msg , in_stream )
32993308
33003309 out_stream = self ._stream_by_id .get (msg .dst_id )
3301- if out_stream is None :
3302- out_stream = self ._stream_by_id .get (mitogen .parent_id )
3310+ if (not out_stream ) and (parent_stream != src_stream or not in_stream ):
3311+ # No downstream route exists. The message could be from a child or
3312+ # ourselves for a parent, in which case we must forward it
3313+ # upstream, or it could be from a parent for a dead child, in which
3314+ # case its src_id/auth_id would fail verification if returned to
3315+ # the parent, so in that case reply with a dead message instead.
3316+ out_stream = parent_stream
33033317
33043318 if out_stream is None :
33053319 self ._maybe_send_dead (True , msg , self .no_route_msg ,
@@ -3310,9 +3324,9 @@ def _async_route(self, msg, in_stream=None):
33103324 (in_stream .protocol .is_privileged or
33113325 out_stream .protocol .is_privileged ):
33123326 self ._maybe_send_dead (True , msg , self .unidirectional_msg ,
3313- in_stream .protocol .remote_id ,
3314- out_stream .protocol .remote_id ,
3315- mitogen .context_id )
3327+ in_stream .protocol .remote_id ,
3328+ out_stream .protocol .remote_id ,
3329+ mitogen .context_id )
33163330 return
33173331
33183332 out_stream .protocol ._send (msg )
0 commit comments