The branch, master has been updated via c788ed7b8b4 samba-gpupdate: Implement enhanced logging from c4f9c372405 s3: smbd: smbd_smb2_setinfo_send(). All calls to SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) clobber fsp->fsp_name->st.st_ex_iflags.
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit c788ed7b8b4e1f8331562715d68af48ad9985383 Author: David Mulder <dmul...@suse.com> Date: Wed Mar 2 02:23:51 2022 -0700 samba-gpupdate: Implement enhanced logging This ports the enhanced logging capabilities from AltLinux gpupdate. It generates log messages such as: 2022-03-02 11:28:54.872|[E40104]| Failed to set interfaces for zone | {'val': 'work'} 2022-03-02 11:28:55.017|[E40104]| Failed to set interfaces for zone | {'val': 'home'} Signed-off-by: David Mulder <dmul...@suse.com> Reviewed-by: Jeremy Allison <j...@samba.org> Autobuild-User(master): Jeremy Allison <j...@samba.org> Autobuild-Date(master): Thu Mar 24 23:40:47 UTC 2022 on sn-devel-184 ----------------------------------------------------------------------- Summary of changes: python/samba/gp/util/logging.py | 98 +++++++++++++++++++++++++++++++++ python/samba/gp_cert_auto_enroll_ext.py | 51 ++++++++--------- python/samba/gp_chromium_ext.py | 21 +++---- python/samba/gp_ext_loader.py | 11 ++-- python/samba/gp_firefox_ext.py | 17 +++--- python/samba/gp_firewalld_ext.py | 19 +++---- python/samba/gp_gnome_settings_ext.py | 22 ++++---- python/samba/gp_sec_ext.py | 21 ++++--- python/samba/gp_smb_conf_ext.py | 4 +- python/samba/gp_sudoers_ext.py | 4 +- python/samba/gpclass.py | 32 +++++------ python/samba/tests/gpo.py | 67 ++++++++-------------- python/samba/tests/gpo_member.py | 3 +- python/samba/vgp_files_ext.py | 7 +-- python/samba/vgp_sudoers_ext.py | 4 +- python/samba/vgp_symlink_ext.py | 4 +- source4/scripting/bin/samba-gpupdate | 24 ++------ 17 files changed, 229 insertions(+), 180 deletions(-) create mode 100644 python/samba/gp/util/logging.py Changeset truncated at 500 lines: diff --git a/python/samba/gp/util/logging.py b/python/samba/gp/util/logging.py new file mode 100644 index 00000000000..7373af0961e --- /dev/null +++ b/python/samba/gp/util/logging.py @@ -0,0 +1,98 @@ +# +# samba-gpupdate enhanced logging +# +# Copyright (C) 2019-2020 BaseALT Ltd. +# Copyright (C) David Mulder <dmul...@samba.org> 2022 +# +# 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; either version 3 of the License, 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import json +import datetime +import logging +import gettext +import random +import sys + +logger = logging.getLogger() +def logger_init(name, log_level): + logger = logging.getLogger(name) + logger.addHandler(logging.StreamHandler(sys.stdout)) + logger.setLevel(logging.CRITICAL) + if log_level == 1: + logger.setLevel(logging.ERROR) + elif log_level == 2: + logger.setLevel(logging.WARNING) + elif log_level == 3: + logger.setLevel(logging.INFO) + elif log_level >= 4: + logger.setLevel(logging.DEBUG) + +class slogm(object): + ''' + Structured log message class + ''' + def __init__(self, message, kwargs=dict()): + self.message = message + self.kwargs = kwargs + if not isinstance(self.kwargs, dict): + self.kwargs = { 'val': self.kwargs } + + def __str__(self): + now = str(datetime.datetime.now().isoformat(sep=' ', timespec='milliseconds')) + args = dict() + args.update(self.kwargs) + result = '{}|{} | {}'.format(now, self.message, args) + + return result + +def message_with_code(mtype, message): + random.seed(message) + code = random.randint(0, 99999) + return '[' + mtype + str(code).rjust(5, '0') + ']| ' + \ + gettext.gettext(message) + +class log(object): + @staticmethod + def info(message, data={}): + msg = message_with_code('I', message) + logger.info(slogm(msg, data)) + return msg + + @staticmethod + def warning(message, data={}): + msg = message_with_code('W', message) + logger.warning(slogm(msg, data)) + return msg + + @staticmethod + def warn(message, data={}): + return log.warning(message, data) + + @staticmethod + def error(message, data={}): + msg = message_with_code('E', message) + logger.error(slogm(msg, data)) + return msg + + @staticmethod + def fatal(message, data={}): + msg = message_with_code('F', message) + logger.fatal(slogm(msg, data)) + return msg + + @staticmethod + def debug(message, data={}): + msg = message_with_code('D', message) + logger.debug(slogm(msg, data)) + return msg diff --git a/python/samba/gp_cert_auto_enroll_ext.py b/python/samba/gp_cert_auto_enroll_ext.py index 60927709eaa..b61aaf7b985 100644 --- a/python/samba/gp_cert_auto_enroll_ext.py +++ b/python/samba/gp_cert_auto_enroll_ext.py @@ -27,6 +27,7 @@ from subprocess import Popen, PIPE import re from glob import glob import json +from samba.gp.util.logging import log cert_wrap = b""" -----BEGIN CERTIFICATE----- @@ -76,7 +77,7 @@ def find_cepces_submit(): '/usr/libexec/certmonger'] return which('cepces-submit', path=':'.join(certmonger_dirs)) -def get_supported_templates(server, logger): +def get_supported_templates(server): cepces_submit = find_cepces_submit() if os.path.exists(cepces_submit): env = os.environ @@ -85,12 +86,12 @@ def get_supported_templates(server, logger): stdout=PIPE, stderr=PIPE) out, err = p.communicate() if p.returncode != 0: - logger.warn('Failed to fetch the list of supported templates.') - logger.debug(err.decode()) + log.warn('Failed to fetch the list of supported templates.') + log.debug(err.decode()) return out.strip().split() return [] -def cert_enroll(ca, trust_dir, private_dir, logger): +def cert_enroll(ca, trust_dir, private_dir): # Install the root certificate chain data = {'files': [], 'templates': []} sscep = which('sscep') @@ -101,9 +102,9 @@ def cert_enroll(ca, trust_dir, private_dir, logger): ret = Popen([sscep, 'getca', '-F', 'sha1', '-c', root_cert, '-u', url]).wait() if ret != 0: - logger.warn('sscep failed to fetch the root certificate chain.') - logger.warn('Ensure you have installed and configured the' + - ' Network Device Enrollment Service.') + log.warn('sscep failed to fetch the root certificate chain.') + log.warn('Ensure you have installed and configured the' + + ' Network Device Enrollment Service.') root_certs = glob('%s*' % root_cert) data['files'].extend(root_certs) for src in root_certs: @@ -113,21 +114,20 @@ def cert_enroll(ca, trust_dir, private_dir, logger): os.symlink(src, dst) data['files'].append(dst) except PermissionError: - logger.warn('Failed to symlink root certificate to the' + - ' admin trust anchors') + log.warn('Failed to symlink root certificate to the' + + ' admin trust anchors') except FileNotFoundError: - logger.warn('Failed to symlink root certificate to the' + - ' admin trust anchors.' + - ' The directory %s was not found' % \ - global_trust_dir) + log.warn('Failed to symlink root certificate to the' + + ' admin trust anchors.' + + ' The directory was not found', global_trust_dir) except FileExistsError: # If we're simply downloading a renewed cert, the symlink # already exists. Ignore the FileExistsError. Preserve the # existing symlink in the unapply data. data['files'].append(dst) else: - logger.warn('sscep is not installed, which prevents the installation' + - ' of the root certificate chain.') + log.warn('sscep is not installed, which prevents the installation' + + ' of the root certificate chain.') update = which('update-ca-certificates') if update is not None: Popen([update]).wait() @@ -139,11 +139,10 @@ def cert_enroll(ca, trust_dir, private_dir, logger): '%s --server=%s' % (cepces_submit, ca['dNSHostName'][0])], stdout=PIPE, stderr=PIPE) out, err = p.communicate() - logger.debug(out.decode()) + log.debug(out.decode()) if p.returncode != 0: - logger.debug(err.decode()) - supported_templates = get_supported_templates(ca['dNSHostName'][0], - logger) + log.debug(err.decode()) + supported_templates = get_supported_templates(ca['dNSHostName'][0]) for template, attrs in ca['certificateTemplates'].items(): if template not in supported_templates: continue @@ -156,16 +155,16 @@ def cert_enroll(ca, trust_dir, private_dir, logger): '-g', attrs['msPKI-Minimal-Key-Size'][0]], stdout=PIPE, stderr=PIPE) out, err = p.communicate() - logger.debug(out.decode()) + log.debug(out.decode()) if p.returncode != 0: - logger.debug(err.decode()) + log.debug(err.decode()) data['files'].extend([keyfile, certfile]) data['templates'].append(nickname) if update is not None: Popen([update]).wait() else: - logger.warn('certmonger and cepces must be installed for ' + - 'certificate auto enrollment to work') + log.warn('certmonger and cepces must be installed for ' + + 'certificate auto enrollment to work') return json.dumps(data) class gp_cert_auto_enroll_ext(gp_pol_ext): @@ -225,8 +224,7 @@ class gp_cert_auto_enroll_ext(gp_pol_ext): lp=self.lp, credentials=self.creds) cas = fetch_certification_authorities(ldb) for ca in cas: - data = cert_enroll(ca, trust_dir, - private_dir, self.logger) + data = cert_enroll(ca, trust_dir, private_dir) self.gp_db.store(str(self), base64.b64encode(ca['cn'][0]).decode(), data) @@ -260,8 +258,7 @@ class gp_cert_auto_enroll_ext(gp_pol_ext): output[policy][cn]['Auto Enrollment Server'] = \ ca['dNSHostName'][0] supported_templates = \ - get_supported_templates(ca['dNSHostName'][0], - self.logger) + get_supported_templates(ca['dNSHostName'][0]) output[policy][cn]['Templates'] = \ [t.decode() for t in supported_templates] return output diff --git a/python/samba/gp_chromium_ext.py b/python/samba/gp_chromium_ext.py index c3193d04433..befce47085d 100644 --- a/python/samba/gp_chromium_ext.py +++ b/python/samba/gp_chromium_ext.py @@ -19,6 +19,7 @@ import json from samba.gpclass import gp_pol_ext from samba.dcerpc import misc from samba.common import get_string +from samba.gp.util.logging import log def parse_entry_data(name, e): dict_entries = ['VirtualKeyboardFeatures', @@ -389,11 +390,10 @@ class gp_chromium_ext(gp_pol_ext): os.makedirs(self.__managed_policies_path, exist_ok=True) with open(managed_policies, 'w') as f: json.dump(managed, f) - self.logger.debug('Wrote Chromium preferences to %s' % \ - managed_policies) + log.debug('Wrote Chromium preferences', managed_policies) except PermissionError: - self.logger.debug('Failed to write Chromium preferences to %s' % \ - managed_policies) + log.debug('Failed to write Chromium preferences', + managed_policies) def set_recommended_machine_policy(self, recommended): @@ -403,11 +403,10 @@ class gp_chromium_ext(gp_pol_ext): os.makedirs(self.__recommended_policies_path, exist_ok=True) with open(recommended_policies, 'w') as f: json.dump(recommended, f) - self.logger.debug('Wrote Chromium preferences to %s' % \ - recommended_policies) + log.debug('Wrote Chromium preferences', recommended_policies) except PermissionError: - self.logger.debug('Failed to write Chromium preferences to %s' % \ - recommended_policies) + log.debug('Failed to write Chromium preferences', + recommended_policies) def get_managed_machine_policy(self): managed_policies = os.path.join(self.__managed_policies_path, @@ -415,8 +414,7 @@ class gp_chromium_ext(gp_pol_ext): if os.path.exists(managed_policies): with open(managed_policies, 'r') as r: managed = json.load(r) - self.logger.debug('Read Chromium preferences from %s' % \ - managed_policies) + log.debug('Read Chromium preferences', managed_policies) else: managed = {} return managed @@ -427,8 +425,7 @@ class gp_chromium_ext(gp_pol_ext): if os.path.exists(recommended_policies): with open(recommended_policies, 'r') as r: recommended = json.load(r) - self.logger.debug('Read Chromium preferences from %s' % \ - recommended_policies) + log.debug('Read Chromium preferences', recommended_policies) else: recommended = {} return recommended diff --git a/python/samba/gp_ext_loader.py b/python/samba/gp_ext_loader.py index 29a265e3d01..3db8442ea3f 100644 --- a/python/samba/gp_ext_loader.py +++ b/python/samba/gp_ext_loader.py @@ -16,6 +16,7 @@ from samba.gpclass import list_gp_extensions from samba.gpclass import gp_ext +from samba.gp.util.logging import log try: import importlib.util @@ -40,7 +41,7 @@ def get_gp_ext_from_module(name, mod): return None -def get_gp_client_side_extensions(logger, smb_conf): +def get_gp_client_side_extensions(smb_conf): user_exts = [] machine_exts = [] gp_exts = list_gp_extensions(smb_conf) @@ -49,10 +50,10 @@ def get_gp_client_side_extensions(logger, smb_conf): ext = get_gp_ext_from_module(gp_ext['ProcessGroupPolicy'], module) if ext and gp_ext['MachinePolicy']: machine_exts.append(ext) - logger.info('Loaded machine extension from %s: %s' - % (gp_ext['DllName'], ext.__name__)) + log.info('Loaded machine extension from %s: %s' + % (gp_ext['DllName'], ext.__name__)) if ext and gp_ext['UserPolicy']: user_exts.append(ext) - logger.info('Loaded user extension from %s: %s' - % (gp_ext['DllName'], ext.__name__)) + log.info('Loaded user extension from %s: %s' + % (gp_ext['DllName'], ext.__name__)) return (machine_exts, user_exts) diff --git a/python/samba/gp_firefox_ext.py b/python/samba/gp_firefox_ext.py index afe582502b1..6dbded0cae7 100644 --- a/python/samba/gp_firefox_ext.py +++ b/python/samba/gp_firefox_ext.py @@ -19,6 +19,7 @@ import json from samba.gpclass import gp_pol_ext from samba.dcerpc import misc from samba.common import get_string +from samba.gp.util.logging import log def parse_entry_data(e): if e.type == misc.REG_MULTI_SZ: @@ -96,33 +97,29 @@ class gp_firefox_ext(gp_pol_ext): os.makedirs(self.__firefox_installdir1, exist_ok=True) with open(self.__destfile1, 'w') as f: json.dump(policies, f) - self.logger.debug('Wrote Firefox preferences to %s' % \ - self.__destfile1) + log.debug('Wrote Firefox preferences', self.__destfile1) except PermissionError: - self.logger.debug('Failed to write Firefox preferences to %s' % \ + log.debug('Failed to write Firefox preferences', self.__destfile1) try: os.makedirs(self.__firefox_installdir2, exist_ok=True) with open(self.__destfile2, 'w') as f: json.dump(policies, f) - self.logger.debug('Wrote Firefox preferences to %s' % \ - self.__destfile2) + log.debug('Wrote Firefox preferences', self.__destfile2) except PermissionError: - self.logger.debug('Failed to write Firefox preferences to %s' % \ + log.debug('Failed to write Firefox preferences', self.__destfile2) def get_machine_policy(self): if os.path.exists(self.__destfile2): with open(self.__destfile2, 'r') as r: policies = json.load(r) - self.logger.debug('Read Firefox preferences from %s' % \ - self.__destfile2) + log.debug('Read Firefox preferences', self.__destfile2) elif os.path.exists(self.__destfile1): with open(self.__destfile1, 'r') as r: policies = json.load(r) - self.logger.debug('Read Firefox preferences from %s' % \ - self.__destfile1) + log.debug('Read Firefox preferences', self.__destfile1) else: policies = {'policies': {}} return policies diff --git a/python/samba/gp_firewalld_ext.py b/python/samba/gp_firewalld_ext.py index 0fbd87371e0..3b60cf44ad3 100644 --- a/python/samba/gp_firewalld_ext.py +++ b/python/samba/gp_firewalld_ext.py @@ -20,6 +20,7 @@ from hashlib import blake2b from shutil import which import json from samba.gpclass import gp_pol_ext +from samba.gp.util.logging import log def firewall_cmd(*args): fw_cmd = which('firewall-cmd') @@ -47,19 +48,18 @@ class gp_firewalld_ext(gp_pol_ext): def apply_zone(self, zone): ret = firewall_cmd('--permanent', '--new-zone=%s' % zone)[0] if ret != 0: - self.logger.error('Failed to add new zone %s' % zone) + log.error('Failed to add new zone', zone) else: self.gp_db.store(str(self), 'zone:%s' % zone, zone) # Default to matching the interface(s) for the default zone ret, out = firewall_cmd('--list-interfaces') if ret != 0: - self.logger.error('Failed to set interfaces for zone: %s' % zone) + log.error('Failed to set interfaces for zone', zone) for interface in out.strip().split(): ret = firewall_cmd('--permanent', '--zone=%s' % zone, '--add-interface=%s' % interface.decode()) if ret != 0: - self.logger.error('Failed to set interfaces for zone: %s' % \ - zone) + log.error('Failed to set interfaces for zone', zone) def apply_rules(self, rule_dict): for zone, rules in rule_dict.items(): @@ -82,12 +82,11 @@ class gp_firewalld_ext(gp_pol_ext): rule_parsed += rule_segment_parse(list(action)[0], rule[list(action)[0]]) else: - self.logger.error('Invalid firewall rule syntax') + log.error('Invalid firewall rule syntax') ret = firewall_cmd('--permanent', '--zone=%s' % zone, '--add-rich-rule', rule_parsed.strip())[0] if ret != 0: - self.logger.error('Failed to add firewall rule: %s' % \ - rule_parsed) + log.error('Failed to add firewall rule', rule_parsed) else: rhash = blake2b(rule_parsed.encode()).hexdigest() self.gp_db.store(str(self), 'rule:%s:%s' % (zone, rhash), @@ -102,8 +101,7 @@ class gp_firewalld_ext(gp_pol_ext): ret = firewall_cmd('--permanent', '--delete-zone=%s' % value)[0] if ret != 0: - self.logger.error('Failed to remove zone: %s' % \ - value) + log.error('Failed to remove zone', value) else: self.gp_db.delete(str(self), attribute) elif attribute.startswith('rule'): @@ -111,8 +109,7 @@ class gp_firewalld_ext(gp_pol_ext): ret = firewall_cmd('--permanent', '--zone=%s' % zone, '--remove-rich-rule', value)[0] if ret != 0: - self.logger.error('Failed to remove firewall' - ' rule: %s' % value) + log.error('Failed to remove firewall rule', value) else: self.gp_db.delete(str(self), attribute) self.gp_db.commit() diff --git a/python/samba/gp_gnome_settings_ext.py b/python/samba/gp_gnome_settings_ext.py index 27425029d05..1a5e73bea0a 100644 --- a/python/samba/gp_gnome_settings_ext.py +++ b/python/samba/gp_gnome_settings_ext.py @@ -23,8 +23,9 @@ from subprocess import Popen, PIPE from samba.common import get_bytes, get_string from glob import glob import xml.etree.ElementTree as etree +from samba.gp.util.logging import log -def dconf_update(log, test_dir): +def dconf_update(test_dir): if test_dir is not None: return dconf = shutil.which('dconf') @@ -34,7 +35,7 @@ def dconf_update(log, test_dir): p = Popen([dconf, 'update'], stdout=PIPE, stderr=PIPE) out, err = p.communicate() if p.returncode != 0: - log.error('Failed to update dconf: %s' % get_string(err)) + log.error('Failed to update dconf', get_string(err)) def create_locks_dir(test_dir): locks_dir = '/etc/dconf/db/local.d/locks' @@ -124,8 +125,7 @@ class gp_gnome_settings_ext(gp_pol_ext): 'Scroll Lock': 'compose:sclk' } if data['Key Name'] not in data_map.keys(): - self.logger.error('Compose Key \'%s\' not recognized' % \ - data['Key Name']) + log.error('Compose Key not recognized', data) return parser = ConfigParser() section = 'org/gnome/desktop/input-sources' @@ -143,7 +143,7 @@ class gp_gnome_settings_ext(gp_pol_ext): with open(lock, 'w') as w: w.write('/org/gnome/desktop/input-sources/xkb-options') - dconf_update(self.logger, self.test_dir) + dconf_update(self.test_dir) self.gp_db.store(str(self), attribute, ';'.join([local_db, lock])) -- Samba Shared Repository