Version = '$Header$';
+
+from enigma import eConsoleAppContainer, eTPM
from Plugins.Plugin import PluginDescriptor
+
from Components.config import config, ConfigBoolean, ConfigSubsection, ConfigInteger, ConfigYesNo, ConfigText
from Components.Network import iNetwork
from Screens.MessageBox import MessageBox
from WebIfConfig import WebIfConfigScreen
from WebChilds.Toplevel import getToplevel
+from Tools.HardwareInfo import HardwareInfo
+
+from Tools.Directories import copyfile, resolveFilename, SCOPE_PLUGINS, SCOPE_CONFIG
from twisted.internet import reactor, ssl
from twisted.web import server, http, util, static, resource
from socket import gethostname as socket_gethostname
from OpenSSL import SSL
-from __init__ import _, __version__
+from os.path import isfile as os_isfile
+from __init__ import _, __version__, decrypt_block
+from webif import get_random, validate_certificate
+tpm = eTPM()
+rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?']
+hw = HardwareInfo()
#CONFIG INIT
#init the config
#===============================================================================
class Closer:
counter = 0
- def __init__(self, session, callback=None):
+ def __init__(self, session, callback=None, l2k=None):
self.callback = callback
self.session = session
-
+ self.l2k = l2k
#===============================================================================
# Closes all running Instances of the Webinterface
#===============================================================================
for d in running_defered:
print "[Webinterface] stopping interface on ", d.interface, " with port", d.port
x = d.stopListening()
+
try:
x.addCallback(self.isDown)
self.counter += 1
running_defered = []
if self.counter < 1:
if self.callback is not None:
- self.callback(self.session)
+ self.callback(self.session, self.l2k)
#===============================================================================
# #Is it already down?
self.counter -= 1
if self.counter < 1:
if self.callback is not None:
- self.callback(self.session)
+ self.callback(self.session, self.l2k)
+def checkCertificates():
+ print "[WebInterface] checking for SSL Certificates"
+ srvcert = '%sserver.pem' %resolveFilename(SCOPE_CONFIG)
+ cacert = '%scacert.pem' %resolveFilename(SCOPE_CONFIG)
+
+ # Check whether there are regular certificates, if not copy the default ones over
+ if not os_isfile(srvcert) or not os_isfile(cacert):
+ return False
+
+ else:
+ return True
+
+def installCertificates(session, callback = None, l2k = None):
+ print "[WebInterface] Installing SSL Certificates to %s" %resolveFilename(SCOPE_CONFIG)
+
+ srvcert = '%sserver.pem' %resolveFilename(SCOPE_CONFIG)
+ cacert = '%scacert.pem' %resolveFilename(SCOPE_CONFIG)
+ scope_webif = '%sExtensions/WebInterface/' %resolveFilename(SCOPE_PLUGINS)
+
+ source = '%setc/server.pem' %scope_webif
+ target = srvcert
+ ret = copyfile(source, target)
+
+ if ret == 0:
+ source = '%setc/cacert.pem' %scope_webif
+ target = cacert
+ ret = copyfile(source, target)
+
+ if ret == 0 and callback != None:
+ callback(session, l2k)
+
+ if ret < 0:
+ config.plugins.Webinterface.https.enabled.value = False
+ config.plugins.Webinterface.https.enabled.save()
+
+ # Start without https
+ callback(session, l2k)
+
+ #Inform the user
+ session.open(MessageBox, "Couldn't install SSL-Certifactes for https access\nHttps access is now disabled!", MessageBox.TYPE_ERROR)
+
#===============================================================================
# restart the Webinterface for all configured Interfaces
#===============================================================================
-def restartWebserver(session):
+def restartWebserver(session, l2k):
try:
del session.mediaplayer
del session.messageboxanswer
global running_defered
if len(running_defered) > 0:
- Closer(session, startWebserver).stop()
+ Closer(session, startWebserver, l2k).stop()
else:
- startWebserver(session)
-
+ startWebserver(session, l2k)
+
#===============================================================================
# start the Webinterface for all configured Interfaces
#===============================================================================
-def startWebserver(session):
- print "[Webinterface] startWebserver - iNetwork.ifaces: %s" %(iNetwork.ifaces)
-
+def startWebserver(session, l2k):
global running_defered
global toplevel
print "[Webinterface] is disabled!"
else:
- for adaptername in iNetwork.ifaces:
- ip = '.'.join("%d" % d for d in iNetwork.ifaces[adaptername]['ip'])
-
- #Network.py sets the IP of inactive Adapters to 0.0.0.0, we do not want to listen on 0.0.0.0
- if ip != '0.0.0.0':
- #HTTP
- if config.plugins.Webinterface.http.enabled.value is True:
- ret = startServerInstance(session, ip, config.plugins.Webinterface.http.port.value, config.plugins.Webinterface.http.auth.value)
- if ret == False:
- errors = "%s%s:%i\n" %(errors, ip, config.plugins.Webinterface.http.port.value)
- #HTTPS
- if config.plugins.Webinterface.https.enabled.value is True:
- ret = startServerInstance(session, ip, config.plugins.Webinterface.https.port.value, config.plugins.Webinterface.https.auth.value, True)
- if ret == False:
- errors = "%s%s:%i\n" %(errors, ip, config.plugins.Webinterface.https.port.value)
-
- #LOCAL HTTP Connections (Streamproxy)
- ret = startServerInstance(session, '127.0.0.1', 80, config.plugins.Webinterface.streamauth.value)
- if ret == False:
- errors = "%s%s:%i\n" %(errors, '127.0.0.1', 80)
-
- if errors != "":
- session.open(MessageBox, "Webinterface - Couldn't listen on:\n %s" % (errors), MessageBox.TYPE_ERROR)
+ # IF SSL is enabled we need to check for the certs first
+ # If they're not there we'll exit via return here
+ # and get called after Certificates are installed properly
+ if config.plugins.Webinterface.https.enabled.value:
+ if not checkCertificates():
+ print "[Webinterface] Installing Webserver Certificates for SSL encryption"
+ installCertificates(session, startWebserver, l2k)
+ return
+ # Listen on all Interfaces
+ ip = "0.0.0.0"
+ #HTTP
+ if config.plugins.Webinterface.http.enabled.value is True:
+ ret = startServerInstance(session, ip, config.plugins.Webinterface.http.port.value, config.plugins.Webinterface.http.auth.value, l2k)
+ if ret == False:
+ errors = "%s%s:%i\n" %(errors, ip, config.plugins.Webinterface.http.port.value)
+ else:
+ registerBonjourService('http', config.plugins.Webinterface.http.port.value)
+
+ #Streaming requires listening on 127.0.0.1:80 no matter what, ensure it its available
+ if config.plugins.Webinterface.http.port.value != 80 or not config.plugins.Webinterface.http.enabled.value:
+ #LOCAL HTTP Connections (Streamproxy)
+ ret = startServerInstance(session, '127.0.0.1', 80, config.plugins.Webinterface.http.auth.value, l2k)
+ if ret == False:
+ errors = "%s%s:%i\n" %(errors, '127.0.0.1', 80)
+
+ if errors != "":
+ session.open(MessageBox, "Webinterface - Couldn't listen on:\n %s" % (errors), type=MessageBox.TYPE_ERROR, timeout=30)
+
+ #HTTPS
+ if config.plugins.Webinterface.https.enabled.value is True:
+ ret = startServerInstance(session, ip, config.plugins.Webinterface.https.port.value, config.plugins.Webinterface.https.auth.value, l2k, True)
+ if ret == False:
+ errors = "%s%s:%i\n" %(errors, ip, config.plugins.Webinterface.https.port.value)
+ else:
+ registerBonjourService('https', config.plugins.Webinterface.https.port.value)
#===============================================================================
# stop the Webinterface for all configured Interfaces
# Starts an Instance of the Webinterface
# on given ipaddress, port, w/o auth, w/o ssl
#===============================================================================
-def startServerInstance(session, ipaddress, port, useauth=False, usessl=False):
- try:
- if useauth:
-# HTTPAuthResource handles the authentication for every Resource you want it to
- root = HTTPAuthResource(toplevel, "Enigma2 WebInterface")
- site = server.Site(root)
- else:
- site = server.Site(toplevel)
+def startServerInstance(session, ipaddress, port, useauth=False, l2k=None, usessl=False):
+ if hw.get_device_name().lower() != "dm7025":
+ l3k = None
+ l3c = tpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT)
+
+ if l3c is None:
+ return False
+
+ l3k = validate_certificate(l3c, l2k)
+ if l3k is None:
+ return False
+
+ random = get_random()
+ if random is None:
+ return False
- if usessl:
- ctx = ssl.DefaultOpenSSLContextFactory('/etc/enigma2/server.pem', '/etc/enigma2/cacert.pem', sslmethod=SSL.SSLv23_METHOD)
- d = reactor.listenSSL(port, site, ctx, interface=ipaddress)
+ value = tpm.challenge(random)
+ result = decrypt_block(value, l3k)
+
+ if result is None:
+ return False
else:
- d = reactor.listenTCP(port, site, interface=ipaddress)
- running_defered.append(d)
- print "[Webinterface] started on %s:%i" % (ipaddress, port), "auth=", useauth, "ssl=", usessl
- return True
+ if result [80:88] != random:
+ return False
+
+ if useauth:
+# HTTPAuthResource handles the authentication for every Resource you want it to
+ root = HTTPAuthResource(toplevel, "Enigma2 WebInterface")
+ site = server.Site(root)
+ else:
+ site = server.Site(toplevel)
+
+ if usessl:
+
+ ctx = ssl.DefaultOpenSSLContextFactory('/etc/enigma2/server.pem', '/etc/enigma2/cacert.pem', sslmethod=SSL.SSLv23_METHOD)
+ d = reactor.listenSSL(port, site, ctx, interface=ipaddress)
+ else:
+ d = reactor.listenTCP(port, site, interface=ipaddress)
+ running_defered.append(d)
+ print "[Webinterface] started on %s:%i auth=%s ssl=%s" % (ipaddress, port, useauth, usessl)
+ return True
- except Exception, e:
- print "[Webinterface] starting FAILED on %s:%i!" % (ipaddress, port), e
- return False
+ #except Exception, e:
+ #print "[Webinterface] starting FAILED on %s:%i!" % (ipaddress, port), e
+ #return False
#===============================================================================
# HTTPAuthResource
# Handles HTTP Authorization for a given Resource
self.realm = realm
self.authorized = False
self.tries = 0
- self.unauthorizedResource = UnauthorizedResource(self.realm)
+ self.unauthorizedResource = UnauthorizedResource(self.realm)
def unautorized(self, request):
request.setResponseCode(http.UNAUTHORIZED)
return self.unauthorizedResource
- def isAuthenticated(self, request):
+ def isAuthenticated(self, request):
+ host = request.getHost().host
+ #If streamauth is disabled allow all acces from localhost
+ if not config.plugins.Webinterface.streamauth.value:
+ if( host == "127.0.0.1" or host == "localhost" ):
+ print "[WebInterface.plugin.isAuthenticated] Streaming auth is disabled bypassing authcheck because host is '%s'" %host
+ return True
+
# get the Session from the Request
sessionNs = request.getSession().sessionNamespaces
# if the auth-information has not yet been stored to the session
if not sessionNs.has_key('authenticated'):
- sessionNs['authenticated'] = check_passwd(request.getUser(), request.getPassword())
+ if request.getUser() != '':
+ sessionNs['authenticated'] = check_passwd(request.getUser(), request.getPassword())
+ else:
+ sessionNs['authenticated'] = False
#if the auth-information already is in the session
else:
return self.resource.render(request)
else:
+ print "[Webinterface.HTTPAuthResource.render] !!! unauthorized !!!"
return self.unautorized(request).render(request)
#===============================================================================
return self.resource.getChildWithDefault(path, request)
else:
+ print "[Webinterface.HTTPAuthResource.render] !!! unauthorized !!!"
return self.unautorized(request)
#===============================================================================
def render(self, request):
return self.errorpage.render(request)
-# Password verfication stuff
-
-from hashlib import md5 as md5_new
-from crypt import crypt
-
-#===============================================================================
-# getpwnam
-#
-# Get a password database entry for the given user name
-# Example from the Python Library Reference.
-#===============================================================================
-def getpwnam(name, pwfile=None):
- if not pwfile:
- pwfile = '/etc/passwd'
-
- f = open(pwfile)
- while 1:
- line = f.readline()
- if not line:
- f.close()
- raise KeyError, name
- entry = tuple(line.strip().split(':', 6))
- if entry[0] == name:
- f.close()
- return entry
-#===============================================================================
-# passcrypt
-#
-# Encrypt a password
-#===============================================================================
-def passcrypt(passwd, salt=None, method='des', magic='$1$'):
- """Encrypt a string according to rules in crypt(3)."""
- if method.lower() == 'des':
- return crypt(passwd, salt)
- elif method.lower() == 'md5':
- return passcrypt_md5(passwd, salt, magic)
- elif method.lower() == 'clear':
- return passwd
-#===============================================================================
-# check_passwd
-#
-# Checks username and Password against a given Unix Password file
-# The default path is '/etc/passwd'
-#===============================================================================
-def check_passwd(name, passwd, pwfile='/etc/passwd'):
- """Validate given user, passwd pair against password database."""
+# Password verfication stuff
+from crypt import crypt
+from pwd import getpwnam
+from spwd import getspnam
- if not pwfile or type(pwfile) == type(''):
- getuser = lambda x, pwfile = pwfile: getpwnam(x, pwfile)[1]
- else:
- getuser = pwfile.get_passwd
+def check_passwd(name, passwd):
+ cryptedpass = None
try:
- enc_passwd = getuser(name)
- except (KeyError, IOError):
- print "!!! EXCEPT"
- return False
- if not enc_passwd:
- "!!! NOT ENC_PASSWD"
+ cryptedpass = getpwnam(name)[1]
+ except:
return False
- elif len(enc_passwd) >= 3 and enc_passwd[:3] == '$1$':
- salt = enc_passwd[3:enc_passwd.find('$', 3)]
- return enc_passwd == passcrypt(passwd, salt, 'md5')
- else:
- return enc_passwd == passcrypt(passwd, enc_passwd[:2])
-
-def _to64(v, n):
- DES_SALT = list('./0123456789' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz')
- r = ''
- while (n - 1 >= 0):
- r = r + DES_SALT[v & 0x3F]
- v = v >> 6
- n = n - 1
- return r
-
-#===============================================================================
-# passcrypt_md5
-# Encrypt a password via md5
-#===============================================================================
-def passcrypt_md5(passwd, salt=None, magic='$1$'):
- if not salt:
- pass
- elif salt[:len(magic)] == magic:
- # remove magic from salt if present
- salt = salt[len(magic):]
-
- # salt only goes up to first '$'
- salt = salt.split('$')[0]
- # limit length of salt to 8
- salt = salt[:8]
-
- ctx = md5_new(passwd)
- ctx.update(magic)
- ctx.update(salt)
-
- ctx1 = md5_new(passwd)
- ctx1.update(salt)
- ctx1.update(passwd)
-
- final = ctx1.digest()
-
- for i in range(len(passwd), 0 , -16):
- if i > 16:
- ctx.update(final)
- else:
- ctx.update(final[:i])
-
- i = len(passwd)
- while i:
- if i & 1:
- ctx.update('\0')
- else:
- ctx.update(passwd[:1])
- i = i >> 1
- final = ctx.digest()
-
- for i in range(1000):
- ctx1 = md5_new()
- if i & 1:
- ctx1.update(passwd)
- else:
- ctx1.update(final)
- if i % 3: ctx1.update(salt)
- if i % 7: ctx1.update(passwd)
- if i & 1:
- ctx1.update(final)
- else:
- ctx1.update(passwd)
- final = ctx1.digest()
-
- rv = magic + salt + '$'
- final = map(ord, final)
- l = (final[0] << 16) + (final[6] << 8) + final[12]
- rv = rv + _to64(l, 4)
- l = (final[1] << 16) + (final[7] << 8) + final[13]
- rv = rv + _to64(l, 4)
- l = (final[2] << 16) + (final[8] << 8) + final[14]
- rv = rv + _to64(l, 4)
- l = (final[3] << 16) + (final[9] << 8) + final[15]
- rv = rv + _to64(l, 4)
- l = (final[4] << 16) + (final[10] << 8) + final[5]
- rv = rv + _to64(l, 4)
- l = final[11]
- rv = rv + _to64(l, 2)
-
- return rv
-
-#===============================================================================
-# Creates an SSL Context to use with twisted.web
-#===============================================================================
-def makeSSLContext(myKey, trustedCA):
- '''Returns an ssl Context Object
- @param myKey a pem formated key and certifcate with for my current host
- the other end of this connection must have the cert from the CA
- that signed this key
- @param trustedCA a pem formated certificat from a CA you trust
- you will only allow connections from clients signed by this CA
- and you will only allow connections to a server signed by this CA
- '''
-
- # our goal in here is to make a SSLContext object to pass to connectSSL
- # or listenSSL
-
- # Why these functioins... Not sure...
- fd = open(myKey, 'r')
- ss = fd.read()
- theCert = ssl.PrivateCertificate.loadPEM(ss)
- fd.close()
- fd = open(trustedCA, 'r')
- theCA = ssl.Certificate.loadPEM(fd.read())
- fd.close()
- #ctx = theCert.options(theCA)
- ctx = theCert.options()
-
- # Now the options you can set look like Standard OpenSSL Library options
-
- # The SSL protocol to use, one of SSLv23_METHOD, SSLv2_METHOD,
- # SSLv3_METHOD, TLSv1_METHOD. Defaults to TLSv1_METHOD.
- ctx.method = ssl.SSL.TLSv1_METHOD
-
- # If True, verify certificates received from the peer and fail
- # the handshake if verification fails. Otherwise, allow anonymous
- # sessions and sessions with certificates which fail validation.
- ctx.verify = True
-
- # Depth in certificate chain down to which to verify.
- ctx.verifyDepth = 1
-
- # If True, do not allow anonymous sessions.
- ctx.requireCertification = True
-
- # If True, do not re-verify the certificate on session resumption.
- ctx.verifyOnce = True
-
- # If True, generate a new key whenever ephemeral DH parameters are used
- # to prevent small subgroup attacks.
- ctx.enableSingleUseKeys = True
-
- # If True, set a session ID on each context. This allows a shortened
- # handshake to be used when a known client reconnects.
- ctx.enableSessions = True
-
- # If True, enable various non-spec protocol fixes for broken
- # SSL implementations.
- ctx.fixBrokenPeers = False
-
- return ctx
+
+ if cryptedpass:
+ #shadowed or not, that's the questions here
+ if cryptedpass == 'x' or cryptedpass == '*':
+ try:
+ cryptedpass = getspnam(name)[1]
+ except:
+ return False
+
+ return crypt(passwd, cryptedpass) == cryptedpass
+ return False
global_session = None
global global_session
global_session = session
+
+def registerBonjourService(protocol, port):
+ try:
+ from Plugins.Extensions.Bonjour.Bonjour import bonjour
+
+ service = bonjour.buildService(protocol, port)
+ bonjour.registerService(service, True)
+ print "[WebInterface.registerBonjourService] Service for protocol '%s' with port '%i' registered!" %(protocol, port)
+ return True
+
+ except ImportError, e:
+ print "[WebInterface.registerBonjourService] %s" %e
+ return False
+
+def unregisterBonjourService(protocol):
+ try:
+ from Plugins.Extensions.Bonjour.Bonjour import bonjour
+
+ bonjour.unregisterService(protocol)
+ print "[WebInterface.unregisterBonjourService] Service for protocol '%s' unregistered!" %(protocol)
+ return True
+
+ except ImportError, e:
+ print "[WebInterface.unregisterBonjourService] %s" %e
+ return False
+
+def checkBonjour():
+ if ( not config.plugins.Webinterface.http.enabled.value ) or ( not config.plugins.Webinterface.enabled.value ):
+ unregisterBonjourService('http')
+ if ( not config.plugins.Webinterface.https.enabled.value ) or ( not config.plugins.Webinterface.enabled.value ):
+ unregisterBonjourService('https')
+
#===============================================================================
# networkstart
# Actions to take place after Network is up (startup the Webserver)
#===============================================================================
def networkstart(reason, **kwargs):
- if reason is True:
- startWebserver(global_session)
-
- elif reason is False:
- stopWebserver(global_session)
-
+ l2r = False
+ l2k = None
+ if hw.get_device_name().lower() != "dm7025":
+ l2c = tpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT)
+
+ if l2c is None:
+ return
+
+ l2k = validate_certificate(l2c, rootkey)
+ if l2k is None:
+ return
+
+ l2r = True
+ else:
+ l2r = True
+
+ if l2r:
+ if reason is True:
+ startWebserver(global_session, l2k)
+ checkBonjour()
+
+ elif reason is False:
+ stopWebserver(global_session)
+ checkBonjour()
+
def openconfig(session, **kwargs):
session.openWithCallback(configCB, WebIfConfigScreen)
def configCB(result, session):
- if result is True:
- print "[WebIf] config changed"
- restartWebserver(session)
+ l2r = False
+ l2k = None
+ if hw.get_device_name().lower() != "dm7025":
+ l2c = tpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT)
+
+ if l2c is None:
+ return
+
+ l2k = validate_certificate(l2c, rootkey)
+ if l2k is None:
+ return
+
+ l2r = True
else:
- print "[WebIf] config not changed"
+ l2r = True
+
+ if l2r:
+ if result:
+ print "[WebIf] config changed"
+ restartWebserver(session, l2k)
+ checkBonjour()
+ else:
+ print "[WebIf] config not changed"
def Plugins(**kwargs):
return [PluginDescriptor(where=[PluginDescriptor.WHERE_SESSIONSTART], fnc=sessionstart),