1
0
mirror of https://github.com/altlinux/gpupdate.git synced 2025-10-12 11:33:18 +03:00

Compare commits

..

1 Commits

Author SHA1 Message Date
81b215003a Manpage for gpupdate-setup added 2020-09-11 18:17:06 +04:00
41 changed files with 263 additions and 1406 deletions

View File

@@ -1,19 +0,0 @@
#!/bin/sh
. /etc/control.d/functions
CONFIG=/etc/pam.d/system-policy-gpupdate
new_subst disabled \
'^[[:space:]]*session[[:space:]]+\[.*default=1.*\][[:space:]]+pam_succeed_if.so user ingroup users.*' \
's,^\([[:space:]]*session[[:space:]]\+\[.*\)default=[[:alnum:]]\+\(.*pam_succeed_if.so user ingroup users.*\)$,\1default=1\2,'
new_subst enabled \
'^[[:space:]]*session[[:space:]]+\[.*default=ignore.*\][[:space:]]+pam_succeed_if.so user ingroup users.*' \
's,^\([[:space:]]*session[[:space:]]\+\[.*\)default=[[:alnum:]]\+\(.*pam_succeed_if.so user ingroup users.*\)$,\1default=ignore\2,'
new_help disabled "Disable group policy applying for users in 'users' group only"
new_help enabled "Enable group policy applying for users in 'users' group only"
new_summary "Group policy applying for users in 'users' group only"
control_subst "$CONFIG" "$*"

View File

@@ -1,19 +0,0 @@
#!/bin/sh
. /etc/control.d/functions
CONFIG=/etc/pam.d/system-policy-gpupdate
new_subst disabled \
'^[[:space:]]*session[[:space:]]+\[.*success=2.*\][[:space:]]+pam_localuser.so' \
's,^\([[:space:]]*session[[:space:]]\+\[.*\)success=[[:alnum:]]\+\(.*pam_localuser.so.*\)$,\1success=2\2,'
new_subst enabled \
'^[[:space:]]*session[[:space:]]+\[.*success=1.*\][[:space:]]+pam_localuser.so' \
's,^\([[:space:]]*session[[:space:]]\+\[.*\)success=[[:alnum:]]\+\(.*pam_localuser.so.*\)$,\1success=1\2,'
new_help disabled 'Disable group policy applying for local users'
new_help enabled 'Enable group policy applying for local users'
new_summary 'Group policy applying for local users'
control_subst "$CONFIG" "$*"

View File

@@ -1,19 +0,0 @@
#!/bin/sh
. /etc/control.d/functions
CONFIG=/etc/pam.d/system-policy-gpupdate
new_subst disabled \
'^[[:space:]]*session[[:space:]]+\[.*default=1.*\][[:space:]]+pam_succeed_if.so uid >= 500.*' \
's,^\([[:space:]]*session[[:space:]]\+\[.*\)default=[[:alnum:]]\+\(.*pam_succeed_if.so uid >= 500.*\)$,\1default=1\2,'
new_subst enabled \
'^[[:space:]]*session[[:space:]]+\[.*default=ignore.*\][[:space:]]+pam_succeed_if.so uid >= 500.*' \
's,^\([[:space:]]*session[[:space:]]\+\[.*\)default=[[:alnum:]]\+\(.*pam_succeed_if.so uid >= 500.*\)$,\1default=ignore\2,'
new_help disabled "Disable group policy applying for users with not system uids only"
new_help enabled "Enable group policy applying for users with not system uids only"
new_summary "Group policy applying for users with not system uids (greater or equal 500) only"
control_subst "$CONFIG" "$*"

4
dist/gpupdate.ini vendored
View File

@@ -1,4 +1,4 @@
[gpoa]
backend = local
local-policy = default
backend = samba
local-policy = auto

View File

@@ -1,6 +1,6 @@
[Unit]
Description=Group policy update for machine
After=syslog.target network-online.target sssd.service
After=sssd.service
[Service]
Environment="PATH=/bin:/sbin:/usr/bin:/usr/sbin"

View File

@@ -1,12 +1,4 @@
#%PAM-1.0
session [success=2 perm_denied=ignore default=die] pam_localuser.so
session required pam_mkhomedir.so silent
session [default=1] pam_permit.so
session [default=6] pam_permit.so
session [success=1 default=ignore] pam_succeed_if.so user ingroup users quiet
session [default=4] pam_permit.so
session [success=1 default=ignore] pam_succeed_if.so uid >= 500 quiet
session [default=2] pam_permit.so
-session required pam_oddjob_gpupdate.so
session optional pam_env.so user_readenv=1 conffile=/etc/gpupdate/environment user_envfile=.gpupdate_environment
session required pam_permit.so

View File

@@ -54,12 +54,15 @@ behavioral defaults for machine in AD domain.
All data is located in \fB/var/cache/gpupdate\fR. Also domain GPTs are
taken from Samba's \fB/var/cache/samba\fR.
.
The configuration file for \fBgpoa\fP is located in
\fB/etc/gpupdate/gpupdate.ini\fP.
.
The settings read from Samba are stored in
\fB/var/cache/gpupdate/registry.sqlite\fR and "Local Policy" settings
read from \fB/usr/local/share/local-policy/default\fR are converted
into GPT and stored as \fB/var/cache/gpupdate/local-policy\fR.
.SH "SEE ALSO"
gpupdate(1)
gpupdate(1) gpupdate-setup(1)
.SH BUGS
Tons of it.
Tons of it. Report at \fBhttps://github.com/altlinux/gpupdate\fP.

View File

@@ -61,7 +61,7 @@ No reply from \fID-Bus\fR when starting \fBgpoa\fR for user using
\fBoddjobd\fR via \fID-Bus\fR.
.
.SH "SEE ALSO"
gpoa(1)
gpoa(1) gpupdate-setup(1)
.SH BUGS
There is tons of bugs since it tries to mimic behavior of Windows
machines.

View File

@@ -41,7 +41,7 @@ def backend_factory(dc, username, is_machine, no_domain = False):
log('D52', ld)
sc = smbcreds(dc)
domain = sc.get_domain()
ldata = dict({'domain': domain, "username": username, 'is_machine': is_machine})
ldata = dict({'domain': domain})
log('D9', ldata)
try:
back = samba_backend(sc, username, domain, is_machine)

View File

@@ -40,8 +40,6 @@ class samba_backend(applier_backend):
def __init__(self, sambacreds, username, domain, is_machine):
self.cache_path = '/var/cache/gpupdate/creds/krb5cc_{}'.format(os.getpid())
self.__kinit_successful = machine_kinit(self.cache_path)
if not self.__kinit_successful:
raise Exception('kinit is not successful')
self.storage = registry_factory('registry')
self.storage.set_info('domain', domain)
machine_name = get_machine_name()
@@ -126,7 +124,7 @@ class samba_backend(applier_backend):
def _get_gpts(self, username, sid):
gpts = list()
log('D45', {'username': username, 'sid': sid})
log('D45')
# util.windows.smbcreds
gpos = self.sambacreds.update_gpos(username)
log('D46')

View File

@@ -32,17 +32,6 @@ def check_experimental_enabled(storage):
return result
def check_windows_mapping_enabled(storage):
windows_mapping_enable_flag = 'Software\\BaseALT\\Policies\\GPUpdate\\WindowsPoliciesMapping'
flag = storage.get_hklm_entry(windows_mapping_enable_flag)
result = True
if flag and '0' == flag.data:
result = False
return result
def check_module_enabled(storage, module_name):
gpupdate_module_enable_branch = 'Software\\BaseALT\\Policies\\GPUpdate'
gpupdate_module_flag = '{}\\{}'.format(gpupdate_module_enable_branch, module_name)

View File

@@ -21,27 +21,11 @@ import threading
import logging
from util.logging import slogm
def control_subst(preg_name):
'''
This is a workaround for control names which can't be used in
PReg/ADMX files.
'''
control_triggers = dict()
control_triggers['dvd_rw-format'] = 'dvd+rw-format'
control_triggers['dvd_rw-mediainfo'] = 'dvd+rw-mediainfo'
control_triggers['dvd_rw-booktype'] = 'dvd+rw-booktype'
result = preg_name
if preg_name in control_triggers:
result = control_triggers[preg_name]
return result
class control:
def __init__(self, name, value):
if type(value) != int and type(value) != str:
raise Exception('Unknown type of value for control')
self.control_name = control_subst(name)
self.control_name = name
self.control_value = value
self.possible_values = self._query_control_values()
if self.possible_values == None:

View File

@@ -1,118 +0,0 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2019-2020 BaseALT Ltd.
#
# 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/>.
from os.path import isfile
from util.logging import slogm
import logging
from gpt.envvars import (
FileAction
, action_letter2enum
)
from util.windows import expand_windows_var
from util.util import (
get_homedir,
homedir_exists
)
class Envvar:
def __init__(self, envvars, username=''):
self.username = username
self.envvars = envvars
if self.username == 'root':
self.envvar_file_path = '/etc/gpupdate/environment'
else:
self.envvar_file_path = get_homedir(self.username) + '/.gpupdate_environment'
def _open_envvar_file(self):
fd = None
if isfile(self.envvar_file_path):
fd = open(self.envvar_file_path, 'r+')
else:
fd = open(self.envvar_file_path, 'w')
fd.close()
fd = open(self.envvar_file_path, 'r+')
return fd
def _create_action(self, create_dict, envvar_file):
lines_old = envvar_file.readlines()
lines_new = list()
for name in create_dict:
exist = False
for line in lines_old:
if line.startswith(name + '='):
exist = True
break
if not exist:
lines_new.append(name + '=' + create_dict[name] + '\n')
if len(lines_new) > 0:
envvar_file.writelines(lines_new)
def _delete_action(self, delete_dict, envvar_file):
lines = envvar_file.readlines()
deleted = False
for name in delete_dict:
for line in lines:
if line.startswith(name + '='):
lines.remove(line)
deleted = True
break
if deleted:
envvar_file.writelines(lines)
def act(self):
if isfile(self.envvar_file_path):
with open(self.envvar_file_path, 'r') as f:
lines = f.readlines()
else:
lines = list()
file_changed = False
for envvar_object in self.envvars:
action = action_letter2enum(envvar_object.action)
name = envvar_object.name
value = expand_windows_var(envvar_object.value, self.username)
if value != envvar_object.value:
#slashes are replaced only if the change of variables was performed and we consider the variable as a path to a file or directory
value = value.replace('\\', '/')
exist_line = None
for line in lines:
if line.split()[0] == name:
exist_line = line
break
if exist_line != None:
if action == FileAction.CREATE:
pass
if action == FileAction.DELETE:
lines.remove(exist_line)
file_changed = True
if action == FileAction.UPDATE or action == FileAction.REPLACE:
if exist_line.split()[1].split('=')[1].replace('"', '') != value: #from 'NAME DEFAULT=value' cut value and compare, don`t change if it matches
lines.remove(exist_line)
lines.append(name + ' ' + 'DEFAULT=\"' + value + '\"\n')
file_changed = True
else:
if action == FileAction.CREATE or action == FileAction.UPDATE or action == FileAction.REPLACE:
lines.append(name + ' ' + 'DEFAULT=\"' + value + '\"\n')
file_changed = True
if action == FileAction.DELETE:
pass
if file_changed:
with open(self.envvar_file_path, 'w') as f:
f.writelines(lines)

View File

@@ -1,7 +1,7 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2019-2021 BaseALT Ltd.
# Copyright (C) 2019-2020 BaseALT Ltd.
#
# 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
@@ -24,112 +24,64 @@ from gi.repository import Gio, GLib
from util.logging import slogm
class system_gsetting:
def __init__(self, schema, path, value, lock, helper_function=None):
__global_schema = '/usr/share/glib-2.0/schemas'
def __init__(self, schema, path, value, override_priority='0'):
self.schema = schema
self.path = path
self.value = value
self.lock = lock
self.helper_function = helper_function
def apply(self, settings, config, locks):
try:
config.add_section(self.schema)
except configparser.DuplicateSectionError:
pass
value = self.value
if self.helper_function:
value = self.helper_function(self.schema, self.path, value)
result = glib_value(self.schema, self.path, value, settings)
config.set(self.schema, self.path, str(result))
if self.lock:
lock_path = dconf_path(settings, self.path)
locks.append(lock_path)
class system_gsettings:
__path_local_dir = '/etc/dconf/db/local.d'
__path_locks = '/etc/dconf/db/policy.d/locks/policy'
__path_profile = '/etc/dconf/profile/user'
__profile_data = 'user-db:user\nsystem-db:policy\nsystem-db:local\n'
def __init__(self, override_file_path):
self.gsettings = list()
self.locks = list()
self.override_file_path = override_file_path
def append(self, schema, path, data, lock, helper):
self.gsettings.append(system_gsetting(schema, path, data, lock, helper))
self.override_priority = override_priority
self.filename = '{}_policy.gschema.override'.format(self.override_priority)
self.file_path = os.path.join(self.__global_schema, self.filename)
def apply(self):
config = configparser.ConfigParser()
for gsetting in self.gsettings:
settings = Gio.Settings(schema=gsetting.schema)
logging.debug(slogm('Applying machine setting {}.{} to {} {}'.format(gsetting.schema,
gsetting.path,
gsetting.value,
gsetting.value,
'locked' if gsetting.lock else 'unlocked')))
gsetting.apply(settings, config, self.locks)
with open(self.override_file_path, 'w') as f:
config.write(f)
os.makedirs(self.__path_local_dir, mode=0o755, exist_ok=True)
os.makedirs(os.path.dirname(self.__path_locks), mode=0o755, exist_ok=True)
os.makedirs(os.path.dirname(self.__path_profile), mode=0o755, exist_ok=True)
try:
os.remove(self.__path_locks)
except OSError as error:
pass
config.read(self.file_path)
except Exception as exc:
logging.error(slogm(exc))
config.add_section(self.schema)
config.set(self.schema, self.path, self.value)
file_locks = open(self.__path_locks,'w')
for lock in self.locks:
file_locks.write(lock +'\n')
file_locks.close()
profile = open(self.__path_profile ,'w')
profile.write(self.__profile_data)
profile.close()
with open(self.file_path, 'w') as f:
config.write(f)
def glib_map(value, glib_type):
result_value = value
if glib_type == 'i' or glib_type == 'b' or glib_type == 'q':
if glib_type == 'i':
result_value = GLib.Variant(glib_type, int(value))
else:
result_value = GLib.Variant(glib_type, value)
return result_value
def dconf_path(settings, path):
return settings.get_property("path") + path
def glib_value(schema, path, value, settings):
# Get the key to modify
key = settings.get_value(path)
# Query the data type for the key
glib_value_type = key.get_type_string()
# Build the new value with the determined type
return glib_map(value, glib_value_type)
class user_gsetting:
def __init__(self, schema, path, value, helper_function=None):
logging.debug('Creating GSettings element {} (in {}) with value {}'.format(path, schema, value))
self.schema = schema
self.path = path
self.value = value
self.helper_function = helper_function
def apply(self):
# Access the current schema
settings = Gio.Settings(schema=self.schema)
# Update result with helper function
value = self.value
logging.debug('Setting GSettings key {} (in {}) to {}'.format(self.path, self.schema, self.value))
if self.helper_function:
value = self.helper_function(self.schema, self.path, value)
# Get typed value by schema
result = glib_value(self.schema, self.path, value, settings)
self.helper_function(self.schema, self.path, self.value)
# Access the current schema
settings = Gio.Settings(self.schema)
# Get the key to modify
key = settings.get_value(self.path)
# Query the data type for the key
glib_value_type = key.get_type_string()
# Build the new value with the determined type
val = glib_map(self.value, glib_value_type)
# Set the value
settings.set_value(self.path, result)
settings.sync()
settings.set_value(self.path, val)
#gso = Gio.Settings.new(self.schema)
#variants = gso.get_property(self.path)
#if (variants.has_key(self.path)):
# key = variants.get_key(self.path)
# print(key.get_range())

View File

@@ -30,8 +30,8 @@ from util.util import is_machine_name
class chromium_applier(applier_frontend):
__module_name = 'ChromiumApplier'
__module_enabled = True
__module_experimental = False
__module_enabled = False
__module_experimental = True
__registry_branch = 'Software\\Policies\\Google\\Chrome'
__managed_policies_path = '/etc/chromium/policies/managed'
__recommended_policies_path = '/etc/chromium/policies/recommended'

View File

@@ -1,69 +0,0 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2019-2020 BaseALT Ltd.
#
# 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/>.
from .applier_frontend import (
applier_frontend
, check_enabled
)
from .appliers.envvar import Envvar
from util.logging import slogm
import logging
class envvar_applier(applier_frontend):
__module_name = 'EnvvarsApplier'
__module_experimental = False
__module_enabled = True
def __init__(self, storage, sid):
self.storage = storage
self.sid = sid
self.envvars = self.storage.get_envvars(self.sid)
#self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
def apply(self):
if self.__module_enabled:
logging.debug(slogm('Running Envvar applier for machine'))
ev = Envvar(self.envvars, 'root')
ev.act()
else:
logging.debug(slogm('Envvar applier for machine will not be started'))
class envvar_applier_user(applier_frontend):
__module_name = 'EnvvarsApplierUser'
__module_experimental = False
__module_enabled = True
def __init__(self, storage, sid, username):
self.storage = storage
self.sid = sid
self.username = username
self.envvars = self.storage.get_envvars(self.sid)
#self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
def admin_context_apply(self):
pass
def user_context_apply(self):
if self.__module_enabled:
logging.debug(slogm('Running Envvar applier for user in user context'))
ev = Envvar(self.envvars, self.username)
ev.act()
else:
logging.debug(slogm('Envvar applier for user in user context will not be started'))

View File

@@ -39,11 +39,10 @@ from util.util import is_machine_name
class firefox_applier(applier_frontend):
__module_name = 'FirefoxApplier'
__module_experimental = False
__module_enabled = True
__module_experimental = True
__module_enabled = False
__registry_branch = 'Software\\Policies\\Mozilla\\Firefox'
__firefox_installdir1 = '/usr/lib64/firefox/distribution'
__firefox_installdir2 = '/etc/firefox/policies'
__firefox_installdir = '/usr/lib64/firefox/distribution'
__user_settings_dir = '.mozilla/firefox'
def __init__(self, storage, sid, username):
@@ -115,78 +114,28 @@ class firefox_applier(applier_frontend):
return homepage
return None
def get_boolean_config(self, name):
def get_block_about_config(self):
'''
Query boolean property from the storage.
Query BlockAboutConfig boolean property from the storage.
'''
response = self.get_hklm_string_entry(name)
response = self.get_hklm_string_entry('BlockAboutConfig')
if response:
data = response.data if isinstance(response.data, int) else str(response.data).lower()
if data in ['0', 'false', None, 'none', 0]:
if response.data.lower() in ['0', 'false', False, None, 'None']:
return False
if data in ['1', 'true', 1]:
return True
return True
return None
def set_boolean_policy(self, name):
'''
Add boolean entry to policy set.
'''
obj = self.get_boolean_config(name)
if obj is not None:
self.policies[name] = obj
logging.info(slogm('Firefox policy \'{}\' set to {}'.format(name, obj)))
def machine_apply(self):
'''
Write policies.json to Firefox installdir.
'''
self.set_policy('Homepage', self.get_home_page())
self.set_boolean_policy('BlockAboutConfig')
self.set_boolean_policy('BlockAboutProfiles')
self.set_boolean_policy('BlockAboutSupport')
self.set_boolean_policy('CaptivePortal')
self.set_boolean_policy('DisableSetDesktopBackground')
self.set_boolean_policy('DisableMasterPasswordCreation')
self.set_boolean_policy('DisableBuiltinPDFViewer')
self.set_boolean_policy('DisableDeveloperTools')
self.set_boolean_policy('DisableFeedbackCommands')
self.set_boolean_policy('DisableFirefoxScreenshots')
self.set_boolean_policy('DisableFirefoxAccounts')
self.set_boolean_policy('DisableFirefoxStudies')
self.set_boolean_policy('DisableForgetButton')
self.set_boolean_policy('DisableFormHistory')
self.set_boolean_policy('DisablePasswordReveal')
self.set_boolean_policy('DisablePocket')
self.set_boolean_policy('DisablePrivateBrowsing')
self.set_boolean_policy('DisableProfileImport')
self.set_boolean_policy('DisableProfileRefresh')
self.set_boolean_policy('DisableSafeMode')
self.set_boolean_policy('DisableSystemAddonUpdate')
self.set_boolean_policy('DisableTelemetry')
self.set_boolean_policy('DontCheckDefaultBrowser')
self.set_boolean_policy('ExtensionUpdate')
self.set_boolean_policy('HardwareAcceleration')
self.set_boolean_policy('PrimaryPassword')
self.set_boolean_policy('NetworkPrediction')
self.set_boolean_policy('NewTabPage')
self.set_boolean_policy('NoDefaultBookmarks')
self.set_boolean_policy('OfferToSaveLogins')
self.set_boolean_policy('PasswordManagerEnabled')
self.set_boolean_policy('PromptForDownloadLocation')
self.set_boolean_policy('SanitizeOnShutdown')
self.set_boolean_policy('SearchSuggestEnabled')
self.set_policy('BlockAboutConfig', self.get_block_about_config())
destfile = os.path.join(self.__firefox_installdir1, 'policies.json')
destfile = os.path.join(self.__firefox_installdir, 'policies.json')
os.makedirs(self.__firefox_installdir1, exist_ok=True)
with open(destfile, 'w') as f:
json.dump(self.policies_json, f)
logging.debug(slogm('Wrote Firefox preferences to {}'.format(destfile)))
destfile = os.path.join(self.__firefox_installdir2, 'policies.json')
os.makedirs(self.__firefox_installdir2, exist_ok=True)
os.makedirs(self.__firefox_installdir, exist_ok=True)
with open(destfile, 'w') as f:
json.dump(self.policies_json, f)
logging.debug(slogm('Wrote Firefox preferences to {}'.format(destfile)))

View File

@@ -17,7 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from storage import registry_factory
from storage.fs_file_cache import fs_file_cache
from .control_applier import control_applier
from .polkit_applier import (
@@ -47,18 +46,14 @@ from .folder_applier import (
)
from .cifs_applier import cifs_applier_user
from .ntp_applier import ntp_applier
from .envvar_applier import (
envvar_applier
, envvar_applier_user
)
from util.windows import get_sid
from util.users import (
is_root,
get_process_user,
username_match_uid,
with_privileges
)
from util.logging import log
from util.system import with_privileges
def determine_username(username=None):
@@ -84,18 +79,6 @@ def determine_username(username=None):
return name
def apply_user_context(user_appliers):
for applier_name, applier_object in user_appliers.items():
log('D55', {'name': applier_name})
try:
applier_object.user_context_apply()
except Exception as exc:
logdata = dict()
logdata['applier'] = applier_name
logdata['exception'] = str(exc)
log('E20', logdata)
class frontend_manager:
'''
The frontend_manager class decides when and how to run appliers
@@ -108,7 +91,6 @@ class frontend_manager:
self.is_machine = is_machine
self.process_uname = get_process_user()
self.sid = get_sid(self.storage.get_info('domain'), self.username, is_machine)
self.file_cache = fs_file_cache('file_cache')
self.machine_appliers = dict()
self.machine_appliers['control'] = control_applier(self.storage)
@@ -117,20 +99,19 @@ class frontend_manager:
self.machine_appliers['firefox'] = firefox_applier(self.storage, self.sid, self.username)
self.machine_appliers['chromium'] = chromium_applier(self.storage, self.sid, self.username)
self.machine_appliers['shortcuts'] = shortcut_applier(self.storage)
self.machine_appliers['gsettings'] = gsettings_applier(self.storage, self.file_cache)
self.machine_appliers['gsettings'] = gsettings_applier(self.storage)
self.machine_appliers['cups'] = cups_applier(self.storage)
self.machine_appliers['firewall'] = firewall_applier(self.storage)
self.machine_appliers['folders'] = folder_applier(self.storage, self.sid)
self.machine_appliers['package'] = package_applier(self.storage)
self.machine_appliers['ntp'] = ntp_applier(self.storage)
self.machine_appliers['envvar'] = envvar_applier(self.storage, self.sid)
# User appliers are expected to work with user-writable
# files and settings, mostly in $HOME.
self.user_appliers = dict()
self.user_appliers['shortcuts'] = shortcut_applier_user(self.storage, self.sid, self.username)
self.user_appliers['folders'] = folder_applier_user(self.storage, self.sid, self.username)
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.file_cache, self.sid, self.username)
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.sid, self.username)
try:
self.user_appliers['cifs'] = cifs_applier_user(self.storage, self.sid, self.username)
except Exception as exc:
@@ -140,7 +121,6 @@ class frontend_manager:
log('E25', logdata)
self.user_appliers['package'] = package_applier_user(self.storage, self.sid, self.username)
self.user_appliers['polkit'] = polkit_applier_user(self.storage, self.sid, self.username)
self.user_appliers['envvar'] = envvar_applier_user(self.storage, self.sid, self.username)
def machine_apply(self):
'''
@@ -150,7 +130,6 @@ class frontend_manager:
log('E13')
return
log('D16')
for applier_name, applier_object in self.machine_appliers.items():
try:
applier_object.apply()
@@ -174,13 +153,13 @@ class frontend_manager:
logdata['exception'] = str(exc)
log('E19', logdata)
try:
with_privileges(self.username, lambda: apply_user_context(self.user_appliers))
except Exception as exc:
logdata = dict()
logdata['username'] = self.username
logdata['exception'] = str(exc)
log('E30', logdata)
try:
with_privileges(self.username, applier_object.user_context_apply)
except Exception as exc:
logdata = dict()
logdata['applier'] = applier_name
logdata['exception'] = str(exc)
log('E20', logdata)
else:
for applier_name, applier_object in self.user_appliers.items():
try:

View File

@@ -1,7 +1,7 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2019-2021 BaseALT Ltd.
# Copyright (C) 2019-2020 BaseALT Ltd.
#
# 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
@@ -20,7 +20,6 @@ import logging
import os
import pwd
import subprocess
import urllib.parse
from gi.repository import (
Gio
@@ -30,108 +29,50 @@ from gi.repository import (
from .applier_frontend import (
applier_frontend
, check_enabled
, check_windows_mapping_enabled
)
from .appliers.gsettings import (
system_gsettings,
system_gsetting,
user_gsetting
)
from util.logging import slogm
def uri_fetch(schema, path, value, cache):
'''
Function to fetch and cache uri
'''
retval = value
logdata = dict()
logdata['schema'] = schema
logdata['path'] = path
logdata['src'] = value
try:
retval = cache.get(value)
logdata['dst'] = retval
logging.debug(slogm('Getting cached file for URI: {}'.format(logdata)))
except Exception as exc:
pass
return retval
class gsettings_applier(applier_frontend):
__module_name = 'GSettingsApplier'
__module_experimental = False
__module_enabled = True
__registry_branch = 'Software\\BaseALT\\Policies\\GSettings\\'
__registry_locks_branch = 'Software\\BaseALT\\Policies\\GSettingsLocks\\'
__wallpaper_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.mate.background.picture-filename'
__vino_authentication_methods_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.gnome.Vino.authentication-methods'
__module_experimental = True
__module_enabled = False
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
__global_schema = '/usr/share/glib-2.0/schemas'
__override_priority_file = 'zzz_policy.gschema.override'
__override_old_file = '0_policy.gschema.override'
__windows_settings = dict()
def __init__(self, storage, file_cache):
def __init__(self, storage):
self.storage = storage
self.file_cache = file_cache
gsettings_filter = '{}%'.format(self.__registry_branch)
gsettings_locks_filter = '{}%'.format(self.__registry_locks_branch)
self.gsettings_keys = self.storage.filter_hklm_entries(gsettings_filter)
self.gsettings_locks = self.storage.filter_hklm_entries(gsettings_locks_filter)
self.override_file = os.path.join(self.__global_schema, self.__override_priority_file)
self.override_old_file = os.path.join(self.__global_schema, self.__override_old_file)
self.gsettings = system_gsettings(self.override_file)
self.locks = dict()
self.gsettings = list()
self.override_file = os.path.join(self.__global_schema, '0_policy.gschema.override')
self.__module_enabled = check_enabled(
self.storage
, self.__module_name
, self.__module_experimental
)
def update_file_cache(self, data):
try:
self.file_cache.store(data)
except Exception as exc:
logdata = dict()
logdata['exception'] = str(exc)
logging.debug(slogm('Unable to cache specified URI for machine: {}'.format(logdata)))
def uri_fetch_helper(self, schema, path, value):
return uri_fetch(schema, path, value, self.file_cache)
def run(self):
# Compatility cleanup of old settings
if os.path.exists(self.override_old_file):
os.remove(self.override_old_file)
# Cleanup settings from previous run
if os.path.exists(self.override_file):
logging.debug(slogm('Removing GSettings policy file from previous run'))
os.remove(self.override_file)
# Get all configured gsettings locks
for lock in self.gsettings_locks:
valuename = lock.hive_key.rpartition('\\')[2]
self.locks[valuename] = int(lock.data)
# Calculate all configured gsettings
for setting in self.gsettings_keys:
helper = None
valuename = setting.hive_key.rpartition('\\')[2]
rp = valuename.rpartition('.')
schema = rp[0]
path = rp[2]
data = setting.data
lock = bool(self.locks[valuename]) if valuename in self.locks else None
if setting.hive_key.lower() == self.__wallpaper_entry.lower():
check = urllib.parse.urlparse(setting.data)
if check.scheme:
self.update_file_cache(setting.data)
helper = self.uri_fetch_helper
elif setting.hive_key.lower() == self.__vino_authentication_methods_entry.lower():
data = [setting.data]
self.gsettings.append(schema, path, data, lock, helper)
self.gsettings.append(system_gsetting(schema, path, setting.data))
# Create GSettings policy with highest available priority
self.gsettings.apply()
for gsetting in self.gsettings:
gsetting.apply()
# Recompile GSettings schemas with overrides
try:
@@ -139,16 +80,9 @@ class gsettings_applier(applier_frontend):
except Exception as exc:
logging.debug(slogm('Error recompiling global GSettings schemas'))
# Update desktop configuration system backend
try:
proc = subprocess.run(args=['/usr/bin/dconf', "update"], capture_output=True, check=True)
except Exception as exc:
logging.debug(slogm('Error update desktop configuration system backend'))
def apply(self):
if self.__module_enabled:
logging.debug(slogm('Running GSettings applier for machine'))
self.run()
else:
logging.debug(slogm('GSettings applier for machine will not be started'))
@@ -185,22 +119,18 @@ class GSettingsMapping:
class gsettings_applier_user(applier_frontend):
__module_name = 'GSettingsApplierUser'
__module_experimental = False
__module_enabled = True
__registry_branch = 'Software\\BaseALT\\Policies\\GSettings\\'
__wallpaper_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.mate.background.picture-filename'
__vino_authentication_methods_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.gnome.Vino.authentication-methods'
__module_experimental = True
__module_enabled = False
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
def __init__(self, storage, file_cache, sid, username):
def __init__(self, storage, sid, username):
self.storage = storage
self.file_cache = file_cache
self.sid = sid
self.username = username
gsettings_filter = '{}%'.format(self.__registry_branch)
self.gsettings_keys = self.storage.filter_hkcu_entries(self.sid, gsettings_filter)
self.gsettings = list()
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
self.__windows_mapping_enabled = check_windows_mapping_enabled(self.storage)
self.__windows_settings = dict()
self.windows_settings = list()
@@ -236,20 +166,6 @@ class gsettings_applier_user(applier_frontend):
self.__windows_settings[element.hive_key] = element
def windows_mapping_append(self):
for setting_key in self.__windows_settings.keys():
value = self.storage.get_hkcu_entry(self.sid, setting_key)
if value:
logging.debug(slogm('Found GSettings windows mapping {} to {}'.format(setting_key, value.data)))
mapping = self.__windows_settings[setting_key]
try:
self.gsettings.append(user_gsetting(mapping.gsettings_schema, mapping.gsettings_key, value.data))
except Exception as exc:
print(exc)
def uri_fetch_helper(self, schema, path, value):
return uri_fetch(schema, path, value, self.file_cache)
def run(self):
#for setting in self.gsettings_keys:
# valuename = setting.hive_key.rpartition('\\')[2]
@@ -258,33 +174,24 @@ class gsettings_applier_user(applier_frontend):
# path = rp[2]
# self.gsettings.append(user_gsetting(schema, path, setting.data))
os.environ['DBUS_SESSION_BUS_ADDRESS'] = 'unix:path=/run/user/{}/bus'.format(pwd.getpwnam(self.username).pw_uid)
# Calculate all mapped gsettings if mapping enabled
if self.__windows_mapping_enabled:
logging.debug(slogm('Mapping Windows policies to GSettings policies'))
self.windows_mapping_append()
else:
logging.debug(slogm('GSettings windows policies mapping not enabled'))
for setting_key in self.__windows_settings.keys():
logging.debug('Checking for GSettings mapping {}'.format(setting_key))
value = self.storage.get_hkcu_entry(self.sid, setting_key)
if value:
logging.debug('Found GSettings mapping {} to {}'.format(setting_key, value.data))
mapping = self.__windows_settings[setting_key]
self.gsettings.append(user_gsetting(mapping.gsettings_schema, mapping.gsettings_key, value.data))
else:
logging.debug('GSettings mapping of {} to {} not found'.format(setting_key, value.data))
# Calculate all configured gsettings
for setting in self.gsettings_keys:
valuename = setting.hive_key.rpartition('\\')[2]
rp = valuename.rpartition('.')
schema = rp[0]
path = rp[2]
data = setting.data
helper = self.uri_fetch_helper if setting.hive_key.lower() == self.__wallpaper_entry.lower() else None
if setting.hive_key.lower() == self.__vino_authentication_methods_entry.lower():
data = [setting.data]
self.gsettings.append(user_gsetting(schema, path, data, helper))
# Create GSettings policy with highest available priority
for gsetting in self.gsettings:
logging.debug(slogm('Applying user setting {}.{} to {}'.format(gsetting.schema,
gsetting.path,
gsetting.value)))
logging.debug('Applying setting {}/{}'.format(gsetting.schema, gsetting.path))
gsetting.apply()
del os.environ['DBUS_SESSION_BUS_ADDRESS']
def user_context_apply(self):
if self.__module_enabled:
logging.debug(slogm('Running GSettings applier for user in user context'))
@@ -293,14 +200,8 @@ class gsettings_applier_user(applier_frontend):
logging.debug(slogm('GSettings applier for user in user context will not be started'))
def admin_context_apply(self):
# Cache files on remote locations
try:
entry = self.__wallpaper_entry
filter_result = self.storage.get_hkcu_entry(self.sid, entry)
if filter_result:
self.file_cache.store(filter_result.data)
except Exception as exc:
logdata = dict()
logdata['exception'] = str(exc)
logging.debug(slogm('Unable to cache specified URI for user: {}'.format(logdata)))
'''
Not implemented because there is no point of doing so.
'''
pass

View File

@@ -31,7 +31,7 @@ from util.util import (
homedir_exists
)
def storage_get_shortcuts(storage, sid, username=None):
def storage_get_shortcuts(storage, sid):
'''
Query storage for shortcuts' rows for specified SID.
'''
@@ -40,15 +40,13 @@ def storage_get_shortcuts(storage, sid, username=None):
for sc_obj in shortcut_objs:
sc = json2sc(sc_obj.shortcut)
if username:
sc.set_expanded_path(expand_windows_var(sc.path, username))
shortcuts.append(sc)
return shortcuts
def apply_shortcut(shortcut, username=None):
def write_shortcut(shortcut, username=None):
'''
Apply the single shortcut file to the disk.
Write the single shortcut file to the disk.
:username: None means working with machine variables and paths
'''
@@ -66,22 +64,22 @@ def apply_shortcut(shortcut, username=None):
if dest_abspath.startswith(get_homedir(username)):
# Don't try to operate on non-existent directory
if not homedir_exists(username):
logging.warning(slogm('No home directory exists for user {}: will not apply link {}'.format(username, dest_abspath)))
logging.warning(slogm('No home directory exists for user {}: will not create link {}'.format(username, dest_abspath)))
return None
else:
logging.warning(slogm('User\'s shortcut not placed to home directory for {}: bad path {}'.format(username, dest_abspath)))
return None
if '%' in dest_abspath:
logging.debug(slogm('Fail for applying shortcut to file with \'%\': {}'.format(dest_abspath)))
logging.debug(slogm('Fail for writing shortcut to file with \'%\': {}'.format(dest_abspath)))
return None
if not dest_abspath.startswith('/'):
logging.debug(slogm('Fail for applying shortcut to not absolute path \'%\': {}'.format(dest_abspath)))
logging.debug(slogm('Fail for writing shortcut to not absolute path \'%\': {}'.format(dest_abspath)))
return None
logging.debug(slogm('Applying shortcut file to {} with action {}'.format(dest_abspath, shortcut.action)))
shortcut.apply_desktop(dest_abspath)
logging.debug(slogm('Writing shortcut file to {}'.format(dest_abspath)))
shortcut.write_desktop(dest_abspath)
class shortcut_applier(applier_frontend):
__module_name = 'ShortcutsApplier'
@@ -100,7 +98,7 @@ class shortcut_applier(applier_frontend):
shortcuts = storage_get_shortcuts(self.storage, self.storage.get_info('machine_sid'))
if shortcuts:
for sc in shortcuts:
apply_shortcut(sc)
write_shortcut(sc)
if len(shortcuts) > 0:
# According to ArchWiki - this thing is needed to rebuild MIME
# type cache in order file bindings to work. This rebuilds
@@ -127,29 +125,27 @@ class shortcut_applier_user(applier_frontend):
self.sid = sid
self.username = username
def run(self, in_usercontext):
shortcuts = storage_get_shortcuts(self.storage, self.sid, self.username)
def run(self):
shortcuts = storage_get_shortcuts(self.storage, self.sid)
if shortcuts:
for sc in shortcuts:
if in_usercontext and sc.is_usercontext():
apply_shortcut(sc, self.username)
if not in_usercontext and not sc.is_usercontext():
apply_shortcut(sc, self.username)
if sc.is_usercontext():
write_shortcut(sc, self.username)
else:
logging.debug(slogm('No shortcuts to process for {}'.format(self.sid)))
def user_context_apply(self):
if self.__module_enabled:
logging.debug(slogm('Running Shortcut applier for user in user context'))
self.run(True)
self.run()
else:
logging.debug(slogm('Shortcut applier for user in user context will not be started'))
def admin_context_apply(self):
if self.__module_enabled:
logging.debug(slogm('Running Shortcut applier for user in administrator context'))
self.run(False)
self.run()
else:
logging.debug(slogm('Shortcut applier for user in administrator context will not be started'))

View File

@@ -73,44 +73,25 @@ class gpoa_controller:
def __init__(self):
self.__args = parse_arguments()
self.is_machine = False
self.noupdate = self.__args.noupdate
if not self.__args.user:
user = get_machine_name()
self.is_machine = True
set_loglevel(self.__args.loglevel)
locale.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
gettext.textdomain('gpoa')
if not self.__args.user:
self.username = get_machine_name()
self.is_machine = True
else:
self.username = self.__args.user
uname = get_process_user()
uid = os.getuid()
logdata = dict()
logdata['username'] = self.username
logdata['is_machine'] = self.is_machine
logdata['process_username'] = uname
logdata['process_uid'] = uid
if self.is_machine:
log('D61', logdata)
else:
log('D1', logdata)
self.username = determine_username(self.username)
logdata['username'] = uname
logdata['uid'] = uid
log('D1', logdata)
if not is_root():
self.noupdate = True
if self.is_machine:
msgtext = message_with_code('E34')
log('E34', {'username': self.username})
raise Exception(msgtext)
log('D59', {'username': self.username})
self.username = uname
else:
log('D60', {'username': self.username})
self.username = determine_username(self.__args.user)
def run(self):
'''
@@ -132,7 +113,7 @@ class gpoa_controller:
if self.__args.nodomain:
nodomain = True
if not self.noupdate:
if not self.__args.noupdate:
if is_root():
back = None
try:

View File

@@ -18,48 +18,21 @@
from util.xml import get_xml_root
from enum import Enum
class FileAction(Enum):
CREATE = 'C'
REPLACE = 'R'
UPDATE = 'U'
DELETE = 'D'
def action_letter2enum(letter):
if letter in ['C', 'R', 'U', 'D']:
if letter == 'C': return FileAction.CREATE
if letter == 'R': return FileAction.REPLACE
if letter == 'U': return FileAction.UPDATE
if letter == 'D': return FileAction.DELETE
return FileAction.CREATE
def read_envvars(envvars_file):
variables = list()
for var in get_xml_root(envvars_file):
props = var.find('Properties')
name = props.get('name')
value = props.get('value')
var_obj = envvar(name, value)
var_obj.set_action(action_letter2enum(props.get('action', default='C')))
var_obj = envvar()
variables.append(var_obj)
return variables
def merge_envvars(storage, sid, envvar_objects, policy_name):
for envv in envvar_objects:
storage.add_envvar(sid, envv, policy_name)
def merge_envvars(storage, sid, envvars_objects, policy_name):
for envvar in envvar_objects:
pass
class envvar:
def __init__(self, name, value):
self.name = name
self.value = value
self.action = FileAction.CREATE
def set_action(self, action):
self.action = action
def __init__(self):
pass

View File

@@ -68,7 +68,7 @@ from .tasks import (
import util
import util.preg
from util.paths import (
local_policy_path,
default_policy_path,
cache_dir,
local_policy_cache
)
@@ -326,7 +326,7 @@ def lp2gpt():
'''
Convert local-policy to full-featured GPT.
'''
lppath = os.path.join(local_policy_path(), 'Machine/Registry.pol.xml')
lppath = os.path.join(default_policy_path(), 'Machine/Registry.pol.xml')
# Load settings from XML PolFile
polparser = GPPolParser()

View File

@@ -79,7 +79,7 @@ def read_shortcuts(shortcuts_file):
# URL or FILESYSTEM
target_type = get_ttype(props.get('targetType'))
sc = shortcut(dest, path, arguments, link.get('name'), props.get('action'), target_type)
sc = shortcut(dest, path, arguments, link.get('name'), target_type)
sc.set_changed(link.get('changed'))
sc.set_clsid(link.get('clsid'))
sc.set_guid(link.get('uid'))
@@ -100,7 +100,7 @@ def json2sc(json_str):
json_obj = json.loads(json_str)
link_type = get_ttype(json_obj['type'])
sc = shortcut(json_obj['dest'], json_obj['path'], json_obj['arguments'], json_obj['name'], json_obj['action'], link_type)
sc = shortcut(json_obj['dest'], json_obj['path'], json_obj['arguments'], json_obj['name'], link_type)
sc.set_changed(json_obj['changed'])
sc.set_clsid(json_obj['clsid'])
sc.set_guid(json_obj['guid'])
@@ -111,7 +111,7 @@ def json2sc(json_str):
return sc
class shortcut:
def __init__(self, dest, path, arguments, name=None, action=None, ttype=TargetType.FILESYSTEM):
def __init__(self, dest, path, arguments, name=None, ttype=TargetType.FILESYSTEM):
'''
:param dest: Path to resulting file on file system
:param path: Path where the link should point to
@@ -121,10 +121,8 @@ class shortcut:
'''
self.dest = dest
self.path = path
self.expanded_path = None
self.arguments = arguments
self.name = name
self.action = action
self.changed = ''
self.icon = None
self.is_in_user_context = self.set_usercontext()
@@ -168,12 +166,6 @@ class shortcut:
self.is_in_user_context = ctx
def set_expanded_path(self, path):
'''
Adjust shortcut path with expanding windows variables
'''
self.expanded_path = path
def is_usercontext(self):
return self.is_in_user_context
@@ -189,7 +181,6 @@ class shortcut:
content['clsid'] = self.clsid
content['guid'] = self.guid
content['changed'] = self.changed
content['action'] = self.action
content['is_in_user_context'] = self.is_in_user_context
content['type'] = ttype2str(self.type)
if self.icon:
@@ -199,78 +190,39 @@ class shortcut:
return json.dumps(result.content)
def desktop(self, dest=None):
def desktop(self):
'''
Returns desktop file object which may be written to disk.
'''
if dest:
self.desktop_file = DesktopEntry(dest)
else:
self.desktop_file = DesktopEntry()
self.desktop_file.addGroup('Desktop Entry')
self.desktop_file.set('Version', '1.0')
self._update_desktop()
self.desktop_file = DesktopEntry()
self.desktop_file.addGroup('Desktop Entry')
return self.desktop_file
def _update_desktop(self):
'''
Update desktop file object from internal data.
'''
if self.type == TargetType.URL:
self.desktop_file.set('Type', 'Link')
else:
self.desktop_file.set('Type', 'Application')
self.desktop_file.set('Version', '1.0')
self.desktop_file.set('Name', self.name)
desktop_path = self.path
if self.expanded_path:
desktop_path = self.expanded_path
if self.type == TargetType.URL:
self.desktop_file.set('URL', desktop_path)
self.desktop_file.set('URL', self.path)
else:
self.desktop_file.set('Terminal', 'false')
self.desktop_file.set('Exec', '{} {}'.format(desktop_path, self.arguments))
self.desktop_file.set('Exec', '{} {}'.format(self.path, self.arguments))
if self.icon:
self.desktop_file.set('Icon', self.icon)
def _write_desktop(self, dest, create_only=False, read_firstly=False):
return self.desktop_file
def write_desktop(self, dest):
'''
Write .desktop file to disk using path 'dest'. Please note that
.desktop files must have executable bit set in order to work in
GUI.
'''
self.desktop().write(dest)
sc = Path(dest)
if sc.exists() and create_only:
return
if sc.exists() and read_firstly:
self.desktop(dest).write(dest)
else:
self.desktop().write(dest)
sc.chmod(sc.stat().st_mode | stat.S_IEXEC)
def _remove_desktop(self, dest):
'''
Remove .desktop file fromo disk using path 'dest'.
'''
sc = Path(dest)
if sc.exists():
sc.unlink()
def apply_desktop(self, dest):
'''
Apply .desktop file by action.
'''
if self.action == 'U':
self._write_desktop(dest, read_firstly=True)
elif self.action == 'D':
self._remove_desktop(dest)
elif self.action == 'R':
self._remove_desktop(dest)
self._write_desktop(dest)
elif self.action == 'C':
self._write_desktop(dest, create_only=True)

View File

@@ -23,6 +23,8 @@ import sys
import argparse
import subprocess
import re
from util.util import (
runcmd
, get_backends
@@ -31,14 +33,17 @@ from util.util import (
, get_policy_variants
)
from util.config import GPConfig
from util.paths import get_custom_policy_dir
class Runner:
__control_path = '/usr/sbin/control'
__systemctl_path = '/bin/systemctl'
__etc_policy_dir = '/etc/local-policy'
__usr_policy_dir = '/usr/share/local-policy'
def __init__(self):
self.etc_policies = get_policy_entries(self.__etc_policy_dir)
self.usr_policies = get_policy_entries(self.__usr_policy_dir)
self.arguments = parse_arguments()
def parse_arguments():
@@ -189,13 +194,13 @@ def disable_gp():
runcmd(cmd_set_local_policy)
runcmd(cmd_disable_gpupdate_service)
runcmd(cmd_disable_gpupdate_user_service)
config.set_local_policy_template()
config.set_backend()
def enable_gp(policy_name, backend_type):
'''
Consistently enable group policy services
'''
policy_dir = '/usr/share/local-policy'
etc_policy_dir = '/etc/local-policy'
cmd_set_gpupdate_policy = ['/usr/sbin/control', 'system-policy', 'gpupdate']
cmd_gpoa_nodomain = ['/usr/sbin/gpoa', '--nodomain', '--loglevel', '5']
cmd_enable_gpupdate_service = ['/bin/systemctl', 'enable', 'gpupdate.service']
@@ -203,17 +208,18 @@ def enable_gp(policy_name, backend_type):
config = GPConfig()
custom_policy_dir = get_custom_policy_dir()
if not os.path.isdir(custom_policy_dir):
os.makedirs(custom_policy_dir)
target_policy_name = get_default_policy_name()
if policy_name:
if validate_policy_name(policy_name):
target_policy_name = policy_name
print (target_policy_name)
config.set_local_policy_template(target_policy_name)
print (target_policy_name)
default_policy_name = os.path.join(policy_dir, target_policy_name)
if not os.path.isdir(etc_policy_dir):
os.makedirs(etc_policy_dir)
config.set_local_policy_template(default_policy_name)
config.set_backend(backend_type)
# Enable oddjobd_gpupdate in PAM config

View File

@@ -289,9 +289,6 @@ msgstr "Пропускаем специальный ключ удаления в
msgid "Read domain name from configuration file"
msgstr "Имя контроллера домена для репликации прочитано из файла конфигурации"
msgid "Saving information about environment variables"
msgstr "Сохранение информации о переменных окружения"
msgid "Unknown debug code"
msgstr "Неизвестный отладочный код"

View File

@@ -57,15 +57,6 @@ def error_code(code):
error_ids[27] = 'Error merging user GPT'
error_ids[28] = 'Error merging machine part of GPT'
error_ids[29] = 'Error merging user part of GPT'
error_ids[30] = 'Error occured while running dropped privileges process for user context appliers'
error_ids[31] = 'Error connecting to DBus Session daemon'
error_ids[32] = 'No reply from DBus Session'
error_ids[33] = 'Error occured while running forked process with dropped privileges'
error_ids[34] = 'Error running GPOA directly for computer'
error_ids[35] = 'Error caching URI to file'
error_ids[36] = 'Error getting cached file for URI'
error_ids[37] = 'Error caching file URIs'
error_ids[38] = 'Unable to cache specified URI'
return error_ids.get(code, 'Unknown error code')
@@ -123,20 +114,6 @@ def debug_code(code):
debug_ids[50] = 'Finished GPO replication from AD DC'
debug_ids[51] = 'Skipping HKCU branch deletion key'
debug_ids[52] = 'Read domain name from configuration file'
debug_ids[53] = 'Saving information about environment variables'
debug_ids[54] = 'Run forked process with droped privileges'
debug_ids[55] = 'Run user context applier with dropped privileges'
debug_ids[56] = 'Kill dbus-daemon and dconf-service in user context'
debug_ids[57] = 'Found connection by org.freedesktop.DBus.GetConnectionUnixProcessID'
debug_ids[58] = 'Connection search return org.freedesktop.DBus.Error.NameHasNoOwner'
debug_ids[59] = 'Running GPOA without GPT update directly for user'
debug_ids[60] = 'Running GPOA by root for user'
debug_ids[61] = 'The GPOA process was started for computer'
debug_ids[62] = 'Path not resolved as UNC URI'
debug_ids[63] = 'Delete HKLM branch key'
debug_ids[64] = 'Delete HKCU branch key'
debug_ids[65] = 'Delete HKLM branch key error'
debug_ids[66] = 'Delete HKCU branch key error'
return debug_ids.get(code, 'Unknown debug code')

View File

@@ -1,97 +0,0 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2021 BaseALT Ltd. <org@basealt.ru>
# Copyright (C) 2021 Igor Chudov <nir@nir.org.ru>
#
# 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 os
import os.path
from pathlib import Path
import smbc
from util.logging import log
from util.paths import file_cache_dir, UNCPath
from util.exceptions import NotUNCPathError
class fs_file_cache:
__read_blocksize = 4096
def __init__(self, cache_name):
self.cache_name = cache_name
self.storage_uri = file_cache_dir()
logdata = dict({'cache_file': self.storage_uri})
log('D20', logdata)
self.samba_context = smbc.Context(use_kerberos=1)
#, debug=10)
def store(self, uri):
destdir = uri
try:
uri_path = UNCPath(uri)
file_name = os.path.basename(uri_path.get_path())
file_path = os.path.dirname(uri_path.get_path())
destdir = Path('{}/{}/{}'.format(self.storage_uri,
uri_path.get_domain(),
file_path))
except Exception as exc:
logdata = dict({'exception': str(exc)})
log('E38', logdata)
raise exc
if not destdir.exists():
destdir.mkdir(parents=True, exist_ok=True)
destfile = Path('{}/{}/{}'.format(self.storage_uri,
uri_path.get_domain(),
uri_path.get_path()))
with open(destfile, 'wb') as df:
df.truncate()
df.flush()
try:
file_handler = self.samba_context.open(str(uri_path), os.O_RDONLY)
while True:
data = file_handler.read(self.__read_blocksize)
if not data:
break
df.write(data)
df.flush()
except Exception as exc:
logdata = dict({'exception': str(exc)})
log('E35', logdata)
raise exc
def get(self, uri):
destfile = uri
try:
uri_path = UNCPath(uri)
file_name = os.path.basename(uri_path.get_path())
file_path = os.path.dirname(uri_path.get_path())
destfile = Path('{}/{}/{}'.format(self.storage_uri,
uri_path.get_domain(),
uri_path.get_path()))
except NotUNCPathError as exc:
logdata = dict({'path': str(exc)})
log('D62', logdata)
except Exception as exc:
logdata = dict({'exception': str(exc)})
log('E36', logdata)
raise exc
return str(destfile)

View File

@@ -22,9 +22,7 @@ class samba_preg(object):
'''
def __init__(self, preg_obj, policy_name):
self.policy_name = policy_name
self.keyname = preg_obj.keyname
self.valuename = preg_obj.valuename
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
self.type = preg_obj.type
self.data = preg_obj.data
@@ -43,9 +41,7 @@ class samba_hkcu_preg(object):
def __init__(self, sid, preg_obj, policy_name):
self.sid = sid
self.policy_name = policy_name
self.keyname = preg_obj.keyname
self.valuename = preg_obj.valuename
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
self.type = preg_obj.type
self.data = preg_obj.data
@@ -152,25 +148,3 @@ class folder_entry(object):
return fields
class envvar_entry(object):
'''
Object mapping representing environment variables
'''
def __init__(self, sid, evobj, policy_name):
self.sid = sid
self.policy_name = policy_name
self.name = evobj.name
self.value = evobj.value
self.action = evobj.action.value
def update_fields(self):
'''
Return list of fields to update
'''
fields = dict()
fields['policy_name'] = self.policy_name
fields['action'] = self.action
fields['value'] = self.value
return fields

View File

@@ -43,7 +43,6 @@ from .record_types import (
, printer_entry
, drive_entry
, folder_entry
, envvar_entry
)
class sqlite_registry(registry):
@@ -66,10 +65,7 @@ class sqlite_registry(registry):
'HKLM'
, self.__metadata
, Column('id', Integer, primary_key=True)
, Column('hive_key', String(65536, collation='NOCASE'),
unique=True)
, Column('keyname', String(collation='NOCASE'))
, Column('valuename', String(collation='NOCASE'))
, Column('hive_key', String(65536), unique=True)
, Column('policy_name', String)
, Column('type', Integer)
, Column('data', String)
@@ -79,9 +75,7 @@ class sqlite_registry(registry):
, self.__metadata
, Column('id', Integer, primary_key=True)
, Column('sid', String)
, Column('hive_key', String(65536, collation='NOCASE'))
, Column('keyname', String(collation='NOCASE'))
, Column('valuename', String(collation='NOCASE'))
, Column('hive_key', String(65536))
, Column('policy_name', String)
, Column('type', Integer)
, Column('data', String)
@@ -132,17 +126,6 @@ class sqlite_registry(registry):
, Column('delete_files', String)
, UniqueConstraint('sid', 'path')
)
self.__envvars = Table(
'Envvars'
, self.__metadata
, Column('id', Integer, primary_key=True)
, Column('sid', String)
, Column('name', String)
, Column('policy_name', String)
, Column('action', String)
, Column('value', String)
, UniqueConstraint('sid', 'name')
)
self.__metadata.create_all(self.db_cnt)
Session = sessionmaker(bind=self.db_cnt)
self.db_session = Session()
@@ -154,7 +137,6 @@ class sqlite_registry(registry):
mapper(printer_entry, self.__printers)
mapper(drive_entry, self.__drives)
mapper(folder_entry, self.__folders)
mapper(envvar_entry, self.__envvars)
except:
pass
#logging.error('Error creating mapper')
@@ -244,52 +226,16 @@ class sqlite_registry(registry):
log('D19', logdata)
self._info_upsert(ientry)
def _delete_hklm_keyname(self, keyname):
'''
Delete PReg hive_key from HKEY_LOCAL_MACHINE
'''
logdata = dict({'keyname': keyname})
try:
(self
.db_session
.query(samba_preg)
.filter(samba_preg.keyname == keyname)
.delete(synchronize_session=False))
self.db_session.commit()
log('D65', logdata)
except Exception as exc:
log('D63', logdata)
def add_hklm_entry(self, preg_entry, policy_name):
'''
Write PReg entry to HKEY_LOCAL_MACHINE
'''
pentry = samba_preg(preg_entry, policy_name)
if not pentry.valuename.startswith('**'):
if not pentry.hive_key.rpartition('\\')[2].startswith('**'):
self._hklm_upsert(pentry)
else:
logdata = dict({'key': pentry.hive_key})
if pentry.valuename.lower() == '**delvals.':
self._delete_hklm_keyname(pentry.keyname)
else:
log('D27', logdata)
def _delete_hkcu_keyname(self, keyname, sid):
'''
Delete PReg hive_key from HKEY_CURRENT_USER
'''
logdata = dict({'sid': sid, 'keyname': keyname})
try:
(self
.db_session
.query(samba_hkcu_preg)
.filter(samba_hkcu_preg.sid == sid)
.filter(samba_hkcu_preg.keyname == keyname)
.delete(synchronize_session=False))
self.db_session.commit()
log('D66', logdata)
except:
log('D64', logdata)
log('D27', logdata)
def add_hkcu_entry(self, preg_entry, sid, policy_name):
'''
@@ -297,14 +243,11 @@ class sqlite_registry(registry):
'''
hkcu_pentry = samba_hkcu_preg(sid, preg_entry, policy_name)
logdata = dict({'sid': sid, 'policy': policy_name, 'key': hkcu_pentry.hive_key})
if not hkcu_pentry.valuename.startswith('**'):
if not hkcu_pentry.hive_key.rpartition('\\')[2].startswith('**'):
log('D26', logdata)
self._hkcu_upsert(hkcu_pentry)
else:
if hkcu_pentry.valuename.lower() == '**delvals.':
self._delete_hkcu_keyname(hkcu_pentry.keyname, sid)
else:
log('D51', logdata)
log('D51', logdata)
def add_shortcut(self, sid, sc_obj, policy_name):
'''
@@ -351,21 +294,6 @@ class sqlite_registry(registry):
.update(fld_entry.update_fields()))
self.db_session.commit()
def add_envvar(self, sid, evobj, policy_name):
ev_entry = envvar_entry(sid, evobj, policy_name)
logdata = dict()
logdata['envvar'] = ev_entry.name
logdata['sid'] = sid
log('D53', logdata)
try:
self._add(ev_entry)
except Exception as exc:
(self
._filter_sid_obj(envvar_entry, sid)
.filter(envvar_entry.name == ev_entry.name)
.update(ev_entry.update_fields()))
self.db_session.commit()
def _filter_sid_obj(self, row_object, sid):
res = (self
.db_session
@@ -378,7 +306,6 @@ class sqlite_registry(registry):
.db_session
.query(row_object)
.filter(row_object.sid == sid)
.order_by(row_object.id)
.all())
return res
@@ -394,9 +321,6 @@ class sqlite_registry(registry):
def get_folders(self, sid):
return self._filter_sid_list(folder_entry, sid)
def get_envvars(self, sid):
return self._filter_sid_list(envvar_entry, sid)
def get_hkcu_entry(self, sid, hive_key):
res = (self
.db_session

View File

@@ -45,7 +45,7 @@ class GPConfig:
return 'samba'
def set_backend(self, backend_name='local'):
def set_backend(self, backend_name):
self.full_config['gpoa']['backend'] = backend_name
self.write_config()
@@ -71,7 +71,7 @@ class GPConfig:
return get_default_policy_name()
def set_local_policy_template(self, template_name='default'):
def set_local_policy_template(self, template_name):
self.full_config['gpoa']['local-policy'] = template_name
self.write_config()

View File

@@ -29,38 +29,22 @@ class dbus_runner:
'''
_bus_name = 'com.redhat.oddjob_gpupdate'
# Interface name is equal to bus name.
_interface_name = 'com.redhat.oddjob_gpupdate'
_object_path = '/'
# The timeout is in milliseconds. The default is -1 which is
# DBUS_TIMEOUT_USE_DEFAULT which is 25 seconds. There is also
# DBUS_TIMEOUT_INFINITE constant which is equal to INT32_MAX or
# 0x7ffffff (largest 32-bit integer).
#
# It was decided to set the timeout to 10 minutes which must be
# sufficient to replicate and apply all recognizable GPOs.
_synchronous_timeout = 600000
def __init__(self, username=None):
self.username = username
self.system_bus = dbus.SystemBus()
system_bus = dbus.SystemBus()
obj = system_bus.get_object(self._bus_name, self._object_path)
self.interface = dbus.Interface(obj, self._bus_name)
def run(self):
#print(obj.Introspect()[0])
if self.username:
logdata = dict({'username': self.username})
log('D6', logdata)
if is_root():
# oddjobd-gpupdate's ACL allows access to this method
# only for superuser. This method is called via PAM
# when user logs in.
try:
result = self.system_bus.call_blocking(self._bus_name,
self._object_path,
self._interface_name,
'gpupdatefor',
(username),
(dbus.String(self.username)),
timeout=self._synchronous_timeout)
result = self.interface.gpupdatefor(dbus.String(self.username))
print_dbus_result(result)
except dbus.exceptions.DBusException as exc:
logdata = dict()
@@ -69,36 +53,20 @@ class dbus_runner:
raise exc
else:
try:
result = self.system_bus.call_blocking(self._bus_name,
self._object_path,
self._interface_name,
'gpupdate',
None,
(),
timeout=self._synchronous_timeout)
result = self.interface.gpupdate()
print_dbus_result(result)
except dbus.exceptions.DBusException as exc:
logdata = dict({'error': str(exc)})
log('E21', logdata)
log('E21')
raise exc
else:
log('D11')
try:
result = self.system_bus.call_blocking(self._bus_name,
self._object_path,
self._interface_name,
'gpupdate_computer',
None,
# The following positional parameter is called "args".
# There is no official documentation for it.
(),
timeout=self._synchronous_timeout)
result = self.interface.gpupdate_computer()
print_dbus_result(result)
except dbus.exceptions.DBusException as exc:
print(exc)
logdata = dict({'error': str(exc)})
log('E22', logdata)
log('E22')
raise exc
#self.interface.Quit()
def start_gpupdate_user():
@@ -170,27 +138,3 @@ def print_dbus_result(result):
for line in message:
print(str(line))
class dbus_session:
def __init__(self):
try:
self.session_bus = dbus.SessionBus()
self.session_dbus = self.session_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
self.session_iface = dbus.Interface(self.session_dbus, 'org.freedesktop.DBus')
except dbus.exceptions.DBusException as exc:
logdata = dict({'error': str(exc)})
log('E31', logdata)
raise exc
def get_connection_pid(self, connection):
pid = -1
try:
pid = self.session_iface.GetConnectionUnixProcessID(connection)
log('D57', {"pid": pid})
except dbus.exceptions.DBusException as exc:
if exc.get_dbus_name() != 'org.freedesktop.DBus.Error.NameHasNoOwner':
logdata = dict({'error': str(exc)})
log('E32', logdata)
raise exc
log('D58', {'connection': connection})
return int(pid)

View File

@@ -39,10 +39,3 @@ def geterr():
return traceinfo
class NotUNCPathError(Exception):
def __init__(self, path):
self.path = path
def __str__(self):
return self.path

View File

@@ -21,19 +21,15 @@ import subprocess
from .util import get_machine_name
from .logging import log
from .samba import smbopts
def machine_kinit(cache_name=None):
'''
Perform kinit with machine credentials
'''
opts = smbopts()
host = get_machine_name()
realm = opts.get_realm()
with_realm = '{}@{}'.format(host, realm)
os.environ['KRB5CCNAME'] = 'FILE:{}'.format(cache_name)
kinit_cmd = ['kinit', '-k', with_realm]
kinit_cmd = ['kinit', '-k', host]
if cache_name:
kinit_cmd.extend(['-c', cache_name])
proc = subprocess.Popen(kinit_cmd)
@@ -59,9 +55,8 @@ def machine_kdestroy(cache_name=None):
if cache_name:
kdestroy_cmd.extend(['-c', cache_name])
if cache_name or 'KRB5CCNAME' in os.environ:
proc = subprocess.Popen(kdestroy_cmd, stderr=subprocess.DEVNULL)
proc.wait()
proc = subprocess.Popen(kdestroy_cmd, stderr=subprocess.DEVNULL)
proc.wait()
if cache_name and os.path.exists(cache_name):
os.unlink(cache_name)
@@ -79,13 +74,10 @@ def check_krb_ticket():
try:
subprocess.check_call(['klist', '-s'])
output = subprocess.check_output('klist', stderr=subprocess.STDOUT).decode()
logging.info(output)
result = True
logdata = dict()
logdata['output'] = output
log('D17', logdata)
except Exception as exc:
logdata = dict()
logdata['krb-exc'] = exc
log('E14', logdata)
log('D17')
except:
log('E14')
return result

View File

@@ -1,8 +1,7 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2019-2021 BaseALT Ltd. <org@basealt.ru>
# Copyright (C) 2019-2021 Igor Chudov <nir@nir.org.ru>
# Copyright (C) 2019-2020 BaseALT Ltd.
#
# 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
@@ -19,38 +18,25 @@
import pathlib
import os
from pathlib import Path
from urllib.parse import urlparse
from .config import GPConfig
from .exceptions import NotUNCPathError
def get_custom_policy_dir():
def default_policy_path():
'''
Returns path pointing to Custom Policy directory.
Returns path pointing to Default Policy directory.
'''
return '/etc/local-policy'
def local_policy_path(default_template_name="default"):
'''
Returns path pointing to Local Policy template directory.
'''
local_policy_dir = '/usr/share/local-policy'
local_policy_default = '/usr/share/local-policy/default'
config = GPConfig()
local_policy_template = config.get_local_policy_template()
local_policy_template_path = os.path.join(local_policy_dir, local_policy_template)
local_policy_default = os.path.join(local_policy_dir, default_template_name)
result_path = pathlib.Path(local_policy_default)
if os.path.exists(local_policy_template):
result_path = pathlib.Path(local_policy_template)
elif os.path.exists(local_policy_template_path):
result_path = pathlib.Path(local_policy_template_path)
if os.path.exists(config.get_local_policy_template()):
result_path = pathlib.Path(config.get_local_policy_template())
return pathlib.Path(result_path)
def cache_dir():
'''
Returns path pointing to gpupdate's cache directory
@@ -62,16 +48,6 @@ def cache_dir():
return cachedir
def file_cache_dir():
'''
Returns path pointing to gpupdate's cache directory
'''
cachedir = pathlib.Path('/var/cache/gpupdate_file_cache')
if not cachedir.exists():
cachedir.mkdir(parents=True, exist_ok=True)
return cachedir
def local_policy_cache():
'''
@@ -85,46 +61,3 @@ def local_policy_cache():
return lpcache
class UNCPath:
def __init__(self, path):
self.path = path
self.type = None
if self.path.startswith(r'smb://'):
self.type = 'uri'
if self.path.startswith(r'\\'):
self.type = 'unc'
if not self.type:
raise NotUNCPathError(path)
def get_uri(self):
path = self.path
if self.type == 'unc':
path = self.path.replace('\\', '/')
path = path.replace('//', 'smb://')
else:
pass
return path
def get_unc(self):
path = self.path
if self.type == 'uri':
path = self.path.replace('//', '\\\\')
path = path.replace('smb:\\\\', '\\\\')
path = path.replace('/', '\\')
else:
pass
return path
def get_domain(self):
schema_struct = urlparse(self.get_uri())
return schema_struct.netloc
def get_path(self):
schema_struct = urlparse(self.get_uri())
return schema_struct.path
def __str__(self):
return self.get_uri()

View File

@@ -18,7 +18,6 @@
import optparse
import socket
from samba import getopt as options
@@ -29,32 +28,11 @@ class smbopts:
self.sambaopts = options.SambaOptions(self.parser)
self.lp = self.sambaopts.get_loadparm()
def get_realm(self):
'''
Get the default realm specified in smb.conf file.
'''
return self._get_prop('realm')
def get_cache_dir(self):
return self._get_prop('cache directory')
def get_server_role(self):
return self._get_prop('server role')
def get_machine_name(self):
'''
Get localhost name looking like DC0$
'''
nb_name = self.get_netbios_name()
result = nb_name + "$"
if result == '':
result = socket.gethostname().split('.', 1)[0].upper() + "$"
return result
def get_netbios_name(self):
return self._get_prop('netbios name')
def _get_prop(self, property_name):
return self.lp.get(property_name)

View File

@@ -1,141 +0,0 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2021 BaseALT Ltd.
#
# 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 os
import sys
import pwd
import signal
import subprocess
from .logging import log
from .dbus import dbus_session
def set_privileges(username, uid, gid, groups, home):
'''
Set current process privileges
'''
os.environ.clear()
os.environ['HOME'] = home
os.environ['USER'] = username
os.environ['USERNAME'] = username
try:
os.setgid(gid)
except Exception as exc:
raise Exception('Error setgid() for drop privileges: {}'.format(str(exc)))
try:
os.setgroups(groups)
except Exception as exc:
raise Exception('Error setgroups() for drop privileges: {}'.format(str(exc)))
try:
os.setuid(uid)
except Exception as exc:
raise Exception('Error setuid() for drop privileges: {}'.format(str(exc)))
os.chdir(home)
logdata = dict()
logdata['uid'] = uid
logdata['gid'] = gid
logdata['username'] = username
log('D37', logdata)
def with_privileges(username, func):
'''
Run supplied function with privileges for specified username.
'''
if not os.getuid() == 0:
raise Exception('Not enough permissions to drop privileges')
user_pw = pwd.getpwnam(username)
user_uid = user_pw.pw_uid
user_gid = user_pw.pw_gid
user_groups = os.getgrouplist(username, user_gid)
user_home = user_pw.pw_dir
if not os.path.isdir(user_home):
raise Exception('User home directory not exists')
pid = os.fork()
if pid > 0:
log('D54', {'pid': pid})
waitpid, status = os.waitpid(pid, 0)
code = os.WEXITSTATUS(status)
if code != 0:
raise Exception('Error in forked process ({})'.format(status))
return
# We need to return child error code to parent
result = 0
dbus_pid = -1
dconf_pid = -1
try:
# Drop privileges
set_privileges(username, user_uid, user_gid, user_groups, user_home)
# Run the D-Bus session daemon in order D-Bus calls to work
proc = subprocess.Popen(
'dbus-launch',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for var in proc.stdout:
sp = var.decode('utf-8').split('=', 1)
os.environ[sp[0]] = sp[1][:-1]
# Save pid of dbus-daemon
dbus_pid = int(os.environ['DBUS_SESSION_BUS_PID'])
# Run user appliers
func()
# Save pid of dconf-service
dconf_connection = "ca.desrt.dconf"
try:
session = dbus_session()
dconf_pid = session.get_connection_pid(dconf_connection)
except Exception:
pass
except Exception as exc:
logdata = dict()
logdata['msg'] = str(exc)
log('E33', logdata)
result = 1;
finally:
logdata = dict()
logdata['dbus_pid'] = dbus_pid
logdata['dconf_pid'] = dconf_pid
log('D56', logdata)
if dbus_pid > 0:
os.kill(dbus_pid, signal.SIGHUP)
if dconf_pid > 0:
os.kill(dconf_pid, signal.SIGTERM)
if dbus_pid > 0:
os.kill(dbus_pid, signal.SIGTERM)
sys.exit(result)

View File

@@ -1,7 +1,7 @@
#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2019-2021 BaseALT Ltd.
# Copyright (C) 2019-2020 BaseALT Ltd.
#
# 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
@@ -55,3 +55,59 @@ def username_match_uid(username):
return False
def set_privileges(username, uid, gid, groups=list()):
'''
Set current process privileges
'''
try:
os.setegid(gid)
except Exception as exc:
print('setegid')
try:
os.seteuid(uid)
except Exception as exc:
print('seteuid')
#try:
# os.setgroups(groups)
#except Exception as exc:
# print('setgroups')
logdata = dict()
logdata['uid'] = uid
logdata['gid'] = gid
logdata['username'] = username
log('D37', logdata)
def with_privileges(username, func):
'''
Run supplied function with privileges for specified username.
'''
current_uid = os.getuid()
current_groups = os.getgrouplist('root', 0)
if not current_uid == 0:
raise Exception('Not enough permissions to drop privileges')
user_uid = pwd.getpwnam(username).pw_uid
user_gid = pwd.getpwnam(username).pw_gid
user_groups = os.getgrouplist(username, user_gid)
# Drop privileges
set_privileges(username, user_uid, user_gid, user_groups)
# We need to catch exception in order to be able to restore
# privileges later in this function
out = None
try:
out = func()
except Exception as exc:
log(str(exc))
# Restore privileges
set_privileges('root', current_uid, 0, current_groups)
return out

View File

@@ -17,10 +17,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import socket
import os
import pwd
import subprocess
import re
from pathlib import Path
from .samba import smbopts
@@ -29,10 +29,7 @@ def get_machine_name():
'''
Get localhost name looking like DC0$
'''
loadparm = smbopts()
result = loadparm.get_machine_name()
return result
return socket.gethostname().split('.', 1)[0].upper() + "$"
def is_machine_name(name):

View File

@@ -105,7 +105,7 @@ class smbcreds (smbopts):
for gpo in gpos:
# These setters are taken from libgpo/pygpo.c
# print(gpo.ds_path) # LDAP entry
ldata = dict({'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path': gpo.file_sys_path})
ldata = dict({'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name})
log('I2', ldata)
except Exception as exc:
@@ -192,7 +192,6 @@ def expand_windows_var(text, username=None):
variables['DesktopDir'] = xdg_get_desktop(username, variables['HOME'])
if username:
variables['LogonUser'] = username
variables['HOME'] = get_homedir(username)
variables['StartMenuDir'] = os.path.join(

View File

@@ -1,8 +1,8 @@
%define _unpackaged_files_terminate_build 1
Name: gpupdate
Version: 0.9.8
Release: alt0.dev1
Version: 0.8.0
Release: alt1
Summary: GPT applier
License: GPLv3+
@@ -13,18 +13,16 @@ BuildArch: noarch
Requires: control
BuildRequires: rpm-build-python3
BuildRequires: gettext-tools
BuildRequires: python-tools-i18n
Requires: python3-module-rpm
Requires: python3-module-dbus
Requires: oddjob-%name >= 0.2.0
Requires: libnss-role >= 0.5.0
Requires: local-policy >= 0.4.9
Requires: local-policy >= 0.4.0
Requires: pam-config >= 1.9.0
Requires: autofs
# This is needed by shortcuts_applier
Requires: desktop-file-utils
# This is needed for smb file cache support
Requires: python3-module-smbc >= 1.0.23-alt3
Source0: %name-%version.tar
@@ -42,7 +40,7 @@ cp -r gpoa \
%buildroot%python3_sitelibdir/
# Generate translations
msgfmt \
pymsgfmt \
-o %buildroot%python3_sitelibdir/gpoa/locale/ru_RU/LC_MESSAGES/gpoa.mo \
%buildroot%python3_sitelibdir/gpoa/locale/ru_RU/LC_MESSAGES/gpoa.po
@@ -50,7 +48,6 @@ mkdir -p \
%buildroot%_bindir/ \
%buildroot%_sbindir/ \
%buildroot%_cachedir/%name/ \
%buildroot%_cachedir/%{name}_file_cache/ \
%buildroot%_cachedir/%name/creds
ln -s %python3_sitelibdir/gpoa/gpoa \
@@ -68,20 +65,12 @@ mkdir -p %buildroot%_sysconfdir/%name
touch %buildroot%_sysconfdir/%name/environment
install -Dm0644 dist/%name.service %buildroot%_unitdir/%name.service
install -Dm0644 dist/%name-user.service %buildroot/usr/lib/systemd/user/%name-user.service
install -Dm0644 dist/%name.service %buildroot/usr/lib/systemd/user/%name-user.service
install -Dm0644 dist/system-policy-%name %buildroot%_sysconfdir/pam.d/system-policy-%name
install -Dm0644 dist/%name.ini %buildroot%_sysconfdir/%name/%name.ini
install -Dm0644 doc/gpoa.1 %buildroot/%_man1dir/gpoa.1
install -Dm0644 doc/gpupdate.1 %buildroot/%_man1dir/gpupdate.1
for i in gpupdate-localusers \
gpupdate-group-users \
gpupdate-system-uids
do
install -pD -m755 "dist/$i" \
"%buildroot%_sysconfdir/control.d/facilities/$i"
done
%preun
%preun_service gpupdate
@@ -90,13 +79,8 @@ done
# Remove storage in case we've lost compatibility between versions.
# The storage will be regenerated on GPOA start.
%define active_policy %_sysconfdir/local-policy/active
%triggerpostun -- %name < 0.9.6
%triggerpostun -- %name > 0.7
rm -f %_cachedir/%name/registry.sqlite
if test -L %active_policy; then
sed -i "s|^\s*local-policy\s*=.*|local-policy = $(readlink -f %active_policy)|" \
%_sysconfdir/%name/%name.ini
fi
%files
%_sbindir/gpoa
@@ -112,12 +96,10 @@ fi
%_man1dir/gpupdate.1.*
/usr/lib/systemd/user/%name-user.service
%dir %_sysconfdir/%name
%_sysconfdir/control.d/facilities/*
%config(noreplace) %_sysconfdir/%name/environment
%config(noreplace) %_sysconfdir/%name/%name.ini
%config(noreplace) %_sysconfdir/pam.d/system-policy-%name
%dir %attr(0700, root, root) %_cachedir/%name
%dir %attr(0755, root, root) %_cachedir/%{name}_file_cache
%dir %attr(0700, root, root) %_cachedir/%name/creds
%exclude %python3_sitelibdir/gpoa/.pylintrc
%exclude %python3_sitelibdir/gpoa/.prospector.yaml
@@ -125,68 +107,6 @@ fi
%exclude %python3_sitelibdir/gpoa/test
%changelog
* Wed Sep 29 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.7-alt1
- Fix regression with kestroy for user credential cache
- Update system-policy-gpupdate PAM-rules to ignore applying group policies
for local users and system users with uid less than 500
- Add control facilities to rule system-policy-gpupdate rules:
+ gpupdate-group-users
+ gpupdate-localusers
+ gpupdate-system-uids
* Mon Sep 20 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.6-alt1
- Add support changed GPO List Processing for '**DelVals.' value name
* Tue Sep 14 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.5-alt1
- Refix local policy path detection
- gpupdate-setup: revert settings to default when disabled
* Tue Sep 14 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.4-alt1
- Add improvement with new local-policy system-policy control
- Fix gpupdate-setup and user service installation regressions
- Set empty local policy and local backend by default
- Fix local policy path detection
* Mon Sep 06 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.3-alt1
- Use NetBIOS name for Kerberos authentification
- Add support actions (create, update, delete, replace) for Shortcuts
- Add support GSettings with locks feature
- Add support file cache for special GSettings policy:
Software\BaseALT\Policies\GSettings\org.mate.background.picture-filename
(requires python smbc module with use_kerberos option support)
* Wed Jul 28 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.2-alt1
- Fix Shortcuts applier double running in user context
- Add LogonUser variable to expand_windows_var() function
- Add support of path variable expansion to Shortcuts applier
* Sun Jul 18 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.1-alt1
- Fix GSettings applier user part support
- Add support additional firefox appliers
- Add new windows policies mapping capability feature ruled by:
Software\BaseALT\Policies\GPUpdate\WindowsPoliciesMapping
- Improve drop privileges mechanism with fork and dbus session
* Fri Jun 25 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.0-alt1
- Change policies.json location for Firefox
- Set GSettings, Chromium and Firefox appliers
not experimental and enabled by default
* Tue Dec 22 2020 Igor Chudov <nir@altlinux.org> 0.8.2-alt1
- Increased D-Bus timeouts on calls
- Minor logging fixes
* Wed Oct 07 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.1-alt3
- Fixed compatibility upgrade trigger condition
* Wed Oct 07 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.1-alt2
- Fixed compatibility upgrade trigger from 0.7 releases for update
active local-policy in new gpupdate.ini configuartion file
* Fri Sep 11 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.1-alt1
- Workaround for control names with special symbols
- Improved logging on Kerberos errors
* Fri Sep 04 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.0-alt1
- Improve gpo applier logging
- Add new configuration file /etc/gpupdate/gpupdate.ini