Description
Since there's a lot of code in common between this and requests/requests-kerberos, I believe requests-gssapi has some concurrency bugs as well.
requests/requests-kerberos#113 has details, and requests/requests-kerberos#114 has a fix which can be adapted.
The problem stems from how we index gssapi.SecurityContext
objects by hostname
. If you have multiple threads sharing an HTTPSPNEGOAuth
object, and they both send requests to the same host:
- thread1 generates a
SecurityContext
to authenticate a request destined tofoo.com
and caches it inself.context["foo.com"]
and uses it tostep()
. - thread2 generates a
SecurityContext
to authenticate another request, also destined tofoo.com
and caches it inself.context["foo.com"]
, overwriting theSecurityContext
placed there by thread1, and uses it tostep()
. - thread1 receives a response, retrieves the
SecurityContext
(generated by thread2) to authenticate the response fromfoo.com
, attempts tostep()
with the token it received, and ...
At this point, we're likely to either get a mutual authentication exception because the SecurityContext
was used for a different request, or it may happen to work due to implementation details (though probably not). And even if it did work, when thread2 receives its response and attempts to authenticate it, it's likely to get a mutual authentication exception because the SecurityContext
was fully established.
And the potential is also there for there to be a concurrency issue within generate_request_header
alone, since it always accesses the SecurityContext
via the self.context
dictionary. Depending on how frequently the interpreter re-schedules threads (and the GIL is released), it's possible for thread1 to generate its context, then have the interpreter schedule thread2 which will replace it with another context. Then whichever one calls step()
on it first will succeed, and whichever one calls step()
on it second will likely fail since they'll both use the same context since they're accessing it via the self.context
dictionary.
The solution for this is to index by the request (or PreparedRequest) object, not the hostname in the url in the request as we currently do.
As the original author of the code in question, my bad.