Skip to content
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

Add support for TLS/SSL mutual authentication #396

Closed
wants to merge 8 commits into from

Conversation

bplost
Copy link

@bplost bplost commented Aug 23, 2016

Add support for client certificat authentication in TLS_FTPHandler

bplost added 2 commits August 23, 2016 10:32
Add support in the TLS_FTPHandler to check a client certificate.  This
type of support strengthens the security between the client and the
server, only allowing clients with a valid certificate to connect to the
server.

Updated the api.rst file with the two new configurable options to make
client authentication work
@@ -3414,6 +3415,9 @@ class TLS_FTPHandler(SSLConnection, FTPHandler):
certfile = None
keyfile = None
ssl_protocol = SSL.SSLv23_METHOD
# client certificate configurable attributes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this line is still using tabs, rather than spaces.

@lurch
Copy link
Contributor

lurch commented Aug 23, 2016

Just out of curiosity, what happens when the certificate is invalid? Does it just print "Bad client certificate detected" or does the connection get refused? Should some kind of warning or exception be raised, so that it can be detected programmatically?

@bplost
Copy link
Author

bplost commented Aug 23, 2016

Connection gets rejected. The error sent to the client through pyOpenSSL is a standard ssl error with the correct information in it - the client I'm using (WinSCP) shows that the certificate is invalid (or if no certificate is sent, then it shows that the server is expecting a certificate). I'll add screenshots when i create a new pull request.

@lurch
Copy link
Contributor

lurch commented Aug 23, 2016

No need to keep creating new PRs - you can simply update the existing PR with git push -f

@bplost
Copy link
Author

bplost commented Aug 23, 2016

Thanks @lurch, will do

@bplost
Copy link
Author

bplost commented Aug 23, 2016

Client error screenshots (WinSCP) when attempting connection with a bad certificate and with no certificate.
nocertificate - clienterror
badcertificate - clienterror

@bplost
Copy link
Author

bplost commented Aug 23, 2016

Anyone have ideas why Python 3.x is getting socket timeout errors in the AppVeyor build/test?

@@ -3449,6 +3453,15 @@ def __init__(self, conn, server, ioloop=None):

def __repr__(self):
return FTPHandler.__repr__(self)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flake8 (which is run by the Travis builds) is complaining about this line being blank, but still containing space characters

@giampaolo
Copy link
Owner

Some preliminary questions:

  • how does this relate to certfile and keyfile class attributes? Is tls_mutual_certfile supposed to be different than certfile or they can be the same?
  • what happens exactly in case the client does not provide a certificate? I see a callback function is called but what will the server do? Raise an exception? At the very least the callback function should log the event.

Some considerations:

  • the tls_mutual_authentication bool option looks unnecessary; I guess you can simply ad/define a tls_mutual_certfile option (which I would rename to mutual_certfile)
  • the PR is lacking tests

#print "Bad client certificate detected."
#else:
#print "Client certificate ok."
return ok
Copy link
Owner

@giampaolo giampaolo Aug 24, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is called when a client connects, as I suppose, then this should be a classmethod.

@bplost
Copy link
Author

bplost commented Aug 24, 2016

Answers to your questions @giampaolo

  • The mutual certfile is what the server checks the client's certificate against, to verify that the client is allowed to connect to that particular server. It should be different than certfile and keyfile
  • When the client provides no certificate, the OpenSSL library takes care of sending the correct response to the client (see screenshot above). I will add logging here.
  • Agreed that the bool option is unnecessary and will remove
  • Will add tests

@giampaolo
Copy link
Owner

When the client provides no certificate, the OpenSSL library takes care of sending the correct
response to the client (see screenshot above). I will add logging here.

But what happens on the server side? The client gets disconnected? When exactly? Is an exception raised? Is the callback function called before or after that happens?
Ideally, the server should gracefully handle that event, meaning catch the exception and log a message. If OpenSSL takes care of sending "something" which tells the client it should provide a certificate, then fine.

@bplost
Copy link
Author

bplost commented Aug 24, 2016

The client gets disconnected during the SSL handshake. Sample log:
noclientcertlog

After the handshake fails, the checks you already have in place take care of handling it gracefully and the server just keeps going.
The callback function is not called when no certificate is passed (as the failure happens earlier). The callback function is called when a client certificate is passed (valid or invalid). If the certificate is valid, the connection is allowed. If the certificate is invalid, again the OpenSSL library sends the correct message to the client that the cert is invalid, and I will add the internal logging into the callback method.

Cleaned up the support for client certificate checking
Added test file for specific client authentication tests
- Good cert (allows connection)
- Bad cert (does not allow connection)
- No cert (does not allow connection)
@bplost
Copy link
Author

bplost commented Aug 25, 2016

Cleaned up code and added tests

CERTFILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
'keycert.pem'))
CLIENT_CERTFILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
'clientcert.pem'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you forgot to add this clientcert.pem file to the PR?

bplost added 3 commits August 26, 2016 07:05
Fix description in api.rst
Add clientcert.pem
Rename test file to avoid tests attempting to run two servers on the
same port, causing all sorts of SSL mayhem and timeout issues
remove trailing whitespace
@lurch
Copy link
Contributor

lurch commented Aug 26, 2016

@giampaolo Apparently flake8 no longer supports Python 2.6 (https://gitlab.com/pycqa/flake8/issues/187 )
Could the Travis config be modified to not run flake8 on python2.6 ? (which would then stop Travis errors like https://travis-ci.org/giampaolo/pyftpdlib/jobs/155328753 )

@bplost
Copy link
Author

bplost commented Aug 26, 2016

I think at this point none of the failing tests are due to my changes

@zuohaocheng
Copy link

This PR is refined by #428 and can be closed.

@giampaolo giampaolo closed this Jul 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants