mirror of
https://github.com/altlinux/gpupdate.git
synced 2025-08-24 13:49:29 +03:00
Compare commits
490 Commits
binary_mod
...
0.9.9-alt1
Author | SHA1 | Date | |
---|---|---|---|
1991f143be | |||
08b5b2262c | |||
b1b08f2ab0 | |||
382fa292bd | |||
ca346cc115 | |||
c8727b0215 | |||
be2aa6889f | |||
edd2a5e7c4 | |||
0165167881 | |||
b2c7144a0d | |||
2f32c71902 | |||
d871e7d717 | |||
db31db0143 | |||
ab74c4e878 | |||
75768fdb48 | |||
72ad8dd9c4 | |||
0f3b0cc265 | |||
b253ce7140 | |||
df37fd051e | |||
776281c0b3 | |||
c5cc32688f | |||
8183fe4f22 | |||
590464f230 | |||
f49a7c7671 | |||
0d2ee48434 | |||
f8c8f89327 | |||
99cdb4a043 | |||
dff638bc57 | |||
693a1d3a08 | |||
653d8c5f19 | |||
382c425b97 | |||
1f48a203ff | |||
0a93d16e04 | |||
d392a01046 | |||
5a39275d1f | |||
90699f8fc1 | |||
f22fc38972 | |||
11a4893e90 | |||
20c651746c | |||
0e9334f3e4 | |||
88887f7111 | |||
c7bafc4d21 | |||
d00e99e5d4 | |||
a45483c550 | |||
e7548bcbc8 | |||
cab3811627 | |||
382a3e2bd2 | |||
9571f46e73 | |||
57dda04216 | |||
431b18e177 | |||
17d35b8f4d | |||
5d34a51e07 | |||
d26eaca24f | |||
9357d5006f | |||
692a950d4a | |||
12ee1d7a8b | |||
87c5e1e75f | |||
7f7064ddd6 | |||
9eb81ea32f | |||
78ff997987 | |||
56aa8078c4 | |||
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 | |||
b426ab5b36 | |||
bb54d3e01e | |||
e2c386b6d0 | |||
66c2303069
|
|||
e9cf33855c | |||
3eae206e6f
|
|||
536d989497
|
|||
6cf0a7b136 | |||
48e484937e | |||
150ed3d29f | |||
cff5ed1932 | |||
b6a41c3843 | |||
564d324b53
|
|||
41de03e6e8
|
|||
828f6099da
|
|||
0a5af77655
|
|||
4abea3cc32
|
|||
2597ae46cd
|
|||
fdec0dc765
|
|||
8f96b1be85
|
|||
1f6b7d4cb7
|
|||
adea356f1b | |||
75bb669ce7
|
|||
e7967e5cc4 | |||
d99ec2d890
|
|||
91b5c7f858 | |||
cb5101fc48
|
|||
a9f9689032 | |||
508fbe4dd5
|
|||
ea379c3181
|
|||
ccfba4b592 | |||
3dcebe25e2 | |||
37416ab77d
|
|||
22cd8844ba
|
|||
c8cfd51915
|
|||
1a4ae69cdf
|
|||
e1170d2096
|
|||
e44b3c96ec
|
|||
7e7450d52a
|
|||
e4113e971f
|
|||
57607d1311
|
|||
103110bdef
|
|||
126f2ffd7d
|
|||
914a20b244
|
|||
55dbdfc246
|
|||
b5059620a7
|
|||
af1e756037
|
|||
e1bc549a51
|
|||
43b1ea392f | |||
b29c13b8dd | |||
035950f234
|
|||
44545943ac
|
|||
14bc3f3f96
|
|||
e01e6f511a
|
|||
79927743ca
|
|||
f9cef07151
|
|||
53b94246a8
|
|||
3f1edd2791
|
|||
165f0defa7
|
|||
0808128b5f
|
|||
c40ae95a08
|
|||
a1bd67f7d2 | |||
30b942d32d | |||
84b7977351 | |||
4c37b15dde
|
|||
3dfb37313d
|
|||
81b9e88d91
|
|||
2bcfd75a5b
|
|||
53ad06b787
|
|||
505f0152f7
|
|||
0ba273ee0e
|
|||
ef55a63c7e
|
|||
6ebbf5f59d
|
|||
dff80ba14d
|
|||
6c5c22a932
|
|||
c67487b56c
|
|||
abe943f399
|
|||
353082a6bb
|
|||
e37796e5b4 | |||
3afe965b75
|
|||
aa93d95012
|
|||
0bc7144da9
|
|||
ffbef2d18f
|
|||
6429b3c290
|
|||
05320f86b3
|
|||
b10b965287
|
|||
716862201e
|
|||
c701565325 | |||
09553a1c9e | |||
674f07569e
|
|||
3d39e1f010
|
|||
c45fa3e552
|
|||
8135b21cd3
|
|||
758421b611
|
|||
25b1774d49
|
|||
70ef7ef384
|
|||
3517f3a67b
|
|||
2f6a287727
|
|||
586528f8e9
|
|||
68910679e6
|
|||
8cb1278f04
|
|||
e12042fb2c
|
|||
8d351dde63
|
|||
ae3672dbdc
|
|||
bc8b6369c2
|
|||
99c7a305de
|
|||
4f6f17024e
|
|||
0d7a1e9740
|
|||
f203a48bee
|
|||
df22fe21f5
|
|||
c4d89921aa
|
|||
0851f96c6f
|
|||
23de5b63f6
|
|||
6f6612862d
|
|||
70886bd605
|
|||
d3caf73dac
|
|||
551020f074
|
|||
1bf4687d41
|
|||
d9afe438bc
|
|||
067f1831ac
|
|||
fcdf3af1de
|
|||
d776cbbc7a
|
|||
58a609f0bf
|
|||
ec0d6fc81a
|
|||
5d51ea63ed
|
|||
67e3a18547
|
|||
93017b2766
|
|||
18aae2995b
|
|||
3e2e90be5b
|
|||
955d15622f
|
|||
4bf59442ac
|
|||
749ce49bf5
|
|||
bbb46f941f
|
|||
d608864f8a
|
|||
24bce0f38b
|
|||
efa5573d6c | |||
118a0ad398
|
|||
70af7e9504 | |||
7b3e2b7968 | |||
f22de34634
|
|||
149f929616
|
|||
211568e345
|
|||
3b70363fbe
|
|||
0cb273a7d0
|
|||
26772794a5
|
|||
8219013b8a
|
|||
eeb8c3ce52
|
|||
ace949d4ec
|
|||
a37d87ae98 | |||
63e7dab1ee
|
|||
6f68917355
|
|||
a6defe8c41
|
|||
48532716ac | |||
e8f72eeab4 | |||
5525cf014e | |||
18422ff986 | |||
3c061169bc | |||
883ee62017
|
|||
adf4ca4614 | |||
a36fe32e05
|
|||
3b258a5b71
|
|||
7a90f3c0e6 | |||
272785a780
|
|||
95e2f5dbb1
|
|||
6df2f45b89
|
|||
beb3ae9359
|
|||
0f8db2fdcb
|
|||
12516b2a4b
|
|||
a92d6c25b9
|
|||
283825ecc2
|
|||
2369384a2a
|
|||
d1e9c31bef | |||
63e4dd0767 | |||
d0c13547a4
|
|||
5880ac963e
|
|||
d4119b47d4
|
|||
daf074d8dd
|
|||
e1df2dde54
|
|||
e8c71aa913
|
|||
3a16e6eb7e
|
|||
44ec5ecc93
|
|||
0352da6d58
|
|||
ac2ec348f2
|
|||
fadd7de447
|
|||
7c753098cd
|
|||
28637d099b
|
|||
e4f17f2704
|
|||
1be13ea8ed
|
|||
59743d7957
|
|||
2d27c5580a | |||
23983f7316
|
|||
b616221562
|
|||
292fd9238c
|
|||
36d0a92247
|
|||
f680a3025e
|
|||
41f49e5bab
|
|||
f5b94ed02d
|
|||
98b7346bf1
|
|||
f275583874
|
|||
7f8d495b1b
|
|||
00c1aaeb55
|
|||
08a09eca81
|
|||
02aa2cdbc6
|
|||
f460ba3ae6
|
|||
917f1b27fb
|
|||
9121534de3
|
|||
8230781e3d
|
|||
db200e2668
|
|||
a74e29d335
|
|||
023739444b
|
|||
eb8cf25690
|
|||
c0a2b13da5
|
|||
1f09cb2c2d
|
|||
08abbc57a9
|
|||
8c87a22ac0 | |||
343ef0d9ff | |||
7f88857227
|
|||
d155769cda | |||
3d1d21ffa1 | |||
21f4d9514e
|
|||
49f900b2a9
|
|||
2803d2be72
|
|||
9bc426fa0c
|
|||
6f5db26688
|
|||
fa8c50f665
|
|||
02e4da1758
|
|||
004fc38962 | |||
1d3f4feec9 | |||
20bafa3e58
|
|||
c0d82c6d22
|
|||
17e65a680d | |||
76308961bd
|
|||
1a1442386d
|
|||
158240614f
|
|||
923bf039bf
|
|||
bfd383659f | |||
94e2c5fb59
|
|||
433f4c063e
|
|||
455bcf727b
|
|||
7713e3c460
|
|||
1cc6dbb784
|
|||
319e0fcf1d
|
|||
b5b6fe7478
|
|||
8132622866
|
|||
3af33dca4b
|
|||
6c06638138
|
|||
d9cad51f52
|
|||
400ea0aced | |||
4fb6f83eb1
|
|||
0f6851601a
|
|||
89412cc8e4
|
|||
580bc0c6b2
|
|||
1332409dc2 | |||
b6fb9abfa1
|
|||
7941157235
|
|||
cb38da0e09
|
|||
2b86ab87eb
|
|||
6325382155
|
|||
1665ab92f3
|
|||
773b8d032f
|
|||
6b8a1a4d0b
|
|||
59f2b3703f
|
|||
4892843c52
|
|||
61eec97b68
|
|||
0696b68085
|
|||
c67e3b7674
|
|||
9592100218
|
|||
1fab02a274
|
|||
c765077b2f
|
|||
baafb18971 | |||
8b7e8547e6
|
|||
19cb5c072a
|
|||
a4607c75eb | |||
c6f0397f51 | |||
01c7e29a87 | |||
d36b92e49f | |||
62f3c5dd96 | |||
2f3fb8810e | |||
9ac94df0b5 | |||
43c1728a43 | |||
5de5ad9801 | |||
05f13610bf
|
|||
8fce5e7dd6
|
|||
ee81010f82
|
|||
9d40910890 | |||
8b322748a7
|
|||
338cc5d80f
|
|||
8c88f825dc
|
|||
047d72dbd1
|
|||
70c233a0df
|
|||
e864235761
|
|||
7d01e331fe
|
|||
ccd429a632
|
|||
e6d9867443
|
|||
87f21867f6
|
|||
f3b1b68f87
|
|||
5012917412
|
|||
816c40ce40
|
|||
6843690340
|
|||
b3fa7f2868
|
|||
6d45289e1a
|
|||
fd7fc8cb1f
|
|||
5f516d2726
|
|||
6d3904ea93
|
|||
fd42505f33
|
|||
1762a23a22 | |||
6170a0dd85
|
|||
087248d172
|
|||
2bd533acb8
|
|||
d96b28025d
|
|||
d240ff2542
|
|||
a12d771efd
|
|||
bf22d58139
|
|||
963fd22f83
|
|||
2053e26c53
|
|||
4d28a42120
|
|||
bf9eeb22eb
|
|||
ad3624d73e | |||
957823b264 | |||
4f1c45970c
|
|||
bfb68aa483
|
|||
fc7cc603cf
|
|||
4e57823983
|
|||
faf9e9a8cf
|
|||
9bcff54817 | |||
8c7d106191
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,5 @@ __pycache__
|
||||
*~
|
||||
_opam
|
||||
_build
|
||||
*.pyc
|
||||
|
||||
|
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" "$*"
|
4
dist/gpupdate-remote-policy
vendored
Normal file
4
dist/gpupdate-remote-policy
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
#%PAM-1.0
|
||||
#auth optional pam_mount.so
|
||||
session required pam_mkhomedir.so silent
|
||||
#session optional pam_mount.so
|
228
dist/gpupdate-setup
vendored
228
dist/gpupdate-setup
vendored
@ -1,228 +0,0 @@
|
||||
#! /usr/bin/env python3
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
import re
|
||||
|
||||
from gpoa.util.samba import smbopts
|
||||
|
||||
|
||||
def command(args):
|
||||
try:
|
||||
subprocess.check_call(args.split())
|
||||
except:
|
||||
print ('command: \'%s\' error' % args)
|
||||
|
||||
def from_command(args):
|
||||
try:
|
||||
with subprocess.Popen(args.split(), stdout=subprocess.PIPE) as proc:
|
||||
value = proc.stdout.readline().decode('utf-8')
|
||||
proc.wait()
|
||||
except:
|
||||
print ('from_command: \'%s\' error' % args)
|
||||
return 'local'
|
||||
|
||||
return value.strip()
|
||||
|
||||
def get_default_policy_name():
|
||||
localpolicy = 'workstation'
|
||||
dcpolicy = 'ad-domain-controller'
|
||||
|
||||
try:
|
||||
if smbopt.get_server_role() == 'active directory domain controller':
|
||||
return dcpolicy
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
release = '/etc/altlinux-release'
|
||||
if os.path.isfile(release):
|
||||
f = open(release)
|
||||
s = f.readline()
|
||||
if re.search('server', s, re.I):
|
||||
localpolicy = 'server'
|
||||
except:
|
||||
pass
|
||||
|
||||
return localpolicy
|
||||
|
||||
def parse_arguments():
|
||||
'''
|
||||
Parse CLI arguments.
|
||||
'''
|
||||
parser = argparse.ArgumentParser(prog='gpupdate-setup')
|
||||
subparsers = parser.add_subparsers(dest='action',
|
||||
metavar='action',
|
||||
help='Group Policy management actions (default action is status)')
|
||||
|
||||
parser_list = subparsers.add_parser('list',
|
||||
help='List avalable types of local policy')
|
||||
parser_status = subparsers.add_parser('status',
|
||||
help='Show current Group Policy status')
|
||||
parser_enable = subparsers.add_parser('enable',
|
||||
help='Enable Group Policy subsystem')
|
||||
parser_disable = subparsers.add_parser('disable',
|
||||
help='Disable Group Policy subsystem')
|
||||
parser_write = subparsers.add_parser('write',
|
||||
help='Operate on Group Policies (enable or disable)')
|
||||
parser_active = subparsers.add_parser('active-policy',
|
||||
help='Show name of policy enabled')
|
||||
|
||||
parser_write.add_argument('status',
|
||||
choices=['enable', 'disable'],
|
||||
help='Enable or disable Group Policies')
|
||||
parser_write.add_argument('localpolicy',
|
||||
default=None,
|
||||
nargs='?',
|
||||
help='Name of local policy to enable')
|
||||
|
||||
parser_enable.add_argument('localpolicy',
|
||||
default=None,
|
||||
nargs='?',
|
||||
help='Name of local policy to enable')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def get_policy_entries(directory):
|
||||
filtered_entries = list()
|
||||
if os.path.isdir(directory):
|
||||
entries = [os.path.join(directory, entry) for entry in os.listdir(directory)]
|
||||
|
||||
for entry in entries:
|
||||
if os.path.isdir(os.path.join(entry)):
|
||||
if not os.path.islink(os.path.join(entry)):
|
||||
if not entry.rpartition('/')[2] == 'default':
|
||||
filtered_entries.append(entry)
|
||||
|
||||
return filtered_entries
|
||||
|
||||
|
||||
def get_policy_variants():
|
||||
'''
|
||||
Get the list of local policy variants deployed on this system.
|
||||
Please note that is case overlapping names the names in
|
||||
/etc/local-policy must override names in /usr/share/local-policy
|
||||
'''
|
||||
policy_dir = '/usr/share/local-policy'
|
||||
etc_policy_dir = '/etc/local-policy'
|
||||
|
||||
system_policies = get_policy_entries(policy_dir)
|
||||
user_policies = get_policy_entries(etc_policy_dir)
|
||||
|
||||
general_listing = list()
|
||||
general_listing.extend(system_policies)
|
||||
general_listing.extend(user_policies)
|
||||
|
||||
return general_listing
|
||||
|
||||
def validate_policy_name(policy_name):
|
||||
return policy_name in [os.path.basename(d) for d in get_policy_variants()]
|
||||
|
||||
def get_status():
|
||||
systemd_unit_link = '/etc/systemd/system/multi-user.target.wants/gpupdate.service'
|
||||
|
||||
return os.path.islink(systemd_unit_link)
|
||||
|
||||
def get_active_policy():
|
||||
policy_dir = '/usr/share/local-policy'
|
||||
etc_policy_dir = '/etc/local-policy'
|
||||
default_policy_name = os.path.join(policy_dir, get_default_policy_name())
|
||||
|
||||
active_policy_name = os.path.join(etc_policy_dir, 'active')
|
||||
|
||||
actual_policy_name = os.path.realpath(default_policy_name)
|
||||
|
||||
if os.path.isdir(active_policy_name):
|
||||
actual_policy_name = os.path.realpath(active_policy_name)
|
||||
|
||||
return actual_policy_name
|
||||
|
||||
|
||||
def disable_gp():
|
||||
if from_command('/usr/sbin/control system-auth') != 'local':
|
||||
command('/usr/sbin/control system-policy global')
|
||||
else:
|
||||
command('/usr/sbin/control system-policy local')
|
||||
command('systemctl disable gpupdate.service')
|
||||
command('systemctl --global disable gpupdate-user.service')
|
||||
|
||||
def enable_gp(policy_name):
|
||||
policy_dir = '/usr/share/local-policy'
|
||||
etc_policy_dir = '/etc/local-policy'
|
||||
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)
|
||||
active_policy_name = os.path.join(etc_policy_dir, 'active')
|
||||
|
||||
if not os.path.isdir(etc_policy_dir):
|
||||
os.makedirs(etc_policy_dir)
|
||||
|
||||
if not os.path.islink(active_policy_name):
|
||||
os.symlink(default_policy_name, active_policy_name)
|
||||
else:
|
||||
os.unlink(active_policy_name)
|
||||
os.symlink(default_policy_name, active_policy_name)
|
||||
|
||||
# Enable oddjobd_gpupdate in PAM config
|
||||
command('/usr/sbin/control system-policy gpupdate')
|
||||
# Bootstrap the Group Policy engine
|
||||
command('/usr/sbin/gpoa --nodomain --loglevel 5')
|
||||
# Enable gpupdate-setup.service for all users
|
||||
command('systemctl --global enable gpupdate-user.service')
|
||||
|
||||
def main():
|
||||
arguments = parse_arguments()
|
||||
|
||||
if arguments.action == 'list':
|
||||
for entry in get_policy_variants():
|
||||
print(entry.rpartition('/')[2])
|
||||
|
||||
if arguments.action == 'status' or arguments.action == None:
|
||||
if get_status():
|
||||
print('enabled')
|
||||
else:
|
||||
print('disabled')
|
||||
|
||||
if arguments.action == 'write':
|
||||
if arguments.status == 'enable' or arguments.status == '#t':
|
||||
enable_gp(arguments.localpolicy)
|
||||
if arguments.status == 'disable' or arguments.status == '#f':
|
||||
disable_gp()
|
||||
|
||||
if arguments.action == "enable":
|
||||
enable_gp(arguments.localpolicy)
|
||||
|
||||
if arguments.action == "disable":
|
||||
disable_gp()
|
||||
|
||||
if arguments.action == 'active-policy':
|
||||
print(get_active_policy())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
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" "$*"
|
3
dist/gpupdate-user.service
vendored
3
dist/gpupdate-user.service
vendored
@ -4,7 +4,8 @@ Description=gpupdate in userspace
|
||||
|
||||
# gpupdate on Windows runs once per hour
|
||||
[Service]
|
||||
Environment="PATH=/bin:/sbin:/usr/bin:/usr/sbin"
|
||||
Environment=PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
||||
Type=simple
|
||||
RestartSec=3600
|
||||
TimeoutSec=3000
|
||||
|
4
dist/gpupdate.ini
vendored
Normal file
4
dist/gpupdate.ini
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[gpoa]
|
||||
backend = local
|
||||
local-policy = default
|
||||
|
5
dist/gpupdate.service
vendored
5
dist/gpupdate.service
vendored
@ -1,9 +1,10 @@
|
||||
[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"
|
||||
Environment=PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
||||
Type=simple
|
||||
RestartSec=3600
|
||||
TimeoutSec=3000
|
||||
|
13
dist/system-policy-gpupdate
vendored
13
dist/system-policy-gpupdate
vendored
@ -1,5 +1,12 @@
|
||||
#%PAM-1.0
|
||||
session required pam_mktemp.so
|
||||
session required pam_mkhomedir.so silent
|
||||
session required pam_limits.so
|
||||
session [success=2 perm_denied=ignore default=die] pam_localuser.so
|
||||
session substack gpupdate-remote-policy
|
||||
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
|
||||
|
@ -16,11 +16,12 @@
|
||||
# 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 logging
|
||||
|
||||
from util.windows import smbcreds
|
||||
from .samba_backend import samba_backend
|
||||
from .nodomain_backend import nodomain_backend
|
||||
from util.logging import log
|
||||
from util.config import GPConfig
|
||||
|
||||
def backend_factory(dc, username, is_machine, no_domain = False):
|
||||
'''
|
||||
@ -30,23 +31,31 @@ def backend_factory(dc, username, is_machine, no_domain = False):
|
||||
policies enforced by domain administrators.
|
||||
'''
|
||||
back = None
|
||||
domain = None
|
||||
if not no_domain:
|
||||
config = GPConfig()
|
||||
|
||||
if config.get_backend() == 'samba' and not no_domain:
|
||||
if not dc:
|
||||
dc = config.get_dc()
|
||||
if dc:
|
||||
ld = dict({'dc': dc})
|
||||
log('D52', ld)
|
||||
sc = smbcreds(dc)
|
||||
domain = sc.get_domain()
|
||||
|
||||
if domain:
|
||||
logging.debug('Initialize Samba backend for domain: {}'.format(domain))
|
||||
ldata = dict({'domain': domain, "username": username, 'is_machine': is_machine})
|
||||
log('D9', ldata)
|
||||
try:
|
||||
back = samba_backend(sc, username, domain, is_machine)
|
||||
except Exception as exc:
|
||||
logging.error('Unable to initialize Samba backend: {}'.format(exc))
|
||||
else:
|
||||
logging.debug('Initialize local backend with no domain')
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E7', logdata)
|
||||
|
||||
if config.get_backend() == 'local' or no_domain:
|
||||
log('D8')
|
||||
try:
|
||||
back = nodomain_backend()
|
||||
except Exception as exc:
|
||||
logging.error('Unable to initialize no-domain backend: {}'.format(exc))
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E8', logdata)
|
||||
|
||||
return back
|
||||
|
||||
|
@ -16,16 +16,10 @@
|
||||
# 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 util.rpm import (
|
||||
install_rpm,
|
||||
remove_rpm
|
||||
)
|
||||
|
||||
class rpm:
|
||||
def __init__(self, name, action):
|
||||
self.name = name
|
||||
self.action = action
|
||||
from .applier_backend import applier_backend
|
||||
|
||||
def apply(self):
|
||||
class freeipa_backend(applier_backend):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
@ -16,7 +16,6 @@
|
||||
# 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 logging
|
||||
import os
|
||||
# Facility to determine GPTs for user
|
||||
from samba.gpclass import check_safe_path, check_refresh_gpo_list
|
||||
@ -28,13 +27,21 @@ from util.util import (
|
||||
get_machine_name,
|
||||
is_machine_name
|
||||
)
|
||||
from util.kerberos import (
|
||||
machine_kinit
|
||||
, machine_kdestroy
|
||||
)
|
||||
from util.windows import get_sid
|
||||
import util.preg
|
||||
from util.logging import slogm
|
||||
from util.logging import log
|
||||
|
||||
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()
|
||||
@ -57,26 +64,51 @@ class samba_backend(applier_backend):
|
||||
self.sambacreds = sambacreds
|
||||
|
||||
self.cache_dir = self.sambacreds.get_cache_dir()
|
||||
logging.debug(slogm('Cache directory is: {}'.format(self.cache_dir)))
|
||||
logdata = dict({'cachedir': self.cache_dir})
|
||||
log('D7', logdata)
|
||||
|
||||
def __del__(self):
|
||||
if self.__kinit_successful:
|
||||
machine_kdestroy()
|
||||
|
||||
def retrieve_and_store(self):
|
||||
'''
|
||||
Retrieve settings and strore it in a database
|
||||
'''
|
||||
# Get policies for machine at first.
|
||||
machine_gpts = self._get_gpts(get_machine_name(), self.storage.get_info('machine_sid'))
|
||||
machine_gpts = list()
|
||||
try:
|
||||
machine_gpts = self._get_gpts(get_machine_name(), self.storage.get_info('machine_sid'))
|
||||
except Exception as exc:
|
||||
log('F2')
|
||||
raise exc
|
||||
self.storage.wipe_hklm()
|
||||
self.storage.wipe_user(self.storage.get_info('machine_sid'))
|
||||
for gptobj in machine_gpts:
|
||||
gptobj.merge()
|
||||
try:
|
||||
gptobj.merge()
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('E26', logdata)
|
||||
|
||||
# Load user GPT values in case user's name specified
|
||||
# This is a buggy implementation and should be tested more
|
||||
if not self._is_machine_username:
|
||||
user_gpts = self._get_gpts(self.username, self.sid)
|
||||
user_gpts = list()
|
||||
try:
|
||||
user_gpts = self._get_gpts(self.username, self.sid)
|
||||
except Exception as exc:
|
||||
log('F3')
|
||||
raise exc
|
||||
self.storage.wipe_user(self.sid)
|
||||
for gptobj in user_gpts:
|
||||
gptobj.merge()
|
||||
try:
|
||||
gptobj.merge()
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('E27', logdata)
|
||||
|
||||
def _check_sysvol_present(self, gpo):
|
||||
'''
|
||||
@ -86,19 +118,23 @@ class samba_backend(applier_backend):
|
||||
# GPO named "Local Policy" has no entry by its nature so
|
||||
# no reason to print warning.
|
||||
if 'Local Policy' != gpo.name:
|
||||
logging.warning(slogm('No SYSVOL entry assigned to GPO {}'.format(gpo.name)))
|
||||
logdata = dict({'gponame': gpo.name})
|
||||
log('W4', logdata)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_gpts(self, username, sid):
|
||||
gpts = list()
|
||||
|
||||
log('D45', {'username': username, 'sid': sid})
|
||||
# util.windows.smbcreds
|
||||
gpos = self.sambacreds.update_gpos(username)
|
||||
log('D46')
|
||||
for gpo in gpos:
|
||||
if self._check_sysvol_present(gpo):
|
||||
logging.debug(slogm('Found SYSVOL entry "{}" for GPO "{}"'.format(gpo.file_sys_path, gpo.display_name)))
|
||||
path = check_safe_path(gpo.file_sys_path).upper()
|
||||
logging.debug(slogm('Path: {}'.format(path)))
|
||||
slogdata = dict({'sysvol_path': gpo.file_sys_path, 'gpo_name': gpo.display_name, 'gpo_path': path})
|
||||
log('D30', slogdata)
|
||||
gpt_abspath = os.path.join(self.cache_dir, 'gpo_cache', path)
|
||||
obj = gpt(gpt_abspath, sid)
|
||||
obj.set_name(gpo.display_name)
|
||||
|
@ -18,6 +18,62 @@
|
||||
|
||||
from abc import ABC
|
||||
|
||||
import logging
|
||||
from util.logging import slogm
|
||||
|
||||
def check_experimental_enabled(storage):
|
||||
experimental_enable_flag = 'Software\\BaseALT\\Policies\\GPUpdate\\GlobalExperimental'
|
||||
flag = storage.get_hklm_entry(experimental_enable_flag)
|
||||
|
||||
result = False
|
||||
|
||||
if flag and '1' == flag.data:
|
||||
result = True
|
||||
|
||||
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)
|
||||
flag = storage.get_hklm_entry(gpupdate_module_flag)
|
||||
|
||||
result = None
|
||||
|
||||
if flag:
|
||||
if '1' == flag.data:
|
||||
result = True
|
||||
if '0' == flag.data:
|
||||
result = False
|
||||
|
||||
return result
|
||||
|
||||
def check_enabled(storage, module_name, is_experimental):
|
||||
module_enabled = check_module_enabled(storage, module_name)
|
||||
exp_enabled = check_experimental_enabled(storage)
|
||||
|
||||
result = False
|
||||
|
||||
if None == module_enabled:
|
||||
if is_experimental and exp_enabled:
|
||||
result = True
|
||||
if not is_experimental:
|
||||
result = True
|
||||
else:
|
||||
result = module_enabled
|
||||
|
||||
return result
|
||||
|
||||
class applier_frontend(ABC):
|
||||
@classmethod
|
||||
def __init__(self, regobj):
|
||||
|
@ -19,13 +19,29 @@
|
||||
import subprocess
|
||||
import threading
|
||||
import logging
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
def control_subst(preg_name):
|
||||
'''
|
||||
This is a workaround for control names which can't be used in
|
||||
PReg/ADMX files.
|
||||
'''
|
||||
control_triggers = dict()
|
||||
control_triggers['dvd_rw-format'] = 'dvd+rw-format'
|
||||
control_triggers['dvd_rw-mediainfo'] = 'dvd+rw-mediainfo'
|
||||
control_triggers['dvd_rw-booktype'] = 'dvd+rw-booktype'
|
||||
|
||||
result = preg_name
|
||||
if preg_name in control_triggers:
|
||||
result = control_triggers[preg_name]
|
||||
|
||||
return result
|
||||
|
||||
class control:
|
||||
def __init__(self, name, value):
|
||||
if type(value) != int and type(value) != str:
|
||||
raise Exception('Unknown type of value for control')
|
||||
self.control_name = name
|
||||
self.control_name = control_subst(name)
|
||||
self.control_value = value
|
||||
self.possible_values = self._query_control_values()
|
||||
if self.possible_values == None:
|
||||
@ -39,10 +55,12 @@ class control:
|
||||
values = list()
|
||||
|
||||
popen_call = ['/usr/sbin/control', self.control_name, 'list']
|
||||
with subprocess.Popen(popen_call, stdout=subprocess.PIPE) as proc:
|
||||
with subprocess.Popen(popen_call, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
|
||||
values = proc.stdout.readline().decode('utf-8').split()
|
||||
valErr = proc.stderr.readline().decode('utf-8')
|
||||
if valErr:
|
||||
raise ValueError(valErr)
|
||||
proc.wait()
|
||||
|
||||
return values
|
||||
|
||||
def _map_control_status(self, int_status):
|
||||
@ -52,7 +70,11 @@ class control:
|
||||
try:
|
||||
str_status = self.possible_values[int_status]
|
||||
except IndexError as exc:
|
||||
logging.error(slogm('Error getting control ({}) value from {} by index {}'.format(self.control_name, self.possible_values, int_status)))
|
||||
logdata = dict()
|
||||
logdata['control'] = self.control_name
|
||||
logdata['value from'] = self.possible_values
|
||||
logdata['by index'] = int_status
|
||||
log('E41', )
|
||||
str_status = None
|
||||
|
||||
return str_status
|
||||
@ -77,20 +99,30 @@ class control:
|
||||
if type(self.control_value) == int:
|
||||
status = self._map_control_status(self.control_value)
|
||||
if status == None:
|
||||
logging.error(slogm('\'{}\' is not in possible values for control {}'.format(self.control_value, self.control_name)))
|
||||
logdata = dict()
|
||||
logdata['control'] = self.control_name
|
||||
logdata['inpossible values'] = self.self.control_value
|
||||
log('E42', logdata)
|
||||
return
|
||||
elif type(self.control_value) == str:
|
||||
if self.control_value not in self.possible_values:
|
||||
logging.error(slogm('\'{}\' is not in possible values for control {}'.format(self.control_value, self.control_name)))
|
||||
logdata = dict()
|
||||
logdata['control'] = self.control_name
|
||||
logdata['inpossible values'] = self.self.control_value
|
||||
log('E59', logdata)
|
||||
return
|
||||
status = self.control_value
|
||||
|
||||
logging.debug(slogm('Setting control {} to {}'.format(self.control_name, status)))
|
||||
logdata = dict()
|
||||
logdata['control'] = self.control_name
|
||||
logdata['status'] = status
|
||||
log('D68', logdata)
|
||||
|
||||
try:
|
||||
popen_call = ['/usr/sbin/control', self.control_name, status]
|
||||
with subprocess.Popen(popen_call, stdout=subprocess.PIPE) as proc:
|
||||
proc.wait()
|
||||
except:
|
||||
logging.error(slogm('Unable to set {} to {}'.format(self.control_name, status)))
|
||||
|
||||
logdata = dict()
|
||||
logdata['control'] = self.control_name
|
||||
logdata['status'] = status
|
||||
log('E43', logdata)
|
||||
|
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)
|
97
gpoa/frontend/appliers/firewall_rule.py
Normal file
97
gpoa/frontend/appliers/firewall_rule.py
Normal file
@ -0,0 +1,97 @@
|
||||
#
|
||||
# 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 enum import Enum
|
||||
import subprocess
|
||||
|
||||
def getprops(param_list):
|
||||
props = dict()
|
||||
|
||||
for entry in param_list:
|
||||
lentry = entry.lower()
|
||||
if lentry.startswith('action'):
|
||||
props['action'] = lentry.rpartition('=')[2]
|
||||
if lentry.startswith('protocol'):
|
||||
props['protocol'] = lentry.rpartition('=')[2]
|
||||
if lentry.startswith('dir'):
|
||||
props['dir'] = lentry.rpartition('=')[2]
|
||||
|
||||
return props
|
||||
|
||||
|
||||
def get_ports(param_list):
|
||||
portlist = list()
|
||||
|
||||
for entry in param_list:
|
||||
lentry = entry.lower()
|
||||
if lentry.startswith('lport'):
|
||||
port = lentry.rpartition('=')[2]
|
||||
portlist.append(port)
|
||||
|
||||
return portlist
|
||||
|
||||
class PortState(Enum):
|
||||
OPEN = 'Allow'
|
||||
CLOSE = 'Deny'
|
||||
|
||||
class Protocol(Enum):
|
||||
TCP = 'tcp'
|
||||
UDP = 'udp'
|
||||
|
||||
class FirewallMode(Enum):
|
||||
ROUTER = 'router'
|
||||
GATEWAY = 'gateway'
|
||||
HOST = 'host'
|
||||
|
||||
# This shi^Wthing named alterator-net-iptables is unable to work in
|
||||
# multi-threaded environment
|
||||
class FirewallRule:
|
||||
__alterator_command = '/usr/bin/alterator-net-iptables'
|
||||
|
||||
def __init__(self, data):
|
||||
data_array = data.split('|')
|
||||
|
||||
self.version = data_array[0]
|
||||
self.ports = get_ports(data_array[1:])
|
||||
self.properties = getprops(data_array[1:])
|
||||
|
||||
def apply(self):
|
||||
tcp_command = []
|
||||
udp_command = []
|
||||
|
||||
for port in self.ports:
|
||||
tcp_port = '{}'.format(port)
|
||||
udp_port = '{}'.format(port)
|
||||
|
||||
if PortState.OPEN.value == self.properties['action']:
|
||||
tcp_port = '+' + tcp_port
|
||||
udp_port = '+' + udp_port
|
||||
if PortState.CLOSE.value == self.properties['action']:
|
||||
tcp_port = '-' + tcp_port
|
||||
udp_port = '-' + udp_port
|
||||
|
||||
portcmd = [
|
||||
self.__alterator_command
|
||||
, 'write'
|
||||
, '-m', FirewallMode.HOST.value
|
||||
, '-t', tcp_port
|
||||
, '-u', udp_port
|
||||
]
|
||||
proc = subprocess.Popen(portcmd)
|
||||
proc.wait()
|
||||
|
77
gpoa/frontend/appliers/folder.py
Normal file
77
gpoa/frontend/appliers/folder.py
Normal file
@ -0,0 +1,77 @@
|
||||
#
|
||||
# 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 pathlib import Path
|
||||
|
||||
|
||||
from gpt.folders import (
|
||||
FileAction
|
||||
, action_letter2enum
|
||||
)
|
||||
from util.windows import expand_windows_var
|
||||
|
||||
def remove_dir_tree(path, delete_files=False, delete_folder=False, delete_sub_folders=False):
|
||||
content = list()
|
||||
for entry in path.iterdir():
|
||||
content.append(entry)
|
||||
if entry.is_file() and delete_files:
|
||||
entry.unlink()
|
||||
content.remove(entry)
|
||||
if entry.is_dir() and delete_sub_folders:
|
||||
content.remove(entry)
|
||||
remove_dir_tree(entry, delete_files, delete_folder, delete_sub_folders)
|
||||
if delete_folder and not content:
|
||||
path.rmdir()
|
||||
|
||||
|
||||
def str2bool(boolstr):
|
||||
if boolstr.lower() in ['true', 'yes', '1']:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Folder:
|
||||
def __init__(self, folder_object, username):
|
||||
self.folder_path = Path(expand_windows_var(folder_object.path, username).replace('\\', '/'))
|
||||
self.action = action_letter2enum(folder_object.action)
|
||||
self.delete_files = str2bool(folder_object.delete_files)
|
||||
self.delete_folder = str2bool(folder_object.delete_folder)
|
||||
self.delete_sub_folders = str2bool(folder_object.delete_sub_folders)
|
||||
|
||||
def _create_action(self):
|
||||
self.folder_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def _delete_action(self):
|
||||
if self.folder_path.exists():
|
||||
remove_dir_tree(self.folder_path,
|
||||
self.delete_files,
|
||||
self.delete_folder,
|
||||
self.delete_sub_folders)
|
||||
|
||||
def act(self):
|
||||
if self.action == FileAction.CREATE:
|
||||
self._create_action()
|
||||
if self.action == FileAction.UPDATE:
|
||||
self._create_action()
|
||||
if self.action == FileAction.DELETE:
|
||||
self._delete_action()
|
||||
if self.action == FileAction.REPLACE:
|
||||
self._delete_action()
|
||||
self._create_action()
|
||||
|
@ -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
|
||||
@ -21,47 +21,156 @@ import os
|
||||
import logging
|
||||
from gi.repository import Gio, GLib
|
||||
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
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):
|
||||
if check_existing_gsettings(schema, path):
|
||||
self.gsettings.append(system_gsetting(schema, path, data, lock, helper))
|
||||
else:
|
||||
logdata = dict()
|
||||
logdata['schema'] = schema
|
||||
logdata['path'] = path
|
||||
logdata['data'] = data
|
||||
logdata['lock'] = lock
|
||||
log('D150', logdata)
|
||||
|
||||
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:
|
||||
logdata = dict()
|
||||
logdata['gsetting.schema'] = gsetting.schema
|
||||
logdata['gsetting.path'] = gsetting.path
|
||||
logdata['gsetting.value'] = gsetting.value
|
||||
logdata['gsetting.lock'] = gsetting.lock
|
||||
settings = Gio.Settings(schema=gsetting.schema)
|
||||
log('D89', logdata)
|
||||
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' 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)
|
||||
|
||||
def check_existing_gsettings (schema, path):
|
||||
source = Gio.SettingsSchemaSource.get_default()
|
||||
sourceSchema = (source.lookup(schema, False))
|
||||
if bool(sourceSchema) and sourceSchema.has_key(path):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class user_gsettings:
|
||||
def __init__(self):
|
||||
self.gsettings = list()
|
||||
|
||||
def append(self, schema, path, value, helper=None):
|
||||
if check_existing_gsettings(schema, path):
|
||||
self.gsettings.append(user_gsetting(schema, path, value, helper))
|
||||
else:
|
||||
logdata = dict()
|
||||
logdata['schema'] = schema
|
||||
logdata['path'] = path
|
||||
logdata['data'] = value
|
||||
log('D151', logdata)
|
||||
|
||||
def apply(self):
|
||||
for gsetting in self.gsettings:
|
||||
logdata = dict()
|
||||
logdata['gsetting.schema'] = gsetting.schema
|
||||
logdata['gsetting.path'] = gsetting.path
|
||||
logdata['gsetting.value'] = gsetting.value
|
||||
log('D85', logdata)
|
||||
gsetting.apply()
|
||||
|
||||
|
||||
class user_gsetting:
|
||||
def __init__(self, schema, path, value):
|
||||
def __init__(self, schema, path, value, helper_function=None):
|
||||
self.schema = schema
|
||||
self.path = path
|
||||
self.value = value
|
||||
self.helper_function = helper_function
|
||||
|
||||
def apply(self):
|
||||
source = Gio.SettingsSchemaSource.get_default()
|
||||
schema = source.lookup(self.schema, True)
|
||||
key = schema.get_key(self.path)
|
||||
gvformat = key.get_value_type()
|
||||
val = GLib.Variant(gvformat.dup_string(), self.value)
|
||||
schema.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())
|
||||
|
||||
# Access the current schema
|
||||
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, result)
|
||||
settings.sync()
|
||||
|
@ -20,7 +20,7 @@ import os
|
||||
import jinja2
|
||||
import logging
|
||||
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
class polkit:
|
||||
__template_path = '/usr/share/gpupdate/templates'
|
||||
@ -28,11 +28,15 @@ class polkit:
|
||||
__template_loader = jinja2.FileSystemLoader(searchpath=__template_path)
|
||||
__template_environment = jinja2.Environment(loader=__template_loader)
|
||||
|
||||
def __init__(self, template_name, arglist):
|
||||
def __init__(self, template_name, arglist, username=None):
|
||||
self.template_name = template_name
|
||||
self.args = arglist
|
||||
self.username = username
|
||||
self.infilename = '{}.rules.j2'.format(self.template_name)
|
||||
self.outfile = os.path.join(self.__policy_dir, '{}.rules'.format(self.template_name))
|
||||
if self.username:
|
||||
self.outfile = os.path.join(self.__policy_dir, '{}.{}.rules'.format(self.template_name, self.username))
|
||||
else:
|
||||
self.outfile = os.path.join(self.__policy_dir, '{}.rules'.format(self.template_name))
|
||||
|
||||
def generate(self):
|
||||
try:
|
||||
@ -42,7 +46,13 @@ class polkit:
|
||||
with open(self.outfile, 'w') as f:
|
||||
f.write(text)
|
||||
|
||||
logging.debug(slogm('Generated file {} with arguments {}'.format(self.outfile, self.args)))
|
||||
logdata = dict()
|
||||
logdata['file'] = self.outfile
|
||||
logdata['arguments'] = self.args
|
||||
log('D77', logdata)
|
||||
except Exception as exc:
|
||||
logging.error(slogm('Unable to generate file {} from {}'.format(self.outfile, self.infilename)))
|
||||
logdata = dict()
|
||||
logdata['file'] = self.outfile
|
||||
logdata['arguments'] = self.args
|
||||
log('E44', logdata)
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
import dbus
|
||||
import logging
|
||||
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
class systemd_unit:
|
||||
def __init__(self, unit_name, state):
|
||||
@ -39,7 +39,9 @@ class systemd_unit:
|
||||
self.manager.UnmaskUnitFiles([self.unit_name], dbus.Boolean(False))
|
||||
self.manager.EnableUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
||||
self.manager.StartUnit(self.unit_name, 'replace')
|
||||
logging.info(slogm('Starting systemd unit: {}'.format(self.unit_name)))
|
||||
logdata = dict()
|
||||
logdata['unit'] = self.unit_name
|
||||
log('I6', logdata)
|
||||
|
||||
# In case the service has 'RestartSec' property set it
|
||||
# switches to 'activating (auto-restart)' state instead of
|
||||
@ -47,17 +49,23 @@ class systemd_unit:
|
||||
service_state = self._get_state()
|
||||
|
||||
if not service_state in ['active', 'activating']:
|
||||
logging.error(slogm('Unable to start systemd unit {}'.format(self.unit_name)))
|
||||
logdata = dict()
|
||||
logdata['unit'] = self.unit_name
|
||||
log('E46', logdata)
|
||||
else:
|
||||
self.manager.StopUnit(self.unit_name, 'replace')
|
||||
self.manager.DisableUnitFiles([self.unit_name], dbus.Boolean(False))
|
||||
self.manager.MaskUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
||||
logging.info(slogm('Stopping systemd unit: {}'.format(self.unit_name)))
|
||||
logdata = dict()
|
||||
logdata['unit'] = self.unit_name
|
||||
log('I6', logdata)
|
||||
|
||||
service_state = self._get_state()
|
||||
|
||||
if not service_state in ['stopped']:
|
||||
logging.error(slogm('Unable to stop systemd unit {}'.format(self.unit_name)))
|
||||
logdata = dict()
|
||||
logdata['unit'] = self.unit_name
|
||||
log('E46', logdata)
|
||||
|
||||
def _get_state(self):
|
||||
'''
|
||||
|
23
gpoa/frontend/appliers/util.py
Normal file
23
gpoa/frontend/appliers/util.py
Normal file
@ -0,0 +1,23 @@
|
||||
#
|
||||
# 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 enum import Enum
|
||||
|
||||
class WallpaperStretchMode(Enum):
|
||||
STRETCH = 2
|
||||
|
@ -16,16 +16,22 @@
|
||||
# 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
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
from util.util import is_machine_name
|
||||
|
||||
class chromium_applier(applier_frontend):
|
||||
__module_name = 'ChromiumApplier'
|
||||
__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'
|
||||
@ -39,6 +45,11 @@ class chromium_applier(applier_frontend):
|
||||
self.username = username
|
||||
self._is_machine_name = is_machine_name(self.username)
|
||||
self.policies = dict()
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def get_hklm_string_entry(self, hive_subkey):
|
||||
query_str = '{}\\{}'.format(self.__registry_branch, hive_subkey)
|
||||
@ -72,7 +83,10 @@ class chromium_applier(applier_frontend):
|
||||
def set_policy(self, name, obj):
|
||||
if obj:
|
||||
self.policies[name] = obj
|
||||
logging.info(slogm('Chromium policy \'{}\' set to {}'.format(name, obj)))
|
||||
logdata = dict()
|
||||
logdata['name'] = name
|
||||
logdata['set to'] = obj
|
||||
log('I8', logdata)
|
||||
|
||||
def set_user_policy(self, name, obj):
|
||||
'''
|
||||
@ -90,16 +104,24 @@ class chromium_applier(applier_frontend):
|
||||
with open(prefpath, 'r') as f:
|
||||
settings = json.load(f)
|
||||
except FileNotFoundError as exc:
|
||||
logging.error(slogm('Chromium preferences file {} does not exist at the moment'.format(prefpath)))
|
||||
logdata = dict()
|
||||
logdata['prefpath'] = prefpath
|
||||
log('E51', logdata)
|
||||
except:
|
||||
logging.error(slogm('Error during attempt to read Chromium preferences for user {}'.format(self.username)))
|
||||
logdata = dict()
|
||||
logdata['username'] = self.username
|
||||
log('E51', logdata)
|
||||
|
||||
if obj:
|
||||
settings[name] = obj
|
||||
|
||||
with open(prefpath, 'w') as f:
|
||||
json.dump(settings, f)
|
||||
logging.info(slogm('Set user ({}) property \'{}\' to {}'.format(self.username, name, obj)))
|
||||
logdata = dict()
|
||||
logdata['user'] = self.username
|
||||
logdata['name'] = name
|
||||
logdata['set to'] = obj
|
||||
log('I9', logdata)
|
||||
|
||||
def get_home_page(self, hkcu=False):
|
||||
response = self.get_hklm_string_entry('HomepageLocation')
|
||||
@ -119,7 +141,9 @@ class chromium_applier(applier_frontend):
|
||||
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
||||
with open(destfile, 'w') as f:
|
||||
json.dump(self.policies, f)
|
||||
logging.debug(slogm('Wrote Chromium preferences to {}'.format(destfile)))
|
||||
logdata = dict()
|
||||
logdata['destfile'] = destfile
|
||||
log('D97', logdata)
|
||||
|
||||
def user_apply(self):
|
||||
'''
|
||||
@ -131,7 +155,12 @@ class chromium_applier(applier_frontend):
|
||||
'''
|
||||
All actual job done here.
|
||||
'''
|
||||
self.machine_apply()
|
||||
if self.__module_enabled:
|
||||
log('D95')
|
||||
self.machine_apply()
|
||||
else:
|
||||
log('D96')
|
||||
#if not self._is_machine_name:
|
||||
# logging.debug('Running user applier for Chromium')
|
||||
# self.user_apply()
|
||||
|
||||
|
164
gpoa/frontend/cifs_applier.py
Normal file
164
gpoa/frontend/cifs_applier.py
Normal file
@ -0,0 +1,164 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import fileinput
|
||||
import jinja2
|
||||
import os
|
||||
import subprocess
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from gpt.drives import json2drive
|
||||
from util.util import get_homedir
|
||||
from util.logging import slogm, log
|
||||
|
||||
def storage_get_drives(storage, sid):
|
||||
drives = storage.get_drives(sid)
|
||||
drive_list = list()
|
||||
|
||||
for drv_obj in drives:
|
||||
drive_list.append(drv_obj)
|
||||
|
||||
return drive_list
|
||||
|
||||
|
||||
def add_line_if_missing(filename, ins_line):
|
||||
with open(filename, 'r+') as f:
|
||||
for line in f:
|
||||
if ins_line == line.strip():
|
||||
break
|
||||
else:
|
||||
f.write(ins_line + '\n')
|
||||
f.flush()
|
||||
|
||||
class cifs_applier(applier_frontend):
|
||||
def __init__(self, storage):
|
||||
pass
|
||||
|
||||
def apply(self):
|
||||
pass
|
||||
|
||||
class cifs_applier_user(applier_frontend):
|
||||
__module_name = 'CIFSApplierUser'
|
||||
__module_enabled = False
|
||||
__module_experimental = True
|
||||
__auto_file = '/etc/auto.master'
|
||||
__auto_dir = '/etc/auto.master.gpupdate.d'
|
||||
__template_path = '/usr/share/gpupdate/templates'
|
||||
__template_mountpoints = 'autofs_mountpoints.j2'
|
||||
__template_identity = 'autofs_identity.j2'
|
||||
__template_auto = 'autofs_auto.j2'
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
self.storage = storage
|
||||
self.sid = sid
|
||||
self.username = username
|
||||
|
||||
self.home = get_homedir(username)
|
||||
conf_file = '{}.conf'.format(sid)
|
||||
autofs_file = '{}.autofs'.format(sid)
|
||||
cred_file = '{}.creds'.format(sid)
|
||||
|
||||
self.auto_master_d = Path(self.__auto_dir)
|
||||
|
||||
self.user_config = self.auto_master_d / conf_file
|
||||
if os.path.exists(self.user_config.resolve()):
|
||||
self.user_config.unlink()
|
||||
self.user_autofs = self.auto_master_d / autofs_file
|
||||
if os.path.exists(self.user_autofs.resolve()):
|
||||
self.user_autofs.unlink()
|
||||
self.user_creds = self.auto_master_d / cred_file
|
||||
|
||||
self.mount_dir = Path(os.path.join(self.home, 'net'))
|
||||
self.drives = storage_get_drives(self.storage, self.sid)
|
||||
|
||||
self.template_loader = jinja2.FileSystemLoader(searchpath=self.__template_path)
|
||||
self.template_env = jinja2.Environment(loader=self.template_loader)
|
||||
|
||||
self.template_mountpoints = self.template_env.get_template(self.__template_mountpoints)
|
||||
self.template_indentity = self.template_env.get_template(self.__template_identity)
|
||||
self.template_auto = self.template_env.get_template(self.__template_auto)
|
||||
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
|
||||
def user_context_apply(self):
|
||||
'''
|
||||
Nothing to implement.
|
||||
'''
|
||||
pass
|
||||
|
||||
def __admin_context_apply(self):
|
||||
# Create /etc/auto.master.gpupdate.d directory
|
||||
self.auto_master_d.mkdir(parents=True, exist_ok=True)
|
||||
# Create user's destination mount directory
|
||||
self.mount_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Add pointer to /etc/auto.master.gpiupdate.d in /etc/auto.master
|
||||
auto_destdir = '+dir:{}'.format(self.__auto_dir)
|
||||
add_line_if_missing(self.__auto_file, auto_destdir)
|
||||
|
||||
# Collect data for drive settings
|
||||
drive_list = list()
|
||||
for drv in self.drives:
|
||||
drive_settings = dict()
|
||||
drive_settings['dir'] = drv.dir
|
||||
drive_settings['login'] = drv.login
|
||||
drive_settings['password'] = drv.password
|
||||
drive_settings['path'] = drv.path.replace('\\', '/')
|
||||
|
||||
drive_list.append(drive_settings)
|
||||
|
||||
if len(drive_list) > 0:
|
||||
mount_settings = dict()
|
||||
mount_settings['drives'] = drive_list
|
||||
mount_text = self.template_mountpoints.render(**mount_settings)
|
||||
|
||||
with open(self.user_config.resolve(), 'w') as f:
|
||||
f.truncate()
|
||||
f.write(mount_text)
|
||||
f.flush()
|
||||
|
||||
autofs_settings = dict()
|
||||
autofs_settings['home_dir'] = self.home
|
||||
autofs_settings['mount_file'] = self.user_config.resolve()
|
||||
autofs_text = self.template_auto.render(**autofs_settings)
|
||||
|
||||
with open(self.user_autofs.resolve(), 'w') as f:
|
||||
f.truncate()
|
||||
f.write(autofs_text)
|
||||
f.flush()
|
||||
|
||||
subprocess.check_call(['/bin/systemctl', 'restart', 'autofs'])
|
||||
|
||||
|
||||
def admin_context_apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D146')
|
||||
self.__admin_context_apply()
|
||||
else:
|
||||
log('D147')
|
||||
|
@ -16,36 +16,68 @@
|
||||
# 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
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from .appliers.control import control
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
import logging
|
||||
|
||||
class control_applier(applier_frontend):
|
||||
__module_name = 'ControlApplier'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
_registry_branch = 'Software\\BaseALT\\Policies\\Control'
|
||||
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
self.control_settings = self.storage.filter_hklm_entries('Software\\BaseALT\\Policies\\Control%')
|
||||
self.controls = list()
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def apply(self):
|
||||
'''
|
||||
Trigger control facility invocation.
|
||||
'''
|
||||
def run(self):
|
||||
for setting in self.control_settings:
|
||||
valuename = setting.hive_key.rpartition('\\')[2]
|
||||
try:
|
||||
self.controls.append(control(valuename, int(setting.data)))
|
||||
logging.info(slogm('Working with control {}'.format(valuename)))
|
||||
logdata = dict()
|
||||
logdata['control'] = valuename
|
||||
logdata['value'] = setting.data
|
||||
log('I3', logdata)
|
||||
except ValueError as exc:
|
||||
self.controls.append(control(valuename, setting.data))
|
||||
logging.info(slogm('Working with control {} with string value'.format(valuename)))
|
||||
try:
|
||||
ctl = control(valuename, setting.data)
|
||||
except Exception as exc:
|
||||
logdata = {'Exception': exc}
|
||||
log('I3', logdata)
|
||||
continue
|
||||
self.controls.append(ctl)
|
||||
logdata = dict()
|
||||
logdata['control'] = valuename
|
||||
logdata['with string value'] = setting.data
|
||||
log('I3', logdata)
|
||||
except Exception as exc:
|
||||
logging.info(slogm('Unable to work with control {}: {}'.format(valuename, exc)))
|
||||
logdata = dict()
|
||||
logdata['control'] = valuename
|
||||
logdata['exc'] = exc
|
||||
log('E39', logdata)
|
||||
#for e in polfile.pol_file.entries:
|
||||
# print('{}:{}:{}:{}:{}'.format(e.type, e.data, e.valuename, e.keyname))
|
||||
for cont in self.controls:
|
||||
cont.set_control_status()
|
||||
|
||||
def apply(self):
|
||||
'''
|
||||
Trigger control facility invocation.
|
||||
'''
|
||||
if self.__module_enabled:
|
||||
log('D67')
|
||||
self.run()
|
||||
else:
|
||||
log('E40')
|
||||
|
@ -18,11 +18,17 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
|
||||
from .applier_frontend import applier_frontend
|
||||
import cups
|
||||
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from gpt.printers import json2printer
|
||||
from util.rpm import is_rpm_installed
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
def storage_get_printers(storage, sid):
|
||||
'''
|
||||
@ -32,42 +38,81 @@ def storage_get_printers(storage, sid):
|
||||
printers = list()
|
||||
|
||||
for prnj in printer_objs:
|
||||
prn_obj = json2printer(prnj)
|
||||
printers.append(prn_obj)
|
||||
printers.append(prnj)
|
||||
|
||||
return printers
|
||||
|
||||
def write_printer(prn):
|
||||
def connect_printer(connection, prn):
|
||||
'''
|
||||
Dump printer cinfiguration to disk as CUPS config
|
||||
'''
|
||||
printer_config_path = os.path.join('/etc/cups', prn.name)
|
||||
with open(printer_config_path, 'r') as f:
|
||||
print(prn.cups_config(), file=f)
|
||||
# PPD file location
|
||||
printer_driver = 'generic'
|
||||
pjson = json.loads(prn.printer)
|
||||
printer_parts = pjson['printer']['path'].partition(' ')
|
||||
# Printer queue name in CUPS
|
||||
printer_name = printer_parts[2].replace('(', '').replace(')', '')
|
||||
# Printer description in CUPS
|
||||
printer_info = printer_name
|
||||
printer_uri = printer_parts[0].replace('\\', '/')
|
||||
printer_uri = 'smb:' + printer_uri
|
||||
|
||||
connection.addPrinter(
|
||||
name=printer_name
|
||||
, info=printer_info
|
||||
, device=printer_uri
|
||||
#filename=printer_driver
|
||||
)
|
||||
|
||||
class cups_applier(applier_frontend):
|
||||
__module_name = 'CUPSApplier'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def run(self):
|
||||
if not is_rpm_installed('cups'):
|
||||
log('W9')
|
||||
return
|
||||
|
||||
self.cups_connection = cups.Connection()
|
||||
self.printers = storage_get_printers(self.storage, self.storage.get_info('machine_sid'))
|
||||
|
||||
if self.printers:
|
||||
for prn in self.printers:
|
||||
connect_printer(self.cups_connection, prn)
|
||||
|
||||
def apply(self):
|
||||
'''
|
||||
Perform configuration of printer which is assigned to computer.
|
||||
'''
|
||||
if not is_rpm_installed('cups'):
|
||||
logging.warning(slogm('CUPS is not installed: no printer settings will be deployed'))
|
||||
return
|
||||
|
||||
printers = storage_get_printers(self.storage, self.storage.get_info('machine_sid'))
|
||||
|
||||
if printers:
|
||||
for prn in printers:
|
||||
write_printer(prn)
|
||||
if self.__module_enabled:
|
||||
log('D113')
|
||||
self.run()
|
||||
else:
|
||||
log('D114')
|
||||
|
||||
class cups_applier_user(applier_frontend):
|
||||
__module_name = 'CUPSApplierUser'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
self.storage = storage
|
||||
self.sid = sid
|
||||
self.username = username
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_enabled
|
||||
)
|
||||
|
||||
def user_context_apply(self):
|
||||
'''
|
||||
@ -76,17 +121,25 @@ class cups_applier_user(applier_frontend):
|
||||
'''
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
if not is_rpm_installed('cups'):
|
||||
log('W9')
|
||||
return
|
||||
|
||||
self.cups_connection = cups.Connection()
|
||||
self.printers = storage_get_printers(self.storage, self.sid)
|
||||
|
||||
if self.printers:
|
||||
for prn in self.printers:
|
||||
connect_printer(self.cups_connection, prn)
|
||||
|
||||
def admin_context_apply(self):
|
||||
'''
|
||||
Perform printer configuration assigned for user.
|
||||
'''
|
||||
if not is_rpm_installed('cups'):
|
||||
logging.warning(slogm('CUPS is not installed: no printer settings will be deployed'))
|
||||
return
|
||||
|
||||
printers = storage_get_printers(self.storage, self.sid)
|
||||
|
||||
if printers:
|
||||
for prn in printers:
|
||||
write_printer(prn)
|
||||
if self.__module_enabled:
|
||||
log('D115')
|
||||
self.run()
|
||||
else:
|
||||
log('D116')
|
||||
|
||||
|
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, log
|
||||
|
||||
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:
|
||||
log('D134')
|
||||
ev = Envvar(self.envvars, 'root')
|
||||
ev.act()
|
||||
else:
|
||||
log('D135')
|
||||
|
||||
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:
|
||||
log('D136')
|
||||
ev = Envvar(self.envvars, self.username)
|
||||
ev.act()
|
||||
else:
|
||||
log('D137')
|
||||
|
@ -30,13 +30,20 @@ import json
|
||||
import os
|
||||
import configparser
|
||||
|
||||
from .applier_frontend import applier_frontend
|
||||
from util.logging import slogm
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from util.logging import slogm, log
|
||||
from util.util import is_machine_name
|
||||
|
||||
class firefox_applier(applier_frontend):
|
||||
__module_name = 'FirefoxApplier'
|
||||
__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):
|
||||
@ -46,6 +53,11 @@ class firefox_applier(applier_frontend):
|
||||
self._is_machine_name = is_machine_name(self.username)
|
||||
self.policies = dict()
|
||||
self.policies_json = dict({ 'policies': self.policies })
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def get_profiles(self):
|
||||
'''
|
||||
@ -86,7 +98,10 @@ class firefox_applier(applier_frontend):
|
||||
'''
|
||||
if obj:
|
||||
self.policies[name] = obj
|
||||
logging.info(slogm('Firefox policy \'{}\' set to {}'.format(name, obj)))
|
||||
logdata = dict()
|
||||
logdata['name'] = name
|
||||
logdata['set to'] = obj
|
||||
log('I7', logdata)
|
||||
|
||||
def get_home_page(self):
|
||||
'''
|
||||
@ -103,41 +118,105 @@ 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
|
||||
logdata = dict()
|
||||
logdata['name'] = name
|
||||
logdata['set to'] = obj
|
||||
log('I7', logdata)
|
||||
|
||||
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)))
|
||||
logdata = dict()
|
||||
logdata['destfile'] = destfile
|
||||
log('D91', logdata)
|
||||
|
||||
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)
|
||||
logdata = dict()
|
||||
logdata['destfile'] = destfile
|
||||
log('D91', logdata)
|
||||
|
||||
def user_apply(self):
|
||||
profiles = self.get_profiles()
|
||||
|
||||
profiledir = os.path.join(util.get_homedir(self.username), self.__user_settings_dir)
|
||||
for profile in profiles:
|
||||
logging.debug(slogm('Found Firefox profile in {}/{}'.format(profiledir, profile)))
|
||||
logdata = dict()
|
||||
logdata['profiledir'] = profiledir
|
||||
logdata['profile'] = profile
|
||||
log('D92', logdata)
|
||||
|
||||
def apply(self):
|
||||
self.machine_apply()
|
||||
if self.__module_enabled:
|
||||
log('D93')
|
||||
self.machine_apply()
|
||||
else:
|
||||
log('D94')
|
||||
#if not self._is_machine_name:
|
||||
# logging.debug('Running user applier for Firefox')
|
||||
# self.user_apply()
|
||||
|
65
gpoa/frontend/firewall_applier.py
Normal file
65
gpoa/frontend/firewall_applier.py
Normal file
@ -0,0 +1,65 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from util.logging import slogm, log
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from .appliers.firewall_rule import FirewallRule
|
||||
|
||||
class firewall_applier(applier_frontend):
|
||||
__module_name = 'FirewallApplier'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
__firewall_branch = 'SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\FirewallRules'
|
||||
__firewall_switch = 'SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\DomainProfile\\EnableFirewall'
|
||||
__firewall_reset_cmd = ['/usr/bin/alterator-net-iptables', 'reset']
|
||||
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
self.firewall_settings = self.storage.filter_hklm_entries('{}%'.format(self.__firewall_branch))
|
||||
self.firewall_enabled = self.storage.get_hklm_entry(self.__firewall_switch)
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def run(self):
|
||||
for setting in self.firewall_settings:
|
||||
rule = FirewallRule(setting.data)
|
||||
rule.apply()
|
||||
|
||||
def apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D117')
|
||||
if '1' == self.firewall_enabled:
|
||||
log('D118')
|
||||
self.run()
|
||||
else:
|
||||
log('D119')
|
||||
proc = subprocess.Popen(self.__firewall_reset_cmd)
|
||||
proc.wait()
|
||||
else:
|
||||
log('D120')
|
||||
|
95
gpoa/frontend/folder_applier.py
Normal file
95
gpoa/frontend/folder_applier.py
Normal file
@ -0,0 +1,95 @@
|
||||
#
|
||||
# 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 pathlib import Path
|
||||
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from .appliers.folder import Folder
|
||||
from util.logging import slogm, log
|
||||
from util.windows import expand_windows_var
|
||||
import re
|
||||
import logging
|
||||
|
||||
class folder_applier(applier_frontend):
|
||||
__module_name = 'FoldersApplier'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
|
||||
def __init__(self, storage, sid):
|
||||
self.storage = storage
|
||||
self.sid = sid
|
||||
self.folders = self.storage.get_folders(self.sid)
|
||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
|
||||
|
||||
def apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D107')
|
||||
for directory_obj in self.folders:
|
||||
check = expand_windows_var(directory_obj.path).replace('\\', '/')
|
||||
win_var = re.findall(r'%.+?%', check)
|
||||
drive = re.findall(r'^[a-z A-Z]\:',check)
|
||||
if drive or win_var:
|
||||
continue
|
||||
fld = Folder(directory_obj)
|
||||
fld.action()
|
||||
else:
|
||||
log('D108')
|
||||
|
||||
class folder_applier_user(applier_frontend):
|
||||
__module_name = 'FoldersApplierUser'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
self.storage = storage
|
||||
self.sid = sid
|
||||
self.username = username
|
||||
self.folders = self.storage.get_folders(self.sid)
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def run(self):
|
||||
for directory_obj in self.folders:
|
||||
check = expand_windows_var(directory_obj.path, self.username).replace('\\', '/')
|
||||
win_var = re.findall(r'%.+?%', check)
|
||||
drive = re.findall(r'^[a-z A-Z]\:',check)
|
||||
if drive or win_var:
|
||||
continue
|
||||
fld = Folder(directory_obj, self.username)
|
||||
fld.act()
|
||||
|
||||
def admin_context_apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D109')
|
||||
self.run()
|
||||
else:
|
||||
log('D110')
|
||||
|
||||
def user_context_apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D111')
|
||||
self.run()
|
||||
else:
|
||||
log('D112')
|
||||
|
@ -17,14 +17,21 @@
|
||||
# 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 polkit_applier
|
||||
from .polkit_applier import (
|
||||
polkit_applier
|
||||
, polkit_applier_user
|
||||
)
|
||||
from .systemd_applier import systemd_applier
|
||||
from .firefox_applier import firefox_applier
|
||||
from .chromium_applier import chromium_applier
|
||||
from .cups_applier import cups_applier
|
||||
from .package_applier import package_applier
|
||||
from .package_applier import (
|
||||
package_applier
|
||||
, package_applier_user
|
||||
)
|
||||
from .shortcut_applier import (
|
||||
shortcut_applier,
|
||||
shortcut_applier_user
|
||||
@ -33,16 +40,26 @@ from .gsettings_applier import (
|
||||
gsettings_applier,
|
||||
gsettings_applier_user
|
||||
)
|
||||
from .firewall_applier import firewall_applier
|
||||
from .folder_applier import (
|
||||
folder_applier
|
||||
, folder_applier_user
|
||||
)
|
||||
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 slogm
|
||||
from util.logging import log
|
||||
from util.system import with_privileges
|
||||
|
||||
import logging
|
||||
|
||||
def determine_username(username=None):
|
||||
'''
|
||||
@ -55,16 +72,30 @@ def determine_username(username=None):
|
||||
# of process owner.
|
||||
if not username:
|
||||
name = get_process_user()
|
||||
logging.debug(slogm('Username is not specified - will use username of current process'))
|
||||
logdata = dict({'username': name})
|
||||
log('D2', logdata)
|
||||
|
||||
if not username_match_uid(name):
|
||||
if not is_root():
|
||||
raise Exception('Current process UID does not match specified username')
|
||||
|
||||
logging.debug(slogm('Username for frontend is set to {}'.format(name)))
|
||||
logdata = dict({'username': name})
|
||||
log('D15', logdata)
|
||||
|
||||
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
|
||||
@ -77,60 +108,86 @@ 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({
|
||||
'control': control_applier(self.storage),
|
||||
'polkit': polkit_applier(self.storage),
|
||||
'systemd': systemd_applier(self.storage),
|
||||
'firefox': firefox_applier(self.storage, self.sid, self.username),
|
||||
'chromium': chromium_applier(self.storage, self.sid, self.username),
|
||||
'shortcuts': shortcut_applier(self.storage),
|
||||
'gsettings': gsettings_applier(self.storage),
|
||||
'cups': cups_applier(self.storage),
|
||||
'package': package_applier(self.storage)
|
||||
})
|
||||
self.machine_appliers = dict()
|
||||
self.machine_appliers['control'] = control_applier(self.storage)
|
||||
self.machine_appliers['polkit'] = polkit_applier(self.storage)
|
||||
self.machine_appliers['systemd'] = systemd_applier(self.storage)
|
||||
self.machine_appliers['firefox'] = firefox_applier(self.storage, self.sid, self.username)
|
||||
self.machine_appliers['chromium'] = chromium_applier(self.storage, self.sid, self.username)
|
||||
self.machine_appliers['shortcuts'] = shortcut_applier(self.storage)
|
||||
self.machine_appliers['gsettings'] = gsettings_applier(self.storage, self.file_cache)
|
||||
self.machine_appliers['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({
|
||||
'shortcuts': shortcut_applier_user(self.storage, self.sid, self.username),
|
||||
'gsettings': gsettings_applier_user(self.storage, self.sid, self.username)
|
||||
})
|
||||
self.user_appliers = dict()
|
||||
self.user_appliers['shortcuts'] = shortcut_applier_user(self.storage, self.sid, self.username)
|
||||
self.user_appliers['folders'] = folder_applier_user(self.storage, self.sid, self.username)
|
||||
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.file_cache, self.sid, self.username)
|
||||
try:
|
||||
self.user_appliers['cifs'] = cifs_applier_user(self.storage, self.sid, self.username)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['applier_name'] = 'cifs'
|
||||
logdata['msg'] = str(exc)
|
||||
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):
|
||||
'''
|
||||
Run global appliers with administrator privileges.
|
||||
'''
|
||||
if not is_root():
|
||||
logging.error('Not sufficient privileges to run machine appliers')
|
||||
log('E13')
|
||||
return
|
||||
logging.debug(slogm('Applying computer part of settings'))
|
||||
self.machine_appliers['systemd'].apply()
|
||||
self.machine_appliers['control'].apply()
|
||||
self.machine_appliers['polkit'].apply()
|
||||
self.machine_appliers['firefox'].apply()
|
||||
self.machine_appliers['chromium'].apply()
|
||||
self.machine_appliers['shortcuts'].apply()
|
||||
self.machine_appliers['gsettings'].apply()
|
||||
self.machine_appliers['cups'].apply()
|
||||
#self.machine_appliers['package'].apply()
|
||||
log('D16')
|
||||
|
||||
for applier_name, applier_object in self.machine_appliers.items():
|
||||
try:
|
||||
applier_object.apply()
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['applier_name'] = applier_name
|
||||
logdata['msg'] = str(exc)
|
||||
log('E24', logdata)
|
||||
|
||||
def user_apply(self):
|
||||
'''
|
||||
Run appliers for users.
|
||||
'''
|
||||
if is_root():
|
||||
logging.debug(slogm('Running user appliers from administrator context'))
|
||||
self.user_appliers['shortcuts'].admin_context_apply()
|
||||
self.user_appliers['gsettings'].admin_context_apply()
|
||||
for applier_name, applier_object in self.user_appliers.items():
|
||||
try:
|
||||
applier_object.admin_context_apply()
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['applier'] = applier_name
|
||||
logdata['exception'] = str(exc)
|
||||
log('E19', logdata)
|
||||
|
||||
logging.debug(slogm('Running user appliers for user context'))
|
||||
with_privileges(self.username, self.user_appliers['shortcuts'].user_context_apply)
|
||||
with_privileges(self.username, self.user_appliers['gsettings'].user_context_apply)
|
||||
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:
|
||||
logging.debug(slogm('Running user appliers from user context'))
|
||||
self.user_appliers['shortcuts'].user_context_apply()
|
||||
self.user_appliers['gsettings'].user_context_apply()
|
||||
for applier_name, applier_object in self.user_appliers.items():
|
||||
try:
|
||||
applier_object.user_context_apply()
|
||||
except Exception as exc:
|
||||
logdata = dict({'applier_name': applier_name, 'message': str(exc)})
|
||||
log('E11', logdata)
|
||||
|
||||
def apply_parameters(self):
|
||||
'''
|
||||
|
@ -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
|
||||
@ -18,75 +18,286 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import subprocess
|
||||
|
||||
from .applier_frontend import applier_frontend
|
||||
from .appliers.gsettings import (
|
||||
system_gsetting,
|
||||
user_gsetting
|
||||
from gi.repository import (
|
||||
Gio
|
||||
, GLib
|
||||
)
|
||||
from util.logging import slogm
|
||||
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
, check_windows_mapping_enabled
|
||||
)
|
||||
from .appliers.gsettings import (
|
||||
system_gsettings,
|
||||
user_gsettings
|
||||
)
|
||||
from util.logging import slogm ,log
|
||||
|
||||
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
|
||||
log('D90', logdata)
|
||||
except Exception as exc:
|
||||
pass
|
||||
|
||||
return retval
|
||||
|
||||
class gsettings_applier(applier_frontend):
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
|
||||
__module_name = 'GSettingsApplier'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\GSettings\\'
|
||||
__registry_locks_branch = 'Software\\BaseALT\\Policies\\GSettingsLocks\\'
|
||||
__wallpaper_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.mate.background.picture-filename'
|
||||
__vino_authentication_methods_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.gnome.Vino.authentication-methods'
|
||||
__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)
|
||||
log('D145', 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)
|
||||
|
||||
def apply(self):
|
||||
# Cleanup settings from previous run
|
||||
if os.path.exists(self.override_file):
|
||||
logging.debug(slogm('Removing GSettings policy file from previous run'))
|
||||
log('D82')
|
||||
os.remove(self.override_file)
|
||||
|
||||
# Get all configured gsettings locks
|
||||
for lock in self.gsettings_locks:
|
||||
valuename = lock.hive_key.rpartition('\\')[2]
|
||||
self.locks[valuename] = int(lock.data)
|
||||
|
||||
# Calculate all configured gsettings
|
||||
for setting in self.gsettings_keys:
|
||||
helper = None
|
||||
valuename = setting.hive_key.rpartition('\\')[2]
|
||||
rp = valuename.rpartition('.')
|
||||
schema = rp[0]
|
||||
path = rp[2]
|
||||
data = setting.data
|
||||
lock = bool(self.locks[valuename]) if valuename in self.locks else None
|
||||
if setting.hive_key.lower() == self.__wallpaper_entry.lower():
|
||||
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
|
||||
self.gsettings.apply()
|
||||
|
||||
# Recompile GSettings schemas with overrides
|
||||
try:
|
||||
proc = subprocess.run(args=['/usr/bin/glib-compile-schemas', self.__global_schema], capture_output=True, check=True)
|
||||
except Exception as exc:
|
||||
log('E48')
|
||||
|
||||
# Update desktop configuration system backend
|
||||
try:
|
||||
proc = subprocess.run(args=['/usr/bin/dconf', "update"], capture_output=True, check=True)
|
||||
except Exception as exc:
|
||||
log('E49')
|
||||
|
||||
def apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D80')
|
||||
self.run()
|
||||
else:
|
||||
log('D81')
|
||||
|
||||
class GSettingsMapping:
|
||||
def __init__(self, hive_key, gsettings_schema, gsettings_key):
|
||||
self.hive_key = hive_key
|
||||
self.gsettings_schema = gsettings_schema
|
||||
self.gsettings_key = gsettings_key
|
||||
|
||||
try:
|
||||
self.schema_source = Gio.SettingsSchemaSource.get_default()
|
||||
self.schema = self.schema_source.lookup(self.gsettings_schema, True)
|
||||
self.gsettings_schema_key = self.schema.get_key(self.gsettings_key)
|
||||
self.gsettings_type = self.gsettings_schema_key.get_value_type()
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['hive_key'] = self.hive_key
|
||||
logdata['gsettings_schema'] = self.gsettings_schema
|
||||
logdata['gsettings_key'] = self.gsettings_key
|
||||
log('W6', logdata)
|
||||
|
||||
def preg2gsettings(self):
|
||||
'''
|
||||
Transform PReg key variant into GLib.Variant. This function
|
||||
performs mapping of PReg type system into GLib type system.
|
||||
'''
|
||||
pass
|
||||
|
||||
def gsettings2preg(self):
|
||||
'''
|
||||
Transform GLib.Variant key type into PReg key type.
|
||||
'''
|
||||
pass
|
||||
|
||||
class gsettings_applier_user(applier_frontend):
|
||||
__module_name = 'GSettingsApplierUser'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\GSettings\\'
|
||||
__wallpaper_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.mate.background.picture-filename'
|
||||
__vino_authentication_methods_entry = 'Software\\BaseALT\\Policies\\GSettings\\org.gnome.Vino.authentication-methods'
|
||||
|
||||
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 = user_gsettings()
|
||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||
self.__windows_mapping_enabled = check_windows_mapping_enabled(self.storage)
|
||||
|
||||
self.__windows_settings = dict()
|
||||
self.windows_settings = list()
|
||||
mapping = [
|
||||
# Disable or enable screen saver
|
||||
GSettingsMapping(
|
||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaveActive'
|
||||
, 'org.mate.screensaver'
|
||||
, 'idle-activation-enabled'
|
||||
)
|
||||
# Timeout in seconds for screen saver activation. The value of zero effectively disables screensaver start
|
||||
, GSettingsMapping(
|
||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaveTimeOut'
|
||||
, 'org.mate.session'
|
||||
, 'idle-delay'
|
||||
)
|
||||
# Enable or disable password protection for screen saver
|
||||
, GSettingsMapping(
|
||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaverIsSecure'
|
||||
, 'org.mate.screensaver'
|
||||
, 'lock-enabled'
|
||||
)
|
||||
# Specify image which will be used as a wallpaper
|
||||
, GSettingsMapping(
|
||||
'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\Wallpaper'
|
||||
, 'org.mate.background'
|
||||
, 'picture-filename'
|
||||
)
|
||||
]
|
||||
self.windows_settings.extend(mapping)
|
||||
|
||||
for element in self.windows_settings:
|
||||
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:
|
||||
logdata = dict()
|
||||
logdata['setting_key'] = setting_key
|
||||
logdata['value.data'] = value.data
|
||||
log('D86', logdata)
|
||||
mapping = self.__windows_settings[setting_key]
|
||||
try:
|
||||
self.gsettings.append(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]
|
||||
# rp = valuename.rpartition('.')
|
||||
# schema = rp[0]
|
||||
# path = rp[2]
|
||||
# self.gsettings.append(user_gsetting(schema, path, setting.data))
|
||||
|
||||
|
||||
# Calculate all mapped gsettings if mapping enabled
|
||||
if self.__windows_mapping_enabled:
|
||||
log('D83')
|
||||
self.windows_mapping_append()
|
||||
else:
|
||||
log('D84')
|
||||
|
||||
# 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]
|
||||
self.gsettings.append(system_gsetting(schema, path, setting.data))
|
||||
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(schema, path, data, helper)
|
||||
|
||||
# Create GSettings policy with highest available priority
|
||||
for gsetting in self.gsettings:
|
||||
gsetting.apply()
|
||||
|
||||
# Recompile GSettings schemas with overrides
|
||||
try:
|
||||
proc = subprocess.run(args=['/usr/bin/glib-compile-schemas', self.__global_schema], capture_output=True, check=True)
|
||||
except Exception as exc:
|
||||
logging.debug(slogm('Error recompiling global GSettings schemas'))
|
||||
|
||||
class gsettings_applier_user(applier_frontend):
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
self.storage = storage
|
||||
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.gsettings.apply()
|
||||
|
||||
def user_context_apply(self):
|
||||
for setting in self.gsettings_keys:
|
||||
valuename = setting.hive_key.rpartition('\\')[2]
|
||||
rp = valuename.rpartition('.')
|
||||
schema = rp[0]
|
||||
path = rp[2]
|
||||
self.gsettings.append(user_gsetting(schema, path, setting.data))
|
||||
|
||||
for gsetting in self.gsettings:
|
||||
gsetting.apply()
|
||||
if self.__module_enabled:
|
||||
log('D87')
|
||||
self.run()
|
||||
else:
|
||||
log('D88')
|
||||
|
||||
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)
|
||||
log('E50', logdata)
|
||||
|
||||
|
||||
|
151
gpoa/frontend/ntp_applier.py
Normal file
151
gpoa/frontend/ntp_applier.py
Normal file
@ -0,0 +1,151 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
from enum import Enum
|
||||
|
||||
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from util.logging import slogm, log
|
||||
|
||||
|
||||
class NTPServerType(Enum):
|
||||
NTP = 'NTP'
|
||||
|
||||
|
||||
class ntp_applier(applier_frontend):
|
||||
__module_name = 'NTPApplier'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
|
||||
__ntp_branch = 'Software\\Policies\\Microsoft\\W32time\\Parameters'
|
||||
__ntp_client_branch = 'Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient'
|
||||
__ntp_server_branch = 'Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpServer'
|
||||
|
||||
__ntp_key_address = 'NtpServer'
|
||||
__ntp_key_type = 'Type'
|
||||
__ntp_key_client_enabled = 'Enabled'
|
||||
__ntp_key_server_enabled = 'Enabled'
|
||||
|
||||
__chrony_config = '/etc/chrony.conf'
|
||||
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
|
||||
self.ntp_server_address_key = '{}\\{}'.format(self.__ntp_branch, self.__ntp_key_address)
|
||||
self.ntp_server_type = '{}\\{}'.format(self.__ntp_branch, self.__ntp_key_type)
|
||||
self.ntp_client_enabled = '{}\\{}'.format(self.__ntp_client_branch, self.__ntp_key_client_enabled)
|
||||
self.ntp_server_enabled = '{}\\{}'.format(self.__ntp_server_branch, self.__ntp_key_server_enabled)
|
||||
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def _chrony_as_client(self):
|
||||
command = ['/usr/sbin/control', 'chrony', 'client']
|
||||
proc = subprocess.Popen(command)
|
||||
proc.wait()
|
||||
|
||||
def _chrony_as_server(self):
|
||||
command = ['/usr/sbin/control', 'chrony', 'server']
|
||||
proc = subprocess.Popen(command)
|
||||
proc.wait()
|
||||
|
||||
def _start_chrony_client(self, server=None):
|
||||
srv = None
|
||||
if server:
|
||||
srv = server.data.rpartition(',')[0]
|
||||
logdata = dict()
|
||||
logdata['srv'] = srv
|
||||
log('D122', logdata)
|
||||
|
||||
start_command = ['/usr/bin/systemctl', 'start', 'chronyd']
|
||||
chrony_set_server = ['/usr/bin/chronyc', 'add', 'server', srv]
|
||||
chrony_disconnect_all = ['/usr/bin/chronyc', 'offline']
|
||||
chrony_connect = ['/usr/bin/chronyc', 'online', srv]
|
||||
|
||||
log('D123')
|
||||
|
||||
proc = subprocess.Popen(start_command)
|
||||
proc.wait()
|
||||
|
||||
if srv:
|
||||
logdata = dict()
|
||||
logdata['srv'] = srv
|
||||
log('D124', logdata)
|
||||
|
||||
proc = subprocess.Popen(chrony_disconnect_all)
|
||||
proc.wait()
|
||||
|
||||
proc = subprocess.Popen(chrony_set_server)
|
||||
proc.wait()
|
||||
|
||||
proc = subprocess.Popen(chrony_connect)
|
||||
proc.wait()
|
||||
|
||||
def _stop_chrony_client(self):
|
||||
stop_command = ['/usr/bin/systemctl', 'stop', 'chronyd']
|
||||
log('D125')
|
||||
proc = subprocess.Popen(stop_command)
|
||||
proc.wait()
|
||||
|
||||
def run(self):
|
||||
server_type = self.storage.get_hklm_entry(self.ntp_server_type)
|
||||
server_address = self.storage.get_hklm_entry(self.ntp_server_address_key)
|
||||
ntp_server_enabled = self.storage.get_hklm_entry(self.ntp_server_enabled)
|
||||
ntp_client_enabled = self.storage.get_hklm_entry(self.ntp_client_enabled)
|
||||
|
||||
if NTPServerType.NTP.value != server_type.data:
|
||||
logdata = dict()
|
||||
logdata['server_type'] = server_type
|
||||
log('W10', logdata)
|
||||
else:
|
||||
log('D126')
|
||||
if '1' == ntp_server_enabled.data:
|
||||
log('D127')
|
||||
self._start_chrony_client(server_address)
|
||||
self._chrony_as_server()
|
||||
elif '0' == ntp_server_enabled.data:
|
||||
log('D128')
|
||||
self._chrony_as_client()
|
||||
else:
|
||||
log('D129')
|
||||
|
||||
if '1' == ntp_client_enabled.data:
|
||||
log('D130')
|
||||
self._start_chrony_client()
|
||||
elif '0' == ntp_client_enabled.data:
|
||||
log('D131')
|
||||
self._stop_chrony_client()
|
||||
else:
|
||||
log('D132')
|
||||
|
||||
def apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D121')
|
||||
self.run()
|
||||
else:
|
||||
log('D133')
|
||||
|
@ -17,20 +17,109 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
from util.logging import slogm, log
|
||||
from util.rpm import (
|
||||
update
|
||||
, install_rpm
|
||||
, remove_rpm
|
||||
)
|
||||
|
||||
from .applier_frontend import applier_frontend
|
||||
from .appliers.rpm import rpm
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
|
||||
class package_applier(applier_frontend):
|
||||
__module_name = 'PackagesApplier'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
__install_key_name = 'Install'
|
||||
__remove_key_name = 'Remove'
|
||||
__sync_key_name = 'Sync'
|
||||
__hklm_branch = 'Software\\BaseALT\\Policies\\Packages'
|
||||
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
|
||||
install_branch = '{}\\{}%'.format(self.__hklm_branch, self.__install_key_name)
|
||||
remove_branch = '{}\\{}%'.format(self.__hklm_branch, self.__remove_key_name)
|
||||
sync_branch = '{}\\{}%'.format(self.__hklm_branch, self.__sync_key_name)
|
||||
self.fulcmd = list()
|
||||
self.fulcmd.append('/usr/libexec/gpupdate/pkcon_runner')
|
||||
self.fulcmd.append('--loglevel')
|
||||
logger = logging.getLogger()
|
||||
self.fulcmd.append(str(logger.level))
|
||||
self.install_packages_setting = self.storage.filter_hklm_entries(install_branch)
|
||||
self.remove_packages_setting = self.storage.filter_hklm_entries(remove_branch)
|
||||
self.sync_packages_setting = self.storage.filter_hklm_entries(sync_branch)
|
||||
self.flagSync = True
|
||||
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
def run(self):
|
||||
for flag in self.sync_packages_setting:
|
||||
if flag.data:
|
||||
self.flagSync = bool(int(flag.data))
|
||||
|
||||
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
||||
if self.flagSync:
|
||||
try:
|
||||
subprocess.check_call(self.fulcmd)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('E55', logdata)
|
||||
else:
|
||||
try:
|
||||
subprocess.Popen(self.fulcmd,close_fds=False)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('E61', logdata)
|
||||
|
||||
def apply(self):
|
||||
pass
|
||||
if self.__module_enabled:
|
||||
log('D138')
|
||||
self.run()
|
||||
else:
|
||||
log('D139')
|
||||
|
||||
|
||||
class package_applier_user(applier_frontend):
|
||||
def __init__(self):
|
||||
pass
|
||||
__module_name = 'PackagesApplierUser'
|
||||
__module_experimental = True
|
||||
__module_enabled = False
|
||||
__install_key_name = 'Install'
|
||||
__remove_key_name = 'Remove'
|
||||
__sync_key_name = 'Sync'
|
||||
__hkcu_branch = 'Software\\BaseALT\\Policies\\Packages'
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
self.storage = storage
|
||||
self.sid = sid
|
||||
self.username = username
|
||||
self.fulcmd = list()
|
||||
self.fulcmd.append('/usr/libexec/gpupdate/pkcon_runner')
|
||||
self.fulcmd.append('--sid')
|
||||
self.fulcmd.append(self.sid)
|
||||
self.fulcmd.append('--loglevel')
|
||||
logger = logging.getLogger()
|
||||
self.fulcmd.append(str(logger.level))
|
||||
|
||||
install_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__install_key_name)
|
||||
remove_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__remove_key_name)
|
||||
sync_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__sync_key_name)
|
||||
|
||||
self.install_packages_setting = self.storage.filter_hkcu_entries(self.sid, install_branch)
|
||||
self.remove_packages_setting = self.storage.filter_hkcu_entries(self.sid, remove_branch)
|
||||
self.sync_packages_setting = self.storage.filter_hkcu_entries(self.sid, sync_branch)
|
||||
self.flagSync = False
|
||||
|
||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
|
||||
|
||||
def user_context_apply(self):
|
||||
'''
|
||||
@ -38,10 +127,35 @@ class package_applier_user(applier_frontend):
|
||||
'''
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
for flag in self.sync_packages_setting:
|
||||
if flag.data:
|
||||
self.flagSync = bool(int(flag.data))
|
||||
|
||||
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
||||
if self.flagSync:
|
||||
try:
|
||||
subprocess.check_call(self.fulcmd)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('E60', logdata)
|
||||
else:
|
||||
try:
|
||||
subprocess.Popen(self.fulcmd,close_fds=False)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('E62', logdata)
|
||||
|
||||
def admin_context_apply(self):
|
||||
'''
|
||||
Install software assigned to specified username regardless
|
||||
which computer he uses to log into system.
|
||||
'''
|
||||
pass
|
||||
if self.__module_enabled:
|
||||
log('D140')
|
||||
self.run()
|
||||
else:
|
||||
log('D141')
|
||||
|
||||
|
@ -16,16 +16,22 @@
|
||||
# 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
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from .appliers.polkit import polkit
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
import logging
|
||||
|
||||
class polkit_applier(applier_frontend):
|
||||
__module_name = 'PolkitApplier'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
__deny_all = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
||||
__polkit_map = {
|
||||
__deny_all: ['99-gpoa_disk_permissions', { 'Deny_All': 0 }]
|
||||
__deny_all: ['49-gpoa_disk_permissions', { 'Deny_All': 0 }]
|
||||
}
|
||||
|
||||
def __init__(self, storage):
|
||||
@ -35,17 +41,78 @@ class polkit_applier(applier_frontend):
|
||||
template_file = self.__polkit_map[self.__deny_all][0]
|
||||
template_vars = self.__polkit_map[self.__deny_all][1]
|
||||
if deny_all:
|
||||
logging.debug(slogm('Deny_All setting found: {}'.format(deny_all.data)))
|
||||
logdata = dict()
|
||||
logdata['Deny_All'] = deny_all.data
|
||||
log('D69', logdata)
|
||||
self.__polkit_map[self.__deny_all][1]['Deny_All'] = deny_all.data
|
||||
else:
|
||||
logging.debug(slogm('Deny_All setting not found'))
|
||||
log('D71')
|
||||
self.policies = []
|
||||
self.policies.append(polkit(template_file, template_vars))
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def apply(self):
|
||||
'''
|
||||
Trigger control facility invocation.
|
||||
'''
|
||||
for policy in self.policies:
|
||||
policy.generate()
|
||||
if self.__module_enabled:
|
||||
log('D73')
|
||||
for policy in self.policies:
|
||||
policy.generate()
|
||||
else:
|
||||
log('D75')
|
||||
|
||||
class polkit_applier_user(applier_frontend):
|
||||
__module_name = 'PolkitApplierUser'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
__deny_all = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
||||
__polkit_map = {
|
||||
__deny_all: ['48-gpoa_disk_permissions_user', { 'Deny_All': 0, 'User': '' }]
|
||||
}
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
self.storage = storage
|
||||
self.sid = sid
|
||||
self.username = username
|
||||
|
||||
deny_all = storage.filter_hkcu_entries(self.sid, self.__deny_all).first()
|
||||
# Deny_All hook: initialize defaults
|
||||
template_file = self.__polkit_map[self.__deny_all][0]
|
||||
template_vars = self.__polkit_map[self.__deny_all][1]
|
||||
if deny_all:
|
||||
logdata = dict()
|
||||
logdata['user'] = self.username
|
||||
logdata['Deny_All'] = deny_all.data
|
||||
log('D70', logdata)
|
||||
self.__polkit_map[self.__deny_all][1]['Deny_All'] = deny_all.data
|
||||
self.__polkit_map[self.__deny_all][1]['User'] = self.username
|
||||
else:
|
||||
log('D72')
|
||||
self.policies = []
|
||||
self.policies.append(polkit(template_file, template_vars, self.username))
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def user_context_apply(self):
|
||||
pass
|
||||
|
||||
def admin_context_apply(self):
|
||||
'''
|
||||
Trigger control facility invocation.
|
||||
'''
|
||||
if self.__module_enabled:
|
||||
log('D74')
|
||||
for policy in self.policies:
|
||||
policy.generate()
|
||||
else:
|
||||
log('D76')
|
||||
|
||||
|
||||
|
@ -19,16 +19,19 @@
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from .applier_frontend import applier_frontend
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from gpt.shortcuts import json2sc
|
||||
from util.windows import expand_windows_var
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
from util.util import (
|
||||
get_homedir,
|
||||
homedir_exists
|
||||
)
|
||||
|
||||
def storage_get_shortcuts(storage, sid):
|
||||
def storage_get_shortcuts(storage, sid, username=None):
|
||||
'''
|
||||
Query storage for shortcuts' rows for specified SID.
|
||||
'''
|
||||
@ -37,17 +40,26 @@ 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
|
||||
'''
|
||||
dest_abspath = expand_windows_var(shortcut.dest, username).replace('\\', '/') + '.desktop'
|
||||
dest_abspath = shortcut.dest
|
||||
if not dest_abspath.startswith('/') and not dest_abspath.startswith('%'):
|
||||
dest_abspath = '%HOME%/' + dest_abspath
|
||||
logdata = dict()
|
||||
logdata['shortcut'] = dest_abspath
|
||||
logdata['for'] = username
|
||||
log('D105', logdata)
|
||||
dest_abspath = expand_windows_var(dest_abspath, username).replace('\\', '/') + '.desktop'
|
||||
|
||||
# Check that we're working for user, not on global system level
|
||||
if username:
|
||||
@ -56,52 +68,106 @@ 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)))
|
||||
logdata = dict()
|
||||
logdata['user'] = username
|
||||
logdata['dest_abspath'] = dest_abspath
|
||||
log('W7', logdata)
|
||||
return None
|
||||
else:
|
||||
logdata = dict()
|
||||
logdata['user'] = username
|
||||
logdata['bad path'] = dest_abspath
|
||||
log('W8', logdata)
|
||||
return None
|
||||
|
||||
logging.debug(slogm('Writing shortcut file to {}'.format(dest_abspath)))
|
||||
shortcut.write_desktop(dest_abspath)
|
||||
if '%' in dest_abspath:
|
||||
logdata = dict()
|
||||
logdata['dest_abspath'] = dest_abspath
|
||||
log('E53', logdata)
|
||||
return None
|
||||
|
||||
if not dest_abspath.startswith('/'):
|
||||
logdata = dict()
|
||||
logdata['dest_abspath'] = dest_abspath
|
||||
log('E54', logdata)
|
||||
return None
|
||||
logdata = dict()
|
||||
logdata['file'] = dest_abspath
|
||||
logdata['with_action'] = shortcut.action
|
||||
log('D106', logdata)
|
||||
shortcut.apply_desktop(dest_abspath)
|
||||
|
||||
class shortcut_applier(applier_frontend):
|
||||
__module_name = 'ShortcutsApplier'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def apply(self):
|
||||
def run(self):
|
||||
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
|
||||
# databases located in /usr/share/applications and
|
||||
# /usr/local/share/applications
|
||||
subprocess.check_call(['/usr/bin/update-desktop-database'])
|
||||
else:
|
||||
logging.debug(slogm('No shortcuts to process for {}'.format(self.storage.get_info('machine_sid'))))
|
||||
# According to ArchWiki - this thing is needed to rebuild MIME
|
||||
# type cache in order file bindings to work. This rebuilds
|
||||
# databases located in /usr/share/applications and
|
||||
# /usr/local/share/applications
|
||||
subprocess.check_call(['/usr/bin/update-desktop-database'])
|
||||
logdata = dict()
|
||||
logdata['machine_sid'] = self.storage.get_info('machine_sid')
|
||||
log('D100', logdata)
|
||||
|
||||
def apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D98')
|
||||
self.run()
|
||||
else:
|
||||
log('D99')
|
||||
|
||||
class shortcut_applier_user(applier_frontend):
|
||||
__module_name = 'ShortcutsApplierUser'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
self.storage = storage
|
||||
self.sid = sid
|
||||
self.username = username
|
||||
|
||||
def user_context_apply(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)))
|
||||
logdata = dict()
|
||||
logdata['sid'] = self.sid
|
||||
log('D100', logdata)
|
||||
|
||||
def user_context_apply(self):
|
||||
if self.__module_enabled:
|
||||
log('D101')
|
||||
self.run(True)
|
||||
else:
|
||||
log('D102')
|
||||
|
||||
def admin_context_apply(self):
|
||||
shortcuts = storage_get_shortcuts(self.storage, self.sid)
|
||||
|
||||
if shortcuts:
|
||||
for sc in shortcuts:
|
||||
if not sc.is_usercontext():
|
||||
write_shortcut(sc, self.username)
|
||||
if self.__module_enabled:
|
||||
log('D103')
|
||||
self.run(False)
|
||||
else:
|
||||
logging.debug(slogm('No shortcuts to process for {}'.format(self.sid)))
|
||||
log('D104')
|
||||
|
||||
|
@ -16,38 +16,66 @@
|
||||
# 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
|
||||
from .applier_frontend import (
|
||||
applier_frontend
|
||||
, check_enabled
|
||||
)
|
||||
from .appliers.systemd import systemd_unit
|
||||
from util.logging import slogm
|
||||
from util.logging import slogm, log
|
||||
|
||||
import logging
|
||||
|
||||
class systemd_applier(applier_frontend):
|
||||
__module_name = 'SystemdApplier'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\SystemdUnits'
|
||||
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
self.systemd_unit_settings = self.storage.filter_hklm_entries('Software\\BaseALT\\Policies\\SystemdUnits%')
|
||||
self.units = []
|
||||
self.__module_enabled = check_enabled(
|
||||
self.storage
|
||||
, self.__module_name
|
||||
, self.__module_experimental
|
||||
)
|
||||
|
||||
def run(self):
|
||||
for setting in self.systemd_unit_settings:
|
||||
valuename = setting.hive_key.rpartition('\\')[2]
|
||||
try:
|
||||
self.units.append(systemd_unit(valuename, int(setting.data)))
|
||||
logdata = dict()
|
||||
logdata['unit'] = format(valuename)
|
||||
log('I4', logdata)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['unit'] = format(valuename)
|
||||
logdata['exc'] = exc
|
||||
log('I5', logdata)
|
||||
for unit in self.units:
|
||||
try:
|
||||
unit.apply()
|
||||
except:
|
||||
logdata = dict()
|
||||
logdata['unit'] = unit.unit_name
|
||||
log('E45', logdata)
|
||||
|
||||
def apply(self):
|
||||
'''
|
||||
Trigger control facility invocation.
|
||||
'''
|
||||
for setting in self.systemd_unit_settings:
|
||||
valuename = setting.hive_key.rpartition('\\')[2]
|
||||
try:
|
||||
self.units.append(systemd_unit(valuename, int(setting.data)))
|
||||
logging.info(slogm('Working with systemd unit {}'.format(valuename)))
|
||||
except Exception as exc:
|
||||
logging.info(slogm('Unable to work with systemd unit {}: {}'.format(valuename, exc)))
|
||||
for unit in self.units:
|
||||
try:
|
||||
unit.apply()
|
||||
except:
|
||||
logging.error(slogm('Failed applying unit {}'.format(unit.unit_name)))
|
||||
if self.__module_enabled:
|
||||
log('D78')
|
||||
self.run()
|
||||
else:
|
||||
log('D79')
|
||||
|
||||
class systemd_applier_user(applier_frontend):
|
||||
__module_name = 'SystemdApplierUser'
|
||||
__module_experimental = False
|
||||
__module_enabled = True
|
||||
__registry_branch = 'Software\\BaseALT\\Policies\\SystemdUnits'
|
||||
|
||||
def __init__(self, storage, sid, username):
|
||||
|
90
gpoa/gpoa
90
gpoa/gpoa
@ -18,16 +18,17 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import gettext
|
||||
import locale
|
||||
|
||||
from backend import backend_factory
|
||||
from frontend.frontend_manager import frontend_manager, determine_username
|
||||
from plugin import plugin_manager
|
||||
from messages import message_with_code
|
||||
|
||||
from util.util import get_machine_name
|
||||
from util.kerberos import machine_kinit
|
||||
from util.users import (
|
||||
is_root,
|
||||
get_process_user
|
||||
@ -35,7 +36,8 @@ from util.users import (
|
||||
from util.arguments import (
|
||||
set_loglevel
|
||||
)
|
||||
from util.logging import slogm
|
||||
from util.logging import log
|
||||
from util.exceptions import geterr
|
||||
from util.signals import signal_handler
|
||||
|
||||
def parse_arguments():
|
||||
@ -56,6 +58,9 @@ def parse_arguments():
|
||||
arguments.add_argument('--noplugins',
|
||||
action='store_true',
|
||||
help='Don\'t start plugins')
|
||||
arguments.add_argument('--list-backends',
|
||||
action='store_true',
|
||||
help='Show list of available backends')
|
||||
arguments.add_argument('--loglevel',
|
||||
type=int,
|
||||
default=4,
|
||||
@ -63,34 +68,60 @@ def parse_arguments():
|
||||
return arguments.parse_args()
|
||||
|
||||
class gpoa_controller:
|
||||
__kinit_successful = False
|
||||
__args = None
|
||||
|
||||
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)
|
||||
self.__kinit_successful = machine_kinit()
|
||||
|
||||
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()
|
||||
logging.debug(slogm('The process was started for user {} with UID {}'.format(uname, uid), uid=uid))
|
||||
logdata = dict()
|
||||
logdata['username'] = self.username
|
||||
logdata['is_machine'] = self.is_machine
|
||||
logdata['process_username'] = uname
|
||||
logdata['process_uid'] = uid
|
||||
|
||||
if self.is_machine:
|
||||
log('D61', logdata)
|
||||
else:
|
||||
log('D1', logdata)
|
||||
self.username = determine_username(self.username)
|
||||
|
||||
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):
|
||||
'''
|
||||
GPOA controller entry point
|
||||
'''
|
||||
if self.__args.list_backends:
|
||||
print('local')
|
||||
print('samba')
|
||||
return
|
||||
self.start_plugins()
|
||||
self.start_backend()
|
||||
self.start_frontend()
|
||||
|
||||
def start_backend(self):
|
||||
'''
|
||||
@ -101,11 +132,33 @@ class gpoa_controller:
|
||||
if self.__args.nodomain:
|
||||
nodomain = True
|
||||
|
||||
if not self.__args.noupdate:
|
||||
if not self.noupdate:
|
||||
if is_root():
|
||||
back = backend_factory(dc, self.username, self.is_machine, nodomain)
|
||||
back = None
|
||||
try:
|
||||
back = backend_factory(dc, self.username, self.is_machine, nodomain)
|
||||
except Exception as exc:
|
||||
logdata = dict({'msg': str(exc)})
|
||||
einfo = geterr()
|
||||
print(einfo)
|
||||
print(type(einfo))
|
||||
#logdata.update(einfo)
|
||||
log('E12', logdata)
|
||||
if back:
|
||||
back.retrieve_and_store()
|
||||
try:
|
||||
back.retrieve_and_store()
|
||||
# Start frontend only on successful backend finish
|
||||
self.start_frontend()
|
||||
except Exception as exc:
|
||||
logdata = dict({'message': str(exc)})
|
||||
# In case we're handling "E3" - it means that
|
||||
# this is a very specific exception that was
|
||||
# not handled properly on lower levels of
|
||||
# code so we're also printing file name and
|
||||
# other information.
|
||||
einfo = geterr()
|
||||
logdata.update(einfo)
|
||||
log('E3', logdata)
|
||||
|
||||
def start_frontend(self):
|
||||
'''
|
||||
@ -115,7 +168,11 @@ class gpoa_controller:
|
||||
appl = frontend_manager(self.username, self.is_machine)
|
||||
appl.apply_parameters()
|
||||
except Exception as exc:
|
||||
logging.error(slogm('Error occured while running applier: {}'.format(exc)))
|
||||
logdata = dict({'message': str(exc)})
|
||||
einfo = geterr()
|
||||
#print(einfo)
|
||||
logdata.update(einfo)
|
||||
log('E4', logdata)
|
||||
|
||||
def start_plugins(self):
|
||||
'''
|
||||
@ -130,6 +187,7 @@ def main():
|
||||
controller.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
default_handler = signal.getsignal(signal.SIGINT)
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
main()
|
||||
|
||||
|
@ -22,24 +22,13 @@ from Crypto.Cipher import AES
|
||||
|
||||
from util.xml import get_xml_root
|
||||
|
||||
def read_drives(drives_file):
|
||||
drives = list()
|
||||
|
||||
for drive in get_xml_root(drives_file):
|
||||
drive_obj = drivemap()
|
||||
|
||||
props = drive.find('Properties')
|
||||
drive_obj.set_login(props.get('username'))
|
||||
drive_obj.set_pass(props.get('cpassword'))
|
||||
|
||||
drives.append(drive_obj)
|
||||
|
||||
return drives
|
||||
|
||||
def decrypt_pass(cpassword):
|
||||
'''
|
||||
AES key for cpassword decryption: http://msdn.microsoft.com/en-us/library/2c15cbf0-f086-4c74-8b70-1f2fa45dd4be%28v=PROT.13%29#endNote2
|
||||
'''
|
||||
if not cpassword:
|
||||
return cpassword
|
||||
|
||||
key = (
|
||||
b'\x4e\x99\x06\xe8'
|
||||
b'\xfc\xb6\x6c\xc9'
|
||||
@ -53,23 +42,80 @@ def decrypt_pass(cpassword):
|
||||
cpass_len = len(cpassword)
|
||||
padded_pass = (cpassword + "=" * ((4 - cpass_len % 4) % 4))
|
||||
password = b64decode(padded_pass)
|
||||
decrypter = AES(key, AES.MODE_CBC, '\x00' * 16)
|
||||
decrypter = AES.new(key, AES.MODE_CBC, '\x00' * 16)
|
||||
|
||||
return decrypter.decrypt(password)
|
||||
# decrypt() returns byte array which is immutable and we need to
|
||||
# strip padding, then convert UTF-16LE to UTF-8
|
||||
binstr = decrypter.decrypt(password)
|
||||
by = list()
|
||||
for item in binstr:
|
||||
if item != 16:
|
||||
by.append(item)
|
||||
utf16str = bytes(by).decode('utf-16', 'ignore')
|
||||
utf8str = utf16str.encode('utf8')
|
||||
|
||||
return utf8str.decode()
|
||||
|
||||
def read_drives(drives_file):
|
||||
drives = list()
|
||||
|
||||
for drive in get_xml_root(drives_file):
|
||||
drive_obj = drivemap()
|
||||
|
||||
props = drive.find('Properties')
|
||||
drive_obj.set_login(props.get('username'))
|
||||
drive_obj.set_pass(decrypt_pass(props.get('cpassword')))
|
||||
drive_obj.set_dir(props.get('letter'))
|
||||
drive_obj.set_path(props.get('path'))
|
||||
|
||||
drives.append(drive_obj)
|
||||
|
||||
return drives
|
||||
|
||||
def merge_drives(storage, sid, drive_objects, policy_name):
|
||||
for drive in drive_objects:
|
||||
storage.add_drive(sid, drive, policy_name)
|
||||
|
||||
def json2drive(json_str):
|
||||
json_obj = json.loads(json_str)
|
||||
drive_obj = drivemap()
|
||||
|
||||
drive_obj.set_login(json_obj['login'])
|
||||
drive_obj.set_pass(json_obj['password'])
|
||||
drive_obj.set_dir(json_obj['dir'])
|
||||
drive_obj.set_path(json_obj['path'])
|
||||
|
||||
return drive_obj
|
||||
|
||||
class drivemap:
|
||||
def __init__(self):
|
||||
self.login = None
|
||||
self.password = None
|
||||
self.dir = None
|
||||
self.path = None
|
||||
|
||||
def set_login(self, username):
|
||||
self.login = username
|
||||
if not username:
|
||||
self.login = ''
|
||||
|
||||
def set_pass(self, password):
|
||||
self.password = password
|
||||
if not password:
|
||||
self.password = ''
|
||||
|
||||
def set_dir(self, path):
|
||||
self.dir = path
|
||||
|
||||
def set_path(self, path):
|
||||
self.path = path
|
||||
|
||||
def to_json(self):
|
||||
drive = dict()
|
||||
drive['login'] = self.login
|
||||
drive['password'] = self.password
|
||||
drive['dir'] = self.dir
|
||||
drive['path'] = self.path
|
||||
|
||||
contents = dict()
|
||||
contents['drive'] = drive
|
||||
|
@ -18,17 +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
|
||||
|
||||
class envvar:
|
||||
def __init__(self):
|
||||
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, name, value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.action = FileAction.CREATE
|
||||
|
||||
def set_action(self, action):
|
||||
self.action = action
|
||||
|
||||
|
@ -28,6 +28,10 @@ def read_files(filesxml):
|
||||
|
||||
return files
|
||||
|
||||
def merge_files(storage, sid, file_objects, policy_name):
|
||||
for fileobj in file_objects:
|
||||
pass
|
||||
|
||||
class fileentry:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
@ -16,19 +16,83 @@
|
||||
# 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 enum import Enum
|
||||
|
||||
|
||||
from util.xml import get_xml_root
|
||||
|
||||
|
||||
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 action_enum2letter(enumitem):
|
||||
return enumitem.value
|
||||
|
||||
|
||||
def folder_int2bool(val):
|
||||
value = val
|
||||
|
||||
if type(value) == str:
|
||||
value = int(value)
|
||||
|
||||
if value == 1:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def read_folders(folders_file):
|
||||
folders = list()
|
||||
|
||||
for fld in get_xml_root(folders_file):
|
||||
fld_obj = folderentry()
|
||||
props = fld.find('Properties')
|
||||
fld_obj = folderentry(props.get('path'))
|
||||
fld_obj.set_action(action_letter2enum(props.get('action', default='C')))
|
||||
fld_obj.set_delete_folder(folder_int2bool(props.get('deleteFolder', default=1)))
|
||||
fld_obj.set_delete_sub_folders(folder_int2bool(props.get('deleteSubFolders', default=1)))
|
||||
fld_obj.set_delete_files(folder_int2bool(props.get('deleteFiles', default=1)))
|
||||
|
||||
folders.append(fld_obj)
|
||||
|
||||
return folders
|
||||
|
||||
class folderentry:
|
||||
def __init__(self):
|
||||
pass
|
||||
def merge_folders(storage, sid, folder_objects, policy_name):
|
||||
for folder in folder_objects:
|
||||
storage.add_folder(sid, folder, policy_name)
|
||||
|
||||
|
||||
class folderentry:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.action = FileAction.CREATE
|
||||
self.delete_folder = False
|
||||
self.delete_sub_folders = False
|
||||
self.delete_files = False
|
||||
|
||||
def set_action(self, action):
|
||||
self.action = action
|
||||
|
||||
def set_delete_folder(self, del_bool):
|
||||
self.delete_folder = del_bool
|
||||
|
||||
def set_delete_sub_folders(self, del_bool):
|
||||
self.delete_sub_folders = del_bool
|
||||
|
||||
def set_delete_files(self, del_bool):
|
||||
self.delete_files = del_bool
|
||||
|
||||
|
359
gpoa/gpt/gpt.py
359
gpoa/gpt/gpt.py
@ -16,28 +16,128 @@
|
||||
# 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 logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from enum import Enum, unique
|
||||
|
||||
from samba.gp_parse.gp_pol import GPPolParser
|
||||
|
||||
from storage import registry_factory
|
||||
from .shortcuts import read_shortcuts
|
||||
from .services import read_services
|
||||
from .printers import read_printers
|
||||
from .inifiles import read_inifiles
|
||||
from .folders import read_folders
|
||||
from .files import read_files
|
||||
from .envvars import read_envvars
|
||||
from .drives import read_drives
|
||||
|
||||
from .polfile import (
|
||||
read_polfile
|
||||
, merge_polfile
|
||||
)
|
||||
from .shortcuts import (
|
||||
read_shortcuts
|
||||
, merge_shortcuts
|
||||
)
|
||||
from .services import (
|
||||
read_services
|
||||
, merge_services
|
||||
)
|
||||
from .printers import (
|
||||
read_printers
|
||||
, merge_printers
|
||||
)
|
||||
from .inifiles import (
|
||||
read_inifiles
|
||||
, merge_inifiles
|
||||
)
|
||||
from .folders import (
|
||||
read_folders
|
||||
, merge_folders
|
||||
)
|
||||
from .files import (
|
||||
read_files
|
||||
, merge_files
|
||||
)
|
||||
from .envvars import (
|
||||
read_envvars
|
||||
, merge_envvars
|
||||
)
|
||||
from .drives import (
|
||||
read_drives
|
||||
, merge_drives
|
||||
)
|
||||
from .tasks import (
|
||||
read_tasks
|
||||
, merge_tasks
|
||||
)
|
||||
|
||||
import util
|
||||
import util.preg
|
||||
from util.paths import (
|
||||
default_policy_path,
|
||||
local_policy_path,
|
||||
cache_dir,
|
||||
local_policy_cache
|
||||
)
|
||||
from util.logging import slogm
|
||||
from util.logging import log
|
||||
|
||||
|
||||
@unique
|
||||
class FileType(Enum):
|
||||
PREG = 'registry.pol'
|
||||
SHORTCUTS = 'shortcuts.xml'
|
||||
FOLDERS = 'folders.xml'
|
||||
FILES = 'files.xml'
|
||||
DRIVES = 'drives.xml'
|
||||
SCHEDULEDTASKS = 'scheduledtasks.xml'
|
||||
ENVIRONMENTVARIABLES = 'environmentvariables.xml'
|
||||
INIFILES = 'inifiles.xml'
|
||||
SERVICES = 'services.xml'
|
||||
PRINTERS = 'printers.xml'
|
||||
|
||||
def get_preftype(path_to_file):
|
||||
fpath = Path(path_to_file)
|
||||
|
||||
if fpath.exists():
|
||||
file_name = fpath.name.lower()
|
||||
for item in FileType:
|
||||
if file_name == item.value:
|
||||
return item
|
||||
|
||||
return None
|
||||
|
||||
def pref_parsers():
|
||||
parsers = dict()
|
||||
|
||||
parsers[FileType.PREG] = read_polfile
|
||||
parsers[FileType.SHORTCUTS] = read_shortcuts
|
||||
parsers[FileType.FOLDERS] = read_folders
|
||||
parsers[FileType.FILES] = read_files
|
||||
parsers[FileType.DRIVES] = read_drives
|
||||
parsers[FileType.SCHEDULEDTASKS] = read_tasks
|
||||
parsers[FileType.ENVIRONMENTVARIABLES] = read_envvars
|
||||
parsers[FileType.INIFILES] = read_inifiles
|
||||
parsers[FileType.SERVICES] = read_services
|
||||
parsers[FileType.PRINTERS] = read_printers
|
||||
|
||||
return parsers
|
||||
|
||||
def get_parser(preference_type):
|
||||
parsers = pref_parsers()
|
||||
return parsers[preference_type]
|
||||
|
||||
def pref_mergers():
|
||||
mergers = dict()
|
||||
|
||||
mergers[FileType.PREG] = merge_polfile
|
||||
mergers[FileType.SHORTCUTS] = merge_shortcuts
|
||||
mergers[FileType.FOLDERS] = merge_folders
|
||||
mergers[FileType.FILES] = merge_files
|
||||
mergers[FileType.DRIVES] = merge_drives
|
||||
mergers[FileType.SCHEDULEDTASKS] = merge_tasks
|
||||
mergers[FileType.ENVIRONMENTVARIABLES] = merge_envvars
|
||||
mergers[FileType.INIFILES] = merge_inifiles
|
||||
mergers[FileType.SERVICES] = merge_services
|
||||
mergers[FileType.PRINTERS] = merge_printers
|
||||
|
||||
return mergers
|
||||
|
||||
def get_merger(preference_type):
|
||||
mergers = pref_mergers()
|
||||
return mergers[preference_type]
|
||||
|
||||
class gpt:
|
||||
__user_policy_mode_key = 'Software\\Policies\\Microsoft\\Windows\\System\\UserPolicyMode'
|
||||
@ -46,27 +146,40 @@ class gpt:
|
||||
self.path = gpt_path
|
||||
self.sid = sid
|
||||
self.storage = registry_factory('registry')
|
||||
self._scan_gpt()
|
||||
|
||||
def _scan_gpt(self):
|
||||
'''
|
||||
Collect the data from the specified GPT on file system (cached
|
||||
by Samba).
|
||||
'''
|
||||
self.guid = self.path.rpartition('/')[2]
|
||||
self.name = ''
|
||||
|
||||
self.guid = self.path.rpartition('/')[2]
|
||||
if 'default' == self.guid:
|
||||
self.guid = 'Local Policy'
|
||||
|
||||
self._machine_path = find_dir(self.path, 'Machine')
|
||||
self._user_path = find_dir(self.path, 'User')
|
||||
self._machine_prefs = find_dir(self._machine_path, 'Preferences')
|
||||
self._user_prefs = find_dir(self._user_path, 'Preferences')
|
||||
|
||||
logging.debug(slogm('Looking for machine part of GPT {}'.format(self.guid)))
|
||||
self._find_machine()
|
||||
logging.debug(slogm('Looking for user part of GPT {}'.format(self.guid)))
|
||||
self._find_user()
|
||||
self.settings_list = [
|
||||
'shortcuts'
|
||||
, 'drives'
|
||||
, 'environmentvariables'
|
||||
, 'printers'
|
||||
, 'folders'
|
||||
, 'files'
|
||||
, 'inifiles'
|
||||
, 'services'
|
||||
, 'scheduledtasks'
|
||||
]
|
||||
self.settings = dict()
|
||||
self.settings['machine'] = dict()
|
||||
self.settings['user'] = dict()
|
||||
self.settings['machine']['regpol'] = find_file(self._machine_path, 'registry.pol')
|
||||
self.settings['user']['regpol'] = find_file(self._user_path, 'registry.pol')
|
||||
for setting in self.settings_list:
|
||||
machine_preffile = find_preffile(self._machine_path, setting)
|
||||
user_preffile = find_preffile(self._user_path, setting)
|
||||
mlogdata = dict({'setting': setting, 'prefpath': machine_preffile})
|
||||
log('D24', mlogdata)
|
||||
self.settings['machine'][setting] = machine_preffile
|
||||
ulogdata = dict({'setting': setting, 'prefpath': user_preffile})
|
||||
log('D23', ulogdata)
|
||||
self.settings['user'][setting] = user_preffile
|
||||
|
||||
def set_name(self, name):
|
||||
'''
|
||||
@ -89,142 +202,55 @@ class gpt:
|
||||
|
||||
return upm
|
||||
|
||||
def _find_user(self):
|
||||
self._user_regpol = self._find_regpol('user')
|
||||
self._user_shortcuts = self._find_shortcuts('user')
|
||||
|
||||
def _find_machine(self):
|
||||
self._machine_regpol = self._find_regpol('machine')
|
||||
self._machine_shortcuts = self._find_shortcuts('machine')
|
||||
|
||||
def _find_regpol(self, part):
|
||||
'''
|
||||
Find Registry.pol files.
|
||||
'''
|
||||
search_path = self._machine_path
|
||||
if 'user' == part:
|
||||
search_path = self._user_path
|
||||
if not search_path:
|
||||
return None
|
||||
|
||||
return find_file(search_path, 'registry.pol')
|
||||
|
||||
def _find_shortcuts(self, part):
|
||||
'''
|
||||
Find Shortcuts.xml files.
|
||||
'''
|
||||
shortcuts_dir = find_dir(self._machine_prefs, 'Shortcuts')
|
||||
shortcuts_file = find_file(shortcuts_dir, 'shortcuts.xml')
|
||||
|
||||
if 'user' == part:
|
||||
shortcuts_dir = find_dir(self._user_prefs, 'Shortcuts')
|
||||
shortcuts_file = find_file(shortcuts_dir, 'shortcuts.xml')
|
||||
|
||||
return shortcuts_file
|
||||
|
||||
def _find_envvars(self, part):
|
||||
'''
|
||||
Find EnvironmentVariables.xml files.
|
||||
'''
|
||||
search_path = os.path.join(self._machine_path, 'Preferences', 'EnvironmentVariables')
|
||||
if 'user' == part:
|
||||
search_path = os.path.join(self._user_path, 'Preferences', 'EnvironmentVariables')
|
||||
if not search_path:
|
||||
return None
|
||||
|
||||
return find_file(search_path, 'environmentvariables.xml')
|
||||
|
||||
def _find_drives(self, part):
|
||||
'''
|
||||
Find Drives.xml files.
|
||||
'''
|
||||
search_path = os.path.join(self._machine_path, 'Preferences', 'Drives')
|
||||
if 'user' == part:
|
||||
search_path = os.path.join(self._user_path, 'Preferences', 'Drives')
|
||||
if not search_path:
|
||||
return None
|
||||
|
||||
return find_file(search_path, 'drives.xml')
|
||||
|
||||
def _find_printers(self, part):
|
||||
'''
|
||||
Find Printers.xml files.
|
||||
'''
|
||||
search_path = os.path.join(self._machine_path, 'Preferences', 'Printers')
|
||||
if 'user' == part:
|
||||
search_path = os.path.join(self._user_path, 'Preferences', 'Printers')
|
||||
if not search_path:
|
||||
return None
|
||||
|
||||
return find_file(search_path, 'printers.xml')
|
||||
|
||||
def _merge_shortcuts(self):
|
||||
shortcuts = list()
|
||||
|
||||
if self.sid == self.storage.get_info('machine_sid'):
|
||||
shortcuts = read_shortcuts(self._machine_shortcuts)
|
||||
else:
|
||||
shortcuts = read_shortcuts(self._user_shortcuts)
|
||||
|
||||
for sc in shortcuts:
|
||||
self.storage.add_shortcut(self.sid, sc)
|
||||
|
||||
def merge(self):
|
||||
'''
|
||||
Merge machine and user (if sid provided) settings to storage.
|
||||
'''
|
||||
if self.sid == self.storage.get_info('machine_sid'):
|
||||
# Merge machine settings to registry if possible
|
||||
if self._machine_regpol:
|
||||
logging.debug(slogm('Merging machine settings from {}'.format(self._machine_regpol)))
|
||||
util.preg.merge_polfile(self._machine_regpol)
|
||||
if self._user_regpol:
|
||||
logging.debug(slogm('Merging machine(user) settings from {}'.format(self._machine_regpol)))
|
||||
util.preg.merge_polfile(self._user_regpol, self.sid)
|
||||
if self._machine_shortcuts:
|
||||
logging.debug(slogm('Merging machine shortcuts from {}'.format(self._machine_shortcuts)))
|
||||
self._merge_shortcuts()
|
||||
try:
|
||||
# Merge machine settings to registry if possible
|
||||
for preference_name, preference_path in self.settings['machine'].items():
|
||||
if preference_path:
|
||||
preference_type = get_preftype(preference_path)
|
||||
logdata = dict({'pref': preference_type.value, 'sid': self.sid})
|
||||
log('D28', logdata)
|
||||
preference_parser = get_parser(preference_type)
|
||||
preference_merger = get_merger(preference_type)
|
||||
preference_objects = preference_parser(preference_path)
|
||||
preference_merger(self.storage, self.sid, preference_objects, self.name)
|
||||
if self.settings['user']['regpol']:
|
||||
mulogdata = dict({'polfile': self.settings['machine']['regpol']})
|
||||
log('D35', mulogdata)
|
||||
util.preg.merge_polfile(self.settings['user']['regpol'], sid=self.sid, policy_name=self.name)
|
||||
if self.settings['machine']['regpol']:
|
||||
mlogdata = dict({'polfile': self.settings['machine']['regpol']})
|
||||
log('D34', mlogdata)
|
||||
util.preg.merge_polfile(self.settings['machine']['regpol'], policy_name=self.name)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['gpt'] = self.name
|
||||
logdata['msg'] = str(exc)
|
||||
log('E28', logdata)
|
||||
else:
|
||||
# Merge user settings if UserPolicyMode set accordingly
|
||||
# and user settings (for HKCU) are exist.
|
||||
policy_mode = upm2str(self.get_policy_mode())
|
||||
if 'Merge' == policy_mode or 'Not configured' == policy_mode:
|
||||
if self._user_regpol:
|
||||
logging.debug(slogm('Merging user settings from {} for {}'.format(self._user_regpol, self.sid)))
|
||||
util.preg.merge_polfile(self._user_regpol, self.sid)
|
||||
if self._user_shortcuts:
|
||||
logging.debug(slogm('Merging user shortcuts from {} for {}'.format(self._user_shortcuts, self.sid)))
|
||||
self._merge_shortcuts()
|
||||
|
||||
def __str__(self):
|
||||
template = '''
|
||||
GUID: {}
|
||||
Name: {}
|
||||
For SID: {}
|
||||
|
||||
Machine part: {}
|
||||
Machine Registry.pol: {}
|
||||
Machine Shortcuts.xml: {}
|
||||
|
||||
User part: {}
|
||||
User Registry.pol: {}
|
||||
User Shortcuts.xml: {}
|
||||
|
||||
'''
|
||||
result = template.format(
|
||||
self.guid,
|
||||
self.name,
|
||||
self.sid,
|
||||
|
||||
self._machine_path,
|
||||
self._machine_regpol,
|
||||
self._machine_shortcuts,
|
||||
|
||||
self._user_path,
|
||||
self._user_regpol,
|
||||
self._user_shortcuts,
|
||||
)
|
||||
return result
|
||||
try:
|
||||
for preference_name, preference_path in self.settings['user'].items():
|
||||
if preference_path:
|
||||
preference_type = get_preftype(preference_path)
|
||||
logdata = dict({'pref': preference_type.value, 'sid': self.sid})
|
||||
log('D29', logdata)
|
||||
preference_parser = get_parser(preference_type)
|
||||
preference_merger = get_merger(preference_type)
|
||||
preference_objects = preference_parser(preference_path)
|
||||
preference_merger(self.storage, self.sid, preference_objects, self.name)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['gpt'] = self.name
|
||||
logdata['msg'] = str(exc)
|
||||
log('E29', logdata)
|
||||
|
||||
def find_dir(search_path, name):
|
||||
'''
|
||||
@ -269,11 +295,38 @@ def find_file(search_path, name):
|
||||
|
||||
return None
|
||||
|
||||
def find_preferences(search_path):
|
||||
'''
|
||||
Find 'Preferences' directory
|
||||
'''
|
||||
if not search_path:
|
||||
return None
|
||||
|
||||
return find_dir(search_path, 'Preferences')
|
||||
|
||||
def find_preffile(search_path, prefname):
|
||||
'''
|
||||
Find file with path like Preferences/prefname/prefname.xml
|
||||
'''
|
||||
# Look for 'Preferences' directory
|
||||
prefdir = find_preferences(search_path)
|
||||
|
||||
if not prefdir:
|
||||
return None
|
||||
|
||||
# Then search for preference directory
|
||||
pref_dir = find_dir(prefdir, prefname)
|
||||
file_name = '{}.xml'.format(prefname)
|
||||
# And then try to find the corresponding file.
|
||||
pref_file = find_file(pref_dir, file_name)
|
||||
|
||||
return pref_file
|
||||
|
||||
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()
|
||||
@ -291,7 +344,7 @@ def get_local_gpt(sid):
|
||||
'''
|
||||
Convert default policy to GPT and create object out of it.
|
||||
'''
|
||||
logging.debug(slogm('Re-caching Local Policy'))
|
||||
log('D25')
|
||||
lp2gpt()
|
||||
local_policy = gpt(str(local_policy_cache()), sid)
|
||||
local_policy.set_name('Local Policy')
|
||||
|
@ -28,6 +28,10 @@ def read_inifiles(inifiles_file):
|
||||
|
||||
return inifiles
|
||||
|
||||
def merge_inifiles(storage, sid, inifile_objects, policy_name):
|
||||
for inifile in inifile_objects:
|
||||
pass
|
||||
|
||||
def inifile():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
32
gpoa/gpt/polfile.py
Normal file
32
gpoa/gpt/polfile.py
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# 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 util.preg import (
|
||||
load_preg
|
||||
)
|
||||
|
||||
def read_polfile(filename):
|
||||
return load_preg(filename).entries
|
||||
|
||||
def merge_polfile(storage, sid, policy_objects, policy_name):
|
||||
for entry in policy_objects:
|
||||
if not sid:
|
||||
storage.add_hklm_entry(entry, policy_name)
|
||||
else:
|
||||
storage.add_hkcu_entry(entry, sid, policy_name)
|
||||
|
@ -41,6 +41,10 @@ def read_printers(printers_file):
|
||||
|
||||
return printers
|
||||
|
||||
def merge_printers(storage, sid, printer_objects, policy_name):
|
||||
for device in printer_objects:
|
||||
storage.add_printer(sid, device, policy_name)
|
||||
|
||||
def json2printer(json_str):
|
||||
'''
|
||||
Build printer object out of string-serialized JSON.
|
||||
|
@ -39,6 +39,10 @@ def read_services(service_file):
|
||||
|
||||
return services
|
||||
|
||||
def merge_services(storage, sid, service_objects, policy_name):
|
||||
for srv in service_objects:
|
||||
pass
|
||||
|
||||
class service:
|
||||
def __init__(self, name):
|
||||
self.unit = name
|
||||
|
@ -79,15 +79,20 @@ 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'))
|
||||
sc.set_usercontext(link.get('userContext', False))
|
||||
sc.set_icon(props.get('iconPath'))
|
||||
shortcuts.append(sc)
|
||||
|
||||
return shortcuts
|
||||
|
||||
def merge_shortcuts(storage, sid, shortcut_objects, policy_name):
|
||||
for shortcut in shortcut_objects:
|
||||
storage.add_shortcut(sid, shortcut, policy_name)
|
||||
|
||||
def json2sc(json_str):
|
||||
'''
|
||||
Build shortcut out of string-serialized JSON
|
||||
@ -95,16 +100,18 @@ 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'])
|
||||
sc.set_usercontext(json_obj['is_in_user_context'])
|
||||
if 'icon' in json_obj:
|
||||
sc.set_icon(json_obj['icon'])
|
||||
|
||||
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
|
||||
@ -114,9 +121,12 @@ 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()
|
||||
self.type = ttype
|
||||
|
||||
@ -136,6 +146,9 @@ class shortcut:
|
||||
def set_guid(self, uid):
|
||||
self.guid = uid
|
||||
|
||||
def set_icon(self, icon_name):
|
||||
self.icon = icon_name
|
||||
|
||||
def set_type(self, ttype):
|
||||
'''
|
||||
Set type of the hyperlink - FILESYSTEM or URL
|
||||
@ -155,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
|
||||
|
||||
@ -170,44 +189,88 @@ 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:
|
||||
content['icon'] = self.icon
|
||||
result = self.desktop()
|
||||
result.content.update(content)
|
||||
|
||||
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))
|
||||
|
||||
return self.desktop_file
|
||||
if self.icon:
|
||||
self.desktop_file.set('Icon', self.icon)
|
||||
|
||||
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)
|
||||
|
25
gpoa/gpt/tasks.py
Normal file
25
gpoa/gpt/tasks.py
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
def read_tasks(filename):
|
||||
pass
|
||||
|
||||
def merge_tasks(storage, sid, task_objects, policy_name):
|
||||
for task in task_objects:
|
||||
pass
|
||||
|
@ -18,11 +18,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import argparse
|
||||
|
||||
import locale
|
||||
import gettext
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import pwd
|
||||
import signal
|
||||
|
||||
@ -31,6 +31,7 @@ from util.users import (
|
||||
)
|
||||
from util.arguments import (
|
||||
process_target,
|
||||
set_loglevel,
|
||||
ExitCodeUpdater
|
||||
)
|
||||
from util.dbus import (
|
||||
@ -39,7 +40,9 @@ from util.dbus import (
|
||||
)
|
||||
from util.signals import signal_handler
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
from util.logging import log
|
||||
|
||||
#logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
class file_runner:
|
||||
_gpoa_exe = '/usr/sbin/gpoa'
|
||||
@ -71,6 +74,10 @@ def parse_cli_arguments():
|
||||
default=None,
|
||||
type=str,
|
||||
help='Specify if it is needed to update user\'s or computer\'s policies')
|
||||
argparser.add_argument('--loglevel',
|
||||
type=int,
|
||||
default=5,
|
||||
help='Set logging verbosity level')
|
||||
|
||||
return argparser.parse_args()
|
||||
|
||||
@ -89,24 +96,18 @@ def runner_factory(args, target):
|
||||
target = 'Computer'
|
||||
except:
|
||||
username = None
|
||||
logstring = (
|
||||
'Unable to perform gpupdate for non-existent user {},'
|
||||
' will update machine settings'
|
||||
)
|
||||
logging.error(logstring.format(args.user))
|
||||
logdata = dict({'username': args.user})
|
||||
log('W1', logdata)
|
||||
else:
|
||||
# User may only perform gpupdate for machine (None) or
|
||||
# itself (os.getusername()).
|
||||
username = pwd.getpwuid(os.getuid()).pw_name
|
||||
if args.user != username:
|
||||
logstring = (
|
||||
'Unable to perform gpupdate for {} with current'
|
||||
' permissions, will update current user settings'
|
||||
)
|
||||
logging.error(logstring.format(args.user))
|
||||
logdata = dict({'username': username})
|
||||
log('W2', logdata)
|
||||
|
||||
if is_oddjobd_gpupdate_accessible():
|
||||
logging.debug('Starting gpupdate via D-Bus')
|
||||
log('D13')
|
||||
computer_runner = None
|
||||
user_runner = None
|
||||
if target == 'All' or target == 'Computer':
|
||||
@ -116,10 +117,10 @@ def runner_factory(args, target):
|
||||
user_runner = dbus_runner(username)
|
||||
return (computer_runner, user_runner)
|
||||
else:
|
||||
logging.warning('oddjobd is inaccessible')
|
||||
log('W3')
|
||||
|
||||
if is_root():
|
||||
logging.debug('Starting gpupdate by command invocation')
|
||||
log('D14')
|
||||
computer_runner = None
|
||||
user_runner = None
|
||||
if target == 'All' or target == 'Computer':
|
||||
@ -128,12 +129,16 @@ def runner_factory(args, target):
|
||||
user_runner = file_runner(username)
|
||||
return (computer_runner, user_runner)
|
||||
else:
|
||||
logging.error('Insufficient permissions to run gpupdate')
|
||||
log('E1')
|
||||
|
||||
return None
|
||||
|
||||
def main():
|
||||
args = parse_cli_arguments()
|
||||
locale.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||
gettext.textdomain('gpoa')
|
||||
set_loglevel(args.loglevel)
|
||||
gpo_appliers = runner_factory(args, process_target(args.target))
|
||||
|
||||
if gpo_appliers:
|
||||
@ -141,17 +146,19 @@ def main():
|
||||
try:
|
||||
gpo_appliers[0].run()
|
||||
except Exception as exc:
|
||||
logging.error('Error running GPOA for computer: {}'.format(exc))
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E5')
|
||||
return int(ExitCodeUpdater.FAIL_GPUPDATE_COMPUTER_NOREPLY)
|
||||
|
||||
if gpo_appliers[1]:
|
||||
try:
|
||||
gpo_appliers[1].run()
|
||||
except Exception as exc:
|
||||
logging.error('Error running GPOA for user: {}'.format(exc))
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E6', logdata)
|
||||
return int(ExitCodeUpdater.FAIL_GPUPDATE_USER_NOREPLY)
|
||||
else:
|
||||
logging.error('gpupdate will not be started')
|
||||
log('E2')
|
||||
return int(ExitCodeUpdater.FAIL_NO_RUNNER)
|
||||
|
||||
return int(ExitCodeUpdater.EXIT_SUCCESS)
|
||||
|
327
gpoa/gpupdate-setup
Executable file
327
gpoa/gpupdate-setup
Executable file
@ -0,0 +1,327 @@
|
||||
#! /usr/bin/env python3
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
from util.util import (
|
||||
runcmd
|
||||
, get_backends
|
||||
, get_default_policy_name
|
||||
, get_policy_entries
|
||||
, 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'
|
||||
|
||||
def __init__(self):
|
||||
self.arguments = parse_arguments()
|
||||
|
||||
def parse_arguments():
|
||||
'''
|
||||
Parse CLI arguments.
|
||||
'''
|
||||
parser = argparse.ArgumentParser(prog='gpupdate-setup')
|
||||
subparsers = parser.add_subparsers(dest='action',
|
||||
metavar='action',
|
||||
help='Group Policy management actions (default action is status)')
|
||||
|
||||
parser_list = subparsers.add_parser('list',
|
||||
help='List avalable types of local policy')
|
||||
parser_list = subparsers.add_parser('list-backends',
|
||||
help='Show list of available backends')
|
||||
parser_status = subparsers.add_parser('status',
|
||||
help='Show current Group Policy status')
|
||||
parser_enable = subparsers.add_parser('enable',
|
||||
help='Enable Group Policy subsystem')
|
||||
|
||||
parser_disable = subparsers.add_parser('disable',
|
||||
help='Disable Group Policy subsystem')
|
||||
|
||||
parser_write = subparsers.add_parser('write',
|
||||
help='Operate on Group Policies (enable or disable)')
|
||||
parser_set_backend = subparsers.add_parser('set-backend',
|
||||
help='Set or change currently active backend')
|
||||
parser_default = subparsers.add_parser('default-policy',
|
||||
help='Show name of default policy')
|
||||
parser_active = subparsers.add_parser('active-policy',
|
||||
help='Show name of policy enabled')
|
||||
parser_active_backend = subparsers.add_parser('active-backend',
|
||||
help='Show currently configured backend')
|
||||
|
||||
parser_set_backend.add_argument('backend',
|
||||
default='samba',
|
||||
type=str,
|
||||
nargs='?',
|
||||
const='backend',
|
||||
choices=['local', 'samba'],
|
||||
help='Backend (source of settings) name')
|
||||
|
||||
parser_write.add_argument('status',
|
||||
choices=['enable', 'disable'],
|
||||
help='Enable or disable Group Policies')
|
||||
parser_write.add_argument('localpolicy',
|
||||
default=None,
|
||||
nargs='?',
|
||||
help='Name of local policy to enable')
|
||||
parser_write.add_argument('backend',
|
||||
default='samba',
|
||||
type=str,
|
||||
nargs='?',
|
||||
const='backend',
|
||||
choices=['local', 'samba'],
|
||||
help='Backend (source of settings) name')
|
||||
|
||||
parser_enable.add_argument('--local-policy',
|
||||
default=None,
|
||||
help='Name of local policy to enable')
|
||||
parser_enable.add_argument('--backend',
|
||||
default='samba',
|
||||
type=str,
|
||||
choices=['local', 'samba'],
|
||||
help='Backend (source of settings) name')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def validate_policy_name(policy_name):
|
||||
return policy_name in [os.path.basename(d) for d in get_policy_variants()]
|
||||
|
||||
def is_unit_enabled(unit_name, unit_global=False):
|
||||
'''
|
||||
Check that designated systemd unit is enabled
|
||||
'''
|
||||
command = ['/bin/systemctl', 'is-enabled', unit_name]
|
||||
if unit_global:
|
||||
command = ['/bin/systemctl', '--global', 'is-enabled', unit_name]
|
||||
value = runcmd(command)
|
||||
|
||||
# If first line of stdout is equal to "enabled" and return code
|
||||
# is zero then unit is considered enabled.
|
||||
rc = value[0]
|
||||
result = []
|
||||
try:
|
||||
result = value[1].replace('\n', '')
|
||||
except IndexError as exc:
|
||||
return False
|
||||
|
||||
if result == 'enabled' and rc == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_status():
|
||||
'''
|
||||
Check that gpupdate.service and gpupdate-user.service are enabled.
|
||||
'''
|
||||
is_gpupdate = is_unit_enabled('gpupdate.service')
|
||||
is_gpupdate_user = is_unit_enabled('gpupdate-user.service', unit_global=True)
|
||||
|
||||
if is_gpupdate and is_gpupdate_user:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_active_policy_name():
|
||||
'''
|
||||
Show the name of an active Local Policy template
|
||||
'''
|
||||
config = GPConfig()
|
||||
return os.path.basename(config.get_local_policy_template())
|
||||
|
||||
def get_active_backend():
|
||||
config = GPConfig()
|
||||
return config.get_backend()
|
||||
|
||||
def rollback_on_error(command_name):
|
||||
'''
|
||||
Disable group policy services in case command returns error code
|
||||
'''
|
||||
if 0 != runcmd(command_name)[0]:
|
||||
disable_gp()
|
||||
return False
|
||||
return True
|
||||
|
||||
def disable_gp():
|
||||
'''
|
||||
Consistently disable group policy services
|
||||
'''
|
||||
cmd_set_global_policy = ['/usr/sbin/control', 'system-policy', 'remote']
|
||||
cmd_set_local_policy = ['/usr/sbin/control', 'system-policy', 'local']
|
||||
cmd_disable_gpupdate_service = ['/bin/systemctl', 'disable', 'gpupdate.service']
|
||||
cmd_disable_gpupdate_user_service = ['/bin/systemctl', '--global', 'disable', 'gpupdate-user.service']
|
||||
cmd_control_system_auth = ['/usr/sbin/control', 'system-auth']
|
||||
|
||||
config = GPConfig()
|
||||
|
||||
auth_result = 'local'
|
||||
try:
|
||||
auth_result = runcmd(cmd_control_system_auth)[1][0]
|
||||
except Exception as exc:
|
||||
print(str(exc))
|
||||
|
||||
if auth_result != 'local':
|
||||
runcmd(cmd_set_global_policy)
|
||||
else:
|
||||
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
|
||||
'''
|
||||
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']
|
||||
cmd_enable_gpupdate_user_service = ['/bin/systemctl', '--global', 'enable', 'gpupdate-user.service']
|
||||
|
||||
config = GPConfig()
|
||||
|
||||
custom_policy_dir = get_custom_policy_dir()
|
||||
if not os.path.isdir(custom_policy_dir):
|
||||
os.makedirs(custom_policy_dir)
|
||||
|
||||
target_policy_name = get_default_policy_name()
|
||||
if policy_name:
|
||||
if validate_policy_name(policy_name):
|
||||
target_policy_name = policy_name
|
||||
print (target_policy_name)
|
||||
|
||||
config.set_local_policy_template(target_policy_name)
|
||||
config.set_backend(backend_type)
|
||||
|
||||
# Enable oddjobd_gpupdate in PAM config
|
||||
if not rollback_on_error(cmd_set_gpupdate_policy):
|
||||
return
|
||||
# Bootstrap the Group Policy engine
|
||||
if not rollback_on_error(cmd_gpoa_nodomain):
|
||||
return
|
||||
# Enable gpupdate.service
|
||||
if not rollback_on_error(cmd_enable_gpupdate_service):
|
||||
return
|
||||
if not is_unit_enabled('gpupdate.service'):
|
||||
disable_gp()
|
||||
return
|
||||
# Enable gpupdate-setup.service for all users
|
||||
if not rollback_on_error(cmd_enable_gpupdate_user_service):
|
||||
return
|
||||
if not is_unit_enabled('gpupdate-user.service', unit_global=True):
|
||||
disable_gp()
|
||||
return
|
||||
|
||||
def act_list():
|
||||
'''
|
||||
Show list of available templates of Local Policy
|
||||
'''
|
||||
for entry in get_policy_variants():
|
||||
print(entry.rpartition('/')[2])
|
||||
|
||||
def act_list_backends():
|
||||
'''
|
||||
List backends supported by GPOA
|
||||
'''
|
||||
backends = get_backends()
|
||||
for backend in backends:
|
||||
print(backend)
|
||||
|
||||
def act_status():
|
||||
'''
|
||||
Check that group policy services are enabled
|
||||
'''
|
||||
if get_status():
|
||||
print('enabled')
|
||||
else:
|
||||
print('disabled')
|
||||
|
||||
def act_set_backend(backend_name):
|
||||
config = GPConfig()
|
||||
config.set_backend(backend_name)
|
||||
|
||||
def act_write(status, localpolicy, backend):
|
||||
'''
|
||||
Enable or disable group policy services
|
||||
'''
|
||||
if status == 'enable' or status == '#t':
|
||||
enable_gp(localpolicy, backend)
|
||||
if status == 'disable' or status == '#f':
|
||||
disable_gp()
|
||||
|
||||
def act_enable(localpolicy, backend):
|
||||
'''
|
||||
Enable group policy services
|
||||
'''
|
||||
enable_gp(localpolicy, backend)
|
||||
|
||||
def act_active_policy():
|
||||
'''
|
||||
Print active Local Policy template name to stdout
|
||||
'''
|
||||
print(get_active_policy_name())
|
||||
|
||||
def act_active_backend():
|
||||
'''
|
||||
Print currently configured backend.
|
||||
'''
|
||||
print(get_active_backend())
|
||||
|
||||
def act_default_policy():
|
||||
'''
|
||||
Print default Local Policy template name to stdout
|
||||
'''
|
||||
print(get_default_policy_name())
|
||||
|
||||
def main():
|
||||
arguments = parse_arguments()
|
||||
|
||||
action = dict()
|
||||
action['list'] = act_list
|
||||
action['list-backends'] = act_list_backends
|
||||
action['status'] = act_status
|
||||
action['set-backend'] = act_set_backend
|
||||
action['write'] = act_write
|
||||
action['enable'] = act_enable
|
||||
action['disable'] = disable_gp
|
||||
action['active-policy'] = act_active_policy
|
||||
action['active-backend'] = act_active_backend
|
||||
action['default-policy'] = act_default_policy
|
||||
|
||||
if arguments.action == None:
|
||||
action['status']()
|
||||
elif arguments.action == 'enable':
|
||||
action[arguments.action](arguments.local_policy, arguments.backend)
|
||||
elif arguments.action == 'write':
|
||||
action[arguments.action](arguments.status, arguments.localpolicy, arguments.backend)
|
||||
elif arguments.action == 'set-backend':
|
||||
action[arguments.action](arguments.backend)
|
||||
else:
|
||||
action[arguments.action]()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
710
gpoa/locale/ru_RU/LC_MESSAGES/gpoa.po
Normal file
710
gpoa/locale/ru_RU/LC_MESSAGES/gpoa.po
Normal file
@ -0,0 +1,710 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
#domain "gpoa"
|
||||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 0.8.0\n"
|
||||
"Report-Msgid-Bugs-To: samba@lists.altlinux.org\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain;charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
|
||||
msgid "Don't start plugins"
|
||||
msgstr "Не запускать модули"
|
||||
|
||||
# Info
|
||||
msgid "Got GPO list for username"
|
||||
msgstr "Получен список GPO для пользователя"
|
||||
|
||||
msgid "Got GPO"
|
||||
msgstr "Получен объект групповой политики"
|
||||
|
||||
msgid "Unknown info code"
|
||||
msgstr "Неизвестный код информационного сообщения"
|
||||
|
||||
msgid "Working with control"
|
||||
msgstr "Применение настроек control"
|
||||
|
||||
msgid "Working with systemd"
|
||||
msgstr "Работа с systemd"
|
||||
|
||||
msgid "Unable to work with systemd unit"
|
||||
msgstr "Невозможно создать оъект для unit systemd"
|
||||
|
||||
msgid "Starting systemd unit"
|
||||
msgstr "Запуск unit systemd"
|
||||
|
||||
msgid "Firefox policy"
|
||||
msgstr "Политика Firefox"
|
||||
|
||||
msgid "Chromium policy"
|
||||
msgstr "Политика Chromium"
|
||||
|
||||
msgid "Set user property to"
|
||||
msgstr "Установка свойств для пользователя"
|
||||
|
||||
# Error
|
||||
msgid "Insufficient permissions to run gpupdate"
|
||||
msgstr "Недостаточно прав для запуска gpupdate"
|
||||
|
||||
msgid "gpupdate will not be started"
|
||||
msgstr "gpupdate не будет запущен"
|
||||
|
||||
msgid "Backend execution error"
|
||||
msgstr "Ошибка бэкэнда"
|
||||
|
||||
msgid "Error occurred while running frontend manager"
|
||||
msgstr "Ошибка фронтенда"
|
||||
|
||||
msgid "Error running GPOA for computer"
|
||||
msgstr "Ошибка запуска GPOA для машины"
|
||||
|
||||
msgid "Error running GPOA for user"
|
||||
msgstr "Ошибка запуска GPOA для пользователя"
|
||||
|
||||
msgid "Unable to initialize Samba backend"
|
||||
msgstr "Невозможно инициализировать бэкэнд Samba"
|
||||
|
||||
msgid "Unable to initialize no-domain backend"
|
||||
msgstr "Невозможно инициализировать бэкэнд-заглушку"
|
||||
|
||||
msgid "Error running ADP"
|
||||
msgstr "Ошибка во время работы ADP"
|
||||
|
||||
msgid "Unable to determine DC hostname"
|
||||
msgstr "Невозможно определить имя контроллера домена"
|
||||
|
||||
msgid "Error occured while running applier with user privileges"
|
||||
msgstr "Ошибка во время работы applier в контексте пользователя"
|
||||
|
||||
msgid "Unable to initialize backend"
|
||||
msgstr "Невозможно инициализировать бэкэнд"
|
||||
|
||||
msgid "Not sufficient privileges to run machine appliers"
|
||||
msgstr "Недостаточно прав для запуска appliers для машины"
|
||||
|
||||
msgid "Kerberos ticket check failed"
|
||||
msgstr "Проверка билета Kerberos закончилась неудачно"
|
||||
|
||||
msgid "Unable to retrieve domain name via CLDAP query"
|
||||
msgstr "Не удалось определить имя домена AD через запрос к LDAP"
|
||||
|
||||
msgid "Error getting SID using wbinfo, will use SID from cache"
|
||||
msgstr "Не удалось определить SID с использованием утилиты wbinfo, будет использоваться фиктивный/кэшированный SID"
|
||||
|
||||
msgid "Unable to get GPO list for user from AD DC"
|
||||
msgstr "Не удалось получить список групповых политик для пользователя от контроллера домена AD"
|
||||
|
||||
msgid "Error getting XDG_DESKTOP_DIR"
|
||||
msgstr "Не удалось получить значение XDG_DESKTOP_DIR"
|
||||
|
||||
msgid "Error occured while running user applier in administrator context"
|
||||
msgstr "Ошибка выполнения applier в контексте администратора"
|
||||
|
||||
msgid "Error occured while running user applier in user context (with dropped privileges)"
|
||||
msgstr "Ошибка работы пользовательского applier в пользовательском контексте (со сбросом привилегий процесса)"
|
||||
|
||||
msgid "No reply from oddjobd GPOA runner via D-Bus for current user"
|
||||
msgstr "Не получен ответ от oddjobd для текущего пользователя"
|
||||
|
||||
msgid "No reply from oddjobd GPOA runner via D-Bus for computer"
|
||||
msgstr "Не получен ответ от oddjobd для компьютера"
|
||||
|
||||
msgid "No reply from oddjobd GPOA runner via D-Bus for user"
|
||||
msgstr "Не получен ответ от oddjobd для пользователя"
|
||||
|
||||
msgid "Error occured while running machine applier"
|
||||
msgstr "Ошибка во время работы applier для машины"
|
||||
|
||||
msgid "Error occured while initializing user applier"
|
||||
msgstr "Ошибка инициализации пользовательского applier"
|
||||
|
||||
msgid "Error merging machine GPT"
|
||||
msgstr "Ошибка слияния машинной групповой политики"
|
||||
|
||||
msgid "Error merging user GPT"
|
||||
msgstr "Ошибка слияния пользовательской групповой политики"
|
||||
|
||||
msgid "Error merging machine part of GPT"
|
||||
msgstr "Ошибка слияния машинной части групповой политики"
|
||||
|
||||
msgid "Error merging user part of GPT"
|
||||
msgstr "Ошибка слияния пользовательской части групповой политики"
|
||||
|
||||
msgid "Unknown error code"
|
||||
msgstr "Неизвестный код ошибки"
|
||||
|
||||
msgid "Unable to work with control"
|
||||
msgstr "Не удалось применить настройки control"
|
||||
|
||||
msgid "Control applier for machine will not be started"
|
||||
msgstr "Приминение Control для машины не удалось"
|
||||
|
||||
msgid "Error getting control"
|
||||
msgstr "Ошибка установки control"
|
||||
|
||||
msgid "Is not in possible values for control"
|
||||
msgstr "Не входит в возможные значения для control"
|
||||
|
||||
msgid "Unable to set"
|
||||
msgstr "Невозможно установить"
|
||||
|
||||
msgid "Unable to generate file"
|
||||
msgstr "Невозможно создать файл"
|
||||
|
||||
msgid "Failed applying unit"
|
||||
msgstr "Не удалось применить настройки"
|
||||
|
||||
msgid "Unable to start systemd unit"
|
||||
msgstr "Невозможно запустить systemd unit"
|
||||
|
||||
msgid "Unable to cache specified URI"
|
||||
msgstr "Невозможно кэшировать указанный URI"
|
||||
|
||||
msgid "Unable to cache specified URI for machine"
|
||||
msgstr "Невозможно кэшировать указанный URI для компьютера"
|
||||
|
||||
msgid "Error recompiling global GSettings schemas"
|
||||
msgstr "Ошибка перекомпиляции глобальных GSettings schemas"
|
||||
|
||||
msgid "Error update configuration dconf"
|
||||
msgstr "Ошибка обновления конфигурации dconf"
|
||||
|
||||
msgid "Unable to cache specified URI for user"
|
||||
msgstr "Невозможно кэшировать указанный URI для пользователя"
|
||||
|
||||
msgid "Chromium preferences file does not exist at the moment"
|
||||
msgstr "Файл настроек Chromium в данный момент не существует"
|
||||
|
||||
msgid "Error during attempt to read Chromium preferences for user"
|
||||
msgstr "Ошибка при попытке прочитать настройки Chromium для пользователя"
|
||||
|
||||
msgid "Fail for applying shortcut to file with %"
|
||||
msgstr "Не удалось применить ярлык к файлу с %"
|
||||
|
||||
msgid "Fail for applying shortcut to not absolute path"
|
||||
msgstr "Не удалось применить ярлык к не абсолютному пути"
|
||||
|
||||
msgid "Error running pkcon_runner sync for machine"
|
||||
msgstr "Ошибка при запуске pkcon_runner синхронно для компьютера"
|
||||
|
||||
msgid "Package install error"
|
||||
msgstr "Ошибка установки пакета"
|
||||
|
||||
msgid "Package remove error"
|
||||
msgstr "Ошибка удаления пакета"
|
||||
|
||||
msgid "Error running pkcon_runner sync for user"
|
||||
msgstr "Ошибка при запуске pkcon_runner синхронно для пользователя"
|
||||
|
||||
msgid "Error running pkcon_runner async for machine"
|
||||
msgstr "Ошибка при запуске pkcon_runner асинхронно для компьютера"
|
||||
|
||||
msgid "Error running pkcon_runner async for user"
|
||||
msgstr "Ошибка при запуске pkcon_runner асинхронно для пользователя"
|
||||
|
||||
# Error_end
|
||||
|
||||
# Debug
|
||||
msgid "The GPOA process was started for user"
|
||||
msgstr "Произведён запуск GPOA для обновления политик пользователя"
|
||||
|
||||
msgid "Username is not specified - will use username of the current process"
|
||||
msgstr "Имя пользователя не указано - будет использовано имя владельца процесса"
|
||||
|
||||
msgid "Initializing plugin manager"
|
||||
msgstr "Инициализация плагинов"
|
||||
|
||||
msgid "ADP plugin initialized"
|
||||
msgstr "Инициализирован плагин ADP"
|
||||
|
||||
msgid "Running ADP plugin"
|
||||
msgstr "Запущен плагин ADP"
|
||||
|
||||
msgid "Starting GPOA for user via D-Bus"
|
||||
msgstr "Запускается GPOA для пользователя обращением к oddjobd через D-Bus"
|
||||
|
||||
msgid "Cache directory determined"
|
||||
msgstr "Определена директория кэша Samba"
|
||||
|
||||
msgid "Initializing local backend without domain"
|
||||
msgstr "Инициализация бэкэнда-заглушки"
|
||||
|
||||
msgid "Initializing Samba backend for domain"
|
||||
msgstr "Инициализация бэкэнда Samba"
|
||||
|
||||
msgid "Group Policy target set for update"
|
||||
msgstr "Групповые политики будут обновлены для указанной цели"
|
||||
|
||||
msgid "Starting GPOA for computer via D-Bus"
|
||||
msgstr "Запускается GPOA для компьютера обращением к oddjobd через D-Bus"
|
||||
|
||||
msgid "Got exit code"
|
||||
msgstr "Получен код возврата из утилиты"
|
||||
|
||||
msgid "Starting GPOA via D-Bus"
|
||||
msgstr "Запускается GPOA обращением к oddjobd через D-Bus"
|
||||
|
||||
msgid "Starting GPOA via command invocation"
|
||||
msgstr "GPOA запускается с помощью прямого вызова приложения"
|
||||
|
||||
msgid "Username for frontend is determined"
|
||||
msgstr "Определено имя пользователя для фронтенда"
|
||||
|
||||
msgid "Applying computer part of settings"
|
||||
msgstr "Применение настроек для машины"
|
||||
|
||||
msgid "Kerberos ticket check succeed"
|
||||
msgstr "Проверка билета Kerberos прошла успешно"
|
||||
|
||||
msgid "Found AD domain via CLDAP query"
|
||||
msgstr "Имя домена Active Directory успешно определено при запросе к LDAP"
|
||||
|
||||
msgid "Setting info"
|
||||
msgstr "Установка вспомогательной переменной"
|
||||
|
||||
msgid "Initializing cache"
|
||||
msgstr "Инициализация кэша"
|
||||
|
||||
msgid "Set operational SID"
|
||||
msgstr "Установка рабочего SID"
|
||||
|
||||
msgid "Got PReg entry"
|
||||
msgstr "Получен ключ реестра"
|
||||
|
||||
msgid "Looking for preference in user part of GPT"
|
||||
msgstr "Поиск настроек в пользовательской части GPT"
|
||||
|
||||
msgid "Looking for preference in machine part of GPT"
|
||||
msgstr "Поиск настроек в машинной части GPT"
|
||||
|
||||
msgid "Re-caching Local Policy"
|
||||
msgstr "Обновление кэша локальной политики"
|
||||
|
||||
msgid "Adding HKCU entry"
|
||||
msgstr "Слияние ключа в пользовательскую (HKCU) часть реестра"
|
||||
|
||||
msgid "Skipping HKLM branch deletion key"
|
||||
msgstr "Пропускаем специальный ключ удаления ветви реестра HKLM"
|
||||
|
||||
msgid "Reading and merging machine preference"
|
||||
msgstr "Вычитывание и слияние машинных настроек"
|
||||
|
||||
msgid "Reading and merging user preference"
|
||||
msgstr "Вычитывание и слияние пользовательских настроек"
|
||||
|
||||
msgid "Found SYSVOL entry"
|
||||
msgstr "Найден путь SYSVOL"
|
||||
|
||||
msgid "Trying to load PReg from .pol file"
|
||||
msgstr "Пробуем загрузить ключи реестра из .pol файла"
|
||||
|
||||
msgid "Finished reading PReg from .pol file"
|
||||
msgstr "Вычитаны ключи реестра из .pol файла"
|
||||
|
||||
msgid "Determined length of PReg file"
|
||||
msgstr "Определена длина .pol файла"
|
||||
|
||||
msgid "Merging machine settings from PReg file"
|
||||
msgstr "Слияние машинных настроек из .pol файла"
|
||||
|
||||
msgid "Merging machine (user part) settings from PReg file"
|
||||
msgstr "Слияние пользовательской части машинных настроек из .pol файла"
|
||||
|
||||
msgid "Loading PReg from XML"
|
||||
msgstr "Загружаем ключи реестра из XML"
|
||||
|
||||
msgid "Setting process permissions"
|
||||
msgstr "Установка прав процесса"
|
||||
|
||||
msgid "Samba DC setting is overriden by user setting"
|
||||
msgstr "Используется указанный пользователем контроллер домена AD"
|
||||
|
||||
msgid "Saving information about drive mapping"
|
||||
msgstr "Сохранение информации о привязках дисков"
|
||||
|
||||
msgid "Saving information about printer"
|
||||
msgstr "Сохранение информации о принтерах"
|
||||
|
||||
msgid "Saving information about link"
|
||||
msgstr "Сохранение информации о ярлычках"
|
||||
|
||||
msgid "Saving information about folder"
|
||||
msgstr "Сохранение информации о папках"
|
||||
|
||||
msgid "No value cached for object"
|
||||
msgstr "Отсутствует кэшированное значение для объекта"
|
||||
|
||||
msgid "Key is already present in cache, will update the value"
|
||||
msgstr "Ключ уже существует, его значение будет обновлено"
|
||||
|
||||
msgid "GPO update started"
|
||||
msgstr "Начато обновление GPO"
|
||||
|
||||
msgid "GPO update finished"
|
||||
msgstr "Завершено обновление GPO"
|
||||
|
||||
msgid "Retrieving list of GPOs to replicate from AD DC"
|
||||
msgstr "Получение списка GPO для репликации с контроллера домена AD"
|
||||
|
||||
msgid "Establishing connection with AD DC"
|
||||
msgstr "Установка соединения с контроллером домена AD"
|
||||
|
||||
msgid "Started GPO replication from AD DC"
|
||||
msgstr "Начата репликация GPO от контроллера домена AD"
|
||||
|
||||
msgid "Finished GPO replication from AD DC"
|
||||
msgstr "Завершена репликация GPO от контроллера домена AD"
|
||||
|
||||
msgid "Skipping HKCU branch deletion key"
|
||||
msgstr "Пропускаем специальный ключ удаления ветви реестра HKCU"
|
||||
|
||||
msgid "Read domain name from configuration file"
|
||||
msgstr "Имя контроллера домена для репликации прочитано из файла конфигурации"
|
||||
|
||||
msgid "Saving information about environment variables"
|
||||
msgstr "Сохранение информации о переменных окружения"
|
||||
|
||||
msgid "Unknown debug code"
|
||||
msgstr "Неизвестный отладочный код"
|
||||
|
||||
msgid "Running Control applier for machine"
|
||||
msgstr "Начато применение Control для машины"
|
||||
|
||||
msgid "Setting control"
|
||||
msgstr "Установка control"
|
||||
|
||||
msgid "Deny_All setting found"
|
||||
msgstr "Deny_All настройка найдена"
|
||||
|
||||
msgid "Deny_All setting for user"
|
||||
msgstr "Deny_All настройка для пользователя"
|
||||
|
||||
msgid "Deny_All setting not found"
|
||||
msgstr "Deny_All настройка не найдена"
|
||||
|
||||
msgid "Deny_All setting not found for user"
|
||||
msgstr "Deny_All настройка не найдена для пользователя"
|
||||
|
||||
msgid "Running Polkit applier for machine"
|
||||
msgstr "Начато применение настроек Polkit для машины"
|
||||
|
||||
msgid "Running Polkit applier for user in administrator context"
|
||||
msgstr "Начато применение настроек Polkit пользователя в контексте администратора"
|
||||
|
||||
msgid "Polkit applier for machine will not be started"
|
||||
msgstr "Polkit для машины не запускается"
|
||||
|
||||
msgid "Polkit applier for user in administrator context will not be started"
|
||||
msgstr "Polkit для пользователя в контексте администратора не запускается"
|
||||
|
||||
msgid "Generated file"
|
||||
msgstr "Созданный файл"
|
||||
|
||||
msgid "Running systemd applier for machine"
|
||||
msgstr "Начато применение настроек systemd для машины"
|
||||
|
||||
msgid "Running systemd applier for machine will not be started"
|
||||
msgstr "Применение настроек systemd для машины не удалось"
|
||||
|
||||
msgid "Running GSettings applier for machine"
|
||||
msgstr "Запуск применение настроек GSettings для машины"
|
||||
|
||||
msgid "GSettings applier for machine will not be started"
|
||||
msgstr "Применение настроек GSettings для машины не удалось"
|
||||
|
||||
msgid "Removing GSettings policy file from previous run"
|
||||
msgstr "Удаление файла политики GSettings от предыдущего запуска"
|
||||
|
||||
msgid "Mapping Windows policies to GSettings policies"
|
||||
msgstr "Сопоставление политик Windows с политиками GSettings"
|
||||
|
||||
msgid "GSettings windows policies mapping not enabled"
|
||||
msgstr "Сопоставление политик Windows GSettings не включено"
|
||||
|
||||
msgid "Applying user setting"
|
||||
msgstr "Применение пользовательских настроек"
|
||||
|
||||
msgid "Found GSettings windows mapping"
|
||||
msgstr "Найдены соответствия настроек windows-GSettings"
|
||||
|
||||
msgid "Running GSettings applier for user in user context"
|
||||
msgstr "Запуск применение настроек GSettings в контексте пользователя"
|
||||
|
||||
msgid "GSettings applier for user in user context will not be started"
|
||||
msgstr "GSettings в контексте пользователя не запускается"
|
||||
|
||||
msgid "Applying machine setting"
|
||||
msgstr "Применение настроек машины"
|
||||
|
||||
msgid "Path not resolved as UNC URI"
|
||||
msgstr "Путь не разрешен"
|
||||
|
||||
msgid "Getting cached file for URI"
|
||||
msgstr "Получение кешированного файла для URI"
|
||||
|
||||
msgid "Wrote Firefox preferences to"
|
||||
msgstr "Настройки Firefox записаны в"
|
||||
|
||||
msgid "Found Firefox profile in"
|
||||
msgstr "Найден профиль Firefox в"
|
||||
|
||||
msgid "Running Firefox applier for machine"
|
||||
msgstr "Запуск применение настроек Firefox для машины"
|
||||
|
||||
msgid "Firefox applier for machine will not be started"
|
||||
msgstr "Применение настроек Firefox для компьютера не запускается"
|
||||
|
||||
msgid "Running Chromium applier for machine"
|
||||
msgstr "Запуск применение настроек Chromium для машины"
|
||||
|
||||
msgid "Chromium applier for machine will not be started"
|
||||
msgstr "Применение настроек Chromium для компьютера не запускается"
|
||||
|
||||
msgid "Wrote Chromium preferences to"
|
||||
msgstr "Настройки Chromium записаны в"
|
||||
|
||||
msgid "Running Shortcut applier for machine"
|
||||
msgstr "Запуск применение ярлыков для машины"
|
||||
|
||||
msgid "Shortcut applier for machine will not be started"
|
||||
msgstr "Применение ярлыков для компьютера не запускается"
|
||||
|
||||
msgid "No shortcuts to process for"
|
||||
msgstr "Нет ярлыков для обработки"
|
||||
|
||||
msgid "Running Shortcut applier for user in user context"
|
||||
msgstr "Запуск применение ярлыков в контексте пользователя"
|
||||
|
||||
msgid "Shortcut applier for user in user context will not be started"
|
||||
msgstr "Применение ярлыков в контексте пользователя не запускается"
|
||||
|
||||
msgid "Running Shortcut applier for user in administrator context"
|
||||
msgstr "Запуск применение ярлыков в контексте администратора"
|
||||
|
||||
msgid "Shortcut applier for user in administrator context will not be started"
|
||||
msgstr "Применение ярлыков в контексте администратора не запускается"
|
||||
|
||||
msgid "Try to expand path for shortcut"
|
||||
msgstr "Попытка расширить путь для ярлыка"
|
||||
|
||||
msgid "Applying shortcut file to"
|
||||
msgstr "Применение ярлыка к файлу"
|
||||
|
||||
msgid "Running Folder applier for machine"
|
||||
msgstr "Запуск применение папок для машины"
|
||||
|
||||
msgid "Folder applier for machine will not be started"
|
||||
msgstr "Применение папок для машины не запускается"
|
||||
|
||||
msgid "Running Folder applier for user in administrator context"
|
||||
msgstr "Запуск применение папок для пользователя в контексте администратора"
|
||||
|
||||
msgid "Folder applier for user in administrator context will not be started"
|
||||
msgstr "Применение папок для пользователя в контексте администратора не запускается"
|
||||
|
||||
msgid "Running Folder applier for user in user context"
|
||||
msgstr "Запуск применение папок для пользователя в контексте пользователя"
|
||||
|
||||
msgid "Folder applier for user in user context will not be started"
|
||||
msgstr "Применение папок для пользователя в контексте пользователя не запускается"
|
||||
|
||||
msgid "Running CUPS applier for machine"
|
||||
msgstr "Запуск применение настроек CUPS для машины"
|
||||
|
||||
msgid "CUPS applier for machine will not be started"
|
||||
msgstr "Применение настроек CUPS для машины не запускается"
|
||||
|
||||
msgid "Running CUPS applier for user in administrator context"
|
||||
msgstr "Запуск применение настроек CUPS для пользователя в контексте администратора"
|
||||
|
||||
msgid "CUPS applier for user in administrator context will not be started"
|
||||
msgstr "Применение настроек CUPS для пользователя в контексте администратора не запускается"
|
||||
|
||||
msgid "Running Firewall applier for machine"
|
||||
msgstr "Запуск применение настроек Firewall для машины"
|
||||
|
||||
msgid "Firewall is enabled"
|
||||
msgstr "Firewall включен"
|
||||
|
||||
msgid "Firewall is disabled, settings will be reset"
|
||||
msgstr "Firewall отключен, настройки будут сброшены"
|
||||
|
||||
msgid "Firewall applier will not be started"
|
||||
msgstr "Применение настроек Firewall не запускается"
|
||||
|
||||
msgid "Running NTP applier for machine"
|
||||
msgstr "Запуск применение настроек NTP для машины"
|
||||
|
||||
msgid "NTP server is configured to"
|
||||
msgstr "Сервер NTP настроен на"
|
||||
|
||||
msgid "Starting Chrony daemon"
|
||||
msgstr "Запуск демона Chrony"
|
||||
|
||||
msgid "Setting reference NTP server to"
|
||||
msgstr "Установка эталонного сервера NTP на"
|
||||
|
||||
msgid "Stopping Chrony daemon"
|
||||
msgstr "Остановка демона Chrony"
|
||||
|
||||
msgid "Configuring NTP server..."
|
||||
msgstr "Настройка NTP-сервера ..."
|
||||
|
||||
msgid "NTP server is enabled"
|
||||
msgstr "Сервер NTP включен"
|
||||
|
||||
msgid "NTP server is disabled"
|
||||
msgstr "NTP сервер отключен"
|
||||
|
||||
msgid "NTP server is not configured"
|
||||
msgstr "NTP сервер не настроен"
|
||||
|
||||
msgid "NTP client is enabled"
|
||||
msgstr "Клиент NTP включен"
|
||||
|
||||
msgid "NTP client is disabled"
|
||||
msgstr "Клиент NTP отключен"
|
||||
|
||||
msgid "NTP client is not configured"
|
||||
msgstr "NTP клиент не настроен"
|
||||
|
||||
msgid "NTP applier for machine will not be started"
|
||||
msgstr "Применение настроек NTP для машины не запускается"
|
||||
|
||||
msgid "Running Envvar applier for machine"
|
||||
msgstr "Запуск применение настроек Envvar для машины"
|
||||
|
||||
msgid "Envvar applier for machine will not be started"
|
||||
msgstr "Применение настроек Envvar для машины не запускается"
|
||||
|
||||
msgid "Running Envvar applier for user in user context"
|
||||
msgstr "Запуск применение настроек Envvar для пользователя в контексте пользователя"
|
||||
|
||||
msgid "Envvar applier for user in user context will not be started"
|
||||
msgstr "Применение настроек Envvar для пользователя в контексте пользователя не запускается"
|
||||
|
||||
msgid "Running Package applier for machine"
|
||||
msgstr "Запуск установки пакетов для машины"
|
||||
|
||||
msgid "Package applier for machine will not be started"
|
||||
msgstr "Применение установки пакетов для машины не запускается"
|
||||
|
||||
msgid "Running Package applier for user in administrator context"
|
||||
msgstr "Запуск установки пакетов для пользователя в контексте администратора"
|
||||
|
||||
msgid "Package applier for user in administrator context will not be started"
|
||||
msgstr "Применение установки пакетов для пользователя в контексте администратора не запускается"
|
||||
|
||||
msgid "Running pkcon_runner to install and remove packages"
|
||||
msgstr "Запуск pkcon_runner для установки и удаления пакетов"
|
||||
|
||||
msgid "Run apt-get update"
|
||||
msgstr "Запускаем apt-get update"
|
||||
|
||||
msgid "Error run apt-get update"
|
||||
msgstr "Ошибка запуска apt-get update"
|
||||
|
||||
msgid "Run user context applier with dropped privileges"
|
||||
msgstr "Запуск из контекста пользователя с удаленными привилегиями"
|
||||
|
||||
msgid "Run forked process with droped privileges"
|
||||
msgstr "Запустить разветвленный процесс с удаленными привилегиями"
|
||||
|
||||
msgid "Found connection by org.freedesktop.DBus.GetConnectionUnixProcessID"
|
||||
msgstr "Найдено соединение org.freedesktop.DBus.GetConnectionUnixProcessID"
|
||||
|
||||
msgid "Kill dbus-daemon and dconf-service in user context"
|
||||
msgstr "Остановка dbus-daemon и dconf-service в контексте пользователя"
|
||||
|
||||
msgid "Running CIFS applier for user in administrator context"
|
||||
msgstr "Запуск применение настроек CIFS для пользователя в контексте администратора"
|
||||
|
||||
msgid "CIFS applier for user in administrator context will not be started"
|
||||
msgstr "Применение настроек CIFS для пользователя в контексте администратора не запускается"
|
||||
|
||||
msgid "Installing the package"
|
||||
msgstr "Установка пакета"
|
||||
|
||||
msgid "Removing a package"
|
||||
msgstr "Удаление пакета"
|
||||
|
||||
msgid "Failed to found gsettings for machine"
|
||||
msgstr "Не удалось найти настройки gsettings для машины"
|
||||
|
||||
msgid "Failed to found user gsettings"
|
||||
msgstr "Не удалось найти настройки gsettings пользователя"
|
||||
|
||||
# Debug_end
|
||||
|
||||
# Warning
|
||||
msgid "Unable to perform gpupdate for non-existent user, will update machine settings"
|
||||
msgstr "Невозможно запустить gpupdate для несуществующего пользователя, будут обновлены настройки машины"
|
||||
|
||||
msgid "Current permissions does not allow to perform gpupdate for designated user. Will update current user settings"
|
||||
msgstr "Текущий уровень привилегий не позволяет выполнить gpupdate для указанного пользователя. Будут обновлены настройки текущего пользователя."
|
||||
|
||||
msgid "oddjobd is inaccessible"
|
||||
msgstr "oddjobd недоступен"
|
||||
|
||||
msgid "No SYSVOL entry assigned to GPO"
|
||||
msgstr "Объект групповой политики не имеет привязанного пути на SYSVOL"
|
||||
|
||||
|
||||
msgid "ADP package is not installed - plugin will not be initialized"
|
||||
msgstr "Пакет ADP не установлен, плагин не будет инициализирован"
|
||||
|
||||
msgid "Unknown warning code"
|
||||
msgstr "Неизвестный код предупреждения"
|
||||
|
||||
msgid "Unable to resolve GSettings parameter"
|
||||
msgstr "Не удалось установить параметр GSettings"
|
||||
|
||||
msgid "No home directory exists for user"
|
||||
msgstr "Для пользователя не существует домашнего каталога"
|
||||
|
||||
msgid "User's shortcut not placed to home directory"
|
||||
msgstr "Ярлык пользователя не помещен в домашний каталог"
|
||||
|
||||
msgid "CUPS is not installed: no printer settings will be deployed"
|
||||
msgstr "CUPS не установлен: настройки принтера не будут развернуты"
|
||||
|
||||
msgid "Unsupported NTP server type"
|
||||
msgstr "Неподдерживаемый тип сервера NTP"
|
||||
|
||||
# Fatal
|
||||
msgid "Unable to refresh GPO list"
|
||||
msgstr "Невозможно обновить список объектов групповых политик"
|
||||
|
||||
msgid "Error getting GPTs for machine"
|
||||
msgstr "Не удалось получить GPT для машины"
|
||||
|
||||
msgid "Error getting GPTs for user"
|
||||
msgstr "Не удалось получить GPT для пользователя"
|
||||
|
||||
msgid "Unknown fatal code"
|
||||
msgstr "Неизвестный код фатальной ошибки"
|
||||
|
||||
# get_message
|
||||
msgid "Unknown message type, no message assigned"
|
||||
msgstr "Неизвестный тип сообщения"
|
||||
|
310
gpoa/messages/__init__.py
Normal file
310
gpoa/messages/__init__.py
Normal file
@ -0,0 +1,310 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
import gettext
|
||||
|
||||
def info_code(code):
|
||||
info_ids = dict()
|
||||
info_ids[1] = 'Got GPO list for username'
|
||||
info_ids[2] = 'Got GPO'
|
||||
info_ids[3] = 'Working with control'
|
||||
info_ids[4] = 'Working with systemd'
|
||||
info_ids[5] = 'Unable to work with systemd unit'
|
||||
info_ids[6] = 'Starting systemd unit'
|
||||
info_ids[7] = 'Firefox policy'
|
||||
info_ids[8] = 'Chromium policy'
|
||||
info_ids[9] = 'Set user property to'
|
||||
|
||||
return info_ids.get(code, 'Unknown info code')
|
||||
|
||||
def error_code(code):
|
||||
error_ids = dict()
|
||||
error_ids[1] = 'Insufficient permissions to run gpupdate'
|
||||
error_ids[2] = 'gpupdate will not be started'
|
||||
error_ids[3] = 'Backend execution error'
|
||||
error_ids[4] = 'Error occurred while running frontend manager'
|
||||
error_ids[5] = 'Error running GPOA for computer'
|
||||
error_ids[6] = 'Error running GPOA for user'
|
||||
error_ids[7] = 'Unable to initialize Samba backend'
|
||||
error_ids[8] = 'Unable to initialize no-domain backend'
|
||||
error_ids[9] = 'Error running ADP'
|
||||
error_ids[10] = 'Unable to determine DC hostname'
|
||||
error_ids[11] = 'Error occured while running applier with user privileges'
|
||||
error_ids[12] = 'Unable to initialize backend'
|
||||
error_ids[13] = 'Not sufficient privileges to run machine appliers'
|
||||
error_ids[14] = 'Kerberos ticket check failed'
|
||||
error_ids[15] = 'Unable to retrieve domain name via CLDAP query'
|
||||
error_ids[16] = 'Error getting SID using wbinfo, will use SID from cache'
|
||||
error_ids[17] = 'Unable to get GPO list for user from AD DC'
|
||||
error_ids[18] = 'Error getting XDG_DESKTOP_DIR'
|
||||
error_ids[19] = 'Error occured while running user applier in administrator context'
|
||||
error_ids[20] = 'Error occured while running user applier in user context (with dropped privileges)'
|
||||
error_ids[21] = 'No reply from oddjobd GPOA runner via D-Bus for current user'
|
||||
error_ids[22] = 'No reply from oddjobd GPOA runner via D-Bus for computer'
|
||||
error_ids[23] = 'No reply from oddjobd GPOA runner via D-Bus for user'
|
||||
error_ids[24] = 'Error occured while running machine applier'
|
||||
error_ids[25] = 'Error occured while initializing user applier'
|
||||
error_ids[26] = 'Error merging machine GPT'
|
||||
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'
|
||||
error_ids[39] = 'Unable to work with control'
|
||||
error_ids[40] = 'Control applier for machine will not be started'
|
||||
error_ids[41] = 'Error getting control'
|
||||
error_ids[42] = 'Is not in possible values for control'
|
||||
error_ids[43] = 'Unable to set'
|
||||
error_ids[44] = 'Unable to generate file'
|
||||
error_ids[45] = 'Failed applying unit'
|
||||
error_ids[46] = 'Unable to start systemd unit'
|
||||
error_ids[47] = 'Unable to cache specified URI for machine'
|
||||
error_ids[48] = 'Error recompiling global GSettings schemas'
|
||||
error_ids[49] = 'Error update configuration dconf'
|
||||
error_ids[50] = 'Unable to cache specified URI for user'
|
||||
error_ids[51] = 'Chromium preferences file does not exist at the moment'
|
||||
error_ids[52] = 'Error during attempt to read Chromium preferences for user'
|
||||
error_ids[53] = 'Fail for applying shortcut to file with \'%\''
|
||||
error_ids[54] = 'Fail for applying shortcut to not absolute path'
|
||||
error_ids[55] = 'Error running pkcon_runner sync for machine'
|
||||
error_ids[56] = 'Error run apt-get update'
|
||||
error_ids[57] = 'Package install error'
|
||||
error_ids[58] = 'Package remove error'
|
||||
error_ids[59] = 'Is not in possible values for control'
|
||||
error_ids[60] = 'Error running pkcon_runner sync for user'
|
||||
error_ids[61] = 'Error running pkcon_runner async for machine'
|
||||
error_ids[62] = 'Error running pkcon_runner async for user'
|
||||
|
||||
|
||||
return error_ids.get(code, 'Unknown error code')
|
||||
|
||||
def debug_code(code):
|
||||
debug_ids = dict()
|
||||
debug_ids[1] = 'The GPOA process was started for user'
|
||||
debug_ids[2] = 'Username is not specified - will use username of the current process'
|
||||
debug_ids[3] = 'Initializing plugin manager'
|
||||
debug_ids[4] = 'ADP plugin initialized'
|
||||
debug_ids[5] = 'Running ADP plugin'
|
||||
debug_ids[6] = 'Starting GPOA for user via D-Bus'
|
||||
debug_ids[7] = 'Cache directory determined'
|
||||
debug_ids[8] = 'Initializing local backend without domain'
|
||||
debug_ids[9] = 'Initializing Samba backend for domain'
|
||||
debug_ids[10] = 'Group Policy target set for update'
|
||||
debug_ids[11] = 'Starting GPOA for computer via D-Bus'
|
||||
debug_ids[12] = 'Got exit code'
|
||||
debug_ids[13] = 'Starting GPOA via D-Bus'
|
||||
debug_ids[14] = 'Starting GPOA via command invocation'
|
||||
debug_ids[15] = 'Username for frontend is determined'
|
||||
debug_ids[16] = 'Applying computer part of settings'
|
||||
debug_ids[17] = 'Kerberos ticket check succeed'
|
||||
debug_ids[18] = 'Found AD domain via CLDAP query'
|
||||
debug_ids[19] = 'Setting info'
|
||||
debug_ids[20] = 'Initializing cache'
|
||||
debug_ids[21] = 'Set operational SID'
|
||||
debug_ids[22] = 'Got PReg entry'
|
||||
debug_ids[23] = 'Looking for preference in user part of GPT'
|
||||
debug_ids[24] = 'Looking for preference in machine part of GPT'
|
||||
debug_ids[25] = 'Re-caching Local Policy'
|
||||
debug_ids[26] = 'Adding HKCU entry'
|
||||
debug_ids[27] = 'Skipping HKLM branch deletion key'
|
||||
debug_ids[28] = 'Reading and merging machine preference'
|
||||
debug_ids[29] = 'Reading and merging user preference'
|
||||
debug_ids[30] = 'Found SYSVOL entry'
|
||||
debug_ids[31] = 'Trying to load PReg from .pol file'
|
||||
debug_ids[32] = 'Finished reading PReg from .pol file'
|
||||
debug_ids[33] = 'Determined length of PReg file'
|
||||
debug_ids[34] = 'Merging machine settings from PReg file'
|
||||
debug_ids[35] = 'Merging machine (user part) settings from PReg file'
|
||||
debug_ids[36] = 'Loading PReg from XML'
|
||||
debug_ids[37] = 'Setting process permissions'
|
||||
debug_ids[38] = 'Samba DC setting is overriden by user setting'
|
||||
debug_ids[39] = 'Saving information about drive mapping'
|
||||
debug_ids[40] = 'Saving information about printer'
|
||||
debug_ids[41] = 'Saving information about link'
|
||||
debug_ids[42] = 'Saving information about folder'
|
||||
debug_ids[43] = 'No value cached for object'
|
||||
debug_ids[44] = 'Key is already present in cache, will update the value'
|
||||
debug_ids[45] = 'GPO update started'
|
||||
debug_ids[46] = 'GPO update finished'
|
||||
debug_ids[47] = 'Retrieving list of GPOs to replicate from AD DC'
|
||||
debug_ids[48] = 'Establishing connection with AD DC'
|
||||
debug_ids[49] = 'Started GPO replication from AD DC'
|
||||
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'
|
||||
debug_ids[67] = 'Running Control applier for machine'
|
||||
debug_ids[68] = 'Setting control'
|
||||
debug_ids[69] = 'Deny_All setting found'
|
||||
debug_ids[70] = 'Deny_All setting for user'
|
||||
debug_ids[71] = 'Deny_All setting not found'
|
||||
debug_ids[72] = 'Deny_All setting not found for user'
|
||||
debug_ids[73] = 'Running Polkit applier for machine'
|
||||
debug_ids[74] = 'Running Polkit applier for user in administrator context'
|
||||
debug_ids[75] = 'Polkit applier for machine will not be started'
|
||||
debug_ids[76] = 'Polkit applier for user in administrator context will not be started'
|
||||
debug_ids[77] = 'Generated file'
|
||||
debug_ids[78] = 'Running systemd applier for machine'
|
||||
debug_ids[79] = 'Running systemd applier for machine will not be started'
|
||||
debug_ids[80] = 'Running GSettings applier for machine'
|
||||
debug_ids[81] = 'GSettings applier for machine will not be started'
|
||||
debug_ids[82] = 'Removing GSettings policy file from previous run'
|
||||
debug_ids[83] = 'Mapping Windows policies to GSettings policies'
|
||||
debug_ids[84] = 'GSettings windows policies mapping not enabled'
|
||||
debug_ids[85] = 'Applying user setting'
|
||||
debug_ids[86] = 'Found GSettings windows mapping'
|
||||
debug_ids[87] = 'Running GSettings applier for user in user context'
|
||||
debug_ids[88] = 'GSettings applier for user in user context will not be started'
|
||||
debug_ids[89] = 'Applying machine setting'
|
||||
debug_ids[90] = 'Getting cached file for URI'
|
||||
debug_ids[91] = 'Wrote Firefox preferences to'
|
||||
debug_ids[92] = 'Found Firefox profile in'
|
||||
debug_ids[93] = 'Running Firefox applier for machine'
|
||||
debug_ids[94] = 'Firefox applier for machine will not be started'
|
||||
debug_ids[95] = 'Running Chromium applier for machine'
|
||||
debug_ids[96] = 'Chromium applier for machine will not be started'
|
||||
debug_ids[97] = 'Wrote Chromium preferences to'
|
||||
debug_ids[98] = 'Running Shortcut applier for machine'
|
||||
debug_ids[99] = 'Shortcut applier for machine will not be started'
|
||||
debug_ids[100] = 'No shortcuts to process for'
|
||||
debug_ids[101] = 'Running Shortcut applier for user in user context'
|
||||
debug_ids[102] = 'Shortcut applier for user in user context will not be started'
|
||||
debug_ids[103] = 'Running Shortcut applier for user in administrator context'
|
||||
debug_ids[104] = 'Shortcut applier for user in administrator context will not be started'
|
||||
debug_ids[105] = 'Try to expand path for shortcut'
|
||||
debug_ids[106] = 'Applying shortcut file to'
|
||||
debug_ids[107] = 'Running Folder applier for machine'
|
||||
debug_ids[108] = 'Folder applier for machine will not be started'
|
||||
debug_ids[109] = 'Running Folder applier for user in administrator context'
|
||||
debug_ids[110] = 'Folder applier for user in administrator context will not be started'
|
||||
debug_ids[111] = 'Running Folder applier for user in user context'
|
||||
debug_ids[112] = 'Folder applier for user in user context will not be started'
|
||||
debug_ids[113] = 'Running CUPS applier for machine'
|
||||
debug_ids[114] = 'CUPS applier for machine will not be started'
|
||||
debug_ids[115] = 'Running CUPS applier for user in administrator context'
|
||||
debug_ids[116] = 'CUPS applier for user in administrator context will not be started'
|
||||
debug_ids[117] = 'Running Firewall applier for machine'
|
||||
debug_ids[118] = 'Firewall is enabled'
|
||||
debug_ids[119] = 'Firewall is disabled, settings will be reset'
|
||||
debug_ids[120] = 'Firewall applier will not be started'
|
||||
debug_ids[121] = 'Running NTP applier for machine'
|
||||
debug_ids[122] = 'NTP server is configured to'
|
||||
debug_ids[123] = 'Starting Chrony daemon'
|
||||
debug_ids[124] = 'Setting reference NTP server to'
|
||||
debug_ids[125] = 'Stopping Chrony daemon'
|
||||
debug_ids[126] = 'Configuring NTP server...'
|
||||
debug_ids[127] = 'NTP server is enabled'
|
||||
debug_ids[128] = 'NTP server is disabled'
|
||||
debug_ids[129] = 'NTP server is not configured'
|
||||
debug_ids[130] = 'NTP client is enabled'
|
||||
debug_ids[131] = 'NTP client is disabled'
|
||||
debug_ids[132] = 'NTP client is not configured'
|
||||
debug_ids[133] = 'NTP applier for machine will not be started'
|
||||
debug_ids[134] = 'Running Envvar applier for machine'
|
||||
debug_ids[135] = 'Envvar applier for machine will not be started'
|
||||
debug_ids[136] = 'Running Envvar applier for user in user context'
|
||||
debug_ids[137] = 'Envvar applier for user in user context will not be started'
|
||||
debug_ids[138] = 'Running Package applier for machine'
|
||||
debug_ids[139] = 'Package applier for machine will not be started'
|
||||
debug_ids[140] = 'Running Package applier for user in administrator context'
|
||||
debug_ids[141] = 'Package applier for user in administrator context will not be started'
|
||||
debug_ids[142] = 'Running pkcon_runner to install and remove packages'
|
||||
debug_ids[143] = 'Run apt-get update'
|
||||
debug_ids[144] = 'Unable to cache specified URI'
|
||||
debug_ids[145] = 'Unable to cache specified URI for machine'
|
||||
debug_ids[146] = 'Running CIFS applier for user in administrator context'
|
||||
debug_ids[147] = 'CIFS applier for user in administrator context will not be started'
|
||||
debug_ids[148] = 'Installing the package'
|
||||
debug_ids[149] = 'Removing a package'
|
||||
debug_ids[150] = 'Failed to found gsettings for machine'
|
||||
debug_ids[151] = 'Failed to found user gsettings'
|
||||
|
||||
return debug_ids.get(code, 'Unknown debug code')
|
||||
|
||||
def warning_code(code):
|
||||
warning_ids = dict()
|
||||
warning_ids[1] = (
|
||||
'Unable to perform gpupdate for non-existent user, '
|
||||
'will update machine settings'
|
||||
)
|
||||
warning_ids[2] = (
|
||||
'Current permissions does not allow to perform gpupdate for '
|
||||
'designated user. Will update current user settings'
|
||||
)
|
||||
warning_ids[3] = 'oddjobd is inaccessible'
|
||||
warning_ids[4] = 'No SYSVOL entry assigned to GPO'
|
||||
warning_ids[5] = 'ADP package is not installed - plugin will not be initialized'
|
||||
warning_ids[6] = 'Unable to resolve GSettings parameter'
|
||||
warning_ids[7] = 'No home directory exists for user'
|
||||
warning_ids[8] = 'User\'s shortcut not placed to home directory'
|
||||
warning_ids[9] = 'CUPS is not installed: no printer settings will be deployed'
|
||||
warning_ids[10] = 'Unsupported NTP server type'
|
||||
warning_ids[11] = 'Unable to refresh GPO list'
|
||||
|
||||
return warning_ids.get(code, 'Unknown warning code')
|
||||
|
||||
def fatal_code(code):
|
||||
fatal_ids = dict()
|
||||
fatal_ids[1] = 'Unable to refresh GPO list'
|
||||
fatal_ids[2] = 'Error getting GPTs for machine'
|
||||
fatal_ids[3] = 'Error getting GPTs for user'
|
||||
|
||||
return fatal_ids.get(code, 'Unknown fatal code')
|
||||
|
||||
def get_message(code):
|
||||
retstr = 'Unknown message type, no message assigned'
|
||||
|
||||
if code.startswith('E'):
|
||||
retstr = error_code(int(code[1:]))
|
||||
if code.startswith('I'):
|
||||
retstr = info_code(int(code[1:]))
|
||||
if code.startswith('D'):
|
||||
retstr = debug_code(int(code[1:]))
|
||||
if code.startswith('W'):
|
||||
retstr = warning_code(int(code[1:]))
|
||||
if code.startswith('F'):
|
||||
retstr = fatal_code(int(code[1:]))
|
||||
|
||||
return retstr
|
||||
|
||||
def message_with_code(code):
|
||||
retstr = '[' + code[0:1] + code[1:].rjust(5, '0') + ']| ' + gettext.gettext(get_message(code))
|
||||
|
||||
return retstr
|
||||
|
150
gpoa/pkcon_runner
Executable file
150
gpoa/pkcon_runner
Executable file
@ -0,0 +1,150 @@
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import rpm
|
||||
import subprocess
|
||||
from gpoa.storage import registry_factory
|
||||
import logging
|
||||
from util.logging import log
|
||||
import argparse
|
||||
import gettext
|
||||
import locale
|
||||
from messages import message_with_code
|
||||
from util.arguments import (
|
||||
set_loglevel
|
||||
)
|
||||
|
||||
|
||||
def is_rpm_installed(rpm_name):
|
||||
'''
|
||||
Check if the package named 'rpm_name' is installed
|
||||
'''
|
||||
ts = rpm.TransactionSet()
|
||||
pm = ts.dbMatch('name', rpm_name)
|
||||
if pm.count() > 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
class Pkcon_applier:
|
||||
|
||||
def __init__(self, sid = None):
|
||||
self.__install_key_name = 'Install'
|
||||
self.__remove_key_name = 'Remove'
|
||||
self.__hkcu_branch = 'Software\\BaseALT\\Policies\\Packages'
|
||||
self.__hklm_branch = 'Software\\BaseALT\\Policies\\Packages'
|
||||
self.__install_command = ['/usr/bin/pkcon', '-y', 'install']
|
||||
self.__remove_command = ['/usr/bin/pkcon', '-y', 'remove']
|
||||
self.__reinstall_command = ['/usr/bin/pkcon', '-y', 'reinstall']
|
||||
self.install_packages = set()
|
||||
self.remove_packages = set()
|
||||
self.storage = registry_factory('registry')
|
||||
if sid:
|
||||
install_branch_user = '{}\\{}%'.format(self.__hkcu_branch, self.__install_key_name)
|
||||
remove_branch_user = '{}\\{}%'.format(self.__hkcu_branch, self.__remove_key_name)
|
||||
self.install_packages_setting = self.storage.filter_hkcu_entries(sid, install_branch_user)
|
||||
self.remove_packages_setting = self.storage.filter_hkcu_entries(sid, remove_branch_user)
|
||||
else:
|
||||
install_branch = '{}\\{}%'.format(self.__hklm_branch, self.__install_key_name)
|
||||
remove_branch = '{}\\{}%'.format(self.__hklm_branch, self.__remove_key_name)
|
||||
self.install_packages_setting = self.storage.filter_hklm_entries(install_branch)
|
||||
self.remove_packages_setting = self.storage.filter_hklm_entries(remove_branch)
|
||||
for package in self.install_packages_setting:
|
||||
if not is_rpm_installed(package.data):
|
||||
self.install_packages.add(package.data)
|
||||
for package in self.remove_packages_setting:
|
||||
if package.data in self.install_packages:
|
||||
self.install_packages.remove(package.data)
|
||||
if is_rpm_installed(package.data):
|
||||
self.remove_packages.add(package.data)
|
||||
|
||||
def apply(self):
|
||||
log('D142')
|
||||
self.update()
|
||||
for package in self.remove_packages:
|
||||
try:
|
||||
logdata = dict()
|
||||
logdata['name'] = package
|
||||
log('D149', logdata)
|
||||
self.remove_pkg(package)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['exc'] = exc
|
||||
log('E58', logdata)
|
||||
|
||||
for package in self.install_packages:
|
||||
try:
|
||||
logdata = dict()
|
||||
logdata['name'] = package
|
||||
log('D148', logdata)
|
||||
self.install_pkg(package)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['exc'] = exc
|
||||
log('E57', logdata)
|
||||
|
||||
|
||||
def install_pkg(self, package_name):
|
||||
fullcmd = list(self.__install_command)
|
||||
fullcmd.append(package_name)
|
||||
return subprocess.check_output(fullcmd)
|
||||
|
||||
def reinstall_pkg(self, package_name):
|
||||
pass
|
||||
|
||||
def remove_pkg(self, package_name):
|
||||
fullcmd = self.__remove_command
|
||||
fullcmd.append(package_name)
|
||||
return subprocess.check_output(fullcmd)
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update APT-RPM database.
|
||||
'''
|
||||
try:
|
||||
res = subprocess.check_output(['/usr/bin/apt-get', 'update'], encoding='utf-8')
|
||||
msg = str(res).split('\n')
|
||||
logdata = dict()
|
||||
for mslog in msg:
|
||||
ms = str(mslog).split(' ')
|
||||
if ms:
|
||||
logdata = {ms[0]: ms[1:-1]}
|
||||
log('D143', logdata)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = exc
|
||||
log('E56',logdata)
|
||||
|
||||
if __name__ == '__main__':
|
||||
locale.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||
gettext.textdomain('gpoa')
|
||||
logger = logging.getLogger()
|
||||
parser = argparse.ArgumentParser(description='Package applier')
|
||||
parser.add_argument('--sid', type = str, help = 'sid', nargs = '?', default = None)
|
||||
parser.add_argument('--loglevel', type = int, help = 'loglevel', nargs = '?', default = 30)
|
||||
|
||||
args = parser.parse_args()
|
||||
logger.setLevel(args.loglevel)
|
||||
if args.sid:
|
||||
applier = Pkcon_applier(args.sid)
|
||||
else:
|
||||
applier = Pkcon_applier()
|
||||
applier.apply()
|
||||
|
@ -22,19 +22,20 @@ import subprocess
|
||||
from util.rpm import is_rpm_installed
|
||||
from .exceptions import PluginInitError
|
||||
from util.logging import slogm
|
||||
from messages import message_with_code
|
||||
|
||||
class adp:
|
||||
def __init__(self):
|
||||
if not is_rpm_installed('adp'):
|
||||
raise PluginInitError('adp is not installed - plugin cannot be initialized')
|
||||
logging.info(slogm('ADP plugin initialized'))
|
||||
raise PluginInitError(message_with_code('W5'))
|
||||
logging.info(slogm(message_with_code('D4')))
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
logging.info('Running ADP plugin')
|
||||
logging.info(slogm(message_with_code('D5')))
|
||||
subprocess.call(['/usr/bin/adp', 'fetch'])
|
||||
subprocess.call(['/usr/bin/adp', 'apply'])
|
||||
except Exception as exc:
|
||||
logging.error(slogm('Error running ADP'))
|
||||
logging.error(slogm(message_with_code('E9')))
|
||||
raise exc
|
||||
|
||||
|
@ -23,15 +23,16 @@ from .roles import roles
|
||||
from .exceptions import PluginInitError
|
||||
from .plugin import plugin
|
||||
from util.logging import slogm
|
||||
from messages import message_with_code
|
||||
|
||||
class plugin_manager:
|
||||
def __init__(self):
|
||||
self.plugins = dict()
|
||||
logging.info(slogm('Starting plugin manager'))
|
||||
logging.debug(slogm(message_with_code('D3')))
|
||||
try:
|
||||
self.plugins['adp'] = adp()
|
||||
except PluginInitError as exc:
|
||||
logging.error(slogm(exc))
|
||||
logging.warning(slogm(str(exc)))
|
||||
|
||||
def run(self):
|
||||
self.plugins.get('adp', plugin('adp')).run()
|
||||
|
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('D144', 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)
|
||||
|
@ -20,40 +20,157 @@ class samba_preg(object):
|
||||
'''
|
||||
Object mapping representing HKLM entry (registry key without SID)
|
||||
'''
|
||||
def __init__(self, preg_obj):
|
||||
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
|
||||
def __init__(self, preg_obj, policy_name):
|
||||
self.policy_name = policy_name
|
||||
self.keyname = preg_obj.keyname
|
||||
self.valuename = preg_obj.valuename
|
||||
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
|
||||
self.type = preg_obj.type
|
||||
self.data = preg_obj.data
|
||||
|
||||
def update_fields(self):
|
||||
fields = dict()
|
||||
fields['policy_name'] = self.policy_name
|
||||
fields['type'] = self.type
|
||||
fields['data'] = self.data
|
||||
|
||||
return fields
|
||||
|
||||
class samba_hkcu_preg(object):
|
||||
'''
|
||||
Object mapping representing HKCU entry (registry key with SID)
|
||||
'''
|
||||
def __init__(self, sid, preg_obj):
|
||||
def __init__(self, sid, preg_obj, policy_name):
|
||||
self.sid = sid
|
||||
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
|
||||
self.policy_name = policy_name
|
||||
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
|
||||
|
||||
def update_fields(self):
|
||||
fields = dict()
|
||||
fields['policy_name'] = self.policy_name
|
||||
fields['type'] = self.type
|
||||
fields['data'] = self.data
|
||||
|
||||
return fields
|
||||
|
||||
class ad_shortcut(object):
|
||||
'''
|
||||
Object mapping representing Windows shortcut.
|
||||
'''
|
||||
def __init__(self, sid, sc):
|
||||
def __init__(self, sid, sc, policy_name):
|
||||
self.sid = sid
|
||||
self.policy_name = policy_name
|
||||
self.path = sc.dest
|
||||
self.shortcut = sc.to_json()
|
||||
|
||||
def update_fields(self):
|
||||
fields = dict()
|
||||
fields['policy_name'] = self.policy_name
|
||||
fields['path'] = self.path
|
||||
fields['shortcut'] = self.shortcut
|
||||
|
||||
return fields
|
||||
|
||||
class info_entry(object):
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def update_fields(self):
|
||||
fields = dict()
|
||||
fields['value'] = self.value
|
||||
|
||||
return fields
|
||||
|
||||
class printer_entry(object):
|
||||
'''
|
||||
Object mapping representing Windows printer of some type.
|
||||
'''
|
||||
def __init__(self, sid, pobj):
|
||||
def __init__(self, sid, pobj, policy_name):
|
||||
self.sid = sid
|
||||
self.policy_name = policy_name
|
||||
self.name = pobj.name
|
||||
self.printer = pobj.to_json()
|
||||
|
||||
def update_fields(self):
|
||||
fields = dict()
|
||||
fields['policy_name'] = self.policy_name
|
||||
fields['name'] = self.name
|
||||
fields['printer'] = self.printer.to_json()
|
||||
|
||||
return fields
|
||||
|
||||
class drive_entry(object):
|
||||
'''
|
||||
Object mapping representing Samba share bound to drive letter
|
||||
'''
|
||||
def __init__(self, sid, dobj, policy_name):
|
||||
self.sid = sid
|
||||
self.policy_name = policy_name
|
||||
self.login = dobj.login
|
||||
self.password = dobj.password
|
||||
self.dir = dobj.dir
|
||||
self.path = dobj.path
|
||||
|
||||
def update_fields(self):
|
||||
fields = dict()
|
||||
fields['policy_name'] = self.policy_name
|
||||
fields['login'] = self.login
|
||||
fields['password'] = self.password
|
||||
fields['dir'] = self.dir
|
||||
fields['path'] = self.path
|
||||
|
||||
return fields
|
||||
|
||||
class folder_entry(object):
|
||||
'''
|
||||
Object mapping representing file system directory
|
||||
'''
|
||||
def __init__(self, sid, fobj, policy_name):
|
||||
self.sid = sid
|
||||
self.policy_name = policy_name
|
||||
self.path = fobj.path
|
||||
self.action = fobj.action.value
|
||||
self.delete_folder = str(fobj.delete_folder)
|
||||
self.delete_sub_folders = str(fobj.delete_sub_folders)
|
||||
self.delete_files = str(fobj.delete_files)
|
||||
|
||||
def update_fields(self):
|
||||
'''
|
||||
Return list of fields to update
|
||||
'''
|
||||
fields = dict()
|
||||
fields['policy_name'] = self.policy_name
|
||||
fields['action'] = self.action
|
||||
fields['delete_folder'] = self.delete_folder
|
||||
fields['delete_sub_folders'] = self.delete_sub_folders
|
||||
fields['delete_files'] = self.delete_files
|
||||
|
||||
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
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
from .cache import cache
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from sqlalchemy import (
|
||||
@ -34,7 +33,7 @@ from sqlalchemy.orm import (
|
||||
sessionmaker
|
||||
)
|
||||
|
||||
from util.logging import slogm
|
||||
from util.logging import log
|
||||
from util.paths import cache_dir
|
||||
|
||||
def mapping_factory(mapper_suffix):
|
||||
@ -53,7 +52,8 @@ class sqlite_cache(cache):
|
||||
self.cache_name = cache_name
|
||||
self.mapper_obj = mapping_factory(self.cache_name)
|
||||
self.storage_uri = os.path.join('sqlite:///{}/{}.sqlite'.format(cache_dir(), self.cache_name))
|
||||
logging.debug(slogm('Initializing cache {}'.format(self.storage_uri)))
|
||||
logdata = dict({'cache_file': self.storage_uri})
|
||||
log('D20', logdata)
|
||||
self.db_cnt = create_engine(self.storage_uri, echo=False)
|
||||
self.__metadata = MetaData(self.db_cnt)
|
||||
self.cache_table = Table(
|
||||
@ -80,7 +80,9 @@ class sqlite_cache(cache):
|
||||
def get_default(self, obj_id, default_value):
|
||||
result = self.get(obj_id)
|
||||
if result == None:
|
||||
logging.debug(slogm('No value cached for {}'.format(obj_id)))
|
||||
logdata = dict()
|
||||
logdata['object'] = obj_id
|
||||
log('D43', logdata)
|
||||
self.store(obj_id, default_value)
|
||||
return str(default_value)
|
||||
return result.value
|
||||
@ -89,9 +91,11 @@ class sqlite_cache(cache):
|
||||
try:
|
||||
self.db_session.add(obj)
|
||||
self.db_session.commit()
|
||||
except:
|
||||
except Exception as exc:
|
||||
self.db_session.rollback()
|
||||
logging.error(slogm('Error inserting value into cache, will update the value'))
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('D44', logdata)
|
||||
self.db_session.query(self.mapper_obj).filter(self.mapper_obj.str_id == obj.str_id).update({ 'value': obj.value })
|
||||
self.db_session.commit()
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
# 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 logging
|
||||
import os
|
||||
|
||||
from sqlalchemy import (
|
||||
@ -33,7 +32,7 @@ from sqlalchemy.orm import (
|
||||
sessionmaker
|
||||
)
|
||||
|
||||
from util.logging import slogm
|
||||
from util.logging import log
|
||||
from util.paths import cache_dir
|
||||
from .registry import registry
|
||||
from .record_types import (
|
||||
@ -42,6 +41,9 @@ from .record_types import (
|
||||
, ad_shortcut
|
||||
, info_entry
|
||||
, printer_entry
|
||||
, drive_entry
|
||||
, folder_entry
|
||||
, envvar_entry
|
||||
)
|
||||
|
||||
class sqlite_registry(registry):
|
||||
@ -61,40 +63,85 @@ class sqlite_registry(registry):
|
||||
Column('value', String(65536))
|
||||
)
|
||||
self.__hklm = Table(
|
||||
'HKLM',
|
||||
self.__metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('hive_key', String(65536), unique=True),
|
||||
Column('type', Integer),
|
||||
Column('data', String)
|
||||
'HKLM'
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('hive_key', String(65536, collation='NOCASE'),
|
||||
unique=True)
|
||||
, Column('keyname', String(collation='NOCASE'))
|
||||
, Column('valuename', String(collation='NOCASE'))
|
||||
, Column('policy_name', String)
|
||||
, Column('type', Integer)
|
||||
, Column('data', String)
|
||||
)
|
||||
self.__hkcu = Table(
|
||||
'HKCU',
|
||||
self.__metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('sid', String),
|
||||
Column('hive_key', String(65536)),
|
||||
Column('type', Integer),
|
||||
Column('data', String),
|
||||
UniqueConstraint('sid', 'hive_key')
|
||||
'HKCU'
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('sid', String)
|
||||
, Column('hive_key', String(65536, collation='NOCASE'))
|
||||
, Column('keyname', String(collation='NOCASE'))
|
||||
, Column('valuename', String(collation='NOCASE'))
|
||||
, Column('policy_name', String)
|
||||
, Column('type', Integer)
|
||||
, Column('data', String)
|
||||
, UniqueConstraint('sid', 'hive_key')
|
||||
)
|
||||
self.__shortcuts = Table(
|
||||
'Shortcuts',
|
||||
self.__metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('sid', String),
|
||||
Column('path', String),
|
||||
Column('shortcut', String),
|
||||
UniqueConstraint('sid', 'path')
|
||||
'Shortcuts'
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('sid', String)
|
||||
, Column('path', String)
|
||||
, Column('policy_name', String)
|
||||
, Column('shortcut', String)
|
||||
, UniqueConstraint('sid', 'path')
|
||||
)
|
||||
self.__printers = Table(
|
||||
'Printers',
|
||||
self.__metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('sid', String),
|
||||
Column('name', String),
|
||||
Column('printer', String),
|
||||
UniqueConstraint('sid', 'name')
|
||||
'Printers'
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('sid', String)
|
||||
, Column('name', String)
|
||||
, Column('policy_name', String)
|
||||
, Column('printer', String)
|
||||
, UniqueConstraint('sid', 'name')
|
||||
)
|
||||
self.__drives = Table(
|
||||
'Drives'
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('sid', String)
|
||||
, Column('login', String)
|
||||
, Column('password', String)
|
||||
, Column('dir', String)
|
||||
, Column('policy_name', String)
|
||||
, Column('path', String)
|
||||
, UniqueConstraint('sid', 'dir')
|
||||
)
|
||||
self.__folders = Table(
|
||||
'Folders'
|
||||
, self.__metadata
|
||||
, Column('id', Integer, primary_key=True)
|
||||
, Column('sid', String)
|
||||
, Column('path', String)
|
||||
, Column('policy_name', String)
|
||||
, Column('action', String)
|
||||
, Column('delete_folder', String)
|
||||
, Column('delete_sub_folders', String)
|
||||
, 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)
|
||||
@ -105,6 +152,9 @@ class sqlite_registry(registry):
|
||||
mapper(samba_hkcu_preg, self.__hkcu)
|
||||
mapper(ad_shortcut, self.__shortcuts)
|
||||
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')
|
||||
@ -121,133 +171,243 @@ class sqlite_registry(registry):
|
||||
try:
|
||||
self._add(row)
|
||||
except:
|
||||
update_obj = dict({ 'value': row.value })
|
||||
(self
|
||||
.db_session.query(info_entry)
|
||||
.filter(info_entry.name == row.name)
|
||||
.update(update_obj))
|
||||
.update(row.update_fields()))
|
||||
self.db_session.commit()
|
||||
|
||||
def _hklm_upsert(self, row):
|
||||
try:
|
||||
self._add(row)
|
||||
except:
|
||||
update_obj = dict({'type': row.type, 'data': row.data })
|
||||
(self
|
||||
.db_session
|
||||
.query(samba_preg)
|
||||
.filter(samba_preg.hive_key == row.hive_key)
|
||||
.update(update_obj))
|
||||
.update(row.update_fields()))
|
||||
self.db_session.commit()
|
||||
|
||||
def _hkcu_upsert(self, row):
|
||||
try:
|
||||
self._add(row)
|
||||
except:
|
||||
update_obj = dict({'type': row.type, 'data': row.data })
|
||||
except Exception as exc:
|
||||
(self
|
||||
.db_session
|
||||
.query(samba_preg)
|
||||
.query(samba_hkcu_preg)
|
||||
.filter(samba_hkcu_preg.sid == row.sid)
|
||||
.filter(samba_hkcu_preg.hive_key == row.hive_key)
|
||||
.update(update_obj))
|
||||
.update(row.update_fields()))
|
||||
self.db_session.commit()
|
||||
|
||||
def _shortcut_upsert(self, row):
|
||||
try:
|
||||
self._add(row)
|
||||
except:
|
||||
update_obj = dict({ 'shortcut': row.shortcut })
|
||||
(self
|
||||
.db_session
|
||||
.query(ad_shortcut)
|
||||
.filter(ad_shortcut.sid == row.sid)
|
||||
.filter(ad_shortcut.path == row.path)
|
||||
.update(update_obj))
|
||||
.update(row.update_fields()))
|
||||
self.db_session.commit()
|
||||
|
||||
def _printer_upsert(self, row):
|
||||
try:
|
||||
self._add(row)
|
||||
except:
|
||||
update_obj = dict({ 'printer': row.printer })
|
||||
(self
|
||||
.db_session
|
||||
.query(printer_entry)
|
||||
.filter(printer_entry.sid == row.sid)
|
||||
.filter(printer_entry.name == row.name)
|
||||
.update(update_obj))
|
||||
.update(row.update_fields()))
|
||||
self.db_session.commit()
|
||||
|
||||
def _drive_upsert(self, row):
|
||||
try:
|
||||
self._add(row)
|
||||
except:
|
||||
(self
|
||||
.db_session
|
||||
.query(drive_entry)
|
||||
.filter(drive_entry.sid == row.sid)
|
||||
.filter(drive_entry.dir == row.dir)
|
||||
.update(row.update_fields()))
|
||||
self.db_session.commit()
|
||||
|
||||
def set_info(self, name, value):
|
||||
ientry = info_entry(name, value)
|
||||
logging.debug(slogm('Setting info {}:{}'.format(name, value)))
|
||||
logdata = dict()
|
||||
logdata['varname'] = name
|
||||
logdata['value'] = value
|
||||
log('D19', logdata)
|
||||
self._info_upsert(ientry)
|
||||
|
||||
def add_hklm_entry(self, preg_entry):
|
||||
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)
|
||||
if not pentry.hive_key.rpartition('\\')[2].startswith('**'):
|
||||
pentry = samba_preg(preg_entry, policy_name)
|
||||
if not pentry.valuename.startswith('**'):
|
||||
self._hklm_upsert(pentry)
|
||||
else:
|
||||
logging.warning(slogm('Skipping branch deletion key: {}'.format(pentry.hive_key)))
|
||||
logdata = dict({'key': pentry.hive_key})
|
||||
if pentry.valuename.lower() == '**delvals.':
|
||||
self._delete_hklm_keyname(pentry.keyname)
|
||||
else:
|
||||
log('D27', logdata)
|
||||
|
||||
def add_hkcu_entry(self, preg_entry, sid):
|
||||
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):
|
||||
'''
|
||||
Write PReg entry to HKEY_CURRENT_USER
|
||||
'''
|
||||
hkcu_pentry = samba_hkcu_preg(sid, preg_entry)
|
||||
if not hkcu_pentry.hive_key.rpartition('\\')[2].startswith('**'):
|
||||
logging.debug(slogm('Adding HKCU entry for {}'.format(sid)))
|
||||
hkcu_pentry = samba_hkcu_preg(sid, preg_entry, policy_name)
|
||||
logdata = dict({'sid': sid, 'policy': policy_name, 'key': hkcu_pentry.hive_key})
|
||||
if not hkcu_pentry.valuename.startswith('**'):
|
||||
log('D26', logdata)
|
||||
self._hkcu_upsert(hkcu_pentry)
|
||||
else:
|
||||
logging.warning(slogm('Skipping branch deletion key: {}'.format(hkcu_pentry.hive_key)))
|
||||
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):
|
||||
def add_shortcut(self, sid, sc_obj, policy_name):
|
||||
'''
|
||||
Store shortcut information in the database
|
||||
'''
|
||||
sc_entry = ad_shortcut(sid, sc_obj)
|
||||
logging.debug(slogm('Saving info about {} link for {}'.format(sc_entry.path, sid)))
|
||||
sc_entry = ad_shortcut(sid, sc_obj, policy_name)
|
||||
logdata = dict()
|
||||
logdata['link'] = sc_entry.path
|
||||
logdata['sid'] = sid
|
||||
log('D41', logdata)
|
||||
self._shortcut_upsert(sc_entry)
|
||||
|
||||
def add_printer(self, sid, pobj):
|
||||
def add_printer(self, sid, pobj, policy_name):
|
||||
'''
|
||||
Store printer configuration in the database
|
||||
'''
|
||||
prn_entry = printer_entry(sid, pobj)
|
||||
logging.debug(slogm('Saving info about printer {} for {}'.format(prn_entry.name, sid)))
|
||||
prn_entry = printer_entry(sid, pobj, policy_name)
|
||||
logdata = dict()
|
||||
logdata['printer'] = prn_entry.name
|
||||
logdata['sid'] = sid
|
||||
log('D40', logdata)
|
||||
self._printer_upsert(prn_entry)
|
||||
|
||||
def get_shortcuts(self, sid):
|
||||
def add_drive(self, sid, dobj, policy_name):
|
||||
drv_entry = drive_entry(sid, dobj, policy_name)
|
||||
logdata = dict()
|
||||
logdata['uri'] = drv_entry.path
|
||||
logdata['sid'] = sid
|
||||
log('D39', logdata)
|
||||
self._drive_upsert(drv_entry)
|
||||
|
||||
def add_folder(self, sid, fobj, policy_name):
|
||||
fld_entry = folder_entry(sid, fobj, policy_name)
|
||||
logdata = dict()
|
||||
logdata['folder'] = fld_entry.path
|
||||
logdata['sid'] = sid
|
||||
log('D42', logdata)
|
||||
try:
|
||||
self._add(fld_entry)
|
||||
except Exception as exc:
|
||||
(self
|
||||
._filter_sid_obj(folder_entry, sid)
|
||||
.filter(folder_entry.path == fld_entry.path)
|
||||
.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
|
||||
.query(ad_shortcut)
|
||||
.filter(ad_shortcut.sid == sid)
|
||||
.query(row_object)
|
||||
.filter(row_object.sid == sid))
|
||||
return res
|
||||
|
||||
def _filter_sid_list(self, row_object, sid):
|
||||
res = (self
|
||||
.db_session
|
||||
.query(row_object)
|
||||
.filter(row_object.sid == sid)
|
||||
.order_by(row_object.id)
|
||||
.all())
|
||||
return res
|
||||
|
||||
def get_shortcuts(self, sid):
|
||||
return self._filter_sid_list(ad_shortcut, sid)
|
||||
|
||||
def get_printers(self, sid):
|
||||
res = (self
|
||||
.db_session
|
||||
.query(printer_entry)
|
||||
.filter(printer_entry.sid == sid)
|
||||
.all())
|
||||
return res
|
||||
return self._filter_sid_list(printer_entry, sid)
|
||||
|
||||
def get_drives(self, sid):
|
||||
return self._filter_sid_list(drive_entry, sid)
|
||||
|
||||
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
|
||||
.query(samba_preg)
|
||||
.query(samba_hkcu_preg)
|
||||
.filter(samba_hkcu_preg.sid == sid)
|
||||
.filter(samba_hkcu_preg.hive_key == hive_key)
|
||||
.first())
|
||||
# Try to get the value from machine SID as a default if no option is set.
|
||||
if not res:
|
||||
machine_sid = self.get_info('machine_sid')
|
||||
res = self.db_session.query(samba_preg).filter(samba_hkcu_preg.sid == machine_sid).filter(samba_hkcu_preg.hive_key == hive_key).first()
|
||||
res = self.db_session.query(samba_hkcu_preg).filter(samba_hkcu_preg.sid == machine_sid).filter(samba_hkcu_preg.hive_key == hive_key).first()
|
||||
return res
|
||||
|
||||
def filter_hkcu_entries(self, sid, startswith):
|
||||
@ -282,31 +442,16 @@ class sqlite_registry(registry):
|
||||
return res
|
||||
|
||||
def wipe_user(self, sid):
|
||||
self.wipe_hkcu(sid)
|
||||
self.wipe_shortcuts(sid)
|
||||
self.wipe_printers(sid)
|
||||
self._wipe_sid(samba_hkcu_preg, sid)
|
||||
self._wipe_sid(ad_shortcut, sid)
|
||||
self._wipe_sid(printer_entry, sid)
|
||||
self._wipe_sid(drive_entry, sid)
|
||||
|
||||
def wipe_shortcuts(self, sid):
|
||||
def _wipe_sid(self, row_object, sid):
|
||||
(self
|
||||
.db_session
|
||||
.query(ad_shortcut)
|
||||
.filter(ad_shortcut.sid == sid)
|
||||
.delete())
|
||||
self.db_session.commit()
|
||||
|
||||
def wipe_printers(self, sid):
|
||||
(self
|
||||
.db_session
|
||||
.query(printer_entry)
|
||||
.filter(printer_entry.sid == sid)
|
||||
.delete())
|
||||
self.db_session.commit()
|
||||
|
||||
def wipe_hkcu(self, sid):
|
||||
(self
|
||||
.db_session
|
||||
.query(samba_hkcu_preg)
|
||||
.filter(samba_hkcu_preg.sid == sid)
|
||||
.query(row_object)
|
||||
.filter(row_object.sid == sid)
|
||||
.delete())
|
||||
self.db_session.commit()
|
||||
|
||||
|
29
gpoa/templates/48-gpoa_disk_permissions_user.rules.j2
Normal file
29
gpoa/templates/48-gpoa_disk_permissions_user.rules.j2
Normal file
@ -0,0 +1,29 @@
|
||||
{#
|
||||
# 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/>.
|
||||
#}
|
||||
|
||||
{% if Deny_All == '1' %}
|
||||
polkit.addRule(function (action, subject) {
|
||||
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
||||
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
||||
action.id == "org.freedesktop.udisks2.filesystem-mount-other-seat") &&
|
||||
subject.user == "{{User}}" ) {
|
||||
return polkit.Result.NO;
|
||||
}
|
||||
});
|
||||
{% endif %}
|
20
gpoa/templates/autofs_auto.j2
Normal file
20
gpoa/templates/autofs_auto.j2
Normal file
@ -0,0 +1,20 @@
|
||||
{#
|
||||
# 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/>.
|
||||
#}
|
||||
{{ home_dir }}/net {{ mount_file }} -t 120 --browse
|
||||
|
25
gpoa/templates/autofs_identity.j2
Normal file
25
gpoa/templates/autofs_identity.j2
Normal file
@ -0,0 +1,25 @@
|
||||
{#
|
||||
# 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/>.
|
||||
#}
|
||||
{% if login %}
|
||||
username={{ login }}
|
||||
{% endif %}
|
||||
{% if password %}
|
||||
password={{ password }}
|
||||
{% endif %}
|
||||
|
21
gpoa/templates/autofs_mountpoints.j2
Normal file
21
gpoa/templates/autofs_mountpoints.j2
Normal file
@ -0,0 +1,21 @@
|
||||
{#
|
||||
# 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/>.
|
||||
#}
|
||||
{%- for drv in drives %}
|
||||
{{ drv.dir }} -fstype=cifs,cruid=$USER,sec=krb5,noperm :{{ drv.path }}
|
||||
{% endfor %}
|
39
gpoa/test/frontend/appliers/test_packages.py
Normal file
39
gpoa/test/frontend/appliers/test_packages.py
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import unittest
|
||||
|
||||
from frontend.appliers.rpm import rpm
|
||||
|
||||
class PackageTestCase(unittest.TestCase):
|
||||
'''
|
||||
Semi-integrational tests for packages installation/removing
|
||||
'''
|
||||
def test_package_not_exist(self):
|
||||
packages_for_install = 'dummy1 dummy2'
|
||||
packages_for_remove = 'dummy3'
|
||||
|
||||
test_rpm = rpm(packages_for_install, packages_for_remove)
|
||||
test_rpm.apply()
|
||||
|
||||
def test_install_remove_same_package(self):
|
||||
packages_for_install = 'gotop'
|
||||
packages_for_remove = 'gotop'
|
||||
|
||||
test_rpm = rpm(packages_for_install, packages_for_remove)
|
||||
test_rpm.apply()
|
3
gpoa/test/gpt/Printers.xml
Normal file
3
gpoa/test/gpt/Printers.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Printers clsid="{1F577D12-3D1B-471e-A1B7-060317597B9C}"><PortPrinter clsid="{C3A739D2-4A44-401e-9F9D-88E5E77DFB3E}" name="10.64.128.250" status="10.64.128.250" image="0" changed="2020-01-23 11:48:07" uid="{88D998C2-9875-4278-A607-EC828839EFCE}" userContext="1" bypassErrors="1"><Properties lprQueue="" snmpCommunity="public" protocol="PROTOCOL_RAWTCP_TYPE" portNumber="9100" doubleSpool="0" snmpEnabled="0" snmpDevIndex="1" ipAddress="10.64.128.250" action="C" location="" localName="printer" comment="" default="1" skipLocal="0" useDNS="0" path="\\prnt" deleteAll="0"/><Filters><FilterGroup bool="AND" not="0" name="DOMAIN\Domain Users" sid="S-1-5-21-3359553909-270469630-9462315-513" userContext="1" primaryGroup="0" localGroup="0"/></Filters></PortPrinter>
|
||||
</Printers>
|
Binary file not shown.
BIN
gpoa/test/gpt/data/Registry.pol
Normal file
BIN
gpoa/test/gpt/data/Registry.pol
Normal file
Binary file not shown.
3
gpoa/test/gpt/data/ScheduledTasks.xml
Normal file
3
gpoa/test/gpt/data/ScheduledTasks.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}"><TaskV2 clsid="{D8896631-B747-47a7-84A6-C155337F3BC8}" name="mytask" image="2" changed="2020-01-24 13:06:25" uid="{0DBF3CAA-3DCF-4AAA-A52F-82B010B35380}"><Properties action="U" name="mytask" runAs="%LogonDomain%\%LogonUser%" logonType="InteractiveToken"><Task version="1.3"><RegistrationInfo><Author>DOMAIN\samba</Author><Description></Description></RegistrationInfo><Principals><Principal id="Author"><UserId>%LogonDomain%\%LogonUser%</UserId><LogonType>InteractiveToken</LogonType><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>true</StopIfGoingOnBatteries><AllowHardTerminate>true</AllowHardTerminate><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><ExecutionTimeLimit>P3D</ExecutionTimeLimit><Priority>7</Priority></Settings><Triggers><CalendarTrigger><StartBoundary>2020-01-24T14:59:48</StartBoundary><Enabled>true</Enabled><ScheduleByDay><DaysInterval>1</DaysInterval></ScheduleByDay></CalendarTrigger></Triggers><Actions><Exec><Command>C:\Program Files (x86)\Google\Chrome\Application\chrome.exe</Command></Exec></Actions></Task></Properties></TaskV2>
|
||||
</ScheduledTasks>
|
42
gpoa/test/gpt/test_drives.py
Normal file
42
gpoa/test/gpt/test_drives.py
Normal file
@ -0,0 +1,42 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
import unittest
|
||||
import unittest.mock
|
||||
|
||||
import os
|
||||
|
||||
import util.paths
|
||||
import json
|
||||
|
||||
|
||||
class GptDrivesTestCase(unittest.TestCase):
|
||||
@unittest.mock.patch('util.paths.cache_dir')
|
||||
def test_drive_reader(self, cdir_mock):
|
||||
'''
|
||||
Test functionality to read objects from Shortcuts.xml
|
||||
'''
|
||||
cdir_mock.return_value = '/var/cache/gpupdate'
|
||||
|
||||
import gpt.drives
|
||||
testdata_path = '{}/test/gpt/data/Drives.xml'.format(os.getcwd())
|
||||
drvs = gpt.drives.read_drives(testdata_path)
|
||||
|
||||
json_obj = json.loads(drvs[0].to_json())
|
||||
self.assertIsNotNone(json_obj['drive'])
|
||||
|
32
gpoa/test/util/test_xdg.py
Normal file
32
gpoa/test/util/test_xdg.py
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from util.xdg import (
|
||||
xdg_get_desktop_user
|
||||
)
|
||||
|
||||
class XDGTestCase(unittest.TestCase):
|
||||
def test_get_desktop_dir(self):
|
||||
print('Machine desktop:')
|
||||
print(xdg_get_desktop_user(None))
|
||||
print('Users desktop:')
|
||||
print(xdg_get_desktop_user('nir'))
|
||||
|
@ -20,6 +20,7 @@ import logging
|
||||
import logging.handlers
|
||||
from enum import IntEnum
|
||||
|
||||
from messages import message_with_code
|
||||
from .logging import slogm
|
||||
|
||||
|
||||
@ -43,7 +44,6 @@ def set_loglevel(loglevel_num=None):
|
||||
|
||||
log_level = 10 * log_num
|
||||
|
||||
print('Setting log level to {}'.format(loglevels[log_num]))
|
||||
logging.basicConfig(format=format_message)
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(log_level)
|
||||
@ -72,7 +72,8 @@ def process_target(target_name=None):
|
||||
if target_name == 'User':
|
||||
target = 'User'
|
||||
|
||||
logging.debug(slogm('Target is: {}'.format(target)))
|
||||
logdata = dict({'target': target})
|
||||
logging.debug(slogm(message_with_code('D10'), logdata))
|
||||
|
||||
return target
|
||||
|
||||
|
81
gpoa/util/config.py
Normal file
81
gpoa/util/config.py
Normal file
@ -0,0 +1,81 @@
|
||||
#
|
||||
# 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 configparser import ConfigParser
|
||||
|
||||
from .util import (
|
||||
get_backends
|
||||
, get_default_policy_name
|
||||
)
|
||||
|
||||
class GPConfig:
|
||||
__config_path = '/etc/gpupdate/gpupdate.ini'
|
||||
|
||||
def __init__(self, config_path=None):
|
||||
if config_path:
|
||||
self.__config_path = config_path
|
||||
|
||||
self.full_config = ConfigParser()
|
||||
self.full_config.read(self.__config_path)
|
||||
|
||||
def get_backend(self):
|
||||
'''
|
||||
Fetch the name of the backend from configuration file.
|
||||
'''
|
||||
if 'gpoa' in self.full_config:
|
||||
if 'backend' in self.full_config['gpoa']:
|
||||
if self.full_config['gpoa']['backend'] in get_backends():
|
||||
return self.full_config['gpoa']['backend']
|
||||
|
||||
return 'samba'
|
||||
|
||||
def set_backend(self, backend_name='local'):
|
||||
self.full_config['gpoa']['backend'] = backend_name
|
||||
self.write_config()
|
||||
|
||||
# This function is not expected corresponding "set_dc()" function
|
||||
# because we have no way to automatically determine such kind
|
||||
# of setting.
|
||||
def get_dc(self):
|
||||
'''
|
||||
Fetch Domain Controller from configuration file.
|
||||
'''
|
||||
if 'samba' in self.full_config:
|
||||
if 'dc' in self.full_config['samba']:
|
||||
return self.full_config['samba']['dc']
|
||||
|
||||
def get_local_policy_template(self):
|
||||
'''
|
||||
Fetch the name of chosen Local Policy template from
|
||||
configuration file.
|
||||
'''
|
||||
if 'gpoa' in self.full_config:
|
||||
if 'local-policy' in self.full_config['gpoa']:
|
||||
return self.full_config['gpoa']['local-policy']
|
||||
|
||||
return get_default_policy_name()
|
||||
|
||||
def set_local_policy_template(self, template_name='default'):
|
||||
self.full_config['gpoa']['local-policy'] = template_name
|
||||
self.write_config()
|
||||
|
||||
def write_config(self):
|
||||
with open(self.__config_path, 'w') as config_file:
|
||||
self.full_config.write(config_file)
|
||||
|
@ -16,10 +16,9 @@
|
||||
# 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 logging
|
||||
import dbus
|
||||
|
||||
from .logging import slogm
|
||||
from .logging import log
|
||||
from .users import is_root
|
||||
|
||||
|
||||
@ -30,41 +29,76 @@ 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:
|
||||
logging.info(slogm('Starting GPO applier for user {} via D-Bus'.format(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:
|
||||
logging.error(slogm('No reply from oddjobd gpoa runner for {}'.format(self.username)))
|
||||
logdata = dict()
|
||||
logdata['username'] = self.username
|
||||
log('E23', logdata)
|
||||
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:
|
||||
logging.error(slogm('No reply from oddjobd gpoa runner for current user'))
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E21', logdata)
|
||||
raise exc
|
||||
else:
|
||||
logging.info(slogm('Starting GPO applier for computer via D-Bus'))
|
||||
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:
|
||||
logging.error(slogm('No reply from oddjobd gpoa runner for computer'))
|
||||
print(exc)
|
||||
logdata = dict({'error': str(exc)})
|
||||
log('E22', logdata)
|
||||
raise exc
|
||||
#self.interface.Quit()
|
||||
|
||||
|
||||
def start_gpupdate_user():
|
||||
@ -130,8 +164,33 @@ def print_dbus_result(result):
|
||||
'''
|
||||
exitcode = result[0]
|
||||
message = result[1:]
|
||||
logging.debug(slogm('Exit code is {}'.format(exitcode)))
|
||||
logdata = dict({'retcode': exitcode})
|
||||
log('D12', logdata)
|
||||
|
||||
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)
|
||||
|
48
gpoa/util/exceptions.py
Normal file
48
gpoa/util/exceptions.py
Normal file
@ -0,0 +1,48 @@
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def geterr():
|
||||
'''
|
||||
Fetches information about recent exception so we will be able
|
||||
to print tracebacks and other information in a uniform way.
|
||||
'''
|
||||
etype, evalue, etrace = sys.exc_info()
|
||||
|
||||
traceinfo = dict({
|
||||
'file': etrace.tb_frame.f_code.co_filename
|
||||
, 'line': etrace.tb_lineno
|
||||
, 'name': etrace.tb_frame.f_code.co_name
|
||||
, 'type': etype.__name__
|
||||
, 'message': evalue
|
||||
})
|
||||
|
||||
del(etype, evalue, etrace)
|
||||
|
||||
return traceinfo
|
||||
|
||||
class NotUNCPathError(Exception):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def __str__(self):
|
||||
return self.path
|
||||
|
@ -16,20 +16,59 @@
|
||||
# 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 logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from .util import get_machine_name
|
||||
from .logging import slogm
|
||||
from .logging import log
|
||||
from .samba import smbopts
|
||||
|
||||
|
||||
def machine_kinit():
|
||||
def machine_kinit(cache_name=None):
|
||||
'''
|
||||
Perform kinit with machine credentials
|
||||
'''
|
||||
opts = smbopts()
|
||||
host = get_machine_name()
|
||||
subprocess.call(['kinit', '-k', host])
|
||||
return check_krb_ticket()
|
||||
realm = opts.get_realm()
|
||||
with_realm = '{}@{}'.format(host, realm)
|
||||
os.environ['KRB5CCNAME'] = 'FILE:{}'.format(cache_name)
|
||||
kinit_cmd = ['kinit', '-k', with_realm]
|
||||
if cache_name:
|
||||
kinit_cmd.extend(['-c', cache_name])
|
||||
proc = subprocess.Popen(kinit_cmd)
|
||||
proc.wait()
|
||||
|
||||
result = False
|
||||
|
||||
if 0 == proc.returncode:
|
||||
result = True
|
||||
|
||||
if result:
|
||||
result = check_krb_ticket()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def machine_kdestroy(cache_name=None):
|
||||
'''
|
||||
Perform kdestroy for machine credentials
|
||||
'''
|
||||
host = get_machine_name()
|
||||
kdestroy_cmd = ['kdestroy']
|
||||
if cache_name:
|
||||
kdestroy_cmd.extend(['-c', cache_name])
|
||||
|
||||
if cache_name or 'KRB5CCNAME' in os.environ:
|
||||
proc = subprocess.Popen(kdestroy_cmd, stderr=subprocess.DEVNULL)
|
||||
proc.wait()
|
||||
|
||||
if cache_name and os.path.exists(cache_name):
|
||||
os.unlink(cache_name)
|
||||
elif 'KRB5CCNAME' in os.environ:
|
||||
path = os.environ['KRB5CCNAME'][5:]
|
||||
if os.path.exists(path):
|
||||
os.unlink(path)
|
||||
|
||||
|
||||
def check_krb_ticket():
|
||||
@ -40,11 +79,13 @@ def check_krb_ticket():
|
||||
try:
|
||||
subprocess.check_call(['klist', '-s'])
|
||||
output = subprocess.check_output('klist', stderr=subprocess.STDOUT).decode()
|
||||
logging.info(output)
|
||||
result = True
|
||||
except:
|
||||
logging.error(slogm('Kerberos ticket check unsuccessful'))
|
||||
|
||||
logging.debug(slogm('Ticket check succeed'))
|
||||
logdata = dict()
|
||||
logdata['output'] = output
|
||||
log('D17', logdata)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['krb-exc'] = exc
|
||||
log('E14', logdata)
|
||||
|
||||
return result
|
||||
|
@ -18,11 +18,15 @@
|
||||
|
||||
import json
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from messages import message_with_code
|
||||
|
||||
|
||||
class encoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
result = super(encoder, self).default(obj)
|
||||
result = super(encoder, self)
|
||||
result = result.default(obj)
|
||||
|
||||
if isinstance(obj, set):
|
||||
result = tuple(obj)
|
||||
@ -36,19 +40,38 @@ class slogm(object):
|
||||
'''
|
||||
Structured log message class
|
||||
'''
|
||||
def __init__(self, message, **kwargs):
|
||||
def __init__(self, message, kwargs=dict()):
|
||||
self.message = message
|
||||
self.kwargs = kwargs
|
||||
if not self.kwargs:
|
||||
self.kwargs = dict()
|
||||
|
||||
def __str__(self):
|
||||
now = str(datetime.datetime.now())
|
||||
now = str(datetime.datetime.now().isoformat(sep=' ', timespec='milliseconds'))
|
||||
args = dict()
|
||||
args.update(dict({'timestamp': now, 'message': str(self.message)}))
|
||||
args.update(self.kwargs)
|
||||
|
||||
kwa = encoder().encode(args)
|
||||
|
||||
result = '{}:{}'.format(now.rpartition('.')[0], self.message)
|
||||
result = '{}|{}|{}'.format(now, self.message, args)
|
||||
|
||||
return result
|
||||
|
||||
def log(message_code, data=None):
|
||||
mtype = message_code[0]
|
||||
|
||||
if 'I' == mtype:
|
||||
logging.info(slogm(message_with_code(message_code), data))
|
||||
return
|
||||
if 'W' == mtype:
|
||||
logging.warning(slogm(message_with_code(message_code), data))
|
||||
return
|
||||
if 'E' == mtype:
|
||||
logging.error(slogm(message_with_code(message_code), data))
|
||||
return
|
||||
if 'F' == mtype:
|
||||
logging.fatal(slogm(message_with_code(message_code), data))
|
||||
return
|
||||
if 'D' == mtype:
|
||||
logging.debug(slogm(message_with_code(message_code), data))
|
||||
return
|
||||
|
||||
logging.error(slogm(message_with_code(message_code), data))
|
||||
|
||||
|
@ -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,23 +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'
|
||||
etc_local_policy_default = '/etc/local-policy/active'
|
||||
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(etc_local_policy_default):
|
||||
result_path = pathlib.Path(etc_local_policy_default)
|
||||
if os.path.exists(local_policy_template):
|
||||
result_path = pathlib.Path(local_policy_template)
|
||||
elif os.path.exists(local_policy_template_path):
|
||||
result_path = pathlib.Path(local_policy_template_path)
|
||||
|
||||
return pathlib.Path(result_path)
|
||||
|
||||
|
||||
def cache_dir():
|
||||
'''
|
||||
Returns path pointing to gpupdate's cache directory
|
||||
@ -46,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():
|
||||
'''
|
||||
@ -59,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()
|
||||
|
||||
|
@ -16,14 +16,13 @@
|
||||
# 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 logging
|
||||
|
||||
from xml.etree import ElementTree
|
||||
from storage import registry_factory
|
||||
|
||||
from samba.gp_parse.gp_pol import GPPolParser
|
||||
|
||||
from .logging import slogm
|
||||
from .logging import log
|
||||
|
||||
|
||||
def load_preg(file_path):
|
||||
@ -40,7 +39,8 @@ def load_xml_preg(xml_path):
|
||||
'''
|
||||
Parse XML/PReg file and return its preg object
|
||||
'''
|
||||
logging.debug('Loading PReg from XML: {}'.format(xml_path))
|
||||
logdata = dict({'polfile': xml_path})
|
||||
log('D36', logdata)
|
||||
gpparser = GPPolParser()
|
||||
xml_root = ElementTree.parse(xml_path).getroot()
|
||||
gpparser.load_xml(xml_root)
|
||||
@ -53,16 +53,20 @@ def load_pol_preg(polfile):
|
||||
'''
|
||||
Parse PReg file and return its preg object
|
||||
'''
|
||||
logging.debug(slogm('Loading PReg from .pol file: {}'.format(polfile)))
|
||||
logdata = dict({'polfile': polfile})
|
||||
log('D31', logdata)
|
||||
gpparser = GPPolParser()
|
||||
data = None
|
||||
|
||||
with open(polfile, 'rb') as f:
|
||||
data = f.read()
|
||||
logdata = dict({'polfile': polfile, 'length': len(data)})
|
||||
log('D33', logdata)
|
||||
gpparser.parse(data)
|
||||
|
||||
#print(gpparser.pol_file.__ndr_print__())
|
||||
return gpparser.pol_file
|
||||
pentries = preg2entries(gpparser.pol_file)
|
||||
return pentries
|
||||
|
||||
|
||||
def preg_keymap(preg):
|
||||
@ -76,15 +80,16 @@ def preg_keymap(preg):
|
||||
return keymap
|
||||
|
||||
|
||||
def merge_polfile(preg, sid=None, reg_name='registry', reg_path=None):
|
||||
def merge_polfile(preg, sid=None, reg_name='registry', reg_path=None, policy_name='Unknown'):
|
||||
pregfile = load_preg(preg)
|
||||
logging.info(slogm('Loaded PReg {}'.format(preg)))
|
||||
logdata = dict({'pregfile': preg})
|
||||
log('D32', logdata)
|
||||
storage = registry_factory(reg_name, reg_path)
|
||||
for entry in pregfile.entries:
|
||||
if not sid:
|
||||
storage.add_hklm_entry(entry)
|
||||
storage.add_hklm_entry(entry, policy_name)
|
||||
else:
|
||||
storage.add_hkcu_entry(entry, sid)
|
||||
storage.add_hkcu_entry(entry, sid, policy_name)
|
||||
|
||||
|
||||
class entry:
|
||||
@ -93,12 +98,22 @@ class entry:
|
||||
self.valuename = e_valuename
|
||||
self.type = e_type
|
||||
self.data = e_data
|
||||
logdata = dict()
|
||||
logdata['keyname'] = self.keyname
|
||||
logdata['valuename'] = self.valuename
|
||||
logdata['type'] = self.type
|
||||
logdata['data'] = self.data
|
||||
log('D22', logdata)
|
||||
|
||||
class pentries:
|
||||
def __init__(self):
|
||||
self.entries = list()
|
||||
|
||||
|
||||
def preg2entries(preg_obj):
|
||||
entries = []
|
||||
for elem in prej_obj.entries:
|
||||
entries = pentries()
|
||||
for elem in preg_obj.entries:
|
||||
entry_obj = entry(elem.keyname, elem.valuename, elem.type, elem.data)
|
||||
entries.append(entry_obj)
|
||||
entries.entries.append(entry_obj)
|
||||
return entries
|
||||
|
||||
|
@ -34,11 +34,11 @@ def is_rpm_installed(rpm_name):
|
||||
return False
|
||||
|
||||
class Package:
|
||||
__install_command = ['/usr/bin/apt-get', '-y', 'install']
|
||||
__remove_command = ['/usr/bin/apt-get', '-y', 'remove']
|
||||
__reinstall_command = ['/usr/bin/apt-get', '-y', 'reinstall']
|
||||
|
||||
def __init__(self, package_name):
|
||||
self.__install_command = ['/usr/bin/apt-get', '-y', 'install']
|
||||
self.__remove_command = ['/usr/bin/apt-get', '-y', 'remove']
|
||||
self.__reinstall_command = ['/usr/bin/apt-get', '-y', 'reinstall']
|
||||
self.package_name = package_name
|
||||
self.for_install = True
|
||||
|
||||
@ -102,7 +102,6 @@ def install_rpm(rpm_name):
|
||||
'''
|
||||
Install single RPM
|
||||
'''
|
||||
update()
|
||||
rpm = Package(rpm_name)
|
||||
return rpm.install()
|
||||
|
||||
|
@ -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)
|
||||
|
245
gpoa/util/sid.py
Normal file
245
gpoa/util/sid.py
Normal file
@ -0,0 +1,245 @@
|
||||
#! /usr/bin/env python3
|
||||
#
|
||||
# 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 enum import Enum
|
||||
|
||||
|
||||
def wbinfo_getsid(domain, user):
|
||||
'''
|
||||
Get SID using wbinfo
|
||||
'''
|
||||
# This part works only on client
|
||||
username = '{}\\{}'.format(domain.upper(), user)
|
||||
sid = pysss_nss_idmap.getsidbyname(username)
|
||||
|
||||
if username in sid:
|
||||
return sid[username]['sid']
|
||||
|
||||
# This part works only on DC
|
||||
wbinfo_cmd = ['wbinfo', '-n', username]
|
||||
output = subprocess.check_output(wbinfo_cmd)
|
||||
sid = output.split()[0].decode('utf-8')
|
||||
|
||||
return sid
|
||||
|
||||
|
||||
def get_sid(domain, username):
|
||||
'''
|
||||
Lookup SID not only using wbinfo or sssd but also using own cache
|
||||
'''
|
||||
domain_username = '{}\\{}'.format(domain, username)
|
||||
sid = 'local-{}'.format(username)
|
||||
|
||||
try:
|
||||
sid = wbinfo_getsid(domain, username)
|
||||
except:
|
||||
sid = 'local-{}'.format(username)
|
||||
logging.warning(
|
||||
slogm('Error getting SID using wbinfo, will use cached SID: {}'.format(sid)))
|
||||
|
||||
logging.debug(slogm('Working with SID: {}'.format(sid)))
|
||||
|
||||
return sid
|
||||
|
||||
|
||||
class IssuingAuthority(Enum):
|
||||
SECURITY_NULL_SID_AUTHORITY = 0
|
||||
SECURITY_WORLD_SID_AUTHORITY = 1
|
||||
SECURITY_LOCAL_SID_AUTHORITY = 2
|
||||
SECURITY_CREATOR_SID_AUTHORITY = 3
|
||||
SECURITY_NON_UNIQUE_AUTHORITY = 4
|
||||
SECURITY_NT_AUTHORITY = 5
|
||||
SECURITY_RESOURCE_MANAGER_AUTHORITY = 9
|
||||
|
||||
class SidRevision(Enum):
|
||||
FIRST = 1
|
||||
|
||||
# This thing exists only after "S-1-5-21-"
|
||||
# Last part of full SID
|
||||
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab
|
||||
class WellKnown21RID(Enum):
|
||||
ENTERPRISE_READONLY_DOMAIN_CONTROLLERS = 498
|
||||
ADMINISTRATOR = 500 # For machine
|
||||
GUEST = 501 # For machine
|
||||
KRBTGT = 502
|
||||
DOMAIN_ADMINS = 512
|
||||
DOMAIN_USERS = 513
|
||||
DOMAIN_GUESTS = 514
|
||||
DOMAIN_COMPUTERS = 515
|
||||
DOMAIN_CONTROLLERS = 516
|
||||
CERT_PUBLISHERS = 517
|
||||
SCHEMA_ADMINISTRATORS = 518 # For root domain
|
||||
ENTERPRISE_ADMINS = 519 # For root domain
|
||||
GROUP_POLICY_CREATOR_OWNERS = 520
|
||||
READONLY_DOMAIN_CONTROLLERS = 521
|
||||
CLONEABLE_CONTROLLERS = 522
|
||||
PROTECTED_USERS = 525
|
||||
KEY_ADMINS = 526
|
||||
ENTERPRISE_KEY_ADMINS = 527
|
||||
RAS_SERVERS = 553
|
||||
ALLOWED_RODC_PASSWORD_REPLICATION_GROUP = 571
|
||||
DENIED_RODC_PASSWORD_REPLICATION_GROUP = 572
|
||||
|
||||
# This thing exists only after "S-1-5-32-"
|
||||
# Last part of full SID
|
||||
# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/81d92bba-d22b-4a8c-908a-554ab29148ab
|
||||
class WellKnown32RID(Enum):
|
||||
BUILTIN_ADMINISTRATORS = 544
|
||||
BUILTIN_USERS = 545
|
||||
BUILTIN_GUESTS = 546
|
||||
POWER_USERS = 547
|
||||
ACCOUNT_OPERATORS = 548
|
||||
SERVER_OPERATORS = 549
|
||||
PRINTER_OPERATORS = 550
|
||||
BACKUP_OPERATORS = 551
|
||||
REPLICATOR = 552
|
||||
ALIAS_PREW2KCOMPACC = 554
|
||||
REMOTE_DESKTOP = 555
|
||||
NETWORK_CONFIGURATION_OPS = 556
|
||||
INCOMING_FOREST_TRUST_BUILDERS = 557
|
||||
PERFMON_USERS = 558
|
||||
PERFLOG_USERS = 559
|
||||
WINDOWS_AUTHORIZATION_ACCESS_GROUP = 560
|
||||
TERMINAL_SERVER_LICENSE_SERVERS = 561
|
||||
DISTRIBUTED_COM_USERS = 562
|
||||
IIS_IUSRS = 568
|
||||
CRYPTOGRAPHIC_OPERATORS = 569
|
||||
EVENT_LOG_READERS = 573
|
||||
CERTIFICATE_SERVICE_DCOM_ACCESS = 574
|
||||
RDS_REMOTE_ACCESS_SERVERS = 575
|
||||
RDS_ENDPOINT_SERVERS = 576
|
||||
RDS_MANAGEMENT_SERVERS = 577
|
||||
HYPER_V_ADMINS = 578
|
||||
ACCESS_CONTROL_ASSISTANCE_OPS = 579
|
||||
REMOTE_MANAGEMENT_USERS = 580
|
||||
|
||||
# This thing exists only after "S-1-5-"
|
||||
class FirstSubAuthority(Enum):
|
||||
SECURITY_DIALUP_RID = 1
|
||||
SECURITY_NETWORK_RID = 2
|
||||
SECURITY_BATCH_RID = 3
|
||||
SECURITY_INTERACTIVE_RID = 4
|
||||
SECURITY_LOGON_IDS_RID = 5
|
||||
SECURITY_SERVICE_RID = 6
|
||||
SECURITY_ANONYMOUS_LOGON_RID = 7
|
||||
SECURITY_PROXY_RID = 8
|
||||
SECURITY_ENTERPRISE_CONTROLLERS_RID = 9
|
||||
SECURITY_PRINCIPAL_SELF_RID = 10
|
||||
SECURITY_AUTHENTICATED_USER_RID = 11
|
||||
SECURITY_RESTRICTED_CODE_RID = 12
|
||||
SECURITY_TERMINAL_SERVER_RID = 13
|
||||
SECURITY_LOCAL_SYSTEM_RID = 18
|
||||
SECURITY_NT_NON_UNIQUE = 21
|
||||
SECURITY_BUILTIN_DOMAIN_RID = 32
|
||||
SECURITY_WRITE_RESTRICTED_CODE_RID = 33
|
||||
|
||||
class SecondSubAuthority(Enum):
|
||||
DOMAIN_ALIAS_RID_ADMINS = 544
|
||||
|
||||
def validate_issuing_authority(ia_num):
|
||||
ia_value = None
|
||||
|
||||
ia_value = int(IssuingAuthority(ia_num))
|
||||
|
||||
return ia_value
|
||||
|
||||
def validate_sid_revision(revnum):
|
||||
rev_value = None
|
||||
|
||||
rev_value = int(SidRevision(revnum))
|
||||
|
||||
return rev_value
|
||||
|
||||
def is_sid(sid):
|
||||
# Check that SID is SID (S)
|
||||
if not sid[0] == 'S':
|
||||
return False
|
||||
|
||||
# Check revision version (1 for Windows-generated SID) (R)
|
||||
if not validate_sid_revision(int(sid[2])):
|
||||
return False
|
||||
|
||||
# Check issuing authority (IA)
|
||||
issuing_authority = validate_issuing_authority(int(sid[4]))
|
||||
if not issuing_authority:
|
||||
return False
|
||||
|
||||
if issuing_authority == 21:
|
||||
pass
|
||||
elif issuing_authority == 32:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
def sid2descr(sid):
|
||||
sids = dict()
|
||||
sids['S-1-0'] = 'Null Authority'
|
||||
sids['S-1-0-0'] = 'Nobody'
|
||||
sids['S-1-1'] = 'World Authority'
|
||||
sids['S-1-1-0'] = 'Everyone'
|
||||
sids['S-1-2'] = 'Local Authority'
|
||||
sids['S-1-2-0'] = 'Local'
|
||||
sids['S-1-3'] = 'Creator Authority'
|
||||
sids['S-1-3-0'] = 'Creator Owner'
|
||||
sids['S-1-3-1'] = 'Creator Group'
|
||||
sids['S-1-3-2'] = 'Creator Owner Server' # Since Windows 2003
|
||||
sids['S-1-3-3'] = 'Creator Group Server' # Since Windows 2003
|
||||
sids['S-1-3-4'] = 'Owner Rights'
|
||||
sids['S-1-4'] = 'Non-unique Authority'
|
||||
sids['S-1-5'] = 'NT Authority'
|
||||
sids['S-1-5-1'] = 'Dialup'
|
||||
sids['S-1-5-2'] = 'Network'
|
||||
sids['S-1-5-3'] = 'Batch'
|
||||
sids['S-1-5-4'] = 'Interactive'
|
||||
sids['S-1-5-6'] = 'Service'
|
||||
sids['S-1-5-7'] = 'Anonymous'
|
||||
sids['S-1-5-8'] = 'Proxy' # Since Windows 2003
|
||||
sids['S-1-5-9'] = 'Enterprise Domain Controllers'
|
||||
sids['S-1-5-10'] = 'Principal Self'
|
||||
sids['S-1-5-11'] = 'Authenticated Users'
|
||||
sids['S-1-5-12'] = 'Restricted Code'
|
||||
sids['S-1-5-13'] = 'Terminal Server Users'
|
||||
sids['S-1-5-14'] = 'Remote Interactive Logon'
|
||||
sids['S-1-5-15'] = 'This Organization' # Since Windows 2003
|
||||
sids['S-1-5-17'] = 'This Organization'
|
||||
sids['S-1-5-18'] = 'Local System'
|
||||
sids['S-1-5-19'] = 'NT Authority' # Local Service
|
||||
sids['S-1-5-20'] = 'NT Authority' # Network Service
|
||||
sids['S-1-5-32-544'] = 'Administrators'
|
||||
sids['S-1-5-32-545'] = 'Users'
|
||||
sids['S-1-5-32-546'] = 'Guests'
|
||||
sids['S-1-5-32-547'] = 'Power Users'
|
||||
sids['S-1-5-32-548'] = 'Account Operators'
|
||||
sids['S-1-5-32-549'] = 'Server Operators'
|
||||
sids['S-1-5-32-550'] = 'Print Operators'
|
||||
sids['S-1-5-32-551'] = 'Backup Operators'
|
||||
sids['S-1-5-32-552'] = 'Replicators'
|
||||
sids['S-1-5-32-554'] = 'Builtin\\Pre-Windows 2000 Compatible Access' # Since Windows 2003
|
||||
sids['S-1-5-32-555'] = 'Builtin\\Remote Desktop Users' # Since Windows 2003
|
||||
sids['S-1-5-32-556'] = 'Builtin\\Network Configuration Operators' # Since Windows 2003
|
||||
sids['S-1-5-32-557'] = 'Builtin\\Incoming Forest Trust Builders' # Since Windows 2003
|
||||
sids['S-1-5-32-558'] = 'Builtin\\Performance Monitor Users' # Since Windows 2003
|
||||
sids['S-1-5-32-582'] = 'Storage Replica Administrators'
|
||||
sids['S-1-5-64-10'] = 'NTLM Authentication'
|
||||
sids['S-1-5-64-14'] = 'SChannel Authentication'
|
||||
sids['S-1-5-64-21'] = 'Digest Authentication'
|
||||
sids['S-1-5-80'] = 'NT Service'
|
||||
|
||||
return sids.get(sid, None)
|
||||
|
@ -16,16 +16,20 @@
|
||||
# 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 signal
|
||||
from sys import exit
|
||||
|
||||
from .arguments import ExitCodeUpdater
|
||||
|
||||
default_handler = signal.getsignal(signal.SIGINT)
|
||||
from .kerberos import machine_kdestroy
|
||||
|
||||
def signal_handler(sig_number, frame):
|
||||
print('Received signal, exiting gracefully')
|
||||
# Ignore extra signals
|
||||
signal.signal(sig_number, signal.SIG_IGN)
|
||||
print('Received signal, exiting gracefully')
|
||||
exit(ExitCodeUpdater.EXIT_SIGINT)
|
||||
|
||||
# Kerberos cache cleanup on interrupt
|
||||
machine_kdestroy()
|
||||
|
||||
os._exit(ExitCodeUpdater.EXIT_SIGINT)
|
||||
|
||||
|
143
gpoa/util/system.py
Normal file
143
gpoa/util/system.py
Normal file
@ -0,0 +1,143 @@
|
||||
#
|
||||
# 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
|
||||
import locale
|
||||
from .logging import log
|
||||
from .dbus import dbus_session
|
||||
|
||||
|
||||
def set_privileges(username, uid, gid, groups, home):
|
||||
'''
|
||||
Set current process privileges
|
||||
'''
|
||||
defaultlocale = locale.getdefaultlocale()
|
||||
os.environ.clear()
|
||||
os.environ['HOME'] = home
|
||||
os.environ['USER'] = username
|
||||
os.environ['USERNAME'] = username
|
||||
if defaultlocale[0] and defaultlocale[1]:
|
||||
os.environ["LANG"] = '.'.join(defaultlocale)
|
||||
|
||||
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
|
||||
@ -18,9 +18,8 @@
|
||||
|
||||
import os
|
||||
import pwd
|
||||
import logging
|
||||
|
||||
from .logging import slogm
|
||||
from .logging import log
|
||||
|
||||
|
||||
def is_root():
|
||||
@ -56,54 +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')
|
||||
|
||||
logging.debug(
|
||||
slogm('Set process permissions to UID {} and GID {} for user {}'.format(
|
||||
uid, gid, username)))
|
||||
|
||||
|
||||
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
|
||||
try:
|
||||
func()
|
||||
except Exception as exc:
|
||||
logging.debug(slogm(exc))
|
||||
|
||||
# Restore privileges
|
||||
set_privileges('root', current_uid, 0, current_groups)
|
||||
|
||||
|
@ -17,17 +17,22 @@
|
||||
# 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
|
||||
|
||||
|
||||
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):
|
||||
@ -83,3 +88,95 @@ def mk_homedir_path(username, homedir_path):
|
||||
os.chown(homedir, uid=uid, gid=gid)
|
||||
longer_path = os.path.join(longer_path, elem)
|
||||
|
||||
def runcmd(command_name):
|
||||
'''
|
||||
Run application.
|
||||
'''
|
||||
try:
|
||||
with subprocess.Popen(command_name, stdout=subprocess.PIPE) as proc:
|
||||
value = proc.stdout.read().decode('utf-8')
|
||||
proc.wait()
|
||||
rc = proc.returncode
|
||||
return (rc, value)
|
||||
except Exception as exc:
|
||||
print(str(exc))
|
||||
|
||||
def get_backends():
|
||||
'''
|
||||
Get the list of backends supported by GPOA
|
||||
'''
|
||||
command = ['/usr/sbin/gpoa', '--list-backends']
|
||||
backends = list()
|
||||
out = list()
|
||||
|
||||
with subprocess.Popen(command, stdout=subprocess.PIPE) as proc:
|
||||
out = proc.stdout.read().decode('utf-8')
|
||||
proc.wait()
|
||||
out = out.split('\n')
|
||||
for line in out:
|
||||
tmpline = line.replace('\n', '')
|
||||
if tmpline != '':
|
||||
backends.append(tmpline)
|
||||
|
||||
return backends
|
||||
|
||||
def get_default_policy_name():
|
||||
'''
|
||||
Determine the preferred Local Policy template name according to
|
||||
ALT distribution type
|
||||
'''
|
||||
localpolicy = 'workstation'
|
||||
dcpolicy = 'ad-domain-controller'
|
||||
|
||||
try:
|
||||
if smbopts().get_server_role() == 'active directory domain controller':
|
||||
return dcpolicy
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
release = '/etc/altlinux-release'
|
||||
if os.path.isfile(release):
|
||||
f = open(release)
|
||||
s = f.readline()
|
||||
if re.search('server', s, re.I):
|
||||
localpolicy = 'server'
|
||||
except:
|
||||
pass
|
||||
|
||||
return localpolicy
|
||||
|
||||
def get_policy_entries(directory):
|
||||
'''
|
||||
Get list of directories representing "Local Policy" templates.
|
||||
'''
|
||||
filtered_entries = list()
|
||||
if os.path.isdir(directory):
|
||||
entries = [os.path.join(directory, entry) for entry in os.listdir(directory)]
|
||||
|
||||
for entry in entries:
|
||||
if os.path.isdir(os.path.join(entry)):
|
||||
if not os.path.islink(os.path.join(entry)):
|
||||
if not entry.rpartition('/')[2] == 'default':
|
||||
filtered_entries.append(entry)
|
||||
|
||||
return filtered_entries
|
||||
|
||||
def get_policy_variants():
|
||||
'''
|
||||
Get the list of local policy variants deployed on this system.
|
||||
Please note that is case overlapping names the names in
|
||||
/etc/local-policy must override names in /usr/share/local-policy
|
||||
'''
|
||||
policy_dir = '/usr/share/local-policy'
|
||||
etc_policy_dir = '/etc/local-policy'
|
||||
|
||||
system_policies = get_policy_entries(policy_dir)
|
||||
user_policies = get_policy_entries(etc_policy_dir)
|
||||
|
||||
general_listing = list()
|
||||
general_listing.extend(system_policies)
|
||||
general_listing.extend(user_policies)
|
||||
|
||||
return general_listing
|
||||
|
||||
|
@ -17,21 +17,23 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
|
||||
from samba import getopt as options
|
||||
|
||||
from samba import NTSTATUSError
|
||||
from samba.gpclass import get_dc_hostname, check_refresh_gpo_list
|
||||
from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
|
||||
import samba.gpo
|
||||
import pysss_nss_idmap
|
||||
|
||||
from storage import cache_factory
|
||||
from .xdg import get_user_dir
|
||||
from messages import message_with_code
|
||||
from .xdg import (
|
||||
xdg_get_desktop
|
||||
)
|
||||
from .util import get_homedir
|
||||
from .logging import slogm
|
||||
from .logging import log
|
||||
from .samba import smbopts
|
||||
|
||||
|
||||
@ -41,7 +43,7 @@ class smbcreds (smbopts):
|
||||
smbopts.__init__(self, 'GPO Applier')
|
||||
self.credopts = options.CredentialsOptions(self.parser)
|
||||
self.creds = self.credopts.get_credentials(self.lp, fallback_machine=True)
|
||||
self.selected_dc = self.set_dc(dc_fqdn)
|
||||
self.set_dc(dc_fqdn)
|
||||
|
||||
def get_dc(self):
|
||||
return self.selected_dc
|
||||
@ -53,21 +55,21 @@ class smbcreds (smbopts):
|
||||
self.selected_dc = None
|
||||
|
||||
try:
|
||||
samba_dc = get_dc_hostname(self.creds, self.lp)
|
||||
|
||||
if samba_dc != dc_fqdn and dc_fqdn is not None:
|
||||
|
||||
logging.debug(
|
||||
slogm('Samba DC setting is {} and is overwritten by user setting {}'.format(
|
||||
samba_dc, dc)))
|
||||
if dc_fqdn is not None:
|
||||
logdata = dict()
|
||||
logdata['user_dc'] = dc_fqdn
|
||||
log('D38', logdata)
|
||||
|
||||
self.selected_dc = dc_fqdn
|
||||
else:
|
||||
self.selected_dc = samba_dc
|
||||
except:
|
||||
logging.error(slogm('Unable to determine DC hostname'))
|
||||
|
||||
return self.selected_dc
|
||||
self.list_selected_dc = set()
|
||||
self.selected_dc = get_dc_hostname(self.creds, self.lp)
|
||||
self.list_selected_dc.add(self.selected_dc)
|
||||
except Exception as exc:
|
||||
logdata = dict()
|
||||
logdata['msg'] = str(exc)
|
||||
log('E10', logdata)
|
||||
raise exc
|
||||
|
||||
def get_domain(self):
|
||||
'''
|
||||
@ -79,9 +81,11 @@ class smbcreds (smbopts):
|
||||
# Look and python/samba/netcmd/domain.py for more examples
|
||||
res = netcmd_get_domain_infos_via_cldap(self.lp, None, self.selected_dc)
|
||||
dns_domainname = res.dns_domain
|
||||
logging.info(slogm('Found domain via CLDAP: {}'.format(dns_domainname)))
|
||||
except:
|
||||
logging.error(slogm('Unable to retrieve domain name via CLDAP query'))
|
||||
logdata = dict({'domain': dns_domainname})
|
||||
log('D18', logdata)
|
||||
except Exception as exc:
|
||||
log('E15')
|
||||
raise exc
|
||||
|
||||
return dns_domainname
|
||||
|
||||
@ -93,34 +97,52 @@ class smbcreds (smbopts):
|
||||
gpos = list()
|
||||
|
||||
try:
|
||||
log('D48')
|
||||
ads = samba.gpo.ADS_STRUCT(self.selected_dc, self.lp, self.creds)
|
||||
if ads.connect():
|
||||
log('D47')
|
||||
gpos = ads.get_gpo_list(username)
|
||||
logging.info(slogm('Got GPO list for {}:'.format(username)))
|
||||
logdata = dict({'username': username})
|
||||
log('I1', logdata)
|
||||
for gpo in gpos:
|
||||
# These setters are taken from libgpo/pygpo.c
|
||||
# print(gpo.ds_path) # LDAP entry
|
||||
logging.info(slogm('GPO: {} ({})'.format(gpo.display_name, 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:
|
||||
logging.error(
|
||||
slogm('Unable to get GPO list for {} from {}'.format(
|
||||
username, self.selected_dc)))
|
||||
logdata = dict({'username': username, 'dc': self.selected_dc})
|
||||
log('E17', logdata)
|
||||
|
||||
return gpos
|
||||
|
||||
def update_gpos(self, username):
|
||||
gpos = self.get_gpos(username)
|
||||
|
||||
try:
|
||||
check_refresh_gpo_list(self.selected_dc, self.lp, self.creds, gpos)
|
||||
except Exception as exc:
|
||||
logging.error(
|
||||
slogm('Unable to refresh GPO list for {} from {}'.format(
|
||||
username, self.selected_dc)))
|
||||
while self.list_selected_dc:
|
||||
logdata = dict()
|
||||
logdata['username'] = username
|
||||
logdata['dc'] = self.selected_dc
|
||||
try:
|
||||
log('D49', logdata)
|
||||
check_refresh_gpo_list(self.selected_dc, self.lp, self.creds, gpos)
|
||||
log('D50', logdata)
|
||||
self.list_selected_dc.clear()
|
||||
except NTSTATUSError as smb_exc:
|
||||
logdata['smb_exc'] = str(smb_exc)
|
||||
self.selected_dc = get_dc_hostname(self.creds, self.lp)
|
||||
if self.selected_dc not in self.list_selected_dc:
|
||||
logdata['action'] = 'Search another dc'
|
||||
log('W11', logdata)
|
||||
self.list_selected_dc.add(self.selected_dc)
|
||||
else:
|
||||
log('F1', logdata)
|
||||
raise smb_exc
|
||||
except Exception as exc:
|
||||
logdata['exc'] = str(exc)
|
||||
log('F1', logdata)
|
||||
raise exc
|
||||
return gpos
|
||||
|
||||
|
||||
def wbinfo_getsid(domain, user):
|
||||
'''
|
||||
Get SID using wbinfo
|
||||
@ -161,10 +183,11 @@ def get_sid(domain, username, is_machine = False):
|
||||
try:
|
||||
sid = wbinfo_getsid(domain, username)
|
||||
except:
|
||||
logging.warning(
|
||||
slogm('Error getting SID using wbinfo, will use cached SID: {}'.format(sid)))
|
||||
logdata = dict({'sid': sid})
|
||||
log('E16', logdata)
|
||||
|
||||
logging.debug(slogm('Working with SID: {}'.format(sid)))
|
||||
logdata = dict({'sid': sid})
|
||||
log('D21', logdata)
|
||||
|
||||
return sid
|
||||
|
||||
@ -174,23 +197,25 @@ def expand_windows_var(text, username=None):
|
||||
Scan the line for percent-encoded variables and expand them.
|
||||
'''
|
||||
variables = dict()
|
||||
variables['HOME'] = '/'
|
||||
variables['HOME'] = '/etc/skel'
|
||||
variables['HOMEPATH'] = '/etc/skel'
|
||||
variables['HOMEDRIVE'] = '/'
|
||||
variables['SystemRoot'] = '/'
|
||||
variables['StartMenuDir'] = '/usr/share/applications'
|
||||
variables['SystemDrive'] = '/'
|
||||
variables['DesktopDir'] = xdg_get_desktop(username, variables['HOME'])
|
||||
|
||||
if username:
|
||||
variables['LogonUser'] = username
|
||||
variables['HOME'] = get_homedir(username)
|
||||
|
||||
variables['DesktopDir'] = get_user_dir(
|
||||
'DESKTOP', os.path.join(variables['HOME'], 'Desktop'))
|
||||
variables['HOMEPATH'] = get_homedir(username)
|
||||
|
||||
variables['StartMenuDir'] = os.path.join(
|
||||
variables['HOME'], '.local', 'share', 'applications')
|
||||
|
||||
result = text
|
||||
for var in variables.keys():
|
||||
result = result.replace('%{}%'.format(var), variables[var])
|
||||
result = result.replace('%{}%'.format(var), variables[var]).replace('/', '//')
|
||||
|
||||
return result
|
||||
|
||||
|
@ -17,21 +17,22 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from configparser import RawConfigParser, DEFAULTSECT
|
||||
import os
|
||||
from xdg.BaseDirectory import xdg_config_home
|
||||
|
||||
from .util import get_homedir
|
||||
from .logging import log
|
||||
|
||||
def get_user_dir(dir_name, default=None):
|
||||
'''
|
||||
Get path to XDG's user directory
|
||||
'''
|
||||
config = RawConfigParser(allow_no_value=True)
|
||||
userdirs_path = os.path.join(xdg_config_home, 'user-dirs.dirs')
|
||||
try:
|
||||
with open(userdirs_path, 'r') as f:
|
||||
config.read_string('[DEFAULT]\n' + f.read())
|
||||
return config.get(DEFAULTSECT, 'XDG_DESKTOP_DIR')
|
||||
except Exception as exc:
|
||||
return default
|
||||
def xdg_get_desktop(username, homedir = None):
|
||||
if username:
|
||||
homedir = get_homedir(username)
|
||||
if not homedir:
|
||||
msgtext = message_with_code('E18')
|
||||
logdata = dict()
|
||||
logdata['username'] = username
|
||||
log('E18', logdata)
|
||||
raise Exception(msgtext)
|
||||
|
||||
stream = os.popen('export HOME={}; xdg-user-dir DESKTOP'.format(homedir))
|
||||
output = stream.read()[:-1]
|
||||
return output
|
||||
|
||||
|
168
gpupdate.spec
168
gpupdate.spec
@ -1,7 +1,7 @@
|
||||
%define _unpackaged_files_terminate_build 1
|
||||
|
||||
Name: gpupdate
|
||||
Version: 0.5.0
|
||||
Version: 0.9.9
|
||||
Release: alt1
|
||||
|
||||
Summary: GPT applier
|
||||
@ -11,17 +11,20 @@ Url: https://github.com/altlinux/gpupdate
|
||||
BuildArch: noarch
|
||||
|
||||
Requires: control
|
||||
Requires: local-policy >= 0.1.0
|
||||
|
||||
BuildRequires: rpm-build-python3
|
||||
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.3.0
|
||||
Requires: pam-config >= 1.8
|
||||
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
|
||||
|
||||
@ -38,27 +41,53 @@ mkdir -p \
|
||||
cp -r gpoa \
|
||||
%buildroot%python3_sitelibdir/
|
||||
|
||||
# Generate translations
|
||||
msgfmt \
|
||||
-o %buildroot%python3_sitelibdir/gpoa/locale/ru_RU/LC_MESSAGES/gpoa.mo \
|
||||
%buildroot%python3_sitelibdir/gpoa/locale/ru_RU/LC_MESSAGES/gpoa.po
|
||||
|
||||
mkdir -p \
|
||||
%buildroot%_bindir/ \
|
||||
%buildroot%_sbindir/ \
|
||||
%buildroot%_cachedir/%name/
|
||||
%buildroot%_cachedir/%name/ \
|
||||
%buildroot%_cachedir/%{name}_file_cache/ \
|
||||
%buildroot%_cachedir/%name/creds
|
||||
|
||||
ln -s %python3_sitelibdir/gpoa/gpoa \
|
||||
%buildroot%_sbindir/gpoa
|
||||
ln -s %python3_sitelibdir/gpoa/gpupdate \
|
||||
%buildroot%_bindir/gpupdate
|
||||
cp dist/gpupdate-setup \
|
||||
ln -s %python3_sitelibdir/gpoa/gpupdate-setup \
|
||||
%buildroot%_sbindir/gpupdate-setup
|
||||
|
||||
mkdir -p \
|
||||
%buildroot%_prefix/libexec/%name
|
||||
|
||||
ln -s %python3_sitelibdir/gpoa/pkcon_runner \
|
||||
%buildroot%_prefix/libexec/%name/pkcon_runner
|
||||
|
||||
mkdir -p %buildroot%_datadir/%name
|
||||
mv %buildroot%python3_sitelibdir/gpoa/templates \
|
||||
%buildroot%_datadir/%name/
|
||||
|
||||
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 doc/gpoa.1 %buildroot/%{_man1dir}/gpoa.1
|
||||
install -Dm0644 doc/gpupdate.1 %buildroot/%{_man1dir}/gpupdate.1
|
||||
install -Dm0644 dist/%name-remote-policy %buildroot%_sysconfdir/pam.d/%name-remote-policy
|
||||
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
|
||||
@ -66,22 +95,139 @@ install -Dm0644 doc/gpupdate.1 %buildroot/%{_man1dir}/gpupdate.1
|
||||
%post
|
||||
%post_service gpupdate
|
||||
|
||||
# Remove storage in case we've lost compatibility between versions.
|
||||
# The storage will be regenerated on GPOA start.
|
||||
%define active_policy %_sysconfdir/local-policy/active
|
||||
%triggerpostun -- %name < 0.9.6
|
||||
rm -f %_cachedir/%name/registry.sqlite
|
||||
if test -L %active_policy; then
|
||||
sed -i "s|^\s*local-policy\s*=.*|local-policy = $(readlink -f %active_policy)|" \
|
||||
%_sysconfdir/%name/%name.ini
|
||||
fi
|
||||
|
||||
%files
|
||||
%_sbindir/gpoa
|
||||
%_sbindir/gpupdate-setup
|
||||
%_bindir/gpupdate
|
||||
%_prefix/libexec/%name/pkcon_runner
|
||||
%attr(755,root,root) %python3_sitelibdir/gpoa/gpoa
|
||||
%attr(755,root,root) %python3_sitelibdir/gpoa/gpupdate
|
||||
%attr(755,root,root) %python3_sitelibdir/gpoa/gpupdate-setup
|
||||
%attr(755,root,root) %python3_sitelibdir/gpoa/pkcon_runner
|
||||
%python3_sitelibdir/gpoa
|
||||
%_datadir/%name
|
||||
%_unitdir/%name.service
|
||||
%_man1dir/gpoa.1.*
|
||||
%_man1dir/gpupdate.1.*
|
||||
/usr/lib/systemd/user/%name-user.service
|
||||
%_sysconfdir/pam.d/system-policy-%name
|
||||
%dir %_cachedir/%name
|
||||
%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
|
||||
%config(noreplace) %_sysconfdir/pam.d/%name-remote-policy
|
||||
%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
|
||||
%exclude %python3_sitelibdir/gpoa/Makefile
|
||||
%exclude %python3_sitelibdir/gpoa/test
|
||||
|
||||
%changelog
|
||||
* Fri Feb 18 2022 Evgeny Sinelnikov <sin@altlinux.org> 0.9.9-alt1
|
||||
- Add gpupdate-remote-policy PAM substack (for pam_mount support)
|
||||
- Added lookup for possible dc if first found is unreadable
|
||||
- Correct folder applier (still experimental)
|
||||
- Update logging and translations
|
||||
- Fix error when control facilites not exists
|
||||
- Add check for the presence of Gsettings schema and keys exists
|
||||
- Add support of package applier via pkcon (still experimental)
|
||||
|
||||
* Mon Oct 25 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.8-alt1
|
||||
- Added exception for org.gnome.Vino authentication-methods
|
||||
- Fixed bug for alternative-port in org.gnome.Vino
|
||||
|
||||
* Wed Sep 29 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.7-alt1
|
||||
- Fix regression with kestroy for user credential cache
|
||||
- Update system-policy-gpupdate PAM-rules to ignore applying group policies
|
||||
for local users and system users with uid less than 500
|
||||
- Add control facilities to rule system-policy-gpupdate rules:
|
||||
+ gpupdate-group-users
|
||||
+ gpupdate-localusers
|
||||
+ gpupdate-system-uids
|
||||
|
||||
* Mon Sep 20 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.6-alt1
|
||||
- Add support changed GPO List Processing for '**DelVals.' value name
|
||||
|
||||
* Tue Sep 14 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.5-alt1
|
||||
- Refix local policy path detection
|
||||
- gpupdate-setup: revert settings to default when disabled
|
||||
|
||||
* Tue Sep 14 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.4-alt1
|
||||
- Add improvement with new local-policy system-policy control
|
||||
- Fix gpupdate-setup and user service installation regressions
|
||||
- Set empty local policy and local backend by default
|
||||
- Fix local policy path detection
|
||||
|
||||
* Mon Sep 06 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.3-alt1
|
||||
- Use NetBIOS name for Kerberos authentification
|
||||
- Add support actions (create, update, delete, replace) for Shortcuts
|
||||
- Add support GSettings with locks feature
|
||||
- Add support file cache for special GSettings policy:
|
||||
Software\BaseALT\Policies\GSettings\org.mate.background.picture-filename
|
||||
(requires python smbc module with use_kerberos option support)
|
||||
|
||||
* Wed Jul 28 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.2-alt1
|
||||
- Fix Shortcuts applier double running in user context
|
||||
- Add LogonUser variable to expand_windows_var() function
|
||||
- Add support of path variable expansion to Shortcuts applier
|
||||
|
||||
* Sun Jul 18 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.1-alt1
|
||||
- Fix GSettings applier user part support
|
||||
- Add support additional firefox appliers
|
||||
- Add new windows policies mapping capability feature ruled by:
|
||||
Software\BaseALT\Policies\GPUpdate\WindowsPoliciesMapping
|
||||
- Improve drop privileges mechanism with fork and dbus session
|
||||
|
||||
* Fri Jun 25 2021 Evgeny Sinelnikov <sin@altlinux.org> 0.9.0-alt1
|
||||
- Change policies.json location for Firefox
|
||||
- Set GSettings, Chromium and Firefox appliers
|
||||
not experimental and enabled by default
|
||||
|
||||
* Tue Dec 22 2020 Igor Chudov <nir@altlinux.org> 0.8.2-alt1
|
||||
- Increased D-Bus timeouts on calls
|
||||
- Minor logging fixes
|
||||
|
||||
* Wed Oct 07 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.1-alt3
|
||||
- Fixed compatibility upgrade trigger condition
|
||||
|
||||
* Wed Oct 07 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.1-alt2
|
||||
- Fixed compatibility upgrade trigger from 0.7 releases for update
|
||||
active local-policy in new gpupdate.ini configuartion file
|
||||
|
||||
* Fri Sep 11 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.1-alt1
|
||||
- Workaround for control names with special symbols
|
||||
- Improved logging on Kerberos errors
|
||||
|
||||
* Fri Sep 04 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.8.0-alt1
|
||||
- Improve gpo applier logging
|
||||
- Add new configuration file /etc/gpupdate/gpupdate.ini
|
||||
- Fix folders applier regression
|
||||
- kinit move to samba backend
|
||||
- Replace gpupdate-setup utility with new implementation
|
||||
|
||||
* Wed Jul 01 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.7.0-alt1
|
||||
- Add multiple appliers, part of which marks as experimental yet
|
||||
|
||||
* Wed May 20 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.6.0-alt2
|
||||
- Update system-policy PAM-rules (clean system-auth-common, add pam_env support)
|
||||
- Add dependency to pam-config later than 1.9.0 release
|
||||
|
||||
* Fri May 15 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.6.0-alt1
|
||||
- Add drives policy for shared folders with cifs applier using autofs
|
||||
- Update shortcuts policy with xdg-users-dir support for DESKTOP
|
||||
|
||||
* Wed Apr 22 2020 Evgeny Sinelnikov <sin@altlinux.org> 0.5.0-alt1
|
||||
- Update samba format: local.xml -> Machine/Registry.pol.xml
|
||||
- Add support of ad-domain-controller local policy profile
|
||||
|
2
wiki
2
wiki
Submodule wiki updated: 8cb284e0f7...79e4ba7f58
Reference in New Issue
Block a user