mirror of
https://github.com/altlinux/gpupdate.git
synced 2025-10-24 03:33:17 +03:00
Compare commits
715 Commits
postinst_r
...
0.10.4-alt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d6a5d750c | ||
|
|
84e1340362 | ||
| 5ee05df574 | |||
|
|
2a993f0400 | ||
|
|
b878b7e1b3 | ||
|
|
c57d1bac9e | ||
| b9b5239448 | |||
| aae2776790 | |||
|
|
a20aa841d6 | ||
|
|
8c7819d96f | ||
|
|
3d9473f979 | ||
|
|
01f48be853 | ||
|
|
1638098fd4 | ||
|
|
047e5459af | ||
|
|
5baa4245e3 | ||
| ec6b9f7887 | |||
| 22d0d23b89 | |||
| fd3a32e8e1 | |||
|
|
9e849e8fe3 | ||
|
|
d65f3ed942 | ||
|
|
31298be840 | ||
|
|
5c889fd57e | ||
|
|
4e2874c972 | ||
|
|
63e50ac2df | ||
|
|
ad2a87e20d | ||
|
|
e9c3a4262a | ||
|
|
b5706ec6e1 | ||
|
|
61e7350429 | ||
|
|
c9a274fc79 | ||
|
|
127c9f7183 | ||
|
|
a27f8ba5dd | ||
|
|
fafe2c34b4 | ||
|
|
9c91ddc7ba | ||
|
|
1f02ed650b | ||
|
|
fc47df4649 | ||
|
|
42b8bdb82a | ||
|
|
2a174edeef | ||
|
|
9b8529b39b | ||
|
|
062ff742c3 | ||
|
|
1764560c49 | ||
|
|
b439e04a2f | ||
|
|
e413f95633 | ||
|
|
675f37ab85 | ||
|
|
9932c682ef | ||
|
|
018b30cdc4 | ||
|
|
249eb69ade | ||
|
|
1ab8c7aee0 | ||
|
|
400a5fab7d | ||
|
|
e7851e88b3 | ||
|
|
0761637666 | ||
|
|
dda4d987cb | ||
|
|
609ec0e8b8 | ||
|
|
c0b28a0655 | ||
|
|
78aad11e06 | ||
|
|
59bebbc45e | ||
|
|
e92656add0 | ||
|
|
5d24579d2f | ||
|
|
ce284b61be | ||
|
|
7a8118ac63 | ||
|
|
18d8e73acd | ||
|
|
58235cb1a1 | ||
|
|
e0d88cc076 | ||
|
|
c8b0927090 | ||
|
|
a4a79d8c99 | ||
|
|
408609fa58 | ||
|
|
6efebfad89 | ||
| 12865b0b43 | |||
| 9117dddcee | |||
| 1e267f5cb6 | |||
| 62ed015ea9 | |||
| 3e6b7cd040 | |||
| 209eb84d6d | |||
| 7f3b47a23c | |||
| 08ba87c8d8 | |||
| f2a45a2a6d | |||
| 9c544adc94 | |||
| a225c9aa7f | |||
| 51c8711da6 | |||
| 54eb4188a7 | |||
|
|
89d5e36d6c | ||
|
|
6cd5ab4ee2 | ||
|
|
0c913c68e3 | ||
|
|
12d746a1dc | ||
|
|
0a25f3a1d6 | ||
|
|
1eaab893c8 | ||
|
|
05ea872831 | ||
|
|
d0506dba29 | ||
|
|
dd28587b20 | ||
|
|
1a288c84f5 | ||
|
|
cadc3eda52 | ||
|
|
8d3e6691d4 | ||
|
|
cb54fa5d78 | ||
|
|
53ffc072f0 | ||
|
|
7a59bcb65b | ||
|
|
b81a727cd4 | ||
|
|
11b33dd148 | ||
|
|
1ccc18a31f | ||
|
|
9a3afeebdf | ||
|
|
0720471cca | ||
|
|
dd43ddaad6 | ||
|
|
6fc059aaac | ||
|
|
8cfb6f0bb3 | ||
|
|
ddcdc322f8 | ||
|
|
4ee52f06d6 | ||
|
|
603efc2deb | ||
|
|
9fc5007590 | ||
|
|
a6210f8b29 | ||
|
|
175f244a5f | ||
|
|
0d4ce533bc | ||
|
|
8e22235df2 | ||
|
|
0519d2703c | ||
|
|
1ca9b006e1 | ||
|
|
8cc5a8904b | ||
|
|
70cdef2e71 | ||
|
|
3baffeb12d | ||
|
|
a0d9dc585f | ||
|
|
388125415b | ||
|
|
14c7e5db21 | ||
|
|
582a85df88 | ||
|
|
18ddc54626 | ||
|
|
6bad9a331d | ||
|
|
16b5747620 | ||
|
|
47015ec312 | ||
|
|
666c88bdf1 | ||
|
|
bd5262353b | ||
|
|
e1d5712b83 | ||
|
|
bcb9108424 | ||
|
|
82bb88ca34 | ||
|
|
518685f361 | ||
|
|
39e3d15fa8 | ||
|
|
7a755bbb3e | ||
|
|
41260df1a1 | ||
|
|
0d1b60158a | ||
|
|
b244df8f2d | ||
|
|
e48ca4fc8e | ||
|
|
82d52d1c9f | ||
|
|
e6a51d02fb | ||
|
|
28e2d9c94b | ||
|
|
60137feed0 | ||
|
|
a86c49e471 | ||
|
|
8c5d0bbb06 | ||
|
|
c26fbf8042 | ||
|
|
83e70d5e7a | ||
|
|
c383b8df9b | ||
|
|
fc810c3362 | ||
|
|
7e225c837a | ||
|
|
b053544512 | ||
|
|
9b4527d334 | ||
|
|
3794ffa5be | ||
|
|
fe68f0cca8 | ||
|
|
d83cf4d29d | ||
|
|
47dc1df796 | ||
|
|
5d2fb3f719 | ||
|
|
3fded83c75 | ||
|
|
aeab315c3d | ||
|
|
446fa532db | ||
|
|
ac2190809a | ||
|
|
66bae5a1af | ||
|
|
4f41c64c98 | ||
|
|
729f916646 | ||
|
|
1b150e21c7 | ||
| 459993d133 | |||
| 7ee065309b | |||
| 22c4f97a15 | |||
|
|
e62b366cf2 | ||
|
|
fbdd8cc79a | ||
|
|
8fddb3494a | ||
|
|
4b3e621650 | ||
|
|
4a2842b872 | ||
|
|
682797fb90 | ||
|
|
12bd7a5b51 | ||
|
|
0674340f74 | ||
|
|
5486bcfcef | ||
|
|
d935557c4c | ||
|
|
c6b6cdfff3 | ||
| 2d7144c1b4 | |||
|
|
4cca8b241a | ||
|
|
a50f8c0d04 | ||
|
|
8c4ce9f8a6 | ||
|
|
bb1183c471 | ||
| db74303e73 | |||
|
|
ced9d35ec4 | ||
|
|
d84b754292 | ||
|
|
7507c558ba | ||
| 9fb411c2e2 | |||
|
|
b8dc00443f | ||
| 179b16baa4 | |||
| 209e4e3128 | |||
| 2fb59a1b7c | |||
| d82cfcfe89 | |||
| 220313a1fb | |||
|
|
38378440ff | ||
| debe48c06b | |||
| b84715cfe4 | |||
| abad246ab2 | |||
|
|
5bc8309abd | ||
|
|
a18e1a6cce | ||
| 8420f50f9c | |||
| 07662349ca | |||
|
|
a1281d3ac0 | ||
|
|
5c0fc9bed0 | ||
|
|
78815c5ecd | ||
|
|
7a0571278f | ||
|
|
7e666043be | ||
|
|
e733c346b3 | ||
|
|
7e26d8397c | ||
|
|
b0d3ab2384 | ||
| d744cf8f6e | |||
| 443b410dfa | |||
|
|
721c66b20d | ||
| 9fbe8f76be | |||
| 3c95c0c84b | |||
|
|
ed42f3cf6a | ||
| 5dabd2c259 | |||
| 1f32d4efae | |||
|
|
5c809a2d5a | ||
|
|
bec19cf69e | ||
|
|
583b47ae7c | ||
|
|
264cedd342 | ||
|
|
de6db7ad2b | ||
| 17c8aef19f | |||
| e402d399e9 | |||
| 5258880419 | |||
| 3fd6d9558e | |||
| d26290a720 | |||
| f1800a834f | |||
| 93806b342d | |||
| 17ea444bcb | |||
| fc0495abd0 | |||
| c9da82376a | |||
| ae9ced2794 | |||
| 6c231c8b4d | |||
| 6461aa6836 | |||
| 5eeba1e73a | |||
| ca4399b9b5 | |||
| 377aa07b9f | |||
| 38d1f0e571 | |||
|
|
04651494be | ||
|
|
4c7e69f7f6 | ||
|
|
51f4b3aa18 | ||
|
|
beb555bdf2 | ||
|
|
bb55c38e21 | ||
|
|
5df3c6f468 | ||
|
|
7edaa4afe7 | ||
|
|
486e035649 | ||
|
|
51bd701b2d | ||
|
|
de0635952f | ||
|
|
21b4ced721 | ||
|
|
2567bb9c45 | ||
|
|
a4db4d9cd0 | ||
|
|
8cdc84aef6 | ||
|
|
8b82278934 | ||
|
|
4b4adbf3e1 | ||
|
|
0e6c3bb6aa | ||
|
|
fa315bb599 | ||
|
|
d54cd790b1 | ||
|
|
c729b8a6d6 | ||
|
|
142d6eda50 | ||
|
|
ae8dd798ab | ||
|
|
8121eb8d6f | ||
|
|
be15051ba5 | ||
|
|
7f7a154e1b | ||
|
|
72c34a7475 | ||
|
|
abc3a3f609 | ||
|
|
ce2d1c6e05 | ||
|
|
58cff92891 | ||
|
|
6bcd916203 | ||
|
|
c924adc4b0 | ||
|
|
9e1760ae9d | ||
|
|
1a90996259 | ||
|
|
11768248e4 | ||
|
|
34d7124a46 | ||
|
|
c5c80b9091 | ||
|
|
1b3d046d05 | ||
|
|
5c2e4fe356 | ||
|
|
ff5645ef73 | ||
|
|
3fb3f2e857 | ||
|
|
f75c79cbeb | ||
|
|
43c8031da5 | ||
|
|
4f1c2f288e | ||
|
|
26908178d3 | ||
|
|
fe63894ad8 | ||
|
|
1bf898f1d0 | ||
|
|
2c71b5e53a | ||
|
|
601e8b1072 | ||
|
|
2c15d1cea0 | ||
|
|
52fc6ea4de | ||
|
|
3621e80055 | ||
|
|
d9191e47fa | ||
|
|
87d873862a | ||
|
|
9dc833a970 | ||
|
|
45bf77a64a | ||
|
|
5be7cc14b0 | ||
|
|
1f0e417ff1 | ||
|
|
1d31c72946 | ||
|
|
eb7538249f | ||
|
|
0dacf2f657 | ||
|
|
13f1529306 | ||
|
|
3b2d0c0af2 | ||
|
|
aea8f6ed0a | ||
|
|
322f28baa7 | ||
|
|
3860bf6b74 | ||
|
|
abcc660118 | ||
|
|
b7e61e4ab8 | ||
|
|
ca50d7f73b | ||
|
|
d9f3bd3b8c | ||
|
|
b4e50c2ef8 | ||
|
|
e46d717af8 | ||
|
|
83c0395ee4 | ||
|
|
eef4823e56 | ||
|
|
4100edcacf | ||
|
|
89e72eeaff | ||
|
|
ce54bae087 | ||
|
|
bbbde0c46a | ||
|
|
a43f47abd4 | ||
|
|
60ab746ce3 | ||
|
|
418d182726 | ||
|
|
ccb3dd53a8 | ||
|
|
bb0beb4a92 | ||
|
|
dda3ca452b | ||
|
|
0d54a2a0c8 | ||
|
|
c1a4e67ba3 | ||
|
|
b10dde3b21 | ||
|
|
c7b632fbb8 | ||
|
|
a00366650a | ||
|
|
a10beac915 | ||
|
|
d409d68052 | ||
|
|
5fdefaecc0 | ||
|
|
0e3d3598f1 | ||
|
|
556a8f833c | ||
|
|
a17dd4a9b4 | ||
|
|
681c4828a6 | ||
|
|
e670c03026 | ||
|
|
5bd64352f1 | ||
|
|
56b7186c15 | ||
|
|
249d3a6caa | ||
|
|
7b6cb64d58 | ||
|
|
da71aaf0dd | ||
|
|
d35dd5433d | ||
|
|
cb6bc1f280 | ||
|
|
3d79315470 | ||
|
|
077d67c417 | ||
|
|
77b6ffb81a | ||
|
|
e4a41e9d07 | ||
|
|
0460f64b47 | ||
|
|
477a99c703 | ||
|
|
385e9ae02f | ||
|
|
18a7426863 | ||
|
|
3f2176659a | ||
|
|
72e756c778 | ||
|
|
bb340112d5 | ||
|
|
fe4a5fa78c | ||
|
|
88efbfe3e3 | ||
|
|
edbdaccb71 | ||
|
|
b9c2b91add | ||
|
|
f289584044 | ||
|
|
88773f4e99 | ||
|
|
cd71ac4c81 | ||
|
|
e08546ad2f | ||
|
|
4c59c4ba7c | ||
|
|
6e1898ca27 | ||
|
|
3cab21e9c3 | ||
|
|
6b0cfbe2b5 | ||
|
|
bc1676dc71 | ||
|
|
5a60253dac | ||
|
|
e14043174a | ||
|
|
bfc05fee36 | ||
|
|
762fc4e525 | ||
|
|
66008b8a37 | ||
|
|
ffc3bc46c1 | ||
|
|
11abedd7b6 | ||
|
|
a01609afc3 | ||
| f1a415bdae | |||
|
|
5d1cf84304 | ||
|
|
3c3147c2fc | ||
|
|
e62739a43b | ||
|
|
727d7e073f | ||
|
|
a13373cf92 | ||
|
|
1c0678957c | ||
|
|
bdf9300be4 | ||
|
|
19acaad7e1 | ||
|
|
6b1aa004c4 | ||
|
|
d3740a106c | ||
|
|
9be2604be9 | ||
|
|
a35e578cf4 | ||
|
|
52eaea95c6 | ||
|
|
8f65f79c6c | ||
|
|
e50c5d7883 | ||
|
|
549315fe48 | ||
|
|
91824acdab | ||
|
|
cd25431bb8 | ||
|
|
10b9fa0ff1 | ||
|
|
44585adddd | ||
|
|
d3213b4d0b | ||
|
|
80e9dba4c4 | ||
|
|
1ed5c0f043 | ||
|
|
f801c09737 | ||
|
|
fd17b19f33 | ||
|
|
f1e22e0cc5 | ||
|
|
efc9dac26e | ||
|
|
e085c10bb3 | ||
|
|
5b08fcd917 | ||
|
|
408bccb76d | ||
|
|
3f32d3bbda | ||
|
|
fa707104b7 | ||
|
|
69ac2abf8b | ||
|
|
3a8af98231 | ||
|
|
41242561e1 | ||
|
|
97e5418666 | ||
|
|
5015da40b7 | ||
|
|
cdfc39540f | ||
|
|
95af821475 | ||
|
|
b63fe63784 | ||
|
|
889bf5124a | ||
|
|
2da7758621 | ||
|
|
cb720084fa | ||
|
|
baba56465c | ||
|
|
020e5f3128 | ||
|
|
f07f752211 | ||
|
|
31bcb2cd2a | ||
|
|
cc80d8c74a | ||
|
|
931aaf9300 | ||
|
|
7ade31de8a | ||
|
|
86d02146e2 | ||
|
|
cf979596b3 | ||
|
|
9a74efefde | ||
|
|
73404ceced | ||
|
|
23be105462 | ||
|
|
03977710a4 | ||
|
|
d76c0a9a00 | ||
|
|
a01d5253dc | ||
|
|
403432ecd2 | ||
|
|
47a3c6b39c | ||
|
|
51c218eb7a | ||
|
|
1513eab336 | ||
|
|
4701847d1b | ||
|
|
1486084594 | ||
|
|
70bc4faea3 | ||
|
|
6283d72ccc | ||
|
|
c795a8323e | ||
|
|
3187b9f0f1 | ||
|
|
0e159d34d0 | ||
|
|
4327f0b17b | ||
|
|
d1169eaeef | ||
|
|
c00e2d7f09 | ||
|
|
fdcbda576b | ||
|
|
43161e61bc | ||
|
|
31ba4ad214 | ||
|
|
877ce7b2aa | ||
|
|
856eecf708 | ||
|
|
b869573f31 | ||
|
|
f01bf08a95 | ||
|
|
5ae9031cda | ||
|
|
1aed44454c | ||
|
|
474378d17d | ||
|
|
1e8a6c61c6 | ||
|
|
925947765d | ||
|
|
8f8b7045b7 | ||
|
|
d6c438f277 | ||
|
|
326dc0600b | ||
|
|
5dd3ca17e8 | ||
|
|
fc650b125a | ||
|
|
dc9479fbbe | ||
|
|
c71356211f | ||
|
|
7ab98ffa6e | ||
|
|
3519be7bc6 | ||
|
|
7926137e84 | ||
|
|
d5ecd040df | ||
|
|
be0603e809 | ||
|
|
3ff6f053ea | ||
|
|
1b95a20cad | ||
|
|
818f5919fe | ||
|
|
8765ef862b | ||
|
|
66ebe87592 | ||
|
|
bced76ac4d | ||
|
|
4ddea369c5 | ||
|
|
6ac15e6be2 | ||
|
|
487483fb6f | ||
|
|
20e4a77ff7 | ||
|
|
22cff21d3a | ||
|
|
b69bc56e38 | ||
|
|
fbf192a984 | ||
|
|
7df737be29 | ||
|
|
d321264866 | ||
|
|
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
|
|||
|
2bcfd75a5b
|
|||
|
53ad06b787
|
|||
|
505f0152f7
|
|||
|
0ba273ee0e
|
|||
|
ef55a63c7e
|
|||
|
6ebbf5f59d
|
22
completions/gpoa
Normal file
22
completions/gpoa
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
_gpoa()
|
||||||
|
{
|
||||||
|
local cur prev words cword split
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
--dc)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
--loglevel)
|
||||||
|
COMPREPLY=($(compgen -W '0 1 2 3 4 5' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W '--dc --nodomain --noupdate --noplugins --list-backends --loglevel --help' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _gpoa gpoa
|
||||||
27
completions/gpupdate
Normal file
27
completions/gpupdate
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
_gpupdate()
|
||||||
|
{
|
||||||
|
local cur prev words cword split
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
-u|--user)
|
||||||
|
_filedir
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
-t|--target)
|
||||||
|
COMPREPLY=($(compgen -W 'ALL USER COMPUTER' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
-l|--loglevel)
|
||||||
|
COMPREPLY=($(compgen -W '0 1 2 3 4 5' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W '--user --target --loglevel --system --help' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _gpupdate gpupdate
|
||||||
|
|
||||||
18
completions/gpupdate-setup
Normal file
18
completions/gpupdate-setup
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
_gpupdate-setup()
|
||||||
|
{
|
||||||
|
local cur prev words cword split
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
case $prev in
|
||||||
|
set-backend)
|
||||||
|
COMPREPLY=($(compgen -W 'local samba' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=($(compgen -W 'list list-backends status enable disable update write set-backend default-policy active-policy active-backend' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _gpupdate-setup gpupdate-setup
|
||||||
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
|
||||||
11
dist/gpupdate-scripts-run-user.service
vendored
Normal file
11
dist/gpupdate-scripts-run-user.service
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Run Group Policy scripts for a user
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
ExecStart=/usr/libexec/gpupdate/scripts_runner --mode USER --action LOGON --user %u
|
||||||
|
ExecStop=/usr/libexec/gpupdate/scripts_runner --mode USER --action LOGOFF --user %u
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
15
dist/gpupdate-scripts-run.service
vendored
Normal file
15
dist/gpupdate-scripts-run.service
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Running Group Policy Scripts
|
||||||
|
After=gpupdate.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
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=oneshot
|
||||||
|
RemainAfterExit=true
|
||||||
|
ExecStart=/usr/libexec/gpupdate/scripts_runner --mode MACHINE --action STARTUP
|
||||||
|
ExecStop=/usr/libexec/gpupdate/scripts_runner --mode MACHINE --action SHUTDOWN
|
||||||
|
StandardOutput=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
233
dist/gpupdate-setup
vendored
233
dist/gpupdate-setup
vendored
@@ -1,233 +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 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 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_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_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_name():
|
|
||||||
etc_policy_dir = '/etc/local-policy'
|
|
||||||
actual_policy_name = 'unknown'
|
|
||||||
|
|
||||||
active_policy_path = os.path.join(etc_policy_dir, 'active')
|
|
||||||
|
|
||||||
if os.path.islink(active_policy_path):
|
|
||||||
active_policy_path = os.path.realpath(active_policy_path)
|
|
||||||
|
|
||||||
if os.path.isdir(active_policy_path):
|
|
||||||
actual_policy_name = os.path.basename(active_policy_path)
|
|
||||||
|
|
||||||
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_name())
|
|
||||||
|
|
||||||
if arguments.action == 'default-policy':
|
|
||||||
print(get_default_policy_name())
|
|
||||||
|
|
||||||
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" "$*"
|
||||||
11
dist/gpupdate-user.service
vendored
11
dist/gpupdate-user.service
vendored
@@ -4,13 +4,10 @@ Description=gpupdate in userspace
|
|||||||
|
|
||||||
# gpupdate on Windows runs once per hour
|
# gpupdate on Windows runs once per hour
|
||||||
[Service]
|
[Service]
|
||||||
Environment="PATH=/bin:/sbin:/usr/bin:/usr/sbin"
|
Environment=PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||||
Type=simple
|
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
|
||||||
RestartSec=3600
|
Type=oneshot
|
||||||
TimeoutSec=3000
|
ExecStart=/usr/bin/gpupdate --target USER
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/sbin/gpoa
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
|
|
||||||
|
|||||||
9
dist/gpupdate-user.timer
vendored
Normal file
9
dist/gpupdate-user.timer
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Run gpupdate-user every hour
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnStartupSec=60min
|
||||||
|
OnUnitActiveSec=60min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
4
dist/gpupdate.ini
vendored
Normal file
4
dist/gpupdate.ini
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[gpoa]
|
||||||
|
backend = local
|
||||||
|
local-policy = default
|
||||||
|
|
||||||
10
dist/gpupdate.service
vendored
10
dist/gpupdate.service
vendored
@@ -1,13 +1,11 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Group policy update for machine
|
Description=Group policy update for machine
|
||||||
After=sssd.service
|
After=syslog.target network-online.target sssd.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Environment="PATH=/bin:/sbin:/usr/bin:/usr/sbin"
|
Environment=PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||||
Type=simple
|
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
|
||||||
RestartSec=3600
|
Type=oneshot
|
||||||
TimeoutSec=3000
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/usr/bin/gpupdate
|
ExecStart=/usr/bin/gpupdate
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
|
|
||||||
|
|||||||
9
dist/gpupdate.timer
vendored
Normal file
9
dist/gpupdate.timer
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Run gpupdate every hour
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnStartupSec=60min
|
||||||
|
OnUnitActiveSec=60min
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
11
dist/system-policy-gpupdate
vendored
11
dist/system-policy-gpupdate
vendored
@@ -1,4 +1,13 @@
|
|||||||
#%PAM-1.0
|
#%PAM-1.0
|
||||||
session required pam_mkhomedir.so silent
|
session [success=2 perm_denied=ignore default=die] pam_localuser.so
|
||||||
|
session substack gpupdate-remote-policy
|
||||||
|
session [default=1] pam_permit.so
|
||||||
|
session [default=7] pam_permit.so
|
||||||
|
session [success=1 default=ignore] pam_succeed_if.so user ingroup users quiet
|
||||||
|
session [default=5] pam_permit.so
|
||||||
|
session [success=1 default=ignore] pam_succeed_if.so uid >= 500 quiet
|
||||||
|
session [default=3] pam_permit.so
|
||||||
|
session [success=1 default=ignore] pam_succeed_if.so service = systemd-user quiet
|
||||||
-session required pam_oddjob_gpupdate.so
|
-session required pam_oddjob_gpupdate.so
|
||||||
session optional pam_env.so user_readenv=1 conffile=/etc/gpupdate/environment user_envfile=.gpupdate_environment
|
session optional pam_env.so user_readenv=1 conffile=/etc/gpupdate/environment user_envfile=.gpupdate_environment
|
||||||
|
session required pam_permit.so
|
||||||
@@ -21,6 +21,10 @@ from util.windows import smbcreds
|
|||||||
from .samba_backend import samba_backend
|
from .samba_backend import samba_backend
|
||||||
from .nodomain_backend import nodomain_backend
|
from .nodomain_backend import nodomain_backend
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
|
from util.config import GPConfig
|
||||||
|
from util.util import get_uid_by_username, touch_file
|
||||||
|
from util.paths import get_dconf_config_path
|
||||||
|
from storage.dconf_registry import Dconf_registry, create_dconf_ini_file
|
||||||
|
|
||||||
def backend_factory(dc, username, is_machine, no_domain = False):
|
def backend_factory(dc, username, is_machine, no_domain = False):
|
||||||
'''
|
'''
|
||||||
@@ -30,20 +34,25 @@ def backend_factory(dc, username, is_machine, no_domain = False):
|
|||||||
policies enforced by domain administrators.
|
policies enforced by domain administrators.
|
||||||
'''
|
'''
|
||||||
back = None
|
back = None
|
||||||
domain = None
|
config = GPConfig()
|
||||||
if not no_domain:
|
|
||||||
|
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)
|
sc = smbcreds(dc)
|
||||||
domain = sc.get_domain()
|
domain = sc.get_domain()
|
||||||
|
ldata = dict({'domain': domain, "username": username, 'is_machine': is_machine})
|
||||||
if domain:
|
|
||||||
ldata = dict({'domain': domain})
|
|
||||||
log('D9', ldata)
|
log('D9', ldata)
|
||||||
try:
|
try:
|
||||||
back = samba_backend(sc, username, domain, is_machine)
|
back = samba_backend(sc, username, domain, is_machine)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict({'error': str(exc)})
|
logdata = dict({'error': str(exc)})
|
||||||
log('E7', logdata)
|
log('E7', logdata)
|
||||||
else:
|
|
||||||
|
if config.get_backend() == 'local' or no_domain:
|
||||||
log('D8')
|
log('D8')
|
||||||
try:
|
try:
|
||||||
back = nodomain_backend()
|
back = nodomain_backend()
|
||||||
@@ -53,3 +62,12 @@ def backend_factory(dc, username, is_machine, no_domain = False):
|
|||||||
|
|
||||||
return back
|
return back
|
||||||
|
|
||||||
|
def save_dconf(username, is_machine):
|
||||||
|
if is_machine:
|
||||||
|
uid = None
|
||||||
|
else:
|
||||||
|
uid = get_uid_by_username(username) if not is_machine else None
|
||||||
|
target_file = get_dconf_config_path(uid)
|
||||||
|
touch_file(target_file)
|
||||||
|
Dconf_registry.apply_template(uid)
|
||||||
|
create_dconf_ini_file(target_file,Dconf_registry.global_registry_dict)
|
||||||
|
|||||||
25
gpoa/backend/freeipa_backend.py
Normal file
25
gpoa/backend/freeipa_backend.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/>.
|
||||||
|
|
||||||
|
|
||||||
|
from .applier_backend import applier_backend
|
||||||
|
|
||||||
|
class freeipa_backend(applier_backend):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ from gpt.gpt import gpt, get_local_gpt
|
|||||||
from util.util import (
|
from util.util import (
|
||||||
get_machine_name
|
get_machine_name
|
||||||
)
|
)
|
||||||
from util.windows import get_sid
|
from util.sid import get_sid
|
||||||
import util.preg
|
import util.preg
|
||||||
from util.logging import slogm
|
from util.logging import slogm
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ class nodomain_backend(applier_backend):
|
|||||||
domain = None
|
domain = None
|
||||||
machine_name = get_machine_name()
|
machine_name = get_machine_name()
|
||||||
machine_sid = get_sid(domain, machine_name, True)
|
machine_sid = get_sid(domain, machine_name, True)
|
||||||
self.storage = registry_factory('registry')
|
self.storage = registry_factory()
|
||||||
self.storage.set_info('domain', domain)
|
self.storage.set_info('domain', domain)
|
||||||
self.storage.set_info('machine_name', machine_name)
|
self.storage.set_info('machine_name', machine_name)
|
||||||
self.storage.set_info('machine_sid', machine_sid)
|
self.storage.set_info('machine_sid', machine_sid)
|
||||||
@@ -52,5 +52,6 @@ class nodomain_backend(applier_backend):
|
|||||||
self.storage.wipe_hklm()
|
self.storage.wipe_hklm()
|
||||||
self.storage.wipe_user(self.storage.get_info('machine_sid'))
|
self.storage.wipe_user(self.storage.get_info('machine_sid'))
|
||||||
local_policy = get_local_gpt(self.sid)
|
local_policy = get_local_gpt(self.sid)
|
||||||
local_policy.merge()
|
local_policy.merge_machine()
|
||||||
|
local_policy.merge_user()
|
||||||
|
|
||||||
|
|||||||
@@ -18,23 +18,36 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
# Facility to determine GPTs for user
|
# Facility to determine GPTs for user
|
||||||
from samba.gpclass import check_safe_path, check_refresh_gpo_list
|
try:
|
||||||
|
from samba.gpclass import check_safe_path
|
||||||
|
except ImportError:
|
||||||
|
from samba.gp.gpclass import check_safe_path
|
||||||
|
|
||||||
from .applier_backend import applier_backend
|
from .applier_backend import applier_backend
|
||||||
from storage import cache_factory, registry_factory
|
from storage import registry_factory
|
||||||
from gpt.gpt import gpt, get_local_gpt
|
from gpt.gpt import gpt, get_local_gpt
|
||||||
from util.util import (
|
from util.util import (
|
||||||
get_machine_name,
|
get_machine_name,
|
||||||
is_machine_name
|
is_machine_name
|
||||||
)
|
)
|
||||||
from util.windows import get_sid
|
from util.kerberos import (
|
||||||
|
machine_kinit
|
||||||
|
, machine_kdestroy
|
||||||
|
)
|
||||||
|
from util.sid import get_sid
|
||||||
import util.preg
|
import util.preg
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
|
|
||||||
class samba_backend(applier_backend):
|
class samba_backend(applier_backend):
|
||||||
|
__user_policy_mode_key = '/SOFTWARE/Policies/Microsoft/Windows/System/UserPolicyMode'
|
||||||
|
__user_policy_mode_key_win = '/Software/Policies/Microsoft/Windows/System/UserPolicyMode'
|
||||||
|
|
||||||
def __init__(self, sambacreds, username, domain, is_machine):
|
def __init__(self, sambacreds, username, domain, is_machine):
|
||||||
self.storage = registry_factory('registry')
|
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()
|
||||||
self.storage.set_info('domain', domain)
|
self.storage.set_info('domain', domain)
|
||||||
machine_name = get_machine_name()
|
machine_name = get_machine_name()
|
||||||
machine_sid = get_sid(domain, machine_name, is_machine)
|
machine_sid = get_sid(domain, machine_name, is_machine)
|
||||||
@@ -49,9 +62,6 @@ class samba_backend(applier_backend):
|
|||||||
else:
|
else:
|
||||||
self.sid = get_sid(self.storage.get_info('domain'), self.username)
|
self.sid = get_sid(self.storage.get_info('domain'), self.username)
|
||||||
|
|
||||||
self.cache = cache_factory('regpol_cache')
|
|
||||||
self.gpo_names = cache_factory('gpo_names')
|
|
||||||
|
|
||||||
# Samba objects - LoadParm() and CredentialsOptions()
|
# Samba objects - LoadParm() and CredentialsOptions()
|
||||||
self.sambacreds = sambacreds
|
self.sambacreds = sambacreds
|
||||||
|
|
||||||
@@ -59,17 +69,46 @@ class samba_backend(applier_backend):
|
|||||||
logdata = dict({'cachedir': self.cache_dir})
|
logdata = dict({'cachedir': self.cache_dir})
|
||||||
log('D7', logdata)
|
log('D7', logdata)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.__kinit_successful:
|
||||||
|
machine_kdestroy()
|
||||||
|
|
||||||
|
def get_policy_mode(self):
|
||||||
|
'''
|
||||||
|
Get UserPolicyMode parameter value in order to determine if it
|
||||||
|
is possible to work with user's part of GPT. This value is
|
||||||
|
checked only if working for user's SID.
|
||||||
|
'''
|
||||||
|
upm_key = self.storage.get_key_value(self.__user_policy_mode_key)
|
||||||
|
upm_win_key = self.storage.get_key_value(self.__user_policy_mode_key_win)
|
||||||
|
upm = upm_key if upm_key else upm_win_key
|
||||||
|
if upm:
|
||||||
|
upm = int(upm)
|
||||||
|
if upm < 0 or upm > 2:
|
||||||
|
upm = 0
|
||||||
|
else:
|
||||||
|
upm = 0
|
||||||
|
|
||||||
|
return upm
|
||||||
|
|
||||||
def retrieve_and_store(self):
|
def retrieve_and_store(self):
|
||||||
'''
|
'''
|
||||||
Retrieve settings and strore it in a database
|
Retrieve settings and strore it in a database
|
||||||
'''
|
'''
|
||||||
# Get policies for machine at first.
|
# Get policies for machine at first.
|
||||||
|
machine_gpts = list()
|
||||||
|
try:
|
||||||
machine_gpts = self._get_gpts(get_machine_name(), self.storage.get_info('machine_sid'))
|
machine_gpts = self._get_gpts(get_machine_name(), self.storage.get_info('machine_sid'))
|
||||||
|
except Exception as exc:
|
||||||
|
log('F2')
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
if self._is_machine_username:
|
||||||
self.storage.wipe_hklm()
|
self.storage.wipe_hklm()
|
||||||
self.storage.wipe_user(self.storage.get_info('machine_sid'))
|
self.storage.wipe_user(self.storage.get_info('machine_sid'))
|
||||||
for gptobj in machine_gpts:
|
for gptobj in machine_gpts:
|
||||||
try:
|
try:
|
||||||
gptobj.merge()
|
gptobj.merge_machine()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
@@ -77,17 +116,40 @@ class samba_backend(applier_backend):
|
|||||||
|
|
||||||
# Load user GPT values in case user's name specified
|
# Load user GPT values in case user's name specified
|
||||||
# This is a buggy implementation and should be tested more
|
# This is a buggy implementation and should be tested more
|
||||||
if not self._is_machine_username:
|
else:
|
||||||
|
user_gpts = list()
|
||||||
|
try:
|
||||||
user_gpts = self._get_gpts(self.username, self.sid)
|
user_gpts = self._get_gpts(self.username, self.sid)
|
||||||
|
except Exception as exc:
|
||||||
|
log('F3')
|
||||||
|
raise exc
|
||||||
self.storage.wipe_user(self.sid)
|
self.storage.wipe_user(self.sid)
|
||||||
|
|
||||||
|
# Merge user settings if UserPolicyMode set accordingly
|
||||||
|
# and user settings (for HKCU) are exist.
|
||||||
|
policy_mode = self.get_policy_mode()
|
||||||
|
logdata = dict({'mode': upm2str(policy_mode), 'sid': self.sid})
|
||||||
|
log('D152', logdata)
|
||||||
|
|
||||||
|
if policy_mode < 2:
|
||||||
for gptobj in user_gpts:
|
for gptobj in user_gpts:
|
||||||
try:
|
try:
|
||||||
gptobj.merge()
|
gptobj.merge_user()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E27', logdata)
|
log('E27', logdata)
|
||||||
|
|
||||||
|
if policy_mode > 0:
|
||||||
|
for gptobj in machine_gpts:
|
||||||
|
try:
|
||||||
|
gptobj.sid = self.sid
|
||||||
|
gptobj.merge_user()
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['msg'] = str(exc)
|
||||||
|
log('E63', logdata)
|
||||||
|
|
||||||
def _check_sysvol_present(self, gpo):
|
def _check_sysvol_present(self, gpo):
|
||||||
'''
|
'''
|
||||||
Check if there is SYSVOL path for GPO assigned
|
Check if there is SYSVOL path for GPO assigned
|
||||||
@@ -104,7 +166,7 @@ class samba_backend(applier_backend):
|
|||||||
def _get_gpts(self, username, sid):
|
def _get_gpts(self, username, sid):
|
||||||
gpts = list()
|
gpts = list()
|
||||||
|
|
||||||
log('D45')
|
log('D45', {'username': username, 'sid': sid})
|
||||||
# util.windows.smbcreds
|
# util.windows.smbcreds
|
||||||
gpos = self.sambacreds.update_gpos(username)
|
gpos = self.sambacreds.update_gpos(username)
|
||||||
log('D46')
|
log('D46')
|
||||||
@@ -114,7 +176,16 @@ class samba_backend(applier_backend):
|
|||||||
slogdata = dict({'sysvol_path': gpo.file_sys_path, 'gpo_name': gpo.display_name, 'gpo_path': path})
|
slogdata = dict({'sysvol_path': gpo.file_sys_path, 'gpo_name': gpo.display_name, 'gpo_path': path})
|
||||||
log('D30', slogdata)
|
log('D30', slogdata)
|
||||||
gpt_abspath = os.path.join(self.cache_dir, 'gpo_cache', path)
|
gpt_abspath = os.path.join(self.cache_dir, 'gpo_cache', path)
|
||||||
obj = gpt(gpt_abspath, sid)
|
gpo_version=None
|
||||||
|
try:
|
||||||
|
gpo_version=gpo.version
|
||||||
|
except:
|
||||||
|
log('D210')
|
||||||
|
|
||||||
|
if self._is_machine_username:
|
||||||
|
obj = gpt(gpt_abspath, sid, None, version=gpo_version)
|
||||||
|
else:
|
||||||
|
obj = gpt(gpt_abspath, sid, self.username, version=gpo_version)
|
||||||
obj.set_name(gpo.display_name)
|
obj.set_name(gpo.display_name)
|
||||||
gpts.append(obj)
|
gpts.append(obj)
|
||||||
else:
|
else:
|
||||||
@@ -123,3 +194,16 @@ class samba_backend(applier_backend):
|
|||||||
|
|
||||||
return gpts
|
return gpts
|
||||||
|
|
||||||
|
def upm2str(upm_num):
|
||||||
|
'''
|
||||||
|
Translate UserPolicyMode to string.
|
||||||
|
'''
|
||||||
|
result = 'Not configured'
|
||||||
|
|
||||||
|
if upm_num in [1, '1']:
|
||||||
|
result = 'Merge'
|
||||||
|
|
||||||
|
if upm_num in [2, '2']:
|
||||||
|
result = 'Replace'
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@@ -18,31 +18,41 @@
|
|||||||
|
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
|
|
||||||
import logging
|
from util.logging import log
|
||||||
from util.logging import slogm
|
|
||||||
|
|
||||||
def check_experimental_enabled(storage):
|
def check_experimental_enabled(storage):
|
||||||
experimental_enable_flag = 'Software\\BaseALT\\Policies\\GPUpdate\\GlobalExperimental'
|
experimental_enable_flag = '/Software/BaseALT/Policies/GPUpdate/GlobalExperimental'
|
||||||
flag = storage.get_hklm_entry(experimental_enable_flag)
|
flag = storage.get_key_value(experimental_enable_flag)
|
||||||
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
if flag and '1' == flag.data:
|
if flag and '1' == str(flag):
|
||||||
result = True
|
result = True
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def check_windows_mapping_enabled(storage):
|
||||||
|
windows_mapping_enable_flag = '/Software/BaseALT/Policies/GPUpdate/WindowsPoliciesMapping'
|
||||||
|
flag = storage.get_key_value(windows_mapping_enable_flag)
|
||||||
|
|
||||||
|
result = True
|
||||||
|
flag = str(flag)
|
||||||
|
if flag and '0' == flag:
|
||||||
|
result = False
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def check_module_enabled(storage, module_name):
|
def check_module_enabled(storage, module_name):
|
||||||
gpupdate_module_enable_branch = 'Software\\BaseALT\\Policies\\GPUpdate'
|
gpupdate_module_enable_branch = '/Software/BaseALT/Policies/GPUpdate'
|
||||||
gpupdate_module_flag = '{}\\{}'.format(gpupdate_module_enable_branch, module_name)
|
gpupdate_module_flag = '{}/{}'.format(gpupdate_module_enable_branch, module_name)
|
||||||
flag = storage.get_hklm_entry(gpupdate_module_flag)
|
flag = storage.get_key_value(gpupdate_module_flag)
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
|
flag = str(flag)
|
||||||
if flag:
|
if flag:
|
||||||
if '1' == flag.data:
|
if '1' == flag:
|
||||||
result = True
|
result = True
|
||||||
if '0' == flag.data:
|
else:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -19,13 +19,29 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import logging
|
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:
|
class control:
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
if type(value) != int and type(value) != str:
|
if type(value) != int and type(value) != str:
|
||||||
raise Exception('Unknown type of value for control')
|
raise Exception('Unknown type of value for control')
|
||||||
self.control_name = name
|
self.control_name = control_subst(name)
|
||||||
self.control_value = value
|
self.control_value = value
|
||||||
self.possible_values = self._query_control_values()
|
self.possible_values = self._query_control_values()
|
||||||
if self.possible_values == None:
|
if self.possible_values == None:
|
||||||
@@ -39,10 +55,12 @@ class control:
|
|||||||
values = list()
|
values = list()
|
||||||
|
|
||||||
popen_call = ['/usr/sbin/control', self.control_name, '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()
|
values = proc.stdout.readline().decode('utf-8').split()
|
||||||
|
valErr = proc.stderr.readline().decode('utf-8')
|
||||||
|
if valErr:
|
||||||
|
raise ValueError(valErr)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
def _map_control_status(self, int_status):
|
def _map_control_status(self, int_status):
|
||||||
@@ -52,7 +70,11 @@ class control:
|
|||||||
try:
|
try:
|
||||||
str_status = self.possible_values[int_status]
|
str_status = self.possible_values[int_status]
|
||||||
except IndexError as exc:
|
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
|
str_status = None
|
||||||
|
|
||||||
return str_status
|
return str_status
|
||||||
@@ -77,20 +99,30 @@ class control:
|
|||||||
if type(self.control_value) == int:
|
if type(self.control_value) == int:
|
||||||
status = self._map_control_status(self.control_value)
|
status = self._map_control_status(self.control_value)
|
||||||
if status == None:
|
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
|
return
|
||||||
elif type(self.control_value) == str:
|
elif type(self.control_value) == str:
|
||||||
if self.control_value not in self.possible_values:
|
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
|
return
|
||||||
status = self.control_value
|
status = self.control_value
|
||||||
|
logdata = dict()
|
||||||
logging.debug(slogm('Setting control {} to {}'.format(self.control_name, status)))
|
logdata['control'] = self.control_name
|
||||||
|
logdata['status'] = status
|
||||||
|
log('D68', logdata)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
popen_call = ['/usr/sbin/control', self.control_name, status]
|
popen_call = ['/usr/sbin/control', self.control_name, status]
|
||||||
with subprocess.Popen(popen_call, stdout=subprocess.PIPE) as proc:
|
with subprocess.Popen(popen_call, stdout=subprocess.PIPE) as proc:
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except:
|
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)
|
||||||
|
|||||||
120
gpoa/frontend/appliers/envvar.py
Normal file
120
gpoa/frontend/appliers/envvar.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#
|
||||||
|
# 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 == '\n':
|
||||||
|
continue
|
||||||
|
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)
|
||||||
268
gpoa/frontend/appliers/file_cp.py
Normal file
268
gpoa/frontend/appliers/file_cp.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 gpt.folders import (
|
||||||
|
FileAction
|
||||||
|
, action_letter2enum
|
||||||
|
)
|
||||||
|
from .folder import str2bool
|
||||||
|
from util.logging import log
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
from util.windows import expand_windows_var
|
||||||
|
from util.util import get_homedir
|
||||||
|
from util.exceptions import NotUNCPathError
|
||||||
|
from util.paths import UNCPath
|
||||||
|
import fnmatch
|
||||||
|
|
||||||
|
class Files_cp:
|
||||||
|
def __init__(self, file_obj, file_cache, exe_check, username=None):
|
||||||
|
self.file_cache = file_cache
|
||||||
|
self.exe_check = exe_check
|
||||||
|
targetPath = expand_windows_var(file_obj.targetPath, username).replace('\\', '/')
|
||||||
|
self.targetPath = check_target_path(targetPath, username)
|
||||||
|
if not self.targetPath:
|
||||||
|
return
|
||||||
|
self.fromPath = (expand_windows_var(file_obj.fromPath, username).replace('\\', '/')
|
||||||
|
if file_obj.fromPath else None)
|
||||||
|
self.isTargetPathDirectory = False
|
||||||
|
self.action = action_letter2enum(file_obj.action)
|
||||||
|
self.readOnly = str2bool(file_obj.readOnly)
|
||||||
|
self.archive = str2bool(file_obj.archive)
|
||||||
|
self.hidden = str2bool(file_obj.hidden)
|
||||||
|
self.suppress = str2bool(file_obj.suppress)
|
||||||
|
self.executable = str2bool(file_obj.executable)
|
||||||
|
self.username = username
|
||||||
|
self.fromPathFiles = list()
|
||||||
|
if self.fromPath:
|
||||||
|
if targetPath[-1] == '/' or self.is_pattern(Path(self.fromPath).name):
|
||||||
|
self.isTargetPathDirectory = True
|
||||||
|
self.get_list_files()
|
||||||
|
self.act()
|
||||||
|
|
||||||
|
def get_target_file(self, targetPath:Path, fromFile:str) -> Path:
|
||||||
|
try:
|
||||||
|
if fromFile:
|
||||||
|
fromFileName = Path(fromFile).name
|
||||||
|
if self.isTargetPathDirectory:
|
||||||
|
targetPath.mkdir(parents = True, exist_ok = True)
|
||||||
|
else:
|
||||||
|
targetPath.parent.mkdir(parents = True, exist_ok = True)
|
||||||
|
targetPath = targetPath.parent
|
||||||
|
fromFileName = self.targetPath.name
|
||||||
|
if self.hidden:
|
||||||
|
return targetPath.joinpath('.' + fromFileName)
|
||||||
|
else:
|
||||||
|
return targetPath.joinpath(fromFileName)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if not self.hidden:
|
||||||
|
return targetPath
|
||||||
|
else:
|
||||||
|
return targetPath.parent.joinpath('.' + targetPath.name)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['targetPath'] = targetPath
|
||||||
|
logdata['fromFile'] = fromFile
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('D163', logdata)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def copy_target_file(self, targetFile:Path, fromFile:str):
|
||||||
|
try:
|
||||||
|
uri_path = UNCPath(fromFile)
|
||||||
|
self.file_cache.store(fromFile, targetFile)
|
||||||
|
except NotUNCPathError as exc:
|
||||||
|
fromFilePath = Path(fromFile)
|
||||||
|
if fromFilePath.exists():
|
||||||
|
targetFile.write_bytes(fromFilePath.read_bytes())
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['targetFile'] = targetFile
|
||||||
|
logdata['fromFile'] = fromFile
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W15', logdata)
|
||||||
|
|
||||||
|
def set_exe_file(self, targetFile, fromFile):
|
||||||
|
if self.executable:
|
||||||
|
return True
|
||||||
|
if Path(fromFile).suffix in self.exe_check.get_list_markers():
|
||||||
|
targetPath = targetFile.parent
|
||||||
|
for i in self.exe_check.get_list_paths():
|
||||||
|
if targetPath == Path(i):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_mod_file(self, targetFile, fromFile):
|
||||||
|
if not targetFile.is_file():
|
||||||
|
return
|
||||||
|
if self.set_exe_file(targetFile, fromFile):
|
||||||
|
if self.readOnly:
|
||||||
|
shutil.os.chmod(targetFile, 0o555)
|
||||||
|
else:
|
||||||
|
shutil.os.chmod(targetFile, 0o755)
|
||||||
|
else:
|
||||||
|
if self.readOnly:
|
||||||
|
shutil.os.chmod(targetFile, 0o444)
|
||||||
|
else:
|
||||||
|
shutil.os.chmod(targetFile, 0o644)
|
||||||
|
|
||||||
|
def _create_action(self):
|
||||||
|
logdata = dict()
|
||||||
|
for fromFile in self.fromPathFiles:
|
||||||
|
targetFile = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
targetFile = self.get_target_file(self.targetPath, fromFile)
|
||||||
|
if targetFile and not targetFile.exists():
|
||||||
|
self.copy_target_file(targetFile, fromFile)
|
||||||
|
if self.username:
|
||||||
|
shutil.chown(targetFile, self.username)
|
||||||
|
self.set_mod_file(targetFile, fromFile)
|
||||||
|
logdata['File'] = targetFile
|
||||||
|
log('D191', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
logdata['fromPath'] = fromFile
|
||||||
|
logdata['targetPath'] = self.targetPath
|
||||||
|
logdata['targetFile'] = targetFile
|
||||||
|
log('D164', logdata)
|
||||||
|
|
||||||
|
def _delete_action(self):
|
||||||
|
list_target = [self.targetPath.name]
|
||||||
|
if self.is_pattern(self.targetPath.name) and self.targetPath.parent.exists() and self.targetPath.parent.is_dir():
|
||||||
|
list_target = fnmatch.filter([str(x.name) for x in self.targetPath.parent.iterdir() if x.is_file()], self.targetPath.name)
|
||||||
|
logdata = dict()
|
||||||
|
for targetFile in list_target:
|
||||||
|
targetFile = self.targetPath.parent.joinpath(targetFile)
|
||||||
|
try:
|
||||||
|
if targetFile.exists():
|
||||||
|
targetFile.unlink()
|
||||||
|
logdata['File'] = targetFile
|
||||||
|
log('D193', logdata)
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
logdata['targetPath'] = self.targetPath
|
||||||
|
logdata['targetFile'] = targetFile
|
||||||
|
log('D165', logdata)
|
||||||
|
|
||||||
|
def _update_action(self):
|
||||||
|
logdata = dict()
|
||||||
|
for fromFile in self.fromPathFiles:
|
||||||
|
targetFile = self.get_target_file(self.targetPath, fromFile)
|
||||||
|
try:
|
||||||
|
self.copy_target_file(targetFile, fromFile)
|
||||||
|
if self.username:
|
||||||
|
shutil.chown(self.targetPath, self.username)
|
||||||
|
self.set_mod_file(targetFile, fromFile)
|
||||||
|
logdata['File'] = targetFile
|
||||||
|
log('D192', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
logdata['fromPath'] = self.fromPath
|
||||||
|
logdata['targetPath'] = self.targetPath
|
||||||
|
logdata['targetFile'] = targetFile
|
||||||
|
log('D166', logdata)
|
||||||
|
|
||||||
|
def act(self):
|
||||||
|
if self.action == FileAction.CREATE:
|
||||||
|
self._create_action()
|
||||||
|
if self.action == FileAction.UPDATE:
|
||||||
|
self._update_action()
|
||||||
|
if self.action == FileAction.DELETE:
|
||||||
|
self._delete_action()
|
||||||
|
if self.action == FileAction.REPLACE:
|
||||||
|
self._delete_action()
|
||||||
|
self._create_action()
|
||||||
|
|
||||||
|
def is_pattern(self, name):
|
||||||
|
if name.find('*') != -1 or name.find('?') != -1:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_list_files(self):
|
||||||
|
logdata = dict()
|
||||||
|
logdata['targetPath'] = str(self.targetPath)
|
||||||
|
fromFilePath = Path(self.fromPath)
|
||||||
|
if not self.is_pattern(fromFilePath.name):
|
||||||
|
self.fromPathFiles.append(self.fromPath)
|
||||||
|
else:
|
||||||
|
fromPathDir = self.fromPath[:self.fromPath.rfind('/')]
|
||||||
|
|
||||||
|
try:
|
||||||
|
uri_path = UNCPath(fromPathDir)
|
||||||
|
ls_files = self.file_cache.get_ls_smbdir(fromPathDir)
|
||||||
|
if ls_files:
|
||||||
|
filtered_ls_files = fnmatch.filter(ls_files, fromFilePath.name)
|
||||||
|
if filtered_ls_files:
|
||||||
|
self.fromPathFiles = [fromPathDir + '/' + file_s for file_s in filtered_ls_files]
|
||||||
|
except NotUNCPathError as exc:
|
||||||
|
try:
|
||||||
|
exact_path = Path(fromPathDir)
|
||||||
|
if exact_path.is_dir():
|
||||||
|
self.fromPathFiles = [str(fromFile) for fromFile in exact_path.iterdir() if fromFile.is_file()]
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['fromPath'] = self.fromPath
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W3316', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['fromPath'] = self.fromPath
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W3317', logdata)
|
||||||
|
|
||||||
|
def check_target_path(path_to_check, username = None):
|
||||||
|
'''
|
||||||
|
Function for checking the correctness of the path
|
||||||
|
'''
|
||||||
|
if not path_to_check:
|
||||||
|
return None
|
||||||
|
|
||||||
|
checking = Path(path_to_check)
|
||||||
|
rootpath = Path('/')
|
||||||
|
if username:
|
||||||
|
rootpath = Path(get_homedir(username))
|
||||||
|
|
||||||
|
return rootpath.joinpath(checking)
|
||||||
|
|
||||||
|
class Execution_check():
|
||||||
|
|
||||||
|
__etension_marker_key_name = 'ExtensionMarker'
|
||||||
|
__marker_usage_path_key_name = 'MarkerUsagePath'
|
||||||
|
__hklm_branch = 'Software\\BaseALT\\Policies\\GroupPolicies\\Files'
|
||||||
|
|
||||||
|
def __init__(self, storage):
|
||||||
|
etension_marker_branch = '{}\\{}%'.format(self.__hklm_branch, self.__etension_marker_key_name)
|
||||||
|
marker_usage_path_branch = '{}\\{}%'.format(self.__hklm_branch, self.__marker_usage_path_key_name)
|
||||||
|
self.etension_marker = storage.filter_hklm_entries(etension_marker_branch)
|
||||||
|
self.marker_usage_path = storage.filter_hklm_entries(marker_usage_path_branch)
|
||||||
|
self.list_paths = list()
|
||||||
|
self.list_markers = list()
|
||||||
|
for marker in self.etension_marker:
|
||||||
|
self.list_markers.append(marker.data)
|
||||||
|
for usage_path in self.marker_usage_path:
|
||||||
|
self.list_paths.append(usage_path.data)
|
||||||
|
|
||||||
|
def get_list_paths(self):
|
||||||
|
return self.list_paths
|
||||||
|
|
||||||
|
def get_list_markers(self):
|
||||||
|
return self.list_markers
|
||||||
@@ -24,46 +24,69 @@ from gpt.folders import (
|
|||||||
FileAction
|
FileAction
|
||||||
, action_letter2enum
|
, action_letter2enum
|
||||||
)
|
)
|
||||||
|
from util.windows import expand_windows_var
|
||||||
|
from util.util import get_homedir
|
||||||
|
|
||||||
def remove_dir_tree(path, delete_files=False, delete_folder=False, delete_sub_folders=False):
|
def remove_dir_tree(path, delete_files=False, delete_folder=False, delete_sub_folders=False):
|
||||||
|
content = list()
|
||||||
for entry in path.iterdir():
|
for entry in path.iterdir():
|
||||||
if entry.is_file():
|
content.append(entry)
|
||||||
|
if entry.is_file() and delete_files:
|
||||||
entry.unlink()
|
entry.unlink()
|
||||||
if entry.is_dir():
|
content.remove(entry)
|
||||||
if delete_sub_folders:
|
if entry.is_dir() and delete_sub_folders:
|
||||||
remove_dir_tree(entry,
|
content.remove(entry)
|
||||||
delete_files,
|
content.extend(remove_dir_tree(entry, delete_files, delete_folder, delete_sub_folders))
|
||||||
delete_folder,
|
|
||||||
delete_sub_folders)
|
|
||||||
|
|
||||||
if delete_folder:
|
if delete_folder and not content:
|
||||||
path.rmdir()
|
path.rmdir()
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
def str2bool(boolstr):
|
def str2bool(boolstr):
|
||||||
if boolstr.lower in ['true', 'yes', '1']:
|
if isinstance(boolstr, bool):
|
||||||
|
return boolstr
|
||||||
|
elif boolstr and boolstr.lower() in ['true', 'yes', '1']:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Folder:
|
class Folder:
|
||||||
def __init__(self, folder_object):
|
def __init__(self, folder_object, username=None):
|
||||||
self.folder_path = Path(folder_object.path)
|
folder_path = expand_windows_var(folder_object.path, username).replace('\\', '/').replace('//', '/')
|
||||||
|
if username:
|
||||||
|
folder_path = folder_path.replace(get_homedir(username), '')
|
||||||
|
self.folder_path = Path(get_homedir(username)).joinpath(folder_path if folder_path [0] != '/' else folder_path [1:])
|
||||||
|
else:
|
||||||
|
self.folder_path = Path(folder_path)
|
||||||
self.action = action_letter2enum(folder_object.action)
|
self.action = action_letter2enum(folder_object.action)
|
||||||
self.delete_files = str2bool(folder_object.delete_files)
|
self.delete_files = str2bool(folder_object.delete_files)
|
||||||
self.delete_folder = str2bool(folder_object.delete_folder)
|
self.delete_folder = str2bool(folder_object.delete_folder)
|
||||||
self.delete_sub_folders = str2bool(folder_object.delete_sub_folders)
|
self.delete_sub_folders = str2bool(folder_object.delete_sub_folders)
|
||||||
|
self.hidden_folder = str2bool(folder_object.hidden_folder)
|
||||||
|
|
||||||
def _create_action(self):
|
def _create_action(self):
|
||||||
self.folder_path.mkdir(parents=True, exist_ok=True)
|
self.folder_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
def _delete_action(self):
|
def _delete_action(self):
|
||||||
|
if self.folder_path.exists():
|
||||||
|
if self.action == FileAction.REPLACE:
|
||||||
|
self.delete_folder = True
|
||||||
remove_dir_tree(self.folder_path,
|
remove_dir_tree(self.folder_path,
|
||||||
self.delete_files,
|
self.delete_files,
|
||||||
self.delete_folders,
|
self.delete_folder,
|
||||||
self.delete_sub_folders)
|
self.delete_sub_folders)
|
||||||
|
|
||||||
def action(self):
|
def act(self):
|
||||||
|
if self.hidden_folder == True and str(self.folder_path.name)[0] != '.':
|
||||||
|
path_components = list(self.folder_path.parts)
|
||||||
|
path_components[-1] = '.' + path_components[-1]
|
||||||
|
new_folder_path = Path(*path_components)
|
||||||
|
self.folder_path = new_folder_path
|
||||||
if self.action == FileAction.CREATE:
|
if self.action == FileAction.CREATE:
|
||||||
self._create_action()
|
self._create_action()
|
||||||
|
if self.action == FileAction.UPDATE:
|
||||||
|
self._create_action()
|
||||||
if self.action == FileAction.DELETE:
|
if self.action == FileAction.DELETE:
|
||||||
self._delete_action()
|
self._delete_action()
|
||||||
if self.action == FileAction.REPLACE:
|
if self.action == FileAction.REPLACE:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,67 +21,156 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
from gi.repository import Gio, GLib
|
from gi.repository import Gio, GLib
|
||||||
|
|
||||||
from util.logging import slogm
|
from util.logging import slogm, log
|
||||||
|
|
||||||
class system_gsetting:
|
class system_gsetting:
|
||||||
__global_schema = '/usr/share/glib-2.0/schemas'
|
def __init__(self, schema, path, value, lock, helper_function=None):
|
||||||
|
|
||||||
def __init__(self, schema, path, value, override_priority='0'):
|
|
||||||
self.schema = schema
|
self.schema = schema
|
||||||
self.path = path
|
self.path = path
|
||||||
self.value = value
|
self.value = value
|
||||||
self.override_priority = override_priority
|
self.lock = lock
|
||||||
self.filename = '{}_policy.gschema.override'.format(self.override_priority)
|
self.helper_function = helper_function
|
||||||
self.file_path = os.path.join(self.__global_schema, self.filename)
|
|
||||||
|
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):
|
def apply(self):
|
||||||
config = configparser.ConfigParser()
|
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)
|
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):
|
def glib_map(value, glib_type):
|
||||||
result_value = value
|
result_value = value
|
||||||
|
|
||||||
if glib_type == 'i':
|
if glib_type == 'i' or glib_type == 'b' or glib_type == 'q':
|
||||||
result_value = GLib.Variant(glib_type, int(value))
|
result_value = GLib.Variant(glib_type, int(value))
|
||||||
else:
|
else:
|
||||||
result_value = GLib.Variant(glib_type, value)
|
result_value = GLib.Variant(glib_type, value)
|
||||||
|
|
||||||
return result_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:
|
class user_gsetting:
|
||||||
def __init__(self, schema, path, value, helper_function=None):
|
def __init__(self, schema, path, value, helper_function=None):
|
||||||
logging.debug('Creating GSettings element {} (in {}) with value {}'.format(path, schema, value))
|
|
||||||
self.schema = schema
|
self.schema = schema
|
||||||
self.path = path
|
self.path = path
|
||||||
self.value = value
|
self.value = value
|
||||||
self.helper_function = helper_function
|
self.helper_function = helper_function
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
logging.debug('Setting GSettings key {} (in {}) to {}'.format(self.path, self.schema, self.value))
|
|
||||||
if self.helper_function:
|
|
||||||
self.helper_function(self.schema, self.path, self.value)
|
|
||||||
# Access the current schema
|
# Access the current schema
|
||||||
settings = Gio.Settings(self.schema)
|
settings = Gio.Settings(schema=self.schema)
|
||||||
# Get the key to modify
|
# Update result with helper function
|
||||||
key = settings.get_value(self.path)
|
value = self.value
|
||||||
# Query the data type for the key
|
if self.helper_function:
|
||||||
glib_value_type = key.get_type_string()
|
value = self.helper_function(self.schema, self.path, value)
|
||||||
# Build the new value with the determined type
|
# Get typed value by schema
|
||||||
val = glib_map(self.value, glib_value_type)
|
result = glib_value(self.schema, self.path, value, settings)
|
||||||
# Set the value
|
# Set the value
|
||||||
settings.set_value(self.path, val)
|
settings.set_value(self.path, result)
|
||||||
|
settings.sync()
|
||||||
#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())
|
|
||||||
|
|
||||||
|
|||||||
114
gpoa/frontend/appliers/ini_file.py
Normal file
114
gpoa/frontend/appliers/ini_file.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 gpt.folders import (
|
||||||
|
FileAction
|
||||||
|
, action_letter2enum
|
||||||
|
)
|
||||||
|
from util.logging import log
|
||||||
|
from pathlib import Path
|
||||||
|
from util.windows import expand_windows_var
|
||||||
|
from util.util import get_homedir
|
||||||
|
from util.gpoa_ini_parsing import GpoaConfigObj
|
||||||
|
|
||||||
|
|
||||||
|
class Ini_file:
|
||||||
|
def __init__(self, ini_obj, username=None):
|
||||||
|
path = expand_windows_var(ini_obj.path, username).replace('\\', '/')
|
||||||
|
self.path = check_path(path, username)
|
||||||
|
if not self.path:
|
||||||
|
logdata = {'path': ini_obj.path}
|
||||||
|
log('D175', logdata)
|
||||||
|
return None
|
||||||
|
self.section = ini_obj.section
|
||||||
|
self.action = action_letter2enum(ini_obj.action)
|
||||||
|
self.key = ini_obj.property
|
||||||
|
self.value = ini_obj.value
|
||||||
|
try:
|
||||||
|
self.config = GpoaConfigObj(str(self.path), unrepr=False)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = {'exc': exc}
|
||||||
|
log('D176', logdata)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.act()
|
||||||
|
|
||||||
|
def _create_action(self):
|
||||||
|
if self.path.is_dir():
|
||||||
|
return
|
||||||
|
if self.section not in self.config:
|
||||||
|
self.config[self.section] = dict()
|
||||||
|
|
||||||
|
self.config[self.section][self.key] = self.value
|
||||||
|
self.config.write()
|
||||||
|
|
||||||
|
|
||||||
|
def _delete_action(self):
|
||||||
|
if not self.path.exists() or self.path.is_dir():
|
||||||
|
return
|
||||||
|
if not self.section:
|
||||||
|
self.path.unlink()
|
||||||
|
return
|
||||||
|
if self.section in self.config:
|
||||||
|
if not self.key:
|
||||||
|
self.config.pop(self.section)
|
||||||
|
elif self.key in self.config[self.section]:
|
||||||
|
self.config[self.section].pop(self.key)
|
||||||
|
self.config.write()
|
||||||
|
|
||||||
|
|
||||||
|
def act(self):
|
||||||
|
try:
|
||||||
|
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._create_action()
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['action'] = self.action
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W23', logdata)
|
||||||
|
|
||||||
|
|
||||||
|
def check_path(path_to_check, username = None):
|
||||||
|
'''
|
||||||
|
Function for checking the right path for Inifile
|
||||||
|
'''
|
||||||
|
checking = Path(path_to_check)
|
||||||
|
if checking.exists():
|
||||||
|
if username and path_to_check == '/':
|
||||||
|
return Path(get_homedir(username))
|
||||||
|
return checking
|
||||||
|
#Check for path directory without '/nameIni' suffix
|
||||||
|
elif (len(path_to_check.split('/')) > 2
|
||||||
|
and Path(path_to_check.replace(path_to_check.split('/')[-1], '')).is_dir()):
|
||||||
|
return checking
|
||||||
|
elif username:
|
||||||
|
target_path = Path(get_homedir(username))
|
||||||
|
res = target_path.joinpath(path_to_check
|
||||||
|
if path_to_check[0] != '/'
|
||||||
|
else path_to_check[1:])
|
||||||
|
return check_path(str(res))
|
||||||
|
else:
|
||||||
|
return False
|
||||||
90
gpoa/frontend/appliers/netshare.py
Normal file
90
gpoa/frontend/appliers/netshare.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 subprocess
|
||||||
|
|
||||||
|
from gpt.folders import (
|
||||||
|
FileAction
|
||||||
|
, action_letter2enum
|
||||||
|
)
|
||||||
|
from util.logging import log
|
||||||
|
from util.windows import expand_windows_var
|
||||||
|
|
||||||
|
|
||||||
|
class Networkshare:
|
||||||
|
|
||||||
|
def __init__(self, networkshare_obj, username = None):
|
||||||
|
self.net_full_cmd = ['/usr/bin/net', 'usershare']
|
||||||
|
self.net_cmd_check = ['/usr/bin/net', 'usershare', 'list']
|
||||||
|
self.cmd = list()
|
||||||
|
self.name = networkshare_obj.name
|
||||||
|
self.path = expand_windows_var(networkshare_obj.path, username).replace('\\', '/') if networkshare_obj.path else None
|
||||||
|
|
||||||
|
self.action = action_letter2enum(networkshare_obj.action)
|
||||||
|
self.allRegular = networkshare_obj.allRegular
|
||||||
|
self.comment = networkshare_obj.comment
|
||||||
|
self.limitUsers = networkshare_obj.limitUsers
|
||||||
|
self.abe = networkshare_obj.abe
|
||||||
|
self._guest = 'guest_ok=y'
|
||||||
|
self.acl = 'Everyone:'
|
||||||
|
self.act()
|
||||||
|
|
||||||
|
def check_list_net(self):
|
||||||
|
try:
|
||||||
|
res = subprocess.check_output(self.net_cmd_check, encoding='utf-8')
|
||||||
|
return res
|
||||||
|
except Exception as exc:
|
||||||
|
return exc
|
||||||
|
|
||||||
|
def _run_net_full_cmd(self):
|
||||||
|
logdata = dict()
|
||||||
|
try:
|
||||||
|
res = subprocess.check_output(self.net_full_cmd, stderr=subprocess.DEVNULL, encoding='utf-8')
|
||||||
|
if res:
|
||||||
|
logdata['cmd'] = self.net_full_cmd
|
||||||
|
logdata['answer'] = res
|
||||||
|
log('D190', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['cmd'] = self.net_full_cmd
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('D182', logdata)
|
||||||
|
|
||||||
|
|
||||||
|
def _create_action(self):
|
||||||
|
self.net_full_cmd.append('add')
|
||||||
|
self.net_full_cmd.append(self.name)
|
||||||
|
self.net_full_cmd.append(self.path)
|
||||||
|
self.net_full_cmd.append(self.comment)
|
||||||
|
self.net_full_cmd.append(self.acl + 'F')
|
||||||
|
self.net_full_cmd.append(self._guest)
|
||||||
|
self._run_net_full_cmd()
|
||||||
|
|
||||||
|
def _delete_action(self):
|
||||||
|
self.net_full_cmd.append('delete')
|
||||||
|
self.net_full_cmd.append(self.name)
|
||||||
|
self._run_net_full_cmd()
|
||||||
|
|
||||||
|
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._create_action()
|
||||||
@@ -18,9 +18,8 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import jinja2
|
import jinja2
|
||||||
import logging
|
|
||||||
|
|
||||||
from util.logging import slogm
|
from util.logging import log
|
||||||
|
|
||||||
class polkit:
|
class polkit:
|
||||||
__template_path = '/usr/share/gpupdate/templates'
|
__template_path = '/usr/share/gpupdate/templates'
|
||||||
@@ -38,7 +37,19 @@ class polkit:
|
|||||||
else:
|
else:
|
||||||
self.outfile = os.path.join(self.__policy_dir, '{}.rules'.format(self.template_name))
|
self.outfile = os.path.join(self.__policy_dir, '{}.rules'.format(self.template_name))
|
||||||
|
|
||||||
|
def _is_empty(self):
|
||||||
|
for key, item in self.args.items():
|
||||||
|
if key == 'User':
|
||||||
|
continue
|
||||||
|
elif item:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
|
if self._is_empty():
|
||||||
|
if os.path.isfile(self.outfile):
|
||||||
|
os.remove(self.outfile)
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
template = self.__template_environment.get_template(self.infilename)
|
template = self.__template_environment.get_template(self.infilename)
|
||||||
text = template.render(**self.args)
|
text = template.render(**self.args)
|
||||||
@@ -46,7 +57,13 @@ class polkit:
|
|||||||
with open(self.outfile, 'w') as f:
|
with open(self.outfile, 'w') as f:
|
||||||
f.write(text)
|
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:
|
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 dbus
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from util.logging import slogm
|
from util.logging import slogm, log
|
||||||
|
|
||||||
class systemd_unit:
|
class systemd_unit:
|
||||||
def __init__(self, unit_name, state):
|
def __init__(self, unit_name, state):
|
||||||
@@ -39,7 +39,9 @@ class systemd_unit:
|
|||||||
self.manager.UnmaskUnitFiles([self.unit_name], dbus.Boolean(False))
|
self.manager.UnmaskUnitFiles([self.unit_name], dbus.Boolean(False))
|
||||||
self.manager.EnableUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
self.manager.EnableUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
||||||
self.manager.StartUnit(self.unit_name, 'replace')
|
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
|
# In case the service has 'RestartSec' property set it
|
||||||
# switches to 'activating (auto-restart)' state instead of
|
# switches to 'activating (auto-restart)' state instead of
|
||||||
@@ -47,17 +49,27 @@ class systemd_unit:
|
|||||||
service_state = self._get_state()
|
service_state = self._get_state()
|
||||||
|
|
||||||
if not service_state in ['active', 'activating']:
|
if not service_state in ['active', 'activating']:
|
||||||
logging.error(slogm('Unable to start systemd unit {}'.format(self.unit_name)))
|
service_timer_name = self.unit_name.replace(".service", ".timer")
|
||||||
|
self.unit = self.manager.LoadUnit(dbus.String(service_timer_name))
|
||||||
|
service_state = self._get_state()
|
||||||
|
if not service_state in ['active', 'activating']:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['unit'] = self.unit_name
|
||||||
|
log('E46', logdata)
|
||||||
else:
|
else:
|
||||||
self.manager.StopUnit(self.unit_name, 'replace')
|
self.manager.StopUnit(self.unit_name, 'replace')
|
||||||
self.manager.DisableUnitFiles([self.unit_name], dbus.Boolean(False))
|
self.manager.DisableUnitFiles([self.unit_name], dbus.Boolean(False))
|
||||||
self.manager.MaskUnitFiles([self.unit_name], dbus.Boolean(False), dbus.Boolean(True))
|
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()
|
service_state = self._get_state()
|
||||||
|
|
||||||
if not service_state in ['stopped']:
|
if not service_state in ['stopped', 'deactivating', 'inactive']:
|
||||||
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):
|
def _get_state(self):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -21,133 +21,194 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
|
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from util.logging import log
|
||||||
from util.logging import slogm
|
from util.util import is_machine_name, string_to_literal_eval
|
||||||
from util.util import is_machine_name
|
|
||||||
|
|
||||||
class chromium_applier(applier_frontend):
|
class chromium_applier(applier_frontend):
|
||||||
__module_name = 'ChromiumApplier'
|
__module_name = 'ChromiumApplier'
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__registry_branch = 'Software\\Policies\\Google\\Chrome'
|
__registry_branch = 'Software/Policies/Google/Chrome'
|
||||||
__managed_policies_path = '/etc/chromium/policies/managed'
|
__managed_policies_path = '/etc/chromium/policies/managed'
|
||||||
__recommended_policies_path = '/etc/chromium/policies/recommended'
|
__recommended_policies_path = '/etc/chromium/policies/recommended'
|
||||||
# JSON file where Chromium stores its settings (and which is
|
|
||||||
# overwritten every exit.
|
|
||||||
__user_settings = '.config/chromium/Default'
|
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, sid, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.username = username
|
self.username = username
|
||||||
self._is_machine_name = is_machine_name(self.username)
|
self._is_machine_name = is_machine_name(self.username)
|
||||||
self.policies = dict()
|
chromium_filter = '{}%'.format(self.__registry_branch)
|
||||||
|
self.chromium_keys = self.storage.filter_hklm_entries(chromium_filter)
|
||||||
|
|
||||||
|
self.policies_json = dict()
|
||||||
|
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_hklm_string_entry(self, hive_subkey):
|
|
||||||
query_str = '{}\\{}'.format(self.__registry_branch, hive_subkey)
|
|
||||||
return self.storage.get_hklm_entry(query_str)
|
|
||||||
|
|
||||||
def get_hkcu_string_entry(self, hive_subkey):
|
|
||||||
query_str = '{}\\{}'.format(self.__registry_branch, hive_subkey)
|
|
||||||
return self.storage.get_hkcu_entry(sid, query_str)
|
|
||||||
|
|
||||||
def get_hklm_string_entry_default(self, hive_subkey, default):
|
|
||||||
'''
|
|
||||||
Return row from HKLM table identified by hive_subkey as string
|
|
||||||
or return supplied default value if such hive_subkey is missing.
|
|
||||||
'''
|
|
||||||
|
|
||||||
defval = str(default)
|
|
||||||
response = self.get_hklm_string_entry(hive_subkey)
|
|
||||||
|
|
||||||
if response:
|
|
||||||
return response.data
|
|
||||||
|
|
||||||
return defval
|
|
||||||
|
|
||||||
def get_hkcu_string_entry_default(self, hive_subkey, default):
|
|
||||||
defval = str(default)
|
|
||||||
response = self.get_hkcu_string_entry(hive_subkey)
|
|
||||||
if response:
|
|
||||||
return response.data
|
|
||||||
return defval
|
|
||||||
|
|
||||||
def set_policy(self, name, obj):
|
|
||||||
if obj:
|
|
||||||
self.policies[name] = obj
|
|
||||||
logging.info(slogm('Chromium policy \'{}\' set to {}'.format(name, obj)))
|
|
||||||
|
|
||||||
def set_user_policy(self, name, obj):
|
|
||||||
'''
|
|
||||||
Please not that writing user preferences file is not considered
|
|
||||||
a good practice and used mostly by various malware.
|
|
||||||
'''
|
|
||||||
if not self._is_machine_name:
|
|
||||||
prefdir = os.path.join(util.get_homedir(self.username), self.__user_settings)
|
|
||||||
os.makedirs(prefdir, exist_ok=True)
|
|
||||||
|
|
||||||
prefpath = os.path.join(prefdir, 'Preferences')
|
|
||||||
util.mk_homedir_path(self.username, self.__user_settings)
|
|
||||||
settings = dict()
|
|
||||||
try:
|
|
||||||
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)))
|
|
||||||
except:
|
|
||||||
logging.error(slogm('Error during attempt to read Chromium preferences for user {}'.format(self.username)))
|
|
||||||
|
|
||||||
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)))
|
|
||||||
|
|
||||||
def get_home_page(self, hkcu=False):
|
|
||||||
response = self.get_hklm_string_entry('HomepageLocation')
|
|
||||||
result = 'about:blank'
|
|
||||||
if response:
|
|
||||||
result = response.data
|
|
||||||
return result
|
|
||||||
|
|
||||||
def machine_apply(self):
|
def machine_apply(self):
|
||||||
'''
|
'''
|
||||||
Apply machine settings.
|
Apply machine settings.
|
||||||
'''
|
'''
|
||||||
self.set_policy('HomepageLocation', self.get_home_page())
|
|
||||||
|
|
||||||
destfile = os.path.join(self.__managed_policies_path, 'policies.json')
|
destfile = os.path.join(self.__managed_policies_path, 'policies.json')
|
||||||
|
|
||||||
|
try:
|
||||||
|
recommended__json = self.policies_json.pop('Recommended')
|
||||||
|
except:
|
||||||
|
recommended__json = {}
|
||||||
|
|
||||||
|
#Replacing all nested dictionaries with a list
|
||||||
|
dict_item_to_list = (
|
||||||
|
lambda target_dict :
|
||||||
|
{key:[*val.values()] if type(val) == dict else string_to_literal_eval(val) for key,val in target_dict.items()}
|
||||||
|
)
|
||||||
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
||||||
with open(destfile, 'w') as f:
|
with open(destfile, 'w') as f:
|
||||||
json.dump(self.policies, f)
|
json.dump(dict_item_to_list(self.policies_json), f)
|
||||||
logging.debug(slogm('Wrote Chromium preferences to {}'.format(destfile)))
|
logdata = dict()
|
||||||
|
logdata['destfile'] = destfile
|
||||||
|
log('D97', logdata)
|
||||||
|
|
||||||
|
destfilerec = os.path.join(self.__recommended_policies_path, 'policies.json')
|
||||||
|
os.makedirs(self.__recommended_policies_path, exist_ok=True)
|
||||||
|
with open(destfilerec, 'w') as f:
|
||||||
|
json.dump(dict_item_to_list(recommended__json), f)
|
||||||
|
logdata = dict()
|
||||||
|
logdata['destfilerec'] = destfilerec
|
||||||
|
log('D97', logdata)
|
||||||
|
|
||||||
def user_apply(self):
|
|
||||||
'''
|
|
||||||
Apply settings for the specified username.
|
|
||||||
'''
|
|
||||||
self.set_user_policy('homepage', self.get_home_page(hkcu=True))
|
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
'''
|
'''
|
||||||
All actual job done here.
|
All actual job done here.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Chromium applier for machine'))
|
log('D95')
|
||||||
|
self.create_dict(self.chromium_keys)
|
||||||
self.machine_apply()
|
self.machine_apply()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Chromium applier for machine will not be started'))
|
log('D96')
|
||||||
#if not self._is_machine_name:
|
|
||||||
# logging.debug('Running user applier for Chromium')
|
|
||||||
# self.user_apply()
|
|
||||||
|
|
||||||
|
def get_valuename_typeint(self):
|
||||||
|
'''
|
||||||
|
List of keys resulting from parsing chrome.admx with parsing_chrom_admx_intvalues.py
|
||||||
|
'''
|
||||||
|
valuename_typeint = (['DefaultClipboardSetting',
|
||||||
|
'DefaultCookiesSetting',
|
||||||
|
'DefaultFileSystemReadGuardSetting',
|
||||||
|
'DefaultFileSystemWriteGuardSetting',
|
||||||
|
'DefaultGeolocationSetting',
|
||||||
|
'DefaultImagesSetting',
|
||||||
|
'DefaultInsecureContentSetting',
|
||||||
|
'DefaultJavaScriptJitSetting',
|
||||||
|
'DefaultJavaScriptSetting',
|
||||||
|
'DefaultLocalFontsSetting',
|
||||||
|
'DefaultNotificationsSetting',
|
||||||
|
'DefaultPopupsSetting',
|
||||||
|
'DefaultSensorsSetting',
|
||||||
|
'DefaultSerialGuardSetting',
|
||||||
|
'DefaultThirdPartyStoragePartitioningSetting',
|
||||||
|
'DefaultWebBluetoothGuardSetting',
|
||||||
|
'DefaultWebHidGuardSetting',
|
||||||
|
'DefaultWebUsbGuardSetting',
|
||||||
|
'DefaultWindowManagementSetting',
|
||||||
|
'DefaultMediaStreamSetting',
|
||||||
|
'DefaultWindowPlacementSetting',
|
||||||
|
'ProxyServerMode',
|
||||||
|
'ExtensionManifestV2Availability',
|
||||||
|
'ExtensionUnpublishedAvailability',
|
||||||
|
'BrowserSwitcherParsingMode',
|
||||||
|
'CloudAPAuthEnabled',
|
||||||
|
'AdsSettingForIntrusiveAdsSites',
|
||||||
|
'AmbientAuthenticationInPrivateModesEnabled',
|
||||||
|
'BatterySaverModeAvailability',
|
||||||
|
'BrowserSignin',
|
||||||
|
'ChromeVariations',
|
||||||
|
'DeveloperToolsAvailability',
|
||||||
|
'DownloadRestrictions',
|
||||||
|
'ForceYouTubeRestrict',
|
||||||
|
'HeadlessMode',
|
||||||
|
'IncognitoModeAvailability',
|
||||||
|
'IntranetRedirectBehavior',
|
||||||
|
'NetworkPredictionOptions',
|
||||||
|
'ProfilePickerOnStartupAvailability',
|
||||||
|
'RelaunchNotification',
|
||||||
|
'SafeSitesFilterBehavior',
|
||||||
|
'UserAgentReduction',
|
||||||
|
'BatterySaverModeAvailability_recommended',
|
||||||
|
'DownloadRestrictions_recommended',
|
||||||
|
'NetworkPredictionOptions_recommended',
|
||||||
|
'PrintPostScriptMode',
|
||||||
|
'PrintRasterizationMode',
|
||||||
|
'ChromeFrameRendererSettings',
|
||||||
|
'DefaultFileHandlingGuardSetting',
|
||||||
|
'DefaultKeygenSetting',
|
||||||
|
'DefaultPluginsSetting',
|
||||||
|
'LegacySameSiteCookieBehaviorEnabled',
|
||||||
|
'ForceMajorVersionToMinorPositionInUserAgent',
|
||||||
|
'PasswordProtectionWarningTrigger',
|
||||||
|
'SafeBrowsingProtectionLevel',
|
||||||
|
'SafeBrowsingProtectionLevel_recommended',
|
||||||
|
'RestoreOnStartup',
|
||||||
|
'RestoreOnStartup_recommended'])
|
||||||
|
return valuename_typeint
|
||||||
|
|
||||||
|
|
||||||
|
def get_boolean(self,data):
|
||||||
|
if data in ['0', 'false', None, 'none', 0]:
|
||||||
|
return False
|
||||||
|
if data in ['1', 'true', 1]:
|
||||||
|
return True
|
||||||
|
def get_parts(self, hivekeyname):
|
||||||
|
'''
|
||||||
|
Parse registry path string and leave key parameters
|
||||||
|
'''
|
||||||
|
parts = hivekeyname.replace(self.__registry_branch, '').split('/')
|
||||||
|
return parts
|
||||||
|
|
||||||
|
|
||||||
|
def create_dict(self, chromium_keys):
|
||||||
|
'''
|
||||||
|
Collect dictionaries from registry keys into a general dictionary
|
||||||
|
'''
|
||||||
|
counts = dict()
|
||||||
|
#getting the list of keys to read as an integer
|
||||||
|
valuename_typeint = self.get_valuename_typeint()
|
||||||
|
for it_data in chromium_keys:
|
||||||
|
branch = counts
|
||||||
|
try:
|
||||||
|
if type(it_data.data) is bytes:
|
||||||
|
it_data.data = it_data.data.decode(encoding='utf-16').replace('\x00','')
|
||||||
|
parts = self.get_parts(it_data.hive_key)
|
||||||
|
#creating a nested dictionary from elements
|
||||||
|
for part in parts[:-1]:
|
||||||
|
branch = branch.setdefault(part, {})
|
||||||
|
#dictionary key value initialization
|
||||||
|
if it_data.type == 4:
|
||||||
|
if it_data.valuename in valuename_typeint:
|
||||||
|
branch[parts[-1]] = int(it_data.data)
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = self.get_boolean(it_data.data)
|
||||||
|
else:
|
||||||
|
if it_data.data[0] == '[' and it_data.data[-1] == ']':
|
||||||
|
try:
|
||||||
|
branch[parts[-1]] = json.loads(str(it_data.data))
|
||||||
|
except:
|
||||||
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['Exception'] = exc
|
||||||
|
logdata['keyname'] = it_data.keyname
|
||||||
|
log('D178', logdata)
|
||||||
|
try:
|
||||||
|
self.policies_json = counts['']
|
||||||
|
except:
|
||||||
|
self.policies_json = {}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2022 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,20 +16,18 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import fileinput
|
|
||||||
import jinja2
|
import jinja2
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import string
|
||||||
|
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from gpt.drives import json2drive
|
|
||||||
from util.util import get_homedir
|
from util.util import get_homedir
|
||||||
from util.logging import slogm
|
from util.logging import log
|
||||||
|
|
||||||
def storage_get_drives(storage, sid):
|
def storage_get_drives(storage, sid):
|
||||||
drives = storage.get_drives(sid)
|
drives = storage.get_drives(sid)
|
||||||
@@ -50,45 +48,159 @@ def add_line_if_missing(filename, ins_line):
|
|||||||
f.write(ins_line + '\n')
|
f.write(ins_line + '\n')
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
|
def remove_chars_before_colon(input_string):
|
||||||
|
if ":" in input_string:
|
||||||
|
colon_index = input_string.index(":")
|
||||||
|
result_string = input_string[colon_index + 1:]
|
||||||
|
return result_string
|
||||||
|
else:
|
||||||
|
return input_string
|
||||||
|
|
||||||
|
def remove_escaped_quotes(input_string):
|
||||||
|
result_string = input_string.replace('"', '').replace("'", '')
|
||||||
|
return result_string
|
||||||
|
|
||||||
|
|
||||||
|
class Drive_list:
|
||||||
|
__alphabet = string.ascii_uppercase
|
||||||
|
def __init__(self):
|
||||||
|
self.dict_drives = dict()
|
||||||
|
|
||||||
|
def __get_letter(self, letter):
|
||||||
|
slice_letters = set(self.__alphabet[self.__alphabet.find(letter) + 1:]) - set(self.dict_drives.keys())
|
||||||
|
free_letters = sorted(slice_letters)
|
||||||
|
if free_letters:
|
||||||
|
return free_letters[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def append(self, drive:dict):
|
||||||
|
cur_dir = drive['dir']
|
||||||
|
if cur_dir not in set(self.dict_drives.keys()):
|
||||||
|
if drive['action'] == 'D':
|
||||||
|
return
|
||||||
|
self.dict_drives[cur_dir] = drive
|
||||||
|
return
|
||||||
|
|
||||||
|
else:
|
||||||
|
if drive['action'] == 'C':
|
||||||
|
if drive['useLetter'] == '1':
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
new_dir = self.__get_letter(cur_dir)
|
||||||
|
if not new_dir:
|
||||||
|
return
|
||||||
|
drive['dir'] = new_dir
|
||||||
|
self.dict_drives[new_dir] = drive
|
||||||
|
return
|
||||||
|
|
||||||
|
if drive['action'] == 'U':
|
||||||
|
self.dict_drives[cur_dir]['thisDrive'] = drive['thisDrive']
|
||||||
|
self.dict_drives[cur_dir]['allDrives'] = drive['allDrives']
|
||||||
|
self.dict_drives[cur_dir]['label'] = drive['label']
|
||||||
|
self.dict_drives[cur_dir]['persistent'] = drive['persistent']
|
||||||
|
self.dict_drives[cur_dir]['useLetter'] = drive['useLetter']
|
||||||
|
return
|
||||||
|
|
||||||
|
if drive['action'] == 'R':
|
||||||
|
self.dict_drives[cur_dir] = drive
|
||||||
|
return
|
||||||
|
if drive['action'] == 'D':
|
||||||
|
if drive['useLetter'] == '1':
|
||||||
|
self.dict_drives.pop(cur_dir, None)
|
||||||
|
else:
|
||||||
|
keys_set = set(self.dict_drives.keys())
|
||||||
|
slice_letters = set(self.__alphabet[self.__alphabet.find(cur_dir):])
|
||||||
|
for letter_dir in (keys_set & slice_letters):
|
||||||
|
self.dict_drives.pop(letter_dir, None)
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
return list(self.dict_drives.values())
|
||||||
|
|
||||||
|
def len(self):
|
||||||
|
return len(self.dict_drives)
|
||||||
|
|
||||||
class cifs_applier(applier_frontend):
|
class cifs_applier(applier_frontend):
|
||||||
def __init__(self, storage):
|
__module_name = 'CIFSApplier'
|
||||||
pass
|
__module_enabled = True
|
||||||
|
__module_experimental = False
|
||||||
|
|
||||||
|
def __init__(self, storage, sid):
|
||||||
|
self.applier_cifs = cifs_applier_user(storage, sid, None)
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
storage
|
||||||
|
, self.__module_name
|
||||||
|
, self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
pass
|
if self.__module_enabled:
|
||||||
|
log('D179')
|
||||||
|
self.applier_cifs._admin_context_apply()
|
||||||
|
else:
|
||||||
|
log('D180')
|
||||||
|
|
||||||
class cifs_applier_user(applier_frontend):
|
class cifs_applier_user(applier_frontend):
|
||||||
__module_name = 'CIFSApplierUser'
|
__module_name = 'CIFSApplierUser'
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__auto_file = '/etc/auto.master'
|
__auto_file = '/etc/auto.master'
|
||||||
__auto_dir = '/etc/auto.master.gpupdate.d'
|
__auto_dir = '/etc/auto.master.gpupdate.d'
|
||||||
__template_path = '/usr/share/gpupdate/templates'
|
__template_path = '/usr/share/gpupdate/templates'
|
||||||
__template_mountpoints = 'autofs_mountpoints.j2'
|
__template_mountpoints = 'autofs_mountpoints.j2'
|
||||||
__template_identity = 'autofs_identity.j2'
|
__template_identity = 'autofs_identity.j2'
|
||||||
__template_auto = 'autofs_auto.j2'
|
__template_auto = 'autofs_auto.j2'
|
||||||
|
__template_mountpoints_hide = 'autofs_mountpoints_hide.j2'
|
||||||
|
__template_auto_hide = 'autofs_auto_hide.j2'
|
||||||
|
__enable_home_link = 'Software\\BaseALT\\Policies\\GPUpdate\\DriveMapsHome'
|
||||||
|
__enable_home_link_user = 'Software\\BaseALT\\Policies\\GPUpdate\\DriveMapsHomeUser'
|
||||||
|
__target_mountpoint = '/media/gpupdate'
|
||||||
|
__target_mountpoint_user = '/run/media'
|
||||||
|
__mountpoint_dirname = 'drives.system'
|
||||||
|
__mountpoint_dirname_user = 'drives'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, sid, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.username = username
|
self.username = username
|
||||||
|
self.state_home_link = False
|
||||||
|
self.state_home_link_user = False
|
||||||
|
|
||||||
|
if username:
|
||||||
|
self.home = self.__target_mountpoint_user + '/' + username
|
||||||
|
self.state_home_link = self.check_enable_home_link(self.__enable_home_link)
|
||||||
|
self.state_home_link_user = self.check_enable_home_link(self.__enable_home_link_user)
|
||||||
|
else:
|
||||||
|
self.home = self.__target_mountpoint
|
||||||
|
|
||||||
self.home = get_homedir(username)
|
|
||||||
conf_file = '{}.conf'.format(sid)
|
conf_file = '{}.conf'.format(sid)
|
||||||
|
conf_hide_file = '{}_hide.conf'.format(sid)
|
||||||
autofs_file = '{}.autofs'.format(sid)
|
autofs_file = '{}.autofs'.format(sid)
|
||||||
|
autofs_hide_file = '{}_hide.autofs'.format(sid)
|
||||||
cred_file = '{}.creds'.format(sid)
|
cred_file = '{}.creds'.format(sid)
|
||||||
|
|
||||||
self.auto_master_d = Path(self.__auto_dir)
|
self.auto_master_d = Path(self.__auto_dir)
|
||||||
|
|
||||||
self.user_config = self.auto_master_d / conf_file
|
self.user_config = self.auto_master_d / conf_file
|
||||||
|
self.user_config_hide = self.auto_master_d / conf_hide_file
|
||||||
if os.path.exists(self.user_config.resolve()):
|
if os.path.exists(self.user_config.resolve()):
|
||||||
self.user_config.unlink()
|
self.user_config.unlink()
|
||||||
|
if os.path.exists(self.user_config_hide.resolve()):
|
||||||
|
self.user_config_hide.unlink()
|
||||||
self.user_autofs = self.auto_master_d / autofs_file
|
self.user_autofs = self.auto_master_d / autofs_file
|
||||||
|
self.user_autofs_hide = self.auto_master_d / autofs_hide_file
|
||||||
if os.path.exists(self.user_autofs.resolve()):
|
if os.path.exists(self.user_autofs.resolve()):
|
||||||
self.user_autofs.unlink()
|
self.user_autofs.unlink()
|
||||||
|
if os.path.exists(self.user_autofs_hide.resolve()):
|
||||||
|
self.user_autofs_hide.unlink()
|
||||||
self.user_creds = self.auto_master_d / cred_file
|
self.user_creds = self.auto_master_d / cred_file
|
||||||
|
|
||||||
self.mount_dir = Path(os.path.join(self.home, 'net'))
|
if username:
|
||||||
|
self.mntTarget = self.__mountpoint_dirname_user
|
||||||
|
else:
|
||||||
|
self.mntTarget = self.__mountpoint_dirname
|
||||||
|
|
||||||
|
self.mount_dir = Path(os.path.join(self.home))
|
||||||
self.drives = storage_get_drives(self.storage, self.sid)
|
self.drives = storage_get_drives(self.storage, self.sid)
|
||||||
|
|
||||||
self.template_loader = jinja2.FileSystemLoader(searchpath=self.__template_path)
|
self.template_loader = jinja2.FileSystemLoader(searchpath=self.__template_path)
|
||||||
@@ -98,12 +210,21 @@ class cifs_applier_user(applier_frontend):
|
|||||||
self.template_indentity = self.template_env.get_template(self.__template_identity)
|
self.template_indentity = self.template_env.get_template(self.__template_identity)
|
||||||
self.template_auto = self.template_env.get_template(self.__template_auto)
|
self.template_auto = self.template_env.get_template(self.__template_auto)
|
||||||
|
|
||||||
|
self.template_mountpoints_hide = self.template_env.get_template(self.__template_mountpoints_hide)
|
||||||
|
self.template_auto_hide = self.template_env.get_template(self.__template_auto_hide)
|
||||||
|
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def check_enable_home_link(self, enable_home_link):
|
||||||
|
if self.storage.get_hkcu_entry(self.sid, enable_home_link):
|
||||||
|
data = self.storage.get_hkcu_entry(self.sid, enable_home_link).data
|
||||||
|
return bool(int(data)) if data else None
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -111,7 +232,7 @@ class cifs_applier_user(applier_frontend):
|
|||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __admin_context_apply(self):
|
def _admin_context_apply(self):
|
||||||
# Create /etc/auto.master.gpupdate.d directory
|
# Create /etc/auto.master.gpupdate.d directory
|
||||||
self.auto_master_d.mkdir(parents=True, exist_ok=True)
|
self.auto_master_d.mkdir(parents=True, exist_ok=True)
|
||||||
# Create user's destination mount directory
|
# Create user's destination mount directory
|
||||||
@@ -122,28 +243,42 @@ class cifs_applier_user(applier_frontend):
|
|||||||
add_line_if_missing(self.__auto_file, auto_destdir)
|
add_line_if_missing(self.__auto_file, auto_destdir)
|
||||||
|
|
||||||
# Collect data for drive settings
|
# Collect data for drive settings
|
||||||
drive_list = list()
|
drive_list = Drive_list()
|
||||||
for drv in self.drives:
|
for drv in self.drives:
|
||||||
drive_settings = dict()
|
drive_settings = dict()
|
||||||
drive_settings['dir'] = drv.dir
|
drive_settings['dir'] = drv.dir
|
||||||
drive_settings['login'] = drv.login
|
drive_settings['login'] = drv.login
|
||||||
drive_settings['password'] = drv.password
|
drive_settings['password'] = drv.password
|
||||||
drive_settings['path'] = drv.path.replace('\\', '/')
|
drive_settings['path'] = remove_chars_before_colon(drv.path.replace('\\', '/'))
|
||||||
|
drive_settings['action'] = drv.action
|
||||||
|
drive_settings['thisDrive'] = drv.thisDrive
|
||||||
|
drive_settings['allDrives'] = drv.allDrives
|
||||||
|
drive_settings['label'] = remove_escaped_quotes(drv.label)
|
||||||
|
drive_settings['persistent'] = drv.persistent
|
||||||
|
drive_settings['useLetter'] = drv.useLetter
|
||||||
|
|
||||||
drive_list.append(drive_settings)
|
drive_list.append(drive_settings)
|
||||||
|
|
||||||
if len(drive_list) > 0:
|
if drive_list.len() > 0:
|
||||||
mount_settings = dict()
|
mount_settings = dict()
|
||||||
mount_settings['drives'] = drive_list
|
mount_settings['drives'] = drive_list()
|
||||||
mount_text = self.template_mountpoints.render(**mount_settings)
|
mount_text = self.template_mountpoints.render(**mount_settings)
|
||||||
|
|
||||||
|
mount_text_hide = self.template_mountpoints_hide.render(**mount_settings)
|
||||||
|
|
||||||
with open(self.user_config.resolve(), 'w') as f:
|
with open(self.user_config.resolve(), 'w') as f:
|
||||||
f.truncate()
|
f.truncate()
|
||||||
f.write(mount_text)
|
f.write(mount_text)
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
|
with open(self.user_config_hide.resolve(), 'w') as f:
|
||||||
|
f.truncate()
|
||||||
|
f.write(mount_text_hide)
|
||||||
|
f.flush()
|
||||||
|
|
||||||
autofs_settings = dict()
|
autofs_settings = dict()
|
||||||
autofs_settings['home_dir'] = self.home
|
autofs_settings['home_dir'] = self.home
|
||||||
|
autofs_settings['mntTarget'] = self.mntTarget
|
||||||
autofs_settings['mount_file'] = self.user_config.resolve()
|
autofs_settings['mount_file'] = self.user_config.resolve()
|
||||||
autofs_text = self.template_auto.render(**autofs_settings)
|
autofs_text = self.template_auto.render(**autofs_settings)
|
||||||
|
|
||||||
@@ -152,13 +287,71 @@ class cifs_applier_user(applier_frontend):
|
|||||||
f.write(autofs_text)
|
f.write(autofs_text)
|
||||||
f.flush()
|
f.flush()
|
||||||
|
|
||||||
|
autofs_settings['mount_file'] = self.user_config_hide.resolve()
|
||||||
|
autofs_text = self.template_auto_hide.render(**autofs_settings)
|
||||||
|
with open(self.user_autofs_hide.resolve(), 'w') as f:
|
||||||
|
f.truncate()
|
||||||
|
f.write(autofs_text)
|
||||||
|
f.flush()
|
||||||
|
|
||||||
|
if self.username:
|
||||||
|
self.update_drivemaps_home_links()
|
||||||
|
|
||||||
subprocess.check_call(['/bin/systemctl', 'restart', 'autofs'])
|
subprocess.check_call(['/bin/systemctl', 'restart', 'autofs'])
|
||||||
|
|
||||||
|
def update_drivemaps_home_links(self):
|
||||||
|
dUser = Path(get_homedir(self.username)+'/net.' + self.__mountpoint_dirname_user)
|
||||||
|
dUserHide = Path(get_homedir(self.username)+'/.net.' + self.__mountpoint_dirname_user)
|
||||||
|
|
||||||
|
if self.state_home_link_user:
|
||||||
|
dUserMountpoint = Path(self.home).joinpath(self.__mountpoint_dirname_user)
|
||||||
|
dUserMountpointHide = Path(self.home).joinpath('.' + self.__mountpoint_dirname_user)
|
||||||
|
|
||||||
|
if not dUser.exists():
|
||||||
|
try:
|
||||||
|
os.symlink(dUserMountpoint, dUser, True)
|
||||||
|
except Exception as exc:
|
||||||
|
log('D194', {'exc': exc})
|
||||||
|
|
||||||
|
if not dUserHide.exists():
|
||||||
|
try:
|
||||||
|
os.symlink(dUserMountpointHide, dUserHide, True)
|
||||||
|
except Exception as exc:
|
||||||
|
log('D196', {'exc': exc})
|
||||||
|
else:
|
||||||
|
if dUser.is_symlink() and dUser.owner() == 'root':
|
||||||
|
dUser.unlink()
|
||||||
|
if dUserHide.is_symlink() and dUserHide.owner() == 'root':
|
||||||
|
dUserHide.unlink()
|
||||||
|
|
||||||
|
dMachine = Path(get_homedir(self.username)+'/net.' + self.__mountpoint_dirname)
|
||||||
|
dMachineHide = Path(get_homedir(self.username)+'/.net.' + self.__mountpoint_dirname)
|
||||||
|
|
||||||
|
if self.state_home_link:
|
||||||
|
dMachineMountpoint = Path(self.__target_mountpoint).joinpath(self.__mountpoint_dirname)
|
||||||
|
dMachineMountpointHide = Path(self.__target_mountpoint).joinpath('.' + self.__mountpoint_dirname)
|
||||||
|
|
||||||
|
if not dMachine.exists():
|
||||||
|
try:
|
||||||
|
os.symlink(dMachineMountpoint, dMachine, True)
|
||||||
|
except Exception as exc:
|
||||||
|
log('D195', {'exc': exc})
|
||||||
|
|
||||||
|
if not dMachineHide.exists():
|
||||||
|
try:
|
||||||
|
os.symlink(dMachineMountpointHide, dMachineHide, True)
|
||||||
|
except Exception as exc:
|
||||||
|
log('D197', {'exc': exc})
|
||||||
|
else:
|
||||||
|
if dMachine.is_symlink() and dMachine.owner() == 'root':
|
||||||
|
dMachine.unlink()
|
||||||
|
if dMachineHide.is_symlink() and dMachineHide.owner() == 'root':
|
||||||
|
dMachineHide.unlink()
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running CIFS applier for user in administrator context'))
|
log('D146')
|
||||||
self.__admin_context_apply()
|
self._admin_context_apply()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('CIFS applier for user in administrator context will not be started'))
|
log('D147')
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from .appliers.control import control
|
from .appliers.control import control
|
||||||
from util.logging import slogm
|
from util.logging import slogm, log
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -29,11 +29,11 @@ class control_applier(applier_frontend):
|
|||||||
__module_name = 'ControlApplier'
|
__module_name = 'ControlApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
_registry_branch = 'Software\\BaseALT\\Policies\\Control'
|
_registry_branch = 'Software/BaseALT/Policies/Control'
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.control_settings = self.storage.filter_hklm_entries('Software\\BaseALT\\Policies\\Control%')
|
self.control_settings = self.storage.filter_hklm_entries(self._registry_branch)
|
||||||
self.controls = list()
|
self.controls = list()
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
@@ -43,15 +43,30 @@ class control_applier(applier_frontend):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for setting in self.control_settings:
|
for setting in self.control_settings:
|
||||||
valuename = setting.hive_key.rpartition('\\')[2]
|
valuename = setting.hive_key.rpartition('/')[2]
|
||||||
try:
|
try:
|
||||||
self.controls.append(control(valuename, int(setting.data)))
|
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:
|
except ValueError as exc:
|
||||||
self.controls.append(control(valuename, setting.data))
|
try:
|
||||||
logging.info(slogm('Working with control {} with string value'.format(valuename)))
|
ctl = control(valuename, setting.data)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.info(slogm('Unable to work with control {}: {}'.format(valuename, 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:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['control'] = valuename
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E39', logdata)
|
||||||
#for e in polfile.pol_file.entries:
|
#for e in polfile.pol_file.entries:
|
||||||
# print('{}:{}:{}:{}:{}'.format(e.type, e.data, e.valuename, e.keyname))
|
# print('{}:{}:{}:{}:{}'.format(e.type, e.data, e.valuename, e.keyname))
|
||||||
for cont in self.controls:
|
for cont in self.controls:
|
||||||
@@ -62,8 +77,7 @@ class control_applier(applier_frontend):
|
|||||||
Trigger control facility invocation.
|
Trigger control facility invocation.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Control applier for machine'))
|
log('D67')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Control applier for machine will not be started'))
|
log('E40')
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from .applier_frontend import (
|
|||||||
)
|
)
|
||||||
from gpt.printers import json2printer
|
from gpt.printers import json2printer
|
||||||
from util.rpm import is_rpm_installed
|
from util.rpm import is_rpm_installed
|
||||||
from util.logging import slogm
|
from util.logging import slogm, log
|
||||||
|
|
||||||
def storage_get_printers(storage, sid):
|
def storage_get_printers(storage, sid):
|
||||||
'''
|
'''
|
||||||
@@ -79,10 +79,14 @@ class cups_applier(applier_frontend):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if not is_rpm_installed('cups'):
|
if not is_rpm_installed('cups'):
|
||||||
logging.warning(slogm('CUPS is not installed: no printer settings will be deployed'))
|
log('W9')
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
self.cups_connection = cups.Connection()
|
self.cups_connection = cups.Connection()
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['exc', exc]
|
||||||
|
log('W20', logdata)
|
||||||
self.printers = storage_get_printers(self.storage, self.storage.get_info('machine_sid'))
|
self.printers = storage_get_printers(self.storage, self.storage.get_info('machine_sid'))
|
||||||
|
|
||||||
if self.printers:
|
if self.printers:
|
||||||
@@ -94,10 +98,10 @@ class cups_applier(applier_frontend):
|
|||||||
Perform configuration of printer which is assigned to computer.
|
Perform configuration of printer which is assigned to computer.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running CUPS applier for machine'))
|
log('D113')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('CUPS applier for machine will not be started'))
|
log('D114')
|
||||||
|
|
||||||
class cups_applier_user(applier_frontend):
|
class cups_applier_user(applier_frontend):
|
||||||
__module_name = 'CUPSApplierUser'
|
__module_name = 'CUPSApplierUser'
|
||||||
@@ -123,7 +127,7 @@ class cups_applier_user(applier_frontend):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if not is_rpm_installed('cups'):
|
if not is_rpm_installed('cups'):
|
||||||
logging.warning(slogm('CUPS is not installed: no printer settings will be deployed'))
|
log('W9')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.cups_connection = cups.Connection()
|
self.cups_connection = cups.Connection()
|
||||||
@@ -138,8 +142,8 @@ class cups_applier_user(applier_frontend):
|
|||||||
Perform printer configuration assigned for user.
|
Perform printer configuration assigned for user.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running CUPS applier for user in administrator context'))
|
log('D115')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('CUPS applier for user in administrator context will not be started'))
|
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')
|
||||||
|
|
||||||
83
gpoa/frontend/file_applier.py
Normal file
83
gpoa/frontend/file_applier.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 .appliers.file_cp import Files_cp, Execution_check
|
||||||
|
from .applier_frontend import (
|
||||||
|
applier_frontend
|
||||||
|
, check_enabled
|
||||||
|
)
|
||||||
|
from util.logging import log
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class file_applier(applier_frontend):
|
||||||
|
__module_name = 'FilesApplier'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
|
||||||
|
def __init__(self, storage, file_cache, sid):
|
||||||
|
self.storage = storage
|
||||||
|
self.exe_check = Execution_check(storage)
|
||||||
|
self.sid = sid
|
||||||
|
self.file_cache = file_cache
|
||||||
|
self.files = self.storage.get_files(self.sid)
|
||||||
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for file in self.files:
|
||||||
|
Files_cp(file, self.file_cache, self.exe_check)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D167')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D168')
|
||||||
|
|
||||||
|
class file_applier_user(applier_frontend):
|
||||||
|
__module_name = 'FilesApplierUser'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
|
||||||
|
def __init__(self, storage, file_cache, sid, username):
|
||||||
|
self.storage = storage
|
||||||
|
self.file_cache = file_cache
|
||||||
|
self.sid = sid
|
||||||
|
self.username = username
|
||||||
|
self.exe_check = Execution_check(storage)
|
||||||
|
self.files = self.storage.get_files(self.sid)
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
self.storage
|
||||||
|
, self.__module_name
|
||||||
|
, self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for file in self.files:
|
||||||
|
Files_cp(file, self.file_cache, self.exe_check, self.username)
|
||||||
|
|
||||||
|
def admin_context_apply(self):
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D169')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D170')
|
||||||
|
|
||||||
|
def user_context_apply(self):
|
||||||
|
pass
|
||||||
@@ -25,25 +25,23 @@
|
|||||||
# This thing must work with keys and subkeys located at:
|
# This thing must work with keys and subkeys located at:
|
||||||
# Software\Policies\Mozilla\Firefox
|
# Software\Policies\Mozilla\Firefox
|
||||||
|
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import configparser
|
|
||||||
|
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from util.logging import slogm
|
from util.logging import log
|
||||||
from util.util import is_machine_name
|
from util.util import is_machine_name, try_dict_to_literal_eval
|
||||||
|
|
||||||
class firefox_applier(applier_frontend):
|
class firefox_applier(applier_frontend):
|
||||||
__module_name = 'FirefoxApplier'
|
__module_name = 'FirefoxApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\Policies\\Mozilla\\Firefox'
|
__registry_branch = 'Software/Policies/Mozilla/Firefox'
|
||||||
__firefox_installdir = '/usr/lib64/firefox/distribution'
|
__firefox_installdir1 = '/usr/lib64/firefox/distribution'
|
||||||
__user_settings_dir = '.mozilla/firefox'
|
__firefox_installdir2 = '/etc/firefox/policies'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, sid, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
@@ -52,108 +50,133 @@ class firefox_applier(applier_frontend):
|
|||||||
self._is_machine_name = is_machine_name(self.username)
|
self._is_machine_name = is_machine_name(self.username)
|
||||||
self.policies = dict()
|
self.policies = dict()
|
||||||
self.policies_json = dict({ 'policies': self.policies })
|
self.policies_json = dict({ 'policies': self.policies })
|
||||||
|
firefox_filter = '{}%'.format(self.__registry_branch)
|
||||||
|
self.firefox_keys = self.storage.filter_hklm_entries(firefox_filter)
|
||||||
|
self.policies_gen = dict()
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_profiles(self):
|
def get_boolean(self,data):
|
||||||
'''
|
if data in ['0', 'false', None, 'none', 0]:
|
||||||
Get directory names of Firefox profiles for specified username.
|
|
||||||
'''
|
|
||||||
profiles_ini = os.path.join(util.get_homedir(self.username), self.__user_settings_dir, 'profiles.ini')
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(profiles_ini)
|
|
||||||
|
|
||||||
profile_paths = list()
|
|
||||||
for section in config.keys():
|
|
||||||
if section.startswith('Profile'):
|
|
||||||
profile_paths.append(config[section]['Path'])
|
|
||||||
|
|
||||||
return profile_paths
|
|
||||||
|
|
||||||
def get_hklm_string_entry(self, hive_subkey):
|
|
||||||
'''
|
|
||||||
Get HKEY_LOCAL_MACHINE hive subkey of
|
|
||||||
'Software\Policies\Mozilla\Firefox'.
|
|
||||||
'''
|
|
||||||
query_str = '{}\\{}'.format(self.__registry_branch, hive_subkey)
|
|
||||||
return self.storage.get_hklm_entry(query_str)
|
|
||||||
|
|
||||||
def get_hklm_string_entry_default(self, hive_subkey, default):
|
|
||||||
'''
|
|
||||||
Get Firefox's subkey or return the default value.
|
|
||||||
'''
|
|
||||||
defval = str(default)
|
|
||||||
response = self.get_hklm_string_entry(hive_subkey)
|
|
||||||
if response:
|
|
||||||
return response.data
|
|
||||||
return defval
|
|
||||||
|
|
||||||
def set_policy(self, name, obj):
|
|
||||||
'''
|
|
||||||
Add entry to policy set.
|
|
||||||
'''
|
|
||||||
if obj:
|
|
||||||
self.policies[name] = obj
|
|
||||||
logging.info(slogm('Firefox policy \'{}\' set to {}'.format(name, obj)))
|
|
||||||
|
|
||||||
def get_home_page(self):
|
|
||||||
'''
|
|
||||||
Query the Homepage property from the storage.
|
|
||||||
'''
|
|
||||||
homepage = dict({
|
|
||||||
'URL': 'about:blank',
|
|
||||||
'Locked': False,
|
|
||||||
'StartPage': 'homepage'
|
|
||||||
})
|
|
||||||
response = self.get_hklm_string_entry('Homepage\\URL')
|
|
||||||
if response:
|
|
||||||
homepage['URL'] = response.data
|
|
||||||
return homepage
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_block_about_config(self):
|
|
||||||
'''
|
|
||||||
Query BlockAboutConfig boolean property from the storage.
|
|
||||||
'''
|
|
||||||
response = self.get_hklm_string_entry('BlockAboutConfig')
|
|
||||||
if response:
|
|
||||||
if response.data.lower() in ['0', 'false', False, None, 'None']:
|
|
||||||
return False
|
return False
|
||||||
|
if data in ['1', 'true', 1]:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return None
|
def get_parts(self, hivekeyname):
|
||||||
|
'''
|
||||||
|
Parse registry path string and leave key parameters
|
||||||
|
'''
|
||||||
|
parts = hivekeyname.replace(self.__registry_branch, '').split('/')
|
||||||
|
return parts
|
||||||
|
|
||||||
|
def create_dict(self, firefox_keys):
|
||||||
|
'''
|
||||||
|
Collect dictionaries from registry keys into a general dictionary
|
||||||
|
'''
|
||||||
|
excp = ['SOCKSVersion']
|
||||||
|
counts = dict()
|
||||||
|
for it_data in firefox_keys:
|
||||||
|
branch = counts
|
||||||
|
try:
|
||||||
|
if type(it_data.data) is bytes:
|
||||||
|
it_data.data = it_data.data.decode(encoding='utf-16').replace('\x00','')
|
||||||
|
json_data = try_dict_to_literal_eval(it_data.data)
|
||||||
|
if json_data:
|
||||||
|
it_data.data = json_data
|
||||||
|
it_data.type = 7
|
||||||
|
#Cases when it is necessary to create nested dictionaries
|
||||||
|
if it_data.valuename != it_data.data:
|
||||||
|
parts = self.get_parts(it_data.hive_key)
|
||||||
|
#creating a nested dictionary from elements
|
||||||
|
for part in parts[:-1]:
|
||||||
|
branch = branch.setdefault(part, {})
|
||||||
|
#dictionary key value initialization
|
||||||
|
if it_data.type == 4:
|
||||||
|
if it_data.valuename in excp:
|
||||||
|
branch[parts[-1]] = int(it_data.data)
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = self.get_boolean(it_data.data)
|
||||||
|
elif it_data.type == 7:
|
||||||
|
branch[parts[-1]] = it_data.data
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
#Cases when it is necessary to create lists in a dictionary
|
||||||
|
else:
|
||||||
|
parts = self.get_parts(it_data.keyname)
|
||||||
|
for part in parts[:-1]:
|
||||||
|
branch = branch.setdefault(part, {})
|
||||||
|
if branch.get(parts[-1]) is None:
|
||||||
|
branch[parts[-1]] = list()
|
||||||
|
if it_data.type == 4:
|
||||||
|
branch[parts[-1]].append(self.get_boolean(it_data.data))
|
||||||
|
else:
|
||||||
|
if os.path.isdir(str(it_data.data).replace('\\', '/')):
|
||||||
|
branch[parts[-1]].append(str(it_data.data).replace('\\', '/'))
|
||||||
|
else:
|
||||||
|
branch[parts[-1]].append(str(it_data.data))
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['Exception'] = exc
|
||||||
|
logdata['keyname'] = it_data.keyname
|
||||||
|
log('W14', logdata)
|
||||||
|
|
||||||
|
self.policies_json = {'policies': dict_item_to_list(counts)}
|
||||||
|
|
||||||
def machine_apply(self):
|
def machine_apply(self):
|
||||||
'''
|
'''
|
||||||
Write policies.json to Firefox installdir.
|
Write policies.json to Firefox installdir.
|
||||||
'''
|
'''
|
||||||
self.set_policy('Homepage', self.get_home_page())
|
self.create_dict(self.firefox_keys)
|
||||||
self.set_policy('BlockAboutConfig', self.get_block_about_config())
|
destfile = os.path.join(self.__firefox_installdir1, 'policies.json')
|
||||||
|
|
||||||
destfile = os.path.join(self.__firefox_installdir, 'policies.json')
|
os.makedirs(self.__firefox_installdir1, exist_ok=True)
|
||||||
|
|
||||||
os.makedirs(self.__firefox_installdir, exist_ok=True)
|
|
||||||
with open(destfile, 'w') as f:
|
with open(destfile, 'w') as f:
|
||||||
json.dump(self.policies_json, f)
|
json.dump(self.policies_json, f)
|
||||||
logging.debug(slogm('Wrote Firefox preferences to {}'.format(destfile)))
|
logdata = dict()
|
||||||
|
logdata['destfile'] = destfile
|
||||||
|
log('D91', logdata)
|
||||||
|
|
||||||
def user_apply(self):
|
destfile = os.path.join(self.__firefox_installdir2, 'policies.json')
|
||||||
profiles = self.get_profiles()
|
os.makedirs(self.__firefox_installdir2, exist_ok=True)
|
||||||
|
with open(destfile, 'w') as f:
|
||||||
profiledir = os.path.join(util.get_homedir(self.username), self.__user_settings_dir)
|
json.dump(self.policies_json, f)
|
||||||
for profile in profiles:
|
logdata = dict()
|
||||||
logging.debug(slogm('Found Firefox profile in {}/{}'.format(profiledir, profile)))
|
logdata['destfile'] = destfile
|
||||||
|
log('D91', logdata)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Firefox applier for machine'))
|
log('D93')
|
||||||
self.machine_apply()
|
self.machine_apply()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Firefox applier for machine will not be started'))
|
log('D94')
|
||||||
#if not self._is_machine_name:
|
|
||||||
# logging.debug('Running user applier for Firefox')
|
|
||||||
# self.user_apply()
|
|
||||||
|
|
||||||
|
def key_dict_is_digit(dictionary:dict) -> bool:
|
||||||
|
'''
|
||||||
|
Checking if a dictionary key is a digit
|
||||||
|
'''
|
||||||
|
if not isinstance(dictionary, dict):
|
||||||
|
return False
|
||||||
|
for dig in dictionary.keys():
|
||||||
|
if dig.isdigit():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def dict_item_to_list(dictionary:dict) -> dict:
|
||||||
|
'''
|
||||||
|
Replacing dictionaries with numeric keys with a List
|
||||||
|
'''
|
||||||
|
if '' in dictionary:
|
||||||
|
dictionary = dictionary.pop('')
|
||||||
|
|
||||||
|
for key,val in dictionary.items():
|
||||||
|
if type(val) == dict:
|
||||||
|
if key_dict_is_digit(val):
|
||||||
|
dictionary[key] = [*val.values()]
|
||||||
|
else:
|
||||||
|
dict_item_to_list(dictionary[key])
|
||||||
|
return dictionary
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from util.logging import slogm
|
from util.logging import slogm, log
|
||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
@@ -52,14 +52,14 @@ class firewall_applier(applier_frontend):
|
|||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Firewall applier for machine'))
|
log('D117')
|
||||||
if '1' == self.firewall_enabled:
|
if '1' == self.firewall_enabled:
|
||||||
logging.debug(slogm('Firewall is enabled'))
|
log('D118')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Firewall is disabled, settings will be reset'))
|
log('D119')
|
||||||
proc = subprocess.Popen(self.__firewall_reset_cmd)
|
proc = subprocess.Popen(self.__firewall_reset_cmd)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Firewall applier will not be started'))
|
log('D120')
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from .appliers.folder import Folder
|
from .appliers.folder import Folder
|
||||||
from util.logging import slogm
|
from util.logging import log
|
||||||
|
from util.windows import expand_windows_var
|
||||||
import logging
|
import re
|
||||||
|
|
||||||
class folder_applier(applier_frontend):
|
class folder_applier(applier_frontend):
|
||||||
__module_name = 'FoldersApplier'
|
__module_name = 'FoldersApplier'
|
||||||
@@ -36,16 +36,22 @@ class folder_applier(applier_frontend):
|
|||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.folders = self.storage.get_folders(self.sid)
|
self.folders = self.storage.get_folders(self.sid)
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Folder applier for machine'))
|
log('D107')
|
||||||
for directory_obj in self.folders:
|
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:
|
||||||
|
log('D109', {"path": directory_obj.path})
|
||||||
|
continue
|
||||||
fld = Folder(directory_obj)
|
fld = Folder(directory_obj)
|
||||||
fld.action()
|
fld.act()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Folder applier for machine will not be started'))
|
log('D108')
|
||||||
|
|
||||||
class folder_applier_user(applier_frontend):
|
class folder_applier_user(applier_frontend):
|
||||||
__module_name = 'FoldersApplierUser'
|
__module_name = 'FoldersApplierUser'
|
||||||
@@ -65,20 +71,22 @@ class folder_applier_user(applier_frontend):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for directory_obj in self.folders:
|
for directory_obj in self.folders:
|
||||||
fld = Folder(directory_obj)
|
check = expand_windows_var(directory_obj.path, self.username).replace('\\', '/')
|
||||||
fld.action()
|
win_var = re.findall(r'%.+?%', check)
|
||||||
|
drive = re.findall(r'^[a-z A-Z]\:',check)
|
||||||
|
if drive or win_var:
|
||||||
|
log('D110', {"path": directory_obj.path})
|
||||||
|
continue
|
||||||
|
fld = Folder(directory_obj, self.username)
|
||||||
|
fld.act()
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
if self.__module_enabled:
|
pass
|
||||||
logging.debug(slogm('Running Folder applier for user in administrator context'))
|
|
||||||
self.run()
|
|
||||||
else:
|
|
||||||
logging.debug(slogm('Folder applier for user in administrator context will not be started'))
|
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Folder applier for user in user context'))
|
log('D111')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Folder applier for user administrator context will not be started'))
|
log('D112')
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from storage import registry_factory
|
from storage import registry_factory
|
||||||
|
from storage.fs_file_cache import fs_file_cache
|
||||||
|
|
||||||
from .control_applier import control_applier
|
from .control_applier import control_applier
|
||||||
from .polkit_applier import (
|
from .polkit_applier import (
|
||||||
@@ -44,16 +45,45 @@ from .folder_applier import (
|
|||||||
folder_applier
|
folder_applier
|
||||||
, folder_applier_user
|
, folder_applier_user
|
||||||
)
|
)
|
||||||
from .cifs_applier import cifs_applier_user
|
from .cifs_applier import (
|
||||||
|
cifs_applier_user
|
||||||
|
, cifs_applier)
|
||||||
from .ntp_applier import ntp_applier
|
from .ntp_applier import ntp_applier
|
||||||
from util.windows import get_sid
|
from .envvar_applier import (
|
||||||
|
envvar_applier
|
||||||
|
, envvar_applier_user
|
||||||
|
)
|
||||||
|
from .scripts_applier import (
|
||||||
|
scripts_applier
|
||||||
|
, scripts_applier_user
|
||||||
|
)
|
||||||
|
|
||||||
|
from .file_applier import (
|
||||||
|
file_applier
|
||||||
|
, file_applier_user
|
||||||
|
)
|
||||||
|
|
||||||
|
from .ini_applier import (
|
||||||
|
ini_applier
|
||||||
|
, ini_applier_user
|
||||||
|
)
|
||||||
|
|
||||||
|
from .kde_applier import (
|
||||||
|
kde_applier
|
||||||
|
, kde_applier_user
|
||||||
|
)
|
||||||
|
|
||||||
|
from .networkshare_applier import networkshare_applier
|
||||||
|
from .yandex_browser_applier import yandex_browser_applier
|
||||||
|
|
||||||
|
from util.sid import get_sid
|
||||||
from util.users import (
|
from util.users import (
|
||||||
is_root,
|
is_root,
|
||||||
get_process_user,
|
get_process_user,
|
||||||
username_match_uid,
|
username_match_uid,
|
||||||
with_privileges
|
|
||||||
)
|
)
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
|
from util.system import with_privileges
|
||||||
|
|
||||||
|
|
||||||
def determine_username(username=None):
|
def determine_username(username=None):
|
||||||
@@ -79,6 +109,18 @@ def determine_username(username=None):
|
|||||||
|
|
||||||
return name
|
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:
|
class frontend_manager:
|
||||||
'''
|
'''
|
||||||
The frontend_manager class decides when and how to run appliers
|
The frontend_manager class decides when and how to run appliers
|
||||||
@@ -86,32 +128,54 @@ class frontend_manager:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, username, is_machine):
|
def __init__(self, username, is_machine):
|
||||||
self.storage = registry_factory('registry')
|
|
||||||
self.username = determine_username(username)
|
self.username = determine_username(username)
|
||||||
|
self.storage = registry_factory('dconf', username=self.username)
|
||||||
self.is_machine = is_machine
|
self.is_machine = is_machine
|
||||||
self.process_uname = get_process_user()
|
self.process_uname = get_process_user()
|
||||||
self.sid = get_sid(self.storage.get_info('domain'), self.username, is_machine)
|
self.sid = get_sid(self.storage.get_info('domain'), self.username, is_machine)
|
||||||
|
self.file_cache = fs_file_cache('file_cache', self.username)
|
||||||
|
|
||||||
self.machine_appliers = dict()
|
self.machine_appliers = dict()
|
||||||
|
self.user_appliers = dict()
|
||||||
|
if is_machine:
|
||||||
|
self._init_machine_appliers()
|
||||||
|
else:
|
||||||
|
self._init_user_appliers()
|
||||||
|
|
||||||
|
def _init_machine_appliers(self):
|
||||||
self.machine_appliers['control'] = control_applier(self.storage)
|
self.machine_appliers['control'] = control_applier(self.storage)
|
||||||
self.machine_appliers['polkit'] = polkit_applier(self.storage)
|
self.machine_appliers['polkit'] = polkit_applier(self.storage)
|
||||||
self.machine_appliers['systemd'] = systemd_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['firefox'] = firefox_applier(self.storage, self.sid, self.username)
|
||||||
self.machine_appliers['chromium'] = chromium_applier(self.storage, self.sid, self.username)
|
self.machine_appliers['chromium'] = chromium_applier(self.storage, self.sid, self.username)
|
||||||
|
self.machine_appliers['yandex_browser'] = yandex_browser_applier(self.storage, self.sid, self.username)
|
||||||
self.machine_appliers['shortcuts'] = shortcut_applier(self.storage)
|
self.machine_appliers['shortcuts'] = shortcut_applier(self.storage)
|
||||||
self.machine_appliers['gsettings'] = gsettings_applier(self.storage)
|
self.machine_appliers['gsettings'] = gsettings_applier(self.storage, self.file_cache)
|
||||||
|
try:
|
||||||
|
self.machine_appliers['cifs'] = cifs_applier(self.storage, self.sid)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['applier_name'] = 'cifs'
|
||||||
|
logdata['msg'] = str(exc)
|
||||||
|
log('E24', logdata)
|
||||||
self.machine_appliers['cups'] = cups_applier(self.storage)
|
self.machine_appliers['cups'] = cups_applier(self.storage)
|
||||||
self.machine_appliers['firewall'] = firewall_applier(self.storage)
|
self.machine_appliers['firewall'] = firewall_applier(self.storage)
|
||||||
self.machine_appliers['folders'] = folder_applier(self.storage, self.sid)
|
self.machine_appliers['folders'] = folder_applier(self.storage, self.sid)
|
||||||
self.machine_appliers['package'] = package_applier(self.storage)
|
self.machine_appliers['package'] = package_applier(self.storage)
|
||||||
self.machine_appliers['ntp'] = ntp_applier(self.storage)
|
self.machine_appliers['ntp'] = ntp_applier(self.storage)
|
||||||
|
self.machine_appliers['envvar'] = envvar_applier(self.storage, self.sid)
|
||||||
|
self.machine_appliers['networkshare'] = networkshare_applier(self.storage, self.sid)
|
||||||
|
self.machine_appliers['scripts'] = scripts_applier(self.storage, self.sid)
|
||||||
|
self.machine_appliers['files'] = file_applier(self.storage, self.file_cache, self.sid)
|
||||||
|
self.machine_appliers['ini'] = ini_applier(self.storage, self.sid)
|
||||||
|
self.machine_appliers['kde'] = kde_applier(self.storage)
|
||||||
|
|
||||||
|
def _init_user_appliers(self):
|
||||||
# User appliers are expected to work with user-writable
|
# User appliers are expected to work with user-writable
|
||||||
# files and settings, mostly in $HOME.
|
# files and settings, mostly in $HOME.
|
||||||
self.user_appliers = dict()
|
|
||||||
self.user_appliers['shortcuts'] = shortcut_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['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['folders'] = folder_applier_user(self.storage, self.sid, self.username)
|
||||||
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['gsettings'] = gsettings_applier_user(self.storage, self.file_cache, self.sid, self.username)
|
||||||
try:
|
try:
|
||||||
self.user_appliers['cifs'] = cifs_applier_user(self.storage, self.sid, self.username)
|
self.user_appliers['cifs'] = cifs_applier_user(self.storage, self.sid, self.username)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -121,6 +185,12 @@ class frontend_manager:
|
|||||||
log('E25', logdata)
|
log('E25', logdata)
|
||||||
self.user_appliers['package'] = package_applier_user(self.storage, self.sid, self.username)
|
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['polkit'] = polkit_applier_user(self.storage, self.sid, self.username)
|
||||||
|
self.user_appliers['envvar'] = envvar_applier_user(self.storage, self.sid, self.username)
|
||||||
|
self.user_appliers['networkshare'] = networkshare_applier(self.storage, self.sid, self.username)
|
||||||
|
self.user_appliers['scripts'] = scripts_applier_user(self.storage, self.sid, self.username)
|
||||||
|
self.user_appliers['files'] = file_applier_user(self.storage, self.file_cache, self.sid, self.username)
|
||||||
|
self.user_appliers['ini'] = ini_applier_user(self.storage, self.sid, self.username)
|
||||||
|
self.user_appliers['kde'] = kde_applier_user(self.storage, self.sid, self.username, self.file_cache)
|
||||||
|
|
||||||
def machine_apply(self):
|
def machine_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -130,6 +200,7 @@ class frontend_manager:
|
|||||||
log('E13')
|
log('E13')
|
||||||
return
|
return
|
||||||
log('D16')
|
log('D16')
|
||||||
|
|
||||||
for applier_name, applier_object in self.machine_appliers.items():
|
for applier_name, applier_object in self.machine_appliers.items():
|
||||||
try:
|
try:
|
||||||
applier_object.apply()
|
applier_object.apply()
|
||||||
@@ -154,12 +225,12 @@ class frontend_manager:
|
|||||||
log('E19', logdata)
|
log('E19', logdata)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with_privileges(self.username, applier_object.user_context_apply)
|
with_privileges(self.username, lambda: apply_user_context(self.user_appliers))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['applier'] = applier_name
|
logdata['username'] = self.username
|
||||||
logdata['exception'] = str(exc)
|
logdata['exception'] = str(exc)
|
||||||
log('E20', logdata)
|
log('E30', logdata)
|
||||||
else:
|
else:
|
||||||
for applier_name, applier_object in self.user_appliers.items():
|
for applier_name, applier_object in self.user_appliers.items():
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
from util.exceptions import NotUNCPathError
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -29,62 +29,125 @@ from gi.repository import (
|
|||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
|
, check_windows_mapping_enabled
|
||||||
)
|
)
|
||||||
from .appliers.gsettings import (
|
from .appliers.gsettings import (
|
||||||
system_gsetting,
|
system_gsettings,
|
||||||
user_gsetting
|
user_gsettings
|
||||||
)
|
)
|
||||||
from util.logging import slogm
|
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):
|
class gsettings_applier(applier_frontend):
|
||||||
__module_name = 'GSettingsApplier'
|
__module_name = 'GSettingsApplier'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
|
__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'
|
__global_schema = '/usr/share/glib-2.0/schemas'
|
||||||
__windows_settings = dict()
|
__override_priority_file = 'zzz_policy.gschema.override'
|
||||||
|
__override_old_file = '0_policy.gschema.override'
|
||||||
|
|
||||||
def __init__(self, storage):
|
|
||||||
|
def __init__(self, storage, file_cache):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
|
self.file_cache = file_cache
|
||||||
gsettings_filter = '{}%'.format(self.__registry_branch)
|
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_keys = self.storage.filter_hklm_entries(gsettings_filter)
|
||||||
self.gsettings = list()
|
self.gsettings_locks = self.storage.filter_hklm_entries(gsettings_locks_filter)
|
||||||
self.override_file = os.path.join(self.__global_schema, '0_policy.gschema.override')
|
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.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, 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):
|
def run(self):
|
||||||
|
# Compatility cleanup of old settings
|
||||||
|
if os.path.exists(self.override_old_file):
|
||||||
|
os.remove(self.override_old_file)
|
||||||
|
|
||||||
# Cleanup settings from previous run
|
# Cleanup settings from previous run
|
||||||
if os.path.exists(self.override_file):
|
if os.path.exists(self.override_file):
|
||||||
logging.debug(slogm('Removing GSettings policy file from previous run'))
|
log('D82')
|
||||||
os.remove(self.override_file)
|
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
|
# Calculate all configured gsettings
|
||||||
for setting in self.gsettings_keys:
|
for setting in self.gsettings_keys:
|
||||||
valuename = setting.hive_key.rpartition('\\')[2]
|
helper = None
|
||||||
|
valuename = setting.hive_key.rpartition('/')[2]
|
||||||
rp = valuename.rpartition('.')
|
rp = valuename.rpartition('.')
|
||||||
schema = rp[0]
|
schema = rp[0]
|
||||||
path = rp[2]
|
path = rp[2]
|
||||||
self.gsettings.append(system_gsetting(schema, path, setting.data))
|
data = setting.data
|
||||||
|
lock = bool(self.locks[valuename]) if valuename in self.locks else None
|
||||||
|
if setting.hive_key.lower() == self.__wallpaper_entry.lower():
|
||||||
|
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
|
# Create GSettings policy with highest available priority
|
||||||
for gsetting in self.gsettings:
|
self.gsettings.apply()
|
||||||
gsetting.apply()
|
|
||||||
|
|
||||||
# Recompile GSettings schemas with overrides
|
# Recompile GSettings schemas with overrides
|
||||||
try:
|
try:
|
||||||
proc = subprocess.run(args=['/usr/bin/glib-compile-schemas', self.__global_schema], capture_output=True, check=True)
|
proc = subprocess.run(args=['/usr/bin/glib-compile-schemas', self.__global_schema], capture_output=True, check=True)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.debug(slogm('Error recompiling global GSettings schemas'))
|
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):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running GSettings applier for machine'))
|
log('D80')
|
||||||
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('GSettings applier for machine will not be started'))
|
log('D81')
|
||||||
|
|
||||||
class GSettingsMapping:
|
class GSettingsMapping:
|
||||||
def __init__(self, hive_key, gsettings_schema, gsettings_key):
|
def __init__(self, hive_key, gsettings_schema, gsettings_key):
|
||||||
@@ -102,7 +165,7 @@ class GSettingsMapping:
|
|||||||
logdata['hive_key'] = self.hive_key
|
logdata['hive_key'] = self.hive_key
|
||||||
logdata['gsettings_schema'] = self.gsettings_schema
|
logdata['gsettings_schema'] = self.gsettings_schema
|
||||||
logdata['gsettings_key'] = self.gsettings_key
|
logdata['gsettings_key'] = self.gsettings_key
|
||||||
logging.warning(slogm('Unable to resolve GSettings parameter {}.{}'.format(self.gsettings_schema, self.gsettings_key)))
|
log('W6', logdata)
|
||||||
|
|
||||||
def preg2gsettings(self):
|
def preg2gsettings(self):
|
||||||
'''
|
'''
|
||||||
@@ -119,43 +182,47 @@ class GSettingsMapping:
|
|||||||
|
|
||||||
class gsettings_applier_user(applier_frontend):
|
class gsettings_applier_user(applier_frontend):
|
||||||
__module_name = 'GSettingsApplierUser'
|
__module_name = 'GSettingsApplierUser'
|
||||||
__module_experimental = True
|
__module_experimental = False
|
||||||
__module_enabled = False
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings'
|
__registry_branch = 'Software\\BaseALT\\Policies\\gsettings\\'
|
||||||
|
__wallpaper_entry = 'Software/BaseALT/Policies/gsettings/org.mate.background.picture-filename'
|
||||||
|
__vino_authentication_methods_entry = 'Software/BaseALT/Policies/gsettings/org.gnome.Vino.authentication-methods'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, file_cache, sid, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
|
self.file_cache = file_cache
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.username = username
|
self.username = username
|
||||||
gsettings_filter = '{}%'.format(self.__registry_branch)
|
gsettings_filter = '{}%'.format(self.__registry_branch)
|
||||||
self.gsettings_keys = self.storage.filter_hkcu_entries(self.sid, gsettings_filter)
|
self.gsettings_keys = self.storage.filter_hkcu_entries(self.sid, gsettings_filter)
|
||||||
self.gsettings = list()
|
self.gsettings = user_gsettings()
|
||||||
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_enabled)
|
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 = dict()
|
||||||
self.windows_settings = list()
|
self.windows_settings = list()
|
||||||
mapping = [
|
mapping = [
|
||||||
# Disable or enable screen saver
|
# Disable or enable screen saver
|
||||||
GSettingsMapping(
|
GSettingsMapping(
|
||||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaveActive'
|
'Software/Policies/Microsoft/Windows/Control Panel/Desktop/ScreenSaveActive'
|
||||||
, 'org.mate.screensaver'
|
, 'org.mate.screensaver'
|
||||||
, 'idle-activation-enabled'
|
, 'idle-activation-enabled'
|
||||||
)
|
)
|
||||||
# Timeout in seconds for screen saver activation. The value of zero effectively disables screensaver start
|
# Timeout in seconds for screen saver activation. The value of zero effectively disables screensaver start
|
||||||
, GSettingsMapping(
|
, GSettingsMapping(
|
||||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaveTimeOut'
|
'Software/Policies/Microsoft/Windows/Control Panel/Desktop/ScreenSaveTimeOut'
|
||||||
, 'org.mate.session'
|
, 'org.mate.session'
|
||||||
, 'idle-delay'
|
, 'idle-delay'
|
||||||
)
|
)
|
||||||
# Enable or disable password protection for screen saver
|
# Enable or disable password protection for screen saver
|
||||||
, GSettingsMapping(
|
, GSettingsMapping(
|
||||||
'Software\\Policies\\Microsoft\\Windows\\Control Panel\\Desktop\\ScreenSaverIsSecure'
|
'Software/Policies/Microsoft/Windows/Control Panel/Desktop/ScreenSaverIsSecure'
|
||||||
, 'org.mate.screensaver'
|
, 'org.mate.screensaver'
|
||||||
, 'lock-enabled'
|
, 'lock-enabled'
|
||||||
)
|
)
|
||||||
# Specify image which will be used as a wallpaper
|
# Specify image which will be used as a wallpaper
|
||||||
, GSettingsMapping(
|
, GSettingsMapping(
|
||||||
'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\\Wallpaper'
|
'Software/Microsoft/Windows/CurrentVersion/Policies/System/Wallpaper'
|
||||||
, 'org.mate.background'
|
, 'org.mate.background'
|
||||||
, 'picture-filename'
|
, 'picture-filename'
|
||||||
)
|
)
|
||||||
@@ -166,42 +233,64 @@ class gsettings_applier_user(applier_frontend):
|
|||||||
self.__windows_settings[element.hive_key] = element
|
self.__windows_settings[element.hive_key] = element
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def windows_mapping_append(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))
|
|
||||||
|
|
||||||
os.environ['DBUS_SESSION_BUS_ADDRESS'] = 'unix:path=/run/user/{}/bus'.format(pwd.getpwnam(self.username).pw_uid)
|
|
||||||
|
|
||||||
for setting_key in self.__windows_settings.keys():
|
for setting_key in self.__windows_settings.keys():
|
||||||
logging.debug('Checking for GSettings mapping {}'.format(setting_key))
|
|
||||||
value = self.storage.get_hkcu_entry(self.sid, setting_key)
|
value = self.storage.get_hkcu_entry(self.sid, setting_key)
|
||||||
if value:
|
if value:
|
||||||
logging.debug('Found GSettings mapping {} to {}'.format(setting_key, value.data))
|
logdata = dict()
|
||||||
|
logdata['setting_key'] = setting_key
|
||||||
|
logdata['value.data'] = value.data
|
||||||
|
log('D86', logdata)
|
||||||
mapping = self.__windows_settings[setting_key]
|
mapping = self.__windows_settings[setting_key]
|
||||||
self.gsettings.append(user_gsetting(mapping.gsettings_schema, mapping.gsettings_key, value.data))
|
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):
|
||||||
|
if self.__windows_mapping_enabled:
|
||||||
|
log('D83')
|
||||||
|
self.windows_mapping_append()
|
||||||
else:
|
else:
|
||||||
logging.debug('GSettings mapping of {} to {} not found'.format(setting_key, value.data))
|
log('D84')
|
||||||
|
|
||||||
for gsetting in self.gsettings:
|
# Calculate all configured gsettings
|
||||||
logging.debug('Applying setting {}/{}'.format(gsetting.schema, gsetting.path))
|
for setting in self.gsettings_keys:
|
||||||
gsetting.apply()
|
valuename = setting.hive_key.rpartition('/')[2]
|
||||||
|
rp = valuename.rpartition('.')
|
||||||
|
schema = rp[0]
|
||||||
|
path = rp[2]
|
||||||
|
data = setting.data
|
||||||
|
helper = self.uri_fetch_helper if setting.hive_key.lower() == self.__wallpaper_entry.lower() else None
|
||||||
|
if setting.hive_key.lower() == self.__vino_authentication_methods_entry.lower():
|
||||||
|
data = [setting.data]
|
||||||
|
self.gsettings.append(schema, path, data, helper)
|
||||||
|
|
||||||
del os.environ['DBUS_SESSION_BUS_ADDRESS']
|
# Create GSettings policy with highest available priority
|
||||||
|
self.gsettings.apply()
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running GSettings applier for user in user context'))
|
log('D87')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('GSettings applier for user in user context will not be started'))
|
log('D88')
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
'''
|
# Cache files on remote locations
|
||||||
Not implemented because there is no point of doing so.
|
try:
|
||||||
'''
|
entry = self.__wallpaper_entry
|
||||||
pass
|
filter_result = self.storage.get_hkcu_entry(self.sid, entry)
|
||||||
|
if filter_result and filter_result.data:
|
||||||
|
self.file_cache.store(filter_result.data)
|
||||||
|
except NotUNCPathError:
|
||||||
|
...
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['exception'] = str(exc)
|
||||||
|
log('E50', logdata)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
78
gpoa/frontend/ini_applier.py
Normal file
78
gpoa/frontend/ini_applier.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 .appliers.ini_file import Ini_file
|
||||||
|
from .applier_frontend import (
|
||||||
|
applier_frontend
|
||||||
|
, check_enabled
|
||||||
|
)
|
||||||
|
from util.logging import log
|
||||||
|
|
||||||
|
class ini_applier(applier_frontend):
|
||||||
|
__module_name = 'InifilesApplier'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
|
||||||
|
def __init__(self, storage, sid):
|
||||||
|
self.storage = storage
|
||||||
|
self.sid = sid
|
||||||
|
self.inifiles_info = self.storage.get_ini(self.sid)
|
||||||
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for inifile in self.inifiles_info:
|
||||||
|
Ini_file(inifile)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D171')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D172')
|
||||||
|
|
||||||
|
class ini_applier_user(applier_frontend):
|
||||||
|
__module_name = 'InifilesApplierUser'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
|
||||||
|
def __init__(self, storage, sid, username):
|
||||||
|
self.sid = sid
|
||||||
|
self.username = username
|
||||||
|
self.storage = storage
|
||||||
|
self.inifiles_info = self.storage.get_ini(self.sid)
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
self.storage
|
||||||
|
, self.__module_name
|
||||||
|
, self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for inifile in self.inifiles_info:
|
||||||
|
Ini_file(inifile, self.username)
|
||||||
|
|
||||||
|
def admin_context_apply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def user_context_apply(self):
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D173')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D174')
|
||||||
292
gpoa/frontend/kde_applier.py
Normal file
292
gpoa/frontend/kde_applier.py
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2024 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 util.logging import log
|
||||||
|
from util.util import get_homedir
|
||||||
|
from util.exceptions import NotUNCPathError
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import dbus
|
||||||
|
|
||||||
|
class kde_applier(applier_frontend):
|
||||||
|
__module_name = 'KdeApplier'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
__hklm_branch = 'Software\\BaseALT\\Policies\\KDE\\'
|
||||||
|
__hklm_lock_branch = 'Software\\BaseALT\\Policies\\KDELocks\\'
|
||||||
|
|
||||||
|
def __init__(self, storage):
|
||||||
|
self.storage = storage
|
||||||
|
self.locks_dict = {}
|
||||||
|
self.locks_data_dict = {}
|
||||||
|
self.all_kde_settings = {}
|
||||||
|
kde_filter = '{}%'.format(self.__hklm_branch)
|
||||||
|
locks_filter = '{}%'.format(self.__hklm_lock_branch)
|
||||||
|
self.locks_settings = self.storage.filter_hklm_entries(locks_filter)
|
||||||
|
self.kde_settings = self.storage.filter_hklm_entries(kde_filter)
|
||||||
|
self.all_kde_settings = {}
|
||||||
|
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
self.storage,
|
||||||
|
self.__module_name,
|
||||||
|
self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D198')
|
||||||
|
create_dict(self.kde_settings, self.all_kde_settings, self.locks_settings, self.locks_dict)
|
||||||
|
apply(self.all_kde_settings, self.locks_dict)
|
||||||
|
else:
|
||||||
|
log('D199')
|
||||||
|
|
||||||
|
class kde_applier_user(applier_frontend):
|
||||||
|
__module_name = 'KdeApplierUser'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
__hkcu_branch = 'Software\\BaseALT\\Policies\\KDE\\'
|
||||||
|
__hkcu_lock_branch = 'Software\\BaseALT\\Policies\\KDELocks\\'
|
||||||
|
|
||||||
|
def __init__(self, storage, sid=None, username=None, file_cache = None):
|
||||||
|
self.storage = storage
|
||||||
|
self.username = username
|
||||||
|
self.sid = sid
|
||||||
|
self.file_cache = file_cache
|
||||||
|
self.locks_dict = {}
|
||||||
|
self.locks_data_dict = {}
|
||||||
|
self.all_kde_settings = {}
|
||||||
|
kde_filter = '{}%'.format(self.__hkcu_branch)
|
||||||
|
locks_filter = '{}%'.format(self.__hkcu_lock_branch)
|
||||||
|
self.locks_settings = self.storage.filter_hkcu_entries(self.sid, locks_filter)
|
||||||
|
self.kde_settings = self.storage.filter_hkcu_entries(self.sid, kde_filter)
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
self.storage,
|
||||||
|
self.__module_name,
|
||||||
|
self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
def admin_context_apply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def user_context_apply(self):
|
||||||
|
'''
|
||||||
|
Change settings applied in user context
|
||||||
|
'''
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D200')
|
||||||
|
create_dict(self.kde_settings, self.all_kde_settings, self.locks_settings, self.locks_dict, self.file_cache, self.username)
|
||||||
|
apply(self.all_kde_settings, self.locks_dict, self.username)
|
||||||
|
else:
|
||||||
|
log('D201')
|
||||||
|
|
||||||
|
def create_dict(kde_settings, all_kde_settings, locks_settings, locks_dict, file_cache = None, username = None):
|
||||||
|
for locks in locks_settings:
|
||||||
|
locks_dict[locks.valuename] = locks.data
|
||||||
|
for setting in kde_settings:
|
||||||
|
try:
|
||||||
|
file_name, section, value = setting.keyname.split("/")[-2], setting.keyname.split("/")[-1], setting.valuename
|
||||||
|
data = setting.data
|
||||||
|
if file_name == 'wallpaper':
|
||||||
|
apply_for_wallpaper(data, file_cache, username)
|
||||||
|
else:
|
||||||
|
if file_name not in all_kde_settings:
|
||||||
|
all_kde_settings[file_name] = {}
|
||||||
|
if section not in all_kde_settings[file_name]:
|
||||||
|
all_kde_settings[file_name][section] = {}
|
||||||
|
all_kde_settings[file_name][section][value] = data
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['file_name'] = file_name
|
||||||
|
logdata['section'] = section
|
||||||
|
logdata['value'] = value
|
||||||
|
logdata['data'] = data
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W16', logdata)
|
||||||
|
|
||||||
|
def apply(all_kde_settings, locks_dict, username = None):
|
||||||
|
logdata = dict()
|
||||||
|
if username is None:
|
||||||
|
system_path_settings = '/etc/xdg/'
|
||||||
|
system_files = [
|
||||||
|
"baloofilerc",
|
||||||
|
"kcminputrc",
|
||||||
|
"kded_device_automounterrc",
|
||||||
|
"kdeglobals",
|
||||||
|
"ksplashrc",
|
||||||
|
"kwinrc",
|
||||||
|
"plasma-localerc",
|
||||||
|
"plasmarc",
|
||||||
|
"powermanagementprofilesrc"
|
||||||
|
]
|
||||||
|
for file in system_files:
|
||||||
|
file_to_remove = f'{system_path_settings}{file}'
|
||||||
|
if os.path.exists(file_to_remove):
|
||||||
|
os.remove(file_to_remove)
|
||||||
|
for file_name, sections in all_kde_settings.items():
|
||||||
|
file_path = f'{system_path_settings}{file_name}'
|
||||||
|
with open(file_path, 'w') as file:
|
||||||
|
for section, keys in sections.items():
|
||||||
|
section = section.replace(')(', '][')
|
||||||
|
file.write(f'[{section}]\n')
|
||||||
|
for key, value in keys.items():
|
||||||
|
lock = f"{file_name}.{section}.{key}".replace('][', ')(')
|
||||||
|
if lock in locks_dict and locks_dict[lock] == 1:
|
||||||
|
file.write(f'{key}[$i]={value}\n')
|
||||||
|
else:
|
||||||
|
file.write(f'{key}={value}\n')
|
||||||
|
file.write('\n')
|
||||||
|
else:
|
||||||
|
for file_name, sections in all_kde_settings.items():
|
||||||
|
path = f'{get_homedir(username)}/.config/{file_name}'
|
||||||
|
if not os.path.exists(path):
|
||||||
|
open(path, 'a').close()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
for section, keys in sections.items():
|
||||||
|
for key, value in keys.items():
|
||||||
|
lock = f"{file_name}.{section}.{key}"
|
||||||
|
if lock in locks_dict and locks_dict[lock] == 1:
|
||||||
|
command = [
|
||||||
|
'kwriteconfig5',
|
||||||
|
'--file', file_name,
|
||||||
|
'--group', section,
|
||||||
|
'--key', key +'/$i/',
|
||||||
|
'--type', 'string',
|
||||||
|
value
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
command = [
|
||||||
|
'kwriteconfig5',
|
||||||
|
'--file', file_name,
|
||||||
|
'--group', section,
|
||||||
|
'--key', key,
|
||||||
|
'--type', 'string',
|
||||||
|
value
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
clear_locks_settings(username, file_name, key)
|
||||||
|
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
except:
|
||||||
|
logdata['command'] = command
|
||||||
|
log('W22', logdata)
|
||||||
|
new_content = []
|
||||||
|
file_path = f'{get_homedir(username)}/.config/{file_name}'
|
||||||
|
try:
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
for line in file:
|
||||||
|
line = line.replace('/$i/', '[$i]').replace(')(', '][')
|
||||||
|
new_content.append(line)
|
||||||
|
with open(file_path, 'w') as file:
|
||||||
|
file.writelines(new_content)
|
||||||
|
logdata['file'] = file_name
|
||||||
|
log('D202', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W19', logdata)
|
||||||
|
|
||||||
|
def clear_locks_settings(username, file_name, key):
|
||||||
|
'''
|
||||||
|
Method to remove old locked settings
|
||||||
|
'''
|
||||||
|
file_path = f'{get_homedir(username)}/.config/{file_name}'
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
with open(file_path, 'w') as file:
|
||||||
|
for line in lines:
|
||||||
|
if f'{key}[$i]=' not in line:
|
||||||
|
file.write(line)
|
||||||
|
for line in lines:
|
||||||
|
if f'{key}[$i]=' in line:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['line'] = line.strip()
|
||||||
|
log('I10', logdata)
|
||||||
|
|
||||||
|
def apply_for_wallpaper(data, file_cache, username):
|
||||||
|
'''
|
||||||
|
Method to change wallpaper
|
||||||
|
'''
|
||||||
|
logdata = dict()
|
||||||
|
path_to_wallpaper = f'{get_homedir(username)}/.config/plasma-org.kde.plasma.desktop-appletsrc'
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
file_cache.store(data)
|
||||||
|
data = file_cache.get(data)
|
||||||
|
except NotUNCPathError:
|
||||||
|
data = data
|
||||||
|
os.environ["XDG_DATA_DIRS"] = "/usr/share/kf5:"
|
||||||
|
#Variable for system detection of directories before files with .colors extension
|
||||||
|
os.environ["DISPLAY"] = ":0"
|
||||||
|
#Variable for command execution plasma-apply-colorscheme
|
||||||
|
os.environ["XDG_RUNTIME_DIR"] = f"/run/user/{os.getuid()}"
|
||||||
|
os.environ["DBUS_SESSION_BUS_ADDRESS"] = f"unix:path=/run/user/{os.getuid()}/bus"#plasma-apply-wallpaperimage
|
||||||
|
os.environ["PATH"] = "/usr/lib/kf5/bin:"
|
||||||
|
#environment variable for accessing binary files without hard links
|
||||||
|
if os.path.isfile(path_to_wallpaper):
|
||||||
|
id_desktop = get_id_desktop(path_to_wallpaper)
|
||||||
|
command = [
|
||||||
|
'kwriteconfig5',
|
||||||
|
'--file', 'plasma-org.kde.plasma.desktop-appletsrc',
|
||||||
|
'--group', 'Containments',
|
||||||
|
'--group', id_desktop,
|
||||||
|
'--group', 'Wallpaper',
|
||||||
|
'--group', 'org.kde.image',
|
||||||
|
'--group', 'General',
|
||||||
|
'--key', 'Image',
|
||||||
|
'--type', 'string',
|
||||||
|
data
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
except:
|
||||||
|
logdata['command'] = command
|
||||||
|
log('E68', logdata)
|
||||||
|
try:
|
||||||
|
session_bus = dbus.SessionBus()
|
||||||
|
plasma_shell = session_bus.get_object('org.kde.plasmashell', '/PlasmaShell', introspect='org.kde.PlasmaShell')
|
||||||
|
plasma_shell_iface = dbus.Interface(plasma_shell, 'org.kde.PlasmaShell')
|
||||||
|
plasma_shell_iface.refreshCurrentShell()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
logdata['file'] = path_to_wallpaper
|
||||||
|
log('W21', logdata)
|
||||||
|
except OSError as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('W17', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E67', logdata)
|
||||||
|
|
||||||
|
def get_id_desktop(path_to_wallpaper):
|
||||||
|
'''
|
||||||
|
Method for getting desktop id. It is currently accepted that this number is one of the sections in the configuration file.
|
||||||
|
'''
|
||||||
|
pattern = r'\[Containments\]\[(\d+)\][^\[]*activityId=([^\s]+)'
|
||||||
|
try:
|
||||||
|
with open(path_to_wallpaper, 'r') as file:
|
||||||
|
file_content = file.read()
|
||||||
|
match = re.search(pattern, file_content)
|
||||||
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
return None
|
||||||
58
gpoa/frontend/networkshare_applier.py
Normal file
58
gpoa/frontend/networkshare_applier.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 .appliers.netshare import Networkshare
|
||||||
|
from .applier_frontend import (
|
||||||
|
applier_frontend
|
||||||
|
, check_enabled
|
||||||
|
)
|
||||||
|
from util.logging import log
|
||||||
|
|
||||||
|
class networkshare_applier(applier_frontend):
|
||||||
|
__module_name = 'NetworksharesApplier'
|
||||||
|
__module_name_user = 'NetworksharesApplierUser'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
|
||||||
|
def __init__(self, storage, sid, username = None):
|
||||||
|
self.storage = storage
|
||||||
|
self.sid = sid
|
||||||
|
self.username = username
|
||||||
|
self.networkshare_info = self.storage.get_networkshare(self.sid)
|
||||||
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
self.__module_enabled_user = check_enabled(self.storage, self.__module_name_user, self.__module_experimental)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
for networkshare in self.networkshare_info:
|
||||||
|
Networkshare(networkshare, self.username)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D187')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D181')
|
||||||
|
def admin_context_apply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def user_context_apply(self):
|
||||||
|
if self.__module_enabled_user:
|
||||||
|
log('D188')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D189')
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ from .applier_frontend import (
|
|||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from util.logging import slogm
|
from util.logging import log
|
||||||
|
|
||||||
|
|
||||||
class NTPServerType(Enum):
|
class NTPServerType(Enum):
|
||||||
@@ -77,20 +77,24 @@ class ntp_applier(applier_frontend):
|
|||||||
srv = None
|
srv = None
|
||||||
if server:
|
if server:
|
||||||
srv = server.data.rpartition(',')[0]
|
srv = server.data.rpartition(',')[0]
|
||||||
logging.debug(slogm('NTP server is configured to {}'.format(srv)))
|
logdata = dict()
|
||||||
|
logdata['srv'] = srv
|
||||||
|
log('D122', logdata)
|
||||||
|
|
||||||
start_command = ['/usr/bin/systemctl', 'start', 'chronyd']
|
start_command = ['/usr/bin/systemctl', 'start', 'chronyd']
|
||||||
chrony_set_server = ['/usr/bin/chronyc', 'add', 'server', srv]
|
chrony_set_server = ['/usr/bin/chronyc', 'add', 'server', srv]
|
||||||
chrony_disconnect_all = ['/usr/bin/chronyc', 'offline']
|
chrony_disconnect_all = ['/usr/bin/chronyc', 'offline']
|
||||||
chrony_connect = ['/usr/bin/chronyc', 'online', srv]
|
chrony_connect = ['/usr/bin/chronyc', 'online', srv]
|
||||||
|
|
||||||
logging.debug(slogm('Starting Chrony daemon'))
|
log('D123')
|
||||||
|
|
||||||
proc = subprocess.Popen(start_command)
|
proc = subprocess.Popen(start_command)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
if srv:
|
if srv:
|
||||||
logging.debug(slogm('Setting reference NTP server to {}'.format(srv)))
|
logdata = dict()
|
||||||
|
logdata['srv'] = srv
|
||||||
|
log('D124', logdata)
|
||||||
|
|
||||||
proc = subprocess.Popen(chrony_disconnect_all)
|
proc = subprocess.Popen(chrony_disconnect_all)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
@@ -103,9 +107,7 @@ class ntp_applier(applier_frontend):
|
|||||||
|
|
||||||
def _stop_chrony_client(self):
|
def _stop_chrony_client(self):
|
||||||
stop_command = ['/usr/bin/systemctl', 'stop', 'chronyd']
|
stop_command = ['/usr/bin/systemctl', 'stop', 'chronyd']
|
||||||
|
log('D125')
|
||||||
logging.debug(slogm('Stopping Chrony daemon'))
|
|
||||||
|
|
||||||
proc = subprocess.Popen(stop_command)
|
proc = subprocess.Popen(stop_command)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
@@ -115,33 +117,38 @@ class ntp_applier(applier_frontend):
|
|||||||
ntp_server_enabled = self.storage.get_hklm_entry(self.ntp_server_enabled)
|
ntp_server_enabled = self.storage.get_hklm_entry(self.ntp_server_enabled)
|
||||||
ntp_client_enabled = self.storage.get_hklm_entry(self.ntp_client_enabled)
|
ntp_client_enabled = self.storage.get_hklm_entry(self.ntp_client_enabled)
|
||||||
|
|
||||||
|
if server_type and server_type.data:
|
||||||
if NTPServerType.NTP.value != server_type.data:
|
if NTPServerType.NTP.value != server_type.data:
|
||||||
logging.warning(slogm('Unsupported NTP server type: {}'.format(server_type)))
|
logdata = dict()
|
||||||
|
logdata['server_type'] = server_type
|
||||||
|
log('W10', logdata)
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Configuring NTP server...'))
|
log('D126')
|
||||||
if '1' == ntp_server_enabled.data:
|
if ntp_server_enabled:
|
||||||
logging.debug(slogm('NTP server is enabled'))
|
if '1' == ntp_server_enabled.data and server_address:
|
||||||
|
log('D127')
|
||||||
self._start_chrony_client(server_address)
|
self._start_chrony_client(server_address)
|
||||||
self._chrony_as_server()
|
self._chrony_as_server()
|
||||||
elif '0' == ntp_server_enabled.data:
|
elif '0' == ntp_server_enabled.data:
|
||||||
logging.debug(slogm('NTP server is disabled'))
|
log('D128')
|
||||||
self._chrony_as_client()
|
self._chrony_as_client()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('NTP server is not configured'))
|
log('D129')
|
||||||
|
|
||||||
|
elif ntp_client_enabled:
|
||||||
if '1' == ntp_client_enabled.data:
|
if '1' == ntp_client_enabled.data:
|
||||||
logging.debug(slogm('NTP client is enabled'))
|
log('D130')
|
||||||
self._start_chrony_client()
|
self._start_chrony_client()
|
||||||
elif '0' == ntp_client_enabled.data:
|
elif '0' == ntp_client_enabled.data:
|
||||||
logging.debug(slogm('NTP client is disabled'))
|
log('D131')
|
||||||
self._stop_chrony_client()
|
self._stop_chrony_client()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('NTP client is not configured'))
|
log('D132')
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running NTP applier for machine'))
|
log('D121')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('NTP applier for machine will not be started'))
|
log('D133')
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from util.logging import slogm
|
import subprocess
|
||||||
|
from util.logging import slogm, log
|
||||||
from util.rpm import (
|
from util.rpm import (
|
||||||
update
|
update
|
||||||
, install_rpm
|
, install_rpm
|
||||||
@@ -35,6 +36,7 @@ class package_applier(applier_frontend):
|
|||||||
__module_enabled = False
|
__module_enabled = False
|
||||||
__install_key_name = 'Install'
|
__install_key_name = 'Install'
|
||||||
__remove_key_name = 'Remove'
|
__remove_key_name = 'Remove'
|
||||||
|
__sync_key_name = 'Sync'
|
||||||
__hklm_branch = 'Software\\BaseALT\\Policies\\Packages'
|
__hklm_branch = 'Software\\BaseALT\\Policies\\Packages'
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
@@ -42,37 +44,48 @@ class package_applier(applier_frontend):
|
|||||||
|
|
||||||
install_branch = '{}\\{}%'.format(self.__hklm_branch, self.__install_key_name)
|
install_branch = '{}\\{}%'.format(self.__hklm_branch, self.__install_key_name)
|
||||||
remove_branch = '{}\\{}%'.format(self.__hklm_branch, self.__remove_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.install_packages_setting = self.storage.filter_hklm_entries(install_branch)
|
||||||
self.remove_packages_setting = self.storage.filter_hklm_entries(remove_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.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
, self.__module_experimental
|
, self.__module_experimental
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
for flag in self.sync_packages_setting:
|
||||||
update()
|
self.flagSync = bool(flag.data)
|
||||||
for package in self.install_packages_setting:
|
|
||||||
try:
|
|
||||||
install_rpm(package.data)
|
|
||||||
except Exception as exc:
|
|
||||||
logging.error(exc)
|
|
||||||
|
|
||||||
for package in self.remove_packages_setting:
|
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
||||||
|
if self.flagSync:
|
||||||
try:
|
try:
|
||||||
remove_rpm(package.data)
|
subprocess.check_call(self.fulcmd)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.error(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):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Package applier for machine'))
|
log('D138')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Package applier for machine will not be started'))
|
log('D139')
|
||||||
|
|
||||||
|
|
||||||
class package_applier_user(applier_frontend):
|
class package_applier_user(applier_frontend):
|
||||||
@@ -81,20 +94,31 @@ class package_applier_user(applier_frontend):
|
|||||||
__module_enabled = False
|
__module_enabled = False
|
||||||
__install_key_name = 'Install'
|
__install_key_name = 'Install'
|
||||||
__remove_key_name = 'Remove'
|
__remove_key_name = 'Remove'
|
||||||
|
__sync_key_name = 'Sync'
|
||||||
__hkcu_branch = 'Software\\BaseALT\\Policies\\Packages'
|
__hkcu_branch = 'Software\\BaseALT\\Policies\\Packages'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, sid, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.username = username
|
self.username = username
|
||||||
|
self.fulcmd = list()
|
||||||
|
self.fulcmd.append('/usr/libexec/gpupdate/pkcon_runner')
|
||||||
|
self.fulcmd.append('--user')
|
||||||
|
self.fulcmd.append(self.username)
|
||||||
|
self.fulcmd.append('--loglevel')
|
||||||
|
logger = logging.getLogger()
|
||||||
|
self.fulcmd.append(str(logger.level))
|
||||||
|
|
||||||
install_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__install_key_name)
|
install_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__install_key_name)
|
||||||
remove_branch = '{}\\{}%'.format(self.__hkcu_branch, self.__remove_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.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.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)
|
self.__module_enabled = check_enabled(self.storage, self.__module_name, self.__module_experimental)
|
||||||
|
|
||||||
def user_context_apply(self):
|
def user_context_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -103,19 +127,25 @@ class package_applier_user(applier_frontend):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
for flag in self.sync_packages_setting:
|
||||||
update()
|
if flag.data:
|
||||||
for package in self.install_packages_setting:
|
self.flagSync = bool(int(flag.data))
|
||||||
try:
|
|
||||||
install_rpm(package.data)
|
|
||||||
except Exception as exc:
|
|
||||||
logging.debug(exc)
|
|
||||||
|
|
||||||
for package in self.remove_packages_setting:
|
if 0 < self.install_packages_setting.count() or 0 < self.remove_packages_setting.count():
|
||||||
|
if self.flagSync:
|
||||||
try:
|
try:
|
||||||
remove_rpm(package.data)
|
subprocess.check_call(self.fulcmd)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.debug(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):
|
def admin_context_apply(self):
|
||||||
'''
|
'''
|
||||||
@@ -123,8 +153,8 @@ class package_applier_user(applier_frontend):
|
|||||||
which computer he uses to log into system.
|
which computer he uses to log into system.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Package applier for user in administrator context'))
|
log('D140')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Package applier for user in administrator context will not be started'))
|
log('D141')
|
||||||
|
|
||||||
|
|||||||
@@ -19,34 +19,74 @@
|
|||||||
from .applier_frontend import (
|
from .applier_frontend import (
|
||||||
applier_frontend
|
applier_frontend
|
||||||
, check_enabled
|
, check_enabled
|
||||||
|
, check_windows_mapping_enabled
|
||||||
)
|
)
|
||||||
from .appliers.polkit import polkit
|
from .appliers.polkit import polkit
|
||||||
from util.logging import slogm
|
from util.logging import log
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class polkit_applier(applier_frontend):
|
class polkit_applier(applier_frontend):
|
||||||
__module_name = 'PolkitApplier'
|
__module_name = 'PolkitApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__deny_all = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
__deny_all_win = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
||||||
|
__registry_branch = 'Software\\BaseALT\\Policies\\Polkit\\'
|
||||||
|
__registry_locks_branch = 'Software\\BaseALT\\Policies\\PolkitLocks\\'
|
||||||
__polkit_map = {
|
__polkit_map = {
|
||||||
__deny_all: ['49-gpoa_disk_permissions', { 'Deny_All': 0 }]
|
__deny_all_win: ['49-gpoa_disk_permissions', { 'Deny_All': 0 }],
|
||||||
|
__registry_branch : ['49-alt_group_policy_permissions', {}],
|
||||||
|
__registry_locks_branch : ['47-alt_group_policy_permissions', {}]
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
deny_all = storage.filter_hklm_entries(self.__deny_all).first()
|
deny_all_win = None
|
||||||
|
if check_windows_mapping_enabled(self.storage):
|
||||||
|
deny_all_win = storage.filter_hklm_entries(self.__deny_all_win).first()
|
||||||
# Deny_All hook: initialize defaults
|
# Deny_All hook: initialize defaults
|
||||||
template_file = self.__polkit_map[self.__deny_all][0]
|
polkit_filter = '{}%'.format(self.__registry_branch)
|
||||||
template_vars = self.__polkit_map[self.__deny_all][1]
|
polkit_locks_filter = '{}%'.format(self.__registry_locks_branch)
|
||||||
if deny_all:
|
self.polkit_keys = self.storage.filter_hklm_entries(polkit_filter)
|
||||||
logging.debug(slogm('Deny_All setting found: {}'.format(deny_all.data)))
|
self.polkit_locks = self.storage.filter_hklm_entries(polkit_locks_filter)
|
||||||
self.__polkit_map[self.__deny_all][1]['Deny_All'] = deny_all.data
|
template_file = self.__polkit_map[self.__deny_all_win][0]
|
||||||
|
template_vars = self.__polkit_map[self.__deny_all_win][1]
|
||||||
|
template_file_all = self.__polkit_map[self.__registry_branch][0]
|
||||||
|
template_vars_all = self.__polkit_map[self.__registry_branch][1]
|
||||||
|
template_file_all_lock = self.__polkit_map[self.__registry_locks_branch][0]
|
||||||
|
template_vars_all_lock = self.__polkit_map[self.__registry_locks_branch][1]
|
||||||
|
locks = list()
|
||||||
|
for lock in self.polkit_locks:
|
||||||
|
if bool(int(lock.data)):
|
||||||
|
locks.append(lock.valuename)
|
||||||
|
|
||||||
|
dict_lists_rules = {'No': [[], []],
|
||||||
|
'Yes': [[], []],
|
||||||
|
'Auth_self' : [[], []],
|
||||||
|
'Auth_admin': [[], []],
|
||||||
|
'Auth_self_keep': [[], []],
|
||||||
|
'Auth_admin_keep': [[], []]}
|
||||||
|
|
||||||
|
check_and_add_to_list = (lambda it, act: dict_lists_rules[act][0].append(it.valuename)
|
||||||
|
if it.valuename not in locks
|
||||||
|
else dict_lists_rules[act][1].append(it.valuename))
|
||||||
|
|
||||||
|
for it_data in self.polkit_keys:
|
||||||
|
check_and_add_to_list(it_data, it_data.data)
|
||||||
|
|
||||||
|
for key, item in dict_lists_rules.items():
|
||||||
|
self.__polkit_map[self.__registry_branch][1][key] = item[0]
|
||||||
|
self.__polkit_map[self.__registry_locks_branch][1][key] = item[1]
|
||||||
|
|
||||||
|
if deny_all_win:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['Deny_All_win'] = deny_all_win.data
|
||||||
|
log('D69', logdata)
|
||||||
|
self.__polkit_map[self.__deny_all_win][1]['Deny_All'] = deny_all_win.data
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Deny_All setting not found'))
|
log('D71')
|
||||||
self.policies = []
|
self.policies = []
|
||||||
self.policies.append(polkit(template_file, template_vars))
|
self.policies.append(polkit(template_file, template_vars))
|
||||||
|
self.policies.append(polkit(template_file_all, template_vars_all))
|
||||||
|
self.policies.append(polkit(template_file_all_lock, template_vars_all_lock))
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
@@ -58,38 +98,65 @@ class polkit_applier(applier_frontend):
|
|||||||
Trigger control facility invocation.
|
Trigger control facility invocation.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Polkit applier for machine'))
|
log('D73')
|
||||||
for policy in self.policies:
|
for policy in self.policies:
|
||||||
policy.generate()
|
policy.generate()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Polkit applier for machine will not be started'))
|
log('D75')
|
||||||
|
|
||||||
class polkit_applier_user(applier_frontend):
|
class polkit_applier_user(applier_frontend):
|
||||||
__module_name = 'PolkitApplierUser'
|
__module_name = 'PolkitApplierUser'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__deny_all = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
__deny_all_win = 'Software\\Policies\\Microsoft\\Windows\\RemovableStorageDevices\\Deny_All'
|
||||||
|
__registry_branch = 'Software\\BaseALT\\Policies\\Polkit\\'
|
||||||
__polkit_map = {
|
__polkit_map = {
|
||||||
__deny_all: ['48-gpoa_disk_permissions_user', { 'Deny_All': 0, 'User': '' }]
|
__deny_all_win: ['48-gpoa_disk_permissions_user', { 'Deny_All': 0, 'User': '' }],
|
||||||
|
__registry_branch : ['48-alt_group_policy_permissions_user', {'User': ''}]
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, sid, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.username = username
|
self.username = username
|
||||||
|
deny_all_win = None
|
||||||
deny_all = storage.filter_hkcu_entries(self.sid, self.__deny_all).first()
|
if check_windows_mapping_enabled(self.storage):
|
||||||
|
deny_all_win = storage.filter_hkcu_entries(self.sid, self.__deny_all_win).first()
|
||||||
|
polkit_filter = '{}%'.format(self.__registry_branch)
|
||||||
|
self.polkit_keys = self.storage.filter_hkcu_entries(self.sid, polkit_filter)
|
||||||
# Deny_All hook: initialize defaults
|
# Deny_All hook: initialize defaults
|
||||||
template_file = self.__polkit_map[self.__deny_all][0]
|
template_file = self.__polkit_map[self.__deny_all_win][0]
|
||||||
template_vars = self.__polkit_map[self.__deny_all][1]
|
template_vars = self.__polkit_map[self.__deny_all_win][1]
|
||||||
if deny_all:
|
template_file_all = self.__polkit_map[self.__registry_branch][0]
|
||||||
logging.debug(slogm('Deny_All setting for user {} found: {}'.format(self.username, deny_all.data)))
|
template_vars_all = self.__polkit_map[self.__registry_branch][1]
|
||||||
self.__polkit_map[self.__deny_all][1]['Deny_All'] = deny_all.data
|
|
||||||
self.__polkit_map[self.__deny_all][1]['User'] = self.username
|
dict_lists_rules = {'No': [],
|
||||||
|
'Yes': [],
|
||||||
|
'Auth_self': [],
|
||||||
|
'Auth_admin': [],
|
||||||
|
'Auth_self_keep': [],
|
||||||
|
'Auth_admin_keep': []}
|
||||||
|
|
||||||
|
for it_data in self.polkit_keys:
|
||||||
|
dict_lists_rules[it_data.data].append(it_data.valuename)
|
||||||
|
|
||||||
|
self.__polkit_map[self.__registry_branch][1]['User'] = self.username
|
||||||
|
|
||||||
|
for key, item in dict_lists_rules.items():
|
||||||
|
self.__polkit_map[self.__registry_branch][1][key] = item
|
||||||
|
|
||||||
|
if deny_all_win:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['user'] = self.username
|
||||||
|
logdata['Deny_All_win'] = deny_all_win.data
|
||||||
|
log('D70', logdata)
|
||||||
|
self.__polkit_map[self.__deny_all_win][1]['Deny_All'] = deny_all_win.data
|
||||||
|
self.__polkit_map[self.__deny_all_win][1]['User'] = self.username
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Deny_All setting not found'))
|
log('D72')
|
||||||
self.policies = []
|
self.policies = []
|
||||||
self.policies.append(polkit(template_file, template_vars, self.username))
|
self.policies.append(polkit(template_file, template_vars, self.username))
|
||||||
|
self.policies.append(polkit(template_file_all, template_vars_all, self.username))
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
, self.__module_name
|
, self.__module_name
|
||||||
@@ -104,9 +171,10 @@ class polkit_applier_user(applier_frontend):
|
|||||||
Trigger control facility invocation.
|
Trigger control facility invocation.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Polkit applier for user in administrator context'))
|
log('D74')
|
||||||
for policy in self.policies:
|
for policy in self.policies:
|
||||||
policy.generate()
|
policy.generate()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Polkit applier for user in administrator context will not be started'))
|
log('D76')
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
157
gpoa/frontend/scripts_applier.py
Normal file
157
gpoa/frontend/scripts_applier.py
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from util.logging import log
|
||||||
|
from .appliers.folder import remove_dir_tree
|
||||||
|
from .applier_frontend import (
|
||||||
|
applier_frontend
|
||||||
|
, check_enabled
|
||||||
|
)
|
||||||
|
|
||||||
|
class scripts_applier(applier_frontend):
|
||||||
|
__module_name = 'ScriptsApplier'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
__cache_scripts = '/var/cache/gpupdate_scripts_cache/machine/'
|
||||||
|
|
||||||
|
def __init__(self, storage, sid):
|
||||||
|
self.storage = storage
|
||||||
|
self.sid = sid
|
||||||
|
self.startup_scripts = self.storage.get_scripts(self.sid, 'STARTUP')
|
||||||
|
self.shutdown_scripts = self.storage.get_scripts(self.sid, 'SHUTDOWN')
|
||||||
|
self.folder_path = Path(self.__cache_scripts)
|
||||||
|
self.__module_enabled = check_enabled(self.storage
|
||||||
|
, self.__module_name
|
||||||
|
, self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
def cleaning_cache(self):
|
||||||
|
log('D160')
|
||||||
|
try:
|
||||||
|
remove_dir_tree(self.folder_path, True, True, True,)
|
||||||
|
except FileNotFoundError as exc:
|
||||||
|
log('D154')
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E64', logdata)
|
||||||
|
|
||||||
|
def filling_cache(self):
|
||||||
|
'''
|
||||||
|
Creating and updating folder directories for scripts and copying them
|
||||||
|
'''
|
||||||
|
self.folder_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
for ts in self.startup_scripts:
|
||||||
|
script_path = os.path.join(self.__cache_scripts, 'STARTUP')
|
||||||
|
install_script(ts, script_path, '700')
|
||||||
|
for ts in self.shutdown_scripts:
|
||||||
|
script_path = os.path.join(self.__cache_scripts, 'SHUTDOWN')
|
||||||
|
install_script(ts, script_path, '700')
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.filling_cache()
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
self.cleaning_cache()
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D156')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D157')
|
||||||
|
|
||||||
|
class scripts_applier_user(applier_frontend):
|
||||||
|
__module_name = 'ScriptsApplierUser'
|
||||||
|
__module_experimental = True
|
||||||
|
__module_enabled = False
|
||||||
|
__cache_scripts = '/var/cache/gpupdate_scripts_cache/users/'
|
||||||
|
|
||||||
|
def __init__(self, storage, sid, username):
|
||||||
|
self.storage = storage
|
||||||
|
self.sid = sid
|
||||||
|
self.logon_scripts = self.storage.get_scripts(self.sid, 'LOGON')
|
||||||
|
self.logoff_scripts = self.storage.get_scripts(self.sid, 'LOGOFF')
|
||||||
|
self.username = username
|
||||||
|
self.folder_path = Path(self.__cache_scripts + self.username)
|
||||||
|
self.__module_enabled = check_enabled(self.storage
|
||||||
|
, self.__module_name
|
||||||
|
, self.__module_experimental
|
||||||
|
)
|
||||||
|
self.filling_cache()
|
||||||
|
|
||||||
|
def cleaning_cache(self):
|
||||||
|
log('D161')
|
||||||
|
try:
|
||||||
|
remove_dir_tree(self.folder_path, True, True, True,)
|
||||||
|
except FileNotFoundError as exc:
|
||||||
|
log('D155')
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E65', logdata)
|
||||||
|
|
||||||
|
def filling_cache(self):
|
||||||
|
'''
|
||||||
|
Creating and updating folder directories for scripts and copying them
|
||||||
|
'''
|
||||||
|
self.folder_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for ts in self.logon_scripts:
|
||||||
|
script_path = os.path.join(self.__cache_scripts, self.username, 'LOGON')
|
||||||
|
install_script(ts, script_path, '755')
|
||||||
|
for ts in self.logoff_scripts:
|
||||||
|
script_path = os.path.join(self.__cache_scripts, self.username, 'LOGOFF')
|
||||||
|
install_script(ts, script_path, '755')
|
||||||
|
|
||||||
|
def user_context_apply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.filling_cache()
|
||||||
|
|
||||||
|
def admin_context_apply(self):
|
||||||
|
self.cleaning_cache()
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D158')
|
||||||
|
self.run()
|
||||||
|
else:
|
||||||
|
log('D159')
|
||||||
|
|
||||||
|
def install_script(storage_script_entry, script_dir, access_permissions):
|
||||||
|
'''
|
||||||
|
Copy scripts to specific directories and
|
||||||
|
if given arguments
|
||||||
|
create directories for them and copy them there
|
||||||
|
'''
|
||||||
|
dir_cr = Path(script_dir)
|
||||||
|
dir_cr.mkdir(parents=True, exist_ok=True)
|
||||||
|
script_name = str(int(storage_script_entry.number)).zfill(5) + '_' + os.path.basename(storage_script_entry.path)
|
||||||
|
script_file = os.path.join(script_dir, script_name)
|
||||||
|
shutil.copyfile(storage_script_entry.path, script_file)
|
||||||
|
|
||||||
|
os.chmod(script_file, int(access_permissions, base = 8))
|
||||||
|
if storage_script_entry.args:
|
||||||
|
dir_path = script_dir + '/' + script_name + '.arg'
|
||||||
|
dir_arg = Path(dir_path)
|
||||||
|
dir_arg.mkdir(parents=True, exist_ok=True)
|
||||||
|
file_arg = open(dir_path + '/arg', 'w')
|
||||||
|
file_arg.write(storage_script_entry.args)
|
||||||
|
file_arg.close()
|
||||||
@@ -25,36 +25,39 @@ from .applier_frontend import (
|
|||||||
)
|
)
|
||||||
from gpt.shortcuts import json2sc
|
from gpt.shortcuts import json2sc
|
||||||
from util.windows import expand_windows_var
|
from util.windows import expand_windows_var
|
||||||
from util.logging import slogm
|
from util.logging import slogm, log
|
||||||
from util.util import (
|
from util.util import (
|
||||||
get_homedir,
|
get_homedir,
|
||||||
homedir_exists
|
homedir_exists
|
||||||
)
|
)
|
||||||
|
|
||||||
def storage_get_shortcuts(storage, sid):
|
def storage_get_shortcuts(storage, sid, username=None):
|
||||||
'''
|
'''
|
||||||
Query storage for shortcuts' rows for specified SID.
|
Query storage for shortcuts' rows for specified SID.
|
||||||
'''
|
'''
|
||||||
shortcut_objs = storage.get_shortcuts(sid)
|
shortcut_objs = storage.get_shortcuts(sid)
|
||||||
shortcuts = list()
|
shortcuts = list()
|
||||||
|
|
||||||
for sc_obj in shortcut_objs:
|
for sc in shortcut_objs:
|
||||||
sc = json2sc(sc_obj.shortcut)
|
if username:
|
||||||
|
sc.set_expanded_path(expand_windows_var(sc.path, username))
|
||||||
shortcuts.append(sc)
|
shortcuts.append(sc)
|
||||||
|
|
||||||
return shortcuts
|
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
|
:username: None means working with machine variables and paths
|
||||||
'''
|
'''
|
||||||
dest_abspath = shortcut.dest
|
dest_abspath = shortcut.dest
|
||||||
if not dest_abspath.startswith('/') and not dest_abspath.startswith('%'):
|
if not dest_abspath.startswith('/') and not dest_abspath.startswith('%'):
|
||||||
dest_abspath = '%HOME%/' + dest_abspath
|
dest_abspath = '%HOME%/' + dest_abspath
|
||||||
|
logdata = dict()
|
||||||
logging.debug(slogm('Try to expand path for shortcut: {} for {}'.format(dest_abspath, username)))
|
logdata['shortcut'] = dest_abspath
|
||||||
|
logdata['for'] = username
|
||||||
|
log('D105', logdata)
|
||||||
dest_abspath = expand_windows_var(dest_abspath, username).replace('\\', '/') + '.desktop'
|
dest_abspath = expand_windows_var(dest_abspath, username).replace('\\', '/') + '.desktop'
|
||||||
|
|
||||||
# Check that we're working for user, not on global system level
|
# Check that we're working for user, not on global system level
|
||||||
@@ -64,22 +67,34 @@ def write_shortcut(shortcut, username=None):
|
|||||||
if dest_abspath.startswith(get_homedir(username)):
|
if dest_abspath.startswith(get_homedir(username)):
|
||||||
# Don't try to operate on non-existent directory
|
# Don't try to operate on non-existent directory
|
||||||
if not homedir_exists(username):
|
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
|
return None
|
||||||
else:
|
else:
|
||||||
logging.warning(slogm('User\'s shortcut not placed to home directory for {}: bad path {}'.format(username, dest_abspath)))
|
logdata = dict()
|
||||||
|
logdata['user'] = username
|
||||||
|
logdata['bad path'] = dest_abspath
|
||||||
|
log('W8', logdata)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if '%' in dest_abspath:
|
if '%' in dest_abspath:
|
||||||
logging.debug(slogm('Fail for writing shortcut to file with \'%\': {}'.format(dest_abspath)))
|
logdata = dict()
|
||||||
|
logdata['dest_abspath'] = dest_abspath
|
||||||
|
log('E53', logdata)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not dest_abspath.startswith('/'):
|
if not dest_abspath.startswith('/'):
|
||||||
logging.debug(slogm('Fail for writing shortcut to not absolute path \'%\': {}'.format(dest_abspath)))
|
logdata = dict()
|
||||||
|
logdata['dest_abspath'] = dest_abspath
|
||||||
|
log('E54', logdata)
|
||||||
return None
|
return None
|
||||||
|
logdata = dict()
|
||||||
logging.debug(slogm('Writing shortcut file to {}'.format(dest_abspath)))
|
logdata['file'] = dest_abspath
|
||||||
shortcut.write_desktop(dest_abspath)
|
logdata['with_action'] = shortcut.action
|
||||||
|
log('D106', logdata)
|
||||||
|
shortcut.apply_desktop(dest_abspath)
|
||||||
|
|
||||||
class shortcut_applier(applier_frontend):
|
class shortcut_applier(applier_frontend):
|
||||||
__module_name = 'ShortcutsApplier'
|
__module_name = 'ShortcutsApplier'
|
||||||
@@ -98,21 +113,24 @@ class shortcut_applier(applier_frontend):
|
|||||||
shortcuts = storage_get_shortcuts(self.storage, self.storage.get_info('machine_sid'))
|
shortcuts = storage_get_shortcuts(self.storage, self.storage.get_info('machine_sid'))
|
||||||
if shortcuts:
|
if shortcuts:
|
||||||
for sc in shortcuts:
|
for sc in shortcuts:
|
||||||
write_shortcut(sc)
|
apply_shortcut(sc)
|
||||||
else:
|
if len(shortcuts) > 0:
|
||||||
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
|
# According to ArchWiki - this thing is needed to rebuild MIME
|
||||||
# type cache in order file bindings to work. This rebuilds
|
# type cache in order file bindings to work. This rebuilds
|
||||||
# databases located in /usr/share/applications and
|
# databases located in /usr/share/applications and
|
||||||
# /usr/local/share/applications
|
# /usr/local/share/applications
|
||||||
subprocess.check_call(['/usr/bin/update-desktop-database'])
|
subprocess.check_call(['/usr/bin/update-desktop-database'])
|
||||||
|
else:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['machine_sid'] = self.storage.get_info('machine_sid')
|
||||||
|
log('D100', logdata)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Shortcut applier for machine'))
|
log('D98')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Shortcut applier for machine will not be started'))
|
log('D99')
|
||||||
|
|
||||||
class shortcut_applier_user(applier_frontend):
|
class shortcut_applier_user(applier_frontend):
|
||||||
__module_name = 'ShortcutsApplierUser'
|
__module_name = 'ShortcutsApplierUser'
|
||||||
@@ -124,27 +142,31 @@ class shortcut_applier_user(applier_frontend):
|
|||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.username = username
|
self.username = username
|
||||||
|
|
||||||
def run(self):
|
def run(self, in_usercontext):
|
||||||
shortcuts = storage_get_shortcuts(self.storage, self.sid)
|
shortcuts = storage_get_shortcuts(self.storage, self.sid, self.username)
|
||||||
|
|
||||||
if shortcuts:
|
if shortcuts:
|
||||||
for sc in shortcuts:
|
for sc in shortcuts:
|
||||||
if sc.is_usercontext():
|
if in_usercontext and sc.is_usercontext():
|
||||||
write_shortcut(sc, self.username)
|
apply_shortcut(sc, self.username)
|
||||||
|
if not in_usercontext and not sc.is_usercontext():
|
||||||
|
apply_shortcut(sc, self.username)
|
||||||
else:
|
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):
|
def user_context_apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Shortcut applier for user in user context'))
|
log('D101')
|
||||||
self.run()
|
self.run(True)
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Shortcut applier for user in user context will not be started'))
|
log('D102')
|
||||||
|
|
||||||
def admin_context_apply(self):
|
def admin_context_apply(self):
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running Shortcut applier for user in administrator context'))
|
log('D103')
|
||||||
self.run()
|
self.run(False)
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('Shortcut applier for user in administrator context will not be started'))
|
log('D104')
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from .applier_frontend import (
|
|||||||
, check_enabled
|
, check_enabled
|
||||||
)
|
)
|
||||||
from .appliers.systemd import systemd_unit
|
from .appliers.systemd import systemd_unit
|
||||||
from util.logging import slogm
|
from util.logging import slogm, log
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -29,11 +29,11 @@ class systemd_applier(applier_frontend):
|
|||||||
__module_name = 'SystemdApplier'
|
__module_name = 'SystemdApplier'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\SystemdUnits'
|
__registry_branch = 'Software/BaseALT/Policies/SystemdUnits'
|
||||||
|
|
||||||
def __init__(self, storage):
|
def __init__(self, storage):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.systemd_unit_settings = self.storage.filter_hklm_entries('Software\\BaseALT\\Policies\\SystemdUnits%')
|
self.systemd_unit_settings = self.storage.filter_hklm_entries(self.__registry_branch)
|
||||||
self.units = []
|
self.units = []
|
||||||
self.__module_enabled = check_enabled(
|
self.__module_enabled = check_enabled(
|
||||||
self.storage
|
self.storage
|
||||||
@@ -43,33 +43,40 @@ class systemd_applier(applier_frontend):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for setting in self.systemd_unit_settings:
|
for setting in self.systemd_unit_settings:
|
||||||
valuename = setting.hive_key.rpartition('\\')[2]
|
valuename = setting.hive_key.rpartition('/')[2]
|
||||||
try:
|
try:
|
||||||
self.units.append(systemd_unit(valuename, int(setting.data)))
|
self.units.append(systemd_unit(valuename, int(setting.data)))
|
||||||
logging.info(slogm('Working with systemd unit {}'.format(valuename)))
|
logdata = dict()
|
||||||
|
logdata['unit'] = format(valuename)
|
||||||
|
log('I4', logdata)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.info(slogm('Unable to work with systemd unit {}: {}'.format(valuename, exc)))
|
logdata = dict()
|
||||||
|
logdata['unit'] = format(valuename)
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('I5', logdata)
|
||||||
for unit in self.units:
|
for unit in self.units:
|
||||||
try:
|
try:
|
||||||
unit.apply()
|
unit.apply()
|
||||||
except:
|
except:
|
||||||
logging.error(slogm('Failed applying unit {}'.format(unit.unit_name)))
|
logdata = dict()
|
||||||
|
logdata['unit'] = unit.unit_name
|
||||||
|
log('E45', logdata)
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
'''
|
'''
|
||||||
Trigger control facility invocation.
|
Trigger control facility invocation.
|
||||||
'''
|
'''
|
||||||
if self.__module_enabled:
|
if self.__module_enabled:
|
||||||
logging.debug(slogm('Running systemd applier for machine'))
|
log('D78')
|
||||||
self.run()
|
self.run()
|
||||||
else:
|
else:
|
||||||
logging.debug(slogm('systemd applier for machine will not be started'))
|
log('D79')
|
||||||
|
|
||||||
class systemd_applier_user(applier_frontend):
|
class systemd_applier_user(applier_frontend):
|
||||||
__module_name = 'SystemdApplierUser'
|
__module_name = 'SystemdApplierUser'
|
||||||
__module_experimental = False
|
__module_experimental = False
|
||||||
__module_enabled = True
|
__module_enabled = True
|
||||||
__registry_branch = 'Software\\BaseALT\\Policies\\SystemdUnits'
|
__registry_branch = 'Software/BaseALT/Policies/SystemdUnits'
|
||||||
|
|
||||||
def __init__(self, storage, sid, username):
|
def __init__(self, storage, sid, username):
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
|
|||||||
198
gpoa/frontend/yandex_browser_applier.py
Normal file
198
gpoa/frontend/yandex_browser_applier.py
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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
|
||||||
|
)
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from util.logging import log
|
||||||
|
from util.util import is_machine_name, string_to_literal_eval
|
||||||
|
|
||||||
|
class yandex_browser_applier(applier_frontend):
|
||||||
|
__module_name = 'YandexBrowserApplier'
|
||||||
|
__module_enabled = True
|
||||||
|
__module_experimental = False
|
||||||
|
__registry_branch = 'Software/Policies/YandexBrowser'
|
||||||
|
__managed_policies_path = '/etc/opt/yandex/browser/policies/managed'
|
||||||
|
__recommended_policies_path = '/etc/opt/yandex/browser/policies/recommended'
|
||||||
|
|
||||||
|
def __init__(self, storage, sid, username):
|
||||||
|
self.storage = storage
|
||||||
|
self.sid = sid
|
||||||
|
self.username = username
|
||||||
|
self._is_machine_name = is_machine_name(self.username)
|
||||||
|
yandex_filter = '{}%'.format(self.__registry_branch)
|
||||||
|
self.yandex_keys = self.storage.filter_hklm_entries(yandex_filter)
|
||||||
|
|
||||||
|
self.policies_json = dict()
|
||||||
|
|
||||||
|
self.__module_enabled = check_enabled(
|
||||||
|
self.storage
|
||||||
|
, self.__module_name
|
||||||
|
, self.__module_experimental
|
||||||
|
)
|
||||||
|
|
||||||
|
def machine_apply(self):
|
||||||
|
'''
|
||||||
|
Apply machine settings.
|
||||||
|
'''
|
||||||
|
|
||||||
|
destfile = os.path.join(self.__managed_policies_path, 'policies.json')
|
||||||
|
|
||||||
|
try:
|
||||||
|
recommended__json = self.policies_json.pop('Recommended')
|
||||||
|
except:
|
||||||
|
recommended__json = {}
|
||||||
|
|
||||||
|
#Replacing all nested dictionaries with a list
|
||||||
|
dict_item_to_list = (
|
||||||
|
lambda target_dict :
|
||||||
|
{key:[*val.values()] if type(val) == dict else string_to_literal_eval(val) for key,val in target_dict.items()}
|
||||||
|
)
|
||||||
|
os.makedirs(self.__managed_policies_path, exist_ok=True)
|
||||||
|
with open(destfile, 'w') as f:
|
||||||
|
json.dump(dict_item_to_list(self.policies_json), f)
|
||||||
|
logdata = dict()
|
||||||
|
logdata['destfile'] = destfile
|
||||||
|
log('D185', logdata)
|
||||||
|
|
||||||
|
destfilerec = os.path.join(self.__recommended_policies_path, 'policies.json')
|
||||||
|
os.makedirs(self.__recommended_policies_path, exist_ok=True)
|
||||||
|
with open(destfilerec, 'w') as f:
|
||||||
|
json.dump(dict_item_to_list(recommended__json), f)
|
||||||
|
logdata = dict()
|
||||||
|
logdata['destfilerec'] = destfilerec
|
||||||
|
log('D185', logdata)
|
||||||
|
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
'''
|
||||||
|
All actual job done here.
|
||||||
|
'''
|
||||||
|
if self.__module_enabled:
|
||||||
|
log('D183')
|
||||||
|
self.create_dict(self.yandex_keys)
|
||||||
|
self.machine_apply()
|
||||||
|
else:
|
||||||
|
log('D184')
|
||||||
|
|
||||||
|
def get_valuename_typeint(self):
|
||||||
|
'''
|
||||||
|
List of keys resulting from parsing chrome.admx with parsing_chrom_admx_intvalues.py
|
||||||
|
'''
|
||||||
|
valuename_typeint = (['DefaultPageSaveSettings',
|
||||||
|
'DefaultUploadSetting',
|
||||||
|
'YandexAutoLaunchMode',
|
||||||
|
'DefaultClipboardSetting',
|
||||||
|
'DefaultFileSystemReadGuardSetting',
|
||||||
|
'DefaultFileSystemWriteGuardSetting',
|
||||||
|
'DefaultImagesSetting',
|
||||||
|
'DefaultJavaScriptJitSetting',
|
||||||
|
'DefaultJavaScriptSetting',
|
||||||
|
'DefaultLocalFontsSetting',
|
||||||
|
'DefaultPopupsSetting',
|
||||||
|
'DefaultSensorsSetting',
|
||||||
|
'DefaultSerialGuardSetting',
|
||||||
|
'DefaultWebBluetoothGuardSetting',
|
||||||
|
'DefaultWebHidGuardSetting',
|
||||||
|
'DefaultWebUsbGuardSetting',
|
||||||
|
'DefaultWindowManagementSetting',
|
||||||
|
'SafeSitesFilterBehavior',
|
||||||
|
'YandexUserFeedbackMode',
|
||||||
|
'TurboSettings',
|
||||||
|
'SidePanelMode',
|
||||||
|
'RestoreOnStartup',
|
||||||
|
'RestoreOnStartup_recommended',
|
||||||
|
'BrowserSwitcherParsingMode',
|
||||||
|
'DefaultNotificationsSetting',
|
||||||
|
'YandexPowerSavingMode',
|
||||||
|
'ChromeVariations',
|
||||||
|
'DeveloperToolsAvailability',
|
||||||
|
'DownloadRestrictions',
|
||||||
|
'NetworkPredictionOptions',
|
||||||
|
'DownloadRestrictions_recommended',
|
||||||
|
'NetworkPredictionOptions_recommended',
|
||||||
|
'DefaultCookiesSetting',
|
||||||
|
'DefaultGeolocationSetting',
|
||||||
|
'IncognitoModeAvailability',
|
||||||
|
'DefaultPrintingSettings',
|
||||||
|
'DefaultPluginsSetting',
|
||||||
|
'DefaultInsecureContentSetting',
|
||||||
|
'PasswordProtectionWarningTrigger',
|
||||||
|
'SafeBrowsingProtectionLevel',
|
||||||
|
'SafeBrowsingProtectionLevel_recommended',
|
||||||
|
'DiskCacheSize'])
|
||||||
|
return valuename_typeint
|
||||||
|
|
||||||
|
|
||||||
|
def get_boolean(self,data):
|
||||||
|
if data in ['0', 'false', None, 'none', 0]:
|
||||||
|
return False
|
||||||
|
if data in ['1', 'true', 1]:
|
||||||
|
return True
|
||||||
|
def get_parts(self, hivekeyname):
|
||||||
|
'''
|
||||||
|
Parse registry path string and leave key parameters
|
||||||
|
'''
|
||||||
|
parts = hivekeyname.replace(self.__registry_branch, '').split('/')
|
||||||
|
return parts
|
||||||
|
|
||||||
|
|
||||||
|
def create_dict(self, yandex_keys):
|
||||||
|
'''
|
||||||
|
Collect dictionaries from registry keys into a general dictionary
|
||||||
|
'''
|
||||||
|
counts = dict()
|
||||||
|
#getting the list of keys to read as an integer
|
||||||
|
valuename_typeint = self.get_valuename_typeint()
|
||||||
|
for it_data in yandex_keys:
|
||||||
|
branch = counts
|
||||||
|
try:
|
||||||
|
if type(it_data.data) is bytes:
|
||||||
|
it_data.data = it_data.data.decode(encoding='utf-16').replace('\x00','')
|
||||||
|
parts = self.get_parts(it_data.hive_key)
|
||||||
|
#creating a nested dictionary from elements
|
||||||
|
for part in parts[:-1]:
|
||||||
|
branch = branch.setdefault(part, {})
|
||||||
|
#dictionary key value initialization
|
||||||
|
if it_data.type == 4:
|
||||||
|
if it_data.valuename in valuename_typeint:
|
||||||
|
branch[parts[-1]] = int(it_data.data)
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = self.get_boolean(it_data.data)
|
||||||
|
else:
|
||||||
|
if it_data.data[0] == '[' and it_data.data[-1] == ']':
|
||||||
|
try:
|
||||||
|
branch[parts[-1]] = json.loads(str(it_data.data))
|
||||||
|
except:
|
||||||
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
else:
|
||||||
|
branch[parts[-1]] = str(it_data.data).replace('\\', '/')
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['Exception'] = exc
|
||||||
|
logdata['keyname'] = it_data.keyname
|
||||||
|
log('D178', logdata)
|
||||||
|
try:
|
||||||
|
self.policies_json = counts['']
|
||||||
|
except:
|
||||||
|
self.policies_json = {}
|
||||||
54
gpoa/gpoa
54
gpoa/gpoa
@@ -23,16 +23,12 @@ import signal
|
|||||||
import gettext
|
import gettext
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
from backend import backend_factory
|
from backend import backend_factory, save_dconf
|
||||||
from frontend.frontend_manager import frontend_manager, determine_username
|
from frontend.frontend_manager import frontend_manager, determine_username
|
||||||
from plugin import plugin_manager
|
from plugin import plugin_manager
|
||||||
from messages import message_with_code
|
from messages import message_with_code
|
||||||
|
|
||||||
from util.util import get_machine_name
|
from util.util import get_machine_name
|
||||||
from util.kerberos import (
|
|
||||||
machine_kinit
|
|
||||||
, machine_kdestroy
|
|
||||||
)
|
|
||||||
from util.users import (
|
from util.users import (
|
||||||
is_root,
|
is_root,
|
||||||
get_process_user
|
get_process_user
|
||||||
@@ -62,6 +58,9 @@ def parse_arguments():
|
|||||||
arguments.add_argument('--noplugins',
|
arguments.add_argument('--noplugins',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Don\'t start plugins')
|
help='Don\'t start plugins')
|
||||||
|
arguments.add_argument('--list-backends',
|
||||||
|
action='store_true',
|
||||||
|
help='Show list of available backends')
|
||||||
arguments.add_argument('--loglevel',
|
arguments.add_argument('--loglevel',
|
||||||
type=int,
|
type=int,
|
||||||
default=4,
|
default=4,
|
||||||
@@ -69,42 +68,60 @@ def parse_arguments():
|
|||||||
return arguments.parse_args()
|
return arguments.parse_args()
|
||||||
|
|
||||||
class gpoa_controller:
|
class gpoa_controller:
|
||||||
__kinit_successful = False
|
|
||||||
__args = None
|
__args = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__args = parse_arguments()
|
self.__args = parse_arguments()
|
||||||
self.is_machine = False
|
self.is_machine = False
|
||||||
if not self.__args.user:
|
self.noupdate = self.__args.noupdate
|
||||||
user = get_machine_name()
|
|
||||||
self.is_machine = True
|
|
||||||
set_loglevel(self.__args.loglevel)
|
set_loglevel(self.__args.loglevel)
|
||||||
self.cache_path = '/var/cache/gpupdate/creds/krb5cc_{}'.format(os.getpid())
|
|
||||||
locale.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
locale.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||||
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||||
gettext.textdomain('gpoa')
|
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()
|
uname = get_process_user()
|
||||||
uid = os.getuid()
|
uid = os.getuid()
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['username'] = uname
|
logdata['username'] = self.username
|
||||||
logdata['uid'] = uid
|
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)
|
log('D1', logdata)
|
||||||
|
self.username = determine_username(self.username)
|
||||||
|
|
||||||
if not is_root():
|
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:
|
else:
|
||||||
self.username = determine_username(self.__args.user)
|
log('D60', {'username': self.username})
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
'''
|
'''
|
||||||
GPOA controller entry point
|
GPOA controller entry point
|
||||||
'''
|
'''
|
||||||
self.__kinit_successful = machine_kinit(self.cache_path)
|
if self.__args.list_backends:
|
||||||
|
print('local')
|
||||||
|
print('samba')
|
||||||
|
return
|
||||||
self.start_plugins()
|
self.start_plugins()
|
||||||
self.start_backend()
|
self.start_backend()
|
||||||
if self.__kinit_successful:
|
|
||||||
machine_kdestroy()
|
|
||||||
|
|
||||||
def start_backend(self):
|
def start_backend(self):
|
||||||
'''
|
'''
|
||||||
@@ -115,7 +132,7 @@ class gpoa_controller:
|
|||||||
if self.__args.nodomain:
|
if self.__args.nodomain:
|
||||||
nodomain = True
|
nodomain = True
|
||||||
|
|
||||||
if not self.__args.noupdate:
|
if not self.noupdate:
|
||||||
if is_root():
|
if is_root():
|
||||||
back = None
|
back = None
|
||||||
try:
|
try:
|
||||||
@@ -142,6 +159,7 @@ class gpoa_controller:
|
|||||||
einfo = geterr()
|
einfo = geterr()
|
||||||
logdata.update(einfo)
|
logdata.update(einfo)
|
||||||
log('E3', logdata)
|
log('E3', logdata)
|
||||||
|
save_dconf(self.username, self.is_machine)
|
||||||
|
|
||||||
def start_frontend(self):
|
def start_frontend(self):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ def read_drives(drives_file):
|
|||||||
drive_obj.set_pass(decrypt_pass(props.get('cpassword')))
|
drive_obj.set_pass(decrypt_pass(props.get('cpassword')))
|
||||||
drive_obj.set_dir(props.get('letter'))
|
drive_obj.set_dir(props.get('letter'))
|
||||||
drive_obj.set_path(props.get('path'))
|
drive_obj.set_path(props.get('path'))
|
||||||
|
drive_obj.set_action(props.get('action'))
|
||||||
|
drive_obj.set_thisDrive(props.get('thisDrive'))
|
||||||
|
drive_obj.set_allDrives(props.get('allDrives'))
|
||||||
|
drive_obj.set_label(props.get('label'))
|
||||||
|
drive_obj.set_persistent(props.get('persistent'))
|
||||||
|
drive_obj.set_useLetter(props.get('useLetter'))
|
||||||
|
|
||||||
drives.append(drive_obj)
|
drives.append(drive_obj)
|
||||||
|
|
||||||
@@ -93,6 +99,12 @@ class drivemap:
|
|||||||
self.password = None
|
self.password = None
|
||||||
self.dir = None
|
self.dir = None
|
||||||
self.path = None
|
self.path = None
|
||||||
|
self.action = None
|
||||||
|
self.thisDrive = None
|
||||||
|
self.allDrives = None
|
||||||
|
self.label = None
|
||||||
|
self.persistent = None
|
||||||
|
self.useLetter = None
|
||||||
|
|
||||||
def set_login(self, username):
|
def set_login(self, username):
|
||||||
self.login = username
|
self.login = username
|
||||||
@@ -110,6 +122,24 @@ class drivemap:
|
|||||||
def set_path(self, path):
|
def set_path(self, path):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
def set_action(self, action):
|
||||||
|
self.action = action
|
||||||
|
|
||||||
|
def set_thisDrive(self, thisDrive):
|
||||||
|
self.thisDrive = thisDrive
|
||||||
|
|
||||||
|
def set_allDrives(self, allDrives):
|
||||||
|
self.allDrives = allDrives
|
||||||
|
|
||||||
|
def set_label(self, label):
|
||||||
|
self.label = label
|
||||||
|
|
||||||
|
def set_persistent(self, persistent):
|
||||||
|
self.persistent = persistent
|
||||||
|
|
||||||
|
def set_useLetter(self, useLetter):
|
||||||
|
self.useLetter = useLetter
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
drive = dict()
|
drive = dict()
|
||||||
drive['login'] = self.login
|
drive['login'] = self.login
|
||||||
|
|||||||
@@ -18,21 +18,48 @@
|
|||||||
|
|
||||||
from util.xml import get_xml_root
|
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):
|
def read_envvars(envvars_file):
|
||||||
variables = list()
|
variables = list()
|
||||||
|
|
||||||
for var in get_xml_root(envvars_file):
|
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)
|
variables.append(var_obj)
|
||||||
|
|
||||||
return variables
|
return variables
|
||||||
|
|
||||||
def merge_envvars(storage, sid, envvars_objects, policy_name):
|
def merge_envvars(storage, sid, envvar_objects, policy_name):
|
||||||
for envvar in envvar_objects:
|
for envv in envvar_objects:
|
||||||
pass
|
storage.add_envvar(sid, envv, policy_name)
|
||||||
|
|
||||||
class envvar:
|
class envvar:
|
||||||
def __init__(self):
|
def __init__(self, name, value):
|
||||||
pass
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
self.action = FileAction.CREATE
|
||||||
|
|
||||||
|
def set_action(self, action):
|
||||||
|
self.action = action
|
||||||
|
|
||||||
|
|||||||
@@ -22,17 +22,38 @@ def read_files(filesxml):
|
|||||||
files = list()
|
files = list()
|
||||||
|
|
||||||
for fil in get_xml_root(filesxml):
|
for fil in get_xml_root(filesxml):
|
||||||
fil_obj = fileentry()
|
props = fil.find('Properties')
|
||||||
|
fil_obj = fileentry(props.get('fromPath'))
|
||||||
|
fil_obj.set_action(props.get('action', default='C'))
|
||||||
|
fil_obj.set_target_path(props.get('targetPath', default=None))
|
||||||
|
fil_obj.set_read_only(props.get('readOnly', default=None))
|
||||||
|
fil_obj.set_archive(props.get('archive', default=None))
|
||||||
|
fil_obj.set_hidden(props.get('hidden', default=None))
|
||||||
|
fil_obj.set_suppress(props.get('suppress', default=None))
|
||||||
|
fil_obj.set_executable(props.get('executable', default=None))
|
||||||
files.append(fil_obj)
|
files.append(fil_obj)
|
||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
||||||
def merge_files(storage, sid, file_objects, policy_name):
|
def merge_files(storage, sid, file_objects, policy_name):
|
||||||
for fileobj in file_objects:
|
for fileobj in file_objects:
|
||||||
pass
|
storage.add_file(sid, fileobj, policy_name)
|
||||||
|
|
||||||
class fileentry:
|
class fileentry:
|
||||||
def __init__(self):
|
def __init__(self, fromPath):
|
||||||
pass
|
self.fromPath = fromPath
|
||||||
|
|
||||||
|
def set_action(self, action):
|
||||||
|
self.action = action
|
||||||
|
def set_target_path(self, targetPath):
|
||||||
|
self.targetPath = targetPath
|
||||||
|
def set_read_only(self, readOnly):
|
||||||
|
self.readOnly = readOnly
|
||||||
|
def set_archive(self, archive):
|
||||||
|
self.archive = archive
|
||||||
|
def set_hidden(self, hidden):
|
||||||
|
self.hidden = hidden
|
||||||
|
def set_suppress(self, suppress):
|
||||||
|
self.suppress = suppress
|
||||||
|
def set_executable(self, executable):
|
||||||
|
self.executable = executable
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ def folder_int2bool(val):
|
|||||||
if type(value) == str:
|
if type(value) == str:
|
||||||
value = int(value)
|
value = int(value)
|
||||||
|
|
||||||
if value == 0:
|
if value == 1:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@@ -62,13 +62,15 @@ def read_folders(folders_file):
|
|||||||
for fld in get_xml_root(folders_file):
|
for fld in get_xml_root(folders_file):
|
||||||
props = fld.find('Properties')
|
props = fld.find('Properties')
|
||||||
fld_obj = folderentry(props.get('path'))
|
fld_obj = folderentry(props.get('path'))
|
||||||
fld_obj.set_action(action_letter2enum(props.get('action')))
|
fld_obj.set_action(action_letter2enum(props.get('action', default='C')))
|
||||||
fld_obj.set_delete_folder(folder_int2bool(props.get('deleteFolder')))
|
fld_obj.set_delete_folder(folder_int2bool(props.get('deleteFolder', default=1)))
|
||||||
fld_obj.set_delete_sub_folder(folder_int2bool(props.get('deleteSubFolders')))
|
fld_obj.set_delete_sub_folders(folder_int2bool(props.get('deleteSubFolders', default=1)))
|
||||||
fld_obj.set_delete_files(folder_int2bool(props.get('deleteFiles')))
|
fld_obj.set_delete_files(folder_int2bool(props.get('deleteFiles', default=1)))
|
||||||
|
fld_obj.set_hidden_folder(folder_int2bool(props.get('hidden', default=0)))
|
||||||
|
|
||||||
folders.append(fld_obj)
|
folders.append(fld_obj)
|
||||||
|
|
||||||
|
|
||||||
return folders
|
return folders
|
||||||
|
|
||||||
def merge_folders(storage, sid, folder_objects, policy_name):
|
def merge_folders(storage, sid, folder_objects, policy_name):
|
||||||
@@ -81,8 +83,9 @@ class folderentry:
|
|||||||
self.path = path
|
self.path = path
|
||||||
self.action = FileAction.CREATE
|
self.action = FileAction.CREATE
|
||||||
self.delete_folder = False
|
self.delete_folder = False
|
||||||
self.delete_sub_folder = False
|
self.delete_sub_folders = False
|
||||||
self.delete_files = False
|
self.delete_files = False
|
||||||
|
self.hidden_folder = False
|
||||||
|
|
||||||
def set_action(self, action):
|
def set_action(self, action):
|
||||||
self.action = action
|
self.action = action
|
||||||
@@ -90,9 +93,11 @@ class folderentry:
|
|||||||
def set_delete_folder(self, del_bool):
|
def set_delete_folder(self, del_bool):
|
||||||
self.delete_folder = del_bool
|
self.delete_folder = del_bool
|
||||||
|
|
||||||
def set_delete_sub_folder(self, del_bool):
|
def set_delete_sub_folders(self, del_bool):
|
||||||
self.delete_sub_folder = del_bool
|
self.delete_sub_folders = del_bool
|
||||||
|
|
||||||
def set_delete_files(self, del_bool):
|
def set_delete_files(self, del_bool):
|
||||||
self.delete_files = del_bool
|
self.delete_files = del_bool
|
||||||
|
|
||||||
|
def set_hidden_folder(self, hid_bool):
|
||||||
|
self.hidden_folder = hid_bool
|
||||||
107
gpoa/gpt/gpt.py
107
gpoa/gpt/gpt.py
@@ -64,11 +64,18 @@ from .tasks import (
|
|||||||
read_tasks
|
read_tasks
|
||||||
, merge_tasks
|
, merge_tasks
|
||||||
)
|
)
|
||||||
|
from .scriptsini import (
|
||||||
|
read_scripts
|
||||||
|
, merge_scripts
|
||||||
|
)
|
||||||
|
from .networkshares import (
|
||||||
|
read_networkshares
|
||||||
|
, merge_networkshares
|
||||||
|
)
|
||||||
import util
|
import util
|
||||||
import util.preg
|
import util.preg
|
||||||
from util.paths import (
|
from util.paths import (
|
||||||
default_policy_path,
|
local_policy_path,
|
||||||
cache_dir,
|
cache_dir,
|
||||||
local_policy_cache
|
local_policy_cache
|
||||||
)
|
)
|
||||||
@@ -87,6 +94,8 @@ class FileType(Enum):
|
|||||||
INIFILES = 'inifiles.xml'
|
INIFILES = 'inifiles.xml'
|
||||||
SERVICES = 'services.xml'
|
SERVICES = 'services.xml'
|
||||||
PRINTERS = 'printers.xml'
|
PRINTERS = 'printers.xml'
|
||||||
|
SCRIPTS = 'scripts.ini'
|
||||||
|
NETWORKSHARES = 'networkshares.xml'
|
||||||
|
|
||||||
def get_preftype(path_to_file):
|
def get_preftype(path_to_file):
|
||||||
fpath = Path(path_to_file)
|
fpath = Path(path_to_file)
|
||||||
@@ -112,6 +121,8 @@ def pref_parsers():
|
|||||||
parsers[FileType.INIFILES] = read_inifiles
|
parsers[FileType.INIFILES] = read_inifiles
|
||||||
parsers[FileType.SERVICES] = read_services
|
parsers[FileType.SERVICES] = read_services
|
||||||
parsers[FileType.PRINTERS] = read_printers
|
parsers[FileType.PRINTERS] = read_printers
|
||||||
|
parsers[FileType.SCRIPTS] = read_scripts
|
||||||
|
parsers[FileType.NETWORKSHARES] = read_networkshares
|
||||||
|
|
||||||
return parsers
|
return parsers
|
||||||
|
|
||||||
@@ -132,6 +143,8 @@ def pref_mergers():
|
|||||||
mergers[FileType.INIFILES] = merge_inifiles
|
mergers[FileType.INIFILES] = merge_inifiles
|
||||||
mergers[FileType.SERVICES] = merge_services
|
mergers[FileType.SERVICES] = merge_services
|
||||||
mergers[FileType.PRINTERS] = merge_printers
|
mergers[FileType.PRINTERS] = merge_printers
|
||||||
|
mergers[FileType.SCRIPTS] = merge_scripts
|
||||||
|
mergers[FileType.NETWORKSHARES] = merge_networkshares
|
||||||
|
|
||||||
return mergers
|
return mergers
|
||||||
|
|
||||||
@@ -140,20 +153,22 @@ def get_merger(preference_type):
|
|||||||
return mergers[preference_type]
|
return mergers[preference_type]
|
||||||
|
|
||||||
class gpt:
|
class gpt:
|
||||||
__user_policy_mode_key = 'Software\\Policies\\Microsoft\\Windows\\System\\UserPolicyMode'
|
def __init__(self, gpt_path, sid, username='Machine', version=None):
|
||||||
|
|
||||||
def __init__(self, gpt_path, sid):
|
|
||||||
self.path = gpt_path
|
self.path = gpt_path
|
||||||
|
self.username = username
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.storage = registry_factory('registry')
|
self.storage = registry_factory()
|
||||||
|
self.storage._gpt_read_flag = True
|
||||||
|
self.version = version
|
||||||
self.name = ''
|
self.name = ''
|
||||||
|
|
||||||
self.guid = self.path.rpartition('/')[2]
|
self.guid = self.path.rpartition('/')[2]
|
||||||
if 'default' == self.guid:
|
if 'default' == self.guid:
|
||||||
self.guid = 'Local Policy'
|
self.guid = 'Local Policy'
|
||||||
|
|
||||||
self._machine_path = find_dir(self.path, 'Machine')
|
self._machine_path = find_dir(self.path, 'Machine')
|
||||||
self._user_path = find_dir(self.path, 'User')
|
self._user_path = find_dir(self.path, 'User')
|
||||||
|
self._scripts_machine_path = find_dir(self._machine_path, 'Scripts')
|
||||||
|
self._scripts_user_path = find_dir(self._user_path, 'Scripts')
|
||||||
|
|
||||||
self.settings_list = [
|
self.settings_list = [
|
||||||
'shortcuts'
|
'shortcuts'
|
||||||
@@ -165,6 +180,8 @@ class gpt:
|
|||||||
, 'inifiles'
|
, 'inifiles'
|
||||||
, 'services'
|
, 'services'
|
||||||
, 'scheduledtasks'
|
, 'scheduledtasks'
|
||||||
|
, 'scripts'
|
||||||
|
, 'networkshares'
|
||||||
]
|
]
|
||||||
self.settings = dict()
|
self.settings = dict()
|
||||||
self.settings['machine'] = dict()
|
self.settings['machine'] = dict()
|
||||||
@@ -181,34 +198,27 @@ class gpt:
|
|||||||
log('D23', ulogdata)
|
log('D23', ulogdata)
|
||||||
self.settings['user'][setting] = user_preffile
|
self.settings['user'][setting] = user_preffile
|
||||||
|
|
||||||
|
self.settings['machine']['scripts'] = find_file(self._scripts_machine_path, 'scripts.ini')
|
||||||
|
self.settings['user']['scripts'] = find_file(self._scripts_user_path, 'scripts.ini')
|
||||||
|
|
||||||
|
|
||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
'''
|
'''
|
||||||
Set human-readable GPT name.
|
Set human-readable GPT name.
|
||||||
'''
|
'''
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def get_policy_mode(self):
|
def merge_machine(self):
|
||||||
'''
|
'''
|
||||||
Get UserPolicyMode parameter value in order to determine if it
|
Merge machine settings to storage.
|
||||||
is possible to work with user's part of GPT. This value is
|
|
||||||
checked only if working for user's SID.
|
|
||||||
'''
|
'''
|
||||||
upm = self.storage.get_hklm_entry(self.__user_policy_mode_key)
|
|
||||||
if not upm:
|
|
||||||
upm = 0
|
|
||||||
upm = int(upm)
|
|
||||||
if 0 > upm or 2 > upm:
|
|
||||||
upm = 0
|
|
||||||
|
|
||||||
return upm
|
|
||||||
|
|
||||||
def merge(self):
|
|
||||||
'''
|
|
||||||
Merge machine and user (if sid provided) settings to storage.
|
|
||||||
'''
|
|
||||||
if self.sid == self.storage.get_info('machine_sid'):
|
|
||||||
try:
|
try:
|
||||||
# Merge machine settings to registry if possible
|
# Merge machine policies to registry if possible
|
||||||
|
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, version=self.version)
|
||||||
|
# Merge machine preferences to registry if possible
|
||||||
for preference_name, preference_path in self.settings['machine'].items():
|
for preference_name, preference_path in self.settings['machine'].items():
|
||||||
if preference_path:
|
if preference_path:
|
||||||
preference_type = get_preftype(preference_path)
|
preference_type = get_preftype(preference_path)
|
||||||
@@ -218,25 +228,27 @@ class gpt:
|
|||||||
preference_merger = get_merger(preference_type)
|
preference_merger = get_merger(preference_type)
|
||||||
preference_objects = preference_parser(preference_path)
|
preference_objects = preference_parser(preference_path)
|
||||||
preference_merger(self.storage, self.sid, preference_objects, self.name)
|
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:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['gpt'] = self.name
|
logdata['gpt'] = self.name
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E28', logdata)
|
log('E28', logdata)
|
||||||
else:
|
|
||||||
# Merge user settings if UserPolicyMode set accordingly
|
def merge_user(self):
|
||||||
# and user settings (for HKCU) are exist.
|
'''
|
||||||
policy_mode = upm2str(self.get_policy_mode())
|
Merge user settings to storage.
|
||||||
if 'Merge' == policy_mode or 'Not configured' == policy_mode:
|
'''
|
||||||
try:
|
try:
|
||||||
|
# Merge user policies to registry if possible
|
||||||
|
if self.settings['user']['regpol']:
|
||||||
|
mulogdata = dict({'polfile': self.settings['user']['regpol']})
|
||||||
|
log('D35', mulogdata)
|
||||||
|
util.preg.merge_polfile(self.settings['user']['regpol'],
|
||||||
|
sid=self.sid,
|
||||||
|
policy_name=self.name,
|
||||||
|
username=self.username,
|
||||||
|
version=self.version)
|
||||||
|
# Merge user preferences to registry if possible
|
||||||
for preference_name, preference_path in self.settings['user'].items():
|
for preference_name, preference_path in self.settings['user'].items():
|
||||||
if preference_path:
|
if preference_path:
|
||||||
preference_type = get_preftype(preference_path)
|
preference_type = get_preftype(preference_path)
|
||||||
@@ -326,7 +338,7 @@ def lp2gpt():
|
|||||||
'''
|
'''
|
||||||
Convert local-policy to full-featured GPT.
|
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
|
# Load settings from XML PolFile
|
||||||
polparser = GPPolParser()
|
polparser = GPPolParser()
|
||||||
@@ -350,18 +362,3 @@ def get_local_gpt(sid):
|
|||||||
local_policy.set_name('Local Policy')
|
local_policy.set_name('Local Policy')
|
||||||
|
|
||||||
return local_policy
|
return local_policy
|
||||||
|
|
||||||
def upm2str(upm_num):
|
|
||||||
'''
|
|
||||||
Translate UserPolicyMode to string.
|
|
||||||
'''
|
|
||||||
result = 'Not configured'
|
|
||||||
|
|
||||||
if upm_num in [1, '1']:
|
|
||||||
result = 'Replace'
|
|
||||||
|
|
||||||
if upm_num in [2, '2']:
|
|
||||||
result = 'Merge'
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2022 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,18 +21,32 @@ from util.xml import get_xml_root
|
|||||||
def read_inifiles(inifiles_file):
|
def read_inifiles(inifiles_file):
|
||||||
inifiles = list()
|
inifiles = list()
|
||||||
|
|
||||||
for inifile in get_xml_root(inifiles_file):
|
for ini in get_xml_root(inifiles_file):
|
||||||
ini_obj = inifile()
|
prors = ini.find('Properties')
|
||||||
|
ini_obj = inifile(prors.get('path'))
|
||||||
|
ini_obj.set_section(prors.get('section', default=None))
|
||||||
|
ini_obj.set_property(prors.get('property', default=None))
|
||||||
|
ini_obj.set_value(prors.get('value', default=None))
|
||||||
|
ini_obj.set_action(prors.get('action'))
|
||||||
|
|
||||||
inifiles.append(ini_obj)
|
inifiles.append(ini_obj)
|
||||||
|
|
||||||
return inifiles
|
return inifiles
|
||||||
|
|
||||||
def merge_inifiles(storage, sid, inifile_objects, policy_name):
|
def merge_inifiles(storage, sid, inifile_objects, policy_name):
|
||||||
for inifile in inifile_objects:
|
for iniobj in inifile_objects:
|
||||||
pass
|
storage.add_ini(sid, iniobj, policy_name)
|
||||||
|
|
||||||
def inifile():
|
class inifile:
|
||||||
def __init__(self):
|
def __init__(self, path):
|
||||||
pass
|
self.path = path
|
||||||
|
|
||||||
|
def set_section(self, section):
|
||||||
|
self.section = section
|
||||||
|
def set_property(self, property):
|
||||||
|
self.property = property
|
||||||
|
def set_value(self, value):
|
||||||
|
self.value = value
|
||||||
|
def set_action(self, action):
|
||||||
|
self.action = action
|
||||||
|
|
||||||
|
|||||||
57
gpoa/gpt/networkshares.py
Normal file
57
gpoa/gpt/networkshares.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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.xml import get_xml_root
|
||||||
|
from storage.dconf_registry import Dconf_registry
|
||||||
|
|
||||||
|
def read_networkshares(networksharesxml):
|
||||||
|
networkshares = list()
|
||||||
|
|
||||||
|
for share in get_xml_root(networksharesxml):
|
||||||
|
props = share.find('Properties')
|
||||||
|
networkshare_obj = networkshare(props.get('name'))
|
||||||
|
networkshare_obj.set_action(props.get('action', default='C'))
|
||||||
|
networkshare_obj.set_path(props.get('path', default=None))
|
||||||
|
networkshare_obj.set_all_regular(props.get('allRegular', default=None))
|
||||||
|
networkshare_obj.set_comment(props.get('comment', default=None))
|
||||||
|
networkshare_obj.set_limitUsers(props.get('limitUsers', default=None))
|
||||||
|
networkshare_obj.set_abe(props.get('abe', default=None))
|
||||||
|
networkshares.append(networkshare_obj)
|
||||||
|
|
||||||
|
return networkshares
|
||||||
|
|
||||||
|
def merge_networkshares(storage, sid, networkshares_objects, policy_name):
|
||||||
|
for networkshareobj in networkshares_objects:
|
||||||
|
storage.add_networkshare(sid, networkshareobj, policy_name)
|
||||||
|
|
||||||
|
class networkshare:
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def set_action(self, action):
|
||||||
|
self.action = action
|
||||||
|
def set_path(self, path):
|
||||||
|
self.path = path
|
||||||
|
def set_all_regular(self, allRegular):
|
||||||
|
self.allRegular = allRegular
|
||||||
|
def set_comment(self, comment):
|
||||||
|
self.comment = comment
|
||||||
|
def set_limitUsers(self, limitUsers):
|
||||||
|
self.limitUsers = limitUsers
|
||||||
|
def set_abe(self, abe):
|
||||||
|
self.abe = abe
|
||||||
@@ -24,9 +24,10 @@ def read_polfile(filename):
|
|||||||
return load_preg(filename).entries
|
return load_preg(filename).entries
|
||||||
|
|
||||||
def merge_polfile(storage, sid, policy_objects, policy_name):
|
def merge_polfile(storage, sid, policy_objects, policy_name):
|
||||||
for entry in policy_objects:
|
pass
|
||||||
if not sid:
|
# for entry in policy_objects:
|
||||||
storage.add_hklm_entry(entry, policy_name)
|
# if not sid:
|
||||||
else:
|
# storage.add_hklm_entry(entry, policy_name)
|
||||||
storage.add_hkcu_entry(entry, sid, policy_name)
|
# else:
|
||||||
|
# storage.add_hkcu_entry(entry, sid, policy_name)
|
||||||
|
|
||||||
|
|||||||
147
gpoa/gpt/scriptsini.py
Normal file
147
gpoa/gpt/scriptsini.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#
|
||||||
|
# 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 configparser
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def read_scripts(scripts_file):
|
||||||
|
scripts = Scripts_lists()
|
||||||
|
|
||||||
|
logon_scripts = dict()
|
||||||
|
logoff_scripts = dict()
|
||||||
|
startup_scripts = dict()
|
||||||
|
shutdown_scripts = dict()
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(scripts_file, encoding = 'utf-16')
|
||||||
|
scripts_file_dir = os.path.dirname(scripts_file)
|
||||||
|
|
||||||
|
actions = config.sections()
|
||||||
|
|
||||||
|
for act in actions:
|
||||||
|
act_upper = act.upper()
|
||||||
|
if act_upper == 'LOGON':
|
||||||
|
section_scripts = logon_scripts
|
||||||
|
elif act_upper == 'LOGOFF':
|
||||||
|
section_scripts = logoff_scripts
|
||||||
|
elif act_upper == 'STARTUP':
|
||||||
|
section_scripts = startup_scripts
|
||||||
|
elif act_upper == 'SHUTDOWN':
|
||||||
|
section_scripts = shutdown_scripts
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for key in config[act]:
|
||||||
|
key_lower = key.lower()
|
||||||
|
key_split = key_lower.split('cmdline')
|
||||||
|
if len(key_split) > 1 and not key_split[1]:
|
||||||
|
if key_split[0].isdigit():
|
||||||
|
key_index = int(key_split[0])
|
||||||
|
section_scripts[key_index] = Script(act, scripts_file_dir, config[act][key])
|
||||||
|
key_split = key_lower.split('parameters')
|
||||||
|
if len(key_split) > 1 and not key_split[1]:
|
||||||
|
if key_split[0].isdigit():
|
||||||
|
key_index = int(key_split[0])
|
||||||
|
section_scripts[key_index].set_args(config[act][key])
|
||||||
|
if logon_scripts:
|
||||||
|
for i in sorted(logon_scripts.keys()):
|
||||||
|
scripts.add_script('LOGON', logon_scripts[i])
|
||||||
|
|
||||||
|
if logoff_scripts:
|
||||||
|
for i in sorted(logoff_scripts.keys()):
|
||||||
|
scripts.add_script('LOGOFF', logoff_scripts[i])
|
||||||
|
|
||||||
|
if startup_scripts:
|
||||||
|
for i in sorted(startup_scripts.keys()):
|
||||||
|
scripts.add_script('STARTUP', startup_scripts[i])
|
||||||
|
|
||||||
|
if shutdown_scripts:
|
||||||
|
for i in sorted(shutdown_scripts.keys()):
|
||||||
|
scripts.add_script('SHUTDOWN', shutdown_scripts[i])
|
||||||
|
|
||||||
|
|
||||||
|
return scripts
|
||||||
|
|
||||||
|
def merge_scripts(storage, sid, scripts_objects, policy_name):
|
||||||
|
for script in scripts_objects.get_logon_scripts():
|
||||||
|
storage.add_script(sid, script, policy_name)
|
||||||
|
for script in scripts_objects.get_logoff_scripts():
|
||||||
|
storage.add_script(sid, script, policy_name)
|
||||||
|
for script in scripts_objects.get_startup_scripts():
|
||||||
|
storage.add_script(sid, script, policy_name)
|
||||||
|
for script in scripts_objects.get_shutdown_scripts():
|
||||||
|
storage.add_script(sid, script, policy_name)
|
||||||
|
|
||||||
|
class Scripts_lists:
|
||||||
|
def __init__ (self):
|
||||||
|
self.__logon_scripts = list()
|
||||||
|
self.__logoff_scripts = list()
|
||||||
|
self.__startup_scripts = list()
|
||||||
|
self.__shutdown_scripts = list()
|
||||||
|
|
||||||
|
def get_logon_scripts(self):
|
||||||
|
return self.__logon_scripts
|
||||||
|
def get_logoff_scripts(self):
|
||||||
|
return self.__logoff_scripts
|
||||||
|
def get_startup_scripts(self):
|
||||||
|
return self.__startup_scripts
|
||||||
|
def get_shutdown_scripts(self):
|
||||||
|
return self.__shutdown_scripts
|
||||||
|
|
||||||
|
def add_script(self, action, script):
|
||||||
|
if action == 'LOGON':
|
||||||
|
self.get_logon_scripts().append(script)
|
||||||
|
elif action == 'LOGOFF':
|
||||||
|
self.get_logoff_scripts().append(script)
|
||||||
|
elif action == 'STARTUP':
|
||||||
|
self.get_startup_scripts().append(script)
|
||||||
|
elif action == 'SHUTDOWN':
|
||||||
|
self.get_shutdown_scripts().append(script)
|
||||||
|
|
||||||
|
|
||||||
|
class Script:
|
||||||
|
__logon_counter = 0
|
||||||
|
__logoff_counter = 0
|
||||||
|
__startup_counter = 0
|
||||||
|
__shutdown_counter = 0
|
||||||
|
|
||||||
|
def __init__(self, action, script_dir, script_filename):
|
||||||
|
action_upper = action.upper()
|
||||||
|
self.action = action_upper
|
||||||
|
self.path = os.path.join(script_dir, action_upper, script_filename.upper())
|
||||||
|
if not os.path.isfile(self.path):
|
||||||
|
return None
|
||||||
|
self.args = None
|
||||||
|
|
||||||
|
if action_upper == 'LOGON':
|
||||||
|
self.number = Script.__logon_counter
|
||||||
|
Script.__logon_counter += 1
|
||||||
|
elif action_upper == 'LOGOFF':
|
||||||
|
self.number = Script.__logoff_counter
|
||||||
|
Script.__logoff_counter += 1
|
||||||
|
elif action_upper == 'STARTUP':
|
||||||
|
self.number = Script.__startup_counter
|
||||||
|
Script.__startup_counter += 1
|
||||||
|
elif action_upper == 'SHUTDOWN':
|
||||||
|
self.number = Script.__shutdown_counter
|
||||||
|
Script.__shutdown_counter += 1
|
||||||
|
|
||||||
|
def set_args(self, args):
|
||||||
|
self.args = args
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ class service:
|
|||||||
self.serviceaction = None
|
self.serviceaction = None
|
||||||
|
|
||||||
def set_clsid(self, clsid):
|
def set_clsid(self, clsid):
|
||||||
self.guid = uid
|
self.guid = clsid
|
||||||
|
|
||||||
def set_usercontext(self, usercontext=False):
|
def set_usercontext(self, usercontext=False):
|
||||||
ctx = False
|
ctx = False
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import json
|
|||||||
|
|
||||||
from util.windows import transform_windows_path
|
from util.windows import transform_windows_path
|
||||||
from util.xml import get_xml_root
|
from util.xml import get_xml_root
|
||||||
|
from util.paths import get_desktop_files_directory
|
||||||
|
|
||||||
class TargetType(Enum):
|
class TargetType(Enum):
|
||||||
FILESYSTEM = 'FILESYSTEM'
|
FILESYSTEM = 'FILESYSTEM'
|
||||||
@@ -79,12 +80,15 @@ def read_shortcuts(shortcuts_file):
|
|||||||
# URL or FILESYSTEM
|
# URL or FILESYSTEM
|
||||||
target_type = get_ttype(props.get('targetType'))
|
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_changed(link.get('changed'))
|
||||||
sc.set_clsid(link.get('clsid'))
|
sc.set_clsid(link.get('clsid'))
|
||||||
sc.set_guid(link.get('uid'))
|
sc.set_guid(link.get('uid'))
|
||||||
sc.set_usercontext(link.get('userContext', False))
|
sc.set_usercontext(link.get('userContext', False))
|
||||||
sc.set_icon(props.get('iconPath'))
|
sc.set_icon(props.get('iconPath'))
|
||||||
|
if props.get('comment'):
|
||||||
|
sc.set_comment(props.get('comment'))
|
||||||
|
|
||||||
shortcuts.append(sc)
|
shortcuts.append(sc)
|
||||||
|
|
||||||
return shortcuts
|
return shortcuts
|
||||||
@@ -100,18 +104,33 @@ def json2sc(json_str):
|
|||||||
json_obj = json.loads(json_str)
|
json_obj = json.loads(json_str)
|
||||||
link_type = get_ttype(json_obj['type'])
|
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_changed(json_obj['changed'])
|
||||||
sc.set_clsid(json_obj['clsid'])
|
sc.set_clsid(json_obj['clsid'])
|
||||||
sc.set_guid(json_obj['guid'])
|
sc.set_guid(json_obj['guid'])
|
||||||
sc.set_usercontext(json_obj['is_in_user_context'])
|
sc.set_usercontext(json_obj['is_in_user_context'])
|
||||||
|
if 'comment' in json_obj:
|
||||||
|
sc.set_comment(json_obj['comment'])
|
||||||
if 'icon' in json_obj:
|
if 'icon' in json_obj:
|
||||||
sc.set_icon(json_obj['icon'])
|
sc.set_icon(json_obj['icon'])
|
||||||
|
|
||||||
return sc
|
return sc
|
||||||
|
|
||||||
|
def find_desktop_entry(binary_path):
|
||||||
|
desktop_dir = get_desktop_files_directory()
|
||||||
|
binary_name = ''.join(binary_path.split('/')[-1])
|
||||||
|
desktop_file_path = Path(f"{desktop_dir}/{binary_name}.desktop")
|
||||||
|
|
||||||
|
if desktop_file_path.exists():
|
||||||
|
desktop_entry = DesktopEntry()
|
||||||
|
desktop_entry.parse(desktop_file_path)
|
||||||
|
return desktop_entry
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class shortcut:
|
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 dest: Path to resulting file on file system
|
||||||
:param path: Path where the link should point to
|
:param path: Path where the link should point to
|
||||||
@@ -119,14 +138,34 @@ class shortcut:
|
|||||||
:param name: Name of the application
|
:param name: Name of the application
|
||||||
:param type: Link type - FILESYSTEM or URL
|
:param type: Link type - FILESYSTEM or URL
|
||||||
'''
|
'''
|
||||||
self.dest = dest
|
self.dest = self.replace_slashes(dest)
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.expanded_path = None
|
||||||
self.arguments = arguments
|
self.arguments = arguments
|
||||||
self.name = name
|
self.name = self.replace_name(name)
|
||||||
|
self.action = action
|
||||||
self.changed = ''
|
self.changed = ''
|
||||||
self.icon = None
|
self.icon = None
|
||||||
|
self.comment = ''
|
||||||
self.is_in_user_context = self.set_usercontext()
|
self.is_in_user_context = self.set_usercontext()
|
||||||
self.type = ttype
|
self.type = ttype
|
||||||
|
self.desktop_file_template = None
|
||||||
|
|
||||||
|
def replace_slashes(self, input_path):
|
||||||
|
if input_path.startswith('%'):
|
||||||
|
index = input_path.find('%', 1)
|
||||||
|
if index != -1:
|
||||||
|
replace_path = input_path[:index + 2] + input_path[index + 2:].replace('/','-')
|
||||||
|
return replace_path
|
||||||
|
return input_path.replace('/','-')
|
||||||
|
|
||||||
|
def replace_name(self, input_name):
|
||||||
|
if input_name.startswith('%'):
|
||||||
|
index = input_name.find('%', 1)
|
||||||
|
if index != -1:
|
||||||
|
replace_name = input_name[index + 2:]
|
||||||
|
return replace_name
|
||||||
|
return input_name
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
result = self.to_json()
|
result = self.to_json()
|
||||||
@@ -147,6 +186,9 @@ class shortcut:
|
|||||||
def set_icon(self, icon_name):
|
def set_icon(self, icon_name):
|
||||||
self.icon = icon_name
|
self.icon = icon_name
|
||||||
|
|
||||||
|
def set_comment(self, comment):
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
def set_type(self, ttype):
|
def set_type(self, ttype):
|
||||||
'''
|
'''
|
||||||
Set type of the hyperlink - FILESYSTEM or URL
|
Set type of the hyperlink - FILESYSTEM or URL
|
||||||
@@ -166,6 +208,12 @@ class shortcut:
|
|||||||
|
|
||||||
self.is_in_user_context = ctx
|
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):
|
def is_usercontext(self):
|
||||||
return self.is_in_user_context
|
return self.is_in_user_context
|
||||||
|
|
||||||
@@ -181,48 +229,98 @@ class shortcut:
|
|||||||
content['clsid'] = self.clsid
|
content['clsid'] = self.clsid
|
||||||
content['guid'] = self.guid
|
content['guid'] = self.guid
|
||||||
content['changed'] = self.changed
|
content['changed'] = self.changed
|
||||||
|
content['action'] = self.action
|
||||||
content['is_in_user_context'] = self.is_in_user_context
|
content['is_in_user_context'] = self.is_in_user_context
|
||||||
content['type'] = ttype2str(self.type)
|
content['type'] = ttype2str(self.type)
|
||||||
if self.icon:
|
if self.icon:
|
||||||
content['icon'] = self.icon
|
content['icon'] = self.icon
|
||||||
|
if self.comment:
|
||||||
|
content['comment'] = self.comment
|
||||||
result = self.desktop()
|
result = self.desktop()
|
||||||
result.content.update(content)
|
result.content.update(content)
|
||||||
|
|
||||||
return json.dumps(result.content)
|
return json.dumps(result.content)
|
||||||
|
|
||||||
def desktop(self):
|
def desktop(self, dest=None):
|
||||||
'''
|
'''
|
||||||
Returns desktop file object which may be written to disk.
|
Returns desktop file object which may be written to disk.
|
||||||
'''
|
'''
|
||||||
|
if dest:
|
||||||
|
self.desktop_file = DesktopEntry(dest)
|
||||||
|
else:
|
||||||
|
self.desktop_file_template = find_desktop_entry(self.path)
|
||||||
self.desktop_file = DesktopEntry()
|
self.desktop_file = DesktopEntry()
|
||||||
self.desktop_file.addGroup('Desktop Entry')
|
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:
|
if self.type == TargetType.URL:
|
||||||
self.desktop_file.set('Type', 'Link')
|
self.desktop_file.set('Type', 'Link')
|
||||||
else:
|
else:
|
||||||
self.desktop_file.set('Type', 'Application')
|
self.desktop_file.set('Type', 'Application')
|
||||||
|
|
||||||
self.desktop_file.set('Version', '1.0')
|
|
||||||
self.desktop_file.set('Name', self.name)
|
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:
|
if self.type == TargetType.URL:
|
||||||
self.desktop_file.set('URL', self.path)
|
self.desktop_file.set('URL', desktop_path)
|
||||||
else:
|
else:
|
||||||
self.desktop_file.set('Terminal', 'false')
|
str2bool_lambda = (lambda boolstr: boolstr if isinstance(boolstr, bool)
|
||||||
self.desktop_file.set('Exec', '{} {}'.format(self.path, self.arguments))
|
else boolstr and boolstr.lower() in ['True', 'true', 'yes', '1'])
|
||||||
|
if self.desktop_file_template:
|
||||||
|
terminal_state = str2bool_lambda(self.desktop_file_template.get('Terminal'))
|
||||||
|
self.desktop_file.set('Terminal', 'true' if terminal_state else 'false')
|
||||||
|
self.desktop_file.set('Exec', '{} {}'.format(desktop_path, self.arguments))
|
||||||
|
self.desktop_file.set('Comment', self.comment)
|
||||||
|
|
||||||
if self.icon:
|
if self.icon:
|
||||||
self.desktop_file.set('Icon', self.icon)
|
self.desktop_file.set('Icon', self.icon)
|
||||||
|
elif self.desktop_file_template and self.desktop_file_template.get('Icon', False):
|
||||||
|
self.desktop_file.set('Icon', self.desktop_file_template.get('Icon'))
|
||||||
|
|
||||||
return self.desktop_file
|
def _write_desktop(self, dest, create_only=False, read_firstly=False):
|
||||||
|
|
||||||
def write_desktop(self, dest):
|
|
||||||
'''
|
'''
|
||||||
Write .desktop file to disk using path 'dest'. Please note that
|
Write .desktop file to disk using path 'dest'. Please note that
|
||||||
.desktop files must have executable bit set in order to work in
|
.desktop files must have executable bit set in order to work in
|
||||||
GUI.
|
GUI.
|
||||||
'''
|
'''
|
||||||
self.desktop().write(dest)
|
|
||||||
sc = Path(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)
|
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)
|
||||||
|
|||||||
@@ -47,33 +47,47 @@ from util.logging import log
|
|||||||
class file_runner:
|
class file_runner:
|
||||||
_gpoa_exe = '/usr/sbin/gpoa'
|
_gpoa_exe = '/usr/sbin/gpoa'
|
||||||
|
|
||||||
def __init__(self, username=None):
|
def __init__(self, loglevel, username=None):
|
||||||
self._user = username
|
self._user = username
|
||||||
|
self._loglevel = loglevel
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
'''
|
'''
|
||||||
Call gpoa utility to generate scripts
|
Call gpoa utility to generate scripts
|
||||||
'''
|
'''
|
||||||
gpoa_cmd = [self._gpoa_exe]
|
gpoa_cmd = [self._gpoa_exe]
|
||||||
|
if self._loglevel != None:
|
||||||
|
gpoa_cmd += ["--loglevel", str(self._loglevel)]
|
||||||
if self._user:
|
if self._user:
|
||||||
gpoa_cmd += [self._user]
|
gpoa_cmd += [self._user]
|
||||||
|
|
||||||
output = subprocess.call(gpoa_cmd)
|
subprocess.check_output(gpoa_cmd)
|
||||||
sys.exit(output)
|
|
||||||
|
|
||||||
def parse_cli_arguments():
|
def parse_cli_arguments():
|
||||||
'''
|
'''
|
||||||
Command line argument parser
|
Command line argument parser
|
||||||
'''
|
'''
|
||||||
argparser = argparse.ArgumentParser(description='Update group policies for the specified user')
|
argparser = argparse.ArgumentParser(description='Update group policies for computer and the specified user')
|
||||||
argparser.add_argument('-u',
|
argparser.add_argument('-u',
|
||||||
'--user',
|
'--user',
|
||||||
default=None,
|
default=None,
|
||||||
help='Name of the user for GPO update')
|
help='Name of the user for GPO update')
|
||||||
argparser.add_argument('--target',
|
argparser.add_argument('-t',
|
||||||
|
'--target',
|
||||||
default=None,
|
default=None,
|
||||||
type=str,
|
type=str.upper,
|
||||||
|
choices=["ALL", "USER", "COMPUTER"],
|
||||||
help='Specify if it is needed to update user\'s or computer\'s policies')
|
help='Specify if it is needed to update user\'s or computer\'s policies')
|
||||||
|
argparser.add_argument('-l',
|
||||||
|
'--loglevel',
|
||||||
|
type=int,
|
||||||
|
default=5,
|
||||||
|
help='Set logging verbosity level')
|
||||||
|
argparser.add_argument('-s',
|
||||||
|
'--system',
|
||||||
|
action='store_true',
|
||||||
|
default=None,
|
||||||
|
help='Run gpoa directly in system mode')
|
||||||
|
|
||||||
return argparser.parse_args()
|
return argparser.parse_args()
|
||||||
|
|
||||||
@@ -83,13 +97,14 @@ def runner_factory(args, target):
|
|||||||
factors taken into account.
|
factors taken into account.
|
||||||
'''
|
'''
|
||||||
username = None
|
username = None
|
||||||
|
target = target.upper()
|
||||||
if is_root():
|
if is_root():
|
||||||
# Only root may specify any username to update.
|
# Only root may specify any username to update.
|
||||||
try:
|
try:
|
||||||
if args.user:
|
if args.user:
|
||||||
username = pwd.getpwnam(args.user).pw_name
|
username = pwd.getpwnam(args.user).pw_name
|
||||||
else:
|
else:
|
||||||
target = 'Computer'
|
target = 'COMPUTER'
|
||||||
except:
|
except:
|
||||||
username = None
|
username = None
|
||||||
logdata = dict({'username': args.user})
|
logdata = dict({'username': args.user})
|
||||||
@@ -99,30 +114,45 @@ def runner_factory(args, target):
|
|||||||
# itself (os.getusername()).
|
# itself (os.getusername()).
|
||||||
username = pwd.getpwuid(os.getuid()).pw_name
|
username = pwd.getpwuid(os.getuid()).pw_name
|
||||||
if args.user != username:
|
if args.user != username:
|
||||||
logdata = dict({'username': args.user})
|
logdata = dict({'username': username})
|
||||||
log('W2', logdata)
|
log('W2', logdata)
|
||||||
|
|
||||||
|
if args.system:
|
||||||
|
return try_directly(username, target, args.loglevel)
|
||||||
|
else:
|
||||||
|
return try_by_oddjob(username, target)
|
||||||
|
|
||||||
|
def try_by_oddjob(username, target):
|
||||||
|
'''
|
||||||
|
Run group policies applying by oddjob service
|
||||||
|
'''
|
||||||
if is_oddjobd_gpupdate_accessible():
|
if is_oddjobd_gpupdate_accessible():
|
||||||
log('D13')
|
log('D13')
|
||||||
computer_runner = None
|
computer_runner = None
|
||||||
user_runner = None
|
user_runner = None
|
||||||
if target == 'All' or target == 'Computer':
|
if target == 'ALL' or target == 'COMPUTER':
|
||||||
computer_runner = dbus_runner()
|
computer_runner = dbus_runner()
|
||||||
if username:
|
if username:
|
||||||
if target == 'All' or target == 'User':
|
if target == 'ALL' or target == 'USER':
|
||||||
user_runner = dbus_runner(username)
|
user_runner = dbus_runner(username)
|
||||||
return (computer_runner, user_runner)
|
return (computer_runner, user_runner)
|
||||||
else:
|
else:
|
||||||
log('W3')
|
log('W3')
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def try_directly(username, target, loglevel):
|
||||||
|
'''
|
||||||
|
Run group policies applying directly
|
||||||
|
'''
|
||||||
if is_root():
|
if is_root():
|
||||||
log('D14')
|
log('D14')
|
||||||
computer_runner = None
|
computer_runner = None
|
||||||
user_runner = None
|
user_runner = None
|
||||||
if target == 'All' or target == 'Computer':
|
if target == 'ALL' or target == 'COMPUTER':
|
||||||
computer_runner = file_runner()
|
computer_runner = file_runner(loglevel)
|
||||||
if target == 'All' or target == 'User':
|
if target == 'ALL' or target == 'USER':
|
||||||
user_runner = file_runner(username)
|
user_runner = file_runner(loglevel, username)
|
||||||
return (computer_runner, user_runner)
|
return (computer_runner, user_runner)
|
||||||
else:
|
else:
|
||||||
log('E1')
|
log('E1')
|
||||||
@@ -134,7 +164,7 @@ def main():
|
|||||||
locale.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
locale.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||||
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
gettext.bindtextdomain('gpoa', '/usr/lib/python3/site-packages/gpoa/locale')
|
||||||
gettext.textdomain('gpoa')
|
gettext.textdomain('gpoa')
|
||||||
set_loglevel(0)
|
set_loglevel(args.loglevel)
|
||||||
gpo_appliers = runner_factory(args, process_target(args.target))
|
gpo_appliers = runner_factory(args, process_target(args.target))
|
||||||
|
|
||||||
if gpo_appliers:
|
if gpo_appliers:
|
||||||
|
|||||||
376
gpoa/gpupdate-setup
Executable file
376
gpoa/gpupdate-setup
Executable file
@@ -0,0 +1,376 @@
|
|||||||
|
#! /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_update = subparsers.add_parser('update',
|
||||||
|
help='Update state')
|
||||||
|
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')
|
||||||
|
|
||||||
|
parser_update.add_argument('--local-policy',
|
||||||
|
default=None,
|
||||||
|
help='Name of local policy to enable')
|
||||||
|
parser_update.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.timer and gpupdate-user.timer are enabled.
|
||||||
|
'''
|
||||||
|
is_gpupdate = is_unit_enabled('gpupdate.timer')
|
||||||
|
is_gpupdate_user = is_unit_enabled('gpupdate-user.timer', 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_disable_gpupdate_timer = ['/bin/systemctl', 'disable', 'gpupdate.timer']
|
||||||
|
cmd_disable_gpupdate_user_timer = ['/bin/systemctl', '--global', 'disable', 'gpupdate-user.timer']
|
||||||
|
cmd_control_system_auth = ['/usr/sbin/control', 'system-auth']
|
||||||
|
cmd_disable_gpupdate_scripts_service = ['/bin/systemctl', 'disable', 'gpupdate-scripts-run.service']
|
||||||
|
cmd_disable_gpupdate_scripts_user_service = ['/bin/systemctl', '--global', 'disable', 'gpupdate-scripts-run-user.service']
|
||||||
|
|
||||||
|
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)
|
||||||
|
runcmd(cmd_disable_gpupdate_timer)
|
||||||
|
runcmd(cmd_disable_gpupdate_user_timer)
|
||||||
|
runcmd(cmd_disable_gpupdate_scripts_service)
|
||||||
|
runcmd(cmd_disable_gpupdate_scripts_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', 'disable', 'gpupdate-user.service']
|
||||||
|
cmd_enable_gpupdate_timer = ['/bin/systemctl', 'enable', 'gpupdate.timer']
|
||||||
|
cmd_enable_gpupdate_user_timer = ['/bin/systemctl', '--global', 'enable', 'gpupdate-user.timer']
|
||||||
|
cmd_enable_gpupdate_scripts_service = ['/bin/systemctl', 'enable', 'gpupdate-scripts-run.service']
|
||||||
|
cmd_enable_gpupdate_user_scripts_service = ['/bin/systemctl', '--global', 'enable', 'gpupdate-scripts-run-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
|
||||||
|
# Enable gpupdate-scripts-run.service
|
||||||
|
if not rollback_on_error(cmd_enable_gpupdate_scripts_service):
|
||||||
|
return
|
||||||
|
if not is_unit_enabled('gpupdate-scripts-run.service'):
|
||||||
|
disable_gp()
|
||||||
|
return
|
||||||
|
# Enable gpupdate-scripts-run-user.service for all users
|
||||||
|
if not rollback_on_error(cmd_enable_gpupdate_user_scripts_service):
|
||||||
|
return
|
||||||
|
if not is_unit_enabled('gpupdate-scripts-run-user.service', unit_global=True):
|
||||||
|
disable_gp()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Enable gpupdate.timer
|
||||||
|
if not rollback_on_error(cmd_enable_gpupdate_timer):
|
||||||
|
return
|
||||||
|
if not is_unit_enabled('gpupdate.timer'):
|
||||||
|
disable_gp()
|
||||||
|
return
|
||||||
|
# Enable gpupdate-setup.timer for all users
|
||||||
|
if not rollback_on_error(cmd_enable_gpupdate_user_timer):
|
||||||
|
return
|
||||||
|
if not is_unit_enabled('gpupdate-user.timer', 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['update'] = 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 == 'update':
|
||||||
|
if get_status():
|
||||||
|
action[arguments.action](arguments.local_policy, arguments.backend)
|
||||||
|
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()
|
||||||
|
|
||||||
@@ -41,6 +41,30 @@ msgstr "Получен объект групповой политики"
|
|||||||
msgid "Unknown info code"
|
msgid "Unknown info code"
|
||||||
msgstr "Неизвестный код информационного сообщения"
|
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 "Установка свойств для пользователя"
|
||||||
|
|
||||||
|
msgid "The line in the configuration file was cleared"
|
||||||
|
msgstr "В конфигурационном файле была очищена строка"
|
||||||
|
|
||||||
# Error
|
# Error
|
||||||
msgid "Insufficient permissions to run gpupdate"
|
msgid "Insufficient permissions to run gpupdate"
|
||||||
msgstr "Недостаточно прав для запуска gpupdate"
|
msgstr "Недостаточно прав для запуска gpupdate"
|
||||||
@@ -132,6 +156,104 @@ msgstr "Ошибка слияния пользовательской части
|
|||||||
msgid "Unknown error code"
|
msgid "Unknown error code"
|
||||||
msgstr "Неизвестный код ошибки"
|
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 "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 асинхронно для пользователя"
|
||||||
|
|
||||||
|
msgid "Error merging user GPT (from machine GPO)"
|
||||||
|
msgstr "Ошибка слияния пользовательской групповой политики (машинная часть)"
|
||||||
|
|
||||||
|
msgid "Error cleaning directory for machine"
|
||||||
|
msgstr "Ошибка очистки каталога для машины"
|
||||||
|
|
||||||
|
msgid "Error cleaning directory for user"
|
||||||
|
msgstr "Ошибка очистки каталога для пользователя"
|
||||||
|
|
||||||
|
msgid "Error while executing command for widgets"
|
||||||
|
msgstr "Ошибка при выполнении команды для виджетов"
|
||||||
|
|
||||||
|
msgid "Error creating environment variables"
|
||||||
|
msgstr "Ошибка создания переменных среды"
|
||||||
|
|
||||||
|
msgid "Error running kwriteconfig5 command"
|
||||||
|
msgstr "Ошибка выполнения команды kwriteconfig5"
|
||||||
|
|
||||||
|
msgid "Error getting list of keys"
|
||||||
|
msgstr "Ошибка получения списка ключей"
|
||||||
|
|
||||||
|
msgid "Error getting key value"
|
||||||
|
msgstr "Ошибка при получении значения ключей"
|
||||||
|
|
||||||
|
msgid "Failed to update dconf database"
|
||||||
|
msgstr "Не удалось обновить базу данных dconf"
|
||||||
|
|
||||||
|
msgid "Exception occurred while updating dconf database"
|
||||||
|
msgstr "Возникло исключение при обновлении базы данных dconf"
|
||||||
|
|
||||||
|
# Error_end
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
msgid "The GPOA process was started for user"
|
msgid "The GPOA process was started for user"
|
||||||
msgstr "Произведён запуск GPOA для обновления политик пользователя"
|
msgstr "Произведён запуск GPOA для обновления политик пользователя"
|
||||||
@@ -286,9 +408,470 @@ msgstr "Завершена репликация GPO от контроллера
|
|||||||
msgid "Skipping HKCU branch deletion key"
|
msgid "Skipping HKCU branch deletion key"
|
||||||
msgstr "Пропускаем специальный ключ удаления ветви реестра HKCU"
|
msgstr "Пропускаем специальный ключ удаления ветви реестра HKCU"
|
||||||
|
|
||||||
|
msgid "Read domain name from configuration file"
|
||||||
|
msgstr "Имя контроллера домена для репликации прочитано из файла конфигурации"
|
||||||
|
|
||||||
|
msgid "Saving information about environment variables"
|
||||||
|
msgstr "Сохранение информации о переменных окружения"
|
||||||
|
|
||||||
msgid "Unknown debug code"
|
msgid "Unknown debug code"
|
||||||
msgstr "Неизвестный отладочный код"
|
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 "Folder creation skipped for machine"
|
||||||
|
msgstr "Создание папки для машины пропущено"
|
||||||
|
|
||||||
|
msgid "Folder creation skipped for user"
|
||||||
|
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 пользователя"
|
||||||
|
|
||||||
|
msgid "Configure user Group Policy loopback processing mode"
|
||||||
|
msgstr "Настройка режима обработки замыкания пользовательской групповой политики"
|
||||||
|
|
||||||
|
msgid "Saving information about script"
|
||||||
|
msgstr "Сохранение информации о скрипте"
|
||||||
|
|
||||||
|
msgid "No machine scripts directory to clean up"
|
||||||
|
msgstr "Нет каталога машинных скриптов для очистки"
|
||||||
|
|
||||||
|
msgid "No user scripts directory to clean up"
|
||||||
|
msgstr "Нет каталога пользовательских скриптов для очистки"
|
||||||
|
|
||||||
|
msgid "Prepare Scripts applier for machine"
|
||||||
|
msgstr "Подготовка к применению машинных скриптов"
|
||||||
|
|
||||||
|
msgid "Scripts applier for machine will not be started"
|
||||||
|
msgstr "Применение машинных скриптов не запускается"
|
||||||
|
|
||||||
|
msgid "Prepare Scripts applier for user in user context"
|
||||||
|
msgstr "Подготовка к применению скриптов пользователя в его контексте"
|
||||||
|
|
||||||
|
msgid "Scripts applier for user in user context will not be started"
|
||||||
|
msgstr "Применение скриптов пользователя в его контексте не запускается"
|
||||||
|
|
||||||
|
msgid "Clean machine scripts directory"
|
||||||
|
msgstr "Очистка каталога машинных скриптов"
|
||||||
|
|
||||||
|
msgid "Clean user scripts directory"
|
||||||
|
msgstr "Очистка каталога пользовательских скриптов"
|
||||||
|
|
||||||
|
msgid "Saving information about file"
|
||||||
|
msgstr "Сохранение информации о файле"
|
||||||
|
|
||||||
|
msgid "Failed to return file path"
|
||||||
|
msgstr "Не удалось вернуть путь к файлу"
|
||||||
|
|
||||||
|
msgid "Failed to create file"
|
||||||
|
msgstr "Не удалось создать файл"
|
||||||
|
|
||||||
|
msgid "Failed to delete file"
|
||||||
|
msgstr "Не удалось удалить файл"
|
||||||
|
|
||||||
|
msgid "Failed to update file"
|
||||||
|
msgstr "Не удалось обновить файл"
|
||||||
|
|
||||||
|
msgid "Running File copy applier for machine"
|
||||||
|
msgstr "Запуск применение настроек копирования файлов для машины"
|
||||||
|
|
||||||
|
msgid "Running File copy applier for machine will not be started"
|
||||||
|
msgstr "Применение настроек копирования файлов для машины не будет запущено"
|
||||||
|
|
||||||
|
msgid "Running File copy applier for user in administrator context"
|
||||||
|
msgstr "Запуск применение настроек копирования файлов для пользователя в контексте администратора"
|
||||||
|
|
||||||
|
msgid "Running File copy applier for user in administrator context will not be started"
|
||||||
|
msgstr "Применение настроек копирования файлов для пользователя в контексте администратора не будет запущено"
|
||||||
|
|
||||||
|
msgid "Running ini applier for machine"
|
||||||
|
msgstr "Запуск применение настроек ini файлов для машины"
|
||||||
|
|
||||||
|
msgid "Running ini applier for machine will not be started"
|
||||||
|
msgstr "Применение настроек ini файлов для машины не будет запущено"
|
||||||
|
|
||||||
|
msgid "Running ini applier for user in user context"
|
||||||
|
msgstr "Запуск применение настроек ini файлов для пользователя в контексте пользователя"
|
||||||
|
|
||||||
|
msgid "Running ini applier for user in user context will not be started"
|
||||||
|
msgstr "Применение настроек ini файлов для пользователя в контексте пользователя не будет запущено"
|
||||||
|
|
||||||
|
msgid "Ini-file path not recognized"
|
||||||
|
msgstr "Путь к ini-файлу не распознан"
|
||||||
|
|
||||||
|
msgid "Ini-file is not readable"
|
||||||
|
msgstr "Ini-файл не читается"
|
||||||
|
|
||||||
|
msgid "Saving information about ini-file"
|
||||||
|
msgstr "Сохранение информации об ini-файле"
|
||||||
|
|
||||||
|
msgid "Dictionary key generation failed"
|
||||||
|
msgstr "Формирования ключа словаря не удалось"
|
||||||
|
|
||||||
|
msgid "Running CIFS applier for machine"
|
||||||
|
msgstr "Запуск применение настроек CIFS для машины"
|
||||||
|
|
||||||
|
msgid "CIFS applier for machine will not be started"
|
||||||
|
msgstr "Применение настроек CIFS для машины не будет запущено"
|
||||||
|
|
||||||
|
msgid "Saving information about network shares"
|
||||||
|
msgstr "Сохранение информации о сетевых ресурсах"
|
||||||
|
|
||||||
|
msgid "Running networkshare applier for machine"
|
||||||
|
msgstr "Запуск применение настроек сетевых каталогов для машины"
|
||||||
|
|
||||||
|
msgid "Running networkshare applier for machine will not be starte"
|
||||||
|
msgstr "Применение настроек сетевых каталогов для машины не будет запущено"
|
||||||
|
|
||||||
|
msgid "Apply network share data action failed"
|
||||||
|
msgstr "Не удалось применить действие с данными общего сетевого ресурса"
|
||||||
|
|
||||||
|
msgid "Running yandex_browser_applier for machine"
|
||||||
|
msgstr "Запуск yandex_browser_applier для машины"
|
||||||
|
|
||||||
|
msgid "Yandex_browser_applier for machine will not be started"
|
||||||
|
msgstr "Yandex_browser_applier для машины не запустится"
|
||||||
|
|
||||||
|
msgid "Wrote YandexBrowser preferences to"
|
||||||
|
msgstr "Запись настройки Яндекс Браузера в"
|
||||||
|
|
||||||
|
msgid "Running networkshare applier for user"
|
||||||
|
msgstr "Запуск применение настроек сетевых каталогов для пользователя"
|
||||||
|
|
||||||
|
msgid "File copy"
|
||||||
|
msgstr "Копирование файла"
|
||||||
|
|
||||||
|
msgid "Running networkshare applier for user will not be started"
|
||||||
|
msgstr "Применение настроек сетевых каталогов для пользователя не будет запущено"
|
||||||
|
|
||||||
|
msgid "File update"
|
||||||
|
msgstr "Обновление файла"
|
||||||
|
|
||||||
|
msgid "Applying settings for network share"
|
||||||
|
msgstr "Применение настроек для сетевой папки"
|
||||||
|
|
||||||
|
msgid "Deleting a file"
|
||||||
|
msgstr "Удаление файла"
|
||||||
|
|
||||||
|
msgid "Running GPOA by root for user"
|
||||||
|
msgstr "Запуск GPOA от root для пользователя"
|
||||||
|
|
||||||
|
msgid "The GPOA process was started for computer"
|
||||||
|
msgstr "Процесс GPOA запущен для компьютера"
|
||||||
|
|
||||||
|
msgid "Running networkshare applier for machine will not be started"
|
||||||
|
msgstr "Применение настроек сетевых каталогов для машины не будет запущено"
|
||||||
|
|
||||||
|
msgid "Failed to create a symlink to the network drives mountpoint"
|
||||||
|
msgstr "Не удалось создать ссылку на точку монтирования сетевых дисков пользователя"
|
||||||
|
|
||||||
|
msgid "Failed to create a symlink to the system network drives mountpoint"
|
||||||
|
msgstr "Не удалось создать ссылку на точку монтирования системных сетевых дисков"
|
||||||
|
|
||||||
|
msgid "Failed to create a symlink to the hidden network drives mountpoint"
|
||||||
|
msgstr "Не удалось создать ссылку на точку монтирования скрытых сетевых дисков пользователя"
|
||||||
|
|
||||||
|
msgid "Failed to create a symlink to the hidden system network drives mountpoint"
|
||||||
|
msgstr "Не удалось создать ссылку на точку монтирования скрытых системных сетевых дисков"
|
||||||
|
|
||||||
|
msgid "Running KDE applier for machine"
|
||||||
|
msgstr "Запуск применения настроек KDE для машины"
|
||||||
|
|
||||||
|
msgid "KDE applier for machine will not be started"
|
||||||
|
msgstr "Применение настроек KDE для машины не удалось"
|
||||||
|
|
||||||
|
msgid "Running KDE applier for user in user context"
|
||||||
|
msgstr "Запуск применения настроек KDE в контексте пользователя"
|
||||||
|
|
||||||
|
msgid "KDE applier for user in user context will not be started"
|
||||||
|
msgstr "KDE в контексте пользователя не запускается"
|
||||||
|
|
||||||
|
msgid "Changing the configuration file"
|
||||||
|
msgstr "Изменение конфигурационного файла"
|
||||||
|
|
||||||
|
msgid "Widget command completed successfully"
|
||||||
|
msgstr "Команда для виджетов выполнена успешно"
|
||||||
|
|
||||||
|
msgid "Getting a list of keys"
|
||||||
|
msgstr "Получение списка ключей"
|
||||||
|
|
||||||
|
msgid "Getting the key value"
|
||||||
|
msgstr "Получение значения ключа"
|
||||||
|
|
||||||
|
msgid "Successfully updated dconf database"
|
||||||
|
msgstr "База данных dconf успешно обновлена"
|
||||||
|
|
||||||
|
msgid "Creating a dictionary with keys and values from the dconf database"
|
||||||
|
msgstr "Формирование словаря с ключами и значениями из базы dconf"
|
||||||
|
|
||||||
|
msgid "No entry found for the specified path"
|
||||||
|
msgstr "Не найдено записей по указанному пути"
|
||||||
|
|
||||||
|
msgid "Creating an ini file with policies for dconf"
|
||||||
|
msgstr "Создание ini-файла с политиками для dconf"
|
||||||
|
|
||||||
|
msgid "GPO version was not found"
|
||||||
|
msgstr "Версия GPO не найдена"
|
||||||
|
|
||||||
|
# Debug_end
|
||||||
|
|
||||||
# Warning
|
# Warning
|
||||||
msgid "Unable to perform gpupdate for non-existent user, will update machine settings"
|
msgid "Unable to perform gpupdate for non-existent user, will update machine settings"
|
||||||
msgstr "Невозможно запустить gpupdate для несуществующего пользователя, будут обновлены настройки машины"
|
msgstr "Невозможно запустить gpupdate для несуществующего пользователя, будут обновлены настройки машины"
|
||||||
@@ -309,10 +892,70 @@ msgstr "Пакет ADP не установлен, плагин не будет
|
|||||||
msgid "Unknown warning code"
|
msgid "Unknown warning code"
|
||||||
msgstr "Неизвестный код предупреждения"
|
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"
|
||||||
|
|
||||||
|
msgid "Failed to read the list of files"
|
||||||
|
msgstr "Не удалось прочитать список файлов"
|
||||||
|
|
||||||
|
msgid "Failed to caching the file"
|
||||||
|
msgstr "Не удалось кэшировать файл"
|
||||||
|
|
||||||
|
msgid "Could not create a valid list of keys"
|
||||||
|
msgstr "Не удалось создать допустимый список ключей"
|
||||||
|
|
||||||
|
msgid "Failed to copy file"
|
||||||
|
msgstr "Не удалось скопировать файл"
|
||||||
|
|
||||||
|
msgid "Failed to create KDE settings list"
|
||||||
|
msgstr "Не удалось создать список настроек KDE"
|
||||||
|
|
||||||
|
msgid "Could not find application tools"
|
||||||
|
msgstr "Не удалось найти инструменты применения"
|
||||||
|
|
||||||
|
msgid "Failed to open KDE settings"
|
||||||
|
msgstr "Не удалось открыть настройки KDE"
|
||||||
|
|
||||||
|
msgid "Failed to change KDE configuration file"
|
||||||
|
msgstr "Не удалось изменить файл конфигурации KDE"
|
||||||
|
|
||||||
|
msgid "Error connecting to server"
|
||||||
|
msgstr "Ошибка при подключении к серверу"
|
||||||
|
|
||||||
|
msgid "Wallpaper configuration file not found"
|
||||||
|
msgstr "Конфигурационный файл для обоев не найден"
|
||||||
|
|
||||||
|
msgid "The user setting was not installed, conflict with computer setting"
|
||||||
|
msgstr "Пользовательская настройка не была установлена, конфликт с настройкой компьютера"
|
||||||
|
|
||||||
|
msgid "Action for ini file failed"
|
||||||
|
msgstr "Не удалось выполнить действие для INI-файла"
|
||||||
|
|
||||||
|
msgid "Couldn't get the uid"
|
||||||
|
msgstr "Не удалось получить uid"
|
||||||
|
|
||||||
# Fatal
|
# Fatal
|
||||||
msgid "Unable to refresh GPO list"
|
msgid "Unable to refresh GPO list"
|
||||||
msgstr "Невозможно обновить список объектов групповых политик"
|
msgstr "Невозможно обновить список объектов групповых политик"
|
||||||
|
|
||||||
|
msgid "Error getting GPTs for machine"
|
||||||
|
msgstr "Не удалось получить GPT для машины"
|
||||||
|
|
||||||
|
msgid "Error getting GPTs for user"
|
||||||
|
msgstr "Не удалось получить GPT для пользователя"
|
||||||
|
|
||||||
msgid "Unknown fatal code"
|
msgid "Unknown fatal code"
|
||||||
msgstr "Неизвестный код фатальной ошибки"
|
msgstr "Неизвестный код фатальной ошибки"
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ def info_code(code):
|
|||||||
info_ids = dict()
|
info_ids = dict()
|
||||||
info_ids[1] = 'Got GPO list for username'
|
info_ids[1] = 'Got GPO list for username'
|
||||||
info_ids[2] = 'Got GPO'
|
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'
|
||||||
|
info_ids[10] = 'The line in the configuration file was cleared'
|
||||||
|
|
||||||
return info_ids.get(code, 'Unknown info code')
|
return info_ids.get(code, 'Unknown info code')
|
||||||
|
|
||||||
@@ -57,7 +65,48 @@ def error_code(code):
|
|||||||
error_ids[27] = 'Error merging user GPT'
|
error_ids[27] = 'Error merging user GPT'
|
||||||
error_ids[28] = 'Error merging machine part of GPT'
|
error_ids[28] = 'Error merging machine part of GPT'
|
||||||
error_ids[29] = 'Error merging user 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[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'
|
||||||
|
error_ids[63] = 'Error merging user GPT (from machine GPO)'
|
||||||
|
error_ids[64] = 'Error to cleanup directory for machine'
|
||||||
|
error_ids[65] = 'Error to cleanup directory for user'
|
||||||
|
error_ids[66] = 'Error while executing command for widgets'
|
||||||
|
error_ids[67] = 'Error creating environment variables'
|
||||||
|
error_ids[68] = 'Error running kwriteconfig5 command'
|
||||||
|
error_ids[69] = 'Error getting list of keys'
|
||||||
|
error_ids[70] = 'Error getting key value'
|
||||||
|
error_ids[71] = 'Failed to update dconf database'
|
||||||
|
error_ids[72] = 'Exception occurred while updating dconf database'
|
||||||
return error_ids.get(code, 'Unknown error code')
|
return error_ids.get(code, 'Unknown error code')
|
||||||
|
|
||||||
def debug_code(code):
|
def debug_code(code):
|
||||||
@@ -113,6 +162,165 @@ def debug_code(code):
|
|||||||
debug_ids[49] = 'Started GPO replication from AD DC'
|
debug_ids[49] = 'Started GPO replication from AD DC'
|
||||||
debug_ids[50] = 'Finished GPO replication from AD DC'
|
debug_ids[50] = 'Finished GPO replication from AD DC'
|
||||||
debug_ids[51] = 'Skipping HKCU branch deletion key'
|
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] = 'Folder creation skipped for machine'
|
||||||
|
debug_ids[110] = 'Folder creation skipped for user'
|
||||||
|
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'
|
||||||
|
debug_ids[152] = 'Configure user Group Policy loopback processing mode'
|
||||||
|
debug_ids[153] = 'Saving information about script'
|
||||||
|
debug_ids[154] = 'No machine scripts directory to clean up'
|
||||||
|
debug_ids[155] = 'No user scripts directory to clean up'
|
||||||
|
debug_ids[156] = 'Prepare Scripts applier for machine'
|
||||||
|
debug_ids[157] = 'Scripts applier for machine will not be started'
|
||||||
|
debug_ids[158] = 'Prepare Scripts applier for user in user context'
|
||||||
|
debug_ids[159] = 'Scripts applier for user in user context will not be started'
|
||||||
|
debug_ids[160] = 'Clean machine scripts directory'
|
||||||
|
debug_ids[161] = 'Clean user scripts directory'
|
||||||
|
debug_ids[162] = 'Saving information about file'
|
||||||
|
debug_ids[163] = 'Failed to return file path'
|
||||||
|
debug_ids[164] = 'Failed to create file'
|
||||||
|
debug_ids[165] = 'Failed to delete file'
|
||||||
|
debug_ids[166] = 'Failed to update file'
|
||||||
|
debug_ids[167] = 'Running File copy applier for machine'
|
||||||
|
debug_ids[168] = 'Running File copy applier for machine will not be started'
|
||||||
|
debug_ids[169] = 'Running File copy applier for user in administrator context'
|
||||||
|
debug_ids[170] = 'Running File copy applier for user in administrator context will not be started'
|
||||||
|
debug_ids[171] = 'Running ini applier for machine'
|
||||||
|
debug_ids[172] = 'Running ini applier for machine will not be started'
|
||||||
|
debug_ids[173] = 'Running ini applier for user in user context'
|
||||||
|
debug_ids[174] = 'Running ini applier for user in user context will not be started'
|
||||||
|
debug_ids[175] = 'Ini-file path not recognized'
|
||||||
|
debug_ids[176] = 'Ini-file is not readable'
|
||||||
|
debug_ids[177] = 'Saving information about ini-file'
|
||||||
|
debug_ids[178] = 'Dictionary key generation failed'
|
||||||
|
debug_ids[179] = 'Running CIFS applier for machine'
|
||||||
|
debug_ids[180] = 'CIFS applier for machine will not be started'
|
||||||
|
debug_ids[181] = 'Running networkshare applier for machine will not be started'
|
||||||
|
debug_ids[182] = 'Apply network share data action failed'
|
||||||
|
debug_ids[183] = 'Running yandex_browser_applier for machine'
|
||||||
|
debug_ids[184] = 'Yandex_browser_applier for machine will not be started'
|
||||||
|
debug_ids[185] = 'Wrote YandexBrowser preferences to'
|
||||||
|
debug_ids[186] = 'Saving information about network shares'
|
||||||
|
debug_ids[187] = 'Running networkshare applier for machine'
|
||||||
|
debug_ids[188] = 'Running networkshare applier for user'
|
||||||
|
debug_ids[189] = 'Running networkshare applier for user will not be started'
|
||||||
|
debug_ids[190] = 'Applying settings for network share'
|
||||||
|
debug_ids[191] = 'File copy'
|
||||||
|
debug_ids[192] = 'File update'
|
||||||
|
debug_ids[193] = 'Deleting a file'
|
||||||
|
debug_ids[194] = 'Failed to create a symlink to the network drives mountpoint'
|
||||||
|
debug_ids[195] = 'Failed to create a symlink to the system network drives mountpoint'
|
||||||
|
debug_ids[196] = 'Failed to create a symlink to the hidden network drives mountpoint'
|
||||||
|
debug_ids[197] = 'Failed to create a symlink to the hidden system network drives mountpoint'
|
||||||
|
debug_ids[198] = 'Running KDE applier for machine'
|
||||||
|
debug_ids[199] = 'KDE applier for machine will not be started'
|
||||||
|
debug_ids[200] = 'Running KDE applier for user in user context'
|
||||||
|
debug_ids[201] = 'KDE applier for user in user context will not be started'
|
||||||
|
debug_ids[202] = 'Changing the configuration file'
|
||||||
|
debug_ids[203] = 'Widget command completed successfully'
|
||||||
|
debug_ids[204] = 'Getting a list of keys'
|
||||||
|
debug_ids[205] = 'Getting the key value'
|
||||||
|
debug_ids[206] = 'Successfully updated dconf database'
|
||||||
|
debug_ids[207] = 'Creating a dictionary with keys and values from the dconf database'
|
||||||
|
debug_ids[208] = 'No entry found for the specified path'
|
||||||
|
debug_ids[209] = 'Creating an ini file with policies for dconf'
|
||||||
|
debug_ids[210] = 'GPO version was not found'
|
||||||
|
|
||||||
return debug_ids.get(code, 'Unknown debug code')
|
return debug_ids.get(code, 'Unknown debug code')
|
||||||
|
|
||||||
@@ -129,12 +337,34 @@ def warning_code(code):
|
|||||||
warning_ids[3] = 'oddjobd is inaccessible'
|
warning_ids[3] = 'oddjobd is inaccessible'
|
||||||
warning_ids[4] = 'No SYSVOL entry assigned to GPO'
|
warning_ids[4] = 'No SYSVOL entry assigned to GPO'
|
||||||
warning_ids[5] = 'ADP package is not installed - plugin will not be initialized'
|
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'
|
||||||
|
warning_ids[12] = 'Failed to read the list of files'
|
||||||
|
warning_ids[13] = 'Failed to caching the file'
|
||||||
|
warning_ids[14] = 'Could not create a valid list of keys'
|
||||||
|
warning_ids[15] = 'Failed to copy file'
|
||||||
|
warning_ids[16] = 'Failed to create KDE settings list'
|
||||||
|
warning_ids[17] = 'Could not find application tools'
|
||||||
|
warning_ids[18] = 'Failed to open KDE settings'
|
||||||
|
warning_ids[19] = 'Failed to change KDE configuration file'
|
||||||
|
warning_ids[20] = 'Error connecting to server'
|
||||||
|
warning_ids[21] = 'Wallpaper configuration file not found'
|
||||||
|
warning_ids[22] = 'The user setting was not installed, conflict with computer setting'
|
||||||
|
warning_ids[23] = 'Action for ini file failed'
|
||||||
|
warning_ids[24] = 'Couldn\'t get the uid'
|
||||||
|
|
||||||
|
|
||||||
return warning_ids.get(code, 'Unknown warning code')
|
return warning_ids.get(code, 'Unknown warning code')
|
||||||
|
|
||||||
def fatal_code(code):
|
def fatal_code(code):
|
||||||
fatal_ids = dict()
|
fatal_ids = dict()
|
||||||
fatal_ids[1] = 'Unable to refresh GPO list'
|
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')
|
return fatal_ids.get(code, 'Unknown fatal code')
|
||||||
|
|
||||||
|
|||||||
144
gpoa/pkcon_runner
Executable file
144
gpoa/pkcon_runner
Executable file
@@ -0,0 +1,144 @@
|
|||||||
|
#!/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, user = None):
|
||||||
|
self.__install_key_name = 'Install'
|
||||||
|
self.__remove_key_name = 'Remove'
|
||||||
|
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(username=user)
|
||||||
|
self.storage.filling_storage_from_dconf()
|
||||||
|
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.get_key_value(install_branch)
|
||||||
|
self.remove_packages_setting = self.storage.get_key_value(remove_branch)
|
||||||
|
for package in self.install_packages_setting:
|
||||||
|
if not is_rpm_installed(package):
|
||||||
|
self.install_packages.add(package)
|
||||||
|
for package in self.remove_packages_setting:
|
||||||
|
if package in self.install_packages:
|
||||||
|
self.install_packages.remove(package)
|
||||||
|
if is_rpm_installed(package):
|
||||||
|
self.remove_packages.add(package)
|
||||||
|
|
||||||
|
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('--user', type = str, help = 'user', nargs = '?', default = None)
|
||||||
|
parser.add_argument('--loglevel', type = int, help = 'loglevel', nargs = '?', default = 30)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
logger.setLevel(args.loglevel)
|
||||||
|
if args.user:
|
||||||
|
applier = Pkcon_applier(args.user)
|
||||||
|
else:
|
||||||
|
applier = Pkcon_applier()
|
||||||
|
applier.apply()
|
||||||
|
|
||||||
156
gpoa/scripts_runner
Executable file
156
gpoa/scripts_runner
Executable file
@@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 subprocess
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import psutil
|
||||||
|
import time
|
||||||
|
|
||||||
|
class Scripts_runner:
|
||||||
|
'''
|
||||||
|
A class for an object that iterates over directories with scripts
|
||||||
|
in the desired sequence and launches them
|
||||||
|
'''
|
||||||
|
def __init__(self, work_mode = None, user_name = None, action = None):
|
||||||
|
self.dir_scripts_machine = '/var/cache/gpupdate_scripts_cache/machine/'
|
||||||
|
self.dir_scripts_users = '/var/cache/gpupdate_scripts_cache/users/'
|
||||||
|
self.user_name = user_name
|
||||||
|
self.list_with_all_commands = list()
|
||||||
|
stack_dir = None
|
||||||
|
if work_mode and work_mode.upper() == 'MACHINE':
|
||||||
|
stack_dir = self.machine_runner_fill()
|
||||||
|
elif work_mode and work_mode.upper() == 'USER':
|
||||||
|
stack_dir = self.user_runner_fill()
|
||||||
|
else:
|
||||||
|
print('Invalid arguments entered')
|
||||||
|
return
|
||||||
|
if action:
|
||||||
|
self.action = action.upper()
|
||||||
|
else:
|
||||||
|
print('Action needed')
|
||||||
|
return
|
||||||
|
|
||||||
|
self.find_action(stack_dir)
|
||||||
|
for it_cmd in self.list_with_all_commands:
|
||||||
|
print(self.run_cmd_subprocess(it_cmd))
|
||||||
|
|
||||||
|
def user_runner_fill(self):
|
||||||
|
return self.get_stack_dir(self.dir_scripts_users + self.user_name)
|
||||||
|
|
||||||
|
def machine_runner_fill(self):
|
||||||
|
return self.get_stack_dir(self.dir_scripts_machine)
|
||||||
|
|
||||||
|
def get_stack_dir(self, path_dir):
|
||||||
|
stack_dir = list()
|
||||||
|
try:
|
||||||
|
dir_script = Path(path_dir)
|
||||||
|
for it_dir in dir_script.iterdir():
|
||||||
|
stack_dir.append(str(it_dir))
|
||||||
|
return stack_dir
|
||||||
|
except Exception as exc:
|
||||||
|
print(exc)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def find_action(self, stack_dir):
|
||||||
|
if not stack_dir:
|
||||||
|
return
|
||||||
|
list_tmp = list()
|
||||||
|
while stack_dir:
|
||||||
|
path_turn = stack_dir.pop()
|
||||||
|
basename = os.path.basename(path_turn)
|
||||||
|
if basename == self.action:
|
||||||
|
list_tmp = self.get_stack_dir(path_turn)
|
||||||
|
if list_tmp:
|
||||||
|
self.fill_list_cmd(list_tmp)
|
||||||
|
|
||||||
|
|
||||||
|
def fill_list_cmd(self, list_tmp):
|
||||||
|
list_tmp = sorted(list_tmp)
|
||||||
|
for file_in_task_dir in list_tmp:
|
||||||
|
suffix = os.path.basename(file_in_task_dir)[-4:]
|
||||||
|
if suffix == '.arg':
|
||||||
|
try:
|
||||||
|
arg = self.read_args(file_in_task_dir)
|
||||||
|
for it_arg in arg.split():
|
||||||
|
self.list_with_all_commands[-1].append(it_arg)
|
||||||
|
except Exception as exc:
|
||||||
|
print('Argument read for {}: {}'.format(self.list_with_all_commands.pop(), exc))
|
||||||
|
else:
|
||||||
|
cmd = list()
|
||||||
|
cmd.append(file_in_task_dir)
|
||||||
|
self.list_with_all_commands.append(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def read_args(self, path):
|
||||||
|
with open(path + '/arg') as f:
|
||||||
|
args = f.readlines()
|
||||||
|
return args[0]
|
||||||
|
|
||||||
|
def run_cmd_subprocess(self, cmd):
|
||||||
|
try:
|
||||||
|
subprocess.run(cmd)
|
||||||
|
return 'Script run: {}'.format(cmd)
|
||||||
|
except Exception as exc:
|
||||||
|
return exc
|
||||||
|
|
||||||
|
def find_process_by_name_and_script(name, script_path):
|
||||||
|
|
||||||
|
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
|
||||||
|
try:
|
||||||
|
# Check if the process name matches and the script path is in the command line arguments
|
||||||
|
if proc.info['name'] == name and script_path in proc.info['cmdline']:
|
||||||
|
return proc
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
def wait_for_process(name, script_path, check_interval=1):
|
||||||
|
|
||||||
|
process = find_process_by_name_and_script(name, script_path)
|
||||||
|
if not process:
|
||||||
|
print(f"Process with name {name} and script path {script_path} not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Loop to wait for the process to finish
|
||||||
|
while process.is_running():
|
||||||
|
print(f"Waiting for process {name} with PID {process.pid} to finish...")
|
||||||
|
time.sleep(check_interval)
|
||||||
|
print(f"Process {name} with PID {process.pid} has finished.")
|
||||||
|
return
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
|
print(f"Process {name} with PID {process.pid} is no longer accessible.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Scripts runner')
|
||||||
|
parser.add_argument('--mode', type = str, help = 'MACHINE or USER', nargs = '?', default = None)
|
||||||
|
parser.add_argument('--user', type = str, help = 'User name ', nargs = '?', default = None)
|
||||||
|
parser.add_argument('--action', type = str, help = 'MACHINE : [STARTUP or SHUTDOWN], USER : [LOGON or LOGOFF]', nargs = '?', default = None)
|
||||||
|
|
||||||
|
process_name = "python3"
|
||||||
|
script_path = "/usr/sbin/gpoa"
|
||||||
|
wait_for_process(process_name, script_path)
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
Scripts_runner(args.mode, args.user, args.action)
|
||||||
|
except Exception as exc:
|
||||||
|
print(exc)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2023 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,12 +16,19 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from .sqlite_registry import sqlite_registry
|
|
||||||
from .sqlite_cache import sqlite_cache
|
|
||||||
|
|
||||||
def cache_factory(cache_name):
|
from storage.dconf_registry import Dconf_registry
|
||||||
return sqlite_cache(cache_name)
|
|
||||||
|
|
||||||
def registry_factory(registry_name='registry', registry_dir=None):
|
def registry_factory(registry_name='', envprofile=None , username=None):
|
||||||
return sqlite_registry(registry_name, registry_dir)
|
if username:
|
||||||
|
Dconf_registry._username = username
|
||||||
|
else:
|
||||||
|
Dconf_registry._envprofile = 'system'
|
||||||
|
if envprofile:
|
||||||
|
Dconf_registry._envprofile = envprofile
|
||||||
|
|
||||||
|
if registry_name == 'dconf':
|
||||||
|
return Dconf_registry()
|
||||||
|
else:
|
||||||
|
return Dconf_registry
|
||||||
|
|
||||||
|
|||||||
591
gpoa/storage/dconf_registry.py
Normal file
591
gpoa/storage/dconf_registry.py
Normal file
@@ -0,0 +1,591 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2023 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 subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from util.util import string_to_literal_eval, touch_file, get_uid_by_username
|
||||||
|
from util.logging import log
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class PregDconf():
|
||||||
|
def __init__(self, keyname, valuename, type_preg, data):
|
||||||
|
self.keyname = keyname
|
||||||
|
self.valuename = valuename
|
||||||
|
self.hive_key = '{}/{}'.format(self.keyname, self.valuename)
|
||||||
|
self.type = type_preg
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
|
||||||
|
class gplist(list):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def first(self):
|
||||||
|
if self:
|
||||||
|
return self[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def count(self):
|
||||||
|
return len(self)
|
||||||
|
|
||||||
|
class Dconf_registry():
|
||||||
|
'''
|
||||||
|
A class variable that represents a global registry dictionary shared among instances of the class
|
||||||
|
'''
|
||||||
|
_ReadQueue = 'Software/BaseALT/Policies/ReadQueue'
|
||||||
|
global_registry_dict = dict({_ReadQueue:{}})
|
||||||
|
__template_file = '/usr/share/dconf/user_mandatory.template'
|
||||||
|
_policies_path = 'Software/'
|
||||||
|
_policies_win_path = 'SOFTWARE/'
|
||||||
|
_gpt_read_flag = False
|
||||||
|
__dconf_dict_flag = False
|
||||||
|
__dconf_dict = dict()
|
||||||
|
_username = None
|
||||||
|
_envprofile = None
|
||||||
|
|
||||||
|
list_keys = list()
|
||||||
|
_info = dict()
|
||||||
|
|
||||||
|
shortcuts = list()
|
||||||
|
folders = list()
|
||||||
|
files = list()
|
||||||
|
drives = list()
|
||||||
|
scheduledtasks = list()
|
||||||
|
environmentvariables = list()
|
||||||
|
inifiles = list()
|
||||||
|
services = list()
|
||||||
|
printers = list()
|
||||||
|
scripts = list()
|
||||||
|
networkshares = list()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_info(cls, key , data):
|
||||||
|
cls._info[key] = data
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_info(cls, key):
|
||||||
|
return cls._info.setdefault(key, None)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_matching_keys(path):
|
||||||
|
if path[0] != '/':
|
||||||
|
path = '/' + path
|
||||||
|
logdata = dict()
|
||||||
|
envprofile = get_dconf_envprofile()
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(['dconf', 'list', path],
|
||||||
|
env=envprofile, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
logdata['path'] = path
|
||||||
|
log('D204', logdata)
|
||||||
|
output, error = process.communicate()
|
||||||
|
if not output and not error:
|
||||||
|
return
|
||||||
|
if not error:
|
||||||
|
keys = output.strip().split('\n')
|
||||||
|
for key in keys:
|
||||||
|
Dconf_registry.get_matching_keys(f'{path}{key}')
|
||||||
|
else:
|
||||||
|
Dconf_registry.list_keys.append(path)
|
||||||
|
return Dconf_registry.list_keys
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E69', logdata)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_key_values(keys):
|
||||||
|
key_values = {}
|
||||||
|
for key in keys:
|
||||||
|
key_values[key] = Dconf_registry.get_key_value(key)
|
||||||
|
return key_values
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_key_value(key):
|
||||||
|
logdata = dict()
|
||||||
|
envprofile = get_dconf_envprofile()
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(['dconf', 'read', key],
|
||||||
|
env=envprofile, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
logdata['key'] = key
|
||||||
|
output, error = process.communicate()
|
||||||
|
|
||||||
|
if not error:
|
||||||
|
return string_to_literal_eval(string_to_literal_eval(output))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E70', logdata)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dconf_update():
|
||||||
|
logdata = dict()
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(['dconf', 'update'],
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
output, error = process.communicate()
|
||||||
|
|
||||||
|
if error:
|
||||||
|
logdata['error'] = error
|
||||||
|
log('E71', logdata)
|
||||||
|
else:
|
||||||
|
logdata['outpupt'] = output
|
||||||
|
log('D206', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
logdata['exc'] = exc
|
||||||
|
log('E72', logdata)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_profile_template(cls):
|
||||||
|
if Path(cls.__template_file).exists():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def apply_template(cls, uid):
|
||||||
|
logdata = dict()
|
||||||
|
if uid and cls.check_profile_template():
|
||||||
|
with open(cls.__template_file, "r") as f:
|
||||||
|
template = f.read()
|
||||||
|
# Replace the "{uid}" placeholder with the actual UID value
|
||||||
|
content = template.replace("{{uid}}", str(uid))
|
||||||
|
|
||||||
|
elif uid:
|
||||||
|
content = f"user-db:user\n" \
|
||||||
|
f"system-db:policy\n" \
|
||||||
|
f"system-db:policy{uid}\n" \
|
||||||
|
f"system-db:local\n" \
|
||||||
|
f"system-db:default\n" \
|
||||||
|
f"system-db:local\n" \
|
||||||
|
f"system-db:policy{uid}\n" \
|
||||||
|
f"system-db:policy\n"
|
||||||
|
else:
|
||||||
|
logdata['uid'] = uid
|
||||||
|
log('W24', logdata)
|
||||||
|
return
|
||||||
|
|
||||||
|
user_mandatory = f'/run/dconf/user/{uid}'
|
||||||
|
touch_file(user_mandatory)
|
||||||
|
|
||||||
|
with open(user_mandatory, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_policies_from_dconf(cls):
|
||||||
|
return cls.get_dictionary_from_dconf(cls._policies_path, cls._policies_win_path)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_dictionary_from_dconf(self, *startswith_list):
|
||||||
|
output_dict = {}
|
||||||
|
for startswith in startswith_list:
|
||||||
|
dconf_dict = self.get_key_values(self.get_matching_keys(startswith))
|
||||||
|
for key, value in dconf_dict.items():
|
||||||
|
keys_tmp = key.split('/')
|
||||||
|
update_dict(output_dict.setdefault('/'.join(keys_tmp[:-1])[1:], {}), {keys_tmp[-1]: str(value)})
|
||||||
|
|
||||||
|
log('D207')
|
||||||
|
return output_dict
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_entries(cls, startswith):
|
||||||
|
if startswith[-1] == '%':
|
||||||
|
startswith = startswith[:-1]
|
||||||
|
if startswith[-1] == '/' or startswith[-1] == '\\':
|
||||||
|
startswith = startswith[:-1]
|
||||||
|
return filter_dict_keys(startswith, flatten_dictionary(cls.global_registry_dict))
|
||||||
|
return filter_dict_keys(startswith, flatten_dictionary(cls.global_registry_dict))
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_hklm_entries(cls, startswith):
|
||||||
|
pregs = cls.filter_entries(startswith)
|
||||||
|
list_entiers = list()
|
||||||
|
for keyname, value in pregs.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
for valuename, data in value.items():
|
||||||
|
list_entiers.append(PregDconf(
|
||||||
|
keyname, convert_string_dconf(valuename), find_preg_type(data), data))
|
||||||
|
elif isinstance(value, list):
|
||||||
|
for data in value:
|
||||||
|
list_entiers.append(PregDconf(
|
||||||
|
keyname, convert_string_dconf(data), find_preg_type(data), data))
|
||||||
|
else:
|
||||||
|
list_entiers.append(PregDconf(
|
||||||
|
'/'.join(keyname.split('/')[:-1]), convert_string_dconf(keyname.split('/')[-1]), find_preg_type(value), value))
|
||||||
|
|
||||||
|
|
||||||
|
return gplist(list_entiers)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filter_hkcu_entries(cls, sid, startswith):
|
||||||
|
return cls.filter_hklm_entries(startswith)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_storage(cls,dictionary = None):
|
||||||
|
if dictionary:
|
||||||
|
result = dictionary
|
||||||
|
elif Dconf_registry._gpt_read_flag:
|
||||||
|
result = Dconf_registry.global_registry_dict
|
||||||
|
else:
|
||||||
|
if Dconf_registry.__dconf_dict_flag:
|
||||||
|
result = Dconf_registry.__dconf_dict
|
||||||
|
else:
|
||||||
|
Dconf_registry.__dconf_dict = Dconf_registry.get_policies_from_dconf()
|
||||||
|
result = Dconf_registry.__dconf_dict
|
||||||
|
Dconf_registry.__dconf_dict_flag = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def filling_storage_from_dconf(cls):
|
||||||
|
Dconf_registry.global_registry_dict = Dconf_registry.get_storage()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_entry(cls, path, dictionary = None):
|
||||||
|
logdata = dict()
|
||||||
|
result = Dconf_registry.get_storage(dictionary)
|
||||||
|
|
||||||
|
keys = path.split("\\") if "\\" in path else path.split("/")
|
||||||
|
key = '/'.join(keys[:-1]) if keys[0] else '/'.join(keys[:-1])[1:]
|
||||||
|
|
||||||
|
if isinstance(result, dict) and key in result.keys():
|
||||||
|
data = result.get(key).get(keys[-1])
|
||||||
|
return PregDconf(
|
||||||
|
key, convert_string_dconf(keys[-1]), find_preg_type(data), data)
|
||||||
|
else:
|
||||||
|
logdata['path'] = path
|
||||||
|
log('D208', logdata)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_hkcu_entry(cls, sid, hive_key, dictionary = None):
|
||||||
|
return cls.get_hklm_entry(hive_key, dictionary)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_hklm_entry(cls, hive_key, dictionary = None):
|
||||||
|
return cls.get_entry(hive_key, dictionary)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_shortcut(cls, sid, sc_obj, policy_name):
|
||||||
|
cls.shortcuts.append(sc_obj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_printer(cls, sid, pobj, policy_name):
|
||||||
|
cls.printers.append(pobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_drive(cls, sid, dobj, policy_name):
|
||||||
|
cls.drives.append(dobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_folder(cls, sid, fobj, policy_name):
|
||||||
|
cls.folders.append(fobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_envvar(self, sid, evobj, policy_name):
|
||||||
|
self.environmentvariables.append(evobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_script(cls, sid, scrobj, policy_name):
|
||||||
|
cls.scripts.append(scrobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_file(cls, sid, fileobj, policy_name):
|
||||||
|
cls.files.append(fileobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_ini(cls, sid, iniobj, policy_name):
|
||||||
|
cls.inifiles.append(iniobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_networkshare(cls, sid, networkshareobj, policy_name):
|
||||||
|
cls.networkshares.append(networkshareobj)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_shortcuts(cls, sid):
|
||||||
|
return cls.shortcuts
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_printers(cls, sid):
|
||||||
|
return cls.printers
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_drives(cls, sid):
|
||||||
|
return cls.drives
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_folders(cls, sid):
|
||||||
|
return cls.folders
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_envvars(cls, sid):
|
||||||
|
return cls.environmentvariables
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_scripts(cls, sid, action):
|
||||||
|
action_scripts = list()
|
||||||
|
for part in cls.scripts:
|
||||||
|
if action == 'LOGON' and part.action == 'LOGON':
|
||||||
|
action_scripts.append(part)
|
||||||
|
elif action == 'LOGOFF' and part.action == 'LOGOFF':
|
||||||
|
action_scripts.append(part)
|
||||||
|
elif action == 'STARTUP' and part.action == 'STARTUP':
|
||||||
|
action_scripts.append(part)
|
||||||
|
elif action == 'SHUTDOWN' and part.action == 'SHUTDOWN':
|
||||||
|
action_scripts.append(part)
|
||||||
|
return action_scripts
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_files(cls, sid):
|
||||||
|
return cls.files
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_networkshare(cls, sid):
|
||||||
|
return cls.networkshares
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_ini(cls, sid):
|
||||||
|
return cls.inifiles
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wipe_user(cls, sid):
|
||||||
|
cls.wipe_hklm()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def wipe_hklm(cls):
|
||||||
|
cls.global_registry_dict = dict({cls._ReadQueue:{}})
|
||||||
|
|
||||||
|
|
||||||
|
def filter_dict_keys(starting_string, input_dict):
|
||||||
|
result = dict()
|
||||||
|
for key in input_dict:
|
||||||
|
key_list = remove_empty_values(re.split(r'\\|/', key))
|
||||||
|
start_list = remove_empty_values(re.split(r'\\|/', starting_string))
|
||||||
|
if key_list[:len(start_list)] == start_list:
|
||||||
|
result[key] = input_dict.get(key)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def find_preg_type(argument):
|
||||||
|
if isinstance(argument, int):
|
||||||
|
return 4
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def update_dict(dict1, dict2):
|
||||||
|
'''
|
||||||
|
Updates dict1 with the key-value pairs from dict2
|
||||||
|
'''
|
||||||
|
for key, value in dict2.items():
|
||||||
|
if key in dict1:
|
||||||
|
# If both values are dictionaries, recursively call the update_dict function
|
||||||
|
if isinstance(dict1[key], dict) and isinstance(value, dict):
|
||||||
|
update_dict(dict1[key], value)
|
||||||
|
# If the value in dict1 is a list, extend it with unique values from value
|
||||||
|
elif isinstance(dict1[key], list):
|
||||||
|
dict1[key].extend(set(value) - set(dict1[key]))
|
||||||
|
else:
|
||||||
|
# If the value in dict1 is not a dictionary or the value in dict2 is not a dictionary,
|
||||||
|
# replace the value in dict1 with the value from dict2
|
||||||
|
dict1[key] = value
|
||||||
|
else:
|
||||||
|
# If the key does not exist in dict1, add the key-value pair from dict2 to dict1
|
||||||
|
dict1[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
def add_to_dict(string, policy_name, username, version):
|
||||||
|
if username is None:
|
||||||
|
correct_path = '/'.join(string.split('/')[:-2])
|
||||||
|
machine= '{}/Machine'.format(Dconf_registry._ReadQueue)
|
||||||
|
dictionary = Dconf_registry.global_registry_dict.setdefault(machine, dict())
|
||||||
|
else:
|
||||||
|
correct_path = '/'.join(string.split('/')[:-2])
|
||||||
|
user = '{}/User'.format(Dconf_registry._ReadQueue)
|
||||||
|
dictionary = Dconf_registry.global_registry_dict.setdefault(user, dict())
|
||||||
|
|
||||||
|
dictionary[len(dictionary)] = (policy_name, correct_path, version)
|
||||||
|
|
||||||
|
|
||||||
|
def load_preg_dconf(pregfile, pathfile, policy_name, username, version=None):
|
||||||
|
'''
|
||||||
|
Loads the configuration from preg registry into a dictionary
|
||||||
|
'''
|
||||||
|
dd = dict()
|
||||||
|
for i in pregfile.entries:
|
||||||
|
# Skip this entry if the valuename starts with '**del'
|
||||||
|
if i.valuename.startswith('**del'):
|
||||||
|
continue
|
||||||
|
valuename = convert_string_dconf(i.valuename)
|
||||||
|
data = check_data(i.data, i.type)
|
||||||
|
if i.valuename != i.data and i.valuename:
|
||||||
|
if i.keyname.replace('\\', '/') in dd:
|
||||||
|
# If the key exists in dd, update its value with the new key-value pair
|
||||||
|
dd[i.keyname.replace('\\', '/')].update({valuename.replace('\\', '/'):data})
|
||||||
|
else:
|
||||||
|
# If the key does not exist in dd, create a new key-value pair
|
||||||
|
dd[i.keyname.replace('\\', '/')] = {valuename.replace('\\', '/'):data}
|
||||||
|
|
||||||
|
elif not i.valuename:
|
||||||
|
keyname_tmp = i.keyname.replace('\\', '/').split('/')
|
||||||
|
keyname = '/'.join(keyname_tmp[:-1])
|
||||||
|
if keyname in dd:
|
||||||
|
# If the key exists in dd, update its value with the new key-value pair
|
||||||
|
dd[keyname].update({keyname_tmp[-1]:data})
|
||||||
|
else:
|
||||||
|
# If the key does not exist in dd, create a new key-value pair
|
||||||
|
dd[keyname] = {keyname_tmp[-1]:data}
|
||||||
|
|
||||||
|
else:
|
||||||
|
# If the value name is the same as the data,
|
||||||
|
# split the keyname and add the data to the appropriate location in dd.
|
||||||
|
all_list_key = i.keyname.split('\\')
|
||||||
|
dd_target = dd.setdefault('/'.join(all_list_key[:-1]),{})
|
||||||
|
dd_target.setdefault(all_list_key[-1], []).append(data)
|
||||||
|
|
||||||
|
# Update the global registry dictionary with the contents of dd
|
||||||
|
add_to_dict(pathfile, policy_name, username, version)
|
||||||
|
update_dict(Dconf_registry.global_registry_dict, dd)
|
||||||
|
|
||||||
|
|
||||||
|
def create_dconf_ini_file(filename, data):
|
||||||
|
'''
|
||||||
|
Create an ini-file based on a dictionary of dictionaries.
|
||||||
|
Args:
|
||||||
|
data (dict): The dictionary of dictionaries containing the data for the ini-file.
|
||||||
|
filename (str): The filename to save the ini-file.
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
Raises:
|
||||||
|
None
|
||||||
|
'''
|
||||||
|
with open(filename, 'w') as file:
|
||||||
|
for section, section_data in data.items():
|
||||||
|
file.write(f'[{section}]\n')
|
||||||
|
for key, value in section_data.items():
|
||||||
|
if isinstance(value, int):
|
||||||
|
file.write(f'{key} = {value}\n')
|
||||||
|
else:
|
||||||
|
file.write(f'{key} = "{value}"\n')
|
||||||
|
file.write('\n')
|
||||||
|
logdata = dict()
|
||||||
|
logdata['path'] = filename
|
||||||
|
log('D209', logdata)
|
||||||
|
Dconf_registry.dconf_update()
|
||||||
|
|
||||||
|
def clean_data(data):
|
||||||
|
try:
|
||||||
|
cleaned_string = data.replace('\n', '').replace('\r', '')
|
||||||
|
cleaned_string = cleaned_string.replace('"', "'")
|
||||||
|
return cleaned_string
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def check_data(data, t_data):
|
||||||
|
if isinstance(data, bytes):
|
||||||
|
if t_data == 7:
|
||||||
|
return clean_data(data.decode('utf-16').replace('\x00',''))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
elif t_data == 4:
|
||||||
|
return data
|
||||||
|
return clean_data(data)
|
||||||
|
|
||||||
|
def convert_string_dconf(input_string):
|
||||||
|
macros = {
|
||||||
|
'#': '%sharp%',
|
||||||
|
';': '%semicolon%',
|
||||||
|
'//': '%doubleslash%'
|
||||||
|
}
|
||||||
|
output_string = input_string
|
||||||
|
for key, value in macros.items():
|
||||||
|
if key in input_string:
|
||||||
|
output_string = input_string.replace(key, value)
|
||||||
|
elif value in input_string:
|
||||||
|
output_string = input_string.replace(value, key)
|
||||||
|
|
||||||
|
return output_string
|
||||||
|
|
||||||
|
def remove_empty_values(input_list):
|
||||||
|
return list(filter(None, input_list))
|
||||||
|
|
||||||
|
def flatten_dictionary(input_dict, result=None, current_key=''):
|
||||||
|
if result is None:
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for key, value in input_dict.items():
|
||||||
|
new_key = f"{current_key}/{key}" if current_key else key
|
||||||
|
|
||||||
|
if isinstance(value, dict):
|
||||||
|
flatten_dictionary(value, result, new_key)
|
||||||
|
else:
|
||||||
|
result[new_key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_dconf_envprofile():
|
||||||
|
dconf_envprofile = {'default': {'DCONF_PROFILE': 'default'},
|
||||||
|
'local': {'DCONF_PROFILE': 'local'},
|
||||||
|
'system': {'DCONF_PROFILE': 'system'}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Dconf_registry._envprofile:
|
||||||
|
return dconf_envprofile.get(Dconf_registry._envprofile, dconf_envprofile['system'])
|
||||||
|
|
||||||
|
if not Dconf_registry._username:
|
||||||
|
return dconf_envprofile['system']
|
||||||
|
|
||||||
|
profile = '/run/dconf/user/{}'.format(get_uid_by_username(Dconf_registry._username))
|
||||||
|
return {'DCONF_PROFILE': profile}
|
||||||
125
gpoa/storage/fs_file_cache.py
Normal file
125
gpoa/storage/fs_file_cache.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
import smbc
|
||||||
|
|
||||||
|
|
||||||
|
from util.logging import log
|
||||||
|
from util.paths import file_cache_dir, file_cache_path_home, UNCPath
|
||||||
|
from util.exceptions import NotUNCPathError
|
||||||
|
|
||||||
|
|
||||||
|
class fs_file_cache:
|
||||||
|
__read_blocksize = 4096
|
||||||
|
|
||||||
|
def __init__(self, cache_name, username = None):
|
||||||
|
self.cache_name = cache_name
|
||||||
|
if username:
|
||||||
|
try:
|
||||||
|
self.storage_uri = file_cache_path_home(username)
|
||||||
|
except:
|
||||||
|
self.storage_uri = file_cache_dir()
|
||||||
|
else:
|
||||||
|
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, destfile = None):
|
||||||
|
try:
|
||||||
|
uri_path = UNCPath(uri)
|
||||||
|
if not destfile:
|
||||||
|
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))
|
||||||
|
else:
|
||||||
|
destdir = destfile.parent
|
||||||
|
except NotUNCPathError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if not destfile:
|
||||||
|
destfile = Path('{}/{}/{}'.format(self.storage_uri,
|
||||||
|
uri_path.get_domain(),
|
||||||
|
uri_path.get_path()))
|
||||||
|
|
||||||
|
try:
|
||||||
|
fd, tmpfile = tempfile.mkstemp('', str(destfile))
|
||||||
|
df = os.fdopen(fd, 'wb')
|
||||||
|
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.close()
|
||||||
|
os.rename(tmpfile, destfile)
|
||||||
|
os.chmod(destfile, 0o644)
|
||||||
|
except:
|
||||||
|
tmppath = Path(tmpfile)
|
||||||
|
if tmppath.exists():
|
||||||
|
tmppath.unlink()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def get_ls_smbdir(self, uri):
|
||||||
|
type_file_smb = 8
|
||||||
|
try:
|
||||||
|
uri_path = UNCPath(uri)
|
||||||
|
opendir = self.samba_context.opendir(str(uri_path))
|
||||||
|
ls_obj = opendir.getdents()
|
||||||
|
ls = [obj.name for obj in ls_obj if obj.smbc_type == type_file_smb]
|
||||||
|
return ls
|
||||||
|
except Exception as exc:
|
||||||
|
if Path(uri).exists():
|
||||||
|
return None
|
||||||
|
logdata = dict({'exception': str(exc)})
|
||||||
|
log('W12', logdata)
|
||||||
|
return None
|
||||||
@@ -22,7 +22,9 @@ class samba_preg(object):
|
|||||||
'''
|
'''
|
||||||
def __init__(self, preg_obj, policy_name):
|
def __init__(self, preg_obj, policy_name):
|
||||||
self.policy_name = policy_name
|
self.policy_name = policy_name
|
||||||
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
|
self.keyname = preg_obj.keyname
|
||||||
|
self.valuename = preg_obj.valuename
|
||||||
|
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
|
||||||
self.type = preg_obj.type
|
self.type = preg_obj.type
|
||||||
self.data = preg_obj.data
|
self.data = preg_obj.data
|
||||||
|
|
||||||
@@ -41,7 +43,9 @@ class samba_hkcu_preg(object):
|
|||||||
def __init__(self, sid, preg_obj, policy_name):
|
def __init__(self, sid, preg_obj, policy_name):
|
||||||
self.sid = sid
|
self.sid = sid
|
||||||
self.policy_name = policy_name
|
self.policy_name = policy_name
|
||||||
self.hive_key = '{}\\{}'.format(preg_obj.keyname, preg_obj.valuename)
|
self.keyname = preg_obj.keyname
|
||||||
|
self.valuename = preg_obj.valuename
|
||||||
|
self.hive_key = '{}\\{}'.format(self.keyname, self.valuename)
|
||||||
self.type = preg_obj.type
|
self.type = preg_obj.type
|
||||||
self.data = preg_obj.data
|
self.data = preg_obj.data
|
||||||
|
|
||||||
@@ -111,6 +115,13 @@ class drive_entry(object):
|
|||||||
self.password = dobj.password
|
self.password = dobj.password
|
||||||
self.dir = dobj.dir
|
self.dir = dobj.dir
|
||||||
self.path = dobj.path
|
self.path = dobj.path
|
||||||
|
self.action = dobj.action
|
||||||
|
self.thisDrive = dobj.thisDrive
|
||||||
|
self.allDrives = dobj.allDrives
|
||||||
|
self.label = dobj.label
|
||||||
|
self.persistent = dobj.persistent
|
||||||
|
self.useLetter = dobj.useLetter
|
||||||
|
|
||||||
|
|
||||||
def update_fields(self):
|
def update_fields(self):
|
||||||
fields = dict()
|
fields = dict()
|
||||||
@@ -119,6 +130,12 @@ class drive_entry(object):
|
|||||||
fields['password'] = self.password
|
fields['password'] = self.password
|
||||||
fields['dir'] = self.dir
|
fields['dir'] = self.dir
|
||||||
fields['path'] = self.path
|
fields['path'] = self.path
|
||||||
|
fields['action'] = self.action
|
||||||
|
fields['thisDrive'] = self.thisDrive
|
||||||
|
fields['allDrives'] = self.allDrives
|
||||||
|
fields['label'] = self.label
|
||||||
|
fields['persistent'] = self.persistent
|
||||||
|
fields['useLetter'] = self.useLetter
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
@@ -132,8 +149,9 @@ class folder_entry(object):
|
|||||||
self.path = fobj.path
|
self.path = fobj.path
|
||||||
self.action = fobj.action.value
|
self.action = fobj.action.value
|
||||||
self.delete_folder = str(fobj.delete_folder)
|
self.delete_folder = str(fobj.delete_folder)
|
||||||
self.delete_sub_folder = str(fobj.delete_sub_folder)
|
self.delete_sub_folders = str(fobj.delete_sub_folders)
|
||||||
self.delete_files = str(fobj.delete_files)
|
self.delete_files = str(fobj.delete_files)
|
||||||
|
self.hidden_folder = str(fobj.hidden_folder)
|
||||||
|
|
||||||
def update_fields(self):
|
def update_fields(self):
|
||||||
'''
|
'''
|
||||||
@@ -143,8 +161,149 @@ class folder_entry(object):
|
|||||||
fields['policy_name'] = self.policy_name
|
fields['policy_name'] = self.policy_name
|
||||||
fields['action'] = self.action
|
fields['action'] = self.action
|
||||||
fields['delete_folder'] = self.delete_folder
|
fields['delete_folder'] = self.delete_folder
|
||||||
fields['delete_sub_folder'] = self.delete_sub_folder
|
fields['delete_sub_folders'] = self.delete_sub_folders
|
||||||
fields['delete_files'] = self.delete_files
|
fields['delete_files'] = self.delete_files
|
||||||
|
fields['hidden_folder'] = self.hidden_folder
|
||||||
|
|
||||||
|
|
||||||
return fields
|
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
|
||||||
|
|
||||||
|
class script_entry(object):
|
||||||
|
'''
|
||||||
|
Object mapping representing scripts.ini
|
||||||
|
'''
|
||||||
|
def __init__(self, sid, scrobj, policy_name):
|
||||||
|
self.sid = sid
|
||||||
|
self.policy_name = policy_name
|
||||||
|
self.action = scrobj.action
|
||||||
|
self.number = scrobj.number
|
||||||
|
self.path = scrobj.path
|
||||||
|
self.arg = scrobj.args
|
||||||
|
|
||||||
|
def update_fields(self):
|
||||||
|
'''
|
||||||
|
Return list of fields to update
|
||||||
|
'''
|
||||||
|
fields = dict()
|
||||||
|
fields['policy_name'] = self.policy_name
|
||||||
|
fields['action'] = self.action
|
||||||
|
fields['number'] = self.number
|
||||||
|
fields['path'] = self.path
|
||||||
|
fields['arg'] = self.arg
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
||||||
|
class file_entry(object):
|
||||||
|
'''
|
||||||
|
Object mapping representing FILES.XML
|
||||||
|
'''
|
||||||
|
def __init__(self, sid, fileobj, policy_name):
|
||||||
|
self.sid = sid
|
||||||
|
self.policy_name = policy_name
|
||||||
|
self.action = fileobj.action
|
||||||
|
self.fromPath = fileobj.fromPath
|
||||||
|
self.targetPath = fileobj.targetPath
|
||||||
|
self.readOnly = fileobj.readOnly
|
||||||
|
self.archive = fileobj.archive
|
||||||
|
self.hidden = fileobj.hidden
|
||||||
|
self.suppress = fileobj.suppress
|
||||||
|
self.executable = fileobj.executable
|
||||||
|
|
||||||
|
def update_fields(self):
|
||||||
|
'''
|
||||||
|
Return list of fields to update
|
||||||
|
'''
|
||||||
|
fields = dict()
|
||||||
|
fields['policy_name'] = self.policy_name
|
||||||
|
fields['action'] = self.action
|
||||||
|
fields['fromPath'] = self.fromPath
|
||||||
|
fields['targetPath'] = self.targetPath
|
||||||
|
fields['readOnly'] = self.readOnly
|
||||||
|
fields['archive'] = self.archive
|
||||||
|
fields['hidden'] = self.hidden
|
||||||
|
fields['suppress'] = self.suppress
|
||||||
|
fields['executable'] = self.executable
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
||||||
|
class ini_entry(object):
|
||||||
|
'''
|
||||||
|
Object mapping representing INIFILES.XML
|
||||||
|
'''
|
||||||
|
def __init__(self, sid, iniobj, policy_name):
|
||||||
|
self.sid = sid
|
||||||
|
self.policy_name = policy_name
|
||||||
|
self.action = iniobj.action
|
||||||
|
self.path = iniobj.path
|
||||||
|
self.section = iniobj.section
|
||||||
|
self.property = iniobj.property
|
||||||
|
self.value = iniobj.value
|
||||||
|
|
||||||
|
|
||||||
|
def update_fields(self):
|
||||||
|
'''
|
||||||
|
Return list of fields to update
|
||||||
|
'''
|
||||||
|
fields = dict()
|
||||||
|
fields['policy_name'] = self.policy_name
|
||||||
|
fields['action'] = self.action
|
||||||
|
fields['path'] = self.path
|
||||||
|
fields['section'] = self.section
|
||||||
|
fields['property'] = self.property
|
||||||
|
fields['value'] = self.value
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
||||||
|
class networkshare_entry(object):
|
||||||
|
'''
|
||||||
|
Object mapping representing NETWORKSHARES.XML
|
||||||
|
'''
|
||||||
|
def __init__(self, sid, networkshareobj, policy_name):
|
||||||
|
self.sid = sid
|
||||||
|
self.policy_name = policy_name
|
||||||
|
self.name = networkshareobj.name
|
||||||
|
self.action = networkshareobj.action
|
||||||
|
self.path = networkshareobj.path
|
||||||
|
self.allRegular = networkshareobj.allRegular
|
||||||
|
self.comment = networkshareobj.comment
|
||||||
|
self.limitUsers = networkshareobj.limitUsers
|
||||||
|
self.abe = networkshareobj.abe
|
||||||
|
|
||||||
|
|
||||||
|
def update_fields(self):
|
||||||
|
'''
|
||||||
|
Return list of fields to update
|
||||||
|
'''
|
||||||
|
fields = dict()
|
||||||
|
fields['policy_name'] = self.policy_name
|
||||||
|
fields['name'] = self.name
|
||||||
|
fields['action'] = self.action
|
||||||
|
fields['path'] = self.path
|
||||||
|
fields['allRegular'] = self.allRegular
|
||||||
|
fields['comment'] = self.comment
|
||||||
|
fields['limitUsers'] = self.limitUsers
|
||||||
|
fields['abe'] = self.abe
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|||||||
@@ -26,13 +26,11 @@ from sqlalchemy import (
|
|||||||
Column,
|
Column,
|
||||||
Integer,
|
Integer,
|
||||||
String,
|
String,
|
||||||
MetaData
|
|
||||||
)
|
|
||||||
from sqlalchemy.orm import (
|
|
||||||
mapper,
|
|
||||||
sessionmaker
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from .sqlite_registry_compat import sqlite_registry_compat
|
||||||
|
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
from util.paths import cache_dir
|
from util.paths import cache_dir
|
||||||
|
|
||||||
@@ -55,7 +53,8 @@ class sqlite_cache(cache):
|
|||||||
logdata = dict({'cache_file': self.storage_uri})
|
logdata = dict({'cache_file': self.storage_uri})
|
||||||
log('D20', logdata)
|
log('D20', logdata)
|
||||||
self.db_cnt = create_engine(self.storage_uri, echo=False)
|
self.db_cnt = create_engine(self.storage_uri, echo=False)
|
||||||
self.__metadata = MetaData(self.db_cnt)
|
self.__compat = sqlite_registry_compat(self.db_cnt)
|
||||||
|
self.__metadata = self.__compat.metadata()
|
||||||
self.cache_table = Table(
|
self.cache_table = Table(
|
||||||
self.cache_name,
|
self.cache_name,
|
||||||
self.__metadata,
|
self.__metadata,
|
||||||
@@ -67,7 +66,8 @@ class sqlite_cache(cache):
|
|||||||
self.__metadata.create_all(self.db_cnt)
|
self.__metadata.create_all(self.db_cnt)
|
||||||
Session = sessionmaker(bind=self.db_cnt)
|
Session = sessionmaker(bind=self.db_cnt)
|
||||||
self.db_session = Session()
|
self.db_session = Session()
|
||||||
mapper(self.mapper_obj, self.cache_table)
|
mapper_reg = self.__compat
|
||||||
|
mapper_reg.map_imperatively(self.mapper_obj, self.cache_table)
|
||||||
|
|
||||||
def store(self, str_id, value):
|
def store(self, str_id, value):
|
||||||
obj = self.mapper_obj(str_id, value)
|
obj = self.mapper_obj(str_id, value)
|
||||||
|
|||||||
@@ -24,13 +24,11 @@ from sqlalchemy import (
|
|||||||
Column,
|
Column,
|
||||||
Integer,
|
Integer,
|
||||||
String,
|
String,
|
||||||
MetaData,
|
|
||||||
UniqueConstraint
|
UniqueConstraint
|
||||||
)
|
)
|
||||||
from sqlalchemy.orm import (
|
|
||||||
mapper,
|
from sqlalchemy.orm import sessionmaker
|
||||||
sessionmaker
|
from .sqlite_registry_compat import sqlite_registry_compat
|
||||||
)
|
|
||||||
|
|
||||||
from util.logging import log
|
from util.logging import log
|
||||||
from util.paths import cache_dir
|
from util.paths import cache_dir
|
||||||
@@ -43,6 +41,11 @@ from .record_types import (
|
|||||||
, printer_entry
|
, printer_entry
|
||||||
, drive_entry
|
, drive_entry
|
||||||
, folder_entry
|
, folder_entry
|
||||||
|
, envvar_entry
|
||||||
|
, script_entry
|
||||||
|
, file_entry
|
||||||
|
, ini_entry
|
||||||
|
, networkshare_entry
|
||||||
)
|
)
|
||||||
|
|
||||||
class sqlite_registry(registry):
|
class sqlite_registry(registry):
|
||||||
@@ -53,7 +56,8 @@ class sqlite_registry(registry):
|
|||||||
cdir = cache_dir()
|
cdir = cache_dir()
|
||||||
self.db_path = os.path.join('sqlite:///{}/{}.sqlite'.format(cdir, self.db_name))
|
self.db_path = os.path.join('sqlite:///{}/{}.sqlite'.format(cdir, self.db_name))
|
||||||
self.db_cnt = create_engine(self.db_path, echo=False)
|
self.db_cnt = create_engine(self.db_path, echo=False)
|
||||||
self.__metadata = MetaData(self.db_cnt)
|
self.__compat = sqlite_registry_compat(self.db_cnt)
|
||||||
|
self.__metadata = self.__compat.metadata()
|
||||||
self.__info = Table(
|
self.__info = Table(
|
||||||
'info',
|
'info',
|
||||||
self.__metadata,
|
self.__metadata,
|
||||||
@@ -65,7 +69,10 @@ class sqlite_registry(registry):
|
|||||||
'HKLM'
|
'HKLM'
|
||||||
, self.__metadata
|
, self.__metadata
|
||||||
, Column('id', Integer, primary_key=True)
|
, Column('id', Integer, primary_key=True)
|
||||||
, Column('hive_key', String(65536), unique=True)
|
, Column('hive_key', String(65536, collation='NOCASE'),
|
||||||
|
unique=True)
|
||||||
|
, Column('keyname', String(collation='NOCASE'))
|
||||||
|
, Column('valuename', String(collation='NOCASE'))
|
||||||
, Column('policy_name', String)
|
, Column('policy_name', String)
|
||||||
, Column('type', Integer)
|
, Column('type', Integer)
|
||||||
, Column('data', String)
|
, Column('data', String)
|
||||||
@@ -75,7 +82,9 @@ class sqlite_registry(registry):
|
|||||||
, self.__metadata
|
, self.__metadata
|
||||||
, Column('id', Integer, primary_key=True)
|
, Column('id', Integer, primary_key=True)
|
||||||
, Column('sid', String)
|
, Column('sid', String)
|
||||||
, Column('hive_key', String(65536))
|
, Column('hive_key', String(65536, collation='NOCASE'))
|
||||||
|
, Column('keyname', String(collation='NOCASE'))
|
||||||
|
, Column('valuename', String(collation='NOCASE'))
|
||||||
, Column('policy_name', String)
|
, Column('policy_name', String)
|
||||||
, Column('type', Integer)
|
, Column('type', Integer)
|
||||||
, Column('data', String)
|
, Column('data', String)
|
||||||
@@ -111,6 +120,12 @@ class sqlite_registry(registry):
|
|||||||
, Column('dir', String)
|
, Column('dir', String)
|
||||||
, Column('policy_name', String)
|
, Column('policy_name', String)
|
||||||
, Column('path', String)
|
, Column('path', String)
|
||||||
|
, Column('action', String)
|
||||||
|
, Column('thisDrive', String)
|
||||||
|
, Column('allDrives', String)
|
||||||
|
, Column('label', String)
|
||||||
|
, Column('persistent', String)
|
||||||
|
, Column('useLetter', String)
|
||||||
, UniqueConstraint('sid', 'dir')
|
, UniqueConstraint('sid', 'dir')
|
||||||
)
|
)
|
||||||
self.__folders = Table(
|
self.__folders = Table(
|
||||||
@@ -122,21 +137,96 @@ class sqlite_registry(registry):
|
|||||||
, Column('policy_name', String)
|
, Column('policy_name', String)
|
||||||
, Column('action', String)
|
, Column('action', String)
|
||||||
, Column('delete_folder', String)
|
, Column('delete_folder', String)
|
||||||
, Column('delete_sub_folder', String)
|
, Column('delete_sub_folders', String)
|
||||||
, Column('delete_files', String)
|
, Column('delete_files', String)
|
||||||
|
, Column('hidden_folder', String)
|
||||||
, UniqueConstraint('sid', 'path')
|
, 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.__scripts = Table(
|
||||||
|
'Scripts'
|
||||||
|
, self.__metadata
|
||||||
|
, Column('id', Integer, primary_key=True)
|
||||||
|
, Column('sid', String)
|
||||||
|
, Column('policy_name', String)
|
||||||
|
, Column('number', String)
|
||||||
|
, Column('action', String)
|
||||||
|
, Column('path', String)
|
||||||
|
, Column('arg', String)
|
||||||
|
, UniqueConstraint('sid', 'path', 'arg')
|
||||||
|
)
|
||||||
|
self.__files = Table(
|
||||||
|
'Files'
|
||||||
|
, self.__metadata
|
||||||
|
, Column('id', Integer, primary_key=True)
|
||||||
|
, Column('sid', String)
|
||||||
|
, Column('policy_name', String)
|
||||||
|
, Column('action', String)
|
||||||
|
, Column('fromPath', String)
|
||||||
|
, Column('targetPath', String)
|
||||||
|
, Column('readOnly', String)
|
||||||
|
, Column('archive', String)
|
||||||
|
, Column('hidden', String)
|
||||||
|
, Column('suppress', String)
|
||||||
|
, Column('executable', String)
|
||||||
|
, UniqueConstraint('sid', 'policy_name', 'targetPath', 'fromPath')
|
||||||
|
)
|
||||||
|
self.__ini = Table(
|
||||||
|
'Ini'
|
||||||
|
, self.__metadata
|
||||||
|
, Column('id', Integer, primary_key=True)
|
||||||
|
, Column('sid', String)
|
||||||
|
, Column('policy_name', String)
|
||||||
|
, Column('action', String)
|
||||||
|
, Column('path', String)
|
||||||
|
, Column('section', String)
|
||||||
|
, Column('property', String)
|
||||||
|
, Column('value', String)
|
||||||
|
, UniqueConstraint('sid', 'action', 'path', 'section', 'property', 'value')
|
||||||
|
)
|
||||||
|
self.__networkshare = Table(
|
||||||
|
'Networkshare'
|
||||||
|
, self.__metadata
|
||||||
|
, Column('id', Integer, primary_key=True)
|
||||||
|
, Column('sid', String)
|
||||||
|
, Column('policy_name', String)
|
||||||
|
, Column('name', String)
|
||||||
|
, Column('action', String)
|
||||||
|
, Column('path', String)
|
||||||
|
, Column('allRegular', String)
|
||||||
|
, Column('comment', String)
|
||||||
|
, Column('limitUsers', String)
|
||||||
|
, Column('abe', String)
|
||||||
|
, UniqueConstraint('sid', 'name', 'path')
|
||||||
|
)
|
||||||
|
|
||||||
self.__metadata.create_all(self.db_cnt)
|
self.__metadata.create_all(self.db_cnt)
|
||||||
Session = sessionmaker(bind=self.db_cnt)
|
Session = sessionmaker(bind=self.db_cnt)
|
||||||
self.db_session = Session()
|
self.db_session = Session()
|
||||||
|
mapper_reg = self.__compat
|
||||||
try:
|
try:
|
||||||
mapper(info_entry, self.__info)
|
mapper_reg.map_imperatively(info_entry, self.__info)
|
||||||
mapper(samba_preg, self.__hklm)
|
mapper_reg.map_imperatively(samba_preg, self.__hklm)
|
||||||
mapper(samba_hkcu_preg, self.__hkcu)
|
mapper_reg.map_imperatively(samba_hkcu_preg, self.__hkcu)
|
||||||
mapper(ad_shortcut, self.__shortcuts)
|
mapper_reg.map_imperatively(ad_shortcut, self.__shortcuts)
|
||||||
mapper(printer_entry, self.__printers)
|
mapper_reg.map_imperatively(printer_entry, self.__printers)
|
||||||
mapper(drive_entry, self.__drives)
|
mapper_reg.map_imperatively(drive_entry, self.__drives)
|
||||||
mapper(folder_entry, self.__folders)
|
mapper_reg.map_imperatively(folder_entry, self.__folders)
|
||||||
|
mapper_reg.map_imperatively(envvar_entry, self.__envvars)
|
||||||
|
mapper_reg.map_imperatively(script_entry, self.__scripts)
|
||||||
|
mapper_reg.map_imperatively(file_entry, self.__files)
|
||||||
|
mapper_reg.map_imperatively(ini_entry, self.__ini)
|
||||||
|
mapper_reg.map_imperatively(networkshare_entry, self.__networkshare)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
#logging.error('Error creating mapper')
|
#logging.error('Error creating mapper')
|
||||||
@@ -226,26 +316,65 @@ class sqlite_registry(registry):
|
|||||||
log('D19', logdata)
|
log('D19', logdata)
|
||||||
self._info_upsert(ientry)
|
self._info_upsert(ientry)
|
||||||
|
|
||||||
|
def _delete_hklm_keyname(self, keyname):
|
||||||
|
'''
|
||||||
|
Delete PReg hive_key from HKEY_LOCAL_MACHINE
|
||||||
|
'''
|
||||||
|
logdata = dict({'keyname': keyname})
|
||||||
|
try:
|
||||||
|
(self
|
||||||
|
.db_session
|
||||||
|
.query(samba_preg)
|
||||||
|
.filter(samba_preg.keyname == keyname)
|
||||||
|
.delete(synchronize_session=False))
|
||||||
|
self.db_session.commit()
|
||||||
|
log('D65', logdata)
|
||||||
|
except Exception as exc:
|
||||||
|
log('D63', logdata)
|
||||||
|
|
||||||
def add_hklm_entry(self, preg_entry, policy_name):
|
def add_hklm_entry(self, preg_entry, policy_name):
|
||||||
'''
|
'''
|
||||||
Write PReg entry to HKEY_LOCAL_MACHINE
|
Write PReg entry to HKEY_LOCAL_MACHINE
|
||||||
'''
|
'''
|
||||||
pentry = samba_preg(preg_entry, policy_name)
|
pentry = samba_preg(preg_entry, policy_name)
|
||||||
if not pentry.hive_key.rpartition('\\')[2].startswith('**'):
|
if not pentry.valuename.startswith('**'):
|
||||||
self._hklm_upsert(pentry)
|
self._hklm_upsert(pentry)
|
||||||
else:
|
else:
|
||||||
logdata = dict({'key': pentry.hive_key})
|
logdata = dict({'key': pentry.hive_key})
|
||||||
|
if pentry.valuename.lower() == '**delvals.':
|
||||||
|
self._delete_hklm_keyname(pentry.keyname)
|
||||||
|
else:
|
||||||
log('D27', logdata)
|
log('D27', logdata)
|
||||||
|
|
||||||
|
def _delete_hkcu_keyname(self, keyname, sid):
|
||||||
|
'''
|
||||||
|
Delete PReg hive_key from HKEY_CURRENT_USER
|
||||||
|
'''
|
||||||
|
logdata = dict({'sid': sid, 'keyname': keyname})
|
||||||
|
try:
|
||||||
|
(self
|
||||||
|
.db_session
|
||||||
|
.query(samba_hkcu_preg)
|
||||||
|
.filter(samba_hkcu_preg.sid == sid)
|
||||||
|
.filter(samba_hkcu_preg.keyname == keyname)
|
||||||
|
.delete(synchronize_session=False))
|
||||||
|
self.db_session.commit()
|
||||||
|
log('D66', logdata)
|
||||||
|
except:
|
||||||
|
log('D64', logdata)
|
||||||
|
|
||||||
def add_hkcu_entry(self, preg_entry, sid, policy_name):
|
def add_hkcu_entry(self, preg_entry, sid, policy_name):
|
||||||
'''
|
'''
|
||||||
Write PReg entry to HKEY_CURRENT_USER
|
Write PReg entry to HKEY_CURRENT_USER
|
||||||
'''
|
'''
|
||||||
hkcu_pentry = samba_hkcu_preg(sid, preg_entry, policy_name)
|
hkcu_pentry = samba_hkcu_preg(sid, preg_entry, policy_name)
|
||||||
logdata = dict({'sid': sid, 'policy': policy_name, 'key': hkcu_pentry.hive_key})
|
logdata = dict({'sid': sid, 'policy': policy_name, 'key': hkcu_pentry.hive_key})
|
||||||
if not hkcu_pentry.hive_key.rpartition('\\')[2].startswith('**'):
|
if not hkcu_pentry.valuename.startswith('**'):
|
||||||
log('D26', logdata)
|
log('D26', logdata)
|
||||||
self._hkcu_upsert(hkcu_pentry)
|
self._hkcu_upsert(hkcu_pentry)
|
||||||
|
else:
|
||||||
|
if hkcu_pentry.valuename.lower() == '**delvals.':
|
||||||
|
self._delete_hkcu_keyname(hkcu_pentry.keyname, sid)
|
||||||
else:
|
else:
|
||||||
log('D51', logdata)
|
log('D51', logdata)
|
||||||
|
|
||||||
@@ -294,6 +423,83 @@ class sqlite_registry(registry):
|
|||||||
.update(fld_entry.update_fields()))
|
.update(fld_entry.update_fields()))
|
||||||
self.db_session.commit()
|
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 add_script(self, sid, scrobj, policy_name):
|
||||||
|
scr_entry = script_entry(sid, scrobj, policy_name)
|
||||||
|
logdata = dict()
|
||||||
|
logdata['script path'] = scrobj.path
|
||||||
|
logdata['sid'] = sid
|
||||||
|
log('D153', logdata)
|
||||||
|
try:
|
||||||
|
self._add(scr_entry)
|
||||||
|
except Exception as exc:
|
||||||
|
(self
|
||||||
|
._filter_sid_obj(script_entry, sid)
|
||||||
|
.filter(script_entry.path == scr_entry.path)
|
||||||
|
.update(scr_entry.update_fields()))
|
||||||
|
self.db_session.commit()
|
||||||
|
|
||||||
|
def add_file(self, sid, fileobj, policy_name):
|
||||||
|
f_entry = file_entry(sid, fileobj, policy_name)
|
||||||
|
logdata = dict()
|
||||||
|
logdata['targetPath'] = f_entry.targetPath
|
||||||
|
logdata['fromPath'] = f_entry.fromPath
|
||||||
|
log('D162', logdata)
|
||||||
|
try:
|
||||||
|
self._add(f_entry)
|
||||||
|
except Exception as exc:
|
||||||
|
(self
|
||||||
|
._filter_sid_obj(file_entry, sid)
|
||||||
|
.filter(file_entry.targetPath == f_entry.targetPath)
|
||||||
|
.update(f_entry.update_fields()))
|
||||||
|
self.db_session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def add_ini(self, sid, iniobj, policy_name):
|
||||||
|
inientry = ini_entry(sid, iniobj, policy_name)
|
||||||
|
logdata = dict()
|
||||||
|
logdata['path'] = inientry.path
|
||||||
|
logdata['action'] = inientry.action
|
||||||
|
log('D177', logdata)
|
||||||
|
try:
|
||||||
|
self._add(inientry)
|
||||||
|
except Exception as exc:
|
||||||
|
(self
|
||||||
|
._filter_sid_obj(ini_entry, sid)
|
||||||
|
.filter(ini_entry.path == inientry.path)
|
||||||
|
.update(inientry.update_fields()))
|
||||||
|
self.db_session.commit()
|
||||||
|
|
||||||
|
def add_networkshare(self, sid, networkshareobj, policy_name):
|
||||||
|
networkshareentry = networkshare_entry(sid, networkshareobj, policy_name)
|
||||||
|
logdata = dict()
|
||||||
|
logdata['name'] = networkshareentry.name
|
||||||
|
logdata['path'] = networkshareentry.path
|
||||||
|
logdata['action'] = networkshareentry.action
|
||||||
|
log('D186', logdata)
|
||||||
|
try:
|
||||||
|
self._add(networkshareentry)
|
||||||
|
except Exception as exc:
|
||||||
|
(self
|
||||||
|
._filter_sid_obj(networkshare_entry, sid)
|
||||||
|
.filter(networkshare_entry.path == networkshareentry.path)
|
||||||
|
.update(networkshareentry.update_fields()))
|
||||||
|
self.db_session.commit()
|
||||||
|
|
||||||
|
|
||||||
def _filter_sid_obj(self, row_object, sid):
|
def _filter_sid_obj(self, row_object, sid):
|
||||||
res = (self
|
res = (self
|
||||||
.db_session
|
.db_session
|
||||||
@@ -306,6 +512,7 @@ class sqlite_registry(registry):
|
|||||||
.db_session
|
.db_session
|
||||||
.query(row_object)
|
.query(row_object)
|
||||||
.filter(row_object.sid == sid)
|
.filter(row_object.sid == sid)
|
||||||
|
.order_by(row_object.id)
|
||||||
.all())
|
.all())
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -321,6 +528,31 @@ class sqlite_registry(registry):
|
|||||||
def get_folders(self, sid):
|
def get_folders(self, sid):
|
||||||
return self._filter_sid_list(folder_entry, sid)
|
return self._filter_sid_list(folder_entry, sid)
|
||||||
|
|
||||||
|
def get_envvars(self, sid):
|
||||||
|
return self._filter_sid_list(envvar_entry, sid)
|
||||||
|
|
||||||
|
def _filter_scripts_list(self, row_object, sid, action):
|
||||||
|
res = (self
|
||||||
|
.db_session
|
||||||
|
.query(row_object)
|
||||||
|
.filter(row_object.sid == sid)
|
||||||
|
.filter(row_object.action == action)
|
||||||
|
.order_by(row_object.id)
|
||||||
|
.all())
|
||||||
|
return res
|
||||||
|
|
||||||
|
def get_scripts(self, sid, action):
|
||||||
|
return self._filter_scripts_list(script_entry, sid, action)
|
||||||
|
|
||||||
|
def get_files(self, sid):
|
||||||
|
return self._filter_sid_list(file_entry, sid)
|
||||||
|
|
||||||
|
def get_networkshare(self, sid):
|
||||||
|
return self._filter_sid_list(networkshare_entry, sid)
|
||||||
|
|
||||||
|
def get_ini(self, sid):
|
||||||
|
return self._filter_sid_list(ini_entry, sid)
|
||||||
|
|
||||||
def get_hkcu_entry(self, sid, hive_key):
|
def get_hkcu_entry(self, sid, hive_key):
|
||||||
res = (self
|
res = (self
|
||||||
.db_session
|
.db_session
|
||||||
@@ -370,6 +602,10 @@ class sqlite_registry(registry):
|
|||||||
self._wipe_sid(ad_shortcut, sid)
|
self._wipe_sid(ad_shortcut, sid)
|
||||||
self._wipe_sid(printer_entry, sid)
|
self._wipe_sid(printer_entry, sid)
|
||||||
self._wipe_sid(drive_entry, sid)
|
self._wipe_sid(drive_entry, sid)
|
||||||
|
self._wipe_sid(script_entry, sid)
|
||||||
|
self._wipe_sid(file_entry, sid)
|
||||||
|
self._wipe_sid(ini_entry, sid)
|
||||||
|
self._wipe_sid(networkshare_entry, sid)
|
||||||
|
|
||||||
def _wipe_sid(self, row_object, sid):
|
def _wipe_sid(self, row_object, sid):
|
||||||
(self
|
(self
|
||||||
|
|||||||
45
gpoa/storage/sqlite_registry_compat.py
Normal file
45
gpoa/storage/sqlite_registry_compat.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 BaseALT Ltd.
|
||||||
|
# Copyright (C) 2024 Evgeny SInelnikov <sin@altlinux.org>.
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
|
||||||
|
__compat__ = False
|
||||||
|
|
||||||
|
from sqlalchemy import MetaData
|
||||||
|
|
||||||
|
try:
|
||||||
|
from sqlalchemy.orm import registry
|
||||||
|
except:
|
||||||
|
from sqlalchemy.orm import mapper
|
||||||
|
__compat__ = True
|
||||||
|
|
||||||
|
class sqlite_registry_compat:
|
||||||
|
def __init__(self, db_cnt):
|
||||||
|
if not __compat__:
|
||||||
|
self.__registry = registry()
|
||||||
|
self.__metadata = MetaData()
|
||||||
|
else:
|
||||||
|
self.__metadata = MetaData(db_cnt)
|
||||||
|
|
||||||
|
def metadata(self):
|
||||||
|
return self.__metadata
|
||||||
|
|
||||||
|
def map_imperatively(self, obj, table):
|
||||||
|
if __compat__:
|
||||||
|
mapper(obj, table)
|
||||||
|
else:
|
||||||
|
self.__registry.map_imperatively(obj, table)
|
||||||
63
gpoa/templates/47-alt_group_policy_permissions.rules.j2
Normal file
63
gpoa/templates/47-alt_group_policy_permissions.rules.j2
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 No|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in No -%}
|
||||||
|
action.id == "{{res}}"{% if No|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.NO;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Yes|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Yes -%}
|
||||||
|
action.id == "{{res}}"{% if Yes|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_self|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_self -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_self|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_SELF;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_admin|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_admin -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_admin|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_ADMIN;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_self_keep|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_self_keep -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_self_keep|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_SELF_KEEP;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_admin_keep|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_admin_keep -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_admin_keep|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_ADMIN_KEEP;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
63
gpoa/templates/48-alt_group_policy_permissions_user.rules.j2
Normal file
63
gpoa/templates/48-alt_group_policy_permissions_user.rules.j2
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 No|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in No -%}
|
||||||
|
action.id == "{{res}}" {% if No|length == loop.index %}&&{% else %}||{% endif %}
|
||||||
|
{% endfor %}subject.user == "{{User}}") {
|
||||||
|
return polkit.Result.NO;
|
||||||
|
}
|
||||||
|
});{% endif %}{% if Yes|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Yes -%}
|
||||||
|
action.id == "{{res}}" {% if Yes|length == loop.index %}&&{% else %}||{% endif %}
|
||||||
|
{% endfor %}subject.user == "{{User}}") {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});{% endif %}{% if Auth_self|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_self -%}
|
||||||
|
action.id == "{{res}}" {% if Auth_self|length == loop.index %}&&{% else %}||{% endif %}
|
||||||
|
{% endfor %}subject.user == "{{User}}") {
|
||||||
|
return polkit.Result.AUTH_SELF;
|
||||||
|
}
|
||||||
|
});{% endif %}{% if Auth_admin|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_admin -%}
|
||||||
|
action.id == "{{res}}" {% if Auth_admin|length == loop.index %}&&{% else %}||{% endif %}
|
||||||
|
{% endfor %}subject.user == "{{User}}") {
|
||||||
|
return polkit.Result.AUTH_ADMIN;
|
||||||
|
}
|
||||||
|
});{% endif %}{% if Auth_self_keep|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_self_keep -%}
|
||||||
|
action.id == "{{res}}" {% if Auth_self_keep|length == loop.index %}&&{% else %}||{% endif %}
|
||||||
|
{% endfor %}subject.user == "{{User}}") {
|
||||||
|
return polkit.Result.AUTH_SELF_KEEP;
|
||||||
|
}
|
||||||
|
});{% endif %}{% if Auth_admin_keep|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_admin_keep -%}
|
||||||
|
action.id == "{{res}}" {% if Auth_admin_keep|length == loop.index %}&&{% else %}||{% endif %}
|
||||||
|
{% endfor %}subject.user == "{{User}}") {
|
||||||
|
return polkit.Result.AUTH_ADMIN_KEEP;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
|
|
||||||
{% if Deny_All == '1' %}
|
{% if Deny_All == 1 %}
|
||||||
polkit.addRule(function (action, subject) {
|
polkit.addRule(function (action, subject) {
|
||||||
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
if ((action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
||||||
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
||||||
|
|||||||
63
gpoa/templates/49-alt_group_policy_permissions.rules.j2
Normal file
63
gpoa/templates/49-alt_group_policy_permissions.rules.j2
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 No|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in No -%}
|
||||||
|
action.id == "{{res}}"{% if No|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.NO;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Yes|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Yes -%}
|
||||||
|
action.id == "{{res}}"{% if Yes|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_self|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_self -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_self|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_SELF;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_admin|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_admin -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_admin|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_ADMIN;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_self_keep|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_self_keep -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_self_keep|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_SELF_KEEP;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{% endif %}{% if Auth_admin_keep|length %}
|
||||||
|
polkit.addRule(function (action, subject) {
|
||||||
|
if ({% for res in Auth_admin_keep -%}
|
||||||
|
action.id == "{{res}}"{% if Auth_admin_keep|length == loop.index %}){ {% else %} ||{% endif %}
|
||||||
|
{% endfor %} return polkit.Result.AUTH_ADMIN_KEEP;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
|
|
||||||
{% if Deny_All == '1' %}
|
{% if Deny_All == 1 %}
|
||||||
polkit.addRule(function (action, subject) {
|
polkit.addRule(function (action, subject) {
|
||||||
if (action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
if (action.id == "org.freedesktop.udisks2.filesystem-mount" ||
|
||||||
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
|
||||||
|
|||||||
@@ -16,5 +16,5 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
{{ home_dir }}/net {{ mount_file }} -t 120
|
{{ home_dir }}/{{mntTarget}} {{ mount_file }} -t 120 --browse
|
||||||
|
|
||||||
|
|||||||
20
gpoa/templates/autofs_auto_hide.j2
Normal file
20
gpoa/templates/autofs_auto_hide.j2
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 }}/.{{mntTarget}} {{ mount_file }} -t 120
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{#
|
{#
|
||||||
# GPOA - GPO Applier for Linux
|
# GPOA - GPO Applier for Linux
|
||||||
#
|
#
|
||||||
# Copyright (C) 2019-2020 BaseALT Ltd.
|
# Copyright (C) 2019-2022 BaseALT Ltd.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,5 +17,11 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
{%- for drv in drives %}
|
{%- for drv in drives %}
|
||||||
{{ drv.dir }} -fstype=cifs,cruid=$USER,sec=krb5,noperm :{{ drv.path }}
|
{% if (drv.thisDrive != 'HIDE') %}
|
||||||
|
{% if drv.label %}
|
||||||
|
"{{ drv.label }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
||||||
|
{% else %}
|
||||||
|
"{{ drv.dir }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
27
gpoa/templates/autofs_mountpoints_hide.j2
Normal file
27
gpoa/templates/autofs_mountpoints_hide.j2
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2022 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 %}
|
||||||
|
{% if (drv.thisDrive == 'HIDE') %}
|
||||||
|
{% if drv.label %}
|
||||||
|
"{{ drv.label }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
||||||
|
{% else %}
|
||||||
|
"{{ drv.dir }}" -fstype=cifs,cruid=$USER,sec=krb5,noperm,cifsacl :{{ drv.path }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
@@ -64,18 +64,15 @@ def process_target(target_name=None):
|
|||||||
The target may be 'All', 'Computer' or 'User'. This function
|
The target may be 'All', 'Computer' or 'User'. This function
|
||||||
determines which one was specified.
|
determines which one was specified.
|
||||||
'''
|
'''
|
||||||
target = 'All'
|
|
||||||
|
|
||||||
if target_name == 'Computer':
|
target = "All"
|
||||||
target = 'Computer'
|
if target_name:
|
||||||
|
target = target_name
|
||||||
if target_name == 'User':
|
|
||||||
target = 'User'
|
|
||||||
|
|
||||||
logdata = dict({'target': target})
|
logdata = dict({'target': target})
|
||||||
logging.debug(slogm(message_with_code('D10'), logdata))
|
logging.debug(slogm(message_with_code('D10'), logdata))
|
||||||
|
|
||||||
return target
|
return target.upper()
|
||||||
|
|
||||||
class ExitCodeUpdater(IntEnum):
|
class ExitCodeUpdater(IntEnum):
|
||||||
'''
|
'''
|
||||||
|
|||||||
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)
|
||||||
|
|
||||||
@@ -28,23 +28,62 @@ class dbus_runner:
|
|||||||
to trigger gpoa for user running in sysadmin context.
|
to trigger gpoa for user running in sysadmin context.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
_bus_name = 'com.redhat.oddjob_gpupdate'
|
_redhat_bus_name = 'com.redhat.oddjob_gpupdate'
|
||||||
|
_basealt_bus_name = 'ru.basealt.oddjob_gpupdate'
|
||||||
|
# Interface name is equal to bus name.
|
||||||
|
_redhat_interface_name = 'com.redhat.oddjob_gpupdate'
|
||||||
|
_basealt_interface_name = 'ru.basealt.oddjob_gpupdate'
|
||||||
_object_path = '/'
|
_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):
|
def __init__(self, username=None):
|
||||||
self.username = username
|
self.username = username
|
||||||
system_bus = dbus.SystemBus()
|
self.system_bus = dbus.SystemBus()
|
||||||
obj = system_bus.get_object(self._bus_name, self._object_path)
|
self.bus_name = self._basealt_bus_name
|
||||||
self.interface = dbus.Interface(obj, self._bus_name)
|
self.interface_name = self._basealt_interface_name
|
||||||
|
self.check_dbus()
|
||||||
|
|
||||||
|
def check_dbus(self):
|
||||||
|
try:
|
||||||
|
# Check privileged operations bus
|
||||||
|
log('D900', {'bus_name': self.bus_name})
|
||||||
|
self.system_bus.get_object(self.bus_name, '/')
|
||||||
|
return
|
||||||
|
|
||||||
|
except dbus.exceptions.DBusException as exc:
|
||||||
|
if exc.get_dbus_name() != 'org.freedesktop.DBus.Error.ServiceUnknown':
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
self.bus_name = self._redhat_bus_name
|
||||||
|
self.interface_name = self._redhat_interface_name
|
||||||
|
|
||||||
|
# Try to check alternative privileged operations bus
|
||||||
|
log('W902', {'origin_bus_name': self._basealt_interface_name, 'bus_name': self.bus_name})
|
||||||
|
self.system_bus.get_object(self.bus_name, '/')
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
#print(obj.Introspect()[0])
|
|
||||||
if self.username:
|
if self.username:
|
||||||
logdata = dict({'username': self.username})
|
logdata = dict({'username': self.username})
|
||||||
log('D6', logdata)
|
log('D6', logdata)
|
||||||
if is_root():
|
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:
|
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',
|
||||||
|
's',
|
||||||
|
[self.username],
|
||||||
|
timeout=self._synchronous_timeout)
|
||||||
print_dbus_result(result)
|
print_dbus_result(result)
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
@@ -53,20 +92,36 @@ class dbus_runner:
|
|||||||
raise exc
|
raise exc
|
||||||
else:
|
else:
|
||||||
try:
|
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)
|
print_dbus_result(result)
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
log('E21')
|
logdata = dict({'error': str(exc)})
|
||||||
|
log('E21', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
else:
|
else:
|
||||||
log('D11')
|
log('D11')
|
||||||
try:
|
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)
|
print_dbus_result(result)
|
||||||
except dbus.exceptions.DBusException as exc:
|
except dbus.exceptions.DBusException as exc:
|
||||||
log('E22')
|
print(exc)
|
||||||
|
logdata = dict({'error': str(exc)})
|
||||||
|
log('E22', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
#self.interface.Quit()
|
|
||||||
|
|
||||||
|
|
||||||
def start_gpupdate_user():
|
def start_gpupdate_user():
|
||||||
@@ -114,6 +169,11 @@ def is_oddjobd_gpupdate_accessible():
|
|||||||
oddjobd_state = oddjobd_properties.Get('org.freedesktop.systemd1.Unit', 'ActiveState')
|
oddjobd_state = oddjobd_properties.Get('org.freedesktop.systemd1.Unit', 'ActiveState')
|
||||||
|
|
||||||
# Check if oddjobd_gpupdate is accesssible
|
# Check if oddjobd_gpupdate is accesssible
|
||||||
|
try:
|
||||||
|
oddjobd_gpupdate = system_bus.get_object('ru.basealt.oddjob_gpupdate', '/')
|
||||||
|
oddjobd_upupdate_interface = dbus.Interface(oddjobd_gpupdate, 'ru.basealt.oddjob_gpupdate')
|
||||||
|
except dbus.exceptions.DBusException as exc:
|
||||||
|
if exc.get_dbus_name() != '.org.freedesktop.DBus.Error.ServiceUnknown':
|
||||||
oddjobd_gpupdate = system_bus.get_object('com.redhat.oddjob_gpupdate', '/')
|
oddjobd_gpupdate = system_bus.get_object('com.redhat.oddjob_gpupdate', '/')
|
||||||
oddjobd_upupdate_interface = dbus.Interface(oddjobd_gpupdate, 'com.redhat.oddjob_gpupdate')
|
oddjobd_upupdate_interface = dbus.Interface(oddjobd_gpupdate, 'com.redhat.oddjob_gpupdate')
|
||||||
#oddjobd_upupdate_interface.gpupdate()
|
#oddjobd_upupdate_interface.gpupdate()
|
||||||
@@ -136,5 +196,29 @@ def print_dbus_result(result):
|
|||||||
log('D12', logdata)
|
log('D12', logdata)
|
||||||
|
|
||||||
for line in message:
|
for line in message:
|
||||||
print(str(line))
|
if line: print(str(line))
|
||||||
|
|
||||||
|
|
||||||
|
class dbus_session:
|
||||||
|
def __init__(self):
|
||||||
|
try:
|
||||||
|
self.session_bus = dbus.SessionBus()
|
||||||
|
self.session_dbus = self.session_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
|
||||||
|
self.session_iface = dbus.Interface(self.session_dbus, 'org.freedesktop.DBus')
|
||||||
|
except dbus.exceptions.DBusException as exc:
|
||||||
|
logdata = dict({'error': str(exc)})
|
||||||
|
log('E31', logdata)
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
def get_connection_pid(self, connection):
|
||||||
|
pid = -1
|
||||||
|
try:
|
||||||
|
pid = self.session_iface.GetConnectionUnixProcessID(connection)
|
||||||
|
log('D57', {"pid": pid})
|
||||||
|
except dbus.exceptions.DBusException as exc:
|
||||||
|
if exc.get_dbus_name() != 'org.freedesktop.DBus.Error.NameHasNoOwner':
|
||||||
|
logdata = dict({'error': str(exc)})
|
||||||
|
log('E32', logdata)
|
||||||
|
raise exc
|
||||||
|
log('D58', {'connection': connection})
|
||||||
|
return int(pid)
|
||||||
|
|||||||
@@ -39,3 +39,17 @@ def geterr():
|
|||||||
|
|
||||||
return traceinfo
|
return traceinfo
|
||||||
|
|
||||||
|
class NotUNCPathError(Exception):
|
||||||
|
def __init__(self, path):
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
class GetGPOListFail(Exception):
|
||||||
|
def __init__(self, exc):
|
||||||
|
self.exc = exc
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.exc
|
||||||
|
|
||||||
|
|||||||
356
gpoa/util/gpoa_ini_parsing.py
Normal file
356
gpoa/util/gpoa_ini_parsing.py
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
#
|
||||||
|
# GPOA - GPO Applier for Linux
|
||||||
|
#
|
||||||
|
# Copyright (C) 2019-2023 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 configobj import (ConfigObj, NestingError, Section,
|
||||||
|
DuplicateError, ParseError, UnreprError,
|
||||||
|
UnknownType,UnreprError,
|
||||||
|
BOM_UTF8, DEFAULT_INDENT_TYPE, BOM_LIST,
|
||||||
|
match_utf8, unrepr)
|
||||||
|
import six
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Michael Foord: fuzzyman AT voidspace DOT org DOT uk
|
||||||
|
# Nicola Larosa: nico AT tekNico DOT net
|
||||||
|
# Rob Dennis: rdennis AT gmail DOT com
|
||||||
|
# Eli Courtwright: eli AT courtwright DOT org
|
||||||
|
# This class based on the ConfigObj module, distributed under the BSD-3-Clause license.
|
||||||
|
# This class includes modified code from the ConfigObj module mentioned above.
|
||||||
|
# The original authors and their contact information are listed in the comments above.
|
||||||
|
# For more information about ConfigObj, please visit the main repository:
|
||||||
|
# https://github.com/DiffSK/configobj
|
||||||
|
|
||||||
|
|
||||||
|
class GpoaConfigObj(ConfigObj):
|
||||||
|
|
||||||
|
_sectionmarker = re.compile(r'''^
|
||||||
|
(\s*) # 1: indentation
|
||||||
|
((?:\[\s*)+) # 2: section marker open
|
||||||
|
( # 3: section name open
|
||||||
|
(?:"\s*\S.*?\s*")| # at least one non-space with double quotes
|
||||||
|
(?:'\s*\S.*?\s*')| # at least one non-space with single quotes
|
||||||
|
(?:[^'"\s].*?) # at least one non-space unquoted
|
||||||
|
) # section name close
|
||||||
|
((?:\s*\])+) # 4: section marker close
|
||||||
|
(\s*(?:[#;].*)?)? # 5: optional comment
|
||||||
|
$''',
|
||||||
|
re.VERBOSE)
|
||||||
|
|
||||||
|
_valueexp = re.compile(r'''^
|
||||||
|
(?:
|
||||||
|
(?:
|
||||||
|
(
|
||||||
|
(?:
|
||||||
|
(?:
|
||||||
|
(?:".*?")| # double quotes
|
||||||
|
(?:'.*?')| # single quotes
|
||||||
|
(?:[^'",\#][^,\#]*?) # unquoted
|
||||||
|
)
|
||||||
|
\s*,\s* # comma
|
||||||
|
)* # match all list items ending in a comma (if any)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
(?:".*?")| # double quotes
|
||||||
|
(?:'.*?')| # single quotes
|
||||||
|
(?:[^'",\#\s][^,]*?)| # unquoted
|
||||||
|
(?:(?<!,)) # Empty value
|
||||||
|
)? # last item in a list - or string value
|
||||||
|
)|
|
||||||
|
(,) # alternatively a single comma - empty list
|
||||||
|
)
|
||||||
|
(\s*(?:[#;].*)?)? # optional comment
|
||||||
|
$''',
|
||||||
|
re.VERBOSE)
|
||||||
|
|
||||||
|
COMMENT_MARKERS = ['#', ';']
|
||||||
|
|
||||||
|
def _handle_comment(self, comment):
|
||||||
|
"""Deal with a comment."""
|
||||||
|
if not comment:
|
||||||
|
return ''
|
||||||
|
start = self.indent_type
|
||||||
|
if not comment.lstrip().startswith(tuple(self.COMMENT_MARKERS)):
|
||||||
|
start += ' # '
|
||||||
|
return start + comment.strip()
|
||||||
|
|
||||||
|
def _parse(self, infile):
|
||||||
|
"""Actually parse the config file."""
|
||||||
|
temp_list_values = self.list_values
|
||||||
|
if self.unrepr:
|
||||||
|
self.list_values = False
|
||||||
|
|
||||||
|
comment_list = []
|
||||||
|
done_start = False
|
||||||
|
this_section = self
|
||||||
|
maxline = len(infile) - 1
|
||||||
|
cur_index = -1
|
||||||
|
reset_comment = False
|
||||||
|
comment_markers = tuple(self.COMMENT_MARKERS)
|
||||||
|
|
||||||
|
while cur_index < maxline:
|
||||||
|
if reset_comment:
|
||||||
|
comment_list = []
|
||||||
|
cur_index += 1
|
||||||
|
line = infile[cur_index]
|
||||||
|
sline = line.strip()
|
||||||
|
# do we have anything on the line ?
|
||||||
|
if not sline or sline.startswith(comment_markers):
|
||||||
|
reset_comment = False
|
||||||
|
comment_list.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not done_start:
|
||||||
|
# preserve initial comment
|
||||||
|
self.initial_comment = comment_list
|
||||||
|
comment_list = []
|
||||||
|
done_start = True
|
||||||
|
|
||||||
|
reset_comment = True
|
||||||
|
# first we check if it's a section marker
|
||||||
|
mat = self._sectionmarker.match(line)
|
||||||
|
if mat is not None:
|
||||||
|
# is a section line
|
||||||
|
(indent, sect_open, sect_name, sect_close, comment) = mat.groups()
|
||||||
|
if indent and (self.indent_type is None):
|
||||||
|
self.indent_type = indent
|
||||||
|
cur_depth = sect_open.count('[')
|
||||||
|
if cur_depth != sect_close.count(']'):
|
||||||
|
self._handle_error("Cannot compute the section depth",
|
||||||
|
NestingError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cur_depth < this_section.depth:
|
||||||
|
# the new section is dropping back to a previous level
|
||||||
|
try:
|
||||||
|
parent = self._match_depth(this_section,
|
||||||
|
cur_depth).parent
|
||||||
|
except SyntaxError:
|
||||||
|
self._handle_error("Cannot compute nesting level",
|
||||||
|
NestingError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
elif cur_depth == this_section.depth:
|
||||||
|
# the new section is a sibling of the current section
|
||||||
|
parent = this_section.parent
|
||||||
|
elif cur_depth == this_section.depth + 1:
|
||||||
|
# the new section is a child the current section
|
||||||
|
parent = this_section
|
||||||
|
else:
|
||||||
|
self._handle_error("Section too nested",
|
||||||
|
NestingError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
|
||||||
|
sect_name = self._unquote(sect_name)
|
||||||
|
if sect_name in parent:
|
||||||
|
self._handle_error('Duplicate section name',
|
||||||
|
DuplicateError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# create the new section
|
||||||
|
this_section = Section(
|
||||||
|
parent,
|
||||||
|
cur_depth,
|
||||||
|
self,
|
||||||
|
name=sect_name)
|
||||||
|
parent[sect_name] = this_section
|
||||||
|
parent.inline_comments[sect_name] = comment
|
||||||
|
parent.comments[sect_name] = comment_list
|
||||||
|
continue
|
||||||
|
#
|
||||||
|
# it's not a section marker,
|
||||||
|
# so it should be a valid ``key = value`` line
|
||||||
|
mat = self._keyword.match(line)
|
||||||
|
if mat is None:
|
||||||
|
self._handle_error(
|
||||||
|
'Invalid line ({!r}) (matched as neither section nor keyword)'.format(line),
|
||||||
|
ParseError, infile, cur_index)
|
||||||
|
else:
|
||||||
|
# is a keyword value
|
||||||
|
# value will include any inline comment
|
||||||
|
(indent, key, value) = mat.groups()
|
||||||
|
if indent and (self.indent_type is None):
|
||||||
|
self.indent_type = indent
|
||||||
|
# check for a multiline value
|
||||||
|
if value[:3] in ['"""', "'''"]:
|
||||||
|
try:
|
||||||
|
value, comment, cur_index = self._multiline(
|
||||||
|
value, infile, cur_index, maxline)
|
||||||
|
except SyntaxError:
|
||||||
|
self._handle_error(
|
||||||
|
'Parse error in multiline value',
|
||||||
|
ParseError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if self.unrepr:
|
||||||
|
comment = ''
|
||||||
|
try:
|
||||||
|
value = unrepr(value)
|
||||||
|
except Exception as cause:
|
||||||
|
if isinstance(cause, UnknownType):
|
||||||
|
msg = 'Unknown name or type in value'
|
||||||
|
else:
|
||||||
|
msg = 'Parse error from unrepr-ing multiline value'
|
||||||
|
self._handle_error(msg, UnreprError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if self.unrepr:
|
||||||
|
comment = ''
|
||||||
|
try:
|
||||||
|
value = unrepr(value)
|
||||||
|
except Exception as cause:
|
||||||
|
if isinstance(cause, UnknownType):
|
||||||
|
msg = 'Unknown name or type in value'
|
||||||
|
else:
|
||||||
|
msg = 'Parse error from unrepr-ing value'
|
||||||
|
self._handle_error(msg, UnreprError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# extract comment and lists
|
||||||
|
try:
|
||||||
|
(value, comment) = self._handle_value(value)
|
||||||
|
except SyntaxError:
|
||||||
|
self._handle_error(
|
||||||
|
'Parse error in value',
|
||||||
|
ParseError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
#
|
||||||
|
key = self._unquote(key)
|
||||||
|
if key in this_section:
|
||||||
|
self._handle_error(
|
||||||
|
'Duplicate keyword name',
|
||||||
|
DuplicateError, infile, cur_index)
|
||||||
|
continue
|
||||||
|
# add the key.
|
||||||
|
# we set unrepr because if we have got this far we will never
|
||||||
|
# be creating a new section
|
||||||
|
this_section.__setitem__(key, value, unrepr=True)
|
||||||
|
this_section.inline_comments[key] = comment
|
||||||
|
this_section.comments[key] = comment_list
|
||||||
|
continue
|
||||||
|
#
|
||||||
|
if self.indent_type is None:
|
||||||
|
# no indentation used, set the type accordingly
|
||||||
|
self.indent_type = ''
|
||||||
|
|
||||||
|
# preserve the final comment
|
||||||
|
if not self and not self.initial_comment:
|
||||||
|
self.initial_comment = comment_list
|
||||||
|
elif not reset_comment:
|
||||||
|
self.final_comment = comment_list
|
||||||
|
self.list_values = temp_list_values
|
||||||
|
|
||||||
|
|
||||||
|
def write(self, outfile=None, section=None):
|
||||||
|
if self.indent_type is None:
|
||||||
|
# this can be true if initialised from a dictionary
|
||||||
|
self.indent_type = DEFAULT_INDENT_TYPE
|
||||||
|
|
||||||
|
out = []
|
||||||
|
comment_markers = tuple(self.COMMENT_MARKERS)
|
||||||
|
comment_marker_default = comment_markers[0] + ' '
|
||||||
|
if section is None:
|
||||||
|
int_val = self.interpolation
|
||||||
|
self.interpolation = False
|
||||||
|
section = self
|
||||||
|
for line in self.initial_comment:
|
||||||
|
line = self._decode_element(line)
|
||||||
|
stripped_line = line.strip()
|
||||||
|
if stripped_line and not stripped_line.startswith(comment_markers):
|
||||||
|
line = comment_marker_default + line
|
||||||
|
out.append(line)
|
||||||
|
|
||||||
|
indent_string = self.indent_type * section.depth
|
||||||
|
for entry in (section.scalars + section.sections):
|
||||||
|
if entry in section.defaults:
|
||||||
|
# don't write out default values
|
||||||
|
continue
|
||||||
|
for comment_line in section.comments[entry]:
|
||||||
|
comment_line = self._decode_element(comment_line.lstrip())
|
||||||
|
if comment_line and not comment_line.startswith(comment_markers):
|
||||||
|
comment_line = comment_marker_default + comment_line
|
||||||
|
out.append(indent_string + comment_line)
|
||||||
|
this_entry = section[entry]
|
||||||
|
comment = self._handle_comment(section.inline_comments[entry])
|
||||||
|
|
||||||
|
if isinstance(this_entry, Section):
|
||||||
|
# a section
|
||||||
|
out.append(self._write_marker(
|
||||||
|
indent_string,
|
||||||
|
this_entry.depth,
|
||||||
|
entry,
|
||||||
|
comment))
|
||||||
|
out.extend(self.write(section=this_entry))
|
||||||
|
else:
|
||||||
|
out.append(self._write_line(
|
||||||
|
indent_string,
|
||||||
|
entry,
|
||||||
|
this_entry,
|
||||||
|
comment))
|
||||||
|
|
||||||
|
if section is self:
|
||||||
|
for line in self.final_comment:
|
||||||
|
line = self._decode_element(line)
|
||||||
|
stripped_line = line.strip()
|
||||||
|
if stripped_line and not stripped_line.startswith(comment_markers):
|
||||||
|
line = comment_marker_default + line
|
||||||
|
out.append(line)
|
||||||
|
self.interpolation = int_val
|
||||||
|
|
||||||
|
if section is not self:
|
||||||
|
return out
|
||||||
|
|
||||||
|
if (self.filename is None) and (outfile is None):
|
||||||
|
# output a list of lines
|
||||||
|
# might need to encode
|
||||||
|
# NOTE: This will *screw* UTF16, each line will start with the BOM
|
||||||
|
if self.encoding:
|
||||||
|
out = [l.encode(self.encoding) for l in out]
|
||||||
|
if (self.BOM and ((self.encoding is None) or
|
||||||
|
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
|
||||||
|
# Add the UTF8 BOM
|
||||||
|
if not out:
|
||||||
|
out.append('')
|
||||||
|
out[0] = BOM_UTF8 + out[0]
|
||||||
|
return out
|
||||||
|
|
||||||
|
# Turn the list to a string, joined with correct newlines
|
||||||
|
newline = self.newlines or os.linesep
|
||||||
|
if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w'
|
||||||
|
and sys.platform == 'win32' and newline == '\r\n'):
|
||||||
|
# Windows specific hack to avoid writing '\r\r\n'
|
||||||
|
newline = '\n'
|
||||||
|
output = newline.join(out)
|
||||||
|
if not output.endswith(newline):
|
||||||
|
output += newline
|
||||||
|
|
||||||
|
if isinstance(output, six.binary_type):
|
||||||
|
output_bytes = output
|
||||||
|
else:
|
||||||
|
output_bytes = output.encode(self.encoding or
|
||||||
|
self.default_encoding or
|
||||||
|
'ascii')
|
||||||
|
|
||||||
|
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
|
||||||
|
# Add the UTF8 BOM
|
||||||
|
output_bytes = BOM_UTF8 + output_bytes
|
||||||
|
|
||||||
|
if outfile is not None:
|
||||||
|
outfile.write(output_bytes)
|
||||||
|
else:
|
||||||
|
with open(self.filename, 'wb') as h:
|
||||||
|
h.write(output_bytes)
|
||||||
@@ -21,15 +21,19 @@ import subprocess
|
|||||||
|
|
||||||
from .util import get_machine_name
|
from .util import get_machine_name
|
||||||
from .logging import log
|
from .logging import log
|
||||||
|
from .samba import smbopts
|
||||||
|
|
||||||
|
|
||||||
def machine_kinit(cache_name=None):
|
def machine_kinit(cache_name=None):
|
||||||
'''
|
'''
|
||||||
Perform kinit with machine credentials
|
Perform kinit with machine credentials
|
||||||
'''
|
'''
|
||||||
|
opts = smbopts()
|
||||||
host = get_machine_name()
|
host = get_machine_name()
|
||||||
|
realm = opts.get_realm()
|
||||||
|
with_realm = '{}@{}'.format(host, realm)
|
||||||
os.environ['KRB5CCNAME'] = 'FILE:{}'.format(cache_name)
|
os.environ['KRB5CCNAME'] = 'FILE:{}'.format(cache_name)
|
||||||
kinit_cmd = ['kinit', '-k', host]
|
kinit_cmd = ['kinit', '-k', with_realm]
|
||||||
if cache_name:
|
if cache_name:
|
||||||
kinit_cmd.extend(['-c', cache_name])
|
kinit_cmd.extend(['-c', cache_name])
|
||||||
proc = subprocess.Popen(kinit_cmd)
|
proc = subprocess.Popen(kinit_cmd)
|
||||||
@@ -55,6 +59,7 @@ def machine_kdestroy(cache_name=None):
|
|||||||
if cache_name:
|
if cache_name:
|
||||||
kdestroy_cmd.extend(['-c', 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 = subprocess.Popen(kdestroy_cmd, stderr=subprocess.DEVNULL)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
@@ -74,11 +79,13 @@ def check_krb_ticket():
|
|||||||
try:
|
try:
|
||||||
subprocess.check_call(['klist', '-s'])
|
subprocess.check_call(['klist', '-s'])
|
||||||
output = subprocess.check_output('klist', stderr=subprocess.STDOUT).decode()
|
output = subprocess.check_output('klist', stderr=subprocess.STDOUT).decode()
|
||||||
logging.info(output)
|
|
||||||
result = True
|
result = True
|
||||||
except:
|
logdata = dict()
|
||||||
log('E14')
|
logdata['output'] = output
|
||||||
|
log('D17', logdata)
|
||||||
log('D17')
|
except Exception as exc:
|
||||||
|
logdata = dict()
|
||||||
|
logdata['krb-exc'] = exc
|
||||||
|
log('E14', logdata)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -49,12 +49,8 @@ class slogm(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
now = str(datetime.datetime.now().isoformat(sep=' ', timespec='milliseconds'))
|
now = str(datetime.datetime.now().isoformat(sep=' ', timespec='milliseconds'))
|
||||||
args = dict()
|
args = dict()
|
||||||
#args.update(dict({'timestamp': now, 'message': str(self.message)}))
|
|
||||||
args.update(self.kwargs)
|
args.update(self.kwargs)
|
||||||
|
result = '{}|{}|{}'.format(now, self.message, args)
|
||||||
kwa = encoder().encode(args)
|
|
||||||
|
|
||||||
result = '{}|{}|{}'.format(now, self.message, kwa)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#
|
#
|
||||||
# GPOA - GPO Applier for Linux
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,23 +19,39 @@
|
|||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
from util.util import get_homedir
|
||||||
|
|
||||||
|
from .config import GPConfig
|
||||||
|
from .exceptions import NotUNCPathError
|
||||||
|
|
||||||
|
|
||||||
def default_policy_path():
|
def get_custom_policy_dir():
|
||||||
'''
|
'''
|
||||||
Returns path pointing to Default Policy directory.
|
Returns path pointing to Custom Policy directory.
|
||||||
'''
|
'''
|
||||||
local_policy_default = '/usr/share/local-policy/default'
|
return '/etc/local-policy'
|
||||||
etc_local_policy_default = '/etc/local-policy/active'
|
|
||||||
|
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)
|
result_path = pathlib.Path(local_policy_default)
|
||||||
|
if os.path.exists(local_policy_template):
|
||||||
if os.path.exists(etc_local_policy_default):
|
result_path = pathlib.Path(local_policy_template)
|
||||||
result_path = pathlib.Path(etc_local_policy_default)
|
elif os.path.exists(local_policy_template_path):
|
||||||
|
result_path = pathlib.Path(local_policy_template_path)
|
||||||
|
|
||||||
return pathlib.Path(result_path)
|
return pathlib.Path(result_path)
|
||||||
|
|
||||||
|
|
||||||
def cache_dir():
|
def cache_dir():
|
||||||
'''
|
'''
|
||||||
Returns path pointing to gpupdate's cache directory
|
Returns path pointing to gpupdate's cache directory
|
||||||
@@ -46,6 +63,23 @@ def cache_dir():
|
|||||||
|
|
||||||
return cachedir
|
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 file_cache_path_home(username) -> str:
|
||||||
|
'''
|
||||||
|
Returns the path pointing to the gpupdate cache directory in the /home directory.
|
||||||
|
'''
|
||||||
|
cachedir = f'{get_homedir(username)}/.cache/gpupdate'
|
||||||
|
|
||||||
|
return cachedir
|
||||||
|
|
||||||
def local_policy_cache():
|
def local_policy_cache():
|
||||||
'''
|
'''
|
||||||
@@ -59,3 +93,55 @@ def local_policy_cache():
|
|||||||
|
|
||||||
return lpcache
|
return lpcache
|
||||||
|
|
||||||
|
def get_dconf_config_path(uid = None):
|
||||||
|
if uid:
|
||||||
|
return f'/etc/dconf/db/policy{uid}.d/policy{uid}.ini'
|
||||||
|
else:
|
||||||
|
return '/etc/dconf/db/policy.d/policy.ini'
|
||||||
|
|
||||||
|
def get_desktop_files_directory():
|
||||||
|
return '/usr/share/applications'
|
||||||
|
|
||||||
|
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'\\') or 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()
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
from storage import registry_factory
|
from storage import registry_factory
|
||||||
|
from storage.dconf_registry import load_preg_dconf
|
||||||
|
|
||||||
from samba.gp_parse.gp_pol import GPPolParser
|
from samba.gp_parse.gp_pol import GPPolParser
|
||||||
|
|
||||||
@@ -80,10 +81,16 @@ def preg_keymap(preg):
|
|||||||
return keymap
|
return keymap
|
||||||
|
|
||||||
|
|
||||||
def merge_polfile(preg, sid=None, reg_name='registry', reg_path=None, policy_name='Unknown'):
|
def merge_polfile(preg, sid=None, reg_name='registry', reg_path=None, policy_name='Unknown', username='Machine', version=None):
|
||||||
pregfile = load_preg(preg)
|
pregfile = load_preg(preg)
|
||||||
|
if sid is None and username == 'Machine':
|
||||||
|
load_preg_dconf(pregfile, preg, policy_name, None, version)
|
||||||
|
else:
|
||||||
|
load_preg_dconf(pregfile, preg, policy_name, username, version)
|
||||||
logdata = dict({'pregfile': preg})
|
logdata = dict({'pregfile': preg})
|
||||||
log('D32', logdata)
|
log('D32', logdata)
|
||||||
|
#log dconf
|
||||||
|
return
|
||||||
storage = registry_factory(reg_name, reg_path)
|
storage = registry_factory(reg_name, reg_path)
|
||||||
for entry in pregfile.entries:
|
for entry in pregfile.entries:
|
||||||
if not sid:
|
if not sid:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import optparse
|
import optparse
|
||||||
|
import socket
|
||||||
from samba import getopt as options
|
from samba import getopt as options
|
||||||
|
|
||||||
|
|
||||||
@@ -28,11 +29,32 @@ class smbopts:
|
|||||||
self.sambaopts = options.SambaOptions(self.parser)
|
self.sambaopts = options.SambaOptions(self.parser)
|
||||||
self.lp = self.sambaopts.get_loadparm()
|
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):
|
def get_cache_dir(self):
|
||||||
return self._get_prop('cache directory')
|
return self._get_prop('cache directory')
|
||||||
|
|
||||||
def get_server_role(self):
|
def get_server_role(self):
|
||||||
return self._get_prop('server role')
|
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):
|
def _get_prop(self, property_name):
|
||||||
return self.lp.get(property_name)
|
return self.lp.get(property_name)
|
||||||
|
|||||||
@@ -19,6 +19,12 @@
|
|||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
import pwd
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import pysss_nss_idmap
|
||||||
|
|
||||||
|
from .logging import log
|
||||||
|
|
||||||
def wbinfo_getsid(domain, user):
|
def wbinfo_getsid(domain, user):
|
||||||
'''
|
'''
|
||||||
@@ -39,25 +45,35 @@ def wbinfo_getsid(domain, user):
|
|||||||
return sid
|
return sid
|
||||||
|
|
||||||
|
|
||||||
def get_sid(domain, username):
|
def get_local_sid_prefix():
|
||||||
|
return "S-1-5-21-0-0-0"
|
||||||
|
|
||||||
|
|
||||||
|
def get_sid(domain, username, is_machine = False):
|
||||||
'''
|
'''
|
||||||
Lookup SID not only using wbinfo or sssd but also using own cache
|
Lookup SID not only using wbinfo or sssd but also using own cache
|
||||||
'''
|
'''
|
||||||
domain_username = '{}\\{}'.format(domain, username)
|
|
||||||
sid = 'local-{}'.format(username)
|
sid = 'local-{}'.format(username)
|
||||||
|
|
||||||
|
# local user
|
||||||
|
if not domain:
|
||||||
|
found_uid = 0
|
||||||
|
if not is_machine:
|
||||||
|
found_uid = pwd.getpwnam(username).pw_uid
|
||||||
|
return '{}-{}'.format(get_local_sid_prefix(), found_uid)
|
||||||
|
|
||||||
|
# domain user
|
||||||
try:
|
try:
|
||||||
sid = wbinfo_getsid(domain, username)
|
sid = wbinfo_getsid(domain, username)
|
||||||
except:
|
except:
|
||||||
sid = 'local-{}'.format(username)
|
logdata = dict({'sid': sid})
|
||||||
logging.warning(
|
log('E16', logdata)
|
||||||
slogm('Error getting SID using wbinfo, will use cached SID: {}'.format(sid)))
|
|
||||||
|
|
||||||
logging.debug(slogm('Working with SID: {}'.format(sid)))
|
logdata = dict({'sid': sid})
|
||||||
|
log('D21', logdata)
|
||||||
|
|
||||||
return sid
|
return sid
|
||||||
|
|
||||||
|
|
||||||
class IssuingAuthority(Enum):
|
class IssuingAuthority(Enum):
|
||||||
SECURITY_NULL_SID_AUTHORITY = 0
|
SECURITY_NULL_SID_AUTHORITY = 0
|
||||||
SECURITY_WORLD_SID_AUTHORITY = 1
|
SECURITY_WORLD_SID_AUTHORITY = 1
|
||||||
|
|||||||
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
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -47,7 +47,6 @@ def username_match_uid(username):
|
|||||||
'''
|
'''
|
||||||
Check the passed username matches current process UID.
|
Check the passed username matches current process UID.
|
||||||
'''
|
'''
|
||||||
uid = os.getuid()
|
|
||||||
process_username = get_process_user()
|
process_username = get_process_user()
|
||||||
|
|
||||||
if process_username == username:
|
if process_username == username:
|
||||||
@@ -55,59 +54,3 @@ def username_match_uid(username):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def set_privileges(username, uid, gid, groups=list()):
|
|
||||||
'''
|
|
||||||
Set current process privileges
|
|
||||||
'''
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.setegid(gid)
|
|
||||||
except Exception as exc:
|
|
||||||
print('setegid')
|
|
||||||
try:
|
|
||||||
os.seteuid(uid)
|
|
||||||
except Exception as exc:
|
|
||||||
print('seteuid')
|
|
||||||
#try:
|
|
||||||
# os.setgroups(groups)
|
|
||||||
#except Exception as exc:
|
|
||||||
# print('setgroups')
|
|
||||||
|
|
||||||
logdata = dict()
|
|
||||||
logdata['uid'] = uid
|
|
||||||
logdata['gid'] = gid
|
|
||||||
logdata['username'] = username
|
|
||||||
log('D37', logdata)
|
|
||||||
|
|
||||||
|
|
||||||
def with_privileges(username, func):
|
|
||||||
'''
|
|
||||||
Run supplied function with privileges for specified username.
|
|
||||||
'''
|
|
||||||
current_uid = os.getuid()
|
|
||||||
current_groups = os.getgrouplist('root', 0)
|
|
||||||
|
|
||||||
if not current_uid == 0:
|
|
||||||
raise Exception('Not enough permissions to drop privileges')
|
|
||||||
|
|
||||||
user_uid = pwd.getpwnam(username).pw_uid
|
|
||||||
user_gid = pwd.getpwnam(username).pw_gid
|
|
||||||
user_groups = os.getgrouplist(username, user_gid)
|
|
||||||
|
|
||||||
# Drop privileges
|
|
||||||
set_privileges(username, user_uid, user_gid, user_groups)
|
|
||||||
|
|
||||||
# We need to catch exception in order to be able to restore
|
|
||||||
# privileges later in this function
|
|
||||||
out = None
|
|
||||||
try:
|
|
||||||
out = func()
|
|
||||||
except Exception as exc:
|
|
||||||
log(str(exc))
|
|
||||||
|
|
||||||
# Restore privileges
|
|
||||||
set_privileges('root', current_uid, 0, current_groups)
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,17 +17,23 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
import socket
|
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from .samba import smbopts
|
||||||
|
import ast
|
||||||
|
|
||||||
|
|
||||||
def get_machine_name():
|
def get_machine_name():
|
||||||
'''
|
'''
|
||||||
Get localhost name looking like DC0$
|
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):
|
def is_machine_name(name):
|
||||||
@@ -83,3 +89,110 @@ def mk_homedir_path(username, homedir_path):
|
|||||||
os.chown(homedir, uid=uid, gid=gid)
|
os.chown(homedir, uid=uid, gid=gid)
|
||||||
longer_path = os.path.join(longer_path, elem)
|
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
|
||||||
|
'''
|
||||||
|
return ['local', 'samba']
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def string_to_literal_eval(string):
|
||||||
|
try:
|
||||||
|
literaleval = ast.literal_eval(string)
|
||||||
|
except:
|
||||||
|
literaleval = string
|
||||||
|
return literaleval
|
||||||
|
|
||||||
|
def try_dict_to_literal_eval(string):
|
||||||
|
try:
|
||||||
|
literaleval = ast.literal_eval(string)
|
||||||
|
if isinstance(literaleval ,dict):
|
||||||
|
return literaleval
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def touch_file(filename):
|
||||||
|
path = Path(filename)
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.touch()
|
||||||
|
|
||||||
|
def get_uid_by_username(username):
|
||||||
|
try:
|
||||||
|
user_info = pwd.getpwnam(username)
|
||||||
|
return user_info.pw_uid
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|||||||
@@ -18,24 +18,33 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pwd
|
import subprocess
|
||||||
|
|
||||||
from samba import getopt as options
|
from samba import getopt as options
|
||||||
|
from samba import NTSTATUSError
|
||||||
|
|
||||||
|
try:
|
||||||
from samba.gpclass import get_dc_hostname, check_refresh_gpo_list
|
from samba.gpclass import get_dc_hostname, check_refresh_gpo_list
|
||||||
|
except ImportError:
|
||||||
|
from samba.gp.gpclass import get_dc_hostname, check_refresh_gpo_list
|
||||||
|
|
||||||
from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
|
from samba.netcmd.common import netcmd_get_domain_infos_via_cldap
|
||||||
import samba.gpo
|
import samba.gpo
|
||||||
import pysss_nss_idmap
|
|
||||||
|
|
||||||
from storage import cache_factory
|
|
||||||
from messages import message_with_code
|
|
||||||
from .xdg import (
|
from .xdg import (
|
||||||
xdg_get_desktop
|
xdg_get_desktop
|
||||||
)
|
)
|
||||||
from .util import get_homedir
|
from .util import get_homedir
|
||||||
|
from .exceptions import GetGPOListFail
|
||||||
from .logging import log
|
from .logging import log
|
||||||
from .samba import smbopts
|
from .samba import smbopts
|
||||||
|
from gpoa.storage import registry_factory
|
||||||
|
from samba.samdb import SamDB
|
||||||
|
from samba.auth import system_session
|
||||||
|
import optparse
|
||||||
|
import ldb
|
||||||
|
import ipaddress
|
||||||
|
import netifaces
|
||||||
|
import random
|
||||||
|
|
||||||
class smbcreds (smbopts):
|
class smbcreds (smbopts):
|
||||||
|
|
||||||
@@ -43,7 +52,14 @@ class smbcreds (smbopts):
|
|||||||
smbopts.__init__(self, 'GPO Applier')
|
smbopts.__init__(self, 'GPO Applier')
|
||||||
self.credopts = options.CredentialsOptions(self.parser)
|
self.credopts = options.CredentialsOptions(self.parser)
|
||||||
self.creds = self.credopts.get_credentials(self.lp, fallback_machine=True)
|
self.creds = self.credopts.get_credentials(self.lp, fallback_machine=True)
|
||||||
self.selected_dc = self.set_dc(dc_fqdn)
|
self.set_dc(dc_fqdn)
|
||||||
|
self.sDomain = SiteDomainScanner(self.creds, self.lp, self.selected_dc)
|
||||||
|
self.dc_site_servers = self.sDomain.select_site_servers()
|
||||||
|
self.all_servers = self.sDomain.select_all_servers()
|
||||||
|
[self.all_servers.remove(element)
|
||||||
|
for element in self.dc_site_servers
|
||||||
|
if element in self.all_servers]
|
||||||
|
self.pdc_emulator_server = self.sDomain.select_pdc_emulator_server()
|
||||||
|
|
||||||
def get_dc(self):
|
def get_dc(self):
|
||||||
return self.selected_dc
|
return self.selected_dc
|
||||||
@@ -55,25 +71,20 @@ class smbcreds (smbopts):
|
|||||||
self.selected_dc = None
|
self.selected_dc = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
samba_dc = get_dc_hostname(self.creds, self.lp)
|
if dc_fqdn is not None:
|
||||||
|
|
||||||
if samba_dc != dc_fqdn and dc_fqdn is not None:
|
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['dc'] = samba_dc
|
logdata['user_dc'] = dc_fqdn
|
||||||
logdata['user_dc'] = dc
|
|
||||||
log('D38', logdata)
|
log('D38', logdata)
|
||||||
|
|
||||||
self.selected_dc = dc_fqdn
|
self.selected_dc = dc_fqdn
|
||||||
else:
|
else:
|
||||||
self.selected_dc = samba_dc
|
self.selected_dc = get_dc_hostname(self.creds, self.lp)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['msg'] = str(exc)
|
logdata['msg'] = str(exc)
|
||||||
log('E10', logdata)
|
log('E10', logdata)
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
return self.selected_dc
|
|
||||||
|
|
||||||
def get_domain(self):
|
def get_domain(self):
|
||||||
'''
|
'''
|
||||||
Get current Active Directory domain name
|
Get current Active Directory domain name
|
||||||
@@ -110,80 +121,176 @@ class smbcreds (smbopts):
|
|||||||
for gpo in gpos:
|
for gpo in gpos:
|
||||||
# These setters are taken from libgpo/pygpo.c
|
# These setters are taken from libgpo/pygpo.c
|
||||||
# print(gpo.ds_path) # LDAP entry
|
# print(gpo.ds_path) # LDAP entry
|
||||||
ldata = dict({'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name})
|
ldata = dict({'gpo_name': gpo.display_name, 'gpo_uuid': gpo.name, 'file_sys_path': gpo.file_sys_path})
|
||||||
log('I2', ldata)
|
log('I2', ldata)
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
if self.selected_dc != self.pdc_emulator_server:
|
||||||
|
raise GetGPOListFail(exc)
|
||||||
logdata = dict({'username': username, 'dc': self.selected_dc})
|
logdata = dict({'username': username, 'dc': self.selected_dc})
|
||||||
log('E17', logdata)
|
log('E17', logdata)
|
||||||
|
|
||||||
return gpos
|
return gpos
|
||||||
|
|
||||||
def update_gpos(self, username):
|
def update_gpos(self, username):
|
||||||
gpos = self.get_gpos(username)
|
|
||||||
|
list_selected_dc = set()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if self.dc_site_servers:
|
||||||
|
self.selected_dc = self.dc_site_servers.pop()
|
||||||
|
|
||||||
|
self.all_servers = [dc for dc in self.all_servers if dc != self.selected_dc]
|
||||||
|
list_selected_dc.add(self.selected_dc)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log('D49')
|
gpos = self.get_gpos(username)
|
||||||
check_refresh_gpo_list(self.selected_dc, self.lp, self.creds, gpos)
|
|
||||||
log('D50')
|
except GetGPOListFail:
|
||||||
except Exception as exc:
|
self.selected_dc = self.pdc_emulator_server
|
||||||
|
gpos = self.get_gpos(username)
|
||||||
|
|
||||||
|
while list_selected_dc:
|
||||||
logdata = dict()
|
logdata = dict()
|
||||||
logdata['username'] = username
|
logdata['username'] = username
|
||||||
logdata['dc'] = self.selected_dc
|
logdata['dc'] = self.selected_dc
|
||||||
logdata['err'] = str(exc)
|
try:
|
||||||
log('F1')
|
log('D49', logdata)
|
||||||
|
check_refresh_gpo_list(self.selected_dc, self.lp, self.creds, gpos)
|
||||||
|
log('D50', logdata)
|
||||||
|
list_selected_dc.clear()
|
||||||
|
except NTSTATUSError as smb_exc:
|
||||||
|
logdata['smb_exc'] = str(smb_exc)
|
||||||
|
if not check_scroll_enabled():
|
||||||
|
if self.pdc_emulator_server and self.selected_dc != self.pdc_emulator_server:
|
||||||
|
self.selected_dc = self.pdc_emulator_server
|
||||||
|
logdata['action'] = 'Selected pdc'
|
||||||
|
logdata['pdc'] = self.selected_dc
|
||||||
|
log('W11', logdata)
|
||||||
|
else:
|
||||||
|
log('F1', logdata)
|
||||||
|
raise smb_exc
|
||||||
|
else:
|
||||||
|
if self.dc_site_servers:
|
||||||
|
self.selected_dc = self.dc_site_servers.pop()
|
||||||
|
elif self.all_servers:
|
||||||
|
self.selected_dc = self.all_servers.pop()
|
||||||
|
else:
|
||||||
|
self.selected_dc = self.pdc_emulator_server
|
||||||
|
|
||||||
|
|
||||||
|
if self.selected_dc not in list_selected_dc:
|
||||||
|
logdata['action'] = 'Search another dc'
|
||||||
|
logdata['another_dc'] = self.selected_dc
|
||||||
|
log('W11', logdata)
|
||||||
|
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
|
raise exc
|
||||||
return gpos
|
return gpos
|
||||||
|
|
||||||
|
|
||||||
def wbinfo_getsid(domain, user):
|
class SiteDomainScanner:
|
||||||
'''
|
def __init__(self, smbcreds, lp, dc):
|
||||||
Get SID using wbinfo
|
self.samdb = SamDB(url='ldap://{}'.format(dc), session_info=system_session(), credentials=smbcreds, lp=lp)
|
||||||
'''
|
self.pdc_emulator = self._search_pdc_emulator()
|
||||||
# This part works only on client
|
|
||||||
username = '{}\\{}'.format(domain.upper(), user)
|
|
||||||
sid = pysss_nss_idmap.getsidbyname(username)
|
|
||||||
|
|
||||||
if username in sid:
|
@staticmethod
|
||||||
return sid[username]['sid']
|
def _get_ldb_single_message_attr(ldb_message, attr_name, encoding='utf8'):
|
||||||
|
if attr_name in ldb_message:
|
||||||
|
return ldb_message[attr_name][0].decode(encoding)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
# This part works only on DC
|
@staticmethod
|
||||||
wbinfo_cmd = ['wbinfo', '-n', username]
|
def _get_ldb_single_result_attr(ldb_result, attr_name, encoding='utf8'):
|
||||||
output = subprocess.check_output(wbinfo_cmd)
|
if len(ldb_result) == 1 and attr_name in ldb_result[0]:
|
||||||
sid = output.split()[0].decode('utf-8')
|
return ldb_result[0][attr_name][0].decode(encoding)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
return sid
|
def _get_server_hostname(self, ds_service_name):
|
||||||
|
ds_service_name_dn = ldb.Dn(self.samdb, ds_service_name)
|
||||||
|
server_dn = ds_service_name_dn.parent()
|
||||||
|
res = self.samdb.search(server_dn, scope=ldb.SCOPE_BASE)
|
||||||
|
return self._get_ldb_single_result_attr(res, 'dNSHostName')
|
||||||
|
|
||||||
|
def _search_pdc_emulator(self):
|
||||||
|
res = self.samdb.search(self.samdb.domain_dn(), scope=ldb.SCOPE_BASE)
|
||||||
|
pdc_settings_object = self._get_ldb_single_result_attr(res, 'fSMORoleOwner')
|
||||||
|
return self._get_server_hostname(pdc_settings_object)
|
||||||
|
|
||||||
def get_local_sid_prefix():
|
def get_ip_addresses(self):
|
||||||
return "S-1-5-21-0-0-0"
|
interface_list = netifaces.interfaces()
|
||||||
|
addresses = []
|
||||||
|
for iface in interface_list:
|
||||||
|
address_entry = netifaces.ifaddresses(iface)
|
||||||
|
if netifaces.AF_INET in address_entry:
|
||||||
|
addresses.extend(ipaddress.ip_address(ipv4_address_entry['addr']) for ipv4_address_entry in address_entry[netifaces.AF_INET])
|
||||||
|
if netifaces.AF_INET6 in address_entry:
|
||||||
|
addresses.extend(ipaddress.ip_address(ipv6_address_entry['addr']) for ipv6_address_entry in address_entry[netifaces.AF_INET6])
|
||||||
|
return addresses
|
||||||
|
|
||||||
|
def get_ad_subnets_sites(self):
|
||||||
|
subnet_dn = ldb.Dn(self.samdb, "CN=Subnets,CN=Sites")
|
||||||
|
config_dn = self.samdb.get_config_basedn()
|
||||||
|
subnet_dn.add_base(config_dn)
|
||||||
|
res = self.samdb.search(subnet_dn, ldb.SCOPE_ONELEVEL, expression='objectClass=subnet', attrs=['cn', 'siteObject'])
|
||||||
|
subnets = {ipaddress.ip_network(self._get_ldb_single_message_attr(msg, 'cn')): self._get_ldb_single_message_attr(msg, 'siteObject') for msg in res}
|
||||||
|
return subnets
|
||||||
|
|
||||||
def get_sid(domain, username, is_machine = False):
|
def get_ad_site_servers(self, site):
|
||||||
'''
|
servers_dn = ldb.Dn(self.samdb, "CN=Servers")
|
||||||
Lookup SID not only using wbinfo or sssd but also using own cache
|
site_dn = ldb.Dn(self.samdb, site)
|
||||||
'''
|
servers_dn.add_base(site_dn)
|
||||||
sid = 'local-{}'.format(username)
|
res = self.samdb.search(servers_dn, ldb.SCOPE_ONELEVEL, expression='objectClass=server', attrs=['dNSHostName'])
|
||||||
|
servers = [self._get_ldb_single_message_attr(msg, 'dNSHostName') for msg in res]
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
|
||||||
# local user
|
def get_ad_all_servers(self):
|
||||||
if not domain:
|
sites_dn = ldb.Dn(self.samdb, "CN=Sites")
|
||||||
found_uid = 0
|
config_dn = self.samdb.get_config_basedn()
|
||||||
if not is_machine:
|
sites_dn.add_base(config_dn)
|
||||||
found_uid = pwd.getpwnam(username).pw_uid
|
res = self.samdb.search(sites_dn, ldb.SCOPE_SUBTREE, expression='objectClass=server', attrs=['dNSHostName'])
|
||||||
return '{}-{}'.format(get_local_sid_prefix(), found_uid)
|
servers = [self._get_ldb_single_message_attr(msg, 'dNSHostName') for msg in res]
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
|
||||||
# domain user
|
def check_ip_in_subnets(self, ip_addresses, subnets_sites):
|
||||||
|
return next((subnets_sites[subnet] for subnet in subnets_sites.keys()
|
||||||
|
if any(ip_address in subnet for ip_address in ip_addresses)), None)
|
||||||
|
|
||||||
|
def select_site_servers(self):
|
||||||
try:
|
try:
|
||||||
sid = wbinfo_getsid(domain, username)
|
ip_addresses = self.get_ip_addresses()
|
||||||
except:
|
subnets_sites = self.get_ad_subnets_sites()
|
||||||
logdata = dict({'sid': sid})
|
|
||||||
log('E16', logdata)
|
|
||||||
|
|
||||||
logdata = dict({'sid': sid})
|
our_site = self.check_ip_in_subnets(ip_addresses, subnets_sites)
|
||||||
log('D21', logdata)
|
|
||||||
|
|
||||||
return sid
|
servers = []
|
||||||
|
if our_site:
|
||||||
|
servers = self.get_ad_site_servers(our_site)
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
except Exception as e:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def select_all_servers(self):
|
||||||
|
try:
|
||||||
|
servers = self.get_ad_all_servers()
|
||||||
|
random.shuffle(servers)
|
||||||
|
return servers
|
||||||
|
except Exception as e:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def select_pdc_emulator_server(self):
|
||||||
|
return self.pdc_emulator
|
||||||
|
|
||||||
def expand_windows_var(text, username=None):
|
def expand_windows_var(text, username=None):
|
||||||
'''
|
'''
|
||||||
@@ -191,20 +298,26 @@ def expand_windows_var(text, username=None):
|
|||||||
'''
|
'''
|
||||||
variables = dict()
|
variables = dict()
|
||||||
variables['HOME'] = '/etc/skel'
|
variables['HOME'] = '/etc/skel'
|
||||||
|
variables['HOMEPATH'] = '/etc/skel'
|
||||||
|
variables['HOMEDRIVE'] = '/'
|
||||||
variables['SystemRoot'] = '/'
|
variables['SystemRoot'] = '/'
|
||||||
variables['StartMenuDir'] = '/usr/share/applications'
|
variables['StartMenuDir'] = '/usr/share/applications'
|
||||||
variables['SystemDrive'] = '/'
|
variables['SystemDrive'] = '/'
|
||||||
variables['DesktopDir'] = xdg_get_desktop(username, variables['HOME'])
|
variables['DesktopDir'] = xdg_get_desktop(username, variables['HOME'])
|
||||||
|
|
||||||
if username:
|
if username:
|
||||||
|
variables['LogonUser'] = username
|
||||||
variables['HOME'] = get_homedir(username)
|
variables['HOME'] = get_homedir(username)
|
||||||
|
variables['HOMEPATH'] = get_homedir(username)
|
||||||
|
|
||||||
variables['StartMenuDir'] = os.path.join(
|
variables['StartMenuDir'] = os.path.join(
|
||||||
variables['HOME'], '.local', 'share', 'applications')
|
variables['HOME'], '.local', 'share', 'applications')
|
||||||
|
|
||||||
result = text
|
result = text
|
||||||
for var in variables.keys():
|
for var in variables.keys():
|
||||||
result = result.replace('%{}%'.format(var), variables[var])
|
result = result.replace('%{}%'.format(var),
|
||||||
|
variables[var] if variables[var][-1] == '/'
|
||||||
|
else variables[var] +'/')
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -220,3 +333,11 @@ def transform_windows_path(text):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def check_scroll_enabled():
|
||||||
|
storage = registry_factory()
|
||||||
|
enable_scroll = '/Software/BaseALT/Policies/GPUpdate/ScrollSysvolDC'
|
||||||
|
if storage.get_key_value(enable_scroll):
|
||||||
|
data = storage.get_hklm_entry(enable_scroll).data
|
||||||
|
return bool(int(data))
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user