[Freeipa-devel] [PATCH] 0042-0048 AD trusts support (master)

Alexander Bokovoy abokovoy at redhat.com
Tue Apr 3 10:41:35 UTC 2012


Hi!

Attached are the current patches for adding support for Active Directory
trusts for FreeIPA v3 (master).

These are tested and working with samba4 build available in ipa-devel@
repo. You have to use --delegate until we'll get all the parts of the
Heimdal puzzle untangled and solved, and Simo patch 490 (s4u2proxy fix)
is committed as well.

Sumit asked me to send patches for review and commit to master so that
he can proceed with his changes (removal of kadmin.local use, SID
population task for 389-ds, etc). Without kadmin.local use fix these
patches are not working with SELinux enabled.

Patches have [../9] mark because they were generated out of my adwork
tree. I have merged two patches together for obvious change reason and
have left out Simo's s4u2proxy patch out, thus there are seven patches
proposed for commit.

-- 
/ Alexander Bokovoy
-------------- next part --------------
>From 7a769c59c9de3ac27577557ae66f5ae874ec167f Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 28 Feb 2012 13:22:49 +0200
Subject: [PATCH 1/9] Add separate attribute to store trusted domain SID

We need two attributes in the ipaNTTrustedDomain objectclass to store different
kind of SID. Currently ipaNTSecurityIdentifier is used to store the Domain-SID
of the trusted domain. A second attribute is needed to store the SID for the
trusted domain user. Since it cannot be derived safely from other values and
since it does not make sense to create a separate object for the user a new
attribute is needed.

https://fedorahosted.org/freeipa/ticket/2191
---
 daemons/ipa-sam/ipa_sam.c                  |    9 +++++----
 install/share/60basev3.ldif                |    3 ++-
 install/share/bootstrap-template.ldif      |    8 ++++++++
 install/share/replica-s4u2proxy.ldif       |    8 ++++++++
 install/updates/60-trusts.update           |   26 ++++++++++++++++++++++++++
 install/updates/61-trusts-s4u2proxy.update |   12 ++++++++++++
 install/updates/Makefile.am                |    2 ++
 7 files changed, 63 insertions(+), 5 deletions(-)
 create mode 100644 install/updates/60-trusts.update
 create mode 100644 install/updates/61-trusts-s4u2proxy.update

diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c
index be97cb7..c362988 100644
--- a/daemons/ipa-sam/ipa_sam.c
+++ b/daemons/ipa-sam/ipa_sam.c
@@ -123,6 +123,7 @@ do { \
 #define LDAP_PAGE_SIZE 1024
 #define LDAP_OBJ_SAMBASAMACCOUNT "ipaNTUserAttrs"
 #define LDAP_OBJ_TRUSTED_DOMAIN "ipaNTTrustedDomain"
+#define LDAP_ATTRIBUTE_TRUST_SID "ipaNTTrustedDomainSID"
 #define LDAP_ATTRIBUTE_SID "ipaNTSecurityIdentifier"
 #define LDAP_OBJ_GROUPMAP "ipaNTGroupAttrs"
 
@@ -1674,7 +1675,7 @@ static bool get_trusted_domain_by_sid_int(struct ldapsam_privates *ldap_state,
 
 	filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(%s=%s))",
 				 LDAP_OBJ_TRUSTED_DOMAIN,
-				 LDAP_ATTRIBUTE_SECURITY_IDENTIFIER, sid);
+				 LDAP_ATTRIBUTE_TRUST_SID, sid);
 	if (filter == NULL) {
 		return false;
 	}
@@ -1734,10 +1735,10 @@ static bool fill_pdb_trusted_domain(TALLOC_CTX *mem_ctx,
 	/* All attributes are MAY */
 
 	dummy = get_single_attribute(NULL, priv2ld(ldap_state), entry,
-				     LDAP_ATTRIBUTE_SECURITY_IDENTIFIER);
+				     LDAP_ATTRIBUTE_TRUST_SID);
 	if (dummy == NULL) {
 		DEBUG(9, ("Attribute %s not present.\n",
-			  LDAP_ATTRIBUTE_SECURITY_IDENTIFIER));
+			  LDAP_ATTRIBUTE_TRUST_SID));
 		ZERO_STRUCT(td->security_identifier);
 	} else {
 		res = string_to_sid(&td->security_identifier, dummy);
@@ -2021,7 +2022,7 @@ static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods,
 
 	if (!is_null_sid(&td->security_identifier)) {
 		smbldap_make_mod(priv2ld(ldap_state), entry, &mods,
-				 LDAP_ATTRIBUTE_SECURITY_IDENTIFIER,
+				 LDAP_ATTRIBUTE_TRUST_SID,
 				 sid_string_talloc(tmp_ctx, &td->security_identifier));
 	}
 
diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index 40412b5..2c24137 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -6,6 +6,7 @@
 dn: cn=schema
 attributeTypes: (2.16.840.1.113730.3.8.11.1 NAME 'ipaExternalMember' DESC 'External Group Member Identifier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.2 NAME 'ipaNTSecurityIdentifier' DESC 'NT Security ID' EQUALITY caseIgnoreIA5Match OREDRING caseIgnoreIA5OrderingMatch SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+attributeTypes: (2.16.840.1.113730.3.8.11.23 NAME 'ipaNTTrustedDomainSID' DESC 'NT Trusted Domain Security ID' EQUALITY caseIgnoreIA5Match OREDRING caseIgnoreIA5OrderingMatch SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.3 NAME 'ipaNTFlatName' DESC 'Flat/Netbios Name' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.4 NAME 'ipaNTFallbackPrimaryGroup' DESC 'Fallback Group to set the Primary group Security Identifier for users with UPGs' SUP distinguishedName X-ORIGIN 'IPA v3' )
 attributeTypes: (2.16.840.1.113730.3.8.11.5 NAME 'ipaNTHash' DESC 'NT Hash of user password' EQUALITY octetStringMatch OREDRING octetStringOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE X-ORIGIN 'IPA v3' )
@@ -32,7 +33,7 @@ objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUC
 objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.4 NAME 'ipaNTDomainAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier $ ipaNTFlatName $ ipaNTDomainGUID ) MAY ( ipaNTFallbackPrimaryGroup ) X-ORIGIN 'IPA v3' )
-objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $ ipaNTTrustAttributes $ ipaNTTrustDirection $ ipaNTTrustPartner $ ipaNTFlatName $ ipaNTTrustAuthOutgoing $ ipaNTTrustAuthIncoming $ ipaNTSecurityIdentifier $ ipaNTTrustForestTrustInfo $ ipaNTTrustPosixOffset $ ipaNTSupportedEncryptionTypes) )
+objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $ ipaNTTrustAttributes $ ipaNTTrustDirection $ ipaNTTrustPartner $ ipaNTFlatName $ ipaNTTrustAuthOutgoing $ ipaNTTrustAuthIncoming $ ipaNTTrustedDomainSID $ ipaNTTrustForestTrustInfo $ ipaNTTrustPosixOffset $ ipaNTSupportedEncryptionTypes) )
 objectClasses: (2.16.840.1.113730.3.8.12.6 NAME 'groupOfPrincipals' SUP top AUXILIARY MUST ( cn ) MAY ( memberPrincipal ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.7 NAME 'ipaKrb5DelegationACL' SUP groupOfPrincipals STRUCTURAL MAY ( ipaAllowToImpersonate $ ipaAllowedTarget ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.10 NAME 'ipaSELinuxUserMap' SUP ipaAssociation STRUCTURAL MUST ipaSELinuxUser MAY ( accessTime $ seeAlso ) X-ORIGIN 'IPA v3')
diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif
index 468a43b..149b6c9 100644
--- a/install/share/bootstrap-template.ldif
+++ b/install/share/bootstrap-template.ldif
@@ -175,6 +175,7 @@ objectClass: top
 cn: ipa-http-delegation
 memberPrincipal: HTTP/$HOST@$REALM
 ipaAllowedTarget: cn=ipa-ldap-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
+ipaAllowedTarget: cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
 
 dn: cn=ipa-ldap-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
 changetype: add
@@ -183,6 +184,13 @@ objectClass: top
 cn: ipa-ldap-delegation-targets
 memberPrincipal: ldap/$HOST@$REALM
 
+dn: cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
+changetype: add
+objectClass: groupOfPrincipals
+objectClass: top
+cn: ipa-cifs-delegation-targets
+memberPrincipal: cifs/$HOST@$REALM
+
 dn: uid=admin,cn=users,cn=accounts,$SUFFIX
 changetype: add
 objectClass: top
diff --git a/install/share/replica-s4u2proxy.ldif b/install/share/replica-s4u2proxy.ldif
index 3cafa46..55c984a 100644
--- a/install/share/replica-s4u2proxy.ldif
+++ b/install/share/replica-s4u2proxy.ldif
@@ -2,8 +2,16 @@ dn: cn=ipa-http-delegation,cn=s4u2proxy,cn=etc,$SUFFIX
 changetype: modify
 add: memberPrincipal
 memberPrincipal: HTTP/$FQDN@$REALM
+add: ipaAllowedTarget
+ipaAllowedTarget: cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
 
 dn: cn=ipa-ldap-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
 changetype: modify
 add: memberPrincipal
 memberPrincipal: ldap/$FQDN@$REALM
+
+dn: cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
+changetype: modify
+add: memberPrincipal
+memberPrincipal: cifs/$FQDN@$REALM
+
diff --git a/install/updates/60-trusts.update b/install/updates/60-trusts.update
new file mode 100644
index 0000000..9a320fc
--- /dev/null
+++ b/install/updates/60-trusts.update
@@ -0,0 +1,26 @@
+dn: cn=schema
+add:attributeTypes: (2.16.840.1.113730.3.8.11.2 NAME 'ipaNTSecurityIdentifier' DESC 'NT Security ID' EQUALITY caseIgnoreIA5Match OREDRING caseIgnoreIA5OrderingMatch SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.23 NAME 'ipaNTTrustedDomainSID' DESC 'NT Trusted Domain Security ID' EQUALITY caseIgnoreIA5Match OREDRING caseIgnoreIA5OrderingMatch SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.3 NAME 'ipaNTFlatName' DESC 'Flat/Netbios Name' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.4 NAME 'ipaNTFallbackPrimaryGroup' DESC 'Fallback Group to set the Primary group Security Identifier for users with UPGs' SUP distinguishedName X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.5 NAME 'ipaNTHash' DESC 'NT Hash of user password' EQUALITY octetStringMatch OREDRING octetStringOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.6 NAME 'ipaNTLogonScript' DESC 'User Logon Script Name' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.7 NAME 'ipaNTProfilePath' DESC 'User Profile Path' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.8 NAME 'ipaNTHomeDirectory' DESC 'User Home Directory Path' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.9 NAME 'ipaNTHomeDirectoryDrive' DESC 'User Home Drive Letter' EQUALITY caseIgnoreMatch OREDRING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.10 NAME 'ipaNTDomainGUID' DESC 'NT Domain GUID' EQUALITY caseIgnoreIA5Match OREDRING caseIgnoreIA5OrderingMatch SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.11 NAME 'ipaNTTrustType' DESC 'Type of trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.12 NAME 'ipaNTTrustAttributes' DESC 'Trust attributes for a trusted domain' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.13 NAME 'ipaNTTrustDirection' DESC 'Direction of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.14 NAME 'ipaNTTrustPartner' DESC 'Fully qualified name of the domain with which a trust exists' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.15 NAME 'ipaNTTrustAuthOutgoing' DESC 'Authentication information for the outgoing portion of a trust' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.16 NAME 'ipaNTTrustAuthIncoming' DESC 'Authentication information for the incoming portion of a trust' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.17 NAME 'ipaNTTrustForestTrustInfo' DESC 'Forest trust information for a trusted domain object' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.18 NAME 'ipaNTTrustPosixOffset' DESC 'POSIX offset of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+add:attributeTypes: ( 2.16.840.1.113730.3.8.11.19 NAME 'ipaNTSupportedEncryptionTypes' DESC 'Supported encryption types of a trust' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+add:objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $$ ipaNTLogonScript $$ ipaNTProfilePath $$ ipaNTHomeDirectory $$ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' )
+add:objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' )
+add:objectClasses: (2.16.840.1.113730.3.8.12.4 NAME 'ipaNTDomainAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier $$ ipaNTFlatName $$ ipaNTDomainGUID ) MAY ( ipaNTFallbackPrimaryGroup ) X-ORIGIN 'IPA v3' )
+replace:objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $$ ipaNTTrustAttributes $$ ipaNTTrustDirection $$ ipaNTTrustPartner $$ ipaNTFlatName $$ ipaNTTrustAuthOutgoing $$ ipaNTTrustAuthIncoming $$ ipaNTSecurityIdentifier $$ ipaNTTrustForestTrustInfo $$ ipaNTTrustPosixOffset $$ ipaNTSupportedEncryptionTypes) )::objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $$ ipaNTTrustAttributes $$ ipaNTTrustDirection $$ ipaNTTrustPartner $$ ipaNTFlatName $$ ipaNTTrustAuthOutgoing $$ ipaNTTrustAuthIncoming $$ ipaNTTrustedDomainSID $$ ipaNTTrustForestTrustInfo $$ ipaNTTrustPosixOffset $$ ipaNTSupportedEncryptionTypes) )
+add:objectClasses: (2.16.840.1.113730.3.8.12.5 NAME 'ipaNTTrustedDomain' SUP top STRUCTURAL DESC 'Trusted Domain Object' MUST ( cn ) MAY ( ipaNTTrustType $$ ipaNTTrustAttributes $$ ipaNTTrustDirection $$ ipaNTTrustPartner $$ ipaNTFlatName $$ ipaNTTrustAuthOutgoing $$ ipaNTTrustAuthIncoming $$ ipaNTTrustedDomainSID $$ ipaNTTrustForestTrustInfo $$ ipaNTTrustPosixOffset $$ ipaNTSupportedEncryptionTypes) )
+
diff --git a/install/updates/61-trusts-s4u2proxy.update b/install/updates/61-trusts-s4u2proxy.update
new file mode 100644
index 0000000..4a71148
--- /dev/null
+++ b/install/updates/61-trusts-s4u2proxy.update
@@ -0,0 +1,12 @@
+dn: cn=ipa-http-delegation,cn=s4u2proxy,cn=etc,$SUFFIX
+add: ipaAllowedTarget: 'cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX'
+
+dn: cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
+default: objectClass: groupOfPrincipals
+default: objectClass: top
+default: cn: ipa-cifs-delegation-targets
+default: memberPrincipal: cifs/$FQDN@$REALM
+
+dn: cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
+add: memberPrincipal: cifs/$FQDN@$REALM
+
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
index e1eb35a..412630e 100644
--- a/install/updates/Makefile.am
+++ b/install/updates/Makefile.am
@@ -33,6 +33,8 @@ app_DATA =				\
 	50-nis.update			\
 	50-ipaconfig.update		\
 	55-pbacmemberof.update		\
+	60-trusts.update		\
+	61-trusts-s4u2proxy.update	\
 	$(NULL)
 
 EXTRA_DIST =				\
-- 
1.7.9.3

-------------- next part --------------
>From 0ca47cd69125ed2718ef1b6dca7bf86d35d05170 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 28 Feb 2012 13:23:51 +0200
Subject: [PATCH 2/9] Use dedicated keytab for Samba

Samba just needs the cifs/ key on the ipa server. Configure samba to use a
different keytab file so that we do not risk samba commands (net, or similar)
to mess up the system keytab.

https://fedorahosted.org/freeipa/ticket/2168
---
 install/share/smb.conf.template      |    4 +++-
 ipaserver/install/adtrustinstance.py |   27 ++++++++++++++++-----------
 2 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/install/share/smb.conf.template b/install/share/smb.conf.template
index 4ab79da..8ed521b 100644
--- a/install/share/smb.conf.template
+++ b/install/share/smb.conf.template
@@ -1,7 +1,8 @@
 [global]
 workgroup = $NETBIOS_NAME
 realm = $REALM
-kerberos method = system keytab
+kerberos method = dedicated keytab
+dedicated keytab file = FILE:/etc/samba/samba.keytab
 create krb5 conf = no
 security = user
 domain master = yes
@@ -10,6 +11,7 @@ log level = 1
 max log size = 100000
 log file = /var/log/samba/log.%m
 passdb backend = ipasam:ldapi://$LDAPI_SOCKET
+disable spoolss = yes
 ldapsam:trusted=yes
 ldap ssl = off
 ldap admin dn = $SMB_DN
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
index f437901..b978146 100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -255,7 +255,10 @@ class ADTRUSTInstance(service.Service):
         conf_fd.close()
 
     def __add_cldap_module(self):
-        self._ldap_mod("ipa-cldap-conf.ldif", self.sub_dict)
+        try:
+            self._ldap_mod("ipa-cldap-conf.ldif", self.sub_dict)
+        except:
+            pass
 
     def __write_smb_registry(self):
         template = os.path.join(ipautil.SHARE_DIR, "smb.conf.template")
@@ -279,21 +282,23 @@ class ADTRUSTInstance(service.Service):
 
     def __setup_principal(self):
         cifs_principal = "cifs/" + self.fqdn + "@" + self.realm_name
-        installutils.kadmin_addprinc(cifs_principal)
 
-        self.move_service(cifs_principal)
+        api.Command.service_add(unicode(cifs_principal))
 
-        try:
-            ipautil.run(["ipa-rmkeytab", "--principal", cifs_principal,
-                                         "-k", "/etc/krb5.keytab"])
-        except ipautil.CalledProcessError, e:
-            if e.returncode != 5:
-                root_logger.critical("Failed to remove old key for %s" % cifs_principal)
+        samba_keytab = "/etc/samba/samba.keytab"
+        if os.path.exists(samba_keytab):
+            try:
+                ipautil.run(["ipa-rmkeytab", "--principal", cifs_principal,
+                                         "-k", samba_keytab])
+            except ipautil.CalledProcessError, e:
+                root_logger.critical("Result of removing old key: %d" % e.returncode)
+                if e.returncode != 5:
+                    root_logger.critical("Failed to remove old key for %s" % cifs_principal)
 
         try:
             ipautil.run(["ipa-getkeytab", "--server", self.fqdn,
                                           "--principal", cifs_principal,
-                                          "-k", "/etc/krb5.keytab"])
+                                          "-k", samba_keytab])
         except ipautil.CalledProcessError, e:
             root_logger.critical("Failed to add key for %s" % cifs_principal)
 
@@ -368,7 +373,7 @@ class ADTRUSTInstance(service.Service):
         try:
             self.ldap_enable('ADTRUST', self.fqdn, self.dm_password, \
                              self.suffix)
-        except ldap.ALREADY_EXISTS:
+        except (ldap.ALREADY_EXISTS, errors.DuplicateEntry), e:
             root_logger.critical("ADTRUST Service startup entry already exists.")
             pass
 
-- 
1.7.9.3

-------------- next part --------------
>From 3c4cdcbf2fb55016da044ad41b6d19e8ee081d2c Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 28 Feb 2012 13:24:41 +0200
Subject: [PATCH 3/9] Add trust management for Active Directory trusts

https://fedorahosted.org/freeipa/ticket/1821
---
 API.txt                              |   57 ++++++
 freeipa.spec.in                      |   20 ++-
 install/share/Makefile.am            |    1 +
 install/share/smb.conf.empty         |    2 +
 install/tools/ipa-adtrust-install    |    1 +
 ipalib/constants.py                  |    5 +-
 ipalib/plugins/trust.py              |  220 +++++++++++++++++++++++
 ipaserver/dcerpc.py                  |  325 ++++++++++++++++++++++++++++++++++
 ipaserver/install/adtrustinstance.py |   14 +-
 9 files changed, 638 insertions(+), 7 deletions(-)
 create mode 100644 install/share/smb.conf.empty
 create mode 100644 ipalib/plugins/trust.py
 create mode 100644 ipaserver/dcerpc.py

diff --git a/API.txt b/API.txt
index e9eb1e1..1a6e5a1 100644
--- a/API.txt
+++ b/API.txt
@@ -3066,6 +3066,63 @@ option: Str('version?', exclude='webui')
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('value', <type 'unicode'>, None)
+command: trust_add_ad
+args: 1,7,3
+arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, required=True)
+option: Str('realm_admin?', cli_name='admin')
+option: Password('realm_passwd?', cli_name='password', confirm=False)
+option: Str('realm_server?', cli_name='server')
+option: Password('trust_passwd?', cli_name='trust_password', confirm=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('value', <type 'unicode'>, None)
+command: trust_del
+args: 1,1,3
+arg: Str('cn', attribute=True, cli_name='realm', multivalue=True, primary_key=True, query=True, required=True)
+option: Flag('continue', autofill=True, cli_name='continue', default=False)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('result', <type 'dict'>, None)
+output: Output('value', <type 'unicode'>, None)
+command: trust_find
+args: 1,7,4
+arg: Str('criteria?', noextrawhitespace=False)
+option: Str('cn', attribute=True, autofill=False, cli_name='realm', multivalue=False, primary_key=True, query=True, required=False)
+option: Int('timelimit?', autofill=False, minvalue=0)
+option: Int('sizelimit?', autofill=False, minvalue=0)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+option: Flag('pkey_only?', autofill=True, default=False)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
+output: Output('count', <type 'int'>, None)
+output: Output('truncated', <type 'bool'>, None)
+command: trust_mod
+args: 1,7,3
+arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, query=True, required=True)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('value', <type 'unicode'>, None)
+command: trust_show
+args: 1,4,3
+arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('rights', autofill=True, default=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('value', <type 'unicode'>, None)
 command: user_add
 args: 1,33,3
 arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', pattern_errmsg='may only include letters, numbers, _, -, . and $', primary_key=True, required=True)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index e7eb7d4..7746c98 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -188,6 +188,19 @@ user, virtual machines, groups, authentication credentials), Policy
 (configuration settings, access control information) and Audit (events,
 logs, analysis thereof). This package provides SELinux rules for the
 daemons included in freeipa-server
+
+%package server-trust-ad
+Summary: Virtual package to install packages required for Active Directory trusts
+Group: System Environment/Base
+Requires: %{name}-server = %version-%release
+Requires: python-crypto
+Requires: samba4-python
+Requires: samba4
+
+%description server-trust-ad
+Cross-realm trusts with Active Directory in IPA require working Samba 4 installation.
+This package is provided for convenience to install all required dependencies at once.
+
 %endif
 
 
@@ -278,7 +291,6 @@ user, virtual machines, groups, authentication credentials), Policy
 logs, analysis thereof). If you are using IPA you need to install this
 package.
 
-
 %prep
 %setup -n freeipa-%{version} -q
 
@@ -630,6 +642,9 @@ fi
 %doc COPYING README Contributors.txt
 %{_usr}/share/selinux/targeted/ipa_httpd.pp
 %{_usr}/share/selinux/targeted/ipa_dogtag.pp
+
+%files server-trust-ad
+%{_usr}/share/ipa/smb.conf.empty
 %endif
 
 %files client
@@ -681,6 +696,9 @@ fi
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
 
 %changelog
+* Mon Mar 12 2012 Alexander Bokovoy <abokovoy at redhat.com> - 2.99.0-23
+- Add freeipa-server-trust-ad virtual package to capture all required dependencies
+  for Active Directory trust management
 
 * Tue Mar 27 2012 Rob Crittenden <rcritten at redhat.com> - 2.99.0-26
 - Add python-krbV Requires on client package
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index 243fc2a..81fd0dc 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -33,6 +33,7 @@ app_DATA =				\
 	krbrealm.con.template		\
 	preferences.html.template 	\
 	smb.conf.template		\
+	smb.conf.empty			\
 	referint-conf.ldif		\
 	dna.ldif			\
 	master-entry.ldif		\
diff --git a/install/share/smb.conf.empty b/install/share/smb.conf.empty
new file mode 100644
index 0000000..6d1e626
--- /dev/null
+++ b/install/share/smb.conf.empty
@@ -0,0 +1,2 @@
+[global]
+
diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 248ea35..0f3e473 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -131,6 +131,7 @@ def main():
                 break
 
     # Check we have a public IP that is associated with the hostname
+    ip = None
     try:
         if options.ip_address:
             ip = ipautil.CheckedIPAddress(options.ip_address, match_local=True)
diff --git a/ipalib/constants.py b/ipalib/constants.py
index dc32533..3376d30 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -101,7 +101,10 @@ DEFAULT_CONFIG = (
     ('container_automember', 'cn=automember,cn=etc'),
     ('container_selinux', 'cn=usermap,cn=selinux'),
     ('container_s4u2proxy', 'cn=s4u2proxy,cn=etc'),
-
+    ('container_cifsdomains', 'cn=ad,cn=etc'),
+    ('container_trusts', 'cn=trusts'),
+    ('container_adtrusts', 'cn=ad,cn=trusts'),
+ 
     # Ports, hosts, and URIs:
     # FIXME: let's renamed xmlrpc_uri to rpc_xml_uri
     ('xmlrpc_uri', 'http://localhost:8888/ipa/xml'),
diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
new file mode 100644
index 0000000..08cb79a
--- /dev/null
+++ b/ipalib/plugins/trust.py
@@ -0,0 +1,220 @@
+# Authors:
+#     Alexander Bokovoy <abokovoy at redhat.com>
+#
+# Copyright (C) 2011  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/>.
+
+from ipalib.plugins.baseldap import *
+from ipalib import api, Str, Password, DefaultFrom, _, ngettext, Object
+from ipalib.parameters import Enum
+from ipalib import Command
+from ipalib import errors
+from ipapython import ipautil
+from ipalib import util
+
+__doc__ = _("""
+Manage trust relationship between realms
+""")
+
+trust_output_params = (
+    Str('ipantflatname',
+        label='Domain NetBIOS name'),
+    Str('ipantsecurityidentifier',
+        label='Domain Security Identifier'),
+    Str('trustdirection',
+        label='Trust direction'),
+    Str('trusttype',
+        label='Trust type'),
+)
+
+def trust_type_string(level):
+    return int(level) in (1,2,3) and (u'Forest',u'Cross-Forest',u'MIT')[int(level)-1]
+
+def trust_direction_string(level):
+    return int(level) in (1,2,3) and (u'Downlevel',u'Uplevel',u'Both directions')[int(level)-1]
+
+class trust(LDAPObject):
+    """
+    Trust object.
+    """
+    trust_types = ('ad', 'ipa')
+    container_dn = api.env.container_trusts
+    object_name = _('trust')
+    object_name_plural = _('trusts')
+    object_class = ['ipaNTTrustedDomain']
+    default_attributes = ['cn', 'ipantflatname', 'ipantsecurityidentifier', 
+        'ipanttrusttype', 'ipanttrustattributes', 'ipanttrustdirection', 'ipanttrustpartner',
+        'ipantauthtrustoutgoing', 'ipanttrustauthincoming', 'ipanttrustforesttrustinfo',
+        'ipanttrustposixoffset', 'ipantsupportedencryptiontypes' ]
+
+    label = _('Trusts')
+    label_singular = _('Trust')
+
+    takes_params = (
+        Str('cn',
+            cli_name='realm',
+            label=_('Realm name'),
+            primary_key=True,
+        ),
+    )
+
+def make_trust_dn(env, trust_type, dn):
+    if trust_type in trust.trust_types:
+        container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn)
+        return unicode(DN(DN(dn)[0], container_dn))
+    return dn
+
+class trust_add_ad(LDAPCreate):
+    __doc__ = _('Add new trust to use against Active Directory Services.')
+
+    takes_options = (
+        Str('realm_admin?',
+            cli_name='admin',
+            label=_("Name of the realm's administrator"),
+        ),
+        Password('realm_passwd?',
+            cli_name='password',
+            label=_("Password of the realm's administrator"),
+            confirm=False,
+        ),
+        Str('realm_server?',
+            cli_name='server',
+            label=_('Domain controller for the realm'),
+        ),
+        Password('trust_passwd?',
+            cli_name='trust_password',
+            label=_('Shared password for the trust'),
+            confirm=False,
+        ),
+    )
+
+
+    msg_summary = _('Added Active Directory trust for realm "%(value)s"')
+
+    def execute(self, *keys, **options):
+        # Join domain using full credentials and with random trustdom
+        # secret (will be generated by the join method)
+        trustinstance = None
+        try:
+            import ipaserver.dcerpc
+        except Exception, e:
+            raise errors.NotFound(name=_('AD Trust setup'),
+                  reason=_('Cannot perform join operation without Samba 4 python bindings installed'))
+
+        if 'realm_server' not in options:
+            realm_server = None
+        else:
+            realm_server = options['realm_server']
+
+        trustinstance = ipaserver.dcerpc.TrustDomainJoins(self.api)
+
+        # 1. Full access to the remote domain. Use admin credentials and
+        # generate random trustdom password to do work on both sides
+        if 'realm_admin' in options:
+            realm_admin = options['realm_admin']
+
+            if 'realm_passwd' not in options:
+                raise errors.ValidationError(name=_('AD Trust setup'), reason=_('Realm administrator password should be specified'))
+            realm_passwd = options['realm_passwd']
+
+            result = trustinstance.join_ad_full_credentials(keys[-1], realm_server, realm_admin, realm_passwd)
+
+            if result is None:
+                raise errors.ValidationError(name=_('AD Trust setup'), reason=_('Unable to verify write permissions to the AD'))
+
+            return dict(result=dict(), value=trustinstance.remote_domain.info['dns_domain'])
+
+        # 2. We don't have access to the remote domain and trustdom password
+        # is provided. Do the work on our side and inform what to do on remote
+        # side.
+        if 'trust_passwd' in options:
+            result = trustinstance.join_ad_ipa_half(keys[-1], realm_server, options['trust_passwd'])
+            return dict(result=dict(), value=trustinstance.remote_domain.info['dns_domain'])
+
+class trust_del(LDAPDelete):
+    __doc__ = _('Delete a trust.')
+
+    msg_summary = _('Deleted trust "%(value)s"')
+
+    def pre_callback(self, ldap, dn, *keys, **options):
+        try:
+            result = self.api.Command.trust_show(keys[-1])
+        except errors.NotFound, e:
+            self.obj.handle_not_found(*keys)
+        return result['result']['dn']
+
+class trust_mod(LDAPUpdate):
+    __doc__ = _('Modify a trust.')
+
+    msg_summary = _('Modified trust "%(value)s"')
+
+    def pre_callback(self, ldap, dn, *keys, **options):
+        result = None
+        try:
+            result = self.api.Command.trust_show(keys[-1])
+        except errors.NotFound, e:
+            self.obj.handle_not_found(*keys)
+
+        # TODO: we found the trust object, now modify it
+        return result['result']['dn']
+
+class trust_find(LDAPSearch):
+    __doc__ = _('Search for trusts.')
+
+    msg_summary = ngettext(
+        '%(count)d trust matched', '%(count)d trusts matched', 0
+    )
+
+    # Since all trusts types are stored within separate containers under 'cn=trusts',
+    # search needs to be done on a sub-tree scope
+    def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+        return (filters, base_dn, ldap.SCOPE_SUBTREE)
+
+class trust_show(LDAPRetrieve):
+    __doc__ = _('Display information about a trust.')
+    has_output_params = LDAPRetrieve.has_output_params + trust_output_params
+
+    def execute(self, *keys, **options):
+        error = None
+        result = None
+        for trust_type in trust.trust_types:
+            options['trust_show_type'] = trust_type
+            try:
+                result = super(trust_show, self).execute(*keys, **options)
+            except errors.NotFound, e:
+                result = None
+                error = e
+            if result:
+                 result['result']['trusttype'] = [trust_type_string(result['result']['ipanttrusttype'][0])]
+                 result['result']['trustdirection'] = [trust_direction_string(result['result']['ipanttrustdirection'][0])]
+                 break
+        if error or not result:
+            self.obj.handle_not_found(*keys)
+
+        return result
+
+    def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        if 'trust_show_type' in options:
+            return make_trust_dn(self.env, options['trust_show_type'], dn)
+        return dn
+
+api.register(trust)
+api.register(trust_add_ad)
+api.register(trust_mod)
+api.register(trust_del)
+api.register(trust_find)
+api.register(trust_show)
+
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
new file mode 100644
index 0000000..3f20fad
--- /dev/null
+++ b/ipaserver/dcerpc.py
@@ -0,0 +1,325 @@
+# Authors:
+#     Alexander Bokovoy <abokovoy at redhat.com>
+#
+# Copyright (C) 2011  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# Portions (C) Andrew Tridgell, Andrew Bartlett
+#
+# 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/>.
+
+# Make sure we only run this module at the server where samba4-python
+# package is installed to avoid issues with unavailable modules
+
+from ipalib.plugins.baseldap import *
+from ipalib import api, Str, Password, DefaultFrom, _, ngettext, Object
+from ipalib.parameters import Enum
+from ipalib import Command
+from ipalib import errors
+from ipapython import ipautil
+from ipalib import util
+
+import os, string, struct, copy
+import uuid
+from samba import param
+from samba import credentials
+from samba.dcerpc import security, lsa, drsblobs, nbt
+from samba.ndr import ndr_pack
+from samba import net
+import samba
+import random
+import ldap as _ldap
+
+__doc__ = _("""
+Classes to manage trust joins using DCE-RPC calls
+
+The code in this module relies heavily on samba4-python package
+and Samba4 python bindings.
+""")
+
+
+class ExtendedDNControl(_ldap.controls.RequestControl):
+    def __init__(self):
+        self.controlType = "1.2.840.113556.1.4.529"
+        self.criticality = False
+        self.integerValue = 1
+
+    def encodeControlValue(self):
+        return '0\x03\x02\x01\x01'
+
+class TrustDomainInstance(object):
+
+    def __init__(self, hostname, creds=None):
+        self.parm = param.LoadParm()
+        self.parm.load(os.path.join(ipautil.SHARE_DIR,"smb.conf.empty"))
+        if len(hostname) > 0:
+            self.parm.set('netbios name', hostname)
+        self.creds = creds
+        self.hostname = hostname
+        self.info = {}
+        self._pipe = None
+        self._policy_handle = None
+        self.read_only = False
+
+    def __gen_lsa_connection(self, binding):
+       if self.creds is None:
+           raise errors.RequirementError(name='CIFS credentials object')
+       try:
+           result = lsa.lsarpc(binding, self.parm, self.creds)
+           return result
+       except:
+           return None
+
+    def __init_lsa_pipe(self, remote_host):
+        """
+        Try to initialize connection to the LSA pipe at remote host.
+        This method tries consequently all possible transport options
+        and selects one that works. See __gen_lsa_bindings() for details.
+
+        The actual result may depend on details of existing credentials.
+        For example, using signing causes NO_SESSION_KEY with Win2K8 and
+        using kerberos against Samba with signing does not work.
+        """
+        # short-cut: if LSA pipe is initialized, skip completely
+        if self._pipe:
+            return
+
+        bindings = self.__gen_lsa_bindings(remote_host)
+        for binding in bindings:
+            self._pipe = self.__gen_lsa_connection(binding)
+            if self._pipe:
+                break
+        if self._pipe is None:
+            raise errors.RequirementError(name='Working LSA pipe')
+
+    def __gen_lsa_bindings(self, remote_host):
+        """
+        There are multiple transports to issue LSA calls. However, depending on a
+        system in use they may be blocked by local operating system policies.
+        Generate all we can use. __init_lsa_pipe() will try them one by one until
+        there is one working.
+
+        We try NCACN_NP before NCACN_IP_TCP and signed sessions before unsigned.
+        """
+        transports = (u'ncacn_np', u'ncacn_ip_tcp')
+        options = ( u',', u'')
+        binding_template=lambda x,y,z: u'%s:%s[%s]' % (x, y, z)
+        return [binding_template(t, remote_host, o) for t in transports for o in options]
+
+    def retrieve_anonymously(self, remote_host, discover_srv=False):
+        """
+        When retrieving DC information anonymously, we can't get SID of the domain
+        """
+        netrc = net.Net(creds=self.creds, lp=self.parm)
+        if discover_srv:
+            result = netrc.finddc(domain=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
+        else:
+            result = netrc.finddc(address=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
+        if not result:
+            return False
+        self.info['name'] = unicode(result.domain_name)
+        self.info['dns_domain'] = unicode(result.dns_domain)
+        self.info['dns_forest'] = unicode(result.forest)
+        self.info['guid'] = unicode(result.domain_uuid)
+
+        # Netlogon response doesn't contain SID of the domain.
+        # We need to do rootDSE search with LDAP_SERVER_EXTENDED_DN_OID control to reveal the SID
+        ldap_uri = 'ldap://%s' % (result.pdc_dns_name)
+        conn = _ldap.initialize(ldap_uri)
+        conn.set_option(_ldap.OPT_SERVER_CONTROLS, [ExtendedDNControl()])
+        result = None
+        try:
+            (objtype, res) = conn.search_s('', _ldap.SCOPE_BASE)[0]
+            result = res['defaultNamingContext'][0]
+            self.info['dns_hostname'] = res['dnsHostName'][0]
+        except _ldap.LDAPError, e:
+            print "LDAP error when connecting to %s: %s" % (unicode(result.pdc_name), str(e))
+
+        if result:
+           self.info['sid'] = self.parse_naming_context(result)
+        return True
+
+    def parse_naming_context(self, context):
+        naming_ref = re.compile('.*<SID=(S-.*)>.*')
+        return naming_ref.match(context).group(1)
+
+    def retrieve(self, remote_host):
+        self.__init_lsa_pipe(remote_host)
+
+        objectAttribute = lsa.ObjectAttribute()
+        objectAttribute.sec_qos = lsa.QosInfo()
+        self._policy_handle = self._pipe.OpenPolicy2(u"", objectAttribute, security.SEC_FLAG_MAXIMUM_ALLOWED)
+        result = self._pipe.QueryInfoPolicy2(self._policy_handle, lsa.LSA_POLICY_INFO_DNS)
+        self.info['name'] = unicode(result.name.string)
+        self.info['dns_domain'] = unicode(result.dns_domain.string)
+        self.info['dns_forest'] = unicode(result.dns_forest.string)
+        self.info['guid'] = unicode(result.domain_guid)
+        self.info['sid'] = unicode(result.sid)
+
+    def generate_auth(self, trustdom_secret):
+        def arcfour_encrypt(key, data):
+            from Crypto.Cipher import ARC4
+            c = ARC4.new(key)
+            return c.encrypt(data)
+        def string_to_array(what):
+            blob = [0] * len(what)
+
+            for i in range(len(what)):
+                blob[i] = ord(what[i])
+            return blob
+
+        password_blob = string_to_array(trustdom_secret.encode('utf-16-le'))
+
+        clear_value = drsblobs.AuthInfoClear()
+        clear_value.size = len(password_blob)
+        clear_value.password = password_blob
+
+        clear_authentication_information = drsblobs.AuthenticationInformation()
+        clear_authentication_information.LastUpdateTime = samba.unix2nttime(int(time.time()))
+        clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
+        clear_authentication_information.AuthInfo = clear_value
+
+        authentication_information_array = drsblobs.AuthenticationInformationArray()
+        authentication_information_array.count = 1
+        authentication_information_array.array = [clear_authentication_information]
+
+        outgoing = drsblobs.trustAuthInOutBlob()
+        outgoing.count = 1
+        outgoing.current = authentication_information_array
+
+        confounder = [3]*512
+        for i in range(512):
+            confounder[i] = random.randint(0, 255)
+
+        trustpass = drsblobs.trustDomainPasswords()
+        trustpass.confounder = confounder
+
+        trustpass.outgoing = outgoing
+        trustpass.incoming = outgoing
+
+        trustpass_blob = ndr_pack(trustpass)
+
+        encrypted_trustpass = arcfour_encrypt(self._pipe.session_key, trustpass_blob)
+
+        auth_blob = lsa.DATA_BUF2()
+        auth_blob.size = len(encrypted_trustpass)
+        auth_blob.data = string_to_array(encrypted_trustpass)
+
+        auth_info = lsa.TrustDomainInfoAuthInfoInternal()
+        auth_info.auth_blob = auth_blob
+        self.auth_info = auth_info
+
+
+
+    def establish_trust(self, another_domain, trustdom_secret):
+        """
+        Establishes trust between our and another domain
+        Input: another_domain -- instance of TrustDomainInstance, initialized with #retrieve call
+               trustdom_secret -- shared secred used for the trust
+        """
+        self.generate_auth(trustdom_secret)
+
+        info = lsa.TrustDomainInfoInfoEx()
+        info.domain_name.string = another_domain.info['dns_domain']
+        info.netbios_name.string = another_domain.info['name']
+        info.sid = security.dom_sid(another_domain.info['sid'])
+        info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
+        info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
+        info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE | lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION
+
+        try:
+            dname = lsa.String()
+            dname.string = another_domain.info['dns_domain']
+            res = self._pipe.QueryTrustedDomainInfoByName(self._policy_handle, dname, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
+            self._pipe.DeleteTrustedDomain(self._policy_handle, res.info_ex.sid)
+        except:
+            pass
+        self._pipe.CreateTrustedDomainEx2(self._policy_handle, info, self.auth_info, security.SEC_STD_DELETE)
+
+class TrustDomainJoins(object):
+    ATTR_FLATNAME = 'ipantflatname'
+
+    def __init__(self, api):
+        self.api = api
+        self.local_domain = None
+        self.remote_domain = None
+
+        self.ldap = self.api.Backend.ldap2
+        cn_trust_local = DN(('cn', self.api.env.domain), self.api.env.container_cifsdomains, self.api.env.basedn)
+        (dn, entry_attrs) = self.ldap.get_entry(unicode(cn_trust_local), [self.ATTR_FLATNAME])
+        self.local_flatname = entry_attrs[self.ATTR_FLATNAME][0]
+        self.local_dn = dn
+
+        self.__populate_local_domain()
+
+    def __populate_local_domain(self):
+        # Initialize local domain info using kerberos only
+        ld = TrustDomainInstance(self.local_flatname)
+        ld.creds = credentials.Credentials()
+        ld.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
+        ld.creds.guess(ld.parm)
+        ld.creds.set_workstation(ld.hostname)
+        ld.retrieve(util.get_fqdn())
+        self.local_domain = ld
+
+    def __populate_remote_domain(self, realm, realm_server=None, realm_admin=None, realm_passwd=None):
+        def get_instance(self):
+            # Fetch data from foreign domain using password only
+            rd = TrustDomainInstance('')
+            rd.parm.set('workgroup', self.local_domain.info['name'])
+            rd.creds = credentials.Credentials()
+            rd.creds.set_kerberos_state(credentials.DONT_USE_KERBEROS)
+            rd.creds.guess(rd.parm)
+            return rd
+
+        rd = get_instance(self)
+        rd.creds.set_anonymous()
+        rd.creds.set_workstation(self.local_domain.hostname)
+        if realm_server is None:
+            rd.retrieve_anonymously(realm, discover_srv=True)
+        else:
+            rd.retrieve_anonymously(realm_server, discover_srv=False)
+        rd.read_only = True
+        if realm_admin and realm_passwd:
+            if 'name' in rd.info:
+                auth_string = u"%s\%s%%%s" % (rd.info['name'], realm_admin, realm_passwd)
+                td = get_instance(self)
+                td.creds.parse_string(auth_string)
+                td.creds.set_workstation(self.local_domain.hostname)
+                if realm_server is None:
+                    # we must have rd.info['dns_hostname'] then, part of anonymous discovery
+                    td.retrieve(rd.info['dns_hostname'])
+                else:
+                    td.retrieve(realm_server)
+                td.read_only = False
+                self.remote_domain = td
+                return
+        # Otherwise, use anonymously obtained data
+        self.remote_domain = rd
+
+    def join_ad_full_credentials(self, realm, realm_server, realm_admin, realm_passwd):
+        self.__populate_remote_domain(realm, realm_server, realm_admin, realm_passwd)
+        if not self.remote_domain.read_only:
+            trustdom_pass = samba.generate_random_password(128, 128)
+            self.remote_domain.establish_trust(self.local_domain, trustdom_pass)
+            self.local_domain.establish_trust(self.remote_domain, trustdom_pass)
+            return dict(local=self.local_domain, remote=self.remote_domain)
+        return None
+
+    def join_ad_ipa_half(self, realm, realm_server, trustdom_passwd):
+        self.__populate_remote_domain(realm, realm_server, realm_passwd=None)
+        self.local_domain.establish_trust(self.remote_domain, trustdom_passwd)
+        return dict(local=self.local_domain, remote=self.remote_domain)
+
+
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
index b978146..38f9eae 100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -114,7 +114,7 @@ class ADTRUSTInstance(service.Service):
         print "The user for Samba is %s" % self.smb_dn
         try:
             self.admin_conn.getEntry(self.smb_dn, ldap.SCOPE_BASE)
-            print "Samba user entry exists, resetting password"
+            root_logger.info("Samba user entry exists, resetting password")
 
             self.admin_conn.modify_s(self.smb_dn, \
                           [(ldap.MOD_REPLACE, "userPassword", self.smb_dn_pwd)])
@@ -215,7 +215,7 @@ class ADTRUSTInstance(service.Service):
 
         try:
             self.admin_conn.getEntry(self.smb_dom_dn, ldap.SCOPE_BASE)
-            print "Samba domain object already exists"
+            root_logger.info("Samba domain object already exists")
             return
         except errors.NotFound:
             pass
@@ -283,7 +283,12 @@ class ADTRUSTInstance(service.Service):
     def __setup_principal(self):
         cifs_principal = "cifs/" + self.fqdn + "@" + self.realm_name
 
-        api.Command.service_add(unicode(cifs_principal))
+        try:
+            api.Command.service_add(unicode(cifs_principal))
+        except errors.DuplicateEntry, e:
+            # CIFS principal already exists, it is not the first time adtrustinstance is managed
+            # That's fine, we we'll re-extract the key again.
+            pass
 
         samba_keytab = "/etc/samba/samba.keytab"
         if os.path.exists(samba_keytab):
@@ -291,7 +296,6 @@ class ADTRUSTInstance(service.Service):
                 ipautil.run(["ipa-rmkeytab", "--principal", cifs_principal,
                                          "-k", samba_keytab])
             except ipautil.CalledProcessError, e:
-                root_logger.critical("Result of removing old key: %d" % e.returncode)
                 if e.returncode != 5:
                     root_logger.critical("Failed to remove old key for %s" % cifs_principal)
 
@@ -374,7 +378,7 @@ class ADTRUSTInstance(service.Service):
             self.ldap_enable('ADTRUST', self.fqdn, self.dm_password, \
                              self.suffix)
         except (ldap.ALREADY_EXISTS, errors.DuplicateEntry), e:
-            root_logger.critical("ADTRUST Service startup entry already exists.")
+            root_logger.info("ADTRUST Service startup entry already exists.")
             pass
 
     def __setup_sub_dict(self):
-- 
1.7.9.3

-------------- next part --------------
>From 4b1e18a82521405ac04f0814fa5acc93e6f05b22 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Wed, 21 Mar 2012 15:03:14 +0200
Subject: [PATCH 5/9] Update specfile with proper changelog

---
 freeipa.spec.in |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 7746c98..6b3240f 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -696,7 +696,7 @@ fi
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
 
 %changelog
-* Mon Mar 12 2012 Alexander Bokovoy <abokovoy at redhat.com> - 2.99.0-23
+* Mon Mar 26 2012 Alexander Bokovoy <abokovoy at redhat.com> - 2.99.0-27
 - Add freeipa-server-trust-ad virtual package to capture all required dependencies
   for Active Directory trust management
 
-- 
1.7.9.3

-------------- next part --------------
>From 27d757f5aae874b52115de4a646e577e3161e6c6 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Mon, 26 Mar 2012 14:23:42 +0300
Subject: [PATCH 6/9] Perform case-insensitive searches for principals on TGS
 requests

We want to always resolve TGS requests even if the user mistakenly sends a
request for a service ticket where the fqdn part contain upper case letters.

The actual implementation follows hints set by KDC. When AP_REQ is done, KDC
sets KRB5_FLAG_ALIAS_OK and we obey it when looking for principals on TGS requests.

https://fedorahosted.org/freeipa/ticket/1577
---
 daemons/ipa-kdb/ipa_kdb_principals.c |   73 ++++++++++++++++++++++++----------
 install/share/61kerberos-ipav3.ldif  |    3 ++
 install/share/Makefile.am            |    1 +
 install/updates/10-60basev3.update   |    2 +
 ipalib/plugins/service.py            |    7 +++-
 ipaserver/install/dsinstance.py      |    1 +
 6 files changed, 65 insertions(+), 22 deletions(-)
 create mode 100644 install/share/61kerberos-ipav3.ldif

diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 1432619..2e190a1 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -22,6 +22,16 @@
 
 #include "ipa_kdb.h"
 
+/*
+ * During TGS request search by ipaKrbPrincipalName (case-insensitive)
+ * and krbPrincipalName (case-sensitive)
+ */
+#define PRINC_TGS_SEARCH_FILTER "(&(|(objectclass=krbprincipalaux)" \
+                                    "(objectclass=krbprincipal)" \
+                                    "(objectclass=ipakrbprincipal))" \
+                                    "(|(ipakrbprincipalalias=%s)" \
+                                      "(krbprincipalname=%s)))"
+
 #define PRINC_SEARCH_FILTER "(&(|(objectclass=krbprincipalaux)" \
                                 "(objectclass=krbprincipal))" \
                               "(krbprincipalname=%s))"
@@ -29,6 +39,7 @@
 static char *std_principal_attrs[] = {
     "krbPrincipalName",
     "krbCanonicalName",
+    "ipaKrbPrincipalAlias",
     "krbUPEnabled",
     "krbPrincipalKey",
     "krbTicketPolicyReference",
@@ -73,6 +84,7 @@ static char *std_principal_obj_classes[] = {
     "krbprincipal",
     "krbprincipalaux",
     "krbTicketPolicyAux",
+    "ipakrbprincipal",
 
     NULL
 };
@@ -636,13 +648,14 @@ done:
 }
 
 static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx,
-                                              char *search_expr,
+                                              unsigned int flags,
+                                              char *principal,
                                               LDAPMessage **result)
 {
     krb5_error_code kerr;
     char *src_filter = NULL;
-    char *esc_search_expr = NULL;
-    int ret;
+    char *esc_original_princ = NULL;
+    int ret, i;
 
     if (!ipactx->lcontext) {
         ret = ipadb_get_connection(ipactx);
@@ -654,13 +667,19 @@ static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx,
 
     /* escape filter but do not touch '*' as this function accepts
      * wildcards in names */
-    esc_search_expr = ipadb_filter_escape(search_expr, false);
-    if (!esc_search_expr) {
+    esc_original_princ = ipadb_filter_escape(principal, false);
+    if (!esc_original_princ) {
         kerr = KRB5_KDB_INTERNAL_ERROR;
         goto done;
     }
 
-    ret = asprintf(&src_filter, PRINC_SEARCH_FILTER, esc_search_expr);
+    if (flags & KRB5_KDB_FLAG_ALIAS_OK) {
+        ret = asprintf(&src_filter, PRINC_TGS_SEARCH_FILTER,
+                       esc_original_princ, esc_original_princ);
+    } else {
+        ret = asprintf(&src_filter, PRINC_SEARCH_FILTER, esc_original_princ);
+    }
+
     if (ret == -1) {
         kerr = KRB5_KDB_INTERNAL_ERROR;
         goto done;
@@ -673,7 +692,7 @@ static krb5_error_code ipadb_fetch_principals(struct ipadb_context *ipactx,
 
 done:
     free(src_filter);
-    free(esc_search_expr);
+    free(esc_original_princ);
     return kerr;
 }
 
@@ -713,9 +732,12 @@ static krb5_error_code ipadb_find_principal(krb5_context kcontext,
         /* we need to check for a strict match as a '*' in the name may have
          * caused the ldap server to return multiple entries */
         for (i = 0; vals[i]; i++) {
-            /* FIXME: use case insensitive compare and tree as alias ?? */
-            if (strcmp(vals[i]->bv_val, (*principal)) == 0) {
-                found = true;
+            /* KDC will accept aliases when doing TGT lookup (ref_tgt_again in do_tgs_req.c */
+            /* Use case-insensitive comparison in such cases */
+            if ((flags & KRB5_KDB_FLAG_ALIAS_OK) != 0) {
+                found = (strcasecmp(vals[i]->bv_val, (*principal)) == 0);
+            } else {
+                found = (strcmp(vals[i]->bv_val, (*principal)) == 0);
             }
         }
 
@@ -731,11 +753,15 @@ static krb5_error_code ipadb_find_principal(krb5_context kcontext,
             continue;
         }
 
-        /* FIXME: use case insensitive compare and treat as alias ?? */
-        if (strcmp(vals[0]->bv_val, (*principal)) != 0 &&
-                !(flags & KRB5_KDB_FLAG_ALIAS_OK)) {
+        /* Again, if aliases are accepted by KDC, use case-insensitive comparison */
+        if ((flags & KRB5_KDB_FLAG_ALIAS_OK) != 0) {
+            found = (strcasecmp(vals[0]->bv_val, (*principal)) == 0);
+        } else {
+            found = (strcmp(vals[0]->bv_val, (*principal)) == 0);
+        }
+
+        if (!found) {
             /* search does not allow aliases */
-            found = false;
             ldap_value_free_len(vals);
             continue;
         }
@@ -883,7 +909,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
         goto done;
     }
 
-    kerr = ipadb_fetch_principals(ipactx, principal, &res);
+    kerr = ipadb_fetch_principals(ipactx, flags, principal, &res);
     if (kerr != 0) {
         goto done;
     }
@@ -1398,6 +1424,11 @@ static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
         if (kerr) {
             goto done;
         }
+        kerr = ipadb_get_ldap_mod_str(imods, "ipaKrbPrincipalAlias",
+                                      principal, mod_op);
+        if (kerr) {
+            goto done;
+        }
     }
 
     /* KADM5_PRINC_EXPIRE_TIME */
@@ -1735,13 +1766,13 @@ static krb5_error_code ipadb_add_principal(krb5_context kcontext,
         goto done;
     }
 
-    kerr = ipadb_entry_to_mods(kcontext, imods,
-                               entry, principal, LDAP_MOD_ADD);
+    kerr = ipadb_entry_default_attrs(imods);
     if (kerr != 0) {
         goto done;
     }
 
-    kerr = ipadb_entry_default_attrs(imods);
+    kerr = ipadb_entry_to_mods(kcontext, imods,
+                               entry, principal, LDAP_MOD_ADD);
     if (kerr != 0) {
         goto done;
     }
@@ -1779,7 +1810,7 @@ static krb5_error_code ipadb_modify_principal(krb5_context kcontext,
             goto done;
         }
 
-        kerr = ipadb_fetch_principals(ipactx, principal, &res);
+        kerr = ipadb_fetch_principals(ipactx, 0, principal, &res);
         if (kerr != 0) {
             goto done;
         }
@@ -1930,7 +1961,7 @@ krb5_error_code ipadb_delete_principal(krb5_context kcontext,
         goto done;
     }
 
-    kerr = ipadb_fetch_principals(ipactx, principal, &res);
+    kerr = ipadb_fetch_principals(ipactx, 0, principal, &res);
     if (kerr != 0) {
         goto done;
     }
@@ -1982,7 +2013,7 @@ krb5_error_code ipadb_iterate(krb5_context kcontext,
     }
 
     /* fetch list of principal matching filter */
-    kerr = ipadb_fetch_principals(ipactx, match_entry, &res);
+    kerr = ipadb_fetch_principals(ipactx, 0, match_entry, &res);
     if (kerr != 0) {
         goto done;
     }
diff --git a/install/share/61kerberos-ipav3.ldif b/install/share/61kerberos-ipav3.ldif
new file mode 100644
index 0000000..dcdaa5d
--- /dev/null
+++ b/install/share/61kerberos-ipav3.ldif
@@ -0,0 +1,3 @@
+dn: cn=schema
+attributeTypes: (2.16.840.1.113730.3.8.11.32 NAME 'ipaKrbPrincipalAlias' DESC 'IPA principal alias' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
+objectClasses: (2.16.840.1.113730.3.8.12.8 NAME 'ipaKrbPrincipal' SUP krbPrincipalAux AUXILIARY MUST ( krbPrincipalName $ ipaKrbPrincipalAlias ) X-ORIGIN 'IPA v3' )
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
index 81fd0dc..68c98e0 100644
--- a/install/share/Makefile.am
+++ b/install/share/Makefile.am
@@ -9,6 +9,7 @@ app_DATA =				\
 	60basev2.ldif			\
 	60basev3.ldif			\
 	60ipadns.ldif			\
+	61kerberos-ipav3.ldif		\
 	65ipasudo.ldif			\
 	anonymous-vlv.ldif		\
 	bootstrap-template.ldif		\
diff --git a/install/updates/10-60basev3.update b/install/updates/10-60basev3.update
index 796eb16..96d012c 100644
--- a/install/updates/10-60basev3.update
+++ b/install/updates/10-60basev3.update
@@ -4,3 +4,5 @@ add:attributeTypes: ( 2.16.840.1.113730.3.8.11.21 NAME 'ipaAllowToImpersonate' D
 add:attributeTypes: ( 2.16.840.1.113730.3.8.11.22 NAME 'ipaAllowedTarget' DESC 'Target principals alowed to get a ticket for' SUP distinguishedName X-ORIGIN 'IPA-v3')
 add:objectClasses: (2.16.840.1.113730.3.8.12.6 NAME 'groupOfPrincipals' SUP top AUXILIARY MUST ( cn ) MAY ( memberPrincipal ) X-ORIGIN 'IPA v3' )
 add:objectClasses: (2.16.840.1.113730.3.8.12.7 NAME 'ipaKrb5DelegationACL' SUP groupOfPrincipals STRUCTURAL MAY ( ipaAllowToImpersonate $$ ipaAllowedTarget ) X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.32 NAME 'ipaKrbPrincipalAlias' DESC 'IPA principal alias' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
+add:objectClasses: (2.16.840.1.113730.3.8.12.8 NAME 'ipaKrbPrincipal' SUP krbPrincipalAux AUXILIARY MUST ( krbPrincipalName $$ ipaKrbPrincipalAlias ) X-ORIGIN 'IPA v3' )
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 7c563b3..d2ddd4e 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -221,7 +221,7 @@ class service(LDAPObject):
     object_name_plural = _('services')
     object_class = [
         'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', 'ipaobject',
-        'ipaservice', 'pkiuser'
+        'ipaservice', 'pkiuser', 'ipakrbprincipal'
     ]
     search_attributes = ['krbprincipalname', 'managedby']
     default_attributes = ['krbprincipalname', 'usercertificate', 'managedby']
@@ -293,6 +293,11 @@ class service_add(LDAPCreate):
         if not 'managedby' in entry_attrs:
              entry_attrs['managedby'] = hostresult['dn']
 
+        # Enforce ipaKrbPrincipalAlias to aid case-insensitive searches
+        # as krbPrincipalName/krbCanonicalName are case-sensitive in Kerberos
+        # schema
+        entry_attrs['ipakrbprincipalalias'] = keys[-1]
+
         return dn
 
 api.register(service_add)
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index d82454d..ca2c3e6 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -362,6 +362,7 @@ class DsInstance(service.Service):
                              "60basev2.ldif",
                              "60basev3.ldif",
                              "60ipadns.ldif",
+                             "61kerberos-ipav3.ldif",
                              "65ipasudo.ldif"):
             target_fname = schema_dirname(self.serverid) + schema_fname
             shutil.copyfile(ipautil.SHARE_DIR + schema_fname, target_fname)
-- 
1.7.9.3

-------------- next part --------------
>From 5c5ae521bd584f30a36739cb3d87c9e47b31aa01 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 27 Mar 2012 12:50:48 +0300
Subject: [PATCH 7/9] Properly handle multiple IP addresses per host when
 installing trust support

resolve_host() function returns a list of IP addresses. Handle it all rather
than expecting that there is a single address.

It wouldn't hurt to make a common function that takes --ip-address into account
when resolving host addresses and use it everywhere.
---
 install/tools/ipa-adtrust-install |   38 +++++++++++++++++++++----------------
 1 file changed, 22 insertions(+), 16 deletions(-)

diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 0f3e473..41e20f3 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -133,25 +133,31 @@ def main():
     # Check we have a public IP that is associated with the hostname
     ip = None
     try:
-        if options.ip_address:
-            ip = ipautil.CheckedIPAddress(options.ip_address, match_local=True)
+        hostaddr = resolve_host(api.env.host)
+        if len(hostaddr) > 1:
+            print >> sys.stderr, "The server hostname resolves to more than one address:"
+            for addr in hostaddr:
+                print >> sys.stderr, "  %s" % addr
+
+            if options.ip_address:
+                if str(options.ip_address) not in hostaddr:
+                    print >> sys.stderr, "Address passed in --ip-address did not match any resolved"
+                    print >> sys.stderr, "address!"
+                    sys.exit(1)
+                print "Selected IP address:", str(options.ip_address)
+                ip = options.ip_address
+            else:
+                if options.unattended:
+                    print >> sys.stderr, "Please use --ip-address option to specify the address"
+                    sys.exit(1)
+                else:
+                    ip = read_ip_address(api.env.host, fstore)
         else:
-            hostaddr = resolve_host(api.env.host)
-            ip = hostaddr and ipautil.CheckedIPAddress(hostaddr, match_local=True)
+                ip = hostaddr and ipautil.CheckedIPAddress(hostaddr[0], match_local=True)
     except Exception, e:
         print "Error: Invalid IP Address %s: %s" % (ip, e)
-        ip = None
-
-    if not ip:
-        if options.unattended:
-            sys.exit("Unable to resolve IP address for host name")
-        else:
-            read_ip = read_ip_address(api.env.host, fstore)
-            try:
-                ip = ipautil.CheckedIPAddress(read_ip, match_local=True)
-            except Exception, e:
-                print "Error: Invalid IP Address %s: %s" % (ip, e)
-                sys.exit("Aborting installation.")
+        print "Aborting installation"
+        sys.exit(1)
 
     ip_address = str(ip)
     root_logger.debug("will use ip_address: %s\n", ip_address)
-- 
1.7.9.3

-------------- next part --------------
>From b491e43fd21af281013cc9b90197bdc2e1becabc Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 27 Mar 2012 13:06:33 +0300
Subject: [PATCH 8/9] Restart KDC after installing trust support to allow MS
 PAC generation

Also make sure all exceptions are captured when creating CIFS service
record. The one we care about is duplicate entry and we do nothing
in that case anyway.

Also make uniform use of action descriptors.
---
 ipaserver/install/adtrustinstance.py |   23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
index 38f9eae..d9609f4 100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -32,6 +32,7 @@ from ipalib import errors, api
 from ipapython import sysrestore
 from ipapython import ipautil
 from ipapython.ipa_log_manager import *
+from ipapython import services as ipaservices
 
 import string
 import struct
@@ -285,7 +286,7 @@ class ADTRUSTInstance(service.Service):
 
         try:
             api.Command.service_add(unicode(cifs_principal))
-        except errors.DuplicateEntry, e:
+        except Exception, e:
             # CIFS principal already exists, it is not the first time adtrustinstance is managed
             # That's fine, we we'll re-extract the key again.
             pass
@@ -369,6 +370,12 @@ class ADTRUSTInstance(service.Service):
         except:
             pass
 
+    def __restart_kdc(self):
+        try:
+            ipaservices.knownservices.krb5kdc.restart()
+        except:
+            pass
+
     def __enable(self):
         self.backup_state("enabled", self.is_enabled())
         # We do not let the system start IPA components on its own,
@@ -418,20 +425,22 @@ class ADTRUSTInstance(service.Service):
         self.ldap_connect()
 
         self.step("stopping smbd", self.__stop)
-        self.step("create samba user", self.__create_samba_user)
-        self.step("create samba domain object", \
+        self.step("creating samba user", self.__create_samba_user)
+        self.step("creating samba domain object", \
                   self.__create_samba_domain_object)
-        self.step("create samba config registry", self.__write_smb_registry)
+        self.step("creating samba config registry", self.__write_smb_registry)
         self.step("writing samba config file", self.__write_smb_conf)
         self.step("setting password for the samba user", \
                   self.__set_smb_ldap_password)
-        self.step("Adding cifs Kerberos principal", self.__setup_principal)
-        self.step("Adding admin(group) SIDs", self.__add_admin_sids)
-        self.step("Activation CLDAP plugin", self.__add_cldap_module)
+        self.step("adding cifs Kerberos principal", self.__setup_principal)
+        self.step("adding admin(group) SIDs", self.__add_admin_sids)
+        self.step("activating CLDAP plugin", self.__add_cldap_module)
         self.step("configuring smbd to start on boot", self.__enable)
         if not self.no_msdcs:
             self.step("adding special DNS service records", \
                       self.__add_dns_service_records)
+        self.step("restarting KDC to take MS PAC changes into account", \
+                  self.__restart_kdc)
         self.step("starting smbd", self.__start)
 
         self.start_creation("Configuring smbd:")
-- 
1.7.9.3



More information about the Freeipa-devel mailing list