Branch 'dev/entitlements' - configure.ac Makefile.am pykolab/conf pykolab/Makefile.am
Jeroen van Meeuwen
vanmeeuwen at kolabsys.com
Wed Jan 4 12:18:05 CET 2012
Makefile.am | 7 -
configure.ac | 10 +
pykolab/Makefile.am | 6
pykolab/conf/__init__.py | 11 +
pykolab/conf/entitlement.py | 265 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 297 insertions(+), 2 deletions(-)
New commits:
commit 84b58bf324226ac321cee8726bb46d864318cb9e
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date: Wed Jan 4 12:17:26 2012 +0100
Add the framework for entitlements (there's no enforcement yet, but initialization fails if data does not check out)
diff --git a/Makefile.am b/Makefile.am
index 1e3db21..0fbe791 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -157,10 +157,15 @@ clean:
execdir = $(sbindir)
install-exec-local:
- mkdir -p $(DESTDIR)/$(sbindir) $(DESTDIR)/$(bindir) \
+ mkdir -p $(DESTDIR)/$(sbindir) \
+ $(DESTDIR)/$(bindir) \
+ $(DESTDIR)/$(sysconfdir)/kolab \
$(DESTDIR)/$(libexecdir)/postfix \
$(DESTDIR)/$(localstatedir)/lib/kolab \
$(DESTDIR)/$(localstatedir)/log/kolab
+if ENTERPRISE
+ mkdir -p $(DESTDIR)/$(sysconfdir)/kolab/entitlement.d
+endif
$(INSTALL) -p -m 755 conf.py $(DESTDIR)/$(sbindir)/kolab-conf
$(INSTALL) -p -m 755 kolab.py $(DESTDIR)/$(sbindir)/kolab
$(INSTALL) -p -m 755 kolabd.py $(DESTDIR)/$(sbindir)/kolabd
diff --git a/configure.ac b/configure.ac
index 8d219fe..209f138 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,6 +15,16 @@ AM_GLIB_GNU_GETTEXT
AC_PROG_INTLTOOL
AC_PROG_LN_S
+AC_ARG_ENABLE([enterprise],
+ [ --enable-enterprise Turn on entitlements, compile binary blob],
+ [case "${enableval}" in
+ yes) enterprise=true ;;
+ no) enterprise=false ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enterprise]) ;;
+ esac], [enterprise=false])
+
+AM_CONDITIONAL([ENTERPRISE], [test "${enterprise}" = "true"])
+
AC_SUBST(DATESTAMP,`date +"%a %b %d %Y"`)
AC_CONFIG_FILES([
diff --git a/pykolab/Makefile.am b/pykolab/Makefile.am
index 97fdec7..83a4ec3 100644
--- a/pykolab/Makefile.am
+++ b/pykolab/Makefile.am
@@ -22,6 +22,10 @@ pykolab_conf_PYTHON = \
conf/defaults.py \
conf/__init__.py
+if ENTERPRISE
+pykolab_conf_PYTHON += conf/entitlement.py
+endif
+
pykolab_imapdir = $(pythondir)/$(PACKAGE)/imap
pykolab_imap_PYTHON = \
imap/__init__.py \
@@ -50,7 +54,7 @@ pykolab_setup_PYTHON = \
setup/ldap_setup.py
pykolab_testsdir = $(pythondir)/$(PACKAGE)/tests
-pykolab_setup_PYTHON = \
+pykolab_tests_PYTHON = \
tests/calendar.py \
tests/constants.py \
tests/contacts.py \
diff --git a/pykolab/conf/__init__.py b/pykolab/conf/__init__.py
index 1e4c82a..b078162 100644
--- a/pykolab/conf/__init__.py
+++ b/pykolab/conf/__init__.py
@@ -46,6 +46,17 @@ class Conf(object):
self.cli_args = None
self.cli_keywords = None
+ self.entitlement = None
+
+ from pykolab.conf.entitlement import Entitlement
+ self.entitlement = Entitlement().get()
+
+ #try:
+ #from pykolab.conf.entitlement import Entitlement
+ #self.entitlement = Entitlement().get()
+ #except:
+ #pass
+
self.plugins = None
# The location where our configuration parser is going to end up
diff --git a/pykolab/conf/entitlement.py b/pykolab/conf/entitlement.py
new file mode 100644
index 0000000..34c6353
--- /dev/null
+++ b/pykolab/conf/entitlement.py
@@ -0,0 +1,265 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010-2011 Kolab Systems AG (http://www.kolabsys.com)
+#
+# Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 or, at your option, any later version
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+from ConfigParser import ConfigParser
+import hashlib
+import OpenSSL
+import os
+import StringIO
+import subprocess
+import sys
+
+from pykolab.translate import _
+
+import pykolab
+log = pykolab.getLogger('pykolab.conf')
+
+class Entitlement(object):
+ def __init__(self, *args, **kw):
+ self.entitlement = {}
+
+ self.entitlement_files = []
+
+ ca_cert_file = '/etc/pki/tls/certs/mirror.kolabsys.com.ca.cert'
+ customer_cert_file = '/etc/pki/tls/private/mirror.kolabsys.com.client.pem'
+ customer_key_file = '/etc/pki/tls/private/mirror.kolabsys.com.client.pem'
+
+ # Licence lock and key verification.
+ self.entitlement_verification = [
+ 'f700660f456a60c92ab2f00d0f1968230920d89829d42aa27d30f678',
+ '95783ba5521ea54aa3a32b7949f145aa5015a4c9e92d12b9e4c95c14'
+ ]
+
+ if os.access(ca_cert_file, os.R_OK):
+ # Verify /etc/kolab/mirror_ca.crt
+ ca_cert = OpenSSL.crypto.load_certificate(
+ OpenSSL.SSL.FILETYPE_PEM,
+ open(ca_cert_file).read()
+ )
+
+ if (bool)(ca_cert.has_expired()):
+ raise Exception, _("Invalid entitlement verification " + \
+ "certificate at %s" %(ca_cert_file))
+
+ # TODO: Check validity and warn ~1-2 months in advance.
+
+ ca_cert_issuer = ca_cert.get_issuer()
+ ca_cert_subject = ca_cert.get_subject()
+
+ ca_cert_issuer_hash = subprocess.Popen(
+ [
+ 'openssl',
+ 'x509',
+ '-in',
+ ca_cert_file,
+ '-noout',
+ '-issuer_hash'
+ ],
+ stdout=subprocess.PIPE
+ ).communicate()[0].strip()
+
+ ca_cert_issuer_hash_digest = hashlib.sha224(ca_cert_issuer_hash).hexdigest()
+
+ if not ca_cert_issuer_hash_digest in self.entitlement_verification:
+ raise Exception, _("Invalid entitlement verification " + \
+ "certificate at %s") %(ca_cert_file)
+
+ ca_cert_subject_hash = subprocess.Popen(
+ [
+ 'openssl',
+ 'x509',
+ '-in',
+ ca_cert_file,
+ '-noout',
+ '-subject_hash'
+ ],
+ stdout=subprocess.PIPE
+ ).communicate()[0].strip()
+
+ ca_cert_subject_hash_digest = hashlib.sha224(ca_cert_subject_hash).hexdigest()
+
+ if not ca_cert_subject_hash_digest in self.entitlement_verification:
+ raise Exception, _("Invalid entitlement verification " + \
+ "certificate at %s") %(ca_cert_file)
+
+ customer_cert_issuer_hash = subprocess.Popen(
+ [
+ 'openssl',
+ 'x509',
+ '-in',
+ customer_cert_file,
+ '-noout',
+ '-issuer_hash'
+ ],
+ stdout=subprocess.PIPE
+ ).communicate()[0].strip()
+
+ customer_cert_issuer_hash_digest = hashlib.sha224(customer_cert_issuer_hash).hexdigest()
+
+ if not customer_cert_issuer_hash_digest in self.entitlement_verification:
+ raise Exception, _("Invalid entitlement verification " + \
+ "certificate at %s") %(customer_cert_file)
+
+ if not ca_cert_issuer.countryName == ca_cert_subject.countryName:
+ raise Exception, _("Invalid entitlement certificate")
+
+ if not ca_cert_issuer.organizationName == ca_cert_subject.organizationName:
+ raise Exception, _("Invalid entitlement certificate")
+
+ if os.path.isdir('/etc/kolab/entitlement.d/') and \
+ os.access('/etc/kolab/entitlement.d/', os.R_OK):
+
+ for root, dirs, files in os.walk('/etc/kolab/entitlement.d/'):
+ if not root == '/etc/kolab/entitlement.d/':
+ continue
+ for entitlement_file in files:
+ log.debug(_("Parsing entitlement file %s") %(entitlement_file), level=8)
+
+ if os.access(os.path.join(root, entitlement_file), os.R_OK):
+ self.entitlement_files.append(
+ os.path.join(root, entitlement_file)
+ )
+
+ else:
+ print >> sys.stderr, \
+ _("License file %s not readable!") %(
+ os.path.join(root, entitlement_file)
+ )
+
+ else:
+ print >> sys.stderr, _("No entitlement directory found")
+
+ for entitlement_file in self.entitlement_files:
+
+ decrypt_command = [
+ 'openssl',
+ 'smime',
+ '-decrypt',
+ '-recip',
+ customer_cert_file,
+ '-in',
+ entitlement_file
+ ]
+
+ decrypt_process = subprocess.Popen(
+ decrypt_command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+
+ verify_command = [
+ 'openssl',
+ 'smime',
+ '-verify',
+ '-certfile',
+ ca_cert_file,
+ '-CAfile',
+ ca_cert_file,
+ '-inform',
+ 'DER'
+ ]
+
+ verify_process = subprocess.Popen(
+ verify_command,
+ stdin=decrypt_process.stdout,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+
+ (stdout, stderr) = verify_process.communicate()
+ license = License(stdout, self.entitlement)
+ license.verify_certificate(customer_cert_file)
+ self.entitlement = license.get()
+
+ else:
+ print "Error reading entitlement certificate authority file"
+
+ def get(self):
+ if len(self.entitlement.keys()) == 0:
+ return None
+ else:
+ return self.entitlement
+
+class License(object):
+ entitlement = {}
+
+ def __init__(self, new_entitlement, existing_entitlement):
+ self.parser = ConfigParser()
+ fp = StringIO.StringIO(new_entitlement)
+ self.parser.readfp(fp)
+
+ self.entitlement['users'] = self.parser.get('kolab_entitlements', 'users')
+ self.entitlement['margin'] = self.parser.get('kolab_entitlements', 'margin')
+
+ def verify_certificate(self, customer_cert_file):
+ # Verify the certificate section as well.
+ cert_serial = self.parser.get('mirror_ca', 'serial_number')
+ cert_issuer_hash = self.parser.get('mirror_ca', 'issuer_hash')
+ cert_subject_hash = self.parser.get('mirror_ca', 'subject_hash')
+
+ customer_cert_serial = subprocess.Popen(
+ [
+ 'openssl',
+ 'x509',
+ '-in',
+ customer_cert_file,
+ '-noout',
+ '-serial'
+ ],
+ stdout=subprocess.PIPE
+ ).communicate()[0].strip().split('=')[1]
+
+ if not customer_cert_serial == cert_serial:
+ raise Exception, _("Invalid entitlement verification " + \
+ "certificate at %s") %(customer_cert_file)
+
+ customer_cert_issuer_hash = subprocess.Popen(
+ [
+ 'openssl',
+ 'x509',
+ '-in',
+ customer_cert_file,
+ '-noout',
+ '-issuer_hash'
+ ],
+ stdout=subprocess.PIPE
+ ).communicate()[0].strip()
+
+ if not customer_cert_issuer_hash == cert_issuer_hash:
+ raise Exception, _("Invalid entitlement verification " + \
+ "certificate at %s") %(customer_cert_file)
+
+ customer_cert_subject_hash = subprocess.Popen(
+ [
+ 'openssl',
+ 'x509',
+ '-in',
+ customer_cert_file,
+ '-noout',
+ '-subject_hash'
+ ],
+ stdout=subprocess.PIPE
+ ).communicate()[0].strip()
+
+ if not customer_cert_subject_hash == cert_subject_hash:
+ raise Exception, _("Invalid entitlement verification " + \
+ "certificate at %s") %(customer_cert_file)
+
+ def get(self):
+ return self.entitlement
More information about the commits
mailing list