-
Notifications
You must be signed in to change notification settings - Fork 264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support not only requesting client certificates but inspecting them once a connection established #482
Comments
Relating to #470. |
I don't think this is related to SNI, as far as I know that has to do with being able to present different certificates in response to the client hello, e.g. Client: Client hello with server name indication Whereas this has to do with additional handshake steps after that: Client: Client hello on the last step, receipt of the I'm not a TLS expert by any means though, so perhaps there's some interaction with SNI that I don't know about. |
You don't have to attach x509 dependency to wai if you extract fields in tls wrapper and put them in a well-known location. That can be a:
|
The For what it's worth, I'm very interested in seeing an easier way to do these things with |
Hi -- I'd like to resurrect this ticket. It looks like it's currently not possible to have access to client certificates in the request handler. The only field in the request that has to do with https is There is a As I understand it, how warp-tls/warp accepts connections is that it sets up a socket with a "connection maker" and then, after creation, runs regular http over it. For warp-tls the session-making is done by the tls library, so to get info on the TLS connection (like client certificates, but perhaps other parameters as well) is that this info needs to be passed back in from the connection maker in the tls into warp-tls's code somehow. A thing to consider is that multiple http requests happen over a single TLS session, and, as far as I can understand, established TLS sessions can be resumed over a new TCP connection. In both cases there wont be any new handshakes happening and thus no possibility to get access to client certificates again. So to have them for every http request they need to be kept around somehow. To do this either the tls library needs to save them (as proposed in [1], but this has performance implications? @vdukhovni) and then pass them constantly in to warp-tls request handler, or the end-user of warp-tls gets some API to access the TLS parameters returned from the connection maker and use some custom way to persist them, and then also tie them to the requests. (I think on this latter case serving https in warp-tls will become two-tier: instead of just providing a request handler to runTLS like now, one would have "TLS session handler" which after running receives TLS parameters, and then could have a closure around the regular request handler to make those parameters available. Found these related github issues: It seems that whatever the implementation might be, both tls and warp-tls need to change. Is there anyone else thinking about this? Are there other ways to get access to client certificates when handling a request? |
I'll try to take a look at this in the next few weeks, but can't make any promises... :-( |
Thanks! I might as well try "getting the thing I want" and then we could work out on what the API should be. I'm basically just looking for tips from someone more knowledgeable on if there is an obvious way this should be. I was also thinking that maybe in the warp-tls there should be a form of runTLS that would take a new |
In terms of "persisting" across resumptions, I would ensure that sufficient client certificate information is encoded in session tickets, so that the state is kept client-side, and returned to the server on each resumption. Ticketless resumption (TLS 1.2 and earlier support both tickets and session-ids) would then not be supported, maintaining server-side state for disconnected clients is too expensive IMHO, though a priority search list can help keep the total size bounded. OpenSSL session objects store only the leaf certificate and its validation status, and the application is expected to configure a session context string that disambiguates sessions that perform validations differently (say different set of trust anchors, ...). Haskell TLS could use the server's SNI string as a proxy for the validation policy, clients should not resume sessions across distinct SNI values. If one wanted to save some space, the saved client certificate could be just the "TBS" portion, without the signature, but that creates some API friction, since most APIs for extracting client cert data expect full certificates, not just the TBS portion. One could of course encapsulate the TBS inside a structure with a fake signature that is compact/constant to save some memory pressure. Once the client certificate is available both on full handshake and each resumption, there needs to be some IORef in each HTTP server connection handle that is used in the TLS onClientCertificate callback or in a suitable (new if required) callback on resumption. |
I don't know too much of TLS internals -- do you mean that it is possible to store custom data inside the session ticket and that the client certificate could be fitted in there so that it would be available at resumption?
Do you mean that in most cases there should be a separate
Could it also make sense to have some pure TLSInfo type that could be collected throughout setting up the TLS connection and then passed in as a pure value to the warp request handler? Or maybe it's too much work to weave it throughout everything. As in: pass it in as an argument, return as part of the result, e.g even |
The session ticket is an opaque encrypted blob, whose content and syntax is private to the issuing server, a server can put anything it wants to in a session ticket, all that's required is that it be able to use the session ticket and its STEK (session ticket encryption key) to recover the master secret for the session. Whatever else the server puts into a session ticket is up to the server.
No. Rather the session tickets issued for a particular SNI name should capture that SNI name, and the server should ignore tickets on resumption if the extracted SNI does not match the requested SNI. This would then prevent saved state about client certs from being used across different virtual servers, that might (at some future date if not yet) have different client cert validation policies (trust anchors, supported algorithms, ...).
I don't think that works well, because the data returned is a waste for some servers and too little for others, and will likely change over time, making for a poor API. Instead each hook should be able to optionally collect whatever relevant state is made available to that hook, and squirrel it away in some suitable IORef (which it captures as a closure). So my sense is that we want an enriched set of callbacks that have access to more of the TLS internals as the handshake is happening, and it is then up to the callbacks to capture whatever state they need. Of course if some state is retained for the lifetime of the connection, then there could be additional functions to look that up where not already provided. |
Alright, I've now dug deeper into tls. This is perhaps a random note but re-reading this haskell-tls/hs-tls#275 issue I found that at least to me it looks like the Continuing from the discussion before, if we were to store the certificate in an IORef then it looks like the best best place to put the IORef would be in the But since this github issue is just one use case then it would be better to have some generic method to store TLS related info from the hooks. So I had the following ideas:
Perhaps these latter ideas are more elaborate and I could just start by adding an IORef to the Context for the possible storage of the client certificate and then we could massage it forward to a more generic solution. |
I forked tls and warp-tls (wai) and did these changes:
The main idea is that
Hope this makes sense! Edit: wai I changes are in the tls-application branch, tls changes are on top of master, sorry about the inconsistency. |
@vdukhovni @kazu-yamamoto Hi! Sorry, I have been busy with other life, but would like to continue with the topic of making tls information available at request time. The need we had was to authenticate requests based on client certificates (based on the email saved in a certificate) that are installed in the browser. We got this working in my forks of hs-tls and wai and are using it. Just to make sure -- is there any interest in getting this into these libraries? And if so then what should I do? I guess rebase both on latest master for both, then perhaps create a small test app that shows what it does? There is probably some refactoring that needs to be done, but could use some help in deciding on how to do it, what is ok and what is not, etc. Also, maybe there is someone else I should ping and work together with? Thank you for your attention! |
@eyeinsky Since this is a real problem, I would like to fix it. The first thing you should do is send a PR of |
I created the pull request here: haskell-tls/hs-tls#405 |
New versions of |
Depended on the new releases in our project with positive results :). AFAIK this issue is now resolved, thank you! |
There is a flag in
TLSSettings
,tlsWantClientCert
, which pokestls
to ask for client certs after receiving theClientHello
however there are two and a half problems:CertificateChain
returned by the client after the demand.onClientCertificate
hook) rejects the client certificates.onClientCertificate
if you want to be selective about which client certs you accept.I've crafted a patch which addresses 1 and 2, though because if how it exposes the certificate chain via
Transport
it entails a new dependency on thex509
package by thewarp
package for that type. I'm not sure if that's acceptable or not, so take the patch as an example if it's not acceptable for whatever reason.My patch also exposes the
runServeTLS
andrunServeTLSSocket
functions which I'm guessing should have been exposed in the first place. This is in order to receive theTransport
.Branch: https://github.com/Dridus/wai/tree/client-certificates
Commit fixing 1 and 2: https://github.com/Dridus/wai/commit/c28f490eb3e2658a292077c28e67421a2671fad7
Commit exposing
runServeTLS
andrunServeTLSSocket
: https://github.com/Dridus/wai/commit/2dd2ffbbec593bb7f31662545f05da527d674038Commit (on separate branch) with test app which exercises the new behavior: https://github.com/Dridus/wai/commit/36ef66f28a8072ed815ff1ff8fe8c0311cdcd788
The text was updated successfully, but these errors were encountered: