diff --git a/API.txt b/API.txt index 6c80028bf..35d95a709 100644 --- a/API.txt +++ b/API.txt @@ -1076,11 +1076,13 @@ args: 0,1,1 option: Str('version?') output: Output('result') command: config_mod/1 -args: 0,29,3 +args: 0,32,3 +option: Flag('add_sids?', autofill=True, default=False) option: Str('addattr*', cli_name='addattr') option: Flag('all', autofill=True, cli_name='all', default=False) option: Str('ca_renewal_master_server?', autofill=False) option: Str('delattr*', cli_name='delattr') +option: Flag('enable_sid?', autofill=True, default=False) option: StrEnum('ipaconfigstring*', autofill=False, cli_name='ipaconfigstring', values=[u'AllowNThash', u'KDC:Disable Last Success', u'KDC:Disable Lockout', u'KDC:Disable Default Preauth for SPNs']) option: Str('ipadefaultemaildomain?', autofill=False, cli_name='emaildomain') option: Str('ipadefaultloginshell?', autofill=False, cli_name='defaultshell') @@ -1102,6 +1104,7 @@ option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', v option: Bool('ipauserdefaultsubordinateid?', autofill=False, cli_name='user_default_subid') option: Str('ipauserobjectclasses*', autofill=False, cli_name='userobjectclasses') option: IA5Str('ipausersearchfields?', autofill=False, cli_name='usersearch') +option: Str('netbios_name?', autofill=False) option: Flag('raw', autofill=True, cli_name='raw', default=False) option: Flag('rights', autofill=True, default=False) option: Str('setattr*', cli_name='setattr') diff --git a/VERSION.m4 b/VERSION.m4 index c43a7b427..fc206c965 100644 --- a/VERSION.m4 +++ b/VERSION.m4 @@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000) # # ######################################################## define(IPA_API_VERSION_MAJOR, 2) -# Last change: add subordinate id feature -define(IPA_API_VERSION_MINOR, 243) +# Last change: add enable_sid to config +define(IPA_API_VERSION_MINOR, 245) ######################################################## diff --git a/freeipa.spec.in b/freeipa.spec.in index 07ce6b292..952c1ad18 100755 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -1378,6 +1378,7 @@ fi %dir %{_libexecdir}/ipa/oddjob %attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.conncheck %attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.trust-enable-agent +%attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.config-enable-sid %config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freeipa.server.conf %config(noreplace) %{_sysconfdir}/oddjobd.conf.d/ipa-server.conf %dir %{_libexecdir}/ipa/certmonger diff --git a/install/oddjob/Makefile.am b/install/oddjob/Makefile.am index 7aeb406a2..bd968eaba 100644 --- a/install/oddjob/Makefile.am +++ b/install/oddjob/Makefile.am @@ -7,6 +7,7 @@ dbusconfdir = $(sysconfdir)/dbus-1/system.d dist_noinst_DATA = \ com.redhat.idm.trust-fetch-domains.in \ org.freeipa.server.trust-enable-agent.in \ + org.freeipa.server.config-enable-sid.in \ etc/oddjobd.conf.d/oddjobd-ipa-trust.conf.in \ etc/oddjobd.conf.d/ipa-server.conf.in \ $(NULL) @@ -18,6 +19,7 @@ dist_oddjob_SCRIPTS = \ nodist_oddjob_SCRIPTS = \ com.redhat.idm.trust-fetch-domains \ org.freeipa.server.trust-enable-agent \ + org.freeipa.server.config-enable-sid \ $(NULL) diff --git a/install/oddjob/etc/oddjobd.conf.d/ipa-server.conf.in b/install/oddjob/etc/oddjobd.conf.d/ipa-server.conf.in index 640b510aa..a79640f88 100644 --- a/install/oddjob/etc/oddjobd.conf.d/ipa-server.conf.in +++ b/install/oddjob/etc/oddjobd.conf.d/ipa-server.conf.in @@ -17,6 +17,12 @@ prepend_user_name="no" argument_passing_method="cmdline"/> + + + diff --git a/install/oddjob/org.freeipa.server.config-enable-sid.in b/install/oddjob/org.freeipa.server.config-enable-sid.in new file mode 100644 index 000000000..ff5fb49e2 --- /dev/null +++ b/install/oddjob/org.freeipa.server.config-enable-sid.in @@ -0,0 +1,76 @@ +#!/usr/bin/python3 +# +# Copyright (C) 2021 FreeIPA Contributors see COPYING for license +# + +import logging + +from ipalib import api +from ipalib.install import sysrestore +from ipaplatform.paths import paths +from ipapython import ipaldap +from ipapython.admintool import AdminTool +from ipaserver.install import adtrust, adtrustinstance + +logger = logging.getLogger(__name__) + +class IPAConfigEnableSid(AdminTool): + command_name = "ipa-enable-sid" + log_file_name = paths.IPASERVER_ENABLESID_LOG + usage = "%prog" + description = "Enable SID generation" + + @classmethod + def add_options(cls, parser): + super(IPAConfigEnableSid, cls).add_options(parser) + + parser.add_option( + "--add-sids", + dest="add_sids", default=False, action="store_true", + help="Add SIDs for existing users and groups as the final step" + ) + + parser.add_option( + "--netbios-name", + dest="netbios_name", default=None, + help="NetBIOS name of the IPA domain" + ) + + parser.add_option( + "--reset-netbios-name", + dest="reset_netbios_name", default=False, action="store_true", + help="Force reset of the existing NetBIOS name" + ) + + + def validate_options(self): + super(IPAConfigEnableSid, self).validate_options(needs_root=True) + + def run(self): + api.bootstrap(in_server=True, confdir=paths.ETC_IPA) + api.finalize() + + try: + api.Backend.ldap2.connect() + fstore = sysrestore.FileStore(paths.SYSRESTORE) + + smb = adtrustinstance.ADTRUSTInstance(fstore, False) + smb.realm = api.env.realm + smb.autobind = ipaldap.AUTOBIND_ENABLED + smb.setup(api.env.host, api.env.realm, + self.options.netbios_name, + self.options.reset_netbios_name, + adtrust.DEFAULT_PRIMARY_RID_BASE, + adtrust.DEFAULT_SECONDARY_RID_BASE, + self.options.add_sids, + enable_compat=False) + smb.find_local_id_range() + smb.create_instance() + + finally: + if api.Backend.ldap2.isconnected(): + api.Backend.ldap2.disconnect() + + return 0 + +IPAConfigEnableSid.run_cli() diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index de217d9ef..42a47f1df 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -364,6 +364,7 @@ class BasePathNamespace: IPAREPLICA_CONNCHECK_LOG = "/var/log/ipareplica-conncheck.log" IPAREPLICA_INSTALL_LOG = "/var/log/ipareplica-install.log" IPARESTORE_LOG = "/var/log/iparestore.log" + IPASERVER_ENABLESID_LOG = "/var/log/ipaserver-enable-sid.log" IPASERVER_INSTALL_LOG = "/var/log/ipaserver-install.log" IPASERVER_ADTRUST_INSTALL_LOG = "/var/log/ipaserver-adtrust-install.log" IPASERVER_DNS_INSTALL_LOG = "/var/log/ipaserver-dns-install.log" diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py index a53760340..b7cd8f7d8 100644 --- a/ipaserver/install/adtrustinstance.py +++ b/ipaserver/install/adtrustinstance.py @@ -335,6 +335,8 @@ class ADTRUSTInstance(service.Service): # _ldap_mod does not return useful error codes, so we must check again # if the fallback group was created properly. try: + # Remove entry from cache otherwise get_entry won't find it + api.Backend.ldap2.remove_cache_entry(fb_group_dn) api.Backend.ldap2.get_entry(fb_group_dn) except errors.NotFound: self.print_msg("Failed to add fallback group.") diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py index ad3dd6a7f..eae401fc3 100644 --- a/ipaserver/plugins/config.py +++ b/ipaserver/plugins/config.py @@ -17,12 +17,16 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import dbus +import dbus.mainloop.glib +import logging from ipalib import api -from ipalib import Bool, Int, Str, IA5Str, StrEnum, DNParam +from ipalib import Bool, Int, Str, IA5Str, StrEnum, DNParam, Flag from ipalib import errors from ipalib.constants import MAXHOSTNAMELEN from ipalib.plugable import Registry +from ipalib.request import context from ipalib.util import validate_domain_name from .baseldap import ( LDAPObject, @@ -30,7 +34,12 @@ from .baseldap import ( LDAPRetrieve) from .selinuxusermap import validate_selinuxuser from ipalib import _ +from ipapython.admintool import ScriptError from ipapython.dn import DN +from ipaserver.plugins.privilege import principal_has_privilege +from ipaserver.install.adtrust import set_and_check_netbios_name + +logger = logging.getLogger(__name__) # 389-ds attributes that should be skipped in attribute checks OPERATIONAL_ATTRIBUTES = ('nsaccountlock', 'member', 'memberof', @@ -340,6 +349,24 @@ class config(LDAPObject): doc=_('DNSec key master'), flags={'virtual_attribute', 'no_create', 'no_update'} ), + Flag( + 'enable_sid?', + label=_('Setup SID configuration'), + doc=_('New users and groups automatically get a SID assigned'), + flags={'virtual_attribute', 'no_create'} + ), + Flag( + 'add_sids?', + label=_('Add SIDs'), + doc=_('Add SIDs for existing users and groups'), + flags={'virtual_attribute', 'no_create'} + ), + Str( + 'netbios_name?', + label=_('NetBIOS name of the IPA domain'), + doc=_('NetBIOS name of the IPA domain'), + flags={'virtual_attribute', 'no_create'} + ), ) def get_dn(self, *keys, **kwargs): @@ -479,6 +506,60 @@ class config(LDAPObject): class config_mod(LDAPUpdate): __doc__ = _('Modify configuration options.') + def _enable_sid(self, ldap, options): + # the user must have the Replication Administrators privilege + privilege = 'Replication Administrators' + if not principal_has_privilege(self.api, context.principal, privilege): + raise errors.ACIError( + info=_("not allowed to enable SID generation")) + + # NetBIOS name is either taken from options or generated + try: + netbios_name, reset_netbios_name = set_and_check_netbios_name( + options.get('netbios_name', None), True, self.api) + except ScriptError: + raise errors.ValidationError(name="NetBIOS name", + error=_('Up to 15 characters and only uppercase ASCII letters' + ', digits and dashes are allowed. Empty string is ' + 'not allowed.')) + + _ret = 0 + _stdout = '' + _stderr = '' + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + method_options = [] + if options.get('add_sids', False): + method_options.extend(["--add-sids"]) + method_options.extend(["--netbios-name", netbios_name]) + if reset_netbios_name: + method_options.append("--reset-netbios-name") + # Dbus definition expects up to 10 arguments + method_options.extend([''] * (10 - len(method_options))) + + try: + bus = dbus.SystemBus() + obj = bus.get_object('org.freeipa.server', '/', + follow_name_owner_changes=True) + server = dbus.Interface(obj, 'org.freeipa.server') + _ret, _stdout, _stderr = server.config_enable_sid(*method_options) + except dbus.DBusException as e: + logger.error('Failed to call org.freeipa.server.config_enable_sid.' + 'DBus exception is %s', str(e)) + raise errors.ExecutionError(message=_('Failed to call DBus')) + + # The oddjob restarts dirsrv, we need to re-establish the conn + if self.api.Backend.ldap2.isconnected(): + self.api.Backend.ldap2.disconnect() + self.api.Backend.ldap2.connect(ccache=context.ccache_name) + + if _ret != 0: + logger.error("Helper config_enable_sid return code is %d", _ret) + raise errors.ExecutionError( + message=_('Configuration of SID failed. ' + 'See details in the error log')) + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) if 'ipadefaultprimarygroup' in entry_attrs: @@ -606,13 +687,27 @@ class config_mod(LDAPUpdate): self.obj.validate_domain_resolution_order(entry_attrs) + # The options add_sids or netbios_name need the enable_sid option + for sid_option in ['add_sids', 'netbios_name']: + if options.get(sid_option, None) and not options['enable_sid']: + opt = "--" + sid_option.replace("_", "-") + error_message = _("You cannot specify %s without " + "the --enable-sid option" % opt) + raise errors.ValidationError( + name=opt, + error=error_message) + + if options['enable_sid']: + self._enable_sid(ldap, options) + return dn def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs): if (isinstance(exc, errors.EmptyModlist) and call_func.__name__ == 'update_entry' and - 'ca_renewal_master_server' in options): + ('ca_renewal_master_server' in options or + 'enable_sid' in options)): return super(config_mod, self).exc_callback( diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py index e612528e0..20c536e4d 100644 --- a/ipaserver/rpcserver.py +++ b/ipaserver/rpcserver.py @@ -383,6 +383,8 @@ class WSGIExecutioner(Executioner): if self.api.env.debug: time_start = time.perf_counter_ns() try: + if 'KRB5CCNAME' in environ: + setattr(context, "ccache_name", environ['KRB5CCNAME']) if ('HTTP_ACCEPT_LANGUAGE' in environ): lang_reg_w_q = environ['HTTP_ACCEPT_LANGUAGE'].split(',')[0] lang_reg = lang_reg_w_q.split(';')[0] diff --git a/selinux/ipa.te b/selinux/ipa.te index 7492fca04..d18a4010d 100644 --- a/selinux/ipa.te +++ b/selinux/ipa.te @@ -155,6 +155,9 @@ manage_dirs_pattern(ipa_helper_t, ipa_var_run_t, ipa_var_run_t) manage_files_pattern(ipa_helper_t, ipa_var_run_t, ipa_var_run_t) files_pid_filetrans(ipa_helper_t, ipa_var_run_t, { dir file }) +manage_files_pattern(ipa_helper_t, ipa_tmp_t, ipa_tmp_t) +files_tmp_filetrans(ipa_helper_t, ipa_tmp_t, { file }) + kernel_read_system_state(ipa_helper_t) kernel_read_network_state(ipa_helper_t)