mirror of
https://github.com/altlinux/gpupdate.git
synced 2025-10-11 07:33:16 +03:00
Compare commits
105 Commits
0.8.1-alt2
...
gsettings_
Author | SHA1 | Date | |
---|---|---|---|
|
285e646986 | ||
|
94d039653a | ||
|
e6f19a2116 | ||
|
86c240b9df | ||
|
dae3cf2c6c | ||
|
4fe7d0a73e | ||
|
54d0c7c2cb | ||
|
954a5598fb | ||
|
ba4eb4bf28 | ||
|
aa10d5bbf9 | ||
|
f3062668fa | ||
|
046079d4c9 | ||
|
414a827eb8 | ||
|
8ce322d552 | ||
|
84d5122319 | ||
|
436eeb3760 | ||
|
4b9ef4335a | ||
|
929f9678ad | ||
|
03cada30cf | ||
|
8199cac510 | ||
|
e050889a07 | ||
|
1bf2bd053d | ||
|
950e132e2a | ||
|
82e255efc9 | ||
|
011a3fbed3 | ||
|
8eda2fbedb | ||
|
3f3fa5f7d9 | ||
|
0210f97e0d | ||
|
7e6dec6b3d | ||
|
5e4ed2f655 | ||
|
721ba96559 | ||
|
c83568cc70 | ||
|
15f99e0171 | ||
|
f84af7e0e8 | ||
7bd1131d5d
|
|||
5fb8e6ff74
|
|||
ce2797e5f1
|
|||
6d9417fb94
|
|||
301a77e90a
|
|||
|
274d9d8555 | ||
|
04f5f98681 | ||
|
9638e5fabb | ||
|
393fd25cdb | ||
|
23f862f9a5 | ||
a85fed7cff
|
|||
bbcb98bb94
|
|||
|
57f4f0678a | ||
|
306b8db34a | ||
|
7c8f9892b5 | ||
|
4c6a099529 | ||
|
e6c563e540 | ||
|
d67d472b1c | ||
|
ac8aba2212 | ||
|
39241bc625 | ||
|
9206a0b732 | ||
|
cdb7306d65 | ||
|
e0ac5f98ac | ||
|
96db0a2200 | ||
|
f3e4d463b9 | ||
|
0fded79484 | ||
|
a6e8f0b352 | ||
|
c8542fa477 | ||
|
31183afa60 | ||
|
17cd27b73e | ||
|
cf82fae5ec | ||
|
00abee6f7c | ||
|
760585f3fb | ||
|
28b4cd7d11 | ||
|
14153c6272 | ||
|
b2801eec07 | ||
|
ae414993e7 | ||
|
26e5126312 | ||
|
45a5df32c3 | ||
c842ce0e07
|
|||
|
2327952896 | ||
|
ee656f52f6 | ||
|
389bad4382 | ||
|
eca3fb43c6 | ||
|
8367fcba99 | ||
|
0480e88e69 | ||
110aee3970 | |||
|
f0f3152d86 | ||
|
4bd03585db | ||
|
e1a30a1436 | ||
|
4dbb290f73 | ||
|
7a588e9b68 | ||
|
c367b04f55 | ||
eaee242639
|
|||
8009467a87
|
|||
|
22c3a9f06e | ||
|
b213c83854 | ||
9480f41469
|
|||
a984e896a5
|
|||
702aead8b5
|
|||
01de884037
|
|||
fba30c9b0e
|
|||
|
2d92b5cb6e | ||
|
02632b1c88 | ||
|
d781a257e9 | ||
|
4b80dc13cf | ||
|
e45cd1fd18 | ||
|
ca01b20464 | ||
|
590fd8c464 | ||
|
d967c0786d | ||
536d989497
|
19
dist/gpupdate-group-users
vendored
Executable file
19
dist/gpupdate-group-users
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/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" "$*"
|
19
dist/gpupdate-localusers
vendored
Executable file
19
dist/gpupdate-localusers
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/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" "$*"
|
19
dist/gpupdate-system-uids
vendored
Executable file
19
dist/gpupdate-system-uids
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/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
4
dist/gpupdate.ini
vendored
@@ -1,4 +1,4 @@
|
||||
[gpoa]
|
||||
backend = samba
|
||||
local-policy = auto
|
||||
backend = local
|
||||
local-policy = default
|
||||
|
||||
|
2
dist/gpupdate.service
vendored
2
dist/gpupdate.service
vendored
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Group policy update for machine
|
||||
After=sssd.service
|
||||
After=syslog.target network-online.target sssd.service
|
||||
|
||||
[Service]
|
||||
Environment="PATH=/bin:/sbin:/usr/bin:/usr/sbin"
|
||||
|
8
dist/system-policy-gpupdate
vendored
8
dist/system-policy-gpupdate
vendored
@@ -1,4 +1,12 @@
|
||||
#%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
|
||||
|
@@ -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})
|
||||
ldata = dict({'domain': domain, "username": username, 'is_machine': is_machine})
|
||||
log('D9', ldata)
|
||||
try:
|
||||
back = samba_backend(sc, username, domain, is_machine)
|
||||
|
@@ -40,6 +40,8 @@ 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()
|
||||
@@ -124,7 +126,7 @@ class samba_backend(applier_backend):
|
||||
def _get_gpts(self, username, sid):
|
||||
gpts = list()
|
||||
|
||||
log('D45')
|
||||
log('D45', {'username': username, 'sid': sid})
|
||||
# util.windows.smbcreds
|
||||
gpos = self.sambacreds.update_gpos(username)
|
||||
log('D46')
|
||||
|
@@ -32,6 +32,17 @@ 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)
|
||||
|
118
gpoa/frontend/appliers/envvar.py
Normal file
118
gpoa/frontend/appliers/envvar.py
Normal file
@@ -0,0 +1,118 @@
|
||||
#
|
||||
# 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)
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# GPOA - GPO Applier for Linux
|
||||
#
|
||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
||||
# Copyright (C) 2019-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
|
||||
@@ -24,64 +24,112 @@ from gi.repository import Gio, GLib
|
||||
from util.logging import slogm
|
||||
|
||||
class system_gsetting:
|
||||
__global_schema = '/usr/share/glib-2.0/schemas'
|
||||
|
||||
def __init__(self, schema, path, value, override_priority='0'):
|
||||
def __init__(self, schema, path, value, lock, helper_function=None):
|
||||
self.schema = schema
|
||||
self.path = path
|
||||
self.value = value
|
||||
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)
|
||||
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))
|
||||
|
||||
def apply(self):
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
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)
|
||||
|
||||
with open(self.file_path, 'w') as f:
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
def glib_map(value, glib_type):
|
||||
result_value = value
|
||||
|
||||
if glib_type == 'i':
|
||||
if glib_type == 'i' or glib_type == 'b' or glib_type == 'q':
|
||||
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):
|
||||
logging.debug('Setting GSettings key {} (in {}) to {}'.format(self.path, self.schema, self.value))
|
||||
if self.helper_function:
|
||||
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)
|
||||
settings = Gio.Settings(schema=self.schema)
|
||||
# Update result with helper function
|
||||
value = 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)
|
||||
# Set the value
|
||||
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())
|
||||
|
||||
settings.set_value(self.path, result)
|
||||
settings.sync()
|
||||
|
@@ -30,8 +30,8 @@ from util.util import is_machine_name
|
||||
|
||||
class chromium_applier(applier_frontend):
|
||||
__module_name = 'ChromiumApplier'
|
||||
__module_enabled = False
|
||||
__module_experimental = True
|
||||
__module_enabled = True
|
||||
__module_experimental = False
|
||||
__registry_branch = 'Software\\Policies\\Google\\Chrome'
|
||||
__managed_policies_path = '/etc/chromium/policies/managed'
|
||||
__recommended_policies_path = '/etc/chromium/policies/recommended'
|
||||
|
69
gpoa/frontend/envvar_applier.py
Normal file
69
gpoa/frontend/envvar_applier.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#
|
||||
# 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'))
|
||||
|
@@ -39,10 +39,11 @@ from util.util import is_machine_name
|
||||
|
||||
class firefox_applier(applier_frontend):
|
||||
__module_name = 'FirefoxApplier'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
__registry_branch = 'Software\\Policies\\Mozilla\\Firefox'
|
||||
__firefox_installdir = '/usr/lib64/firefox/distribution'
|
||||
__firefox_installdir1 = '/usr/lib64/firefox/distribution'
|
||||
__firefox_installdir2 = '/etc/firefox/policies'
|
||||
__user_settings_dir = '.mozilla/firefox'
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
@@ -114,28 +115,78 @@ class firefox_applier(applier_frontend):
|
||||
return homepage
|
||||
return None
|
||||
|
||||
def get_block_about_config(self):
|
||||
def get_boolean_config(self, name):
|
||||
'''
|
||||
Query BlockAboutConfig boolean property from the storage.
|
||||
Query boolean property from the storage.
|
||||
'''
|
||||
response = self.get_hklm_string_entry('BlockAboutConfig')
|
||||
response = self.get_hklm_string_entry(name)
|
||||
if response:
|
||||
if response.data.lower() in ['0', 'false', False, None, 'None']:
|
||||
data = response.data if isinstance(response.data, int) else str(response.data).lower()
|
||||
if data in ['0', 'false', None, 'none', 0]:
|
||||
return False
|
||||
return True
|
||||
if data in ['1', 'true', 1]:
|
||||
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_policy('BlockAboutConfig', self.get_block_about_config())
|
||||
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')
|
||||
|
||||
destfile = os.path.join(self.__firefox_installdir, 'policies.json')
|
||||
destfile = os.path.join(self.__firefox_installdir1, 'policies.json')
|
||||
|
||||
os.makedirs(self.__firefox_installdir, exist_ok=True)
|
||||
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)
|
||||
with open(destfile, 'w') as f:
|
||||
json.dump(self.policies_json, f)
|
||||
logging.debug(slogm('Wrote Firefox preferences to {}'.format(destfile)))
|
||||
|
@@ -17,6 +17,7 @@
|
||||
# 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 (
|
||||
@@ -46,14 +47,18 @@ 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):
|
||||
@@ -79,6 +84,18 @@ 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
|
||||
@@ -91,6 +108,7 @@ 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)
|
||||
@@ -99,19 +117,20 @@ 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.machine_appliers['gsettings'] = gsettings_applier(self.storage, self.file_cache)
|
||||
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.sid, self.username)
|
||||
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.file_cache, self.sid, self.username)
|
||||
try:
|
||||
self.user_appliers['cifs'] = cifs_applier_user(self.storage, self.sid, self.username)
|
||||
except Exception as exc:
|
||||
@@ -121,6 +140,7 @@ 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):
|
||||
'''
|
||||
@@ -130,6 +150,7 @@ class frontend_manager:
|
||||
log('E13')
|
||||
return
|
||||
log('D16')
|
||||
|
||||
for applier_name, applier_object in self.machine_appliers.items():
|
||||
try:
|
||||
applier_object.apply()
|
||||
@@ -153,13 +174,13 @@ class frontend_manager:
|
||||
logdata['exception'] = str(exc)
|
||||
log('E19', 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)
|
||||
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)
|
||||
else:
|
||||
for applier_name, applier_object in self.user_appliers.items():
|
||||
try:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# GPOA - GPO Applier for Linux
|
||||
#
|
||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
||||
# Copyright (C) 2019-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
|
||||
@@ -20,6 +20,7 @@ import logging
|
||||
import os
|
||||
import pwd
|
||||
import subprocess
|
||||
import urllib.parse
|
||||
|
||||
from gi.repository import (
|
||||
Gio
|
||||
@@ -29,50 +30,108 @@ from gi.repository import (
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
, check_windows_mapping_enabled
|
||||
)
|
||||
from .appliers.gsettings import (
|
||||
system_gsetting,
|
||||
system_gsettings,
|
||||
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 = True
|
||||
__module_enabled = False
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
|
||||
__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'
|
||||
__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):
|
||||
def __init__(self, storage, file_cache):
|
||||
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 = list()
|
||||
self.override_file = os.path.join(self.__global_schema, '0_policy.gschema.override')
|
||||
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.__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]
|
||||
self.gsettings.append(system_gsetting(schema, path, setting.data))
|
||||
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)
|
||||
|
||||
# Create GSettings policy with highest available priority
|
||||
for gsetting in self.gsettings:
|
||||
gsetting.apply()
|
||||
self.gsettings.apply()
|
||||
|
||||
# Recompile GSettings schemas with overrides
|
||||
try:
|
||||
@@ -80,9 +139,16 @@ 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'))
|
||||
|
||||
@@ -119,18 +185,22 @@ class GSettingsMapping:
|
||||
|
||||
class gsettings_applier_user(applier_frontend):
|
||||
__module_name = 'GSettingsApplierUser'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
|
||||
__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'
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
def __init__(self, storage, file_cache, 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()
|
||||
@@ -166,6 +236,20 @@ 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]
|
||||
@@ -174,24 +258,33 @@ 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)
|
||||
|
||||
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 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'))
|
||||
|
||||
# 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('Applying setting {}/{}'.format(gsetting.schema, gsetting.path))
|
||||
logging.debug(slogm('Applying user setting {}.{} to {}'.format(gsetting.schema,
|
||||
gsetting.path,
|
||||
gsetting.value)))
|
||||
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'))
|
||||
@@ -200,8 +293,14 @@ 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):
|
||||
'''
|
||||
Not implemented because there is no point of doing so.
|
||||
'''
|
||||
pass
|
||||
# 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)))
|
||||
|
||||
|
@@ -31,7 +31,7 @@ from util.util import (
|
||||
homedir_exists
|
||||
)
|
||||
|
||||
def storage_get_shortcuts(storage, sid):
|
||||
def storage_get_shortcuts(storage, sid, username=None):
|
||||
'''
|
||||
Query storage for shortcuts' rows for specified SID.
|
||||
'''
|
||||
@@ -40,13 +40,15 @@ def storage_get_shortcuts(storage, sid):
|
||||
|
||||
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 write_shortcut(shortcut, username=None):
|
||||
def apply_shortcut(shortcut, username=None):
|
||||
'''
|
||||
Write the single shortcut file to the disk.
|
||||
Apply the single shortcut file to the disk.
|
||||
|
||||
:username: None means working with machine variables and paths
|
||||
'''
|
||||
@@ -64,22 +66,22 @@ def write_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 create link {}'.format(username, dest_abspath)))
|
||||
logging.warning(slogm('No home directory exists for user {}: will not apply 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 writing shortcut to file with \'%\': {}'.format(dest_abspath)))
|
||||
logging.debug(slogm('Fail for applying shortcut to file with \'%\': {}'.format(dest_abspath)))
|
||||
return None
|
||||
|
||||
if not dest_abspath.startswith('/'):
|
||||
logging.debug(slogm('Fail for writing shortcut to not absolute path \'%\': {}'.format(dest_abspath)))
|
||||
logging.debug(slogm('Fail for applying shortcut to not absolute path \'%\': {}'.format(dest_abspath)))
|
||||
return None
|
||||
|
||||
logging.debug(slogm('Writing shortcut file to {}'.format(dest_abspath)))
|
||||
shortcut.write_desktop(dest_abspath)
|
||||
logging.debug(slogm('Applying shortcut file to {} with action {}'.format(dest_abspath, shortcut.action)))
|
||||
shortcut.apply_desktop(dest_abspath)
|
||||
|
||||
class shortcut_applier(applier_frontend):
|
||||
__module_name = 'ShortcutsApplier'
|
||||
@@ -98,7 +100,7 @@ class shortcut_applier(applier_frontend):
|
||||
shortcuts = storage_get_shortcuts(self.storage, self.storage.get_info('machine_sid'))
|
||||
if shortcuts:
|
||||
for sc in shortcuts:
|
||||
write_shortcut(sc)
|
||||
apply_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
|
||||
@@ -125,27 +127,29 @@ class shortcut_applier_user(applier_frontend):
|
||||
self.sid = sid
|
||||
self.username = username
|
||||
|
||||
def run(self):
|
||||
shortcuts = storage_get_shortcuts(self.storage, self.sid)
|
||||
def run(self, in_usercontext):
|
||||
shortcuts = storage_get_shortcuts(self.storage, self.sid, self.username)
|
||||
|
||||
if shortcuts:
|
||||
for sc in shortcuts:
|
||||
if sc.is_usercontext():
|
||||
write_shortcut(sc, self.username)
|
||||
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)
|
||||
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()
|
||||
self.run(True)
|
||||
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()
|
||||
self.run(False)
|
||||
else:
|
||||
logging.debug(slogm('Shortcut applier for user in administrator context will not be started'))
|
||||
|
||||
|
37
gpoa/gpoa
37
gpoa/gpoa
@@ -73,25 +73,44 @@ class gpoa_controller:
|
||||
def __init__(self):
|
||||
self.__args = parse_arguments()
|
||||
self.is_machine = False
|
||||
if not self.__args.user:
|
||||
user = get_machine_name()
|
||||
self.is_machine = True
|
||||
self.noupdate = self.__args.noupdate
|
||||
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'] = uname
|
||||
logdata['uid'] = uid
|
||||
log('D1', logdata)
|
||||
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)
|
||||
|
||||
if not is_root():
|
||||
self.username = uname
|
||||
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})
|
||||
else:
|
||||
self.username = determine_username(self.__args.user)
|
||||
log('D60', {'username': self.username})
|
||||
|
||||
def run(self):
|
||||
'''
|
||||
@@ -113,7 +132,7 @@ class gpoa_controller:
|
||||
if self.__args.nodomain:
|
||||
nodomain = True
|
||||
|
||||
if not self.__args.noupdate:
|
||||
if not self.noupdate:
|
||||
if is_root():
|
||||
back = None
|
||||
try:
|
||||
|
@@ -18,21 +18,48 @@
|
||||
|
||||
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):
|
||||
var_obj = envvar()
|
||||
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')))
|
||||
|
||||
variables.append(var_obj)
|
||||
|
||||
return variables
|
||||
|
||||
def merge_envvars(storage, sid, envvars_objects, policy_name):
|
||||
for envvar in envvar_objects:
|
||||
pass
|
||||
def merge_envvars(storage, sid, envvar_objects, policy_name):
|
||||
for envv in envvar_objects:
|
||||
storage.add_envvar(sid, envv, policy_name)
|
||||
|
||||
class envvar:
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.action = FileAction.CREATE
|
||||
|
||||
def set_action(self, action):
|
||||
self.action = action
|
||||
|
||||
|
@@ -68,7 +68,7 @@ from .tasks import (
|
||||
import util
|
||||
import util.preg
|
||||
from util.paths import (
|
||||
default_policy_path,
|
||||
local_policy_path,
|
||||
cache_dir,
|
||||
local_policy_cache
|
||||
)
|
||||
@@ -326,7 +326,7 @@ def lp2gpt():
|
||||
'''
|
||||
Convert local-policy to full-featured GPT.
|
||||
'''
|
||||
lppath = os.path.join(default_policy_path(), 'Machine/Registry.pol.xml')
|
||||
lppath = os.path.join(local_policy_path(), 'Machine/Registry.pol.xml')
|
||||
|
||||
# Load settings from XML PolFile
|
||||
polparser = GPPolParser()
|
||||
|
@@ -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'), target_type)
|
||||
sc = shortcut(dest, path, arguments, link.get('name'), props.get('action'), 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'], link_type)
|
||||
sc = shortcut(json_obj['dest'], json_obj['path'], json_obj['arguments'], json_obj['name'], json_obj['action'], 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, ttype=TargetType.FILESYSTEM):
|
||||
def __init__(self, dest, path, arguments, name=None, action=None, ttype=TargetType.FILESYSTEM):
|
||||
'''
|
||||
:param dest: Path to resulting file on file system
|
||||
:param path: Path where the link should point to
|
||||
@@ -121,8 +121,10 @@ 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()
|
||||
@@ -166,6 +168,12 @@ 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
|
||||
|
||||
@@ -181,6 +189,7 @@ 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:
|
||||
@@ -190,39 +199,78 @@ class shortcut:
|
||||
|
||||
return json.dumps(result.content)
|
||||
|
||||
def desktop(self):
|
||||
def desktop(self, dest=None):
|
||||
'''
|
||||
Returns desktop file object which may be written to disk.
|
||||
'''
|
||||
self.desktop_file = DesktopEntry()
|
||||
self.desktop_file.addGroup('Desktop Entry')
|
||||
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()
|
||||
|
||||
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', self.path)
|
||||
self.desktop_file.set('URL', desktop_path)
|
||||
else:
|
||||
self.desktop_file.set('Terminal', 'false')
|
||||
self.desktop_file.set('Exec', '{} {}'.format(self.path, self.arguments))
|
||||
self.desktop_file.set('Exec', '{} {}'.format(desktop_path, self.arguments))
|
||||
|
||||
if self.icon:
|
||||
self.desktop_file.set('Icon', self.icon)
|
||||
|
||||
return self.desktop_file
|
||||
|
||||
def write_desktop(self, dest):
|
||||
def _write_desktop(self, dest, create_only=False, read_firstly=False):
|
||||
'''
|
||||
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)
|
||||
|
@@ -23,8 +23,6 @@ import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
import re
|
||||
|
||||
from util.util import (
|
||||
runcmd
|
||||
, get_backends
|
||||
@@ -33,17 +31,14 @@ 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():
|
||||
@@ -194,13 +189,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']
|
||||
@@ -208,18 +203,17 @@ 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)
|
||||
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_local_policy_template(target_policy_name)
|
||||
config.set_backend(backend_type)
|
||||
|
||||
# Enable oddjobd_gpupdate in PAM config
|
||||
|
@@ -289,6 +289,9 @@ msgstr "Пропускаем специальный ключ удаления в
|
||||
msgid "Read domain name from configuration file"
|
||||
msgstr "Имя контроллера домена для репликации прочитано из файла конфигурации"
|
||||
|
||||
msgid "Saving information about environment variables"
|
||||
msgstr "Сохранение информации о переменных окружения"
|
||||
|
||||
msgid "Unknown debug code"
|
||||
msgstr "Неизвестный отладочный код"
|
||||
|
||||
|
@@ -57,6 +57,15 @@ 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')
|
||||
|
||||
@@ -114,6 +123,20 @@ 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')
|
||||
|
||||
|
97
gpoa/storage/fs_file_cache.py
Normal file
97
gpoa/storage/fs_file_cache.py
Normal file
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# 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)
|
||||
|
@@ -22,7 +22,9 @@ class samba_preg(object):
|
||||
'''
|
||||
def __init__(self, preg_obj, policy_name):
|
||||
self.policy_name = policy_name
|
||||
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
|
||||
self.keyname = preg_obj.keyname
|
||||
self.valuename = preg_obj.valuename
|
||||
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
|
||||
self.type = preg_obj.type
|
||||
self.data = preg_obj.data
|
||||
|
||||
@@ -41,7 +43,9 @@ class samba_hkcu_preg(object):
|
||||
def __init__(self, sid, preg_obj, policy_name):
|
||||
self.sid = sid
|
||||
self.policy_name = policy_name
|
||||
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
|
||||
self.keyname = preg_obj.keyname
|
||||
self.valuename = preg_obj.valuename
|
||||
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
|
||||
self.type = preg_obj.type
|
||||
self.data = preg_obj.data
|
||||
|
||||
@@ -148,3 +152,25 @@ 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
|
||||
|
||||
|
@@ -43,6 +43,7 @@ from .record_types import (
|
||||
, printer_entry
|
||||
, drive_entry
|
||||
, folder_entry
|
||||
, envvar_entry
|
||||
)
|
||||
|
||||
class sqlite_registry(registry):
|
||||
@@ -65,7 +66,10 @@ class sqlite_registry(registry):
|
||||
'HKLM'
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('hive_key', String(65536), unique=True)
|
||||
, Column('hive_key', String(65536, collation='NOCASE'),
|
||||
unique=True)
|
||||
, Column('keyname', String(collation='NOCASE'))
|
||||
, Column('valuename', String(collation='NOCASE'))
|
||||
, Column('policy_name', String)
|
||||
, Column('type', Integer)
|
||||
, Column('data', String)
|
||||
@@ -75,7 +79,9 @@ class sqlite_registry(registry):
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('sid', String)
|
||||
, Column('hive_key', String(65536))
|
||||
, Column('hive_key', String(65536, collation='NOCASE'))
|
||||
, Column('keyname', String(collation='NOCASE'))
|
||||
, Column('valuename', String(collation='NOCASE'))
|
||||
, Column('policy_name', String)
|
||||
, Column('type', Integer)
|
||||
, Column('data', String)
|
||||
@@ -126,6 +132,17 @@ 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()
|
||||
@@ -137,6 +154,7 @@ 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')
|
||||
@@ -226,16 +244,52 @@ 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.hive_key.rpartition('\\')[2].startswith('**'):
|
||||
if not pentry.valuename.startswith('**'):
|
||||
self._hklm_upsert(pentry)
|
||||
else:
|
||||
logdata = dict({'key': pentry.hive_key})
|
||||
log('D27', logdata)
|
||||
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)
|
||||
|
||||
def add_hkcu_entry(self, preg_entry, sid, policy_name):
|
||||
'''
|
||||
@@ -243,11 +297,14 @@ 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.hive_key.rpartition('\\')[2].startswith('**'):
|
||||
if not hkcu_pentry.valuename.startswith('**'):
|
||||
log('D26', logdata)
|
||||
self._hkcu_upsert(hkcu_pentry)
|
||||
else:
|
||||
log('D51', logdata)
|
||||
if hkcu_pentry.valuename.lower() == '**delvals.':
|
||||
self._delete_hkcu_keyname(hkcu_pentry.keyname, sid)
|
||||
else:
|
||||
log('D51', logdata)
|
||||
|
||||
def add_shortcut(self, sid, sc_obj, policy_name):
|
||||
'''
|
||||
@@ -294,6 +351,21 @@ 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
|
||||
@@ -306,6 +378,7 @@ class sqlite_registry(registry):
|
||||
.db_session
|
||||
.query(row_object)
|
||||
.filter(row_object.sid == sid)
|
||||
.order_by(row_object.id)
|
||||
.all())
|
||||
return res
|
||||
|
||||
@@ -321,6 +394,9 @@ 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
|
||||
|
@@ -45,7 +45,7 @@ class GPConfig:
|
||||
|
||||
return 'samba'
|
||||
|
||||
def set_backend(self, backend_name):
|
||||
def set_backend(self, backend_name='local'):
|
||||
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):
|
||||
def set_local_policy_template(self, template_name='default'):
|
||||
self.full_config['gpoa']['local-policy'] = template_name
|
||||
self.write_config()
|
||||
|
||||
|
@@ -29,22 +29,38 @@ 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
|
||||
system_bus = dbus.SystemBus()
|
||||
obj = system_bus.get_object(self._bus_name, self._object_path)
|
||||
self.interface = dbus.Interface(obj, self._bus_name)
|
||||
self.system_bus = dbus.SystemBus()
|
||||
|
||||
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.interface.gpupdatefor(dbus.String(self.username))
|
||||
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)
|
||||
print_dbus_result(result)
|
||||
except dbus.exceptions.DBusException as exc:
|
||||
logdata = dict()
|
||||
@@ -53,20 +69,36 @@ class dbus_runner:
|
||||
raise exc
|
||||
else:
|
||||
try:
|
||||
result = self.interface.gpupdate()
|
||||
result = self.system_bus.call_blocking(self._bus_name,
|
||||
self._object_path,
|
||||
self._interface_name,
|
||||
'gpupdate',
|
||||
None,
|
||||
(),
|
||||
timeout=self._synchronous_timeout)
|
||||
print_dbus_result(result)
|
||||
except dbus.exceptions.DBusException as exc:
|
||||
log('E21')
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E21', logdata)
|
||||
raise exc
|
||||
else:
|
||||
log('D11')
|
||||
try:
|
||||
result = self.interface.gpupdate_computer()
|
||||
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)
|
||||
print_dbus_result(result)
|
||||
except dbus.exceptions.DBusException as exc:
|
||||
log('E22')
|
||||
print(exc)
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E22', logdata)
|
||||
raise exc
|
||||
#self.interface.Quit()
|
||||
|
||||
|
||||
def start_gpupdate_user():
|
||||
@@ -138,3 +170,27 @@ 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)
|
||||
|
@@ -39,3 +39,10 @@ def geterr():
|
||||
|
||||
return traceinfo
|
||||
|
||||
class NotUNCPathError(Exception):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def __str__(self):
|
||||
return self.path
|
||||
|
||||
|
@@ -21,15 +21,19 @@ 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', host]
|
||||
kinit_cmd = ['kinit', '-k', with_realm]
|
||||
if cache_name:
|
||||
kinit_cmd.extend(['-c', cache_name])
|
||||
proc = subprocess.Popen(kinit_cmd)
|
||||
@@ -55,8 +59,9 @@ def machine_kdestroy(cache_name=None):
|
||||
if cache_name:
|
||||
kdestroy_cmd.extend(['-c', cache_name])
|
||||
|
||||
proc = subprocess.Popen(kdestroy_cmd, stderr=subprocess.DEVNULL)
|
||||
proc.wait()
|
||||
if cache_name or 'KRB5CCNAME' in os.environ:
|
||||
proc = subprocess.Popen(kdestroy_cmd, stderr=subprocess.DEVNULL)
|
||||
proc.wait()
|
||||
|
||||
if cache_name and os.path.exists(cache_name):
|
||||
os.unlink(cache_name)
|
||||
|
@@ -1,7 +1,8 @@
|
||||
#
|
||||
# GPOA - GPO Applier for Linux
|
||||
#
|
||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
||||
# Copyright (C) 2019-2021 BaseALT Ltd. <org@basealt.ru>
|
||||
# Copyright (C) 2019-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
|
||||
@@ -18,25 +19,38 @@
|
||||
|
||||
import pathlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from .config import GPConfig
|
||||
from .exceptions import NotUNCPathError
|
||||
|
||||
|
||||
def default_policy_path():
|
||||
def get_custom_policy_dir():
|
||||
'''
|
||||
Returns path pointing to Default Policy directory.
|
||||
Returns path pointing to Custom Policy directory.
|
||||
'''
|
||||
local_policy_default = '/usr/share/local-policy/default'
|
||||
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'
|
||||
|
||||
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(config.get_local_policy_template()):
|
||||
result_path = pathlib.Path(config.get_local_policy_template())
|
||||
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)
|
||||
|
||||
return pathlib.Path(result_path)
|
||||
|
||||
|
||||
def cache_dir():
|
||||
'''
|
||||
Returns path pointing to gpupdate's cache directory
|
||||
@@ -48,6 +62,16 @@ 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():
|
||||
'''
|
||||
@@ -61,3 +85,46 @@ 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()
|
||||
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
|
||||
import optparse
|
||||
import socket
|
||||
from samba import getopt as options
|
||||
|
||||
|
||||
@@ -28,11 +29,32 @@ 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)
|
||||
|
141
gpoa/util/system.py
Normal file
141
gpoa/util/system.py
Normal file
@@ -0,0 +1,141 @@
|
||||
#
|
||||
# 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)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# GPOA - GPO Applier for Linux
|
||||
#
|
||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
||||
# Copyright (C) 2019-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
|
||||
@@ -55,59 +55,3 @@ 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
|
||||
|
||||
|
@@ -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,7 +29,10 @@ def get_machine_name():
|
||||
'''
|
||||
Get localhost name looking like DC0$
|
||||
'''
|
||||
return socket.gethostname().split('.', 1)[0].upper() + "$"
|
||||
loadparm = smbopts()
|
||||
result = loadparm.get_machine_name()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def is_machine_name(name):
|
||||
|
@@ -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})
|
||||
ldata = dict({'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path': gpo.file_sys_path})
|
||||
log('I2', ldata)
|
||||
|
||||
except Exception as exc:
|
||||
@@ -192,6 +192,7 @@ 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(
|
||||
|
@@ -1,8 +1,8 @@
|
||||
%define _unpackaged_files_terminate_build 1
|
||||
|
||||
Name: gpupdate
|
||||
Version: 0.8.1
|
||||
Release: alt2
|
||||
Version: 0.9.8
|
||||
Release: alt0.dev1
|
||||
|
||||
Summary: GPT applier
|
||||
License: GPLv3+
|
||||
@@ -13,16 +13,18 @@ BuildArch: noarch
|
||||
Requires: control
|
||||
|
||||
BuildRequires: rpm-build-python3
|
||||
BuildRequires: python-tools-i18n
|
||||
BuildRequires: gettext-tools
|
||||
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.0
|
||||
Requires: local-policy >= 0.4.9
|
||||
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
|
||||
|
||||
@@ -40,7 +42,7 @@ cp -r gpoa \
|
||||
%buildroot%python3_sitelibdir/
|
||||
|
||||
# Generate translations
|
||||
pymsgfmt \
|
||||
msgfmt \
|
||||
-o %buildroot%python3_sitelibdir/gpoa/locale/ru_RU/LC_MESSAGES/gpoa.mo \
|
||||
%buildroot%python3_sitelibdir/gpoa/locale/ru_RU/LC_MESSAGES/gpoa.po
|
||||
|
||||
@@ -48,6 +50,7 @@ mkdir -p \
|
||||
%buildroot%_bindir/ \
|
||||
%buildroot%_sbindir/ \
|
||||
%buildroot%_cachedir/%name/ \
|
||||
%buildroot%_cachedir/%{name}_file_cache/ \
|
||||
%buildroot%_cachedir/%name/creds
|
||||
|
||||
ln -s %python3_sitelibdir/gpoa/gpoa \
|
||||
@@ -65,12 +68,20 @@ mkdir -p %buildroot%_sysconfdir/%name
|
||||
touch %buildroot%_sysconfdir/%name/environment
|
||||
|
||||
install -Dm0644 dist/%name.service %buildroot%_unitdir/%name.service
|
||||
install -Dm0644 dist/%name.service %buildroot/usr/lib/systemd/user/%name-user.service
|
||||
install -Dm0644 dist/%name-user.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
|
||||
|
||||
@@ -80,7 +91,7 @@ install -Dm0644 doc/gpupdate.1 %buildroot/%_man1dir/gpupdate.1
|
||||
# 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.7
|
||||
%triggerpostun -- %name < 0.9.6
|
||||
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)|" \
|
||||
@@ -101,10 +112,12 @@ 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
|
||||
@@ -112,6 +125,60 @@ 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
|
||||
|
Reference in New Issue
Block a user