1
0
mirror of https://github.com/altlinux/gpupdate.git synced 2025-12-01 12:23:52 +03:00

Compare commits

..

11 Commits

Author SHA1 Message Date
Valery Sinelnikov
54608e3f80 0.14.0-alt1
- Added:
  Comprehensive plugin development documentation in English and Russian
  GDM backup and restore functionality
  FreeIPA backend configuration and authentication
  libcng-dpapi dependency for LAPS support
  systemd-logind dependency to gpupdate service
  DMApplier plugin for display manager configuration
- Changed:
  Refactored plugin system infrastructure
  Optimized FreeIPA backend and configuration
- Fixed:
  GPO downloading and error handling
  Firewall reset command path safety check
  LAPS applier encryption mechanism
  Drive letters from GPO application instead of labels
  GPO processing with trusted domain support
  Shortcuts created via GPO are now marked as trusted (closes:56019)
2025-11-27 14:39:04 +04:00
Valery Sinelnikov
4569fe907c Update LightDM greeter configuration mappings
- Remove unsupported lightdm-webkit2-greeter and lightdm-unity-greeter
- Fix slick-greeter section name to 'Greeter' (capitalized)
- Correct lightdm-kde-greeter section and background key names
2025-11-27 13:43:40 +04:00
Valery Sinelnikov
1f8ca1e436 Improve GDM background handling and add fallback mechanism
- Add comprehensive background pattern matching for GDM CSS files
- Implement fallback configuration when gresource modification fails
- Add detailed logging for background modification process
- Fix trailing whitespace and clean up code formatting
- Update Russian translations for new log messages
2025-11-27 10:50:19 +04:00
Valery Sinelnikov
cabdc82e8b Fix plugin list parsing in plugin manager
- Apply string_to_literal_eval to PluginsList configuration
- Ensure proper list parsing from dconf settings
2025-11-26 10:32:57 +04:00
Valery Sinelnikov
2ebf86b19f Fix backup mode handling in DM applier 2025-11-25 14:15:44 +04:00
Valery Sinelnikov
1d45155436 Fix typo in Russian translation string
- Correct missing closing quote in trust attribute message
2025-11-25 12:43:13 +04:00
Valentin Sokolov
1a245a259f Added new exception for Yandex Browser 138 2025-11-24 17:21:18 +04:00
Valentin Sokolov
54303abebb Added new exception for Chromium 141 2025-11-24 17:21:12 +04:00
Valentin Sokolov
784098b3a2 Added setting of trust attribute for shortcuts 2025-11-24 17:21:05 +04:00
Valery Sinelnikov
c62aae9568 Check AllowTrustUserPolicy and AllowX-ForestPolicy-and-RUP settings 2025-11-24 16:32:55 +04:00
Valery Sinelnikov
2bcdb33171 Fix LAPS applier administrator account validation
- Strip whitespace from AdministratorAccountName configuration
- Simplify user existence check logic
- Ensure default 'root' account is used when name is empty
2025-11-21 18:41:56 +04:00
11 changed files with 232 additions and 60 deletions

View File

@@ -95,7 +95,8 @@ class chromium_applier(applier_frontend):
'''
List of keys resulting from parsing chrome.admx with parsing_chrom_admx_intvalues.py
'''
valuename_typeint = (['DefaultClipboardSetting',
valuename_typeint = (['CACertificateManagementAllowed',
'DefaultClipboardSetting',
'DefaultCookiesSetting',
'DefaultFileSystemReadGuardSetting',
'DefaultFileSystemWriteGuardSetting',
@@ -103,27 +104,33 @@ class chromium_applier(applier_frontend):
'DefaultImagesSetting',
'DefaultInsecureContentSetting',
'DefaultJavaScriptJitSetting',
'DefaultJavaScriptOptimizerSetting',
'DefaultJavaScriptSetting',
'DefaultLocalFontsSetting',
'DefaultNotificationsSetting',
'DefaultPopupsSetting',
'DefaultSensorsSetting',
'DefaultSerialGuardSetting',
'DefaultThirdPartyStoragePartitioningSetting',
'DefaultWebBluetoothGuardSetting',
'DefaultWebHidGuardSetting',
'DefaultWebUsbGuardSetting',
'DefaultWindowManagementSetting',
'DefaultMediaStreamSetting',
'DefaultThirdPartyStoragePartitioningSetting',
'DefaultWindowPlacementSetting',
'UserAgentReduction',
'ProxyServerMode',
'ExtensionManifestV2Availability',
'ExtensionDeveloperModeSettings',
'ExtensionUnpublishedAvailability',
'AIModeSettings',
'AutofillPredictionSettings',
'CreateThemesSettings',
'DevToolsGenAiSettings',
'GeminiSettings',
'GenAILocalFoundationalModelSettings',
'HelpMeWriteSettings',
'TabOrganizerSettings',
'HistorySearchSettings',
'TabCompareSettings',
'BrowserSwitcherParsingMode',
'CloudAPAuthEnabled',
'AdsSettingForIntrusiveAdsSites',
@@ -133,6 +140,8 @@ class chromium_applier(applier_frontend):
'ChromeVariations',
'DeveloperToolsAvailability',
'DownloadRestrictions',
'DynamicCodeSettings',
'EnterpriseProfileBadgeToolbarSettings',
'ForceYouTubeRestrict',
'HeadlessMode',
'IncognitoModeAvailability',
@@ -144,11 +153,10 @@ class chromium_applier(applier_frontend):
'ProfileReauthPrompt',
'RelaunchNotification',
'SafeSitesFilterBehavior',
'ToolbarAvatarLabelSettings',
'UserAgentReduction',
'BatterySaverModeAvailability_recommended',
'DownloadRestrictions_recommended',
'NetworkPredictionOptions_recommended',
'AutomatedPasswordChangeSettings',
'PrintPostScriptMode',
'PrintRasterizationMode',
'ChromeFrameRendererSettings',
@@ -156,7 +164,10 @@ class chromium_applier(applier_frontend):
'DefaultKeygenSetting',
'DefaultPluginsSetting',
'LegacySameSiteCookieBehaviorEnabled',
'ExtensionManifestV2Availability',
'TabOrganizerSettings',
'ForceMajorVersionToMinorPositionInUserAgent',
'ToolbarAvatarLabelSettings',
'PasswordProtectionWarningTrigger',
'SafeBrowsingProtectionLevel',
'SafeBrowsingProtectionLevel_recommended',

View File

@@ -148,8 +148,8 @@ class laps_applier(applier_frontend):
self.password_age_days = self.config.get('PasswordAgeDays', 30)
self.post_authentication_actions = self.config.get('PostAuthenticationActions', 3)
self.post_authentication_reset_delay = self.config.get('PostAuthenticationResetDelay', 24)
name = self.config.get('AdministratorAccountName', 'root')
if name and check_local_user_exists(name):
name = self.config.get('AdministratorAccountName', '').strip() or 'root'
if check_local_user_exists(name):
self.target_user = name
else:
log('W36')

View File

@@ -17,11 +17,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import subprocess
import shutil
from gpt.shortcuts import get_ttype, shortcut
from util.logging import log
from util.util import get_homedir, homedir_exists, string_to_literal_eval
from util.windows import expand_windows_var
from pathlib import Path
from .applier_frontend import applier_frontend, check_enabled
@@ -84,6 +86,23 @@ def apply_shortcut(shortcut, username=None):
log('D106', logdata)
shortcut.apply_desktop(dest_abspath)
try:
if getattr(shortcut, 'action', None) in ('C', 'U', 'R'):
if Path(dest_abspath).exists() and shutil.which('gio'):
user_home = get_homedir(username) if username else None
if username and user_home and dest_abspath.startswith(user_home.rstrip('/') + '/'):
command = ['gio', 'set', dest_abspath, 'metadata::trusted', 'true']
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False)
gio_out = (result.stderr or result.stdout or '').strip()
logdata = {'command': command, 'gio_out': gio_out}
if result.returncode != 0:
log('D238', logdata)
else:
log('D239', logdata)
except Exception as e:
log('E81', logdata)
class shortcut_applier(applier_frontend):
__module_name = 'ShortcutsApplier'
__module_experimental = False

View File

@@ -96,13 +96,19 @@ class yandex_browser_applier(applier_frontend):
valuename_typeint = (['DefaultPageSaveSettings',
'DefaultUploadSetting',
'YandexAutoLaunchMode',
'SafeSitesFilterBehavior',
'RestoreOnStartup',
'RestoreOnStartup_recommended',
'DefaultClipboardSetting',
'DefaultCookiesSetting',
'DefaultFileSystemReadGuardSetting',
'DefaultFileSystemWriteGuardSetting',
'DefaultGeolocationSetting',
'DefaultImagesSetting',
'DefaultJavaScriptJitSetting',
'DefaultJavaScriptSetting',
'DefaultLocalFontsSetting',
'DefaultNotificationsSetting',
'DefaultPopupsSetting',
'DefaultSensorsSetting',
'DefaultSerialGuardSetting',
@@ -110,14 +116,10 @@ class yandex_browser_applier(applier_frontend):
'DefaultWebHidGuardSetting',
'DefaultWebUsbGuardSetting',
'DefaultWindowManagementSetting',
'SafeSitesFilterBehavior',
'YandexUserFeedbackMode',
'TurboSettings',
'SidePanelMode',
'RestoreOnStartup',
'RestoreOnStartup_recommended',
'BrowserSwitcherParsingMode',
'DefaultNotificationsSetting',
'YandexPowerSavingMode',
'ChromeVariations',
'DeveloperToolsAvailability',
@@ -125,16 +127,12 @@ class yandex_browser_applier(applier_frontend):
'NetworkPredictionOptions',
'DownloadRestrictions_recommended',
'NetworkPredictionOptions_recommended',
'DefaultCookiesSetting',
'DefaultGeolocationSetting',
'PasswordProtectionWarningTrigger',
'IncognitoModeAvailability',
'DefaultPrintingSettings',
'DefaultPluginsSetting',
'DefaultInsecureContentSetting',
'PasswordProtectionWarningTrigger',
'SafeBrowsingProtectionLevel',
'SafeBrowsingProtectionLevel_recommended',
'DiskCacheSize'])
'SafeBrowsingProtectionLevel_recommended'])
return valuename_typeint

View File

@@ -86,7 +86,33 @@ class DMApplier(FrontendPlugin):
31: "Display manager configuration details",
32: "Removed empty configuration value",
33: "GDM background modification details",
34: "GDM backup operation details"
34: "GDM backup operation details",
35: "GDM CSS file not found",
36: "GDM background pattern analysis",
37: "GDM background added to CSS"
},
'e': {
20: "Configuration file path is invalid or inaccessible",
21: "Failed to generate display manager configuration",
22: "Unknown display manager config directory",
23: "Failed to generate display manager configuration",
24: "Display Manager Applier execution failed",
25: "GDM theme gresource not found",
26: "Failed to extract GDM gresource",
27: "Failed to modify GDM background",
28: "Failed to recompile GDM gresource",
29: "Failed to restore GDM backup",
30: "Failed to add GDM background"
},
'i': {
1: "Display Manager Applier initialized",
2: "Display manager configuration generated successfully",
3: "Display Manager Applier execution started",
4: "Display manager configuration completed successfully",
5: "LightDM greeter configuration generated successfully",
6: "GDM theme modified successfully",
7: "GDM backup restored successfully",
8: "GDM fallback configuration generated successfully"
}
},
# locale_dir will be set by plugin_manager during plugin loading
@@ -191,7 +217,7 @@ class DMApplier(FrontendPlugin):
gresource_path = self._find_gnome_shell_gresource()
if not gresource_path:
self.log("E25", {"path": "gnome-shell-theme.gresource"})
return None
return self._generate_gdm_fallback(path, background_path)
# Create backup if it doesn't exist
backup_path = gresource_path + '.backup'
@@ -202,13 +228,13 @@ class DMApplier(FrontendPlugin):
# Extract gresource to temporary directory
temp_dir = self._extract_gresource(gresource_path)
if not temp_dir:
return None
return self._generate_gdm_fallback(path, background_path)
# Modify background in theme files
modified = self._modify_gdm_background(temp_dir, background_path)
if not modified:
shutil.rmtree(temp_dir)
return None
return self._generate_gdm_fallback(path, background_path)
# Recompile gresource
success = self._recompile_gresource(temp_dir, gresource_path)
@@ -221,11 +247,11 @@ class DMApplier(FrontendPlugin):
return True
else:
self.log("E28", {"path": gresource_path})
return None
return self._generate_gdm_fallback(path, background_path)
except Exception as exc:
self.log("E21", {"path": "gnome-shell-theme.gresource", "error": str(exc), "dm": "gdm"})
return None
return self._generate_gdm_fallback(path, background_path)
def _find_gnome_shell_gresource(self):
"""Find gnome-shell-theme.gresource file"""
@@ -336,17 +362,26 @@ class DMApplier(FrontendPlugin):
for css_filename in target_css_files:
css_file = os.path.join(temp_dir, css_filename)
if not os.path.exists(css_file):
self.log("D35", {"file": css_filename, "action": "file_not_found"})
continue
with open(css_file, 'r') as f:
content = f.read()
# Look for background-related CSS rules
# Look for background-related CSS rules in #lockDialogGroup
patterns = [
# Handle only #lockDialogGroup background with file://// (4 slashes)
# Handle #lockDialogGroup background with file://// (4 slashes)
r'(#lockDialogGroup\s*{[^}]*background:\s*[^;]*)url\(file:////[^)]+\)',
# Handle only #lockDialogGroup background with file:/// (3 slashes)
r'(#lockDialogGroup\s*{[^}]*background:\s*[^;]*)url\(file:///[^)]+\)'
# Handle #lockDialogGroup background with file:/// (3 slashes)
r'(#lockDialogGroup\s*{[^}]*background:\s*[^;]*)url\(file:///[^)]+\)',
# Handle #lockDialogGroup background with resource:///
r'(#lockDialogGroup\s*{[^}]*background:\s*[^;]*)url\(resource:///[^)]+\)',
# Handle #lockDialogGroup background-image with file:////
r'(#lockDialogGroup\s*{[^}]*background-image:\s*[^;]*)url\(file:////[^)]+\)',
# Handle #lockDialogGroup background-image with file:///
r'(#lockDialogGroup\s*{[^}]*background-image:\s*[^;]*)url\(file:///[^)]+\)',
# Handle #lockDialogGroup background-image with resource:///
r'(#lockDialogGroup\s*{[^}]*background-image:\s*[^;]*)url\(resource:///[^)]+\)'
]
for pattern in patterns:
@@ -363,12 +398,52 @@ class DMApplier(FrontendPlugin):
self.log("D33", {"file": css_filename, "background": background_path})
break
if not modified:
# Log what we found in the file for debugging
if "#lockDialogGroup" in content:
self.log("D36", {
"file": css_filename,
"lockDialogGroup_found": True,
"background_patterns": len(re.findall(r'background.*url', content)),
"background_image_patterns": len(re.findall(r'background-image.*url', content))
})
# Try to add background if #lockDialogGroup exists but has no background
modified = self._add_gdm_background(css_file, content, background_path)
else:
self.log("D36", {"file": css_filename, "lockDialogGroup_found": False})
return modified
except Exception as exc:
self.log("E27", {"path": temp_dir, "error": str(exc)})
return False
def _add_gdm_background(self, css_file, content, background_path):
"""Add background to #lockDialogGroup if it doesn't exist"""
try:
# Pattern to find #lockDialogGroup block
pattern = r'(#lockDialogGroup\s*\{[^}]*)(\})'
def add_background(match):
opening = match.group(1)
closing = match.group(2)
# Add background property before closing brace
return f'{opening}\n background: url(file:///{background_path});{closing}'
new_content = re.sub(pattern, add_background, content)
if new_content != content:
with open(css_file, 'w') as f:
f.write(new_content)
self.log("D37", {"file": os.path.basename(css_file), "action": "background_added", "background": background_path})
return True
return False
except Exception as exc:
self.log("E30", {"file": css_file, "error": str(exc)})
return False
def _recompile_gresource(self, temp_dir, gresource_path):
"""Recompile gresource from modified files using temporary XML"""
try:
@@ -397,6 +472,28 @@ class DMApplier(FrontendPlugin):
self.log("E28", {"path": gresource_path, "error": str(exc)})
return False
def _generate_gdm_fallback(self, path, background_path):
"""Generate fallback GDM configuration file when gresource modification fails"""
try:
conf = self._prepare_conf(path)
if conf is None:
return None
# Set background in daemon section
daemon = conf.setdefault("daemon", {})
daemon["Background"] = background_path
# Clean up empty values
self._clean_empty_values(daemon)
conf.write()
self.log("I8", {"path": path, "background": background_path, "method": "fallback"})
return conf
except Exception as exc:
self.log("E21", {"path": path, "error": str(exc), "dm": "gdm"})
return None
def generate_sddm(self, path):
conf = self._prepare_conf(path)
if conf is None:
@@ -496,32 +593,21 @@ class DMApplier(FrontendPlugin):
"background_key": "background",
"theme_key": "theme-name"
},
"lightdm-webkit2-greeter": {
"config_path": "/etc/lightdm/lightdm-webkit2-greeter.conf",
"section": "greeter",
"background_key": "background",
"theme_key": "theme"
},
"lightdm-unity-greeter": {
"config_path": "/etc/lightdm/lightdm-unity-greeter.conf",
"section": "greeter",
"background_key": "background",
"theme_key": "theme-name"
},
"lightdm-slick-greeter": {
"config_path": "/etc/lightdm/lightdm-slick-greeter.conf",
"section": "greeter",
"slick-greeter": {
"config_path": "/etc/lightdm/slick-greeter.conf",
"section": "Greeter",
"background_key": "background",
"theme_key": "theme-name"
},
"lightdm-kde-greeter": {
"config_path": "/etc/lightdm/lightdm-kde-greeter.conf",
"section": "greeter",
"background_key": "background",
"section": "lightdm_theme_userbar",
"background_key": "Background",
"theme_key": "theme"
}
}
config_info = greeter_configs.get(greeter_name)
if not config_info:
self.log("E22", {"greeter": greeter_name}) # Unknown greeter type
@@ -707,6 +793,8 @@ class DMApplier(FrontendPlugin):
if result:
self.log("I4", {"dm": target_dm, "config_dir": config_dir})
return True
elif self.backup:
return False
else:
self.log("E23", {"dm": target_dm, "config_dir": config_dir})
return False

View File

@@ -91,3 +91,18 @@ msgstr "Детали изменения фона GDM"
msgid "GDM backup operation details"
msgstr "Детали операции резервного копирования GDM"
msgid "GDM CSS file not found"
msgstr "CSS файл GDM не найден"
msgid "GDM background pattern analysis"
msgstr "Анализ паттернов фона GDM"
msgid "GDM background added to CSS"
msgstr "Фон GDM добавлен в CSS"
msgid "Failed to add GDM background"
msgstr "Не удалось добавить фон GDM"
msgid "GDM fallback configuration generated successfully"
msgstr "Резервная конфигурация GDM успешно сгенерирована"

View File

@@ -285,6 +285,9 @@ msgstr "Невозможно инициализировать бэкэнд Freei
msgid "FreeIPA API Error"
msgstr "Ошибка API FreeIPA"
msgid "Error setting trust attribute for shortcut"
msgstr "Ошибка установки атрибута доверия для ярлыка"
# Error_end
# Debug
@@ -984,6 +987,12 @@ msgstr "Запуск плагина"
msgid "Failed to load cached versions"
msgstr "Не удалось загрузить кешированные версии"
msgid "The trust attribute is not supported"
msgstr "Атрибут доверия не поддерживается"
msgid "Setting the trust attribute for a shortcut"
msgstr "Установка атрибута доверия для ярлыка"
# Debug_end
# Warning

View File

@@ -119,6 +119,7 @@ def error_code(code):
error_ids[78] = 'Kerberos info unavailable; cannot construct DPAPI parameters'
error_ids[79] = 'Unable to initialize Freeipa backend'
error_ids[80] = 'FreeIPA API error'
error_ids[81] = 'Error setting trust attribute for shortcut'
return error_ids.get(code, 'Unknown error code')
def debug_code(code):
@@ -359,6 +360,8 @@ def debug_code(code):
debug_ids[235] = 'User not found in passwd database'
debug_ids[236] = 'Plugin is disabled'
debug_ids[237] = 'Failed to load cached versions'
debug_ids[238] = 'The trust attribute is not supported'
debug_ids[239] = 'Setting the trust attribute for a shortcut'
return debug_ids.get(code, 'Unknown debug code')

View File

@@ -55,7 +55,7 @@ class plugin_manager:
dict_gpupdate_key = string_to_literal_eval(
self.dict_dconf_db.get('Software/BaseALT/Policies/GPUpdate',{}))
self.plugins_enable = dict_gpupdate_key.get('Plugins')
self.plugins_list = dict_gpupdate_key.get('PluginsList')
self.plugins_list = string_to_literal_eval(dict_gpupdate_key.get('PluginsList'))
def check_enabled_plugin(self, plugin_name):
"""Check if the plugin is enabled"""

View File

@@ -121,18 +121,26 @@ class smbcreds (smbopts):
dconf_dict = self.get_dconf_dict(username)
if not self.is_machine and Dconf_registry.get_info('trust'):
try:
info = with_privileges(username, get_kerberos_domain_info)
pdc_dns_name = info.get('pdc_dns_name')
if pdc_dns_name:
Dconf_registry.set_info('pdc_dns_name', pdc_dns_name)
gpos = get_gpo_list(pdc_dns_name, self.creds, self.lp, username)
logdata = {'username': username}
log('I12', logdata)
self.process_gpos(gpos, username, dconf_dict)
return gpos
except Exception as exc:
pass
dconf_dict_machine = self.get_dconf_dict()
allow_trust_user_policy = dconf_dict_machine.get(
'Software/BaseALT/Policies/GPUpdate', {}).get(
'AllowTrustUserPolicy', False)
allow_trust_user_policy_win = dconf_dict_machine.get(
'Software\Policies\Microsoft\Windows\System', {}).get(
'AllowX-ForestPolicy-and-RUP', False)
if allow_trust_user_policy or allow_trust_user_policy_win:
try:
info = with_privileges(username, get_kerberos_domain_info)
pdc_dns_name = info.get('pdc_dns_name')
if pdc_dns_name:
Dconf_registry.set_info('pdc_dns_name', pdc_dns_name)
gpos = get_gpo_list(pdc_dns_name, self.creds, self.lp, username)
logdata = {'username': username}
log('I12', logdata)
self.process_gpos(gpos, username, dconf_dict)
return gpos
except Exception as exc:
return []
try:
log('D48')
@@ -157,7 +165,9 @@ class smbcreds (smbopts):
Retrieve dconf dictionary either for the machine itself
or for a specific user depending on the given username
"""
if Dconf_registry.get_info('machine_name') == username:
if username is None:
return Dconf_registry.get_dictionary_from_dconf_file_db(save_dconf_db=True)
elif Dconf_registry.get_info('machine_name') == username:
dconf_dict = Dconf_registry.get_dictionary_from_dconf_file_db(save_dconf_db=True)
self.is_machine = True
else:

View File

@@ -39,7 +39,7 @@
%add_python3_req_skip frontend.appliers.ini_file
Name: gpupdate
Version: 0.13.4
Version: 0.14.0
Release: alt1
Summary: GPT applier
@@ -211,6 +211,25 @@ fi
%exclude %python3_sitelibdir/gpoa/test
%changelog
* Thu Nov 27 2025 Valery Sinelnikov <greh@altlinux.org> 0.14.0-alt1
- Added:
Comprehensive plugin development documentation in English and Russian
GDM backup and restore functionality
FreeIPA backend configuration and authentication
libcng-dpapi dependency for LAPS support
systemd-logind dependency to gpupdate service
DMApplier plugin for display manager configuration
- Changed:
Refactored plugin system infrastructure
Optimized FreeIPA backend and configuration
- Fixed:
GPO downloading and error handling
Firewall reset command path safety check
LAPS applier encryption mechanism
Drive letters from GPO application instead of labels
GPO processing with trusted domain support
Shortcuts created via GPO are now marked as trusted (closes:56019)
* Mon Aug 25 2025 Valery Sinelnikov <greh@altlinux.org> 0.13.4-alt1
- Added:
Production-ready modules: CUPS, file management, INI config (default),