Imported Upstream version 4.3.1

This commit is contained in:
Mario Fetka
2021-08-10 02:37:58 +02:00
parent a791de49a2
commit 2f177da8f2
2056 changed files with 421730 additions and 1668138 deletions

View File

@@ -17,39 +17,31 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import
import abc
import base64
import datetime
import logging
import hashlib
import hmac
import os
import uuid
import struct
from lxml import etree
import dateutil.parser
import dateutil.tz
import nss.nss as nss
import gssapi
import six
from six.moves import xrange
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.primitives.kdf import pbkdf2
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from ipaplatform.paths import paths
from ipapython import admintool
from ipalib import api, errors
from ipaserver.plugins.ldap2 import AUTOBIND_DISABLED
from ipaserver.plugins.ldap2 import ldap2, AUTOBIND_DISABLED
if six.PY3:
unicode = str
long = int
logger = logging.getLogger(__name__)
class ValidationError(Exception):
pass
@@ -57,7 +49,6 @@ class ValidationError(Exception):
def fetchAll(element, xpath, conv=lambda x: x):
return [conv(e) for e in element.xpath(xpath, namespaces={
"pkcs5": "http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#",
"pskc": "urn:ietf:params:xml:ns:keyprov:pskc",
"xenc11": "http://www.w3.org/2009/xmlenc11#",
"xenc": "http://www.w3.org/2001/04/xmlenc#",
@@ -97,9 +88,7 @@ def convertTokenType(value):
def convertHashName(value):
"Converts hash names to their canonical names."
default_hash = u"sha1"
known_prefixes = ("", "hmac-",)
known_hashes = {
return {
"sha1": u"sha1",
"sha224": u"sha224",
"sha256": u"sha256",
@@ -110,56 +99,32 @@ def convertHashName(value):
"sha-256": u"sha256",
"sha-384": u"sha384",
"sha-512": u"sha512",
}
if value is None:
return default_hash
v = value.lower()
for prefix in known_prefixes:
if prefix:
w = v[len(prefix):]
else:
w = v
result = known_hashes.get(w)
if result is not None:
break
else:
result = default_hash
return result
}.get(value.lower(), u"sha1")
def convertHMACType(value):
"Converts HMAC URI to hashlib object."
return {
"http://www.w3.org/2000/09/xmldsig#hmac-sha1": hashes.SHA1,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha224": hashes.SHA224,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256": hashes.SHA256,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha384": hashes.SHA384,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha512": hashes.SHA512,
}.get(value.lower(), hashes.SHA1)
"http://www.w3.org/2000/09/xmldsig#hmac-sha1": hashlib.sha1,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha224": hashlib.sha224,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256": hashlib.sha256,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha384": hashlib.sha384,
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha512": hashlib.sha512,
}.get(value.lower(), hashlib.sha1)
def convertAlgorithm(value):
"Converts encryption URI to (mech, ivlen)."
return {
"http://www.w3.org/2001/04/xmlenc#aes128-cbc": (
algorithms.AES, modes.CBC, 128),
"http://www.w3.org/2001/04/xmlenc#aes192-cbc": (
algorithms.AES, modes.CBC, 192),
"http://www.w3.org/2001/04/xmlenc#aes256-cbc": (
algorithms.AES, modes.CBC, 256),
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc": (
algorithms.TripleDES, modes.CBC, 64),
"http://www.w3.org/2001/04/xmldsig-more#camellia128": (
algorithms.Camellia, modes.CBC, 128),
"http://www.w3.org/2001/04/xmldsig-more#camellia192": (
algorithms.Camellia, modes.CBC, 192),
"http://www.w3.org/2001/04/xmldsig-more#camellia256": (
algorithms.Camellia, modes.CBC, 256),
"http://www.w3.org/2001/04/xmlenc#aes128-cbc": (nss.CKM_AES_CBC_PAD, 128),
"http://www.w3.org/2001/04/xmlenc#aes192-cbc": (nss.CKM_AES_CBC_PAD, 192),
"http://www.w3.org/2001/04/xmlenc#aes256-cbc": (nss.CKM_AES_CBC_PAD, 256),
"http://www.w3.org/2001/04/xmlenc#tripledes-cbc": (nss.CKM_DES3_CBC_PAD, 64),
"http://www.w3.org/2001/04/xmldsig-more#camellia128": (nss.CKM_CAMELLIA_CBC_PAD, 128),
"http://www.w3.org/2001/04/xmldsig-more#camellia192": (nss.CKM_CAMELLIA_CBC_PAD, 192),
"http://www.w3.org/2001/04/xmldsig-more#camellia256": (nss.CKM_CAMELLIA_CBC_PAD, 256),
# TODO: add support for these formats.
# "http://www.w3.org/2001/04/xmlenc#kw-aes128": "kw-aes128",
@@ -169,7 +134,7 @@ def convertAlgorithm(value):
# "http://www.w3.org/2001/04/xmldsig-more#kw-camellia128": "kw-camellia128",
# "http://www.w3.org/2001/04/xmldsig-more#kw-camellia192": "kw-camellia192",
# "http://www.w3.org/2001/04/xmldsig-more#kw-camellia256": "kw-camellia256",
}.get(value.lower(), (None, None, None))
}.get(value.lower(), (None, None))
def convertEncrypted(value, decryptor=None, pconv=base64.b64decode, econv=lambda x: x):
@@ -200,34 +165,54 @@ class XMLKeyDerivation(six.with_metaclass(abc.ABCMeta, object)):
class PBKDF2KeyDerivation(XMLKeyDerivation):
def __init__(self, enckey):
params = fetch(enckey, "./xenc11:DerivedKey/xenc11:KeyDerivationMethod/pkcs5:PBKDF2-params")
params = fetch(enckey, "./xenc11:DerivedKey/xenc11:KeyDerivationMethod/xenc11:PBKDF2-params")
if params is None:
raise ValueError("XML file is missing PBKDF2 parameters!")
salt = fetch(params, "./Salt/Specified/text()", base64.b64decode)
itrs = fetch(params, "./IterationCount/text()", int)
klen = fetch(params, "./KeyLength/text()", int)
hmod = fetch(params, "./PRF/@Algorithm", convertHMACType, hashes.SHA1)
self.salt = fetch(params, "./xenc11:Salt/xenc11:Specified/text()", base64.b64decode)
self.iter = fetch(params, "./xenc11:IterationCount/text()", int)
self.klen = fetch(params, "./xenc11:KeyLength/text()", int)
self.hmod = fetch(params, "./xenc11:PRF/@Algorithm", convertHMACType, hashlib.sha1)
if salt is None:
if self.salt is None:
raise ValueError("XML file is missing PBKDF2 salt!")
if itrs is None:
if self.iter is None:
raise ValueError("XML file is missing PBKDF2 iteration count!")
if klen is None:
if self.klen is None:
raise ValueError("XML file is missing PBKDF2 key length!")
self.kdf = pbkdf2.PBKDF2HMAC(
algorithm=hmod(),
length=klen,
salt=salt,
iterations=itrs,
backend=default_backend()
)
def derive(self, masterkey):
return self.kdf.derive(masterkey)
mac = hmac.HMAC(masterkey, None, self.hmod)
# Figure out how many blocks we will have to combine
# to expand the master key to the desired length.
blocks = self.klen // mac.digest_size
if self.klen % mac.digest_size != 0:
blocks += 1
# Loop through each block adding it to the derived key.
dk = []
for i in range(1, blocks + 1):
# Set initial values.
last = self.salt + struct.pack('>I', i)
hash = [0] * mac.digest_size
# Perform n iterations.
for j in xrange(self.iter):
tmp = mac.copy()
tmp.update(last)
last = tmp.digest()
# XOR the previous hash with the new hash.
for k in range(mac.digest_size):
hash[k] ^= ord(last[k])
# Add block to derived key.
dk.extend(hash)
return ''.join([chr(c) for c in dk])[:self.klen]
def convertKeyDerivation(value):
@@ -244,44 +229,27 @@ class XMLDecryptor(object):
* RFC 6931"""
def __init__(self, key, hmac=None):
self.__key = key
self.__key = nss.SecItem(key)
self.__hmac = hmac
def __call__(self, element, mac=None):
algo, mode, klen = fetch(
element, "./xenc:EncryptionMethod/@Algorithm", convertAlgorithm)
data = fetch(
element,
"./xenc:CipherData/xenc:CipherValue/text()",
base64.b64decode
)
# Make sure the key is the right length.
if len(self.__key) * 8 != klen:
raise ValidationError("Invalid key length!")
(mech, ivlen) = fetch(element, "./xenc:EncryptionMethod/@Algorithm", convertAlgorithm)
data = fetch(element, "./xenc:CipherData/xenc:CipherValue/text()", base64.b64decode)
# If a MAC is present, perform validation.
if mac:
tmp = self.__hmac.copy()
tmp.update(data)
try:
tmp.verify(mac)
except InvalidSignature as e:
raise ValidationError("MAC validation failed!", e)
iv = data[:algo.block_size // 8]
data = data[len(iv):]
algorithm = algo(self.__key)
cipher = Cipher(algorithm, mode(iv), default_backend())
decryptor = cipher.decryptor()
padded = decryptor.update(data)
padded += decryptor.finalize()
unpadder = PKCS7(algorithm.block_size).unpadder()
out = unpadder.update(padded)
out += unpadder.finalize()
if tmp.digest() != mac:
raise ValidationError("MAC validation failed!")
# Decrypt the data.
slot = nss.get_best_slot(mech)
key = nss.import_sym_key(slot, mech, nss.PK11_OriginUnwrap, nss.CKA_ENCRYPT, self.__key)
iv = nss.param_from_iv(mech, nss.SecItem(data[0:ivlen//8]))
ctx = nss.create_context_by_sym_key(mech, nss.CKA_DECRYPT, key, iv)
out = ctx.cipher_op(data[ivlen // 8:])
out += ctx.digest_final()
return out
@@ -336,11 +304,7 @@ class PSKCKeyPackage(object):
('model', 'ipatokenmodel', lambda v, o: v.strip()),
('serial', 'ipatokenserial', lambda v, o: v.strip()),
('issueno', 'ipatokenserial', lambda v, o: o.get('ipatokenserial', '') + '-' + v.strip()),
(
'key',
'ipatokenotpkey',
lambda v, o: base64.b32encode(v).decode('ascii')
),
('key', 'ipatokenotpkey', lambda v, o: unicode(base64.b32encode(v))),
('digits', 'ipatokenotpdigits', lambda v, o: v),
('algorithm', 'ipatokenotpalgorithm', lambda v, o: v),
('counter', 'ipatokenhotpcounter', lambda v, o: v),
@@ -403,10 +367,7 @@ class PSKCKeyPackage(object):
result = fetch(element, path)
if result is not None:
lambda_code_attr = "__code__" if six.PY3 else "func_code"
if getattr(
getattr(v[1], lambda_code_attr, None),
"co_argcount", 0) > 1:
if getattr(getattr(v[1], "func_code", None), "co_argcount", 0) > 1:
data[v[0]] = v[1](result, decryptor)
else:
data[v[0]] = v[1](result)
@@ -491,11 +452,7 @@ class PSKCDocument(object):
# Load the decryptor.
self.__decryptor = XMLDecryptor(key)
if self.__mkey is not None and self.__algo is not None:
tmp = hmac.HMAC(
self.__decryptor(self.__mkey),
self.__algo(),
backend=default_backend()
)
tmp = hmac.HMAC(self.__decryptor(self.__mkey), digestmod=self.__algo)
self.__decryptor = XMLDecryptor(key, tmp)
def getKeyPackages(self):
@@ -511,6 +468,14 @@ class OTPTokenImport(admintool.AdminTool):
description = "Import OTP tokens."
usage = "%prog [options] <PSKC file> <output file>"
@classmethod
def main(cls, argv):
nss.nss_init_nodb()
try:
super(OTPTokenImport, cls).main(argv)
finally:
nss.nss_shutdown()
@classmethod
def add_options(cls, parser):
super(OTPTokenImport, cls).add_options(parser)
@@ -544,7 +509,7 @@ class OTPTokenImport(admintool.AdminTool):
self.doc.setKey(f.read())
def run(self):
api.bootstrap(in_server=True, confdir=paths.ETC_IPA)
api.bootstrap(in_server=True)
api.finalize()
try:
@@ -559,9 +524,9 @@ class OTPTokenImport(admintool.AdminTool):
try:
api.Command.otptoken_add(keypkg.id, no_qrcode=True, **keypkg.options)
except Exception as e:
logger.warning("Error adding token: %s", e)
self.log.warn("Error adding token: %s", e)
else:
logger.info("Added token: %s", keypkg.id)
self.log.info("Added token: %s", keypkg.id)
keypkg.remove()
finally:
api.Backend.ldap2.disconnect()