From 92238999965e5d59cd1c295a89abcdd2e634b762 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 25 Feb 2015 00:07:26 -0500 Subject: [PATCH] ssl: support client certificates Password-protected keys are not supported. --- docs/ssl-tutorial.rst | 20 ++++++++++++++++++++ vdirsyncer/storage/dav.py | 13 ++++++++----- vdirsyncer/storage/http.py | 16 ++++++++++++++-- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/docs/ssl-tutorial.rst b/docs/ssl-tutorial.rst index 18b80c2..83163d1 100644 --- a/docs/ssl-tutorial.rst +++ b/docs/ssl-tutorial.rst @@ -53,4 +53,24 @@ the system certificate CAs. Normally these two stores are similar enough for you not to care. If the behavior on your system is somehow confusing, your best bet is explicitly setting the SSL options above. +.. _ssl-client-certs: + +Client Certificates +------------------- + +Client certificates may be specified with the ``auth_cert`` parameter. If the +key and certificate are stored in the same file, it may be a string:: + + [storage foo] + type = caldav + ... + auth_cert = "/path/to/certificate" + +If the key and certificate are separate, a list may be used:: + + [storage foo] + type = caldav + ... + auth_cert = ["/path/to/certificate", "/path/to/key"] + .. _requests: http://www.python-requests.org/ diff --git a/vdirsyncer/storage/dav.py b/vdirsyncer/storage/dav.py index 4524462..9980a7f 100644 --- a/vdirsyncer/storage/dav.py +++ b/vdirsyncer/storage/dav.py @@ -9,7 +9,7 @@ from requests.exceptions import HTTPError from .base import Item, Storage from .http import HTTP_STORAGE_PARAMETERS, USERAGENT, prepare_auth, \ - prepare_verify + prepare_verify, prepare_client_cert from .. import exceptions, log, utils from ..utils.compat import text_type @@ -208,7 +208,8 @@ class DavSession(object): ''' def __init__(self, url, username='', password='', verify=True, auth=None, - useragent=USERAGENT, verify_fingerprint=None): + useragent=USERAGENT, verify_fingerprint=None, + auth_cert=None): if username and not password: password = utils.get_password(username, url) @@ -216,6 +217,7 @@ class DavSession(object): 'verify': prepare_verify(verify), 'auth': prepare_auth(auth, username, password), 'verify_fingerprint': verify_fingerprint, + 'cert': prepare_client_cert(auth_cert), } self.useragent = useragent self.url = url.rstrip('/') + '/' @@ -270,12 +272,13 @@ class DavStorage(Storage): def __init__(self, url, username='', password='', verify=True, auth=None, useragent=USERAGENT, unsafe_href_chars='@', - verify_fingerprint=None, **kwargs): + verify_fingerprint=None, auth_cert=None, **kwargs): super(DavStorage, self).__init__(**kwargs) url = url.rstrip('/') + '/' self.session = DavSession(url, username, password, verify, auth, - useragent, verify_fingerprint) + useragent, verify_fingerprint, + auth_cert) self.unsafe_href_chars = unsafe_href_chars # defined for _repr_attributes @@ -286,7 +289,7 @@ class DavStorage(Storage): def _get_session(cls, **kwargs): discover_args, _ = utils.split_dict(kwargs, lambda key: key in ( 'url', 'username', 'password', 'verify', 'auth', 'useragent', - 'verify_fingerprint', + 'verify_fingerprint', 'auth_cert', )) return DavSession(**discover_args) diff --git a/vdirsyncer/storage/http.py b/vdirsyncer/storage/http.py index bcad99c..a5832c5 100644 --- a/vdirsyncer/storage/http.py +++ b/vdirsyncer/storage/http.py @@ -37,6 +37,14 @@ def prepare_verify(verify): return verify +def prepare_client_cert(cert): + if isinstance(cert, (text_type, bytes)): + cert = expand_path(cert) + elif isinstance(cert, list): + cert = map(prepare_client_cert, cert) + return cert + + HTTP_STORAGE_PARAMETERS = ''' :param username: Username for authentication. :param password: Password for authentication. @@ -49,6 +57,8 @@ HTTP_STORAGE_PARAMETERS = ''' :param auth: Optional. Either ``basic``, ``digest`` or ``guess``. Default ``guess``. If you know yours, consider setting it explicitly for performance. + :param auth_cert: Optional. Either a path to a certificate with a client + certificate and the key or a list of paths to the files with them. :param useragent: Default ``vdirsyncer``. ''' @@ -82,7 +92,8 @@ class HttpStorage(Storage): _items = None def __init__(self, url, username='', password='', verify=True, auth=None, - useragent=USERAGENT, verify_fingerprint=None, **kwargs): + useragent=USERAGENT, verify_fingerprint=None, auth_cert=None, + **kwargs): super(HttpStorage, self).__init__(**kwargs) if username and not password: @@ -92,7 +103,8 @@ class HttpStorage(Storage): 'verify': prepare_verify(verify), 'verify_fingerprint': verify_fingerprint, 'auth': prepare_auth(auth, username, password), - 'latin1_fallback': False + 'cert': prepare_client_cert(auth_cert), + 'latin1_fallback': False, } self.username, self.password = username, password self.useragent = useragent