diff --git a/SABnzbd.py b/SABnzbd.py index 280b01f..93f72b0 100755 --- a/SABnzbd.py +++ b/SABnzbd.py @@ -1353,10 +1353,11 @@ def main(): logging.info("pygtk2 not found. No SysTray.") print_modules() - - from sabnzbd.utils.sslinfo import sslversion, sslprotocols - logging.info("SSL version %s", sslversion()) - logging.info("SSL supported protocols %s", str(sslprotocols())) + + import sabnzbd.utils.sslinfo + logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version()) + logging.info("SSL potentially supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_potential())) + logging.info("SSL actually supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols())) cherrylogtoscreen = False sabnzbd.WEBLOGFILE = None diff --git a/interfaces/Config/templates/config_server.tmpl b/interfaces/Config/templates/config_server.tmpl index d073d83..ba5c5fb 100644 --- a/interfaces/Config/templates/config_server.tmpl +++ b/interfaces/Config/templates/config_server.tmpl @@ -62,10 +62,24 @@ $T('srv-explain-ssl_type') @@ -174,10 +188,24 @@ $T('srv-explain-ssl_type') diff --git a/sabnzbd/downloader.py b/sabnzbd/downloader.py index e567b53..84c5d13 100644 --- a/sabnzbd/downloader.py +++ b/sabnzbd/downloader.py @@ -31,6 +31,7 @@ import sys import sabnzbd from sabnzbd.decorators import synchronized, synchronized_CV, CV from sabnzbd.decoder import Decoder +from sabnzbd.utils.sslinfo import ssl_protocols from sabnzbd.newswrapper import NewsWrapper, request_server_info import sabnzbd.growler as growler from sabnzbd.constants import * @@ -60,6 +61,12 @@ class Server(object): def __init__(self, id, displayname, host, port, timeout, threads, priority, ssl, ssl_type, send_group, username=None, password=None, optional=False, retention=0, categories=None): + + # If no ssl is protocol set, used highest available one + protocols = ssl_protocols() + if ssl and protocols and ssl_type not in protocols: + ssl_type = protocols[0] + self.id = id self.newid = None self.restart = False diff --git a/sabnzbd/interface.py b/sabnzbd/interface.py index ea1d283..cf92846 100644 --- a/sabnzbd/interface.py +++ b/sabnzbd/interface.py @@ -48,6 +48,7 @@ from sabnzbd.downloader import Downloader from sabnzbd.nzbqueue import NzbQueue import sabnzbd.wizard from sabnzbd.utils.servertests import test_nntp_server_dict +from sabnzbd.utils.sslinfo import ssl_protocols from sabnzbd.constants import \ REC_RAR_VERSION, NORMAL_PRIORITY, PNFO_NZO_ID_FIELD, PNFO_REPAIR_FIELD, \ @@ -1659,6 +1660,7 @@ class ConfigServer(object): if sabnzbd.newswrapper.HAVE_SSL: conf['have_ssl'] = 1 + conf['ssl_protocols'] = ssl_protocols() else: conf['have_ssl'] = 0 diff --git a/sabnzbd/newswrapper.py b/sabnzbd/newswrapper.py index e787e65..9a03a2a 100644 --- a/sabnzbd/newswrapper.py +++ b/sabnzbd/newswrapper.py @@ -30,6 +30,7 @@ import re import sabnzbd from sabnzbd.constants import * import sabnzbd.cfg +from sabnzbd.utils.sslinfo import ssl_method try: from OpenSSL import SSL @@ -51,6 +52,8 @@ except ImportError: def __str__(self): return repr(self.parameter) + + import threading _RLock = threading.RLock del threading @@ -100,7 +103,7 @@ def GetServerParms(host, port): port = 119 opt = sabnzbd.cfg.ipv6_servers() ''' ... with the following meaning for 'opt': - Control the use of IPv6 Usenet server addresses. Meaning: + Control the use of IPv6 Usenet server addresses. Meaning: 0 = don't use 1 = use when available and reachable (DEFAULT) 2 = force usage (when SABnzbd's detection fails) @@ -164,16 +167,6 @@ def con(sock, host, port, sslenabled, write_fds, nntp): except _ssl.Error, e: nntp.error(e) -try: - _SSL_TYPES = { - 't1': _ssl.TLSv1_METHOD, - 'v2': _ssl.SSLv2_METHOD, - 'v3': _ssl.SSLv3_METHOD, - 'v23': _ssl.SSLv23_METHOD - } -except: - _SSL_TYPES = {} - def probablyipv4(ip): if ip.count('.') == 3 and re.sub('[0123456789.]', '', ip) == '': @@ -214,7 +207,7 @@ class NNTP(object): af = socket.AF_INET6 if sslenabled and _ssl: - ctx = _ssl.Context(_SSL_TYPES.get(ssl_type, _ssl.TLSv1_METHOD)) + ctx = _ssl.Context(ssl_method(ssl_type)) self.sock = SSLConnection(ctx, socket.socket(af, socktype, proto)) elif sslenabled and not _ssl: logging.error(T('Error importing OpenSSL module. Connecting with NON-SSL')) @@ -470,3 +463,4 @@ class SSLConnection(object): return apply(self._ssl_conn.%s, args) finally: self._lock.release()\n""" % (f, f) + diff --git a/sabnzbd/skintext.py b/sabnzbd/skintext.py index 8fd34d9..fd3fd10 100644 --- a/sabnzbd/skintext.py +++ b/sabnzbd/skintext.py @@ -542,7 +542,7 @@ SKIN_TEXT = { 'srv-optional' : TT('Optional'), #: Server optional tickbox 'srv-enable' : TT('Enable'), #: Enable server tickbox 'srv-ssl_type' : TT('SSL type'), - 'srv-explain-ssl_type' : TT('Use TLS1 unless your provider requires otherwise!'), + 'srv-explain-ssl_type' : TT('Use the top value, unless your Usenet provider requires otherwise!'), 'button-addServer' : TT('Add Server'), #: Button: Add server 'button-delServer' : TT('Remove Server'), #: Button: Remove server 'button-testServer' : TT('Test Server'), #: Button: Test server @@ -868,7 +868,7 @@ SKIN_TEXT = { 'Plush-idle' : TT('IDLE'), 'Plush-downloads' : TT('Downloads'), 'Plush-tab-repair' : TT('Queue repair'), - + #smpl skin 'smpl-purgehist' : TT('Delete Completed'), 'smpl-purgefailhistOK?' : TT('Delete the all failed items from the history?'), diff --git a/sabnzbd/utils/sslinfo.py b/sabnzbd/utils/sslinfo.py index 9af5ac5..6dbf359 100644 --- a/sabnzbd/utils/sslinfo.py +++ b/sabnzbd/utils/sslinfo.py @@ -1,36 +1,72 @@ -import ssl -import logging -def sslversion(): - try: - return ssl.OPENSSL_VERSION - except: - logging.info("ssl.OPENSSL_VERSION not defined") - return None - -def sslversioninfo(): +_ALL_PROTOCOLS = ('t12', 't11', 't1', 'v23', 'v3', 'v2') +_SSL_PROTOCOLS = {} + +def ssl_potential(): + ''' Return a list of potentially supported SSL protocols''' try: - return ssl.OPENSSL_VERSION_INFO - except: - logging.info("ssl.OPENSSL_VERSION_INFO not defined") - return None + import ssl + except ImportError: + return [] + return [p[9:] for p in dir(ssl) if p.startswith('PROTOCOL_')] -def sslprotocols(): - protocollist = [] +try: + from OpenSSL import SSL + + _potential = ssl_potential() try: - for i in dir(ssl): - if i.find('PROTOCOL_') == 0: - protocollist.append(i[9:]) - return protocollist - except: - return None + if 'TLSv1_2' in _potential: + _SSL_PROTOCOLS['t12'] = SSL.TLSv1_2_METHOD + except AttributeError: + pass + try: + if 'TLSv1_1' in _potential: + _SSL_PROTOCOLS['t11'] = SSL.TLSv1_1_METHOD + except AttributeError: + pass + try: + if 'TLSv1' in _potential: + _SSL_PROTOCOLS['t1'] = SSL.TLSv1_METHOD + except AttributeError: + pass + try: + if 'SSLv23' in _potential: + _SSL_PROTOCOLS['v23'] = SSL.SSLv23_METHOD + except AttributeError: + pass + try: + if 'SSLv3' in _potential: + _SSL_PROTOCOLS['v3'] = SSL.SSLv3_METHOD + except AttributeError: + pass + try: + if 'SSLv2' in _potential: + _SSL_PROTOCOLS['v2'] = SSL.SSLv2_METHOD + except AttributeError: + pass +except ImportError: + SSL = None -if __name__ == '__main__': +def ssl_method(method): + ''' Translate SSL acronym to a method value ''' + if method in _SSL_PROTOCOLS: + return _SSL_PROTOCOLS[method] + else: + return _SSL_PROTOCOLS[0] - logger = logging.getLogger('') - logger.setLevel(logging.INFO) +def ssl_protocols(): + ''' Return acronyms for SSL protocols, highest quality first ''' + return [p for p in _ALL_PROTOCOLS if p in _SSL_PROTOCOLS] - print sslversion() - print sslversioninfo() - print sslprotocols() +def ssl_version(): + if SSL: + return SSL.SSLeay_version(SSL.SSLEAY_VERSION) + else: + return None + + +if __name__ == '__main__': + print 'SSL version: %s' % ssl_version() + print 'Potentials: %s' % ssl_potential() + print 'Actuals: %s' % ssl_protocols()