# -*- coding: utf-8 -*-
#
# Copyright 2004-2022 Univention GmbH
#
# https://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <https://www.gnu.org/licenses/>.
"""
license check
"""
import getopt
import datetime
import traceback
from ldap.filter import filter_format
import univention.admin.license
import univention.admin.uexceptions as uexceptions
import univention.admin.uldap
import univention.config_registry
import univention.license
License = univention.admin.license.License
_license = univention.admin.license._license
[docs]class UsageError(Exception):
pass
[docs]def usage(msg=None):
out = []
script_name = 'univention-license-check'
if msg:
out.append('E: %s' % msg)
out.append('usage: %s [options]' % script_name)
out.append('options:')
out.append(' --%-30s %s' % ('binddn', 'bind DN'))
out.append(' --%-30s %s' % ('bindpw', 'bind password'))
out.append(' --%-30s %s' % ('list-dns', 'list DNs of found objects'))
out.append('OPERATION FAILED')
return out
[docs]def parse_options(argv):
options = {}
long_opts = ['binddn=', 'bindpw=', 'list-dns']
try:
opts, args = getopt.getopt(argv, '', long_opts)
except getopt.error as msg:
raise UsageError(str(msg))
if args:
raise UsageError('options "%s" not recognized' % ' '.join(args))
for opt, val in opts:
options[opt[2:]] = val
return options
[docs]def default_pw():
with open('/etc/ldap.secret', 'r') as secret:
return secret.readline().strip()
[docs]def find_licenses(lo, baseDN, module='*'):
def find_wrap(dir):
try:
return lo.searchDn(base=dir, filter='(univentionLicenseObject=*)')
except uexceptions.noObject:
return []
filter = filter_format('univentionLicenseModule=%s', [module])
dirs = ['cn=directory,cn=univention,%s' % baseDN, 'cn=default containers,cn=univention,%s' % baseDN]
objects = [o for d in dirs for o in find_wrap(d)]
containers = [c.decode('UTF-8') for o in objects for c in lo.get(o)['univentionLicenseObject']]
licenses = [l for c in containers for l in lo.searchDn(base=c, filter=filter)]
return licenses
[docs]def choose_license(lo, dns):
for dn in dns:
retval = univention.license.check(dn)
if retval == -1:
continue
return dn, retval
return None, -1
[docs]def check_license(lo, dn, list_dns, expired):
if expired == -1:
return ['No valid license object found', 'OPERATION FAILED']
out = []
def check_code(code):
for label, value in [('searchpath', 8), ('basedn', 4), ('enddate', 2), ('signature', 1)]:
if code >= value:
code -= value
ok = 'FAILED'
else:
ok = 'OK'
out.append('Checking %s... %s' % ((label.ljust(10)), ok))
def check_type():
v = _license.version
types = _license.licenses[v]
if dn is None:
maximum = [_license.licenses[v][type] for type in types]
else:
maximum = [lo.get(dn)[_license.keys[v][type]][0].decode('utf-8') for type in types]
objs = [lo.searchDn(filter=_license.filters[v][type]) for type in types]
num = [len(obj or '') for obj in objs]
_license.checkObjectCounts(maximum, num)
for i, m, n, odn in zip(range(len(types)), maximum, num, objs):
if i == License.USERS or i == License.ACCOUNT:
n -= _license.sysAccountsFound
if n < 0:
n = 0
ln = _license.names[v][i]
if m:
if list_dns:
out.append("")
ignored = False
if v == '2' and i == License.SERVERS:
# Ignore the server count
ignored = True
out.append(format(ln, n, m, 0, _license.compare, ignored))
if list_dns and maximum != 'unlimited':
for dnout in odn:
out.extend([" %s" % dnout, ])
if list_dns and (i == License.USERS or i == License.ACCOUNT):
out.append(" %s Systemaccounts are ignored." % _license.sysAccountsFound)
def check_time():
now = datetime.date.today()
then = lo.get(dn)['univentionLicenseEndDate'][0].decode('UTF-8')
if then != 'unlimited':
(day, month, year) = then.split(u'.')
then = datetime.date(int(year), int(month), int(day))
if now > then:
out.append('Has expired on: %s -- EXPIRED' % then)
else:
out.append('Will expire on: %s' % then)
if dn is not None and list_dns:
out.append('License found at: %s' % dn)
check_code(expired)
check_type()
if dn is not None:
check_time()
return out
[docs]def main(argv):
options = parse_options(argv)
configRegistry = univention.config_registry.ConfigRegistry()
configRegistry.load()
baseDN = configRegistry['ldap/base']
master = configRegistry['ldap/master']
port = int(configRegistry.get('ldap/master/port', '7389'))
binddn = options.get('binddn', 'cn=admin,%s' % baseDN)
bindpw = options.get('bindpw', None)
if bindpw is None:
try:
bindpw = default_pw()
except IOError:
raise UsageError("Permission denied, try `--binddn' and `--bindpw'")
try:
lo = univention.admin.uldap.access(host=master, port=port, base=baseDN, binddn=binddn, bindpw=bindpw)
except uexceptions.authFail:
raise UsageError("Authentication failed, try `--bindpw'")
out = ['Base DN: %s' % baseDN]
try:
_license.init_select(lo, 'admin')
out.extend(check_license(lo, None, 'list-dns' in options, 0))
except uexceptions.base:
dns = find_licenses(lo, baseDN, 'admin')
dn, expired = choose_license(lo, dns)
out.extend(check_license(lo, dn, 'list-dns' in options, expired))
except Exception:
# output any other tracebacks
trace_out = traceback.format_exc().splitlines()
out.extend(trace_out)
finally:
return out
[docs]def doit(argv):
try:
out = main(argv[1:])
return out
except UsageError as msg:
return usage(str(msg))
if __name__ == '__main__':
import sys
print('\n'.join(doit(sys.argv)))