# Copyright (c) Cloud Linux Software, Inc # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENCE.TXT import os import json import hashlib from . import utils from . import auth from . import selinux from . import http_utils from . import config from . import errors from . import constants import kcsig_verify # noqa: E402 SIG_VERIFY_ORDER = [constants.SIG, constants.SIG_JSON] GPG_BIN = '/usr/bin/gpg' GPG_KEY_DIR = '/var/lib/kcare/gpg' CONTENT_FILE = 'release.content.json' def fetch_signature(url, dst, do_auth=False): urlopen_local = http_utils.urlopen if do_auth: urlopen_local = auth.urlopen_auth if config.FORCE_JSON_SIG_V3: sig_exts = SIG_VERIFY_ORDER[::-1] else: sig_exts = SIG_VERIFY_ORDER for sig_ext in sig_exts: try: signature = urlopen_local(url + sig_ext) break except errors.NotFound as nf: if sig_ext == sig_exts[-1]: raise nf # pragma: no cover sig_dst = dst + sig_ext # pragma: no cover utils.save_to_file(signature, sig_dst) return sig_dst def check_gpg_bin(): if not os.path.isfile(GPG_BIN): raise errors.KcareError('No {0} present. Please install gnupg'.format(GPG_BIN)) def check_gpg_signature(file_path, signature): # mocked: tests/unit """ Check a file signature using the gpg tool. If signature is wrong BadSignatureException will be raised. :param file_path: path to file which signature will be checked :param signature: a file with the signature :return: True in case of valid signature :raises: BadSignatureException """ check_gpg_bin() if signature.endswith(constants.SIG_JSON): root_keys = os.path.join(GPG_KEY_DIR, 'root-keys.json') try: kcsig_verify.verify(signature, file_path, root_keys) except kcsig_verify.Error as e: raise errors.BadSignatureException('Bad Signature: {0}: {1}'.format(file_path, str(e))) else: with open(signature, 'rb') as f: sigdata = f.read() keyring = os.path.join(GPG_KEY_DIR, 'kcare_pub.key') try: kcsig_verify.run_gpg_verify(keyring, file_path, sigdata) except Exception as e: raise errors.BadSignatureException('Bad Signature: {0}: {1}'.format(file_path, str(e))) # BadSignatureException is the only side effect of interrupted connection, # should retry file extraction in this case @utils.retry(errors.check_exc(errors.BadSignatureException), count=3, delay=0) def fetch_url(url, dst, check_signature=False, hash_checker=None): response = auth.urlopen_auth(url) tmp = selinux.selinux_safe_tmpname(dst) utils.save_to_file(response, tmp) if hash_checker: hash_checker.check(url, tmp) elif check_signature: signature = fetch_signature(url, tmp, do_auth=True) check_gpg_signature(tmp, signature) os.rename(tmp, dst) return response class HashChecker(object): def __init__(self, baseurl, content_file): self.content_file = content_file self.url_prefix = utils.get_patch_server_url(baseurl).rstrip('/') + '/' self.hashes = json.loads(utils.read_file(content_file))['files'] def check(self, url, fname): cfname = url[len(self.url_prefix) :] if cfname not in self.hashes: raise errors.KcareError('Invalid checksum: {0} not found in content file {1}'.format(cfname, self.content_file)) hsh = hashlib.sha256(utils.read_file_bin(fname)).hexdigest() expected_hsh = self.hashes[cfname]['sha256'] if hsh != expected_hsh: raise errors.BadSignatureException( 'Invalid checksum: {0} has invalid checksum {1}, expected {2}'.format(fname, hsh, expected_hsh) ) @utils.cached def get_hash_checker(level): if not config.USE_CONTENT_FILE_V3: return None if not level.baseurl: return None dst = level.cache_path(CONTENT_FILE) if not os.path.exists(dst): try: # here we also implicitly check content file signature fetch_url(utils.get_patch_server_url(level.baseurl, CONTENT_FILE), dst, config.USE_SIGNATURE) except errors.NotFound: return None return HashChecker(level.baseurl, dst) def wrap_with_cache_key(clbl): """Enrish request with a cache key, and save it if responce had.""" def wrapper(*args, **kwargs): cache_key = utils.get_cache_key() if cache_key is not None: if 'headers' not in kwargs: kwargs['headers'] = {} kwargs['headers'][constants.CACHE_KEY_HEADER] = cache_key resp = clbl(*args, **kwargs) new_cache_key = resp.headers.get(constants.CACHE_KEY_HEADER) if new_cache_key is not None and new_cache_key != cache_key: utils.atomic_write(constants.CACHE_KEY_DUMP_PATH, new_cache_key) return resp return wrapper
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__init__.py | File | 69.58 KB | 0644 |
|
__init__.pyc | File | 64.25 KB | 0644 |
|
__init__.pyo | File | 64.25 KB | 0644 |
|
__main__.py | File | 829 B | 0644 |
|
__main__.pyc | File | 926 B | 0644 |
|
__main__.pyo | File | 926 B | 0644 |
|
auth.py | File | 11.87 KB | 0644 |
|
auth.pyc | File | 11.82 KB | 0644 |
|
auth.pyo | File | 11.82 KB | 0644 |
|
config.py | File | 1.71 KB | 0644 |
|
config.pyc | File | 2.01 KB | 0644 |
|
config.pyo | File | 2.01 KB | 0644 |
|
config_handlers.py | File | 7.26 KB | 0644 |
|
config_handlers.pyc | File | 8.47 KB | 0644 |
|
config_handlers.pyo | File | 8.47 KB | 0644 |
|
constants.py | File | 1.27 KB | 0644 |
|
constants.pyc | File | 1.34 KB | 0644 |
|
constants.pyo | File | 1.34 KB | 0644 |
|
errors.py | File | 1.29 KB | 0644 |
|
errors.pyc | File | 2.94 KB | 0644 |
|
errors.pyo | File | 2.94 KB | 0644 |
|
fetch.py | File | 4.88 KB | 0644 |
|
fetch.pyc | File | 5.47 KB | 0644 |
|
fetch.pyo | File | 5.47 KB | 0644 |
|
http_utils.py | File | 4.06 KB | 0644 |
|
http_utils.pyc | File | 4.16 KB | 0644 |
|
http_utils.pyo | File | 4.16 KB | 0644 |
|
kcare.py | File | 8.56 KB | 0644 |
|
kcare.pyc | File | 11.09 KB | 0644 |
|
kcare.pyo | File | 11.09 KB | 0644 |
|
libcare.py | File | 17.24 KB | 0644 |
|
libcare.pyc | File | 19.16 KB | 0644 |
|
libcare.pyo | File | 19.16 KB | 0644 |
|
log_utils.py | File | 2.82 KB | 0644 |
|
log_utils.pyc | File | 3.75 KB | 0644 |
|
log_utils.pyo | File | 3.75 KB | 0644 |
|
platform_utils.py | File | 3.4 KB | 0644 |
|
platform_utils.pyc | File | 4.56 KB | 0644 |
|
platform_utils.pyo | File | 4.56 KB | 0644 |
|
process_utils.py | File | 3.81 KB | 0644 |
|
process_utils.pyc | File | 3.67 KB | 0644 |
|
process_utils.pyo | File | 3.67 KB | 0644 |
|
py23.py | File | 1.74 KB | 0644 |
|
py23.pyc | File | 2.15 KB | 0644 |
|
py23.pyo | File | 2.15 KB | 0644 |
|
selinux.py | File | 1.68 KB | 0644 |
|
selinux.pyc | File | 2.43 KB | 0644 |
|
selinux.pyo | File | 2.43 KB | 0644 |
|
server_info.py | File | 2.99 KB | 0644 |
|
server_info.pyc | File | 3.48 KB | 0644 |
|
server_info.pyo | File | 3.48 KB | 0644 |
|
update_utils.py | File | 923 B | 0644 |
|
update_utils.pyc | File | 1.18 KB | 0644 |
|
update_utils.pyo | File | 1.18 KB | 0644 |
|
utils.py | File | 7.07 KB | 0644 |
|
utils.pyc | File | 7.8 KB | 0644 |
|
utils.pyo | File | 7.8 KB | 0644 |
|