From 482cb68bd67c32990661bafb57e317bf5b13dc63 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sat, 24 Aug 2013 09:51:58 +0530 Subject: [PATCH] Fixed up some bugs/bad imports, added lib/certgen --- Headphones.py | 4 +-- headphones/__init__.py | 4 +-- headphones/helpers.py | 2 ++ headphones/webstart.py | 33 +++++++++++++++-- lib/certgen.py | 82 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 lib/certgen.py diff --git a/Headphones.py b/Headphones.py index 1ced1fba..19cc82dd 100755 --- a/Headphones.py +++ b/Headphones.py @@ -142,7 +142,7 @@ def main(): # Force the http port if neccessary if args.port: http_port = args.port - logger.info('Starting Headphones on forced port: %i' % http_port) + logger.info('Using forced port: %i' % http_port) else: http_port = int(headphones.HTTP_PORT) @@ -159,8 +159,6 @@ def main(): 'http_password': headphones.HTTP_PASSWORD, }) - logger.info('Starting Headphones on port: %i' % http_port) - if headphones.LAUNCH_BROWSER and not args.nolaunch: headphones.launch_browser(headphones.HTTP_HOST, http_port, headphones.HTTP_ROOT) diff --git a/headphones/__init__.py b/headphones/__init__.py index 23485334..648382a8 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -350,8 +350,8 @@ def initialize(): HTTP_ROOT = check_setting_str(CFG, 'General', 'http_root', '/') HTTP_PROXY = bool(check_setting_int(CFG, 'General', 'http_proxy', 0)) ENABLE_HTTPS = bool(check_setting_int(CFG, 'General', 'enable_https', 0)) - HTTPS_CERT = check_setting_str(CFG, 'General', 'https_cert', 'server.crt') - HTTPS_KEY = check_setting_str(CFG, 'General', 'https_key', 'server.key') + HTTPS_CERT = check_setting_str(CFG, 'General', 'https_cert', os.path.join(DATA_DIR, 'server.crt')) + HTTPS_KEY = check_setting_str(CFG, 'General', 'https_key', os.path.join(DATA_DIR, 'server.key')) LAUNCH_BROWSER = bool(check_setting_int(CFG, 'General', 'launch_browser', 1)) API_ENABLED = bool(check_setting_int(CFG, 'General', 'api_enabled', 0)) API_KEY = check_setting_str(CFG, 'General', 'api_key', '') diff --git a/headphones/helpers.py b/headphones/helpers.py index 97b7abbf..79ac9b85 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -351,6 +351,8 @@ def create_https_certificates(ssl_cert, ssl_key): Stolen from SickBeard (http://github.com/midgetspy/Sick-Beard): Create self-signed HTTPS certificares and store in paths 'ssl_cert' and 'ssl_key' """ + from headphones import logger + try: from OpenSSL import crypto #@UnresolvedImport from lib.certgen import createKeyPair, createCertRequest, createCertificate, TYPE_RSA, serial #@UnresolvedImport diff --git a/headphones/webstart.py b/headphones/webstart.py index b67c801f..a30d3694 100644 --- a/headphones/webstart.py +++ b/headphones/webstart.py @@ -20,12 +20,31 @@ import cherrypy import headphones +from headphones import logger from headphones.webserve import WebInterface +from headphones.helpers import create_https_certificates def initialize(options={}): + #HTTPS stuff stolen from sickbeard + enable_https = options['enable_https'] + https_cert = options['https_cert'] + https_key = options['https_key'] - cherrypy.config.update({ + if enable_https: + # If either the HTTPS certificate or key do not exist, make some self-signed ones. + if not (https_cert and os.path.exists(https_cert)) or not (https_key and os.path.exists(https_key)): + if not create_https_certificates(https_cert, https_key): + logger.warn(u"Unable to create cert/key files, disabling HTTPS") + headphones.ENABLE_HTTPS = False + enable_https = False + + if not (os.path.exists(https_cert) and os.path.exists(https_key)): + logger.warn(u"Disabled HTTPS because of missing CERT and KEY files") + headphones.ENABLE_HTTPS = False + enable_https = False + + options_dict = { 'log.screen': False, 'server.thread_pool': 10, 'server.socket_port': options['http_port'], @@ -34,7 +53,17 @@ def initialize(options={}): 'tools.encode.on' : True, 'tools.encode.encoding' : 'utf-8', 'tools.decode.on' : True, - }) + } + + if enable_https: + options_dict['server.ssl_certificate'] = https_cert + options_dict['server.ssl_private_key'] = https_key + protocol = "https" + else: + protocol = "http" + + logger.info(u"Starting Headphones on " + protocol + "://" + str(options['http_host']) + ":" + str(options['http_port']) + "/") + cherrypy.config.update(options_dict) conf = { '/': { diff --git a/lib/certgen.py b/lib/certgen.py new file mode 100644 index 00000000..1b941161 --- /dev/null +++ b/lib/certgen.py @@ -0,0 +1,82 @@ +# -*- coding: latin-1 -*- +# +# Copyright (C) Martin Sjögren and AB Strakt 2001, All rights reserved +# Copyright (C) Jean-Paul Calderone 2008, All rights reserved +# This file is licenced under the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1 or later (aka LGPL v2.1) +# Please see LGPL2.1.txt for more information +""" +Certificate generation module. +""" + +from OpenSSL import crypto +import time + +TYPE_RSA = crypto.TYPE_RSA +TYPE_DSA = crypto.TYPE_DSA + +serial = int(time.time()) + + +def createKeyPair(type, bits): + """ + Create a public/private key pair. + + Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA + bits - Number of bits to use in the key + Returns: The public/private key pair in a PKey object + """ + pkey = crypto.PKey() + pkey.generate_key(type, bits) + return pkey + +def createCertRequest(pkey, digest="md5", **name): + """ + Create a certificate request. + + Arguments: pkey - The key to associate with the request + digest - Digestion method to use for signing, default is md5 + **name - The name of the subject of the request, possible + arguments are: + C - Country name + ST - State or province name + L - Locality name + O - Organization name + OU - Organizational unit name + CN - Common name + emailAddress - E-mail address + Returns: The certificate request in an X509Req object + """ + req = crypto.X509Req() + subj = req.get_subject() + + for (key,value) in name.items(): + setattr(subj, key, value) + + req.set_pubkey(pkey) + req.sign(pkey, digest) + return req + +def createCertificate(req, (issuerCert, issuerKey), serial, (notBefore, notAfter), digest="md5"): + """ + Generate a certificate given a certificate request. + + Arguments: req - Certificate reqeust to use + issuerCert - The certificate of the issuer + issuerKey - The private key of the issuer + serial - Serial number for the certificate + notBefore - Timestamp (relative to now) when the certificate + starts being valid + notAfter - Timestamp (relative to now) when the certificate + stops being valid + digest - Digest method to use for signing, default is md5 + Returns: The signed certificate in an X509 object + """ + cert = crypto.X509() + cert.set_serial_number(serial) + cert.gmtime_adj_notBefore(notBefore) + cert.gmtime_adj_notAfter(notAfter) + cert.set_issuer(issuerCert.get_subject()) + cert.set_subject(req.get_subject()) + cert.set_pubkey(req.get_pubkey()) + cert.sign(issuerKey, digest) + return cert