Allow PassSync user to locate and update NT users

Add new PassSync Service privilege that have sufficient access to
let AD PassSync service search for NT users and update the password.
To make sure existing PassSync user keeps working, it is added as
a member of the new privilege.

New update plugin is added to add link to the new privilege to the
potentially existing PassSync user to avoid breaking the PassSync
service.

https://fedorahosted.org/freeipa/ticket/4837

Reviewed-By: David Kupka <dkupka@redhat.com>
This commit is contained in:
Martin Kosek 2015-01-13 18:09:17 +01:00
parent 426759f47f
commit 282d1ec2f9
6 changed files with 152 additions and 25 deletions

View File

@ -269,6 +269,8 @@ aci: (targetattr = "krblastadminunlock || krblastfailedauth || krblastpwdchange
dn: cn=users,cn=accounts,dc=ipa,dc=example dn: cn=users,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "memberof")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Membership";allow (compare,read,search) userdn = "ldap:///all";) aci: (targetattr = "memberof")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Membership";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=users,cn=accounts,dc=ipa,dc=example dn: cn=users,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "ntuniqueid || ntuseracctexpires || ntusercodepage || ntuserdeleteaccount || ntuserdomainid || ntuserlastlogoff || ntuserlastlogon")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User NT Attributes";allow (compare,read,search) groupdn = "ldap:///cn=System: Read User NT Attributes,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=users,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || description || displayname || entryusn || gecos || gidnumber || givenname || homedirectory || initials || ipantsecurityidentifier || loginshell || manager || modifytimestamp || objectclass || sn || title || uid || uidnumber")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Standard Attributes";allow (compare,read,search) userdn = "ldap:///anyone";) aci: (targetattr = "cn || createtimestamp || description || displayname || entryusn || gecos || gidnumber || givenname || homedirectory || initials || ipantsecurityidentifier || loginshell || manager || modifytimestamp || objectclass || sn || title || uid || uidnumber")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Standard Attributes";allow (compare,read,search) userdn = "ldap:///anyone";)
dn: dc=ipa,dc=example dn: dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || entryusn || gecos || gidnumber || homedirectory || loginshell || modifytimestamp || objectclass || uid || uidnumber")(target = "ldap:///cn=users,cn=*,cn=views,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read User Views Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";) aci: (targetattr = "cn || createtimestamp || entryusn || gecos || gidnumber || homedirectory || loginshell || modifytimestamp || objectclass || uid || uidnumber")(target = "ldap:///cn=users,cn=*,cn=views,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read User Views Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)

View File

@ -184,3 +184,33 @@ default:description: Read list of IPA masters
dn: cn=masters,cn=ipa,cn=etc,$SUFFIX dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
add:aci:'(targetfilter = "(objectClass=nsContainer)")(targetattr = "cn || objectClass || ipaConfigString")(version 3.0; acl "Read IPA Masters"; allow (read, search, compare) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";)' add:aci:'(targetfilter = "(objectClass=nsContainer)")(targetattr = "cn || objectClass || ipaConfigString")(version 3.0; acl "Read IPA Masters"; allow (read, search, compare) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";)'
add:aci:'(targetfilter = "(objectClass=nsContainer)")(targetattr = "ipaConfigString")(version 3.0; acl "Modify IPA Masters"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";)' add:aci:'(targetfilter = "(objectClass=nsContainer)")(targetattr = "ipaConfigString")(version 3.0; acl "Modify IPA Masters"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";)'
# PassSync
dn: cn=PassSync Service,cn=privileges,cn=pbac,$SUFFIX
default:objectClass: nestedgroup
default:objectClass: groupofnames
default:objectClass: top
default:cn: PassSync Service
default:description: PassSync Service
dn: cn=Read PassSync Managers Configuration,cn=permissions,cn=pbac,$SUFFIX
default:objectClass: groupofnames
default:objectClass: ipapermission
default:objectClass: top
default:cn: Read PassSync Managers Configuration
default:member: cn=Replication Administrators,cn=privileges,cn=pbac,$SUFFIX
default:ipapermissiontype: SYSTEM
dn: cn=config
add:aci: '(targetattr = "cn || createtimestamp || entryusn || modifytimestamp || objectclass || passsyncmanagersdns*")(target = "ldap:///cn=ipa_pwd_extop,cn=plugins,cn=config")(version 3.0;acl "permission:Read PassSync Managers Configuration";allow (compare,read,search) groupdn = "ldap:///cn=Read PassSync Managers Configuration,cn=permissions,cn=pbac,$SUFFIX";)'
dn: cn=Modify PassSync Managers Configuration,cn=permissions,cn=pbac,$SUFFIX
default:objectClass: groupofnames
default:objectClass: ipapermission
default:objectClass: top
default:cn: Modify PassSync Managers Configuration
default:member: cn=Replication Administrators,cn=privileges,cn=pbac,$SUFFIX
default:ipapermissiontype: SYSTEM
dn: cn=config
add:aci: '(targetattr = "passsyncmanagersdns*")(target = "ldap:///cn=ipa_pwd_extop,cn=plugins,cn=config")(version 3.0;acl "permission:Modify PassSync Managers Configuration";allow (write) groupdn = "ldap:///cn=Modify PassSync Managers Configuration,cn=permissions,cn=pbac,$SUFFIX";)'

View File

@ -373,10 +373,12 @@ class user(LDAPObject):
'replaces': [ 'replaces': [
'(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)', '(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)',
'(targetfilter = "(!(memberOf=cn=admins,cn=groups,cn=accounts,$SUFFIX))")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)', '(targetfilter = "(!(memberOf=cn=admins,cn=groups,cn=accounts,$SUFFIX))")(target = "ldap:///uid=*,cn=users,cn=accounts,$SUFFIX")(targetattr = "userpassword || krbprincipalkey || sambalmpassword || sambantpassword || passwordhistory")(version 3.0;acl "permission:Change a user password";allow (write) groupdn = "ldap:///cn=Change a user password,cn=permissions,cn=pbac,$SUFFIX";)',
'(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Windows PassSync service can write passwords"; allow (write) userdn="ldap:///uid=passsync,cn=sysaccounts,cn=etc,$SUFFIX";)',
], ],
'default_privileges': { 'default_privileges': {
'User Administrators', 'User Administrators',
'Modify Users and Reset passwords', 'Modify Users and Reset passwords',
'PassSync Service',
}, },
}, },
'System: Manage User SSH Public Keys': { 'System: Manage User SSH Public Keys': {
@ -446,6 +448,16 @@ class user(LDAPObject):
'homedirectory', 'loginshell', 'homedirectory', 'loginshell',
}, },
}, },
'System: Read User NT Attributes': {
'ipapermbindruletype': 'permission',
'ipapermright': {'read', 'search', 'compare'},
'ipapermdefaultattr': {
'ntuserdomainid', 'ntuniqueid', 'ntuseracctexpires',
'ntusercodepage', 'ntuserdeleteaccount', 'ntuserlastlogoff',
'ntuserlastlogon',
},
'default_privileges': {'PassSync Service'},
},
} }
label = _('Users') label = _('Users')

View File

@ -14,6 +14,7 @@ app_PYTHON = \
update_referint.py \ update_referint.py \
ca_renewal_master.py \ ca_renewal_master.py \
update_uniqueness.py \ update_uniqueness.py \
update_passsync.py \
$(NULL) $(NULL)
EXTRA_DIST = \ EXTRA_DIST = \

View File

@ -0,0 +1,78 @@
#
# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
#
from ipaserver.install.plugins import MIDDLE, LAST
from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
from ipalib import api, errors
from ipapython.dn import DN
from ipapython.ipa_log_manager import root_logger
from ipaserver.install import sysupgrade
class update_passync_privilege_check(PreUpdate):
order = MIDDLE
def execute(self, **options):
update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
if update_done:
root_logger.debug("PassSync privilege update pre-check not needed")
return False, False, []
root_logger.debug("Check if there is existing PassSync privilege")
passsync_privilege_dn = DN(('cn','PassSync Service'),
self.api.env.container_privilege,
self.api.env.basedn)
ldap = self.obj.backend
try:
ldap.get_entry(passsync_privilege_dn, [''])
except errors.NotFound:
root_logger.debug("PassSync privilege not found, this is a new update")
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', False)
else:
root_logger.debug("PassSync privilege found, skip updating PassSync")
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
return False, False, []
api.register(update_passync_privilege_check)
class update_passync_privilege_update(PostUpdate):
"""
Add PassSync user as a member of PassSync privilege, if it exists
"""
order = LAST
def execute(self, **options):
update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
if update_done:
root_logger.debug("PassSync privilege update not needed")
return False, False, []
root_logger.debug("Add PassSync user as a member of PassSync privilege")
ldap = self.obj.backend
passsync_dn = DN(('uid','passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'),
api.env.basedn)
passsync_privilege_dn = DN(('cn','PassSync Service'),
self.api.env.container_privilege,
self.api.env.basedn)
try:
entry = ldap.get_entry(passsync_dn, [''])
except errors.NotFound:
root_logger.debug("PassSync user not found, no update needed")
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
return False, False, []
else:
root_logger.debug("PassSync user found, do update")
update = {'dn': passsync_privilege_dn,
'updates': ["add:member:'%s'" % passsync_dn]}
updates = {passsync_privilege_dn: update}
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
return (False, True, [updates])
api.register(update_passync_privilege_update)

View File

@ -528,39 +528,43 @@ class ReplicationManager(object):
print "The user for the Windows PassSync service is %s" % pass_dn print "The user for the Windows PassSync service is %s" % pass_dn
try: try:
conn.get_entry(pass_dn) conn.get_entry(pass_dn)
print "Windows PassSync entry exists, not resetting password" print "Windows PassSync system account exists, not resetting password"
return
except errors.NotFound: except errors.NotFound:
pass # The user doesn't exist, add it
print "Adding Windows PassSync system account"
entry = conn.make_entry(
pass_dn,
objectclass=["account", "simplesecurityobject"],
uid=["passsync"],
userPassword=[password],
)
conn.add_entry(entry)
# The user doesn't exist, add it # Add the user to the list of users allowed to bypass password policy
entry = conn.make_entry(
pass_dn,
objectclass=["account", "simplesecurityobject"],
uid=["passsync"],
userPassword=[password],
)
conn.add_entry(entry)
# Add it to the list of users allowed to bypass password policy
extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config')) extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config'))
entry = conn.get_entry(extop_dn) entry = conn.get_entry(extop_dn)
pass_mgrs = entry.get('passSyncManagersDNs') pass_mgrs = entry.get('passSyncManagersDNs', [])
if not pass_mgrs:
pass_mgrs = []
if not isinstance(pass_mgrs, list):
pass_mgrs = [pass_mgrs]
pass_mgrs.append(pass_dn) pass_mgrs.append(pass_dn)
mod = [(ldap.MOD_REPLACE, 'passSyncManagersDNs', pass_mgrs)] mod = [(ldap.MOD_REPLACE, 'passSyncManagersDNs', pass_mgrs)]
conn.modify_s(extop_dn, mod)
# And finally grant it permission to write passwords
mod = [(ldap.MOD_ADD, 'aci',
['(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Windows PassSync service can write passwords"; allow (write) userdn="ldap:///%s";)' % pass_dn])]
try: try:
conn.modify_s(self.suffix, mod) conn.modify_s(extop_dn, mod)
except ldap.TYPE_OR_VALUE_EXISTS: except ldap.TYPE_OR_VALUE_EXISTS:
root_logger.debug("passsync aci already exists in suffix %s on %s" % (self.suffix, conn.host)) root_logger.debug("Plugin '%s' already '%s' in passSyncManagersDNs",
extop_dn, pass_dn)
# And finally add it is a member of PassSync privilege to allow
# displaying user NT attributes and reset passwords
passsync_privilege_dn = DN(('cn','PassSync Service'),
api.env.container_privilege,
api.env.basedn)
members = entry.get('member', [])
members.append(pass_dn)
mod = [(ldap.MOD_REPLACE, 'member', members)]
try:
conn.modify_s(passsync_privilege_dn, mod)
except ldap.TYPE_OR_VALUE_EXISTS:
root_logger.debug("PassSync service '%s' already have '%s' as member",
passsync_privilege_dn, pass_dn)
def setup_winsync_agmt(self, entry, win_subtree=None): def setup_winsync_agmt(self, entry, win_subtree=None):
if win_subtree is None: if win_subtree is None: