Imported Debian patch 4.7.2-3
This commit is contained in:
committed by
Mario Fetka
parent
27edeba051
commit
8bc559c5a1
@@ -1,41 +1,36 @@
|
||||
# Copyright (C) 2012-2019 FreeIPA Contributors see COPYING for license
|
||||
# Authors:
|
||||
# Martin Kosek <mkosek@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2012 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
from textwrap import dedent
|
||||
|
||||
from ipalib import Registry, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipapython import ipautil
|
||||
from ipaplatform.paths import paths
|
||||
from ipaserver.install import service
|
||||
from ipaserver.install import sysupgrade
|
||||
from ipaserver.install.adtrustinstance import (
|
||||
ADTRUSTInstance, map_Guests_to_nobody)
|
||||
|
||||
from ipaserver.dcerpc_common import TRUST_BIDIRECTIONAL
|
||||
|
||||
try:
|
||||
from samba.ndr import ndr_unpack
|
||||
from samba.dcerpc import lsa, drsblobs
|
||||
except ImportError:
|
||||
# If samba.ndr is not available, this machine is not provisioned
|
||||
# for serving a trust to Active Directory. As result, it does
|
||||
# not matter what ndr_unpack does but we save on pylint checks
|
||||
def ndr_unpack(x):
|
||||
raise NotImplementedError
|
||||
|
||||
drsblobs = None
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
register = Registry()
|
||||
|
||||
DEFAULT_ID_RANGE_SIZE = 200000
|
||||
trust_read_keys_template = \
|
||||
["cn=adtrust agents,cn=sysaccounts,cn=etc,{basedn}",
|
||||
"cn=trust admins,cn=groups,cn=accounts,{basedn}"]
|
||||
|
||||
|
||||
@register()
|
||||
@@ -330,28 +325,6 @@ class update_sids(Updater):
|
||||
return False, ()
|
||||
|
||||
|
||||
def get_gidNumber(ldap, env):
|
||||
# Read the gidnumber of the fallback group and returns a list with it
|
||||
dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME),
|
||||
env.container_group,
|
||||
env.basedn)
|
||||
|
||||
try:
|
||||
entry = ldap.get_entry(dn, ['gidnumber'])
|
||||
gidNumber = entry.get('gidnumber')
|
||||
except errors.NotFound:
|
||||
logger.error("%s not found",
|
||||
ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
||||
return None
|
||||
|
||||
if gidNumber is None:
|
||||
logger.error("%s does not have a gidnumber",
|
||||
ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
||||
return None
|
||||
|
||||
return gidNumber
|
||||
|
||||
|
||||
@register()
|
||||
class update_tdo_gidnumber(Updater):
|
||||
"""
|
||||
@@ -367,55 +340,43 @@ class update_tdo_gidnumber(Updater):
|
||||
logger.debug('AD Trusts are not enabled on this server')
|
||||
return False, []
|
||||
|
||||
gidNumber = get_gidNumber(ldap, self.api.env)
|
||||
# Read the gidnumber of the fallback group
|
||||
dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME),
|
||||
self.api.env.container_group,
|
||||
self.api.env.basedn)
|
||||
|
||||
try:
|
||||
entry = ldap.get_entry(dn, ['gidnumber'])
|
||||
gidNumber = entry.get('gidnumber')
|
||||
except errors.NotFound:
|
||||
logger.error("%s not found",
|
||||
ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
||||
return False, ()
|
||||
|
||||
if not gidNumber:
|
||||
logger.error("%s does not have a gidnumber",
|
||||
ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
||||
return False, ()
|
||||
|
||||
# For each trusted domain object, add posix attributes
|
||||
# to allow use of a trusted domain account by AD DCs
|
||||
# to authenticate against our Samba instance
|
||||
# For each trusted domain object, add gidNumber
|
||||
try:
|
||||
tdos = ldap.get_entries(
|
||||
DN(self.api.env.container_adtrusts, self.api.env.basedn),
|
||||
scope=ldap.SCOPE_ONELEVEL,
|
||||
filter="(&(objectclass=ipaNTTrustedDomain)"
|
||||
"(objectclass=ipaIDObject))",
|
||||
attrs_list=['gidnumber', 'uidnumber', 'objectclass',
|
||||
'ipantsecurityidentifier',
|
||||
'ipaNTTrustDirection'
|
||||
'uid', 'cn', 'ipantflatname'])
|
||||
filter="(objectclass=ipaNTTrustedDomain)",
|
||||
attrs_list=['gidnumber'])
|
||||
for tdo in tdos:
|
||||
# if the trusted domain object does not contain gidnumber,
|
||||
# add the default fallback group gidnumber
|
||||
if not tdo.get('gidnumber'):
|
||||
tdo['gidnumber'] = gidNumber
|
||||
|
||||
# Generate uidNumber and ipaNTSecurityIdentifier if
|
||||
# uidNumber is missing. We rely on sidgen plugin here
|
||||
# to generate ipaNTSecurityIdentifier.
|
||||
if not tdo.get('uidnumber'):
|
||||
tdo['uidnumber'] = ['-1']
|
||||
|
||||
if 'posixAccount' not in tdo.get('objectclass'):
|
||||
tdo['objectclass'].extend(['posixAccount'])
|
||||
# Based on the flat name of a TDO,
|
||||
# add user name FLATNAME$ (note dollar sign)
|
||||
# to allow SSSD to map this TDO to a POSIX account
|
||||
if not tdo.get('uid'):
|
||||
tdo['uid'] = ["{flatname}$".format(
|
||||
flatname=tdo.single_value['ipantflatname'])]
|
||||
if not tdo.get('homedirectory'):
|
||||
tdo['homedirectory'] = ['/dev/null']
|
||||
|
||||
# Store resulted entry
|
||||
try:
|
||||
ldap.update_entry(tdo)
|
||||
except errors.ExecutionError as e:
|
||||
logger.warning(
|
||||
"Failed to update trusted domain object %s", tdo.dn)
|
||||
logger.debug("Exception during TDO update: %s", str(e))
|
||||
try:
|
||||
tdo['gidnumber'] = gidNumber
|
||||
ldap.update_entry(tdo)
|
||||
logger.debug("Added gidnumber %s to %s",
|
||||
gidNumber, tdo.dn)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Failed to add gidnumber to %s", tdo.dn)
|
||||
|
||||
except errors.NotFound:
|
||||
logger.debug("No trusted domain object to update")
|
||||
@@ -439,501 +400,3 @@ class update_mapping_Guests_to_nobody(Updater):
|
||||
|
||||
map_Guests_to_nobody()
|
||||
return False, []
|
||||
|
||||
|
||||
@register()
|
||||
class update_tdo_to_new_layout(Updater):
|
||||
"""
|
||||
Transform trusted domain objects into a new layout
|
||||
|
||||
There are now two Kerberos principals per direction of trust:
|
||||
|
||||
INBOUND:
|
||||
- krbtgt/<OUR REALM>@<REMOTE REALM>, enabled by default
|
||||
|
||||
- <OUR FLATNAME$>@<REMOTE REALM>, disabled by default on our side
|
||||
as it is only used by SSSD to retrieve TDO creds when operating
|
||||
as an AD Trust agent across IPA topology
|
||||
|
||||
OUTBOUND:
|
||||
- krbtgt/<REMOTE REALM>@<OUR REALM>, enabled by default
|
||||
|
||||
- <REMOTE FLATNAME$>@<OUR REALM>, enabled by default and
|
||||
used by remote trusted DCs to authenticate against us
|
||||
|
||||
This principal also has krbtgt/<REMOTE FLATNAME>@<OUR REALM> defined
|
||||
as a Kerberos principal alias. This is due to how Kerberos
|
||||
key salt is derived for cross-realm principals on AD side
|
||||
|
||||
Finally, Samba requires <REMOTE FLATNAME$> account to also possess POSIX
|
||||
and SMB identities. We ensure this by making the trusted domain object to
|
||||
be this account with 'uid' and 'cn' attributes being '<REMOTE FLATNAME$>'
|
||||
and uidNumber/gidNumber generated automatically. Also, we ensure the
|
||||
trusted domain object is given a SID.
|
||||
|
||||
The update to <REMOTE FLATNAME$> POSIX/SMB identities is done through
|
||||
the update plugin update_tdo_gidnumber.
|
||||
"""
|
||||
tgt_principal_template = "krbtgt/{remote}@{local}"
|
||||
nbt_principal_template = "{nbt}$@{realm}"
|
||||
trust_filter = \
|
||||
"(&(objectClass=ipaNTTrustedDomain)(objectClass=ipaIDObject))"
|
||||
trust_attrs = ("ipaNTFlatName", "ipaNTTrustPartner", "ipaNTTrustDirection",
|
||||
"cn", "ipaNTTrustAttributes", "ipaNTAdditionalSuffixes",
|
||||
"ipaNTTrustedDomainSID", "ipaNTTrustType",
|
||||
"ipaNTTrustAuthIncoming", "ipaNTTrustAuthOutgoing")
|
||||
change_password_template = \
|
||||
"change_password -pw {password} " \
|
||||
"-e aes256-cts-hmac-sha1-96,aes128-cts-hmac-sha1-96 " \
|
||||
"{principal}"
|
||||
|
||||
KRB_PRINC_CREATE_DEFAULT = 0x00000000
|
||||
KRB_PRINC_CREATE_DISABLED = 0x00000001
|
||||
KRB_PRINC_CREATE_AGENT_PERMISSION = 0x00000002
|
||||
KRB_PRINC_CREATE_IDENTITY = 0x00000004
|
||||
KRB_PRINC_MUST_EXIST = 0x00000008
|
||||
|
||||
# This is a flag for krbTicketFlags attribute
|
||||
# to disallow creating any tickets using this principal
|
||||
KRB_DISALLOW_ALL_TIX = 0x00000040
|
||||
|
||||
def retrieve_trust_password(self, packed):
|
||||
# The structure of the trust secret is described at
|
||||
# https://github.com/samba-team/samba/blob/master/
|
||||
# librpc/idl/drsblobs.idl#L516-L569
|
||||
# In our case in LDAP TDO object stores
|
||||
# `struct trustAuthInOutBlob` that has `count` and
|
||||
# the `current` of `AuthenticationInformationArray` struct
|
||||
# which has own `count` and `array` of `AuthenticationInformation`
|
||||
# structs that have `AuthType` field which should be equal to
|
||||
# `LSA_TRUST_AUTH_TYPE_CLEAR`.
|
||||
# Then AuthInfo field would contain a password as an array of bytes
|
||||
assert(packed.count != 0)
|
||||
assert(packed.current.count != 0)
|
||||
assert(packed.current.array[0].AuthType == lsa.TRUST_AUTH_TYPE_CLEAR)
|
||||
clear_value = packed.current.array[0].AuthInfo.password
|
||||
|
||||
return ''.join(map(chr, clear_value))
|
||||
|
||||
def set_krb_principal(self, principals, password, trustdn, flags=None):
|
||||
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
if isinstance(principals, (list, tuple)):
|
||||
trust_principal = principals[0]
|
||||
alias = principals[1]
|
||||
else:
|
||||
trust_principal = principals
|
||||
alias = None
|
||||
|
||||
entry = None
|
||||
en = None
|
||||
try:
|
||||
entry = ldap.get_entry(
|
||||
DN(('krbprincipalname', trust_principal), trustdn))
|
||||
dn = entry.dn
|
||||
action = ldap.update_entry
|
||||
ticket_flags = int(entry.single_value.get('krbticketflags', 0))
|
||||
logger.debug("Updating Kerberos principal entry for %s",
|
||||
trust_principal)
|
||||
except errors.NotFound:
|
||||
# For a principal that must exist, we re-raise the exception
|
||||
# to let the caller to handle this situation
|
||||
if flags & self.KRB_PRINC_MUST_EXIST:
|
||||
raise
|
||||
|
||||
ticket_flags = 0
|
||||
if alias:
|
||||
try:
|
||||
en = ldap.get_entry(
|
||||
DN(('krbprincipalname', alias), trustdn))
|
||||
ldap.delete_entry(en.dn)
|
||||
ticket_flags = int(en.single_value.get(
|
||||
'krbticketflags', 0))
|
||||
except errors.NotFound:
|
||||
logger.debug("Entry for alias TDO does not exist for "
|
||||
"trusted domain object %s, skip it",
|
||||
alias)
|
||||
|
||||
dn = DN(('krbprincipalname', trust_principal), trustdn)
|
||||
entry = ldap.make_entry(dn)
|
||||
logger.debug("Adding Kerberos principal entry for %s",
|
||||
trust_principal)
|
||||
action = ldap.add_entry
|
||||
|
||||
entry_data = {
|
||||
'objectclass':
|
||||
['krbPrincipal', 'krbPrincipalAux',
|
||||
'krbTicketPolicyAux', 'top'],
|
||||
'krbcanonicalname': [trust_principal],
|
||||
'krbprincipalname': [trust_principal],
|
||||
}
|
||||
|
||||
if flags & self.KRB_PRINC_CREATE_DISABLED:
|
||||
entry_data['krbticketflags'] = (ticket_flags |
|
||||
self.KRB_DISALLOW_ALL_TIX)
|
||||
|
||||
if flags & self.KRB_PRINC_CREATE_AGENT_PERMISSION:
|
||||
entry_data['objectclass'].extend(['ipaAllowedOperations'])
|
||||
|
||||
if alias:
|
||||
entry_data['krbprincipalname'].extend([alias])
|
||||
if en:
|
||||
entry_data['krbprincipalkey'] = en.single_value.get(
|
||||
'krbprincipalkey')
|
||||
entry_data['krbextradata'] = en.single_value.get(
|
||||
'krbextradata')
|
||||
read_keys = en.get('ipaAllowedToPerform;read_keys', [])
|
||||
if not read_keys:
|
||||
# Old style, no ipaAllowedToPerform;read_keys in the entry,
|
||||
# use defaults that ipasam should have set when creating a
|
||||
# trust
|
||||
read_keys = list(map(
|
||||
lambda x: x.format(basedn=self.api.env.basedn),
|
||||
trust_read_keys_template))
|
||||
entry_data['ipaAllowedToPerform;read_keys'] = read_keys
|
||||
|
||||
entry.update(entry_data)
|
||||
try:
|
||||
action(entry)
|
||||
except errors.EmptyModlist:
|
||||
logger.debug("No update was required for Kerberos principal %s",
|
||||
trust_principal)
|
||||
|
||||
# If entry existed, no need to set Kerberos keys on it
|
||||
if action == ldap.update_entry:
|
||||
logger.debug("No need to update Kerberos keys for "
|
||||
"existing Kerberos principal %s",
|
||||
trust_principal)
|
||||
return
|
||||
|
||||
# Now that entry is updated, set its Kerberos keys.
|
||||
#
|
||||
# It would be a complication to use ipa-getkeytab LDAP extended control
|
||||
# here because we would need to encode the request in ASN.1 sequence
|
||||
# and we don't have the code to do so exposed in Python bindings.
|
||||
# Instead, as we run on IPA master, we can use kadmin.local for that
|
||||
# directly.
|
||||
# We pass the command as a stdin to both avoid shell interpolation
|
||||
# of the passwords and also to avoid its exposure to other processes
|
||||
# Since we don't want to record the output, make also a redacted log
|
||||
change_password = self.change_password_template.format(
|
||||
password=password,
|
||||
principal=trust_principal)
|
||||
|
||||
redacted = self.change_password_template.format(
|
||||
password='<REDACTED OUT>',
|
||||
principal=trust_principal)
|
||||
logger.debug("Updating Kerberos keys for %s with the following "
|
||||
"kadmin command:\n\t%s", trust_principal, redacted)
|
||||
|
||||
ipautil.run([paths.KADMIN_LOCAL, "-x",
|
||||
"ipa-setup-override-restrictions"],
|
||||
stdin=change_password, skip_output=True)
|
||||
|
||||
def execute(self, **options):
|
||||
# First, see if trusts are enabled on the server
|
||||
if not self.api.Command.adtrust_is_enabled()['result']:
|
||||
logger.debug('AD Trusts are not enabled on this server')
|
||||
return False, []
|
||||
|
||||
# If we have no Samba bindings, this master is not a trust controller
|
||||
if drsblobs is None:
|
||||
return False, []
|
||||
|
||||
ldap = self.api.Backend.ldap2
|
||||
gidNumber = get_gidNumber(ldap, self.api.env)
|
||||
if gidNumber is None:
|
||||
return False, []
|
||||
|
||||
result = self.api.Command.trustconfig_show()['result']
|
||||
our_nbt_name = result.get('ipantflatname', [None])[0]
|
||||
if not our_nbt_name:
|
||||
return False, []
|
||||
|
||||
trusts_dn = self.api.env.container_adtrusts + self.api.env.basedn
|
||||
|
||||
# We might be in a situation when no trusts exist yet
|
||||
# In such case there is nothing to upgrade but we have to catch
|
||||
# an exception or it will abort the whole upgrade process
|
||||
try:
|
||||
trusts = ldap.get_entries(
|
||||
base_dn=trusts_dn,
|
||||
scope=ldap.SCOPE_ONELEVEL,
|
||||
filter=self.trust_filter,
|
||||
attrs_list=self.trust_attrs)
|
||||
except errors.EmptyResult:
|
||||
trusts = []
|
||||
|
||||
# For every trust, retrieve its principals and convert
|
||||
for t_entry in trusts:
|
||||
t_dn = t_entry.dn
|
||||
logger.debug('Processing trust domain object %s', str(t_dn))
|
||||
t_realm = t_entry.single_value.get('ipaNTTrustPartner').upper()
|
||||
direction = int(t_entry.single_value.get('ipaNTTrustDirection'))
|
||||
passwd_incoming = self.retrieve_trust_password(
|
||||
ndr_unpack(drsblobs.trustAuthInOutBlob,
|
||||
t_entry.single_value.get('ipaNTTrustAuthIncoming')))
|
||||
passwd_outgoing = self.retrieve_trust_password(
|
||||
ndr_unpack(drsblobs.trustAuthInOutBlob,
|
||||
t_entry.single_value.get('ipaNTTrustAuthOutgoing')))
|
||||
# For outbound and inbound trusts, process four principals total
|
||||
if (direction & TRUST_BIDIRECTIONAL) == TRUST_BIDIRECTIONAL:
|
||||
# 1. OUTBOUND: krbtgt/<REMOTE REALM>@<OUR REALM> must exist
|
||||
trust_principal = self.tgt_principal_template.format(
|
||||
remote=t_realm, local=self.api.env.realm)
|
||||
try:
|
||||
self.set_krb_principal(trust_principal,
|
||||
passwd_outgoing,
|
||||
t_dn,
|
||||
flags=self.KRB_PRINC_CREATE_DEFAULT)
|
||||
except errors.NotFound:
|
||||
# It makes no sense to convert this one, skip the trust
|
||||
# completely, better to re-establish one
|
||||
logger.error(
|
||||
"Broken trust to AD: %s not found, "
|
||||
"please re-establish the trust to %s",
|
||||
trust_principal, t_realm)
|
||||
continue
|
||||
|
||||
# 2. Create <REMOTE FLATNAME$>@<OUR REALM>
|
||||
nbt_name = t_entry.single_value.get('ipaNTFlatName')
|
||||
nbt_principal = self.nbt_principal_template.format(
|
||||
nbt=nbt_name, realm=self.api.env.realm)
|
||||
tgt_principal = self.tgt_principal_template.format(
|
||||
remote=nbt_name, local=self.api.env.realm)
|
||||
self.set_krb_principal([nbt_principal, tgt_principal],
|
||||
passwd_incoming,
|
||||
t_dn,
|
||||
flags=self.KRB_PRINC_CREATE_DEFAULT)
|
||||
|
||||
# 3. INBOUND: krbtgt/<OUR REALM>@<REMOTE REALM> must exist
|
||||
trust_principal = self.tgt_principal_template.format(
|
||||
remote=self.api.env.realm, local=t_realm)
|
||||
try:
|
||||
self.set_krb_principal(trust_principal, passwd_outgoing,
|
||||
t_dn,
|
||||
flags=self.KRB_PRINC_CREATE_DEFAULT)
|
||||
except errors.NotFound:
|
||||
# It makes no sense to convert this one, skip the trust
|
||||
# completely, better to re-establish one
|
||||
logger.error(
|
||||
"Broken trust to AD: %s not found, "
|
||||
"please re-establish the trust to %s",
|
||||
trust_principal, t_realm)
|
||||
continue
|
||||
|
||||
# 4. Create krbtgt/<OUR FLATNAME>@<REMOTE REALM>, disabled
|
||||
nbt_principal = self.nbt_principal_template.format(
|
||||
nbt=our_nbt_name, realm=t_realm)
|
||||
tgt_principal = self.tgt_principal_template.format(
|
||||
remote=our_nbt_name, local=t_realm)
|
||||
self.set_krb_principal([tgt_principal, nbt_principal],
|
||||
passwd_incoming,
|
||||
t_dn,
|
||||
flags=self.KRB_PRINC_CREATE_DEFAULT |
|
||||
self.KRB_PRINC_CREATE_AGENT_PERMISSION |
|
||||
self.KRB_PRINC_CREATE_DISABLED)
|
||||
|
||||
return False, []
|
||||
|
||||
|
||||
KeyEntry = namedtuple('KeyEntry',
|
||||
['kvno', 'principal', 'etype', 'key'])
|
||||
|
||||
|
||||
@register()
|
||||
class update_host_cifs_keytabs(Updater):
|
||||
"""Synchronize host keytab and Samba keytab
|
||||
|
||||
Samba needs access to host/domain.controller principal keys to allow
|
||||
validation of DCE RPC requests sent by domain members since those use a
|
||||
service ticket to host/domain.controller principal because in Active
|
||||
Directory service keys are the same as the machine account credentials
|
||||
and services are just aliases to the machine account object.
|
||||
"""
|
||||
|
||||
host_princ_template = "host/{master}@{realm}"
|
||||
valid_etypes = ['aes256-cts-hmac-sha1-96', 'aes128-cts-hmac-sha1-96']
|
||||
|
||||
def extract_key_refs(self, keytab):
|
||||
host_princ = self.host_princ_template.format(
|
||||
master=self.api.env.host, realm=self.api.env.realm)
|
||||
result = ipautil.run([paths.KLIST, "-eK", "-k", keytab],
|
||||
capture_output=True, raiseonerr=False,
|
||||
nolog_output=True)
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
|
||||
keys_to_sync = []
|
||||
for l in result.output.splitlines():
|
||||
if (host_princ in l and any(e in l for e in self.valid_etypes)):
|
||||
|
||||
els = l.split()
|
||||
els[-2] = els[-2].strip('()')
|
||||
els[-1] = els[-1].strip('()')
|
||||
keys_to_sync.append(KeyEntry._make(els))
|
||||
|
||||
return keys_to_sync
|
||||
|
||||
def copy_key(self, keytab, keyentry):
|
||||
# keyentry.key is a hex value of the actual key
|
||||
# prefixed with 0x, as produced by klist -K -k.
|
||||
# However, ktutil accepts hex value without 0x, so
|
||||
# we should strip first two characters.
|
||||
stdin = dedent("""\
|
||||
rkt {keytab}
|
||||
addent -key -p {principal} -k {kvno} -e {etype}
|
||||
{key}
|
||||
wkt {keytab}
|
||||
""").format(keytab=keytab, principal=keyentry.principal,
|
||||
kvno=keyentry.kvno, etype=keyentry.etype,
|
||||
key=keyentry.key[2:])
|
||||
|
||||
result = ipautil.run([paths.KTUTIL], stdin=stdin, raiseonerr=False,
|
||||
umask=0o077, nolog_output=True)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.warning('Unable to update %s with new keys', keytab)
|
||||
|
||||
def execute(self, **options):
|
||||
# First, see if trusts are enabled on the server
|
||||
if not self.api.Command.adtrust_is_enabled()['result']:
|
||||
logger.debug('AD Trusts are not enabled on this server')
|
||||
return False, []
|
||||
|
||||
# Extract keys from the host and samba keytabs
|
||||
hostkeys = self.extract_key_refs(paths.KRB5_KEYTAB)
|
||||
cifskeys = self.extract_key_refs(paths.SAMBA_KEYTAB)
|
||||
if any([hostkeys is None, cifskeys is None]):
|
||||
logger.warning('Either %s or %s are missing or unreadable',
|
||||
paths.KRB5_KEYTAB, paths.SAMBA_KEYTAB)
|
||||
return False, []
|
||||
|
||||
# If there are missing host keys in the samba keytab, copy them over
|
||||
# Also copy those keys that differ in the content and/or KVNO
|
||||
for hostkey in hostkeys:
|
||||
copied = False
|
||||
uptodate = False
|
||||
for cifskey in cifskeys:
|
||||
if all([cifskey.principal == hostkey.principal,
|
||||
cifskey.etype == hostkey.etype]):
|
||||
if any([cifskey.key != hostkey.key,
|
||||
cifskey.kvno != hostkey.kvno]):
|
||||
self.copy_key(paths.SAMBA_KEYTAB, hostkey)
|
||||
copied = True
|
||||
break
|
||||
uptodate = True
|
||||
if not (copied or uptodate):
|
||||
self.copy_key(paths.SAMBA_KEYTAB, hostkey)
|
||||
|
||||
return False, []
|
||||
|
||||
|
||||
@register()
|
||||
class update_tdo_default_read_keys_permissions(Updater):
|
||||
trust_filter = \
|
||||
"(&(objectClass=krbPrincipal)(krbPrincipalName=krbtgt/{nbt}@*))"
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
# First, see if trusts are enabled on the server
|
||||
if not self.api.Command.adtrust_is_enabled()['result']:
|
||||
logger.debug('AD Trusts are not enabled on this server')
|
||||
return False, []
|
||||
|
||||
result = self.api.Command.trustconfig_show()['result']
|
||||
our_nbt_name = result.get('ipantflatname', [None])[0]
|
||||
if not our_nbt_name:
|
||||
return False, []
|
||||
|
||||
trusts_dn = self.api.env.container_adtrusts + self.api.env.basedn
|
||||
trust_filter = self.trust_filter.format(nbt=our_nbt_name)
|
||||
|
||||
# We might be in a situation when no trusts exist yet
|
||||
# In such case there is nothing to upgrade but we have to catch
|
||||
# an exception or it will abort the whole upgrade process
|
||||
try:
|
||||
tdos = ldap.get_entries(
|
||||
base_dn=trusts_dn,
|
||||
scope=ldap.SCOPE_SUBTREE,
|
||||
filter=trust_filter,
|
||||
attrs_list=['*'])
|
||||
except errors.EmptyResult:
|
||||
tdos = []
|
||||
|
||||
for tdo in tdos:
|
||||
updates = dict()
|
||||
oc = tdo.get('objectClass', [])
|
||||
if 'ipaAllowedOperations' not in oc:
|
||||
updates['objectClass'] = oc + ['ipaAllowedOperations']
|
||||
|
||||
read_keys = tdo.get('ipaAllowedToPerform;read_keys', [])
|
||||
if not read_keys:
|
||||
read_keys_values = list(map(
|
||||
lambda x: x.format(basedn=self.api.env.basedn),
|
||||
trust_read_keys_template))
|
||||
updates['ipaAllowedToPerform;read_keys'] = read_keys_values
|
||||
|
||||
tdo.update(updates)
|
||||
try:
|
||||
ldap.update_entry(tdo)
|
||||
except errors.EmptyModlist:
|
||||
logger.debug("No update was required for TDO %s",
|
||||
tdo.single_value.get('krbCanonicalName'))
|
||||
|
||||
return False, []
|
||||
|
||||
|
||||
@register()
|
||||
class update_adtrust_agents_members(Updater):
|
||||
""" Ensure that each adtrust agent is a member of the adtrust agents group
|
||||
|
||||
cn=adtrust agents,cn=sysaccounts,cn=etc,$BASEDN must contain:
|
||||
- member: krbprincipalname=cifs/master@realm,cn=services,cn=accounts,base
|
||||
- member: fqdn=master,cn=computers,cn=accounts,base
|
||||
"""
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
# First, see if trusts are enabled on the server
|
||||
if not self.api.Command.adtrust_is_enabled()['result']:
|
||||
logger.debug('AD Trusts are not enabled on this server')
|
||||
return False, []
|
||||
|
||||
agents_dn = DN(
|
||||
('cn', 'adtrust agents'), self.api.env.container_sysaccounts,
|
||||
self.api.env.basedn)
|
||||
|
||||
try:
|
||||
agents_entry = ldap.get_entry(agents_dn, ['member'])
|
||||
except errors.NotFound:
|
||||
logger.error("No adtrust agents group found")
|
||||
return False, []
|
||||
|
||||
# Build a list of agents from the cifs/.. members
|
||||
agents_list = []
|
||||
members = agents_entry.get('member', [])
|
||||
suffix = '@{}'.format(self.api.env.realm).lower()
|
||||
|
||||
for amember in members:
|
||||
if amember[0].attr.lower() == 'krbprincipalname':
|
||||
# Extract krbprincipalname=cifs/hostname@realm from the DN
|
||||
value = amember[0].value
|
||||
if (value.lower().startswith('cifs/') and
|
||||
value.lower().endswith(suffix)):
|
||||
# 5 = length of 'cifs/'
|
||||
hostname = value[5:-len(suffix)]
|
||||
agents_list.append(DN(('fqdn', hostname),
|
||||
self.api.env.container_host,
|
||||
self.api.env.basedn))
|
||||
|
||||
# Add the fqdn=hostname... to the group
|
||||
service.add_principals_to_group(
|
||||
ldap,
|
||||
agents_dn,
|
||||
"member",
|
||||
agents_list)
|
||||
|
||||
return False, []
|
||||
|
||||
@@ -47,7 +47,8 @@ class update_ca_renewal_master(Updater):
|
||||
return False, []
|
||||
|
||||
ldap = self.api.Backend.ldap2
|
||||
base_dn = DN(self.api.env.container_masters, self.api.env.basedn)
|
||||
base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
|
||||
self.api.env.basedn)
|
||||
dn = DN(('cn', 'CA'), ('cn', self.api.env.host), base_dn)
|
||||
filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'
|
||||
try:
|
||||
|
||||
@@ -533,7 +533,7 @@ class update_dnsserver_configuration_into_ldap(DNSUpdater):
|
||||
return False, []
|
||||
|
||||
result = self.api.Command.server_show(self.api.env.host)['result']
|
||||
if 'DNS server' not in result.get('enabled_role_servrole', []):
|
||||
if not 'DNS server' in result.get('enabled_role_servrole', []):
|
||||
logger.debug('This server is not DNS server, nothing to upgrade')
|
||||
sysupgrade.set_upgrade_state('dns', 'server_config_to_ldap', True)
|
||||
return False, []
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from ipalib import Registry, Updater, x509
|
||||
from ipapython.dn import DN
|
||||
from ipaplatform.paths import paths
|
||||
from ipaserver.install import krainstance
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
register = Registry()
|
||||
|
||||
|
||||
@register()
|
||||
class fix_kra_people_entry(Updater):
|
||||
"""
|
||||
Update the KRA uid=ipakra agent user entry.
|
||||
|
||||
There was a bug where this was created with an incorrect
|
||||
'description' attribute, breaking authentication:
|
||||
https://pagure.io/freeipa/issue/8084.
|
||||
|
||||
"""
|
||||
def execute(self, **options):
|
||||
kra = krainstance.KRAInstance(self.api.env.realm)
|
||||
if not kra.is_installed():
|
||||
return False, []
|
||||
|
||||
cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
|
||||
entry = self.api.Backend.ldap2.get_entry(krainstance.KRA_AGENT_DN)
|
||||
|
||||
# check description attribute
|
||||
description_values = entry.get('description', [])
|
||||
if len(description_values) < 1:
|
||||
# missing 'description' attribute is unexpected, but we can
|
||||
# add it
|
||||
do_fix = True
|
||||
else:
|
||||
# There should only be one value, so we will take the first value.
|
||||
# But ignore the serial number when comparing, just in case.
|
||||
description = description_values[0]
|
||||
parts = description.split(';', 2) # see below for syntax
|
||||
|
||||
if len(parts) < 3:
|
||||
do_fix = True # syntax error (not expected)
|
||||
elif parts[2] != '{};{}'.format(DN(cert.issuer), DN(cert.subject)):
|
||||
# issuer/subject does not match cert. THIS is the condition
|
||||
# caused by issue 8084, which we want to fix.
|
||||
do_fix = True
|
||||
else:
|
||||
do_fix = False # everything is fine
|
||||
|
||||
if do_fix:
|
||||
# If other replicas have a different iteration of the IPA RA
|
||||
# cert (e.g. renewal was triggered prematurely on some master
|
||||
# and not on others) then authentication on those replicas will
|
||||
# fail. But the 'description' attribute needed fixing because
|
||||
# the issuer value was wrong, meaning authentication was broken
|
||||
# on ALL replicas. So even for the corner case where different
|
||||
# replicas have different IPA RA certs, updating the attribute
|
||||
# will at least mean THIS replica can authenticate to the KRA.
|
||||
|
||||
logger.debug("Fixing KRA user entry 'description' attribute")
|
||||
entry['description'] = [
|
||||
'2;{};{};{}'.format(
|
||||
cert.serial_number,
|
||||
DN(cert.issuer),
|
||||
DN(cert.subject)
|
||||
)
|
||||
]
|
||||
self.api.Backend.ldap2.update_entry(entry)
|
||||
|
||||
return False, [] # don't restart DS; no LDAP updates to perform
|
||||
@@ -53,7 +53,7 @@ def entry_to_update(entry):
|
||||
return update
|
||||
|
||||
|
||||
class GenerateUpdateMixin:
|
||||
class GenerateUpdateMixin(object):
|
||||
def _dn_suffix_replace(self, dn, old_suffix, new_suffix):
|
||||
"""Replace all occurences of "old" AVAs in a DN by "new"
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
import logging
|
||||
from ipalib import Registry, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
register = Registry()
|
||||
|
||||
|
||||
@register()
|
||||
class update_changelog_maxage(Updater):
|
||||
"""
|
||||
Update the changelog maxage if it is not set
|
||||
"""
|
||||
|
||||
def update_entry(self, cl_entry, conn):
|
||||
maxage = cl_entry.single_value.get('nsslapd-changelogmaxage')
|
||||
if maxage is None:
|
||||
cl_entry['nsslapd-changelogmaxage'] = '30d'
|
||||
conn.update_entry(cl_entry)
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
for backend in ('userroot', 'ipaca'):
|
||||
dn = DN(
|
||||
('cn', 'changelog'),
|
||||
('cn', backend),
|
||||
('cn', 'ldbm database'),
|
||||
('cn', 'plugins'),
|
||||
('cn', 'config'))
|
||||
try:
|
||||
cl_entry = ldap.get_entry(dn, ['nsslapd-changelogmaxage'])
|
||||
self.update_entry(cl_entry, ldap)
|
||||
except errors.NotFound:
|
||||
# Try the old global changelog, and return
|
||||
dn = DN(
|
||||
('cn', 'changelog5'),
|
||||
('cn', 'config'))
|
||||
try:
|
||||
cl_entry = ldap.get_entry(dn, ['nsslapd-changelogmaxage'])
|
||||
self.update_entry(cl_entry, ldap)
|
||||
except errors.NotFound:
|
||||
logger.debug('Error retrieving: %s', str(dn))
|
||||
return False, []
|
||||
|
||||
return False, []
|
||||
@@ -35,16 +35,16 @@ class update_fix_duplicate_cacrt_in_ldap(Updater):
|
||||
"""
|
||||
When multiple entries exist for IPA CA cert in ldap, remove the duplicate
|
||||
|
||||
After this plugin has removed duplicate entries, DS needs to be
|
||||
restarted. This ensures that the attribute uniqueness plugin is working
|
||||
and prevents other plugins from adding duplicates.
|
||||
After this plugin, ds needs to be restarted. This ensures that
|
||||
the attribute uniqueness plugin is working and prevents
|
||||
other plugins from adding duplicates.
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
# If CA is disabled, no need to check for duplicates of IPA CA
|
||||
ca_enabled = self.api.Command.ca_is_enabled()['result']
|
||||
if not ca_enabled:
|
||||
return False, []
|
||||
return True, []
|
||||
|
||||
# Look for the IPA CA cert subject
|
||||
ldap = self.api.Backend.ldap2
|
||||
@@ -52,22 +52,13 @@ class update_fix_duplicate_cacrt_in_ldap(Updater):
|
||||
ldap,
|
||||
self.api.env.container_ca,
|
||||
self.api.env.basedn)
|
||||
cacert_nick = get_ca_nickname(self.api.env.realm)
|
||||
|
||||
# Find if there are other certificates with the same subject
|
||||
# They are duplicates resulting of BZ 1480102
|
||||
base_dn = DN(('cn', 'certificates'), ('cn', 'ipa'), ('cn', 'etc'),
|
||||
self.api.env.basedn)
|
||||
filter = ldap.combine_filters(
|
||||
[
|
||||
# all certificates with CA cert subject
|
||||
ldap.make_filter({'ipaCertSubject': cacert_subject}),
|
||||
# except the default certificate
|
||||
ldap.make_filter({'cn': cacert_nick}, rules=ldap.MATCH_NONE),
|
||||
],
|
||||
rules=ldap.MATCH_ALL
|
||||
)
|
||||
try:
|
||||
filter = ldap.make_filter({'ipaCertSubject': cacert_subject})
|
||||
result, _truncated = ldap.find_entries(
|
||||
base_dn=base_dn,
|
||||
filter=filter,
|
||||
@@ -75,10 +66,13 @@ class update_fix_duplicate_cacrt_in_ldap(Updater):
|
||||
except errors.NotFound:
|
||||
# No duplicate, we're good
|
||||
logger.debug("No duplicates for IPA CA in LDAP")
|
||||
return False, []
|
||||
return True, []
|
||||
|
||||
logger.debug("Found %d entrie(s) for IPA CA in LDAP", len(result))
|
||||
cacert_dn = DN(('cn', get_ca_nickname(self.api.env.realm)), base_dn)
|
||||
for entry in result:
|
||||
if entry.dn == cacert_dn:
|
||||
continue
|
||||
# Remove the duplicate
|
||||
try:
|
||||
ldap.delete_entry(entry)
|
||||
|
||||
@@ -24,7 +24,7 @@ class update_ldap_server_list(Updater):
|
||||
entry = ldap.get_entry(dn)
|
||||
srvlist = entry.single_value.get('defaultServerList', '')
|
||||
srvlist = srvlist.split()
|
||||
if self.api.env.host not in srvlist:
|
||||
if not self.api.env.host in srvlist:
|
||||
srvlist.append(self.api.env.host)
|
||||
attr = ' '.join(srvlist)
|
||||
entry['defaultServerList'] = attr
|
||||
|
||||
@@ -560,7 +560,8 @@ class update_managed_permissions(Updater):
|
||||
if current_aci != default_aci:
|
||||
logger.debug('ACIs not compatible')
|
||||
continue
|
||||
all_incompatible = False
|
||||
else:
|
||||
all_incompatible = False
|
||||
|
||||
if attrs_in_all_defaults is None:
|
||||
attrs_in_all_defaults = set(default_attrs)
|
||||
@@ -635,10 +636,6 @@ class update_managed_permissions(Updater):
|
||||
|
||||
# Attributes from template
|
||||
bindruletype = template.pop('ipapermbindruletype', 'permission')
|
||||
if bindruletype not in {"all", "anonymous", "self", "permission"}:
|
||||
raise ValueError(
|
||||
f"Invalid ipapermbindruletype '{bindruletype}'"
|
||||
)
|
||||
if is_new:
|
||||
entry.single_value['ipapermbindruletype'] = bindruletype
|
||||
|
||||
|
||||
@@ -56,11 +56,8 @@ class update_passync_privilege_update(Updater):
|
||||
|
||||
logger.debug("Add PassSync user as a member of PassSync privilege")
|
||||
ldap = self.api.Backend.ldap2
|
||||
passsync_dn = DN(
|
||||
('uid', 'passsync'),
|
||||
self.api.env.container_sysaccounts,
|
||||
self.api.env.basedn
|
||||
)
|
||||
passsync_dn = DN(('uid','passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'),
|
||||
self.api.env.basedn)
|
||||
passsync_privilege_dn = DN(('cn','PassSync Service'),
|
||||
self.api.env.container_privilege,
|
||||
self.api.env.basedn)
|
||||
|
||||
@@ -52,7 +52,7 @@ class update_ra_cert_store(Updater):
|
||||
|
||||
# stop tracking the old cert and remove it
|
||||
certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname=ra_nick)
|
||||
certdb.delete_key_and_cert(ra_nick)
|
||||
certdb.delete_cert(ra_nick)
|
||||
if os.path.exists(paths.OLD_KRA_AGENT_PEM):
|
||||
os.remove(paths.OLD_KRA_AGENT_PEM)
|
||||
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
# Authors:
|
||||
# Thierry Bordaz <tbordaz@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2019 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
|
||||
from ipalib import Registry, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
register = Registry()
|
||||
|
||||
|
||||
@register()
|
||||
class update_unhashed_password(Updater):
|
||||
"""
|
||||
DS
|
||||
"""
|
||||
def __remove_update(self, update, key, value):
|
||||
statement = dict(action='remove', attr=key, value=value)
|
||||
update.setdefault('updates', []).append(statement)
|
||||
|
||||
def __add_update(self, update, key, value):
|
||||
statement = dict(action='add', attr=key, value=value)
|
||||
update.setdefault('updates', []).append(statement)
|
||||
|
||||
def execute(self, **options):
|
||||
logger.debug("Upgrading unhashed password configuration")
|
||||
ldap = self.api.Backend.ldap2
|
||||
base_config = DN(('cn', 'config'))
|
||||
try:
|
||||
entry = ldap.get_entry(base_config,
|
||||
['nsslapd-unhashed-pw-switch'])
|
||||
except errors.NotFound:
|
||||
logger.error("Unhashed password configuration not found")
|
||||
return False, []
|
||||
|
||||
config_dn = entry.dn
|
||||
|
||||
toggle = entry.single_value.get("nsslapd-unhashed-pw-switch")
|
||||
if toggle.lower() not in ['off', 'on', 'nolog']:
|
||||
logger.error("Unhashed password had invalid value '%s'", toggle)
|
||||
|
||||
# Check if it exists winsync agreements
|
||||
searchfilter = '(objectclass=nsDSWindowsReplicationAgreement)'
|
||||
try:
|
||||
winsync_agmts, _truncated = ldap.find_entries(
|
||||
base_dn=base_config,
|
||||
filter=searchfilter,
|
||||
attrs_list=[]
|
||||
)
|
||||
except errors.NotFound:
|
||||
logger.debug("Unhashed password this is not a winsync deployment")
|
||||
winsync_agmts = []
|
||||
|
||||
update = {
|
||||
'dn': config_dn,
|
||||
'updates': [],
|
||||
}
|
||||
if len(winsync_agmts) > 0:
|
||||
# We are running in a winsync environment
|
||||
# Log a warning that changelog will contain sensitive data
|
||||
try:
|
||||
# Check if the new per-backend changelog exists...
|
||||
cldb = ldap.get_entry(
|
||||
DN(('cn', 'changelog'),
|
||||
('cn', 'userRoot'),
|
||||
('cn', 'ldbm database'),
|
||||
('cn', 'plugins'),
|
||||
('cn', 'config')))
|
||||
|
||||
# We have a backend changelog so get the db dir in this case
|
||||
db_entry = ldap.get_entry(
|
||||
DN(('cn', 'userRoot'),
|
||||
('cn', 'ldbm database'),
|
||||
('cn', 'plugins'),
|
||||
('cn', 'config')),
|
||||
['nsslapd-directory'])
|
||||
cldb = db_entry.single_value.get("nsslapd-directory")
|
||||
logger.warning("This server is configured for winsync, "
|
||||
"the changelog files under %s "
|
||||
"may contain clear text passwords.\n"
|
||||
"Please ensure that these files can be accessed"
|
||||
" only by trusted accounts.\n", cldb)
|
||||
except errors.NotFound:
|
||||
# Did not find backend changelog, check the global changelog
|
||||
try:
|
||||
cldb_e = ldap.get_entry(
|
||||
DN(('cn', 'changelog5'),
|
||||
('cn', 'config')),
|
||||
['nsslapd-changelogdir'])
|
||||
cldb = cldb_e.single_value.get("nsslapd-changelogdir")
|
||||
logger.warning("This server is configured for winsync, "
|
||||
"the changelog files under %s "
|
||||
"may contain clear text passwords.\n"
|
||||
"Please ensure that these files can be "
|
||||
"accessed only by trusted accounts.\n",
|
||||
cldb)
|
||||
except errors.NotFound:
|
||||
logger.warning("This server is configured for winsync, "
|
||||
"the changelog files may contain "
|
||||
"clear text passwords.\n"
|
||||
"Please ensure that these files can be "
|
||||
"accessed only by trusted accounts.\n")
|
||||
if toggle.lower() == 'on':
|
||||
# The current DS configuration already logs the
|
||||
# unhashed password
|
||||
updates = []
|
||||
else:
|
||||
self.__remove_update(update, 'nsslapd-unhashed-pw-switch',
|
||||
toggle)
|
||||
self.__add_update(update, 'nsslapd-unhashed-pw-switch', 'on')
|
||||
updates = [update]
|
||||
else:
|
||||
if toggle.lower() == 'nolog':
|
||||
updates = []
|
||||
else:
|
||||
self.__remove_update(update, 'nsslapd-unhashed-pw-switch',
|
||||
toggle)
|
||||
self.__add_update(update, 'nsslapd-unhashed-pw-switch',
|
||||
'nolog')
|
||||
updates = [update]
|
||||
|
||||
return False, updates
|
||||
@@ -21,7 +21,7 @@ import logging
|
||||
|
||||
from ipalib.install import certstore
|
||||
from ipaserver.install import certs, dsinstance
|
||||
from ipapython.ipaldap import realm_to_serverid
|
||||
from ipaserver.install.installutils import realm_to_serverid
|
||||
from ipalib import Registry, errors
|
||||
from ipalib import Updater
|
||||
from ipapython import certdb
|
||||
@@ -92,7 +92,7 @@ class update_upload_cacrt(Updater):
|
||||
config = entry.setdefault('ipaConfigString', [])
|
||||
if ca_enabled:
|
||||
config.append('ipaCa')
|
||||
config.append('compatCA')
|
||||
config.append('ipaCa')
|
||||
|
||||
try:
|
||||
ldap.add_entry(entry)
|
||||
|
||||
Reference in New Issue
Block a user