|
|
e3ffab |
From 3998814d3abb5143f06479d2dbf93bf28285a66e Mon Sep 17 00:00:00 2001
|
|
|
e3ffab |
From: Martin Kosek <mkosek@redhat.com>
|
|
|
e3ffab |
Date: Tue, 13 Jan 2015 18:09:17 +0100
|
|
|
e3ffab |
Subject: [PATCH] Allow PassSync user to locate and update NT users
|
|
|
e3ffab |
|
|
|
e3ffab |
Add new PassSync Service privilege that have sufficient access to
|
|
|
e3ffab |
let AD PassSync service search for NT users and update the password.
|
|
|
e3ffab |
To make sure existing PassSync user keeps working, it is added as
|
|
|
e3ffab |
a member of the new privilege.
|
|
|
e3ffab |
|
|
|
e3ffab |
New update plugin is added to add link to the new privilege to the
|
|
|
e3ffab |
potentially existing PassSync user to avoid breaking the PassSync
|
|
|
e3ffab |
service.
|
|
|
e3ffab |
|
|
|
e3ffab |
https://fedorahosted.org/freeipa/ticket/4837
|
|
|
e3ffab |
|
|
|
e3ffab |
Reviewed-By: David Kupka <dkupka@redhat.com>
|
|
|
e3ffab |
---
|
|
|
e3ffab |
ACI.txt | 2 +
|
|
|
e3ffab |
install/updates/40-delegation.update | 30 +++++++++++
|
|
|
e3ffab |
ipalib/plugins/user.py | 12 +++++
|
|
|
e3ffab |
ipaserver/install/plugins/Makefile.am | 1 +
|
|
|
e3ffab |
ipaserver/install/plugins/update_passsync.py | 78 ++++++++++++++++++++++++++++
|
|
|
e3ffab |
ipaserver/install/replication.py | 54 ++++++++++---------
|
|
|
e3ffab |
6 files changed, 152 insertions(+), 25 deletions(-)
|
|
|
e3ffab |
create mode 100644 ipaserver/install/plugins/update_passsync.py
|
|
|
e3ffab |
|
|
|
e3ffab |
diff --git a/ACI.txt b/ACI.txt
|
|
|
e3ffab |
index 6680f658ee1aa0f961b2681f700557ce6b9238f8..fe45d063e7d48c487e380ca3568b0f9368762c6d 100644
|
|
|
e3ffab |
--- a/ACI.txt
|
|
|
e3ffab |
+++ b/ACI.txt
|
|
|
e3ffab |
@@ -267,6 +267,8 @@ aci: (targetattr = "krblastadminunlock || krblastfailedauth || krblastpwdchange
|
|
|
e3ffab |
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
|
|
e3ffab |
aci: (targetattr = "memberof")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Membership";allow (compare,read,search) userdn = "ldap:///all";)
|
|
|
e3ffab |
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
|
|
e3ffab |
+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";)
|
|
|
e3ffab |
+dn: cn=users,cn=accounts,dc=ipa,dc=example
|
|
|
e3ffab |
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";)
|
|
|
e3ffab |
dn: dc=ipa,dc=example
|
|
|
e3ffab |
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";)
|
|
|
e3ffab |
diff --git a/install/updates/40-delegation.update b/install/updates/40-delegation.update
|
|
|
e3ffab |
index 988de5e1962fabc6787f5914522b8f133e71a8ff..a79f906ea3e29b8b6755a62ac84d318d6abdd6cc 100644
|
|
|
e3ffab |
--- a/install/updates/40-delegation.update
|
|
|
e3ffab |
+++ b/install/updates/40-delegation.update
|
|
|
e3ffab |
@@ -184,3 +184,33 @@ default:description: Read list of IPA masters
|
|
|
e3ffab |
dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
|
|
|
e3ffab |
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";)'
|
|
|
e3ffab |
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";)'
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+# PassSync
|
|
|
e3ffab |
+dn: cn=PassSync Service,cn=privileges,cn=pbac,$SUFFIX
|
|
|
e3ffab |
+default:objectClass: nestedgroup
|
|
|
e3ffab |
+default:objectClass: groupofnames
|
|
|
e3ffab |
+default:objectClass: top
|
|
|
e3ffab |
+default:cn: PassSync Service
|
|
|
e3ffab |
+default:description: PassSync Service
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+dn: cn=Read PassSync Managers Configuration,cn=permissions,cn=pbac,$SUFFIX
|
|
|
e3ffab |
+default:objectClass: groupofnames
|
|
|
e3ffab |
+default:objectClass: ipapermission
|
|
|
e3ffab |
+default:objectClass: top
|
|
|
e3ffab |
+default:cn: Read PassSync Managers Configuration
|
|
|
e3ffab |
+default:member: cn=Replication Administrators,cn=privileges,cn=pbac,$SUFFIX
|
|
|
e3ffab |
+default:ipapermissiontype: SYSTEM
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+dn: cn=config
|
|
|
e3ffab |
+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";)'
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+dn: cn=Modify PassSync Managers Configuration,cn=permissions,cn=pbac,$SUFFIX
|
|
|
e3ffab |
+default:objectClass: groupofnames
|
|
|
e3ffab |
+default:objectClass: ipapermission
|
|
|
e3ffab |
+default:objectClass: top
|
|
|
e3ffab |
+default:cn: Modify PassSync Managers Configuration
|
|
|
e3ffab |
+default:member: cn=Replication Administrators,cn=privileges,cn=pbac,$SUFFIX
|
|
|
e3ffab |
+default:ipapermissiontype: SYSTEM
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+dn: cn=config
|
|
|
e3ffab |
+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";)'
|
|
|
e3ffab |
diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py
|
|
|
e3ffab |
index e206289248dfe9ae79bd87271ff2c7672fb98b4f..56585b9f86593c0c5879139103bc71707b88e15f 100644
|
|
|
e3ffab |
--- a/ipalib/plugins/user.py
|
|
|
e3ffab |
+++ b/ipalib/plugins/user.py
|
|
|
e3ffab |
@@ -373,10 +373,12 @@ class user(LDAPObject):
|
|
|
e3ffab |
'replaces': [
|
|
|
e3ffab |
'(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";)',
|
|
|
e3ffab |
'(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";)',
|
|
|
e3ffab |
+ '(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";)',
|
|
|
e3ffab |
],
|
|
|
e3ffab |
'default_privileges': {
|
|
|
e3ffab |
'User Administrators',
|
|
|
e3ffab |
'Modify Users and Reset passwords',
|
|
|
e3ffab |
+ 'PassSync Service',
|
|
|
e3ffab |
},
|
|
|
e3ffab |
},
|
|
|
e3ffab |
'System: Manage User SSH Public Keys': {
|
|
|
e3ffab |
@@ -446,6 +448,16 @@ class user(LDAPObject):
|
|
|
e3ffab |
'homedirectory', 'loginshell',
|
|
|
e3ffab |
},
|
|
|
e3ffab |
},
|
|
|
e3ffab |
+ 'System: Read User NT Attributes': {
|
|
|
e3ffab |
+ 'ipapermbindruletype': 'permission',
|
|
|
e3ffab |
+ 'ipapermright': {'read', 'search', 'compare'},
|
|
|
e3ffab |
+ 'ipapermdefaultattr': {
|
|
|
e3ffab |
+ 'ntuserdomainid', 'ntuniqueid', 'ntuseracctexpires',
|
|
|
e3ffab |
+ 'ntusercodepage', 'ntuserdeleteaccount', 'ntuserlastlogoff',
|
|
|
e3ffab |
+ 'ntuserlastlogon',
|
|
|
e3ffab |
+ },
|
|
|
e3ffab |
+ 'default_privileges': {'PassSync Service'},
|
|
|
e3ffab |
+ },
|
|
|
e3ffab |
}
|
|
|
e3ffab |
|
|
|
e3ffab |
label = _('Users')
|
|
|
e3ffab |
diff --git a/ipaserver/install/plugins/Makefile.am b/ipaserver/install/plugins/Makefile.am
|
|
|
e3ffab |
index d651297ac141b0f05831e7fabbb9b561cdd239c7..ead1d8f7d972c1b016bac8f2b8f7fd1f9a71b563 100644
|
|
|
e3ffab |
--- a/ipaserver/install/plugins/Makefile.am
|
|
|
e3ffab |
+++ b/ipaserver/install/plugins/Makefile.am
|
|
|
e3ffab |
@@ -14,6 +14,7 @@ app_PYTHON = \
|
|
|
e3ffab |
update_referint.py \
|
|
|
e3ffab |
ca_renewal_master.py \
|
|
|
e3ffab |
update_uniqueness.py \
|
|
|
e3ffab |
+ update_passsync.py \
|
|
|
e3ffab |
$(NULL)
|
|
|
e3ffab |
|
|
|
e3ffab |
EXTRA_DIST = \
|
|
|
e3ffab |
diff --git a/ipaserver/install/plugins/update_passsync.py b/ipaserver/install/plugins/update_passsync.py
|
|
|
e3ffab |
new file mode 100644
|
|
|
e3ffab |
index 0000000000000000000000000000000000000000..d6595a06f4deb62b853d716012a8c594c6a76451
|
|
|
e3ffab |
--- /dev/null
|
|
|
e3ffab |
+++ b/ipaserver/install/plugins/update_passsync.py
|
|
|
e3ffab |
@@ -0,0 +1,78 @@
|
|
|
e3ffab |
+#
|
|
|
e3ffab |
+# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
|
|
|
e3ffab |
+#
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+from ipaserver.install.plugins import MIDDLE, LAST
|
|
|
e3ffab |
+from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
|
|
|
e3ffab |
+from ipalib import api, errors
|
|
|
e3ffab |
+from ipapython.dn import DN
|
|
|
e3ffab |
+from ipapython.ipa_log_manager import root_logger
|
|
|
e3ffab |
+from ipaserver.install import sysupgrade
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+class update_passync_privilege_check(PreUpdate):
|
|
|
e3ffab |
+ order = MIDDLE
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ def execute(self, **options):
|
|
|
e3ffab |
+ update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
|
|
|
e3ffab |
+ if update_done:
|
|
|
e3ffab |
+ root_logger.debug("PassSync privilege update pre-check not needed")
|
|
|
e3ffab |
+ return False, False, []
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ root_logger.debug("Check if there is existing PassSync privilege")
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ passsync_privilege_dn = DN(('cn','PassSync Service'),
|
|
|
e3ffab |
+ self.api.env.container_privilege,
|
|
|
e3ffab |
+ self.api.env.basedn)
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ ldap = self.obj.backend
|
|
|
e3ffab |
+ try:
|
|
|
e3ffab |
+ ldap.get_entry(passsync_privilege_dn, [''])
|
|
|
e3ffab |
+ except errors.NotFound:
|
|
|
e3ffab |
+ root_logger.debug("PassSync privilege not found, this is a new update")
|
|
|
e3ffab |
+ sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', False)
|
|
|
e3ffab |
+ else:
|
|
|
e3ffab |
+ root_logger.debug("PassSync privilege found, skip updating PassSync")
|
|
|
e3ffab |
+ sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ return False, False, []
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+api.register(update_passync_privilege_check)
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+class update_passync_privilege_update(PostUpdate):
|
|
|
e3ffab |
+ """
|
|
|
e3ffab |
+ Add PassSync user as a member of PassSync privilege, if it exists
|
|
|
e3ffab |
+ """
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ order = LAST
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ def execute(self, **options):
|
|
|
e3ffab |
+ update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
|
|
|
e3ffab |
+ if update_done:
|
|
|
e3ffab |
+ root_logger.debug("PassSync privilege update not needed")
|
|
|
e3ffab |
+ return False, False, []
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ root_logger.debug("Add PassSync user as a member of PassSync privilege")
|
|
|
e3ffab |
+ ldap = self.obj.backend
|
|
|
e3ffab |
+ passsync_dn = DN(('uid','passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'),
|
|
|
e3ffab |
+ api.env.basedn)
|
|
|
e3ffab |
+ passsync_privilege_dn = DN(('cn','PassSync Service'),
|
|
|
e3ffab |
+ self.api.env.container_privilege,
|
|
|
e3ffab |
+ self.api.env.basedn)
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ try:
|
|
|
e3ffab |
+ entry = ldap.get_entry(passsync_dn, [''])
|
|
|
e3ffab |
+ except errors.NotFound:
|
|
|
e3ffab |
+ root_logger.debug("PassSync user not found, no update needed")
|
|
|
e3ffab |
+ sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
|
|
|
e3ffab |
+ return False, False, []
|
|
|
e3ffab |
+ else:
|
|
|
e3ffab |
+ root_logger.debug("PassSync user found, do update")
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ update = {'dn': passsync_privilege_dn,
|
|
|
e3ffab |
+ 'updates': ["add:member:'%s'" % passsync_dn]}
|
|
|
e3ffab |
+ updates = {passsync_privilege_dn: update}
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
|
|
|
e3ffab |
+ return (False, True, [updates])
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+api.register(update_passync_privilege_update)
|
|
|
e3ffab |
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
|
|
|
e3ffab |
index 5778cab036ad87ccb5b69254aa307a6bc8dec871..66764c22f69328942fe2e4581cfafb3806438d7c 100644
|
|
|
e3ffab |
--- a/ipaserver/install/replication.py
|
|
|
e3ffab |
+++ b/ipaserver/install/replication.py
|
|
|
e3ffab |
@@ -528,39 +528,43 @@ class ReplicationManager(object):
|
|
|
e3ffab |
print "The user for the Windows PassSync service is %s" % pass_dn
|
|
|
e3ffab |
try:
|
|
|
e3ffab |
conn.get_entry(pass_dn)
|
|
|
e3ffab |
- print "Windows PassSync entry exists, not resetting password"
|
|
|
e3ffab |
- return
|
|
|
e3ffab |
+ print "Windows PassSync system account exists, not resetting password"
|
|
|
e3ffab |
except errors.NotFound:
|
|
|
e3ffab |
- pass
|
|
|
e3ffab |
-
|
|
|
e3ffab |
- # The user doesn't exist, add it
|
|
|
e3ffab |
- entry = conn.make_entry(
|
|
|
e3ffab |
- pass_dn,
|
|
|
e3ffab |
- objectclass=["account", "simplesecurityobject"],
|
|
|
e3ffab |
- uid=["passsync"],
|
|
|
e3ffab |
- userPassword=[password],
|
|
|
e3ffab |
- )
|
|
|
e3ffab |
- conn.add_entry(entry)
|
|
|
e3ffab |
+ # The user doesn't exist, add it
|
|
|
e3ffab |
+ print "Adding Windows PassSync system account"
|
|
|
e3ffab |
+ entry = conn.make_entry(
|
|
|
e3ffab |
+ pass_dn,
|
|
|
e3ffab |
+ objectclass=["account", "simplesecurityobject"],
|
|
|
e3ffab |
+ uid=["passsync"],
|
|
|
e3ffab |
+ userPassword=[password],
|
|
|
e3ffab |
+ )
|
|
|
e3ffab |
+ conn.add_entry(entry)
|
|
|
e3ffab |
|
|
|
e3ffab |
- # Add it to the list of users allowed to bypass password policy
|
|
|
e3ffab |
+ # Add the user to the list of users allowed to bypass password policy
|
|
|
e3ffab |
extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config'))
|
|
|
e3ffab |
entry = conn.get_entry(extop_dn)
|
|
|
e3ffab |
- pass_mgrs = entry.get('passSyncManagersDNs')
|
|
|
e3ffab |
- if not pass_mgrs:
|
|
|
e3ffab |
- pass_mgrs = []
|
|
|
e3ffab |
- if not isinstance(pass_mgrs, list):
|
|
|
e3ffab |
- pass_mgrs = [pass_mgrs]
|
|
|
e3ffab |
+ pass_mgrs = entry.get('passSyncManagersDNs', [])
|
|
|
e3ffab |
pass_mgrs.append(pass_dn)
|
|
|
e3ffab |
mod = [(ldap.MOD_REPLACE, 'passSyncManagersDNs', pass_mgrs)]
|
|
|
e3ffab |
- conn.modify_s(extop_dn, mod)
|
|
|
e3ffab |
-
|
|
|
e3ffab |
- # And finally grant it permission to write passwords
|
|
|
e3ffab |
- mod = [(ldap.MOD_ADD, 'aci',
|
|
|
e3ffab |
- ['(targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Windows PassSync service can write passwords"; allow (write) userdn="ldap:///%s";)' % pass_dn])]
|
|
|
e3ffab |
try:
|
|
|
e3ffab |
- conn.modify_s(self.suffix, mod)
|
|
|
e3ffab |
+ conn.modify_s(extop_dn, mod)
|
|
|
e3ffab |
+ except ldap.TYPE_OR_VALUE_EXISTS:
|
|
|
e3ffab |
+ root_logger.debug("Plugin '%s' already '%s' in passSyncManagersDNs",
|
|
|
e3ffab |
+ extop_dn, pass_dn)
|
|
|
e3ffab |
+
|
|
|
e3ffab |
+ # And finally add it is a member of PassSync privilege to allow
|
|
|
e3ffab |
+ # displaying user NT attributes and reset passwords
|
|
|
e3ffab |
+ passsync_privilege_dn = DN(('cn','PassSync Service'),
|
|
|
e3ffab |
+ api.env.container_privilege,
|
|
|
e3ffab |
+ api.env.basedn)
|
|
|
e3ffab |
+ members = entry.get('member', [])
|
|
|
e3ffab |
+ members.append(pass_dn)
|
|
|
e3ffab |
+ mod = [(ldap.MOD_REPLACE, 'member', members)]
|
|
|
e3ffab |
+ try:
|
|
|
e3ffab |
+ conn.modify_s(passsync_privilege_dn, mod)
|
|
|
e3ffab |
except ldap.TYPE_OR_VALUE_EXISTS:
|
|
|
e3ffab |
- root_logger.debug("passsync aci already exists in suffix %s on %s" % (self.suffix, conn.host))
|
|
|
e3ffab |
+ root_logger.debug("PassSync service '%s' already have '%s' as member",
|
|
|
e3ffab |
+ passsync_privilege_dn, pass_dn)
|
|
|
e3ffab |
|
|
|
e3ffab |
def setup_winsync_agmt(self, entry, win_subtree=None):
|
|
|
e3ffab |
if win_subtree is None:
|
|
|
e3ffab |
--
|
|
|
e3ffab |
2.1.0
|
|
|
e3ffab |
|