More forest trust related patches

Stefan (metze) Metzmacher metze at samba.org
Wed Apr 8 16:07:22 MDT 2015


Hi,

here're some more patches ready in my master4-forest-ok branch.
https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master4-forest-ok

There's more in the master4-forest-tmp, but I need to finish some of the
commits...
https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master4-forest-tmp

metze

Am 11.02.2015 um 06:07 schrieb Andrew Bartlett:
> On Tue, 2015-02-10 at 22:16 +0100, Stefan (metze) Metzmacher wrote:
>> Am 10.02.2015 um 10:05 schrieb Andrew Bartlett:
>>> On Tue, 2015-02-10 at 09:41 +0100, Stefan (metze) Metzmacher wrote:
>>>> Hi,
>>>>
>>>> there're some more patches ready in my master4-forest-ok branch.
>>>> https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master4-forest-ok
>>>>
>>>> Please review and push:-)
>>>>
>>>> Thanks!
>>>> metze
>>>
>>> In
>>> https://git.samba.org/?p=metze/samba/wip.git;a=commitdiff;h=17cfcc3b65d19c1b683d3beec84f1ec159e1bea6
>>>
>>> why do we have:
>>>
>>>      ok = samdb_is_pdc(state->ldb);
>>> +       if (!ok) {
>>> +               DEBUG(2, ("Password changes for domain %s are only
>>> allowed on a PDC.\n",
>>> +                         domain));
>>> +               TALLOC_FREE(tmp_ctx);
>>> +               ldb_transaction_cancel(state->ldb);
>>> +               return false;
>>> +       }
>>
>> Because only the PDC should change the trust password,
>> the caller should also check and not try at all it's not running on the PDC.
>> If more than one DC changes the password (maybe against multiple other
>> DCs in the remote
>> domain) we're very likely to break the trust.
>>
>>> Also, I would really like some tests along the lines of what I just did
>>> in krb5.kdc to:
>>>  - set a trust password (both ascii and binary) over LSA
>>>  - connect as that trust over NETLOGON
>>>  - get a ticket to that trust from the KDC
>>>  - process that ticket and verify that we can decrypt it. 
>>>
>>> That would give us the certainty that we are getting this UTF16-MUNGED
>>> stuff and other KDC parts right.
>>
>> I don't see how this is any different from a workstation trust.
> 
> It is different because the server-side store is very different, and so
> far the access and use of that store isn't very well tested. 
> 
>> I'm currently working on blackbox tests using trusts between
>> two dc environments.
>>
>> https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master4-forest
>> has all the work in progress, but I need to squash them a lot...
> 
> Thanks, I'll take a look.
> 
> My concern recently working on our KDC has been that blackbox testing
> doesn't trigger enough of the behaviours to be comprehensive.  That's
> why I started writing specific tests.  
> 
> Andrew Bartlett
> 
-------------- next part --------------
From 902878e5db750ba2df51ee02b05a25bdc3e6ffe0 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 28 Mar 2015 10:07:41 +0100
Subject: [PATCH 01/28] selftest/knownfail: remove unused
 ^samba4.winbind.struct.show_sequence\(ad_dc\) line

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail | 1 -
 1 file changed, 1 deletion(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index c16e916..d4a6923 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -241,7 +241,6 @@
 #
 # The Samba4 winbind does not cover the full winbind protocol, so these are expected
 #
-^samba4.winbind.struct.show_sequence\(ad_dc\)
 ^samba.blackbox.wbinfo\(ad_dc_ntvfs:local\).wbinfo -N against ad_dc_ntvfs
 ^samba.blackbox.wbinfo\(ad_dc_ntvfs:local\).wbinfo -I against ad_dc_ntvfs
 ^samba.blackbox.wbinfo\(ad_dc_ntvfs:local\).wbinfo  --trusted-domains against ad_dc_ntvfs
-- 
1.9.1


From ae561c846e288e7904246a9511f19159dd2b27f0 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sun, 29 Mar 2015 11:15:29 +0200
Subject: [PATCH 02/28] selftest/Samba4: use 'testallowed account' instead of
 'test allowed'

local.nss test might print lines starting with 'test allowed:...'
and that confused the subunit parser.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/target/Samba4.pm | 17 ++++++++++-------
 source4/selftest/tests.py |  2 +-
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 9d765c4..26f6dda 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -816,8 +816,9 @@ sub provision_raw_step2($$$)
 		return undef;
 	}
 
+	my $testallowed_account = "testallowed";
 	my $samba_tool_cmd = Samba::bindir_path($self, "samba-tool") 
-	    . " user add --configfile=$ctx->{smb_conf} testallowed $ctx->{password}";
+	    . " user add --configfile=$ctx->{smb_conf} $testallowed_account $ctx->{password}";
 	unless (system($samba_tool_cmd) == 0) {
 		warn("Unable to add testallowed user: \n$samba_tool_cmd\n");
 		return undef;
@@ -830,12 +831,13 @@ sub provision_raw_step2($$$)
 		$base_dn = "DC=$ctx->{netbiosname}";
 	}
 
-	my $user_dn = "cn=testallowed,cn=users,$base_dn";
+	my $user_dn = "cn=$testallowed_account,cn=users,$base_dn";
+	$testallowed_account = "testallowed account";
 	open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb");
 	print LDIF "dn: $user_dn
 changetype: modify
 replace: samAccountName
-samAccountName: test allowed
+samAccountName: $testallowed_account
 -
 ";
 	close(LDIF);
@@ -869,9 +871,9 @@ userPrincipalName: testdenied_upn\@$ctx->{realm}.upn
 	close(LDIF);
 
 	$samba_tool_cmd = Samba::bindir_path($self, "samba-tool") 
-	    . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' 'test allowed'";
+	    . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' '$testallowed_account'";
 	unless (system($samba_tool_cmd) == 0) {
-		warn("Unable to add 'test allowed' user to 'Allowed RODC Password Replication Group': \n$samba_tool_cmd\n");
+		warn("Unable to add '$testallowed_account' user to 'Allowed RODC Password Replication Group': \n$samba_tool_cmd\n");
 		return undef;
 	}
 
@@ -1637,10 +1639,11 @@ sub provision_rodc($$$)
 		return undef;
 	}
 
-        # This ensures deterministic behaviour for tests that want to have the 'test allowed'
+        # This ensures deterministic behaviour for tests that want to have the 'testallowed account'
         # user password verified on the RODC
+	my $testallowed_account = "testallowed account";
 	$cmd = "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" ";
-	$cmd .= "$samba_tool rodc preload 'test allowed' $ret->{CONFIGURATION}";
+	$cmd .= "$samba_tool rodc preload '$testallowed_account' $ret->{CONFIGURATION}";
 	$cmd .= " --server=$dcvars->{DC_SERVER}";
 
 	unless (system($cmd) == 0) {
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index bea1540..3fcf2b5 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -572,7 +572,7 @@ for env in ["ad_dc_ntvfs", "rodc", "promoted_dc", "ad_dc", "fl2000dc", "fl2003dc
 
     plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:krb5-hostname=$SERVER', '--option=torture:expect_machine_account=true'] + extra_options,
                              "samba4.krb5.kdc with machine account")
-    plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utest\ allowed%$PASSWORD',
+    plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed\ account%$PASSWORD',
                                                '--workgroup=$DOMAIN', '--realm=$REALM',
                                                '--option=torture:expect_machine_account=true',
                                                '--option=torture:krb5-upn=testallowed\ upn@$REALM',
-- 
1.9.1


From 4e142c7998303e3eb5c394d0fb4b3402117239f4 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sun, 29 Mar 2015 11:21:16 +0200
Subject: [PATCH 03/28] s4:torture/local: add more torture_assert() checks

We need to make sure we return when torture_assert_passwd_equal()
or torture_assert_group_equal() fails.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=11183

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/local/nss_tests.c | 255 ++++++++++++++++++++++----------------
 1 file changed, 145 insertions(+), 110 deletions(-)

diff --git a/source4/torture/local/nss_tests.c b/source4/torture/local/nss_tests.c
index a6baf21..546d7eb 100644
--- a/source4/torture/local/nss_tests.c
+++ b/source4/torture/local/nss_tests.c
@@ -30,12 +30,17 @@ static bool copy_passwd(struct torture_context *tctx,
 			struct passwd *p)
 {
 	p->pw_name	= talloc_strdup(tctx, pwd->pw_name);
+	torture_assert(tctx, (p->pw_name != NULL || pwd->pw_name == NULL), __location__);
 	p->pw_passwd	= talloc_strdup(tctx, pwd->pw_passwd);
+	torture_assert(tctx, (p->pw_passwd != NULL || pwd->pw_passwd == NULL), __location__);
 	p->pw_uid	= pwd->pw_uid;
 	p->pw_gid	= pwd->pw_gid;
 	p->pw_gecos	= talloc_strdup(tctx, pwd->pw_gecos);
+	torture_assert(tctx, (p->pw_gecos != NULL || pwd->pw_gecos == NULL), __location__);
 	p->pw_dir	= talloc_strdup(tctx, pwd->pw_dir);
+	torture_assert(tctx, (p->pw_dir != NULL || pwd->pw_dir == NULL), __location__);
 	p->pw_shell	= talloc_strdup(tctx, pwd->pw_shell);
+	torture_assert(tctx, (p->pw_shell != NULL || pwd->pw_shell == NULL), __location__);
 
 	return true;
 }
@@ -58,19 +63,22 @@ static bool test_getpwnam(struct torture_context *tctx,
 			  struct passwd *pwd_p)
 {
 	struct passwd *pwd;
+	int ret;
 
 	torture_comment(tctx, "Testing getpwnam: %s\n", name);
 
+	errno = 0;
 	pwd = getpwnam(name);
-	if (pwd) {
-		print_passwd(pwd);
-	}
+	ret = errno;
+	torture_assert(tctx, (pwd != NULL), talloc_asprintf(tctx,
+		       "getpwnam(%s) failed - %d - %s",
+		       name, ret, strerror(ret)));
 
-	if (pwd_p) {
-		copy_passwd(tctx, pwd, pwd_p);
+	if (pwd_p != NULL) {
+		torture_assert(tctx, copy_passwd(tctx, pwd, pwd_p), __location__);
 	}
 
-	return pwd ? true : false;
+	return true;
 }
 
 static bool test_getpwnam_r(struct torture_context *tctx,
@@ -84,17 +92,14 @@ static bool test_getpwnam_r(struct torture_context *tctx,
 	torture_comment(tctx, "Testing getpwnam_r: %s\n", name);
 
 	ret = getpwnam_r(name, &pwd, buffer, sizeof(buffer), &pwdp);
-	if (ret != 0) {
-		if (ret != ENOENT) {
-			torture_comment(tctx, "got %d return code\n", ret);
-		}
-		return false;
-	}
+	torture_assert(tctx, ret == 0, talloc_asprintf(tctx,
+		       "getpwnam_r(%s) failed - %d - %s",
+		       name, ret, strerror(ret)));
 
 	print_passwd(&pwd);
 
-	if (pwd_p) {
-		copy_passwd(tctx, &pwd, pwd_p);
+	if (pwd_p != NULL) {
+		torture_assert(tctx, copy_passwd(tctx, &pwd, pwd_p), __location__);
 	}
 
 	return true;
@@ -105,19 +110,24 @@ static bool test_getpwuid(struct torture_context *tctx,
 			  struct passwd *pwd_p)
 {
 	struct passwd *pwd;
+	int ret;
 
 	torture_comment(tctx, "Testing getpwuid: %lu\n", (unsigned long)uid);
 
+	errno = 0;
 	pwd = getpwuid(uid);
-	if (pwd) {
-		print_passwd(pwd);
-	}
+	ret = errno;
+	torture_assert(tctx, (pwd != NULL), talloc_asprintf(tctx,
+		       "getpwuid(%lu) failed - %d - %s",
+		       (unsigned long)uid, ret, strerror(ret)));
+
+	print_passwd(pwd);
 
-	if (pwd_p) {
-		copy_passwd(tctx, pwd, pwd_p);
+	if (pwd_p != NULL) {
+		torture_assert(tctx, copy_passwd(tctx, pwd, pwd_p), __location__);
 	}
 
-	return pwd ? true : false;
+	return true;
 }
 
 static bool test_getpwuid_r(struct torture_context *tctx,
@@ -131,17 +141,14 @@ static bool test_getpwuid_r(struct torture_context *tctx,
 	torture_comment(tctx, "Testing getpwuid_r: %lu\n", (unsigned long)uid);
 
 	ret = getpwuid_r(uid, &pwd, buffer, sizeof(buffer), &pwdp);
-	if (ret != 0) {
-		if (ret != ENOENT) {
-			torture_comment(tctx, "got %d return code\n", ret);
-		}
-		return false;
-	}
+	torture_assert(tctx, ret == 0, talloc_asprintf(tctx,
+		       "getpwuid_r(%lu) failed - %d - %s",
+		       (unsigned long)uid, ret, strerror(ret)));
 
 	print_passwd(&pwd);
 
-	if (pwd_p) {
-		copy_passwd(tctx, &pwd, pwd_p);
+	if (pwd_p != NULL) {
+		torture_assert(tctx, copy_passwd(tctx, &pwd, pwd_p), __location__);
 	}
 
 	return true;
@@ -155,13 +162,17 @@ static bool copy_group(struct torture_context *tctx,
 	int i;
 
 	g->gr_name	= talloc_strdup(tctx, grp->gr_name);
+	torture_assert(tctx, (g->gr_name != NULL || grp->gr_name == NULL), __location__);
 	g->gr_passwd	= talloc_strdup(tctx, grp->gr_passwd);
+	torture_assert(tctx, (g->gr_passwd != NULL || grp->gr_passwd == NULL), __location__);
 	g->gr_gid	= grp->gr_gid;
 	g->gr_mem	= NULL;
 
 	for (i=0; grp->gr_mem && grp->gr_mem[i]; i++) {
 		g->gr_mem = talloc_realloc(tctx, g->gr_mem, char *, i + 2);
+		torture_assert(tctx, (g->gr_mem != NULL), __location__);
 		g->gr_mem[i] = talloc_strdup(g->gr_mem, grp->gr_mem[i]);
+		torture_assert(tctx, (g->gr_mem[i] != NULL), __location__);
 		g->gr_mem[i+1] = NULL;
 	}
 
@@ -192,19 +203,24 @@ static bool test_getgrnam(struct torture_context *tctx,
 			  struct group *grp_p)
 {
 	struct group *grp;
+	int ret;
 
 	torture_comment(tctx, "Testing getgrnam: %s\n", name);
 
+	errno = 0;
 	grp = getgrnam(name);
-	if (grp) {
-		print_group(grp);
-	}
+	ret = errno;
+	torture_assert(tctx, (grp != NULL), talloc_asprintf(tctx,
+		       "getgrnam(%s) failed - %d - %s",
+		       name, ret, strerror(ret)));
+
+	print_group(grp);
 
-	if (grp_p) {
-		copy_group(tctx, grp, grp_p);
+	if (grp_p != NULL) {
+		torture_assert(tctx, copy_group(tctx, grp, grp_p), __location__);
 	}
 
-	return grp ? true : false;
+	return true;
 }
 
 static bool test_getgrnam_r(struct torture_context *tctx,
@@ -218,17 +234,14 @@ static bool test_getgrnam_r(struct torture_context *tctx,
 	torture_comment(tctx, "Testing getgrnam_r: %s\n", name);
 
 	ret = getgrnam_r(name, &grp, buffer, sizeof(buffer), &grpp);
-	if (ret != 0) {
-		if (ret != ENOENT) {
-			torture_comment(tctx, "got %d return code\n", ret);
-		}
-		return false;
-	}
+	torture_assert(tctx, ret == 0, talloc_asprintf(tctx,
+		       "getgrnam_r(%s) failed - %d - %s",
+		       name, ret, strerror(ret)));
 
 	print_group(&grp);
 
-	if (grp_p) {
-		copy_group(tctx, &grp, grp_p);
+	if (grp_p != NULL) {
+		torture_assert(tctx, copy_group(tctx, &grp, grp_p), __location__);
 	}
 
 	return true;
@@ -240,19 +253,24 @@ static bool test_getgrgid(struct torture_context *tctx,
 			  struct group *grp_p)
 {
 	struct group *grp;
+	int ret;
 
 	torture_comment(tctx, "Testing getgrgid: %lu\n", (unsigned long)gid);
 
+	errno = 0;
 	grp = getgrgid(gid);
-	if (grp) {
-		print_group(grp);
-	}
+	ret = errno;
+	torture_assert(tctx, (grp != NULL), talloc_asprintf(tctx,
+		       "getgrgid(%lu) failed - %d - %s",
+		       (unsigned long)gid, ret, strerror(ret)));
+
+	print_group(grp);
 
-	if (grp_p) {
-		copy_group(tctx, grp, grp_p);
+	if (grp_p != NULL) {
+		torture_assert(tctx, copy_group(tctx, grp, grp_p), __location__);
 	}
 
-	return grp ? true : false;
+	return true;
 }
 
 static bool test_getgrgid_r(struct torture_context *tctx,
@@ -266,17 +284,14 @@ static bool test_getgrgid_r(struct torture_context *tctx,
 	torture_comment(tctx, "Testing getgrgid_r: %lu\n", (unsigned long)gid);
 
 	ret = getgrgid_r(gid, &grp, buffer, sizeof(buffer), &grpp);
-	if (ret != 0) {
-		if (ret != ENOENT) {
-			torture_comment(tctx, "got %d return code\n", ret);
-		}
-		return false;
-	}
+	torture_assert(tctx, ret == 0, talloc_asprintf(tctx,
+		       "getgrgid_r(%lu) failed - %d - %s",
+		       (unsigned long)gid, ret, strerror(ret)));
 
 	print_group(&grp);
 
-	if (grp_p) {
-		copy_group(tctx, &grp, grp_p);
+	if (grp_p != NULL) {
+		torture_assert(tctx, copy_group(tctx, &grp, grp_p), __location__);
 	}
 
 	return true;
@@ -391,14 +406,17 @@ static bool test_passwd(struct torture_context *tctx)
 	for (i=0; i < num_pwd; i++) {
 		torture_assert(tctx, test_getpwnam(tctx, pwd[i].pw_name, &pwd1),
 			"failed to call getpwnam for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
-			"getpwent and getpwnam gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
+			"getpwent and getpwnam gave different results"),
+			__location__);
 		torture_assert(tctx, test_getpwuid(tctx, pwd[i].pw_uid, &pwd2),
 			"failed to call getpwuid for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
-			"getpwent and getpwuid gave different results");
-		torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
-			"getpwnam and getpwuid gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
+			"getpwent and getpwuid gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
+			"getpwnam and getpwuid gave different results"),
+			__location__);
 	}
 
 	return true;
@@ -416,14 +434,17 @@ static bool test_passwd_r(struct torture_context *tctx)
 	for (i=0; i < num_pwd; i++) {
 		torture_assert(tctx, test_getpwnam_r(tctx, pwd[i].pw_name, &pwd1),
 			"failed to call getpwnam_r for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
-			"getpwent_r and getpwnam_r gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
+			"getpwent_r and getpwnam_r gave different results"),
+			__location__);
 		torture_assert(tctx, test_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2),
 			"failed to call getpwuid_r for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
-			"getpwent_r and getpwuid_r gave different results");
-		torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
-			"getpwnam_r and getpwuid_r gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
+			"getpwent_r and getpwuid_r gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
+			"getpwnam_r and getpwuid_r gave different results"),
+			__location__);
 	}
 
 	return true;
@@ -441,24 +462,30 @@ static bool test_passwd_r_cross(struct torture_context *tctx)
 	for (i=0; i < num_pwd; i++) {
 		torture_assert(tctx, test_getpwnam_r(tctx, pwd[i].pw_name, &pwd1),
 			"failed to call getpwnam_r for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
-			"getpwent_r and getpwnam_r gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd1,
+			"getpwent_r and getpwnam_r gave different results"),
+			__location__);
 		torture_assert(tctx, test_getpwuid_r(tctx, pwd[i].pw_uid, &pwd2),
 			"failed to call getpwuid_r for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
-			"getpwent_r and getpwuid_r gave different results");
-		torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
-			"getpwnam_r and getpwuid_r gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd2,
+			"getpwent_r and getpwuid_r gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd1, &pwd2,
+			"getpwnam_r and getpwuid_r gave different results"),
+			__location__);
 		torture_assert(tctx, test_getpwnam(tctx, pwd[i].pw_name, &pwd3),
 			"failed to call getpwnam for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd3,
-			"getpwent_r and getpwnam gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd3,
+			"getpwent_r and getpwnam gave different results"),
+			__location__);
 		torture_assert(tctx, test_getpwuid(tctx, pwd[i].pw_uid, &pwd4),
 			"failed to call getpwuid for enumerated user");
-		torture_assert_passwd_equal(tctx, &pwd[i], &pwd4,
-			"getpwent_r and getpwuid gave different results");
-		torture_assert_passwd_equal(tctx, &pwd3, &pwd4,
-			"getpwnam and getpwuid gave different results");
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd[i], &pwd4,
+			"getpwent_r and getpwuid gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_passwd_equal(tctx, &pwd3, &pwd4,
+			"getpwnam and getpwuid gave different results"),
+			__location__);
 	}
 
 	return true;
@@ -554,12 +581,8 @@ static bool torture_assert_group_equal(struct torture_context *tctx,
 	torture_assert_str_equal(tctx, g1->gr_name, g2->gr_name, comment);
 	torture_assert_str_equal(tctx, g1->gr_passwd, g2->gr_passwd, comment);
 	torture_assert_int_equal(tctx, g1->gr_gid, g2->gr_gid, comment);
-	if (g1->gr_mem && !g2->gr_mem) {
-		return false;
-	}
-	if (!g1->gr_mem && g2->gr_mem) {
-		return false;
-	}
+	torture_assert(tctx, !(g1->gr_mem && !g2->gr_mem), __location__);
+	torture_assert(tctx, !(!g1->gr_mem && g2->gr_mem), __location__);
 	if (!g1->gr_mem && !g2->gr_mem) {
 		return true;
 	}
@@ -582,14 +605,17 @@ static bool test_group(struct torture_context *tctx)
 	for (i=0; i < num_grp; i++) {
 		torture_assert(tctx, test_getgrnam(tctx, grp[i].gr_name, &grp1),
 			"failed to call getgrnam for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp1,
-			"getgrent and getgrnam gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp1,
+			"getgrent and getgrnam gave different results"),
+			__location__);
 		torture_assert(tctx, test_getgrgid(tctx, grp[i].gr_gid, &grp2),
 			"failed to call getgrgid for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp2,
-			"getgrent and getgrgid gave different results");
-		torture_assert_group_equal(tctx, &grp1, &grp2,
-			"getgrnam and getgrgid gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp2,
+			"getgrent and getgrgid gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp1, &grp2,
+			"getgrnam and getgrgid gave different results"),
+			__location__);
 	}
 
 	return true;
@@ -607,14 +633,17 @@ static bool test_group_r(struct torture_context *tctx)
 	for (i=0; i < num_grp; i++) {
 		torture_assert(tctx, test_getgrnam_r(tctx, grp[i].gr_name, &grp1),
 			"failed to call getgrnam_r for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp1,
-			"getgrent_r and getgrnam_r gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp1,
+			"getgrent_r and getgrnam_r gave different results"),
+			__location__);
 		torture_assert(tctx, test_getgrgid_r(tctx, grp[i].gr_gid, &grp2),
 			"failed to call getgrgid_r for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp2,
-			"getgrent_r and getgrgid_r gave different results");
-		torture_assert_group_equal(tctx, &grp1, &grp2,
-			"getgrnam_r and getgrgid_r gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp2,
+			"getgrent_r and getgrgid_r gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp1, &grp2,
+			"getgrnam_r and getgrgid_r gave different results"),
+			__location__);
 	}
 
 	return true;
@@ -632,24 +661,30 @@ static bool test_group_r_cross(struct torture_context *tctx)
 	for (i=0; i < num_grp; i++) {
 		torture_assert(tctx, test_getgrnam_r(tctx, grp[i].gr_name, &grp1),
 			"failed to call getgrnam_r for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp1,
-			"getgrent_r and getgrnam_r gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp1,
+			"getgrent_r and getgrnam_r gave different results"),
+			__location__);
 		torture_assert(tctx, test_getgrgid_r(tctx, grp[i].gr_gid, &grp2),
 			"failed to call getgrgid_r for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp2,
-			"getgrent_r and getgrgid_r gave different results");
-		torture_assert_group_equal(tctx, &grp1, &grp2,
-			"getgrnam_r and getgrgid_r gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp2,
+			"getgrent_r and getgrgid_r gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp1, &grp2,
+			"getgrnam_r and getgrgid_r gave different results"),
+			__location__);
 		torture_assert(tctx, test_getgrnam(tctx, grp[i].gr_name, &grp3),
 			"failed to call getgrnam for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp3,
-			"getgrent_r and getgrnam gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp3,
+			"getgrent_r and getgrnam gave different results"),
+			__location__);
 		torture_assert(tctx, test_getgrgid(tctx, grp[i].gr_gid, &grp4),
 			"failed to call getgrgid for enumerated user");
-		torture_assert_group_equal(tctx, &grp[i], &grp4,
-			"getgrent_r and getgrgid gave different results");
-		torture_assert_group_equal(tctx, &grp3, &grp4,
-			"getgrnam and getgrgid gave different results");
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp[i], &grp4,
+			"getgrent_r and getgrgid gave different results"),
+			__location__);
+		torture_assert(tctx, torture_assert_group_equal(tctx, &grp3, &grp4,
+			"getgrnam and getgrgid gave different results"),
+			__location__);
 	}
 
 	return true;
-- 
1.9.1


From 9b21b7de917ea15388699c5b9e72e9e8815cfffd Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 28 Mar 2015 10:04:30 +0100
Subject: [PATCH 04/28] s4:torture/winbind: add
 torture:winbindd_domain_without_prefix option

We should not assume that names in the domain
specified by 'torture:winbindd_netbios_domain' have no DOMAIN\ prefix.

On an AD DC we prefix all principals.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=11183

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/selftest/tests.py              | 4 +++-
 source4/torture/winbind/struct_based.c | 8 +++++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 3fcf2b5..015e902 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -368,11 +368,13 @@ plansmbtorture4testsuite('base.xcopy', "ad_dc_ntvfs", ['//$NETBIOSNAME/xcopy_sha
 plansmbtorture4testsuite('base.xcopy', "s4member", ['//$NETBIOSNAME/xcopy_share', '-k', 'no', '--signing=no', '-U%'], modname="samba4.smb.signing --signing=no anon")
 
 
-wb_opts = ["--option=\"torture:strict mode=no\"", "--option=\"torture:timelimit=1\"", "--option=\"torture:winbindd_separator=/\"", "--option=\"torture:winbindd_netbios_name=$SERVER\"", "--option=\"torture:winbindd_netbios_domain=$DOMAIN\""]
+wb_opts_default = ["--option=\"torture:strict mode=no\"", "--option=\"torture:timelimit=1\"", "--option=\"torture:winbindd_separator=/\"", "--option=\"torture:winbindd_netbios_name=$SERVER\"", "--option=\"torture:winbindd_netbios_domain=$DOMAIN\""]
 
 winbind_ad_client_tests = smbtorture4_testsuites("winbind.struct") + smbtorture4_testsuites("winbind.pac")
 winbind_wbclient_tests = smbtorture4_testsuites("winbind.wbclient")
 for env in ["ad_dc", "s4member", "ad_member"]:
+    wb_opts = wb_opts_default
+    wb_opts += ["--option=\"torture:winbindd_domain_without_prefix=$DOMAIN\""]
     for t in winbind_ad_client_tests:
         plansmbtorture4testsuite(t, "%s:local" % env, wb_opts + ['//$SERVER/tmp', '--realm=$REALM', '--machine-pass', '--option=torture:addc=$DC_SERVER'])
 
diff --git a/source4/torture/winbind/struct_based.c b/source4/torture/winbind/struct_based.c
index 76e448c..be6ca51 100644
--- a/source4/torture/winbind/struct_based.c
+++ b/source4/torture/winbind/struct_based.c
@@ -929,8 +929,8 @@ static bool lookup_name_sid_list(struct torture_context *torture, char **list)
 		char *sid;
 		char *name;
 		const char *domain_name = torture_setting_string(torture,
-						"winbindd_netbios_domain",
-						lpcfg_workgroup(torture->lp_ctx));
+						"winbindd_domain_without_prefix",
+						NULL);
 
 		ZERO_STRUCT(req);
 		ZERO_STRUCT(rep);
@@ -949,7 +949,9 @@ static bool lookup_name_sid_list(struct torture_context *torture, char **list)
 
 		DO_STRUCT_REQ_REP(WINBINDD_LOOKUPSID, &req, &rep);
 
-		if (strequal(rep.data.name.dom_name, domain_name)) {
+		if (domain_name != NULL &&
+		    strequal(rep.data.name.dom_name, domain_name))
+		{
 			name = talloc_asprintf(torture, "%s",
 					       rep.data.name.name);
 		} else {
-- 
1.9.1


From a5bb540e6aabf9f4447b9572c9376beb2930ef92 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 28 Mar 2015 08:31:05 +0000
Subject: [PATCH 05/28] s3:winbindd: list users/groups of our own domain as AD
 DC

The AD users/groups of the local domain of an AD DC
only exist via winbindd and not in /etc/passwd or /etc/group.

This also matches the behaviour of the source4/winbind code.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=11183

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail               | 4 +++-
 source3/winbindd/winbindd_util.c | 7 +++++--
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index d4a6923..865f7e3 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -267,7 +267,6 @@
 ^samba4.winbind.struct.lookup_name_sid\(s4member:local\)
 ^samba.blackbox.wbinfo\(s4member:local\).wbinfo -r against s4member\(s4member:local\)
 ^samba.blackbox.wbinfo\(s4member:local\).wbinfo --user-sids against s4member\(s4member:local\)
-^samba4.winbind.struct.getpwent\(ad_dc:local\)
 ^samba.wbinfo_simple.\(s4member:local\).--user-groups
 ^samba.nss.test using winbind\(s4member:local\)
 #
@@ -277,6 +276,9 @@
 ^samba3.local.nss.reentrant enumeration crosschecks\(ad_dc_ntvfs:local\)
 ^samba3.local.nss.reentrant enumeration\(ad_dc_ntvfs:local\)
 ^samba3.local.nss.enumeration\(ad_dc_ntvfs:local\)
+^samba3.local.nss.reentrant enumeration crosschecks\(ad_dc:local\)
+^samba3.local.nss.reentrant enumeration\(ad_dc:local\)
+^samba3.local.nss.enumeration\(ad_dc:local\)
 #
 # These fail only if we run the unix.whoami test before them
 # in the member and ad_member environments. ==> Strange!!!
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
index 9134bd0..38758af 100644
--- a/source3/winbindd/winbindd_util.c
+++ b/source3/winbindd/winbindd_util.c
@@ -87,10 +87,13 @@ struct winbindd_domain *wb_next_domain(struct winbindd_domain *domain)
 		domain = domain->next;
 	}
 
-	if ((domain != NULL)
-	    && sid_check_is_our_sam(&domain->sid)) {
+	if ((domain != NULL) &&
+	    (lp_server_role() != ROLE_ACTIVE_DIRECTORY_DC) &&
+	    sid_check_is_our_sam(&domain->sid))
+	{
 		domain = domain->next;
 	}
+
 	return domain;
 }
 
-- 
1.9.1


From 2b25ddb1c4f0fc6b8d790db94a553e8a59ab597d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 28 Mar 2015 08:31:05 +0000
Subject: [PATCH 06/28] s3:winbindd: don't remove the DOMAIN\ prefix for
 principals of our own domain as AD DC

This also matches the behaviour of the source4/winbind code.

In Samba 4.0 and 4.1 we had the following

> getent passwd administrator
S4XDOM\Administrator:*:0:100::/home/S4XDOM/Administrator:/bin/false
> getent passwd S4XDOM\\administrator
S4XDOM\Administrator:*:0:100::/home/S4XDOM/Administrator:/bin/false

With Samba 4.2.0 we have:

> getent passwd administrator
administrator:*:0:100::/home/S4XDOM/administrator:/bin/false
> getent passwd S4XDOM\\administrator
administrator:*:0:100::/home/S4XDOM/administrator:/bin/false

With the patches we have:

> getent passwd administrator
S4XDOM\administrator:*:0:100::/home/S4XDOM/administrator:/bin/false
> getent passwd S4XDOM\\administrator
S4XDOM\administrator:*:0:100::/home/S4XDOM/administrator:/bin/false

Bug: https://bugzilla.samba.org/show_bug.cgi?id=11183

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail               |  1 -
 source3/winbindd/winbindd_util.c | 10 ++++++++++
 source4/selftest/tests.py        |  3 ++-
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index 865f7e3..3314d5d 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -264,7 +264,6 @@
 #
 ^samba4.winbind.struct.domain_info\(s4member:local\)
 ^samba4.winbind.struct.getdcname\(s4member:local\)
-^samba4.winbind.struct.lookup_name_sid\(s4member:local\)
 ^samba.blackbox.wbinfo\(s4member:local\).wbinfo -r against s4member\(s4member:local\)
 ^samba.blackbox.wbinfo\(s4member:local\).wbinfo --user-sids against s4member\(s4member:local\)
 ^samba.wbinfo_simple.\(s4member:local\).--user-groups
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
index 38758af..edbde2c 100644
--- a/source3/winbindd/winbindd_util.c
+++ b/source3/winbindd/winbindd_util.c
@@ -1055,12 +1055,18 @@ bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
     Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
     username is then unqualified in unix
 
+    On an AD DC we always fill DOMAIN\\USERNAME.
+
     We always canonicalize as UPPERCASE DOMAIN, lowercase username.
 */
 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
 {
 	fstring tmp_user;
 
+	if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+		can_assume = false;
+	}
+
 	fstrcpy(tmp_user, user);
 	(void)strlower_m(tmp_user);
 
@@ -1084,6 +1090,10 @@ char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
 {
 	char *tmp_user, *name;
 
+	if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+		can_assume = false;
+	}
+
 	tmp_user = talloc_strdup(mem_ctx, user);
 	if (!strlower_m(tmp_user)) {
 		TALLOC_FREE(tmp_user);
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 015e902..a71db32 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -374,7 +374,8 @@ winbind_ad_client_tests = smbtorture4_testsuites("winbind.struct") + smbtorture4
 winbind_wbclient_tests = smbtorture4_testsuites("winbind.wbclient")
 for env in ["ad_dc", "s4member", "ad_member"]:
     wb_opts = wb_opts_default
-    wb_opts += ["--option=\"torture:winbindd_domain_without_prefix=$DOMAIN\""]
+    if env == "ad_member":
+        wb_opts += ["--option=\"torture:winbindd_domain_without_prefix=$DOMAIN\""]
     for t in winbind_ad_client_tests:
         plansmbtorture4testsuite(t, "%s:local" % env, wb_opts + ['//$SERVER/tmp', '--realm=$REALM', '--machine-pass', '--option=torture:addc=$DC_SERVER'])
 
-- 
1.9.1


From 1a2b8daa727bd7cfef6d6123ddfb563cb3d6b842 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 28 Mar 2015 08:36:11 +0000
Subject: [PATCH 07/28] s3:winbindd: list local groups for our internal domains
 too (as AD DC)

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/winbindd/winbindd_dual_srv.c | 87 +++++++++++++++++++++++++++++++-----
 1 file changed, 75 insertions(+), 12 deletions(-)

diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index 061de72..97d8a1b 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -380,43 +380,106 @@ NTSTATUS _wbint_QueryGroupList(struct pipes_struct *p,
 			       struct wbint_QueryGroupList *r)
 {
 	struct winbindd_domain *domain = wb_child_domain();
-	uint32_t i, num_groups;
-	struct wb_acct_info *groups;
+	uint32_t i;
+	uint32_t num_local_groups = 0;
+	struct wb_acct_info *local_groups = NULL;
+	uint32_t num_dom_groups = 0;
+	struct wb_acct_info *dom_groups = NULL;
+	uint32_t ti = 0;
+	uint64_t num_total = 0;
 	struct wbint_Principal *result;
 	NTSTATUS status;
+	bool include_local_groups = false;
 
 	if (domain == NULL) {
 		return NT_STATUS_REQUEST_NOT_ACCEPTED;
 	}
 
+	switch (lp_server_role()) {
+	case ROLE_ACTIVE_DIRECTORY_DC:
+		if (domain->internal) {
+			/*
+			 * we want to include local groups
+			 * for BUILTIN and WORKGROUP
+			 */
+			include_local_groups = true;
+		}
+		break;
+	default:
+		/*
+		 * We might include local groups in more
+		 * setups later, but that requires more work
+		 * elsewhere.
+		 */
+		break;
+	}
+
+	if (include_local_groups) {
+		status = domain->methods->enum_local_groups(domain, talloc_tos(),
+							    &num_local_groups,
+							    &local_groups);
+		reset_cm_connection_on_error(domain, status);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
+		}
+	}
+
 	status = domain->methods->enum_dom_groups(domain, talloc_tos(),
-						  &num_groups, &groups);
+						  &num_dom_groups,
+						  &dom_groups);
 	reset_cm_connection_on_error(domain, status);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
 
+	num_total = num_local_groups + num_dom_groups;
+	if (num_total > UINT32_MAX) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
 	result = talloc_array(r->out.groups, struct wbint_Principal,
-			      num_groups);
+			      num_total);
 	if (result == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	for (i=0; i<num_groups; i++) {
-		sid_compose(&result[i].sid, &domain->sid, groups[i].rid);
-		result[i].type = SID_NAME_DOM_GRP;
-		result[i].name = talloc_strdup(result, groups[i].acct_name);
-		if (result[i].name == NULL) {
+	for (i = 0; i < num_local_groups; i++) {
+		struct wb_acct_info *lg = &local_groups[i];
+		struct wbint_Principal *rg = &result[ti++];
+
+		sid_compose(&rg->sid, &domain->sid, lg->rid);
+		rg->type = SID_NAME_ALIAS;
+		rg->name = talloc_strdup(result, lg->acct_name);
+		if (rg->name == NULL) {
+			TALLOC_FREE(result);
+			TALLOC_FREE(dom_groups);
+			TALLOC_FREE(local_groups);
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
+	num_local_groups = 0;
+	TALLOC_FREE(local_groups);
+
+	for (i = 0; i < num_dom_groups; i++) {
+		struct wb_acct_info *dg = &dom_groups[i];
+		struct wbint_Principal *rg = &result[ti++];
+
+		sid_compose(&rg->sid, &domain->sid, dg->rid);
+		rg->type = SID_NAME_DOM_GRP;
+		rg->name = talloc_strdup(result, dg->acct_name);
+		if (rg->name == NULL) {
 			TALLOC_FREE(result);
-			TALLOC_FREE(groups);
+			TALLOC_FREE(dom_groups);
+			TALLOC_FREE(local_groups);
 			return NT_STATUS_NO_MEMORY;
 		}
 	}
+	num_dom_groups = 0;
+	TALLOC_FREE(dom_groups);
 
-	r->out.groups->num_principals = num_groups;
+	r->out.groups->num_principals = ti;
 	r->out.groups->principals = result;
 
-	TALLOC_FREE(groups);
 	return NT_STATUS_OK;
 }
 
-- 
1.9.1


From 82993b8b61220c01364e6a9b9b843d51cc748823 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 10 Feb 2015 13:27:57 +0100
Subject: [PATCH 08/28] heimdal:lib/krb5: correctly follow
 KRB5_KDC_ERR_WRONG_REALM client referrals

An AS-REQ with an enterprise principal will always directed a kdc of the local
(default) realm. The KDC directs the client into the direction of the
final realm. See rfc6806.txt.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/heimdal/lib/krb5/init_creds_pw.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/source4/heimdal/lib/krb5/init_creds_pw.c b/source4/heimdal/lib/krb5/init_creds_pw.c
index 6c87412..b6c0a64 100644
--- a/source4/heimdal/lib/krb5/init_creds_pw.c
+++ b/source4/heimdal/lib/krb5/init_creds_pw.c
@@ -1798,6 +1798,18 @@ krb5_init_creds_step(krb5_context context,
 					       ctx->cred.client,
 					       *ctx->error.crealm);
 
+		if (ret)
+		    goto out;
+
+		if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
+		    ret = krb5_init_creds_set_service(context, ctx, NULL);
+		    if (ret)
+			goto out;
+		}
+
+		free_AS_REQ(&ctx->as_req);
+		memset(&ctx->as_req, 0, sizeof(ctx->as_req));
+
 		ctx->used_pa_types = 0;
 	    }
 	    if (ret)
@@ -1805,6 +1817,15 @@ krb5_init_creds_step(krb5_context context,
 	}
     }
 
+    if (ctx->as_req.req_body.cname == NULL) {
+	ret = init_as_req(context, ctx->flags, &ctx->cred,
+			  ctx->addrs, ctx->etypes, &ctx->as_req);
+	if (ret) {
+	    free_init_creds_ctx(context, ctx);
+	    return ret;
+	}
+    }
+
     if (ctx->as_req.padata) {
 	free_METHOD_DATA(ctx->as_req.padata);
 	free(ctx->as_req.padata);
-- 
1.9.1


From 9d2582392c7a4f8d5310125e3fd91d88cf5775bb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 13 Feb 2015 08:55:11 +0100
Subject: [PATCH 09/28] heimdal:lib/krb5: add krb5_mk_error_ext() helper
 function

KRB5_KDC_ERR_WRONG_REALM error messages skip the cname in the response.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/heimdal/lib/krb5/mk_error.c         | 49 +++++++++++++++++++++--------
 source4/heimdal/lib/krb5/version-script.map |  1 +
 2 files changed, 37 insertions(+), 13 deletions(-)

diff --git a/source4/heimdal/lib/krb5/mk_error.c b/source4/heimdal/lib/krb5/mk_error.c
index 5fee1d6..7f0be71 100644
--- a/source4/heimdal/lib/krb5/mk_error.c
+++ b/source4/heimdal/lib/krb5/mk_error.c
@@ -34,15 +34,16 @@
 #include "krb5_locl.h"
 
 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_mk_error(krb5_context context,
-	      krb5_error_code error_code,
-	      const char *e_text,
-	      const krb5_data *e_data,
-	      const krb5_principal client,
-	      const krb5_principal server,
-	      time_t *client_time,
-	      int *client_usec,
-	      krb5_data *reply)
+krb5_mk_error_ext(krb5_context context,
+		  krb5_error_code error_code,
+		  const char *e_text,
+		  const krb5_data *e_data,
+		  const krb5_principal server,
+		  const PrincipalName *client_name,
+		  const Realm *client_realm,
+		  time_t *client_time,
+		  int *client_usec,
+		  krb5_data *reply)
 {
     const char *e_text2 = NULL;
     KRB_ERROR msg;
@@ -78,10 +79,8 @@ krb5_mk_error(krb5_context context,
 	static char unspec[] = "<unspecified realm>";
 	msg.realm = unspec;
     }
-    if(client){
-	msg.crealm = &client->realm;
-	msg.cname = &client->name;
-    }
+    msg.crealm = rk_UNCONST(client_realm);
+    msg.cname = rk_UNCONST(client_name);
 
     ASN1_MALLOC_ENCODE(KRB_ERROR, reply->data, reply->length, &msg, &len, ret);
     if (e_text2)
@@ -92,3 +91,27 @@ krb5_mk_error(krb5_context context,
 	krb5_abortx(context, "internal error in ASN.1 encoder");
     return 0;
 }
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_error(krb5_context context,
+	      krb5_error_code error_code,
+	      const char *e_text,
+	      const krb5_data *e_data,
+	      const krb5_principal client,
+	      const krb5_principal server,
+	      time_t *client_time,
+	      int *client_usec,
+	      krb5_data *reply)
+{
+    const PrincipalName *client_name = NULL;
+    const Realm *client_realm = NULL;
+
+    if (client) {
+	client_realm = &client->realm;
+	client_name = &client->name;
+    }
+
+    return krb5_mk_error_ext(context, error_code, e_text, e_data,
+			     server, client_name, client_realm,
+			     client_time, client_usec, reply);
+}
diff --git a/source4/heimdal/lib/krb5/version-script.map b/source4/heimdal/lib/krb5/version-script.map
index 818e6e0..4044147 100644
--- a/source4/heimdal/lib/krb5/version-script.map
+++ b/source4/heimdal/lib/krb5/version-script.map
@@ -435,6 +435,7 @@ HEIMDAL_KRB5_2.0 {
 		krb5_make_principal;
 		krb5_max_sockaddr_size;
 		krb5_mk_error;
+		krb5_mk_error_ext;
 		krb5_mk_priv;
 		krb5_mk_rep;
 		krb5_mk_req;
-- 
1.9.1


From 33c4fb4199414fb98b4e14628012088ba38d8cc1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 12 Feb 2015 00:07:14 +0100
Subject: [PATCH 10/28] heimdal:kdc: generic support for 3part
 servicePrincipalNames

This is not DRSUAPI specific...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/heimdal/kdc/krb5tgs.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
index 4568177..ca589e8 100644
--- a/source4/heimdal/kdc/krb5tgs.c
+++ b/source4/heimdal/kdc/krb5tgs.c
@@ -1120,15 +1120,14 @@ need_referral(krb5_context context, krb5_kdc_configuration *config,
 
     if (server->name.name_string.len == 1)
 	name = server->name.name_string.val[0];
-    else if (server->name.name_string.len == 3 &&
-	     strcasecmp("E3514235-4B06-11D1-AB04-00C04FC2DCD2", server->name.name_string.val[0]) == 0) {
+    else if (server->name.name_string.len == 3) {
 	/*
 	  This is used to give referrals for the
 	  E3514235-4B06-11D1-AB04-00C04FC2DCD2/NTDSGUID/DNSDOMAIN
 	  SPN form, which is used for inter-domain communication in AD
 	 */
 	name = server->name.name_string.val[2];
-	kdc_log(context, config, 0, "Giving 3 part DRSUAPI referral for %s", name);
+	kdc_log(context, config, 0, "Giving 3 part referral for %s", name);
 	*realms = malloc(sizeof(char *)*2);
 	if (*realms == NULL) {
 	    krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
-- 
1.9.1


From d75da8f1885cebba9217bd34d7db06a39df4c607 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 10 Feb 2015 14:37:29 +0100
Subject: [PATCH 11/28] heimdal:kdc: add support for HDB_ERR_WRONG_REALM

A backend can return this if asked with HDB_F_GET_CLIENT|HDB_F_FOR_AS_REQ
for a KRB5_NT_ENTERPRISE_PRINCIPAL record or for HDB_F_GET_SERVER | HDB_F_FOR_TGS_REQ.

entry_ex->entry.principal->realm needs to return the real of the principal
(or at least a the realm of the next cross-realm trust hop).

This is needed to route enterprise principals between AD domain trusts.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/heimdal/kdc/kerberos5.c    | 26 +++++++++++++++++++++++++-
 source4/heimdal/kdc/krb5tgs.c      | 24 ++++++++++++++++++++++++
 source4/heimdal/kdc/misc.c         |  7 +++++++
 source4/heimdal/lib/hdb/hdb_err.et |  1 +
 4 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c
index cb97390..7e7aefd 100644
--- a/source4/heimdal/kdc/kerberos5.c
+++ b/source4/heimdal/kdc/kerberos5.c
@@ -1060,6 +1060,30 @@ _kdc_as_rep(krb5_context context,
     if(ret == HDB_ERR_NOT_FOUND_HERE) {
 	kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name);
 	goto out;
+    } else if (ret == HDB_ERR_WRONG_REALM) {
+	char *fixed_client_name = NULL;
+
+	ret = krb5_unparse_name(context, client->entry.principal,
+				&fixed_client_name);
+	if (ret) {
+	    goto out;
+	}
+
+	kdc_log(context, config, 0, "WRONG_REALM - %s -> %s",
+		client_name, fixed_client_name);
+	free(fixed_client_name);
+
+	ret = krb5_mk_error_ext(context,
+				KRB5_KDC_ERR_WRONG_REALM,
+				NULL, /* e_text */
+				NULL, /* e_data */
+				server_princ,
+				NULL, /* client_name */
+				&client->entry.principal->realm,
+				NULL, /* client_time */
+				NULL, /* client_usec */
+				reply);
+	goto out;
     } else if(ret){
 	const char *msg = krb5_get_error_message(context, ret);
 	kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg);
@@ -1779,7 +1803,7 @@ _kdc_as_rep(krb5_context context,
 
 out:
     free_AS_REP(&rep);
-    if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE){
+    if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && reply->length == 0) {
 	krb5_mk_error(context,
 		      ret,
 		      e_text,
diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
index ca589e8..f2e581e 100644
--- a/source4/heimdal/kdc/krb5tgs.c
+++ b/source4/heimdal/kdc/krb5tgs.c
@@ -1616,6 +1616,30 @@ server_lookup:
     if(ret == HDB_ERR_NOT_FOUND_HERE) {
 	kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp);
 	goto out;
+    } else if (ret == HDB_ERR_WRONG_REALM) {
+	if (ref_realm)
+	    free(ref_realm);
+	ref_realm = strdup(server->entry.principal->realm);
+	if (ref_realm == NULL) {
+	    ret = ENOMEM;
+	    goto out;
+	}
+
+	kdc_log(context, config, 5,
+		"Returning a referral to realm %s for "
+		"server %s.",
+		ref_realm, spn);
+	krb5_free_principal(context, sp);
+	free(spn);
+	ret = krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
+				  ref_realm, NULL);
+	if (ret)
+	    goto out;
+	ret = krb5_unparse_name(context, sp, &spn);
+	if (ret)
+	    goto out;
+
+	goto server_lookup;
     } else if(ret){
 	const char *new_rlm, *msg;
 	Realm req_rlm;
diff --git a/source4/heimdal/kdc/misc.c b/source4/heimdal/kdc/misc.c
index 4ef5439..b0bc38a 100644
--- a/source4/heimdal/kdc/misc.c
+++ b/source4/heimdal/kdc/misc.c
@@ -99,6 +99,13 @@ _kdc_db_fetch(krb5_context context,
 	config->db[i]->hdb_close(context, config->db[i]);
 
 	switch (ret) {
+	case HDB_ERR_WRONG_REALM:
+	    /*
+	     * the ent->entry.principal just contains hints for the client
+	     * to retry. This is important for enterprise principal routing
+	     * between trusts.
+	     */
+	    /* fall through */
 	case 0:
 	    if (db)
 		*db = config->db[i];
diff --git a/source4/heimdal/lib/hdb/hdb_err.et b/source4/heimdal/lib/hdb/hdb_err.et
index 0bdcb38..135747b 100644
--- a/source4/heimdal/lib/hdb/hdb_err.et
+++ b/source4/heimdal/lib/hdb/hdb_err.et
@@ -27,5 +27,6 @@ error_code MANDATORY_OPTION,	"Entry contains unknown mandatory extension"
 error_code NO_WRITE_SUPPORT,	"HDB backend doesn't contain write support"
 error_code NOT_FOUND_HERE,	"The secret for this entry is not replicated to this database"
 error_code MISUSE,		"Incorrect use of the API"
+error_code WRONG_REALM,		"The principal exists in another realm."
 
 end
-- 
1.9.1


From d7adc33fe62057b97ca69f1f140e5750df06e6a8 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 2 Feb 2015 13:12:36 +0100
Subject: [PATCH 12/28] s4:dsdb/common: add helper functions for trusted domain
 objects (tdo)

The most important things is the dsdb_trust_routing_table with the
dsdb_trust_routing_table_load() and dsdb_trust_routing_tln() functions.

The routing table has knowledge about trusted domains/forests and
enables the dsdb_trust_routing_tln() function to find the direct trust
that is responsable for the given top level name (tln).

This will be used in the kdc and later winbindd to handle cross-trust/forest
routing.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/common/util_trusts.c | 1296 +++++++++++++++++++++++++++++++++++++
 source4/dsdb/samdb/samdb.h        |    2 +
 source4/dsdb/wscript_build        |    2 +-
 3 files changed, 1299 insertions(+), 1 deletion(-)
 create mode 100644 source4/dsdb/common/util_trusts.c

diff --git a/source4/dsdb/common/util_trusts.c b/source4/dsdb/common/util_trusts.c
new file mode 100644
index 0000000..b04fd78
--- /dev/null
+++ b/source4/dsdb/common/util_trusts.c
@@ -0,0 +1,1296 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Stefan Metzmacher 2015
+
+   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/>.
+*/
+
+#include "includes.h"
+#include "ldb.h"
+#include "../lib/util/util_ldb.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "../libds/common/flags.h"
+#include "dsdb/common/proto.h"
+#include "param/param.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "lib/util/tsort.h"
+#include "dsdb/common/util.h"
+#include "libds/common/flag_mapping.h"
+#include "../lib/util/dlinklist.h"
+#include "../lib/crypto/crypto.h"
+
+static NTSTATUS dsdb_trust_forest_record_to_lsa(TALLOC_CTX *mem_ctx,
+					 const struct ForestTrustInfoRecord *ftr,
+					 struct lsa_ForestTrustRecord **_lftr)
+{
+	struct lsa_ForestTrustRecord *lftr = NULL;
+	const struct ForestTrustString *str = NULL;
+	struct lsa_StringLarge *lstr = NULL;
+	const struct ForestTrustDataDomainInfo *info = NULL;
+	struct lsa_ForestTrustDomainInfo *linfo = NULL;
+
+	*_lftr = NULL;
+
+	lftr = talloc_zero(mem_ctx, struct lsa_ForestTrustRecord);
+	if (lftr == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	lftr->flags = ftr->flags;
+	lftr->time = ftr->timestamp;
+	lftr->type = ftr->type;
+
+	switch (lftr->type) {
+	case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+		lstr = &lftr->forest_trust_data.top_level_name;
+		str = &ftr->data.name;
+
+		lstr->string = talloc_strdup(mem_ctx, str->string);
+		if (lstr->string == NULL) {
+			TALLOC_FREE(lftr);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		break;
+
+	case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+		lstr = &lftr->forest_trust_data.top_level_name_ex;
+		str = &ftr->data.name;
+
+		lstr->string = talloc_strdup(mem_ctx, str->string);
+		if (lstr->string == NULL) {
+			TALLOC_FREE(lftr);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		break;
+
+	case LSA_FOREST_TRUST_DOMAIN_INFO:
+		linfo = &lftr->forest_trust_data.domain_info;
+		info = &ftr->data.info;
+
+		linfo->domain_sid = dom_sid_dup(lftr, &info->sid);
+		if (linfo->domain_sid == NULL) {
+			TALLOC_FREE(lftr);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		lstr = &linfo->dns_domain_name;
+		str = &info->dns_name;
+		lstr->string = talloc_strdup(mem_ctx, str->string);
+		if (lstr->string == NULL) {
+			TALLOC_FREE(lftr);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		lstr = &linfo->netbios_domain_name;
+		str = &info->netbios_name;
+		lstr->string = talloc_strdup(mem_ctx, str->string);
+		if (lstr->string == NULL) {
+			TALLOC_FREE(lftr);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		break;
+
+	default:
+		return NT_STATUS_NOT_SUPPORTED;
+	}
+
+	*_lftr = lftr;
+	return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_forest_info_to_lsa(TALLOC_CTX *mem_ctx,
+				       const struct ForestTrustInfo *fti,
+				       struct lsa_ForestTrustInformation **_lfti)
+{
+	struct lsa_ForestTrustInformation *lfti;
+	uint32_t i;
+
+	*_lfti = NULL;
+
+	if (fti->version != 1) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	lfti = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
+	if (fti == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	lfti->count = fti->count;
+	lfti->entries = talloc_zero_array(mem_ctx,
+					  struct lsa_ForestTrustRecord *,
+					  lfti->count);
+	if (lfti->entries == NULL) {
+		TALLOC_FREE(lfti);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i = 0; i < fti->count; i++) {
+		struct ForestTrustInfoRecord *ftr = &fti->records[i].record;
+		struct lsa_ForestTrustRecord *lftr = NULL;
+		NTSTATUS status;
+
+		status = dsdb_trust_forest_record_to_lsa(lfti->entries, ftr,
+							 &lftr);
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(lfti);
+			return NT_STATUS_NO_MEMORY;
+		}
+		lfti->entries[i] = lftr;
+	}
+
+	*_lfti = lfti;
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS dsdb_trust_parse_crossref_info(TALLOC_CTX *mem_ctx,
+					struct ldb_context *sam_ctx,
+					const struct ldb_message *msg,
+					struct lsa_TrustDomainInfoInfoEx **_tdo)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+	const char *dns = NULL;
+	const char *netbios = NULL;
+	struct ldb_dn *nc_dn = NULL;
+	struct dom_sid sid = {};
+	NTSTATUS status;
+
+	*_tdo = NULL;
+	tdo = talloc_zero(mem_ctx, struct lsa_TrustDomainInfoInfoEx);
+	if (tdo == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+	talloc_steal(frame, tdo);
+
+	dns = ldb_msg_find_attr_as_string(msg, "dnsRoot", NULL);
+	if (dns == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+	tdo->domain_name.string = talloc_strdup(tdo, dns);
+	if (tdo->domain_name.string == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	netbios = ldb_msg_find_attr_as_string(msg, "nETBIOSName", NULL);
+	if (netbios == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+	tdo->netbios_name.string = talloc_strdup(tdo, netbios);
+	if (tdo->netbios_name.string == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	nc_dn = samdb_result_dn(sam_ctx, frame, msg, "ncName", NULL);
+	if (nc_dn == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+
+	status = dsdb_get_extended_dn_sid(nc_dn, &sid, "SID");
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return status;
+	}
+	tdo->sid = dom_sid_dup(tdo, &sid);
+	if (tdo->sid == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	tdo->trust_type = LSA_TRUST_TYPE_UPLEVEL;
+	tdo->trust_direction = LSA_TRUST_DIRECTION_INBOUND |
+			       LSA_TRUST_DIRECTION_OUTBOUND;
+	tdo->trust_attributes = LSA_TRUST_ATTRIBUTE_WITHIN_FOREST;
+
+	*_tdo = talloc_move(mem_ctx, &tdo);
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS dsdb_trust_crossref_tdo_info(TALLOC_CTX *mem_ctx,
+			struct ldb_context *sam_ctx,
+			struct ldb_dn *domain_dn,
+			const char *extra_filter,
+			struct lsa_TrustDomainInfoInfoEx **_tdo,
+			struct lsa_TrustDomainInfoInfoEx **_root_trust_tdo,
+			struct lsa_TrustDomainInfoInfoEx **_trust_parent_tdo)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+	struct lsa_TrustDomainInfoInfoEx *root_trust_tdo = NULL;
+	struct lsa_TrustDomainInfoInfoEx *trust_parent_tdo = NULL;
+	struct ldb_dn *partitions_dn = NULL;
+	const char * const cross_attrs[] = {
+		"dnsRoot",
+		"nETBIOSName",
+		"nCName",
+		"rootTrust",
+		"trustParent",
+		NULL,
+	};
+	struct ldb_result *cross_res = NULL;
+	struct ldb_message *msg = NULL;
+	struct ldb_dn *root_trust_dn = NULL;
+	struct ldb_dn *trust_parent_dn = NULL;
+	NTSTATUS status;
+	int ret;
+
+	if (extra_filter == NULL) {
+		extra_filter = "";
+	}
+
+	*_tdo = NULL;
+	if (_root_trust_tdo != NULL) {
+		*_root_trust_tdo = NULL;
+	}
+	if (_trust_parent_tdo != NULL) {
+		*_trust_parent_tdo = NULL;
+	}
+
+	domain_dn = ldb_get_default_basedn(sam_ctx);
+	if (domain_dn == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	partitions_dn = samdb_partitions_dn(sam_ctx, frame);
+	if (partitions_dn == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ret = dsdb_search(sam_ctx, partitions_dn, &cross_res,
+			  partitions_dn, LDB_SCOPE_ONELEVEL,
+			  cross_attrs,
+			  DSDB_SEARCH_ONE_ONLY |
+			  DSDB_SEARCH_SHOW_EXTENDED_DN,
+			  "(&"
+			    "(ncName=%s)"
+			    "(objectClass=crossRef)"
+			    "(systemFlags:%s:=%u)"
+			    "%s"
+			  ")",
+			  ldb_dn_get_linearized(domain_dn),
+			  LDB_OID_COMPARATOR_AND,
+			  SYSTEM_FLAG_CR_NTDS_DOMAIN,
+			  extra_filter);
+	if (ret != LDB_SUCCESS) {
+		TALLOC_FREE(frame);
+		return dsdb_ldb_err_to_ntstatus(ret);
+	}
+	msg = cross_res->msgs[0];
+
+	status = dsdb_trust_parse_crossref_info(mem_ctx, sam_ctx, msg, &tdo);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return status;
+	}
+	talloc_steal(frame, tdo);
+
+	if (_root_trust_tdo != NULL) {
+		root_trust_dn = samdb_result_dn(sam_ctx, frame, msg,
+						"rootTrust", NULL);
+	}
+	if (_trust_parent_tdo != NULL) {
+		trust_parent_dn = samdb_result_dn(sam_ctx, frame, msg,
+						   "trustParent", NULL);
+	}
+
+	if (root_trust_dn != NULL) {
+		struct ldb_message *root_trust_msg = NULL;
+
+		ret = dsdb_search_one(sam_ctx, frame,
+				      &root_trust_msg,
+				      root_trust_dn,
+				      LDB_SCOPE_BASE,
+				      cross_attrs,
+				      DSDB_SEARCH_NO_GLOBAL_CATALOG,
+				      "(objectClass=crossRef)");
+		if (ret != LDB_SUCCESS) {
+			status = dsdb_ldb_err_to_ntstatus(ret);
+			DEBUG(3, ("Failed to search for %s: %s - %s\n",
+				  ldb_dn_get_linearized(root_trust_dn),
+				  nt_errstr(status), ldb_errstring(sam_ctx)));
+			TALLOC_FREE(frame);
+			return status;
+		}
+
+		status = dsdb_trust_parse_crossref_info(mem_ctx, sam_ctx,
+							root_trust_msg,
+							&root_trust_tdo);
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(frame);
+			return status;
+		}
+		talloc_steal(frame, root_trust_tdo);
+	}
+
+	if (trust_parent_dn != NULL) {
+		struct ldb_message *trust_parent_msg = NULL;
+
+		ret = dsdb_search_one(sam_ctx, frame,
+				      &trust_parent_msg,
+				      trust_parent_dn,
+				      LDB_SCOPE_BASE,
+				      cross_attrs,
+				      DSDB_SEARCH_NO_GLOBAL_CATALOG,
+				      "(objectClass=crossRef)");
+		if (ret != LDB_SUCCESS) {
+			status = dsdb_ldb_err_to_ntstatus(ret);
+			DEBUG(3, ("Failed to search for %s: %s - %s\n",
+				  ldb_dn_get_linearized(trust_parent_dn),
+				  nt_errstr(status), ldb_errstring(sam_ctx)));
+			TALLOC_FREE(frame);
+			return status;
+		}
+
+		status = dsdb_trust_parse_crossref_info(mem_ctx, sam_ctx,
+							trust_parent_msg,
+							&trust_parent_tdo);
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(frame);
+			return status;
+		}
+		talloc_steal(frame, trust_parent_tdo);
+	}
+
+	*_tdo = talloc_move(mem_ctx, &tdo);
+	if (_root_trust_tdo != NULL) {
+		*_root_trust_tdo = talloc_move(mem_ctx, &root_trust_tdo);
+	}
+	if (_trust_parent_tdo != NULL) {
+		*_trust_parent_tdo = talloc_move(mem_ctx, &trust_parent_tdo);
+	}
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+#define DNS_CMP_MATCH 0
+#define DNS_CMP_FIRST_IS_CHILD 1
+#define DNS_CMP_SECOND_IS_CHILD 2
+#define DNS_CMP_NO_MATCH 3
+
+/* this function assumes names are well formed DNS names.
+ * it doesn't validate them */
+static int dns_cmp(const char *s1, const char *s2)
+{
+	size_t l1 = 0, l2 = 0;
+	const char *p1 = NULL, *p2 = NULL;
+	size_t t1, t2;
+	int cret;
+
+	if (s1 != NULL) {
+		l1 = strlen(s1);
+	}
+
+	if (s2 != NULL) {
+		l2 = strlen(s2);
+	}
+
+	/*
+	 * trailing '.' are ignored.
+	 */
+	if (l1 > 1 && s1[l1 - 1] == '.') {
+		l1--;
+	}
+	if (l2 > 1 && s2[l2 - 1] == '.') {
+		l2--;
+	}
+
+	if (l1 == l2) {
+		if (strcasecmp_m(s1, s2) == 0) {
+			return DNS_CMP_MATCH;
+		}
+		return DNS_CMP_NO_MATCH;
+	}
+
+	if (l1 > l2) {
+		p1 = s1;
+		p2 = s2;
+		t1 = l1;
+		t2 = l2;
+		cret = DNS_CMP_FIRST_IS_CHILD;
+	} else {
+		p1 = s2;
+		p2 = s1;
+		t1 = l2;
+		t2 = l1;
+		cret = DNS_CMP_SECOND_IS_CHILD;
+	}
+
+	if (p1[t1 - t2 - 1] != '.') {
+		return DNS_CMP_NO_MATCH;
+	}
+
+	if (strcasecmp_m(&p1[t1 - t2], p2) == 0) {
+		return cret;
+	}
+
+	return DNS_CMP_NO_MATCH;
+}
+
+static int sort_forest_domain_msgs(struct ldb_message **_m1,
+				   struct ldb_message **_m2)
+{
+	struct ldb_message *m1 = *_m1;
+	struct ldb_message *m2 = *_m2;
+	const char *dns1 = NULL;
+	const char *dns2 = NULL;
+	int cmp;
+	struct ldb_message_element *rootTrust1 = NULL;
+	struct ldb_message_element *trustParent1 = NULL;
+	struct ldb_message_element *rootTrust2 = NULL;
+	struct ldb_message_element *trustParent2 = NULL;
+
+	dns1 = ldb_msg_find_attr_as_string(m1, "dnsRoot", NULL);
+	dns2 = ldb_msg_find_attr_as_string(m2, "dnsRoot", NULL);
+
+	if (dns1 == NULL && dns2 == NULL) {
+		return 0;
+	}
+	if (dns1 == NULL) {
+		return 1;
+	}
+	if (dns2 == NULL) {
+		return -1;
+	}
+
+	cmp = dns_cmp(dns1, dns2);
+	switch (cmp) {
+	case DNS_CMP_MATCH:
+		break;
+	case DNS_CMP_FIRST_IS_CHILD:
+		return -1;
+	case DNS_CMP_SECOND_IS_CHILD:
+		return 1;
+	case DNS_CMP_NO_MATCH:
+		break;
+	}
+
+	rootTrust1 = ldb_msg_find_element(m1, "rootTrust");
+	trustParent1 = ldb_msg_find_element(m1, "trustParent");
+	rootTrust2 = ldb_msg_find_element(m2, "rootTrust");
+	trustParent2 = ldb_msg_find_element(m2, "trustParent");
+
+	if (rootTrust1 == NULL && trustParent1 == NULL) {
+		/* m1 is the forest root */
+		return -1;
+	}
+	if (rootTrust2 == NULL && trustParent2 == NULL) {
+		/* m2 is the forest root */
+		return 1;
+	}
+
+	return strcasecmp(dns1, dns2);
+}
+
+static bool find_top_level_match(const struct lsa_ForestTrustInformation *info,
+				 const char *dns)
+{
+	struct lsa_ForestTrustRecord *e = NULL;
+	struct lsa_StringLarge *t = NULL;
+	unsigned int j;
+
+	for (j=0; j < info->count; j++) {
+		int cmp;
+
+		e = info->entries[j];
+
+		if (e->type != LSA_FOREST_TRUST_TOP_LEVEL_NAME) {
+			continue;
+		}
+
+		t = &e->forest_trust_data.top_level_name;
+
+		cmp = dns_cmp(dns, t->string);
+		switch (cmp) {
+		case DNS_CMP_MATCH:
+		case DNS_CMP_FIRST_IS_CHILD:
+			/* already in the list */
+			return true;
+
+		case DNS_CMP_SECOND_IS_CHILD:
+			/*
+			 * This might be a configuration error
+			 */
+			break;
+
+		case DNS_CMP_NO_MATCH:
+			break;
+		}
+	}
+
+	return false;
+}
+
+NTSTATUS dsdb_trust_xref_forest_info(TALLOC_CTX *mem_ctx,
+				     struct ldb_context *sam_ctx,
+				     struct lsa_ForestTrustInformation **_info)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct lsa_ForestTrustInformation *info = NULL;
+	struct ldb_dn *partitions_dn = NULL;
+	const char * const cross_attrs1[] = {
+		"uPNSuffixes",
+		"msDS-SPNSuffixes",
+		NULL,
+	};
+	struct ldb_result *cross_res1 = NULL;
+	struct ldb_message_element *upn_el = NULL;
+	struct ldb_message_element *spn_el = NULL;
+	const char * const cross_attrs2[] = {
+		"dnsRoot",
+		"nETBIOSName",
+		"nCName",
+		"rootTrust",
+		"trustParent",
+		NULL,
+	};
+	struct ldb_result *cross_res2 = NULL;
+	int ret;
+	unsigned int i;
+
+	*_info = NULL;
+	info = talloc_zero(mem_ctx, struct lsa_ForestTrustInformation);
+	if (info == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+	talloc_steal(frame, info);
+
+	partitions_dn = samdb_partitions_dn(sam_ctx, frame);
+	if (partitions_dn == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ret = dsdb_search_dn(sam_ctx, partitions_dn, &cross_res1,
+			     partitions_dn, cross_attrs1, 0);
+	if (ret != LDB_SUCCESS) {
+		TALLOC_FREE(frame);
+		return dsdb_ldb_err_to_ntstatus(ret);
+	}
+
+	ret = dsdb_search(sam_ctx, partitions_dn, &cross_res2,
+			  partitions_dn, LDB_SCOPE_ONELEVEL,
+			  cross_attrs2,
+			  DSDB_SEARCH_SHOW_EXTENDED_DN,
+			  "(&(objectClass=crossRef)"
+			   "(systemFlags:%s:=%u))",
+			  LDB_OID_COMPARATOR_AND,
+			  SYSTEM_FLAG_CR_NTDS_DOMAIN);
+	if (ret != LDB_SUCCESS) {
+		TALLOC_FREE(frame);
+		return dsdb_ldb_err_to_ntstatus(ret);
+	}
+
+	/*
+	 * Sort the domains as trees, starting with the forest root
+	 */
+	TYPESAFE_QSORT(cross_res2->msgs, cross_res2->count,
+		       sort_forest_domain_msgs);
+
+	/*
+	 * One TOP_LEVEL_NAME and one DOMAIN_INFO element
+	 * per domain.
+	 */
+	info->count += cross_res2->count;
+	info->count += cross_res2->count;
+
+	/*
+	 * And one TOP_LEVEL_NAME per uPNSuffixes or msDS-SPNSuffixes value
+	 */
+	upn_el = ldb_msg_find_element(cross_res1->msgs[0], "uPNSuffixes");
+	if (upn_el != NULL) {
+		info->count += upn_el->num_values;
+	}
+	spn_el = ldb_msg_find_element(cross_res1->msgs[0], "msDS-SPNSuffixes");
+	if (spn_el != NULL) {
+		info->count += spn_el->num_values;
+	}
+
+	info->entries = talloc_zero_array(info, struct lsa_ForestTrustRecord *,
+					  info->count);
+	if (info->entries == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+	info->count = 0;
+
+	for (i=0; i < cross_res2->count; i++) {
+		struct ldb_message *m = cross_res2->msgs[i];
+		const char *dns = NULL;
+		const char *netbios = NULL;
+		struct ldb_dn *nc_dn = NULL;
+		struct dom_sid sid = {};
+		struct lsa_ForestTrustRecord *e = NULL;
+		struct lsa_ForestTrustDomainInfo *d = NULL;
+		struct lsa_StringLarge *t = NULL;
+		bool match = false;
+		NTSTATUS status;
+
+		dns = ldb_msg_find_attr_as_string(m, "dnsRoot", NULL);
+		if (dns == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		}
+
+		netbios = ldb_msg_find_attr_as_string(m, "nETBIOSName", NULL);
+		if (netbios == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		}
+
+		nc_dn = samdb_result_dn(sam_ctx, m, m, "ncName", NULL);
+		if (nc_dn == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		}
+
+		status = dsdb_get_extended_dn_sid(nc_dn, &sid, "SID");
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(frame);
+			return status;
+		}
+
+		match = find_top_level_match(info, dns);
+		if (!match) {
+			/*
+			 * First the TOP_LEVEL_NAME, if required
+			 */
+			e = talloc_zero(info->entries, struct lsa_ForestTrustRecord);
+			if (e == NULL) {
+				TALLOC_FREE(frame);
+				return NT_STATUS_NO_MEMORY;
+			}
+
+			e->flags = 0;
+			e->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
+			e->time = 0; /* so far always 0 in traces. */
+			t = &e->forest_trust_data.top_level_name;
+			t->string = talloc_strdup(e, dns);
+			if (t->string == NULL) {
+				TALLOC_FREE(frame);
+				return NT_STATUS_NO_MEMORY;
+			}
+
+			info->entries[info->count++] = e;
+		}
+
+		/*
+		 * Then the DOMAIN_INFO
+		 */
+		e = talloc_zero(info->entries, struct lsa_ForestTrustRecord);
+		if (e == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		e->flags = 0;
+		e->type = LSA_FOREST_TRUST_DOMAIN_INFO;
+		e->time = 0; /* so far always 0 in traces. */
+		d = &e->forest_trust_data.domain_info;
+		d->domain_sid = dom_sid_dup(e, &sid);
+		if (d->domain_sid == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+		d->dns_domain_name.string = talloc_strdup(e, dns);
+		if (d->dns_domain_name.string == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+		d->netbios_domain_name.string = talloc_strdup(e, netbios);
+		if (d->netbios_domain_name.string == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		info->entries[info->count++] = e;
+	}
+
+	for (i=0; (upn_el != NULL) && i < upn_el->num_values; i++) {
+		const struct ldb_val *v = &upn_el->values[i];
+		const char *dns = (const char *)v->data;
+		struct lsa_ForestTrustRecord *e = NULL;
+		struct lsa_StringLarge *t = NULL;
+		bool match = false;
+
+		if (dns == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		}
+
+		match = find_top_level_match(info, dns);
+		if (match) {
+			continue;
+		}
+
+		/*
+		 * an additional the TOP_LEVEL_NAME
+		 */
+		e = talloc_zero(info->entries, struct lsa_ForestTrustRecord);
+		if (e == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		e->flags = 0;
+		e->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
+		e->time = 0; /* so far always 0 in traces. */
+		t = &e->forest_trust_data.top_level_name;
+		t->string = talloc_strdup(e, dns);
+		if (t->string == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		info->entries[info->count++] = e;
+	}
+
+	for (i=0; (spn_el != NULL) && i < spn_el->num_values; i++) {
+		const struct ldb_val *v = &spn_el->values[i];
+		const char *dns = (const char *)v->data;
+		struct lsa_ForestTrustRecord *e = NULL;
+		struct lsa_StringLarge *t = NULL;
+		bool match = false;
+
+		if (dns == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		}
+
+		match = find_top_level_match(info, dns);
+		if (match) {
+			continue;
+		}
+
+		/*
+		 * an additional the TOP_LEVEL_NAME
+		 */
+		e = talloc_zero(info->entries, struct lsa_ForestTrustRecord);
+		if (e == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		e->flags = 0;
+		e->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
+		e->time = 0; /* so far always 0 in traces. */
+		t = &e->forest_trust_data.top_level_name;
+		t->string = talloc_strdup(e, dns);
+		if (t->string == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		info->entries[info->count++] = e;
+	}
+
+	*_info = talloc_move(mem_ctx, &info);
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_parse_tdo_info(TALLOC_CTX *mem_ctx,
+				   struct ldb_message *m,
+				   struct lsa_TrustDomainInfoInfoEx **_tdo)
+{
+	struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+	const char *dns = NULL;
+	const char *netbios = NULL;
+
+	*_tdo = NULL;
+
+	tdo = talloc_zero(mem_ctx, struct lsa_TrustDomainInfoInfoEx);
+	if (tdo == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	dns = ldb_msg_find_attr_as_string(m, "trustPartner", NULL);
+	if (dns == NULL) {
+		TALLOC_FREE(tdo);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+	tdo->domain_name.string = talloc_strdup(tdo, dns);
+	if (tdo->domain_name.string == NULL) {
+		TALLOC_FREE(tdo);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	netbios = ldb_msg_find_attr_as_string(m, "flatName", NULL);
+	if (netbios == NULL) {
+		TALLOC_FREE(tdo);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+	tdo->netbios_name.string = talloc_strdup(tdo, netbios);
+	if (tdo->netbios_name.string == NULL) {
+		TALLOC_FREE(tdo);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	tdo->sid = samdb_result_dom_sid(tdo, m, "securityIdentifier");
+	if (tdo->sid == NULL) {
+		TALLOC_FREE(tdo);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+
+	tdo->trust_type = ldb_msg_find_attr_as_uint(m, "trustType", 0);
+	tdo->trust_direction = ldb_msg_find_attr_as_uint(m, "trustDirection", 0);
+	tdo->trust_attributes = ldb_msg_find_attr_as_uint(m, "trustAttributes", 0);
+
+	*_tdo = tdo;
+	return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_parse_forest_info(TALLOC_CTX *mem_ctx,
+				      struct ldb_message *m,
+				      struct ForestTrustInfo **_fti)
+{
+	const struct ldb_val *ft_blob = NULL;
+	struct ForestTrustInfo *fti = NULL;
+	enum ndr_err_code ndr_err;
+
+	*_fti = NULL;
+
+	ft_blob = ldb_msg_find_ldb_val(m, "msDS-TrustForestTrustInfo");
+	if (ft_blob == NULL) {
+		return NT_STATUS_NOT_FOUND;
+	}
+
+	fti = talloc_zero(mem_ctx, struct ForestTrustInfo);
+	if (fti == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	/* ldb_val is equivalent to DATA_BLOB */
+	ndr_err = ndr_pull_struct_blob_all(ft_blob, fti, fti,
+				(ndr_pull_flags_fn_t)ndr_pull_ForestTrustInfo);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		TALLOC_FREE(fti);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+
+	*_fti = fti;
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS dsdb_trust_search_tdos(struct ldb_context *sam_ctx,
+				const char *exclude,
+				const char * const *attrs,
+				TALLOC_CTX *mem_ctx,
+				struct ldb_result **res)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	int ret;
+	struct ldb_dn *system_dn = NULL;
+	const char *filter = NULL;
+	char *exclude_encoded = NULL;
+
+	*res = NULL;
+
+	system_dn = ldb_dn_copy(frame, ldb_get_default_basedn(sam_ctx));
+	if (system_dn == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (!ldb_dn_add_child_fmt(system_dn, "CN=System")) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (exclude != NULL) {
+		exclude_encoded = ldb_binary_encode_string(frame, exclude);
+		if (exclude_encoded == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		filter = talloc_asprintf(frame,
+				"(&(objectClass=trustedDomain)"
+				  "(|(trustPartner=%s)(flatName=%s))"
+				")",
+				exclude_encoded, exclude_encoded);
+		if (filter == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+	} else {
+		filter = "(objectClass=trustedDomain)";
+	}
+
+	ret = dsdb_search(sam_ctx, mem_ctx, res,
+			  system_dn,
+			  LDB_SCOPE_ONELEVEL, attrs,
+			  DSDB_SEARCH_NO_GLOBAL_CATALOG,
+			  "%s", filter);
+	if (ret != LDB_SUCCESS) {
+		NTSTATUS status = dsdb_ldb_err_to_ntstatus(ret);
+		DEBUG(3, ("Failed to search for %s: %s - %s\n",
+			  filter, nt_errstr(status), ldb_errstring(sam_ctx)));
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+struct dsdb_trust_routing_domain;
+
+struct dsdb_trust_routing_table {
+	struct dsdb_trust_routing_domain *domains;
+};
+
+struct dsdb_trust_routing_domain {
+	struct dsdb_trust_routing_domain *prev, *next;
+
+	struct lsa_TrustDomainInfoInfoEx *tdo;
+	struct lsa_ForestTrustInformation *fti;
+};
+
+NTSTATUS dsdb_trust_routing_table_load(struct ldb_context *sam_ctx,
+				       TALLOC_CTX *mem_ctx,
+				       struct dsdb_trust_routing_table **_table)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct dsdb_trust_routing_table *table;
+	struct dsdb_trust_routing_domain *d = NULL;
+	struct ldb_dn *domain_dn = NULL;
+	struct lsa_TrustDomainInfoInfoEx *root_trust_tdo = NULL;
+	struct lsa_TrustDomainInfoInfoEx *trust_parent_tdo = NULL;
+	struct lsa_TrustDomainInfoInfoEx *root_direction_tdo = NULL;
+	const char * const trusts_attrs[] = {
+		"securityIdentifier",
+		"flatName",
+		"trustPartner",
+		"trustAttributes",
+		"trustDirection",
+		"trustType",
+		"msDS-TrustForestTrustInfo",
+		NULL
+	};
+	struct ldb_result *trusts_res = NULL;
+	unsigned int i;
+	NTSTATUS status;
+
+	*_table = NULL;
+
+	domain_dn = ldb_get_default_basedn(sam_ctx);
+	if (domain_dn == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	table = talloc_zero(mem_ctx, struct dsdb_trust_routing_table);
+	if (table == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+	talloc_steal(frame, table);
+
+	d = talloc_zero(table, struct dsdb_trust_routing_domain);
+	if (d == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	status = dsdb_trust_crossref_tdo_info(d, sam_ctx,
+					      domain_dn, NULL,
+					      &d->tdo,
+					      &root_trust_tdo,
+					      &trust_parent_tdo);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	if (root_trust_tdo != NULL) {
+		root_direction_tdo = root_trust_tdo;
+	} else if (trust_parent_tdo != NULL) {
+		root_direction_tdo = trust_parent_tdo;
+	}
+
+	if (root_direction_tdo == NULL) {
+		/* we're the forest root */
+		status = dsdb_trust_xref_forest_info(d, sam_ctx, &d->fti);
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(frame);
+			return status;
+		}
+	}
+
+	DLIST_ADD(table->domains, d);
+
+	status = dsdb_trust_search_tdos(sam_ctx, NULL, trusts_attrs,
+					frame, &trusts_res);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	for (i = 0; i < trusts_res->count; i++) {
+		bool ok;
+		int cmp;
+
+		d = talloc_zero(table, struct dsdb_trust_routing_domain);
+		if (d == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		status = dsdb_trust_parse_tdo_info(d,
+						   trusts_res->msgs[i],
+						   &d->tdo);
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(frame);
+			return status;
+		}
+
+		DLIST_ADD_END(table->domains, d, NULL);
+
+		if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+			struct ForestTrustInfo *fti = NULL;
+
+			status = dsdb_trust_parse_forest_info(frame,
+							      trusts_res->msgs[i],
+							      &fti);
+			if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+				fti = NULL;
+				status = NT_STATUS_OK;
+			}
+			if (!NT_STATUS_IS_OK(status)) {
+				TALLOC_FREE(frame);
+				return status;
+			}
+
+			if (fti == NULL) {
+				continue;
+			}
+
+			status = dsdb_trust_forest_info_to_lsa(d, fti, &d->fti);
+			if (!NT_STATUS_IS_OK(status)) {
+				TALLOC_FREE(frame);
+				return status;
+			}
+
+			continue;
+		}
+
+		if (!(d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
+			continue;
+		}
+
+		if (root_direction_tdo == NULL) {
+			continue;
+		}
+
+		ok = dom_sid_equal(root_direction_tdo->sid, d->tdo->sid);
+		if (!ok) {
+			continue;
+		}
+
+		cmp = strcasecmp_m(root_direction_tdo->netbios_name.string,
+				   d->tdo->netbios_name.string);
+		if (cmp != 0) {
+			continue;
+		}
+
+		cmp = strcasecmp_m(root_direction_tdo->domain_name.string,
+				   d->tdo->domain_name.string);
+		if (cmp != 0) {
+			continue;
+		}
+
+		/* this our route to the forest root */
+		status = dsdb_trust_xref_forest_info(d, sam_ctx, &d->fti);
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(frame);
+			return status;
+		}
+	}
+
+	*_table = talloc_move(mem_ctx, &table);
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+static void dsdb_trust_update_best_tln(
+	const struct dsdb_trust_routing_domain **best_d,
+	const char **best_tln,
+	const struct dsdb_trust_routing_domain *d,
+	const char *tln)
+{
+	int cmp;
+
+	if (*best_tln == NULL) {
+		*best_tln = tln;
+		*best_d = d;
+		return;
+	}
+
+	cmp = dns_cmp(*best_tln, tln);
+	if (cmp != DNS_CMP_FIRST_IS_CHILD) {
+		return;
+	}
+
+	*best_tln = tln;
+	*best_d = d;
+}
+
+const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_routing_tln(
+		const struct dsdb_trust_routing_table *table,
+		const char *tln)
+{
+	const struct dsdb_trust_routing_domain *best_d = NULL;
+	const char *best_tln = NULL;
+	const struct dsdb_trust_routing_domain *d = NULL;
+
+	if (tln == NULL) {
+		return NULL;
+	}
+
+	for (d = table->domains; d != NULL; d = d->next) {
+		const char *matched_tln = NULL;
+		int matched_cmp = -1;
+		int cmp;
+		bool transitive = false;
+		uint32_t i;
+
+		if (d->tdo->trust_type != LSA_TRUST_TYPE_UPLEVEL) {
+			/*
+			 * Only uplevel trusts have top level names
+			 */
+			continue;
+		}
+
+		if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+			transitive = true;
+		}
+
+		if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+			transitive = true;
+		}
+
+		if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE) {
+			transitive = false;
+		}
+
+		if (!transitive || d->fti == NULL) {
+			if (d->tdo->domain_name.string == NULL) {
+				continue;
+			}
+
+			cmp = dns_cmp(tln, d->tdo->domain_name.string);
+			if (cmp == DNS_CMP_MATCH) {
+				/*
+				 * exact match
+				 */
+				return d->tdo;
+			}
+			if (cmp != DNS_CMP_FIRST_IS_CHILD) {
+				continue;
+			}
+
+			if (!transitive) {
+				continue;
+			}
+
+			dsdb_trust_update_best_tln(&best_d, &best_tln, d,
+						   d->tdo->domain_name.string);
+			continue;
+		}
+
+		for (i = 0; i < d->fti->count; i++ ) {
+			const struct lsa_ForestTrustRecord *f = d->fti->entries[i];
+			const union lsa_ForestTrustData *u = NULL;
+			const char *fti_tln = NULL;
+			bool exclude = false;
+
+			if (f == NULL) {
+				/* broken record */
+				continue;
+			}
+
+			if (f->flags != 0) {
+				/*
+				 * any flag disables the entry.
+				 */
+				continue;
+			}
+
+			u = &f->forest_trust_data;
+
+			switch (f->type) {
+			case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+				fti_tln = u->top_level_name.string;
+				break;
+			case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+				fti_tln = u->top_level_name_ex.string;
+				exclude = true;
+				break;
+			default:
+				break;
+			}
+
+			if (fti_tln == NULL) {
+				continue;
+			}
+
+			cmp = dns_cmp(tln, fti_tln);
+			switch (cmp) {
+			case DNS_CMP_MATCH:
+			case DNS_CMP_FIRST_IS_CHILD:
+				if (exclude) {
+					matched_tln = NULL;
+					matched_cmp = -1;
+				} else {
+					if (matched_cmp != DNS_CMP_MATCH) {
+						matched_tln = fti_tln;
+						matched_cmp = cmp;
+					}
+				}
+				break;
+			default:
+				break;
+			}
+		}
+
+		if (matched_tln == NULL) {
+			continue;
+		}
+
+		dsdb_trust_update_best_tln(&best_d, &best_tln, d, matched_tln);
+	}
+
+	if (best_d != NULL) {
+		return best_d->tdo;
+	}
+
+	return NULL;
+}
diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h
index 635ac70..a25fa13 100644
--- a/source4/dsdb/samdb/samdb.h
+++ b/source4/dsdb/samdb/samdb.h
@@ -29,6 +29,8 @@ struct dsdb_extended_replicated_objects;
 struct loadparm_context;
 struct tevent_context;
 
+struct dsdb_trust_routing_table;
+
 #include "librpc/gen_ndr/security.h"
 #include <ldb.h>
 #include "lib/ldb-samba/ldif_handlers.h"
diff --git a/source4/dsdb/wscript_build b/source4/dsdb/wscript_build
index a80c45c..d045a81 100755
--- a/source4/dsdb/wscript_build
+++ b/source4/dsdb/wscript_build
@@ -13,7 +13,7 @@ bld.SAMBA_LIBRARY('samdb',
 	)
 
 bld.SAMBA_LIBRARY('samdb-common',
-	source='common/util.c common/util_groups.c common/util_samr.c common/dsdb_dn.c common/dsdb_access.c',
+	source='common/util.c common/util_trusts.c common/util_groups.c common/util_samr.c common/dsdb_dn.c common/dsdb_access.c',
 	autoproto='common/proto.h',
 	private_library=True,
 	deps='ldb NDR_DRSBLOBS util_ldb LIBCLI_AUTH samba-hostconfig samba_socket cli-ldap-common flag_mapping'
-- 
1.9.1


From ec96a1435617ee7cfd8e320d0365cd47a3d50cb3 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 10 Feb 2015 14:43:01 +0100
Subject: [PATCH 13/28] s4:kdc/db-glue: implement cross forest routing by
 return HDB_ERR_WRONG_REALM

We lookup the principal against our trust routing table
and return HDB_ERR_WRONG_REALM and the realm of the next trust hoop.

Routing within our own forest is not supported yet.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/kdc/db-glue.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 244 insertions(+)

diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
index 4237c65..edd12f6 100644
--- a/source4/kdc/db-glue.c
+++ b/source4/kdc/db-glue.c
@@ -1744,6 +1744,242 @@ static krb5_error_code samba_kdc_fetch_server(krb5_context context,
 	return ret;
 }
 
+static krb5_error_code samba_kdc_lookup_realm(krb5_context context,
+					      struct samba_kdc_db_context *kdc_db_ctx,
+					      TALLOC_CTX *mem_ctx,
+					      krb5_const_principal principal,
+					      unsigned flags,
+					      hdb_entry_ex *entry_ex)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	NTSTATUS status;
+	krb5_error_code ret;
+	char *_realm = NULL;
+	bool check_realm = false;
+	const char *realm = NULL;
+	const char *dns_parent = NULL;
+	struct dsdb_trust_routing_table *trt = NULL;
+	const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+	unsigned int num_comp;
+	bool ok;
+	char *upper = NULL;
+
+	num_comp = krb5_princ_size(context, principal);
+
+	if (flags & HDB_F_GET_CLIENT) {
+		if (flags & HDB_F_FOR_AS_REQ) {
+			check_realm = true;
+		}
+	}
+	if (flags & HDB_F_GET_SERVER) {
+		if (flags & HDB_F_FOR_TGS_REQ) {
+			check_realm = true;
+		}
+	}
+
+	if (!check_realm) {
+		TALLOC_FREE(frame);
+		return 0;
+	}
+
+	_realm = smb_krb5_principal_get_realm(context, principal);
+	if (_realm == NULL) {
+		TALLOC_FREE(frame);
+		return ENOMEM;
+	}
+
+	/*
+	 * The requested realm needs to be our own
+	 */
+	ok = lpcfg_is_my_domain_or_realm(kdc_db_ctx->lp_ctx, _realm);
+	if (!ok) {
+		/*
+		 * The request should is not for us...
+		 */
+		SAFE_FREE(_realm);
+		TALLOC_FREE(frame);
+		return HDB_ERR_NOENTRY;
+	}
+
+	realm = talloc_strdup(frame, _realm);
+	SAFE_FREE(_realm);
+	if (realm == NULL) {
+		TALLOC_FREE(frame);
+		return ENOMEM;
+	}
+
+	if (krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+		char *principal_string = NULL;
+		krb5_principal enterprise_principal = NULL;
+		char *enterprise_realm = NULL;
+
+		if (num_comp != 1) {
+			TALLOC_FREE(frame);
+			return HDB_ERR_NOENTRY;
+		}
+
+		principal_string = smb_krb5_principal_get_comp_string(frame, context,
+								      principal, 0);
+		if (principal_string == NULL) {
+			TALLOC_FREE(frame);
+			return ENOMEM;
+		}
+
+		ret = krb5_parse_name(context, principal_string,
+				      &enterprise_principal);
+		TALLOC_FREE(principal_string);
+		if (ret) {
+			TALLOC_FREE(frame);
+			return ret;
+		}
+
+		enterprise_realm = smb_krb5_principal_get_realm(context,
+							enterprise_principal);
+		krb5_free_principal(context, enterprise_principal);
+		if (enterprise_realm != NULL) {
+			realm = talloc_strdup(frame, enterprise_realm);
+			SAFE_FREE(enterprise_realm);
+			if (realm == NULL) {
+				TALLOC_FREE(frame);
+				return ENOMEM;
+			}
+		}
+	}
+
+	if (flags & HDB_F_GET_SERVER) {
+		char *service_realm = NULL;
+
+		/*
+		 * servicePrincipalName: ldap/W2K8R2-219.bla.base
+		 * servicePrincipalName: ldap/W2K8R2-219.bla.base/bla.base
+		 * servicePrincipalName: ldap/W2K8R2-219.bla.base/ForestDnsZones.bla.base
+		 * servicePrincipalName: ldap/W2K8R2-219.bla.base/DomainDnsZones.bla.base
+		 *
+		 * we need to check the last component against the routing table.
+		 */
+
+
+		if (num_comp >= 1) {
+			char *service = NULL;
+
+			service = smb_krb5_principal_get_comp_string(frame, context,
+								     principal, 0);
+			if (service == NULL) {
+				TALLOC_FREE(frame);
+				return ENOMEM;
+			}
+
+			if (strcmp(service, KRB5_TGS_NAME) == 0) {
+				/*
+				 * we need to search krbtgt/ locally
+				 */
+				TALLOC_FREE(frame);
+				return 0;
+			}
+		}
+
+		if (num_comp == 3) {
+			service_realm = smb_krb5_principal_get_comp_string(frame, context,
+									   principal, 2);
+		} else if (num_comp == 2) {
+			service_realm = smb_krb5_principal_get_comp_string(frame, context,
+									   principal, 1);
+
+			if (service_realm != NULL) {
+				char *p;
+
+				/*
+				 * optimize the ldap/W2K8R2-219.bla.base
+				 * case here is 'bla.base' is our realm
+				 */
+				p = strchr_m(service_realm, '.');
+				if (p != NULL) {
+					p++;
+					dns_parent = p;
+				}
+			}
+		}
+
+		if (service_realm != NULL) {
+			realm = service_realm;
+		}
+	}
+
+	if (dns_parent != NULL) {
+		/*
+		 * optimize the ldap/W2K8R2-219.bla.base
+		 * case here is 'bla.base' is our realm
+		 */
+		ok = lpcfg_is_my_domain_or_realm(kdc_db_ctx->lp_ctx, dns_parent);
+		if (ok) {
+			/*
+			 * skip the expensive routing lookup
+			 */
+			TALLOC_FREE(frame);
+			return 0;
+		}
+	}
+
+	ok = lpcfg_is_my_domain_or_realm(kdc_db_ctx->lp_ctx, realm);
+	if (ok) {
+		/*
+		 * skip the expensive routing lookup
+		 */
+		TALLOC_FREE(frame);
+		return 0;
+	}
+
+	status = dsdb_trust_routing_table_load(kdc_db_ctx->samdb,
+					       frame, &trt);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return EINVAL;
+	}
+
+	tdo = dsdb_trust_routing_tln(trt, realm);
+
+	if (tdo != NULL) {
+		if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+			/* TODO: handle the routing within the forest */
+			tdo = NULL;
+		}
+	}
+
+	if (tdo == NULL) {
+		/*
+		 * This principal has to be local
+		 */
+		TALLOC_FREE(frame);
+		return 0;
+	}
+
+	ZERO_STRUCT(entry_ex->entry);
+
+	ret = krb5_copy_principal(context, principal,
+				  &entry_ex->entry.principal);
+	if (ret) {
+		TALLOC_FREE(frame);
+		return ret;
+	}
+
+	upper = strupper_talloc(frame, tdo->domain_name.string);
+	if (upper == NULL) {
+		TALLOC_FREE(frame);
+		return ENOMEM;
+	}
+
+	ret = krb5_principal_set_realm(context,
+				       entry_ex->entry.principal,
+				       upper);
+	if (ret) {
+		TALLOC_FREE(frame);
+		return ret;
+	}
+
+	TALLOC_FREE(frame);
+	return HDB_ERR_WRONG_REALM;
+}
+
 krb5_error_code samba_kdc_fetch(krb5_context context,
 				struct samba_kdc_db_context *kdc_db_ctx,
 				krb5_const_principal principal,
@@ -1761,6 +1997,14 @@ krb5_error_code samba_kdc_fetch(krb5_context context,
 		return ret;
 	}
 
+	ret = samba_kdc_lookup_realm(context, kdc_db_ctx, mem_ctx,
+				     principal, flags, entry_ex);
+	if (ret != 0) {
+		goto done;
+	}
+
+	ret = HDB_ERR_NOENTRY;
+
 	if (flags & HDB_F_GET_CLIENT) {
 		ret = samba_kdc_fetch_client(context, kdc_db_ctx, mem_ctx, principal, flags, entry_ex);
 		if (ret != HDB_ERR_NOENTRY) goto done;
-- 
1.9.1


From 841b5640083d6858afebce05e2432377fd40c30b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 2 Feb 2015 13:12:36 +0100
Subject: [PATCH 14/28] s4:dsdb/common: add dsdb_trust_search_tdo*() helper
 functions

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/common/util_trusts.c | 153 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 153 insertions(+)

diff --git a/source4/dsdb/common/util_trusts.c b/source4/dsdb/common/util_trusts.c
index b04fd78..afe475a 100644
--- a/source4/dsdb/common/util_trusts.c
+++ b/source4/dsdb/common/util_trusts.c
@@ -899,6 +899,159 @@ NTSTATUS dsdb_trust_parse_forest_info(TALLOC_CTX *mem_ctx,
 	return NT_STATUS_OK;
 }
 
+NTSTATUS dsdb_trust_search_tdo(struct ldb_context *sam_ctx,
+			       const char *netbios, const char *dns,
+			       const char * const *attrs,
+			       TALLOC_CTX *mem_ctx,
+			       struct ldb_message **msg)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	int ret;
+	struct ldb_dn *system_dn = NULL;
+	char *netbios_encoded = NULL;
+	char *dns_encoded = NULL;
+	char *filter = NULL;
+
+	*msg = NULL;
+
+	if (netbios == NULL && dns == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INVALID_PARAMETER_MIX;
+	}
+
+	system_dn = ldb_dn_copy(frame, ldb_get_default_basedn(sam_ctx));
+	if (system_dn == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (!ldb_dn_add_child_fmt(system_dn, "CN=System")) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (netbios != NULL) {
+		netbios_encoded = ldb_binary_encode_string(frame, netbios);
+		if (netbios_encoded == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
+
+	if (dns != NULL) {
+		dns_encoded = ldb_binary_encode_string(frame, dns);
+		if (dns_encoded == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
+
+	if (netbios != NULL && dns != NULL) {
+		filter = talloc_asprintf(frame,
+				"(&(objectClass=trustedDomain)"
+				  "(|(trustPartner=%s)(flatName=%s))"
+				")",
+				dns_encoded, netbios_encoded);
+		if (filter == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+	} else if (netbios != NULL) {
+		filter = talloc_asprintf(frame,
+				"(&(objectClass=trustedDomain)(flatName=%s))",
+				netbios_encoded);
+		if (filter == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+	} else if (dns != NULL) {
+		filter = talloc_asprintf(frame,
+				"(&(objectClass=trustedDomain)(trustPartner=%s))",
+				dns_encoded);
+		if (filter == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
+
+	ret = dsdb_search_one(sam_ctx, mem_ctx, msg,
+			      system_dn,
+			      LDB_SCOPE_ONELEVEL, attrs,
+			      DSDB_SEARCH_NO_GLOBAL_CATALOG,
+			      "%s", filter);
+	if (ret != LDB_SUCCESS) {
+		NTSTATUS status = dsdb_ldb_err_to_ntstatus(ret);
+		DEBUG(3, ("Failed to search for %s: %s - %s\n",
+			  filter, nt_errstr(status), ldb_errstring(sam_ctx)));
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+NTSTATUS dsdb_trust_search_tdo_by_type(struct ldb_context *sam_ctx,
+				       enum netr_SchannelType type,
+				       const char *name,
+				       const char * const *attrs,
+				       TALLOC_CTX *mem_ctx,
+				       struct ldb_message **msg)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	NTSTATUS status;
+	size_t len;
+	char trailer = '$';
+	bool require_trailer = true;
+	char *encoded_name = NULL;
+	const char *netbios = NULL;
+	const char *dns = NULL;
+
+	if (type != SEC_CHAN_DOMAIN && type != SEC_CHAN_DNS_DOMAIN) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	if (type == SEC_CHAN_DNS_DOMAIN) {
+		trailer = '.';
+		require_trailer = false;
+	}
+
+	encoded_name = ldb_binary_encode_string(frame, name);
+	if (encoded_name == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	len = strlen(encoded_name);
+	if (len < 2) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (require_trailer && encoded_name[len - 1] != trailer) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+	encoded_name[len - 1] = '\0';
+
+	if (type == SEC_CHAN_DNS_DOMAIN) {
+		dns = encoded_name;
+	} else {
+		netbios = encoded_name;
+	}
+
+	status = dsdb_trust_search_tdo(sam_ctx, netbios, dns,
+				       attrs, mem_ctx, msg);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS dsdb_trust_search_tdos(struct ldb_context *sam_ctx,
 				const char *exclude,
 				const char * const *attrs,
-- 
1.9.1


From 33f92e64c35e98e3911569c4798eaa7e43df86bf Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 30 Mar 2015 10:17:51 +0200
Subject: [PATCH 15/28] s4:kdc/db-glue: make use of dsdb_trust_search_tdo()

dsdb_trust_search_tdo() is almost the same as sam_get_results_trust(),
so we can remove sam_get_results_trust() later.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/kdc/db-glue.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
index edd12f6..805c83d 100644
--- a/source4/kdc/db-glue.c
+++ b/source4/kdc/db-glue.c
@@ -1346,12 +1346,11 @@ static krb5_error_code samba_kdc_lookup_trust(krb5_context context, struct ldb_c
 	NTSTATUS status;
 	const char * const *attrs = trust_attrs;
 
-	status = sam_get_results_trust(ldb_ctx,
-				       mem_ctx, realm, realm, attrs,
-				       pmsg);
+	status = dsdb_trust_search_tdo(ldb_ctx, realm, realm,
+				       attrs, mem_ctx, pmsg);
 	if (NT_STATUS_IS_OK(status)) {
 		return 0;
-	} else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+	} else if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
 		return HDB_ERR_NOENTRY;
 	} else if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
 		int ret = ENOMEM;
-- 
1.9.1


From fbca9559d6d962cb7c04a4616f84bf8d3b55ecfb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 30 Mar 2015 10:17:51 +0200
Subject: [PATCH 16/28] s3:pdb_samba_dsdb: make use of dsdb_trust_search_tdo()

dsdb_trust_search_tdo() is almost the same as sam_get_results_trust(),
so we can remove sam_get_results_trust() later.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/passdb/pdb_samba_dsdb.c | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/source3/passdb/pdb_samba_dsdb.c b/source3/passdb/pdb_samba_dsdb.c
index a933b2a..eacab29 100644
--- a/source3/passdb/pdb_samba_dsdb.c
+++ b/source3/passdb/pdb_samba_dsdb.c
@@ -2157,15 +2157,16 @@ static bool pdb_samba_dsdb_get_trusteddom_pw(struct pdb_methods *m,
 	const char *netbios_domain = NULL;
 	const struct dom_sid *domain_sid = NULL;
 
-	status = sam_get_results_trust(state->ldb, tmp_ctx, domain,
-				       NULL, attrs, &msg);
+	status = dsdb_trust_search_tdo(state->ldb, domain, NULL,
+				       attrs, tmp_ctx, &msg);
 	if (!NT_STATUS_IS_OK(status)) {
 		/*
 		 * This can be called to work out of a domain is
 		 * trusted, rather than just to get the password
 		 */
-		DEBUG(2, ("Failed to get trusted domain password for %s.  "
-			  "It may not be a trusted domain.\n", domain));
+		DEBUG(2, ("Failed to get trusted domain password for %s - %s.  "
+			  "It may not be a trusted domain.\n", domain,
+			  nt_errstr(status)));
 		TALLOC_FREE(tmp_ctx);
 		return false;
 	}
@@ -2317,17 +2318,18 @@ static NTSTATUS pdb_samba_dsdb_get_trusteddom_creds(struct pdb_methods *m,
 	char *principal_name = NULL;
 	const char *dns_domain = NULL;
 
-	status = sam_get_results_trust(state->ldb, tmp_ctx, domain,
-				       NULL, attrs, &msg);
+	status = dsdb_trust_search_tdo(state->ldb, domain, NULL,
+				       attrs, tmp_ctx, &msg);
 	if (!NT_STATUS_IS_OK(status)) {
 		/*
 		 * This can be called to work out of a domain is
 		 * trusted, rather than just to get the password
 		 */
-		DEBUG(2, ("Failed to get trusted domain password for %s.  "
-			  "It may not be a trusted domain.\n", domain));
+		DEBUG(2, ("Failed to get trusted domain password for %s - %s "
+			  "It may not be a trusted domain.\n", domain,
+			  nt_errstr(status)));
 		TALLOC_FREE(tmp_ctx);
-		return status;
+		return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
 	}
 
 	netbios_domain = ldb_msg_find_attr_as_string(msg, "flatName", NULL);
@@ -2612,15 +2614,16 @@ static bool pdb_samba_dsdb_set_trusteddom_pw(struct pdb_methods *m,
 		return false;
 	}
 
-	status = sam_get_results_trust(state->ldb, tmp_ctx, domain,
-				       NULL, attrs, &msg);
+	status = dsdb_trust_search_tdo(state->ldb, domain, NULL,
+				       attrs, tmp_ctx, &msg);
 	if (!NT_STATUS_IS_OK(status)) {
 		/*
 		 * This can be called to work out of a domain is
 		 * trusted, rather than just to get the password
 		 */
-		DEBUG(2, ("Failed to get trusted domain password for %s.  "
-			  "It may not be a trusted domain.\n", domain));
+		DEBUG(2, ("Failed to get trusted domain password for %s - %s.  "
+			  "It may not be a trusted domain.\n", domain,
+			  nt_errstr(status)));
 		TALLOC_FREE(tmp_ctx);
 		ldb_transaction_cancel(state->ldb);
 		return false;
-- 
1.9.1


From c76c1701c7d6928cb6eff19078bc7b1daf273de9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 30 Mar 2015 10:22:46 +0200
Subject: [PATCH 17/28] s4:auth/sam: remove unused sam_get_results_trust()

This is replaced by dsdb_trust_search_tdo() now.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/auth/sam.c | 74 ------------------------------------------------------
 1 file changed, 74 deletions(-)

diff --git a/source4/auth/sam.c b/source4/auth/sam.c
index 6e9e63b..f7bc693 100644
--- a/source4/auth/sam.c
+++ b/source4/auth/sam.c
@@ -560,80 +560,6 @@ NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
 	return NT_STATUS_OK;
 }
 
-NTSTATUS sam_get_results_trust(struct ldb_context *sam_ctx,
-			       TALLOC_CTX *mem_ctx, const char *domain,
-			       const char *realm, const char * const *attrs,
-			       struct ldb_message **msg)
-{
-	TALLOC_CTX *frame = talloc_stackframe();
-	int lret;
-	struct ldb_dn *system_dn;
-	char *filter;
-	struct ldb_result *res = NULL;
-	char *domain_encoded;
-
-	system_dn = ldb_dn_copy(frame, ldb_get_default_basedn(sam_ctx));
-	if (system_dn == NULL) {
-		TALLOC_FREE(frame);
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	if (!ldb_dn_add_child_fmt(system_dn, "CN=System")) {
-		TALLOC_FREE(frame);
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	domain_encoded = ldb_binary_encode_string(mem_ctx, domain);
-	if (!domain_encoded) {
-		TALLOC_FREE(frame);
-		return NT_STATUS_NO_MEMORY;
-	}
-	if (realm == NULL) {
-		filter = talloc_asprintf(mem_ctx,
-				"(&(objectClass=trustedDomain)(flatname=%s))",
-				domain_encoded);
-		if (!filter) {
-			TALLOC_FREE(frame);
-			return NT_STATUS_NO_MEMORY;
-		}
-	} else {
-		char *realm_encoded = ldb_binary_encode_string(mem_ctx, realm);
-		if (!realm_encoded) {
-			TALLOC_FREE(frame);
-			return NT_STATUS_NO_MEMORY;
-		}
-
-		filter = talloc_asprintf(mem_ctx,
-				"(&(objectClass=trustedDomain)"
-				  "(|(trustPartner=%s)(flatname=%s))"
-				")",
-				realm_encoded, domain_encoded);
-		if (!filter) {
-			TALLOC_FREE(frame);
-			return NT_STATUS_NO_MEMORY;
-		}
-	}
-
-	lret = dsdb_search(sam_ctx, frame, &res,
-			   system_dn,
-			   LDB_SCOPE_ONELEVEL, attrs,
-			   DSDB_SEARCH_NO_GLOBAL_CATALOG|DSDB_SEARCH_ONE_ONLY,
-			   "%s", filter);
-	if (lret == LDB_ERR_NO_SUCH_OBJECT) {
-		DEBUG(3, ("Failed to find result for %s: %s\n", filter, ldb_errstring(sam_ctx)));
-		TALLOC_FREE(frame);
-		return NT_STATUS_NOT_FOUND;
-	} else if (lret != LDB_SUCCESS) {
-		DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(sam_ctx)));
-		TALLOC_FREE(frame);
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	talloc_steal(mem_ctx, res->msgs);
-	*msg = res->msgs[0];
-	TALLOC_FREE(frame);
-	return NT_STATUS_OK;
-}
-
 /* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available, and for tokenGroups in the DSDB stack.
 
  Supply either a principal or a DN
-- 
1.9.1


From e9d5af8ab2d61cc39c21ce2b3e86b21bf8731dc5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 25 Mar 2015 15:14:44 +0000
Subject: [PATCH 18/28] s4:dsdb/netlogon: add support for CLDAP requests with
 AAC=0x00000400(ACB_AUTOLOCK) and user="example.com."

Windows reuses the ACB_AUTOLOCK flag to handle SEC_CHAN_DNS_DOMAIN domains,
but this not documented yet...

This is triggered by the NETLOGON_CONTROL_REDISCOVER with a domain string
of "example.com\somedc.example.com".

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/samdb/ldb_modules/netlogon.c | 47 +++++++++++++++++++++++++++----
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/source4/dsdb/samdb/ldb_modules/netlogon.c b/source4/dsdb/samdb/ldb_modules/netlogon.c
index a381da8..c5f194d 100644
--- a/source4/dsdb/samdb/ldb_modules/netlogon.c
+++ b/source4/dsdb/samdb/ldb_modules/netlogon.c
@@ -58,7 +58,7 @@ NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx,
 {
 	const char *dom_attrs[] = {"objectGUID", NULL};
 	const char *none_attrs[] = {NULL};
-	struct ldb_result *dom_res = NULL, *user_res = NULL;
+	struct ldb_result *dom_res = NULL;
 	int ret;
 	const char **services = lpcfg_server_services(lp_ctx);
 	uint32_t server_type;
@@ -74,6 +74,7 @@ NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx,
 	struct ldb_dn *domain_dn = NULL;
 	struct interface *ifaces;
 	bool user_known = false, am_rodc = false;
+	uint32_t uac = 0;
 	NTSTATUS status;
 
 	/* the domain parameter could have an optional trailing "." */
@@ -191,12 +192,48 @@ NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx,
 	/* Enquire about any valid username with just a CLDAP packet -
 	 * if kerberos didn't also do this, the security folks would
 	 * scream... */
-	if (user[0]) {							\
+	if (user[0]) {
 		/* Only allow some bits to be enquired:  [MS-ATDS] 7.3.3.2 */
 		if (acct_control == (uint32_t)-1) {
 			acct_control = 0;
 		}
-		acct_control = acct_control & (ACB_TEMPDUP | ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST);
+		/*
+		 * ACB_AUTOLOCK/UF_LOCKOUT seems to be a special
+		 * hack for SEC_CHAN_DNS_DOMAIN.
+		 *
+		 * It's used together with user = "example.com."
+		 */
+		if (acct_control != ACB_AUTOLOCK) {
+			acct_control &= (ACB_TEMPDUP | ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST);
+		}
+		uac = ds_acb2uf(acct_control);
+	}
+
+	if (uac == UF_LOCKOUT) {
+		struct ldb_message *tdo_msg = NULL;
+
+		/*
+		 * ACB_AUTOLOCK/UF_LOCKOUT seems to be a special
+		 * hack for SEC_CHAN_DNS_DOMAIN.
+		 *
+		 * It's used together with user = "example.com."
+		 */
+		status = dsdb_trust_search_tdo_by_type(sam_ctx,
+						       SEC_CHAN_DNS_DOMAIN,
+						       user, none_attrs,
+						       mem_ctx, &tdo_msg);
+		if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+			user_known = false;
+		} else if (NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(tdo_msg);
+			user_known = true;
+		} else {
+			DEBUG(2,("Unable to find reference to TDO '%s' - %s\n",
+				 user, nt_errstr(status)));
+			return status;
+		}
+	} else if (user[0]) {
+		struct ldb_result *user_res = NULL;
 
 		/* We must exclude disabled accounts, but otherwise do the bitwise match the client asked for */
 		ret = ldb_search(sam_ctx, mem_ctx, &user_res,
@@ -206,7 +243,7 @@ NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx,
 					 "(!(userAccountControl:" LDB_OID_COMPARATOR_AND ":=%u))"
 					 "(userAccountControl:" LDB_OID_COMPARATOR_OR ":=%u))", 
 					 ldb_binary_encode_string(mem_ctx, user),
-					 UF_ACCOUNTDISABLE, ds_acb2uf(acct_control));
+					 UF_ACCOUNTDISABLE, uac);
 		if (ret != LDB_SUCCESS) {
 			DEBUG(2,("Unable to find reference to user '%s' with ACB 0x%8x under %s: %s\n",
 				 user, acct_control, ldb_dn_get_linearized(dom_res->msgs[0]->dn),
@@ -217,7 +254,7 @@ NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx,
 		} else {
 			user_known = false;
 		}
-
+		TALLOC_FREE(user_res);
 	} else {
 		user_known = true;
 	}
-- 
1.9.1


From 5521f132a7fa3b5b1a574b76e438ad9cb72ab884 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 5 Feb 2015 12:09:34 +0100
Subject: [PATCH 19/28] s4:dsdb/common: pass optional new_version to
 samdb_set_password_sid()

For trust account we need to store version number provided by the client.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/common/util.c                    | 1 +
 source4/kdc/kpasswdd.c                        | 2 +-
 source4/rpc_server/netlogon/dcerpc_netlogon.c | 3 +++
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index 7b948f2..f76f4b1 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -2283,6 +2283,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
  */
 NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 				const struct dom_sid *user_sid,
+				const uint32_t *new_version, /* optional for trusts */
 				const DATA_BLOB *new_password,
 				const struct samr_Password *lmNewHash,
 				const struct samr_Password *ntNewHash,
diff --git a/source4/kdc/kpasswdd.c b/source4/kdc/kpasswdd.c
index 7b32229..e42e346 100644
--- a/source4/kdc/kpasswdd.c
+++ b/source4/kdc/kpasswdd.c
@@ -213,7 +213,7 @@ static bool kpasswdd_change_password(struct kdc_server *kdc,
 	/* Performs the password change */
 	status = samdb_set_password_sid(samdb, mem_ctx,
 					&session_info->security_token->sids[PRIMARY_USER_SID_INDEX],
-					password, NULL, NULL,
+					NULL, password, NULL, NULL,
 					oldLmHash, oldNtHash, /* this is a user password change */
 					&reject_reason,
 					&dominfo);
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 50e53a0..bb47de4 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -557,6 +557,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
 	/* Using the sid for the account as the key, set the password */
 	nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
 					   creds->sid,
+					   NULL, /* Don't have version */
 					   NULL, /* Don't have plaintext */
 					   NULL, r->in.new_password,
 					   NULL, oldNtHash, /* Password change */
@@ -576,6 +577,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
 	const char * const attrs[] = { "dBCSPwd", "unicodePwd", NULL };
 	struct ldb_message **res;
 	struct samr_Password *oldLmHash, *oldNtHash;
+	const uint32_t *new_version = NULL;
 	NTSTATUS nt_status;
 	DATA_BLOB new_password;
 	int ret;
@@ -627,6 +629,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
 	/* Using the sid for the account as the key, set the password */
 	nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
 					   creds->sid,
+					   new_version,
 					   &new_password, /* we have plaintext */
 					   NULL, NULL,
 					   oldLmHash, oldNtHash, /* Password change */
-- 
1.9.1


From db720878f8ee0a8ddf28f6094ed33c3030398b3e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 5 Feb 2015 10:42:08 +0000
Subject: [PATCH 20/28] s4:dsdb/common: make use of dsdb_search_one() in
 samdb_set_password_sid()

This will simplify the following commits.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/common/util.c | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index f76f4b1..d18688a 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -2292,48 +2292,57 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 				enum samPwdChangeReason *reject_reason,
 				struct samr_DomInfo1 **_dominfo) 
 {
+	TALLOC_CTX *frame = talloc_stackframe();
 	NTSTATUS nt_status;
-	struct ldb_dn *user_dn;
+	const char * const user_attrs[] = {
+		NULL
+	};
+	struct ldb_message *user_msg = NULL;
 	int ret;
 
 	ret = ldb_transaction_start(ldb);
 	if (ret != LDB_SUCCESS) {
 		DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ldb)));
+		TALLOC_FREE(frame);
 		return NT_STATUS_TRANSACTION_ABORTED;
 	}
 
-	user_dn = samdb_search_dn(ldb, mem_ctx, NULL,
-				  "(&(objectSid=%s)(objectClass=user))", 
-				  ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
-	if (!user_dn) {
+	ret = dsdb_search_one(ldb, frame, &user_msg, ldb_get_default_basedn(ldb),
+			      LDB_SCOPE_SUBTREE, user_attrs, 0,
+			      "(&(objectSid=%s)(objectClass=user))",
+			      ldap_encode_ndr_dom_sid(frame, user_sid));
+	if (ret != LDB_SUCCESS) {
 		ldb_transaction_cancel(ldb);
-		DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
-			  dom_sid_string(mem_ctx, user_sid)));
+		DEBUG(3, ("samdb_set_password_sid: SID[%s] not found in samdb %s - %s, "
+			  "returning NO_SUCH_USER\n",
+			  dom_sid_string(frame, user_sid),
+			  ldb_strerror(ret), ldb_errstring(ldb)));
+		TALLOC_FREE(frame);
 		return NT_STATUS_NO_SUCH_USER;
 	}
 
 	nt_status = samdb_set_password(ldb, mem_ctx,
-				       user_dn, NULL,
+				       user_msg->dn, NULL,
 				       new_password,
 				       lmNewHash, ntNewHash,
 				       lmOldHash, ntOldHash,
 				       reject_reason, _dominfo);
 	if (!NT_STATUS_IS_OK(nt_status)) {
 		ldb_transaction_cancel(ldb);
-		talloc_free(user_dn);
+		TALLOC_FREE(frame);
 		return nt_status;
 	}
 
 	ret = ldb_transaction_commit(ldb);
 	if (ret != LDB_SUCCESS) {
 		DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
-			 ldb_dn_get_linearized(user_dn),
+			 ldb_dn_get_linearized(user_msg->dn),
 			 ldb_errstring(ldb)));
-		talloc_free(user_dn);
+		TALLOC_FREE(frame);
 		return NT_STATUS_TRANSACTION_ABORTED;
 	}
 
-	talloc_free(user_dn);
+	TALLOC_FREE(frame);
 	return NT_STATUS_OK;
 }
 
-- 
1.9.1


From 236abd73918747402c7ad7d14a26b89c2f34dcf6 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 5 Feb 2015 10:42:08 +0000
Subject: [PATCH 21/28] s4:dsdb/common: supported trusted domains in
 samdb_set_password_sid()

We also need to update trustAuthIncoming of the trustedDomain object.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/common/util.c | 370 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 362 insertions(+), 8 deletions(-)

diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index d18688a..04d3de3 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -2079,7 +2079,7 @@ int samdb_set_password_callback(struct ldb_request *req, struct ldb_reply *ares)
  * Results: NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL,
  *   NT_STATUS_WRONG_PASSWORD, NT_STATUS_PASSWORD_RESTRICTION
  */
-NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+static NTSTATUS samdb_set_password_internal(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 			    struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
 			    const DATA_BLOB *new_password,
 			    const struct samr_Password *lmNewHash,
@@ -2087,7 +2087,8 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 			    const struct samr_Password *lmOldHash,
 			    const struct samr_Password *ntOldHash,
 			    enum samPwdChangeReason *reject_reason,
-			    struct samr_DomInfo1 **_dominfo)
+			    struct samr_DomInfo1 **_dominfo,
+			    bool permit_interdomain_trust)
 {
 	struct ldb_message *msg;
 	struct ldb_message_element *el;
@@ -2178,6 +2179,16 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 			return NT_STATUS_NO_MEMORY;
 		}
 	}
+	if (permit_interdomain_trust) {
+		ret = ldb_request_add_control(req,
+					      DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
+					      false, NULL);
+		if (ret != LDB_SUCCESS) {
+			talloc_free(req);
+			talloc_free(msg);
+			return NT_STATUS_NO_MEMORY;
+		}
+	}
 	ret = ldb_request_add_control(req,
 				      DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
 				      true, NULL);
@@ -2263,6 +2274,25 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 	return status;
 }
 
+NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+			    struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
+			    const DATA_BLOB *new_password,
+			    const struct samr_Password *lmNewHash,
+			    const struct samr_Password *ntNewHash,
+			    const struct samr_Password *lmOldHash,
+			    const struct samr_Password *ntOldHash,
+			    enum samPwdChangeReason *reject_reason,
+			    struct samr_DomInfo1 **_dominfo)
+{
+	return samdb_set_password_internal(ldb, mem_ctx,
+			    user_dn, domain_dn,
+			    new_password,
+			    lmNewHash, ntNewHash,
+			    lmOldHash, ntOldHash,
+			    reject_reason, _dominfo,
+			    false); /* reject trusts */
+}
+
 /*
  * Sets the user password using plaintext UTF16 (attribute "new_password") or
  * LM (attribute "lmNewHash") or NT (attribute "ntNewHash") hash. Also pass
@@ -2295,10 +2325,13 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 	TALLOC_CTX *frame = talloc_stackframe();
 	NTSTATUS nt_status;
 	const char * const user_attrs[] = {
+		"userAccountControl",
+		"sAMAccountName",
 		NULL
 	};
 	struct ldb_message *user_msg = NULL;
 	int ret;
+	uint32_t uac = 0;
 
 	ret = ldb_transaction_start(ldb);
 	if (ret != LDB_SUCCESS) {
@@ -2321,12 +2354,333 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
 		return NT_STATUS_NO_SUCH_USER;
 	}
 
-	nt_status = samdb_set_password(ldb, mem_ctx,
-				       user_msg->dn, NULL,
-				       new_password,
-				       lmNewHash, ntNewHash,
-				       lmOldHash, ntOldHash,
-				       reject_reason, _dominfo);
+	uac = ldb_msg_find_attr_as_uint(user_msg, "userAccountControl", 0);
+	if (!(uac & UF_ACCOUNT_TYPE_MASK)) {
+		ldb_transaction_cancel(ldb);
+		DEBUG(1, ("samdb_set_password_sid: invalid "
+			  "userAccountControl[0x%08X] for SID[%s] DN[%s], "
+			  "returning NO_SUCH_USER\n",
+			  (unsigned)uac, dom_sid_string(frame, user_sid),
+			  ldb_dn_get_linearized(user_msg->dn)));
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_SUCH_USER;
+	}
+
+	if (uac & UF_INTERDOMAIN_TRUST_ACCOUNT) {
+		const char * const tdo_attrs[] = {
+			"trustAuthIncoming",
+			"trustDirection",
+			NULL
+		};
+		struct ldb_message *tdo_msg = NULL;
+		const char *account_name = NULL;
+		uint32_t trust_direction;
+		uint32_t i;
+		const struct ldb_val *old_val = NULL;
+		struct trustAuthInOutBlob old_blob = {};
+		uint32_t old_version = 0;
+		struct AuthenticationInformation *old_version_a = NULL;
+		uint32_t _new_version = 0;
+		struct trustAuthInOutBlob new_blob = {};
+		struct ldb_val new_val = {};
+		struct timeval tv = timeval_current();
+		NTTIME now = timeval_to_nttime(&tv);
+		enum ndr_err_code ndr_err;
+
+		if (new_password == NULL && ntNewHash == NULL) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(1, ("samdb_set_password_sid: "
+				  "no new password provided "
+				  "sAMAccountName for SID[%s] DN[%s], "
+				  "returning INVALID_PARAMETER\n",
+				  dom_sid_string(frame, user_sid),
+				  ldb_dn_get_linearized(user_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_INVALID_PARAMETER;
+		}
+
+		if (new_password != NULL && ntNewHash != NULL) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(1, ("samdb_set_password_sid: "
+				  "two new passwords provided "
+				  "sAMAccountName for SID[%s] DN[%s], "
+				  "returning INVALID_PARAMETER\n",
+				  dom_sid_string(frame, user_sid),
+				  ldb_dn_get_linearized(user_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_INVALID_PARAMETER;
+		}
+
+		if (new_password != NULL && (new_password->length % 2)) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(2, ("samdb_set_password_sid: "
+				  "invalid utf16 length (%zu) "
+				  "sAMAccountName for SID[%s] DN[%s], "
+				  "returning WRONG_PASSWORD\n",
+				  new_password->length,
+				  dom_sid_string(frame, user_sid),
+				  ldb_dn_get_linearized(user_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_WRONG_PASSWORD;
+		}
+
+		if (new_password != NULL && new_password->length >= 500) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(2, ("samdb_set_password_sid: "
+				  "utf16 password too long (%zu) "
+				  "sAMAccountName for SID[%s] DN[%s], "
+				  "returning WRONG_PASSWORD\n",
+				  new_password->length,
+				  dom_sid_string(frame, user_sid),
+				  ldb_dn_get_linearized(user_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_WRONG_PASSWORD;
+		}
+
+		account_name = ldb_msg_find_attr_as_string(user_msg,
+							"sAMAccountName", NULL);
+		if (account_name == NULL) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(1, ("samdb_set_password_sid: missing "
+				  "sAMAccountName for SID[%s] DN[%s], "
+				  "returning NO_SUCH_USER\n",
+				  dom_sid_string(frame, user_sid),
+				  ldb_dn_get_linearized(user_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_SUCH_USER;
+		}
+
+		nt_status = dsdb_trust_search_tdo_by_type(ldb,
+							  SEC_CHAN_DOMAIN,
+							  account_name,
+							  tdo_attrs,
+							  frame, &tdo_msg);
+		if (!NT_STATUS_IS_OK(nt_status)) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(1, ("samdb_set_password_sid: dsdb_trust_search_tdo "
+				  "failed(%s) for sAMAccountName[%s] SID[%s] DN[%s], "
+				  "returning INTERNAL_DB_CORRUPTION\n",
+				  nt_errstr(nt_status), account_name,
+				  dom_sid_string(frame, user_sid),
+				  ldb_dn_get_linearized(user_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		}
+
+		trust_direction = ldb_msg_find_attr_as_int(tdo_msg,
+							   "trustDirection", 0);
+		if (!(trust_direction & LSA_TRUST_DIRECTION_INBOUND)) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(1, ("samdb_set_password_sid: direction[0x%08X] is "
+				  "not inbound for sAMAccountName[%s] "
+				  "DN[%s] TDO[%s], "
+				  "returning INTERNAL_DB_CORRUPTION\n",
+				  (unsigned)trust_direction,
+				  account_name,
+				  ldb_dn_get_linearized(user_msg->dn),
+				  ldb_dn_get_linearized(tdo_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		}
+
+		old_val = ldb_msg_find_ldb_val(tdo_msg, "trustAuthIncoming");
+		if (old_val != NULL) {
+			ndr_err = ndr_pull_struct_blob(old_val, frame, &old_blob,
+					(ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+			if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+				ldb_transaction_cancel(ldb);
+				DEBUG(1, ("samdb_set_password_sid: "
+					  "failed(%s) to parse "
+					  "trustAuthOutgoing sAMAccountName[%s] "
+					  "DN[%s] TDO[%s], "
+					  "returning INTERNAL_DB_CORRUPTION\n",
+					  ndr_map_error2string(ndr_err),
+					  account_name,
+					  ldb_dn_get_linearized(user_msg->dn),
+					  ldb_dn_get_linearized(tdo_msg->dn)));
+
+				TALLOC_FREE(frame);
+				return NT_STATUS_INTERNAL_DB_CORRUPTION;
+			}
+		}
+
+		for (i = old_blob.current.count; i > 0; i--) {
+			struct AuthenticationInformation *a =
+				&old_blob.current.array[i - 1];
+
+			switch (a->AuthType) {
+			case TRUST_AUTH_TYPE_NONE:
+				if (i == old_blob.current.count) {
+					/*
+					 * remove TRUST_AUTH_TYPE_NONE at the
+					 * end
+					 */
+					old_blob.current.count--;
+				}
+				break;
+
+			case TRUST_AUTH_TYPE_VERSION:
+				old_version_a = a;
+				old_version = a->AuthInfo.version.version;
+				break;
+
+			case TRUST_AUTH_TYPE_CLEAR:
+				break;
+
+			case TRUST_AUTH_TYPE_NT4OWF:
+				break;
+			}
+		}
+
+		if (new_version == NULL) {
+			_new_version = 0;
+			new_version = &_new_version;
+		}
+
+		if (old_version_a != NULL && *new_version <= old_version) {
+			old_version_a->LastUpdateTime = now;
+			old_version_a->AuthType = TRUST_AUTH_TYPE_NONE;
+		}
+
+		new_blob.count = MAX(old_blob.current.count, 2);
+		new_blob.current.array = talloc_zero_array(frame,
+						struct AuthenticationInformation,
+						new_blob.count);
+		if (new_blob.current.array == NULL) {
+			ldb_transaction_cancel(ldb);
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+		new_blob.previous.array = talloc_zero_array(frame,
+						struct AuthenticationInformation,
+						new_blob.count);
+		if (new_blob.current.array == NULL) {
+			ldb_transaction_cancel(ldb);
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		for (i = 0; i < old_blob.current.count; i++) {
+			struct AuthenticationInformation *o =
+				&old_blob.current.array[i];
+			struct AuthenticationInformation *p =
+				&new_blob.previous.array[i];
+
+			*p = *o;
+			new_blob.previous.count++;
+		}
+		for (; i < new_blob.count; i++) {
+			struct AuthenticationInformation *pi =
+				&new_blob.previous.array[i];
+
+			if (i == 0) {
+				/*
+				 * new_blob.previous is still empty so
+				 * we'll do new_blob.previous = new_blob.current
+				 * below.
+				 */
+				break;
+			}
+
+			pi->LastUpdateTime = now;
+			pi->AuthType = TRUST_AUTH_TYPE_NONE;
+			new_blob.previous.count++;
+		}
+
+		for (i = 0; i < new_blob.count; i++) {
+			struct AuthenticationInformation *ci =
+				&new_blob.current.array[i];
+
+			ci->LastUpdateTime = now;
+			switch (i) {
+			case 0:
+				if (ntNewHash != NULL) {
+					ci->AuthType = TRUST_AUTH_TYPE_NT4OWF;
+					ci->AuthInfo.nt4owf.password = *ntNewHash;
+					break;
+				}
+
+				ci->AuthType = TRUST_AUTH_TYPE_CLEAR;
+				ci->AuthInfo.clear.size = new_password->length;
+				ci->AuthInfo.clear.password = new_password->data;
+				break;
+			case 1:
+				ci->AuthType = TRUST_AUTH_TYPE_VERSION;
+				ci->AuthInfo.version.version = *new_version;
+				break;
+			default:
+				ci->AuthType = TRUST_AUTH_TYPE_NONE;
+				break;
+			}
+
+			new_blob.current.count++;
+		}
+
+		if (new_blob.previous.count == 0) {
+			TALLOC_FREE(new_blob.previous.array);
+			new_blob.previous = new_blob.current;
+		}
+
+		ndr_err = ndr_push_struct_blob(&new_val, frame, &new_blob,
+				(ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+			ldb_transaction_cancel(ldb);
+			DEBUG(1, ("samdb_set_password_sid: "
+				  "failed(%s) to generate "
+				  "trustAuthOutgoing sAMAccountName[%s] "
+				  "DN[%s] TDO[%s], "
+				  "returning UNSUCCESSFUL\n",
+				  ndr_map_error2string(ndr_err),
+				  account_name,
+				  ldb_dn_get_linearized(user_msg->dn),
+				  ldb_dn_get_linearized(tdo_msg->dn)));
+			TALLOC_FREE(frame);
+			return NT_STATUS_UNSUCCESSFUL;
+		}
+
+		tdo_msg->num_elements = 0;
+		TALLOC_FREE(tdo_msg->elements);
+
+		ret = ldb_msg_add_empty(tdo_msg, "trustAuthIncoming",
+					LDB_FLAG_MOD_REPLACE, NULL);
+		if (ret != LDB_SUCCESS) {
+			ldb_transaction_cancel(ldb);
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+		ret = ldb_msg_add_value(tdo_msg, "trustAuthIncoming",
+					&new_val, NULL);
+		if (ret != LDB_SUCCESS) {
+			ldb_transaction_cancel(ldb);
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		ret = ldb_modify(ldb, tdo_msg);
+		if (ret != LDB_SUCCESS) {
+			nt_status = dsdb_ldb_err_to_ntstatus(ret);
+			ldb_transaction_cancel(ldb);
+			DEBUG(1, ("samdb_set_password_sid: "
+				  "failed to replace "
+				  "trustAuthOutgoing sAMAccountName[%s] "
+				  "DN[%s] TDO[%s], "
+				  "%s - %s\n",
+				  account_name,
+				  ldb_dn_get_linearized(user_msg->dn),
+				  ldb_dn_get_linearized(tdo_msg->dn),
+				  nt_errstr(nt_status), ldb_errstring(ldb)));
+			TALLOC_FREE(frame);
+			return nt_status;
+		}
+	}
+
+	nt_status = samdb_set_password_internal(ldb, mem_ctx,
+						user_msg->dn, NULL,
+						new_password,
+						lmNewHash, ntNewHash,
+						lmOldHash, ntOldHash,
+						reject_reason, _dominfo,
+						true); /* permit trusts */
 	if (!NT_STATUS_IS_OK(nt_status)) {
 		ldb_transaction_cancel(ldb);
 		TALLOC_FREE(frame);
-- 
1.9.1


From 897208ab30cc0ab9ce3be7c0eb4b88612568be25 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 30 Mar 2015 12:31:01 +0200
Subject: [PATCH 22/28] s4:dsdb/password_hash: reject interdomain trust
 password changes via LDAP

Only the LSA and NETLOGON server should be able to change this, otherwise
the incoming passwords in the trust account and trusted domain object
get out of sync.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/samdb/ldb_modules/password_hash.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index cd23ab7..8acaa4e 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -2261,6 +2261,19 @@ static int setup_io(struct ph_context *ac,
 		return LDB_ERR_CONSTRAINT_VIOLATION;
 	}
 
+	if (io->u.userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
+		struct ldb_control *permit_trust = ldb_request_get_control(ac->req,
+				DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID);
+
+		if (permit_trust == NULL) {
+			ldb_asprintf_errstring(ldb,
+				"setup_io: changing the interdomain trust password "
+				"on %s not allowed via LDAP. Use LSA or NETLOGON",
+				ldb_dn_get_linearized(searched_msg->dn));
+			return LDB_ERR_CONSTRAINT_VIOLATION;
+		}
+	}
+
 	/* Only non-trust accounts have restrictions (possibly this test is the
 	 * wrong way around, but we like to be restrictive if possible */
 	io->u.restrictions = !(io->u.userAccountControl
-- 
1.9.1


From ddabc03081e0ab7e6fb64ccb64147bd50bcae061 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 30 Jan 2015 09:42:15 +0000
Subject: [PATCH 23/28] s4:rpc_server/netlogon: extract and pass down the
 password version in dcesrv_netr_ServerPasswordSet2()

For domain trusts we need to extract NL_PASSWORD_VERSION from the password
buffer.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/netlogon/dcerpc_netlogon.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index bb47de4..e9f07f8 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -38,6 +38,7 @@
 #include "lib/tsocket/tsocket.h"
 #include "librpc/gen_ndr/ndr_netlogon.h"
 #include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_samr.h"
 #include "librpc/gen_ndr/ndr_irpc.h"
 #include "lib/socket/netif.h"
 
@@ -577,11 +578,11 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
 	const char * const attrs[] = { "dBCSPwd", "unicodePwd", NULL };
 	struct ldb_message **res;
 	struct samr_Password *oldLmHash, *oldNtHash;
+	struct NL_PASSWORD_VERSION version = {};
 	const uint32_t *new_version = NULL;
 	NTSTATUS nt_status;
 	DATA_BLOB new_password;
 	int ret;
-
 	struct samr_CryptPassword password_buf;
 
 	nt_status = dcesrv_netr_creds_server_step_check(dce_call,
@@ -605,6 +606,29 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
 		netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
 	}
 
+	switch (creds->secure_channel_type) {
+	case SEC_CHAN_DOMAIN:
+	case SEC_CHAN_DNS_DOMAIN: {
+		uint32_t len = IVAL(password_buf.data, 512);
+		if (len <= 500) {
+			uint32_t ofs = 500 - len;
+			uint8_t *p;
+
+			p = password_buf.data + ofs;
+
+			version.ReservedField = IVAL(p, 0);
+			version.PasswordVersionNumber = IVAL(p, 4);
+			version.PasswordVersionPresent = IVAL(p, 8);
+
+			if (version.PasswordVersionPresent == NETLOGON_PASSWORD_VERSION_NUMBER_PRESENT) {
+				new_version = &version.PasswordVersionNumber;
+			}
+		}}
+		break;
+	default:
+		break;
+	}
+
 	if (!extract_pw_from_buffer(mem_ctx, password_buf.data, &new_password)) {
 		DEBUG(3,("samr: failed to decode password buffer\n"));
 		return NT_STATUS_WRONG_PASSWORD;
-- 
1.9.1


From 25df927c49e89e68b936c75ba5fd77e04725e2ce Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 2 Feb 2015 13:12:36 +0100
Subject: [PATCH 24/28] s4:dsdb/common: add dsdb_trust_get_incoming_passwords()
 helper function

This extracts the current and previous nt hashes from trustAuthIncoming
as the passed TDO ldb_message.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dsdb/common/util_trusts.c | 116 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/source4/dsdb/common/util_trusts.c b/source4/dsdb/common/util_trusts.c
index afe475a..2e4d2d3 100644
--- a/source4/dsdb/common/util_trusts.c
+++ b/source4/dsdb/common/util_trusts.c
@@ -1052,6 +1052,122 @@ NTSTATUS dsdb_trust_search_tdo_by_type(struct ldb_context *sam_ctx,
 	return NT_STATUS_OK;
 }
 
+NTSTATUS dsdb_trust_get_incoming_passwords(struct ldb_message *msg,
+					   TALLOC_CTX *mem_ctx,
+					   struct samr_Password **_current,
+					   struct samr_Password **_previous)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	struct samr_Password __current = {};
+	struct samr_Password __previous = {};
+	struct samr_Password *current = NULL;
+	struct samr_Password *previous = NULL;
+	const struct ldb_val *blob = NULL;
+	enum ndr_err_code ndr_err;
+	struct trustAuthInOutBlob incoming = {};
+	uint32_t i;
+
+	if (_current != NULL) {
+		*_current = NULL;
+	}
+	if (_previous != NULL) {
+		*_previous = NULL;
+	}
+
+	blob = ldb_msg_find_ldb_val(msg, "trustAuthIncoming");
+	if (blob == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_ACCOUNT_DISABLED;
+	}
+
+	/* ldb_val is equivalent to DATA_BLOB */
+	ndr_err = ndr_pull_struct_blob_all(blob, frame, &incoming,
+				(ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+
+	for (i = 0; i < incoming.current.count; i++) {
+		struct AuthenticationInformation *a =
+			&incoming.current.array[i];
+
+		if (current != NULL) {
+			break;
+		}
+
+		switch (a->AuthType) {
+		case TRUST_AUTH_TYPE_NONE:
+		case TRUST_AUTH_TYPE_VERSION:
+			break;
+		case TRUST_AUTH_TYPE_NT4OWF:
+			current = &a->AuthInfo.nt4owf.password;
+			break;
+		case TRUST_AUTH_TYPE_CLEAR:
+			mdfour(__current.hash,
+			       a->AuthInfo.clear.password,
+			       a->AuthInfo.clear.size);
+			current = &__current;
+			break;
+		}
+	}
+
+	if (current == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+	}
+
+	for (i = 0; i < incoming.previous.count; i++) {
+		struct AuthenticationInformation *a =
+			&incoming.previous.array[i];
+
+		if (previous != NULL) {
+			break;
+		}
+
+		switch (a->AuthType) {
+		case TRUST_AUTH_TYPE_NONE:
+		case TRUST_AUTH_TYPE_VERSION:
+			break;
+		case TRUST_AUTH_TYPE_NT4OWF:
+			previous = &a->AuthInfo.nt4owf.password;
+			break;
+		case TRUST_AUTH_TYPE_CLEAR:
+			mdfour(__previous.hash,
+			       a->AuthInfo.clear.password,
+			       a->AuthInfo.clear.size);
+			previous = &__previous;
+			break;
+		}
+	}
+
+	if (previous == NULL) {
+		previous = current;
+	}
+
+	if (_current != NULL) {
+		*_current = talloc(mem_ctx, struct samr_Password);
+		if (*_current == NULL) {
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+		**_current = *current;
+	}
+	if (_previous != NULL) {
+		*_previous = talloc(mem_ctx, struct samr_Password);
+		if (*_previous == NULL) {
+			TALLOC_FREE(*_current);
+			TALLOC_FREE(frame);
+			return NT_STATUS_NO_MEMORY;
+		}
+		**_previous = *previous;
+	}
+	ZERO_STRUCTP(current);
+	ZERO_STRUCTP(previous);
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
 static NTSTATUS dsdb_trust_search_tdos(struct ldb_context *sam_ctx,
 				const char *exclude,
 				const char * const *attrs,
-- 
1.9.1


From 08db3b9373caff846446eda6a7399bde56577ec9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 5 Feb 2015 15:53:37 +0000
Subject: [PATCH 25/28] s4:rpc_server/netlogon: let
 dcesrv_netr_ServerAuthenticate3() fallback to the previous hash for trusts

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/netlogon/dcerpc_netlogon.c | 126 +++++++++++++++++++-------
 1 file changed, 93 insertions(+), 33 deletions(-)

diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index e9f07f8..9ed287f 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -112,15 +112,14 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
 	struct netlogon_server_pipe_state challenge;
 	struct netlogon_creds_CredentialState *creds;
 	struct ldb_context *sam_ctx;
-	struct samr_Password *mach_pwd;
+	struct samr_Password *curNtHash = NULL;
+	struct samr_Password *prevNtHash = NULL;
 	uint32_t user_account_control;
 	int num_records;
 	struct ldb_message **msgs;
 	NTSTATUS nt_status;
 	const char *attrs[] = {"unicodePwd", "userAccountControl",
 			       "objectSid", NULL};
-
-	const char *trust_dom_attrs[] = {"flatname", NULL};
 	const char *account_name;
 	uint32_t server_flags = 0;
 	uint32_t negotiate_flags = 0;
@@ -256,48 +255,83 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
 		return NT_STATUS_INVALID_SYSTEM_SERVICE;
 	}
 
-	if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
-		char *encoded_account = ldb_binary_encode_string(mem_ctx, r->in.account_name);
-		const char *flatname;
-		if (!encoded_account) {
+	if (r->in.secure_channel_type == SEC_CHAN_DOMAIN ||
+	    r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN)
+	{
+		struct ldb_message *tdo_msg = NULL;
+		const char * const tdo_attrs[] = {
+			"trustAuthIncoming",
+			"trustAttributes",
+			"flatName",
+			NULL
+		};
+		char *encoded_name = NULL;
+		size_t len;
+		const char *flatname = NULL;
+		char trailer = '$';
+		bool require_trailer = true;
+		const char *netbios = NULL;
+		const char *dns = NULL;
+
+		if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+			trailer = '.';
+			require_trailer = false;
+		}
+
+		encoded_name = ldb_binary_encode_string(mem_ctx,
+							r->in.account_name);
+		if (encoded_name == NULL) {
 			return NT_STATUS_NO_MEMORY;
 		}
 
-		/* Kill the trailing dot */
-		if (encoded_account[strlen(encoded_account)-1] == '.') {
-			encoded_account[strlen(encoded_account)-1] = '\0';
+		len = strlen(encoded_name);
+		if (len < 2) {
+			return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+		}
+
+		if (require_trailer && encoded_name[len - 1] != trailer) {
+			return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
 		}
+		encoded_name[len - 1] = '\0';
 
-		/* pull the user attributes */
-		num_records = gendb_search(sam_ctx, mem_ctx, NULL, &msgs,
-					   trust_dom_attrs,
-					   "(&(trustPartner=%s)(objectclass=trustedDomain))",
-					   encoded_account);
+		if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+			dns = encoded_name;
+		} else {
+			netbios = encoded_name;
+		}
 
-		if (num_records == 0) {
-			DEBUG(3,("Couldn't find trust [%s] in samdb.\n",
-				 encoded_account));
+		nt_status = dsdb_trust_search_tdo(sam_ctx,
+						  netbios, dns,
+						  tdo_attrs, mem_ctx, &tdo_msg);
+		if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+			DEBUG(2, ("Client asked for a trusted domain secure channel, "
+				  "but there's no tdo for [%s] => [%s] \n",
+				  r->in.account_name, encoded_name));
 			return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
 		}
+		if (!NT_STATUS_IS_OK(nt_status)) {
+			return nt_status;
+		}
 
-		if (num_records > 1) {
-			DEBUG(0,("Found %d records matching user [%s]\n", num_records, r->in.account_name));
-			return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		nt_status = dsdb_trust_get_incoming_passwords(tdo_msg, mem_ctx,
+							      &curNtHash,
+							      &prevNtHash);
+		if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) {
+			return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
+		}
+		if (!NT_STATUS_IS_OK(nt_status)) {
+			return nt_status;
 		}
 
-		flatname = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
-		if (!flatname) {
-			/* No flatname for this trust - we can't proceed */
-			DEBUG(3,("Couldn't find flatname for trust [%s] in samdb.\n",
-				 encoded_account));
+		flatname = ldb_msg_find_attr_as_string(tdo_msg, "flatName", NULL);
+		if (flatname == NULL) {
 			return NT_STATUS_NO_TRUST_SAM_ACCOUNT;
 		}
-		account_name = talloc_asprintf(mem_ctx, "%s$", flatname);
 
-		if (!account_name) {
+		account_name = talloc_asprintf(mem_ctx, "%s$", flatname);
+		if (account_name == NULL) {
 			return NT_STATUS_NO_MEMORY;
 		}
-
 	} else {
 		account_name = r->in.account_name;
 	}
@@ -352,8 +386,16 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
 		return NT_STATUS_INTERNAL_ERROR;
 	}
 
-	mach_pwd = samdb_result_hash(mem_ctx, msgs[0], "unicodePwd");
-	if (mach_pwd == NULL) {
+	if (!(user_account_control & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+		nt_status = samdb_result_passwords_no_lockout(mem_ctx,
+					dce_call->conn->dce_ctx->lp_ctx,
+					msgs[0], NULL, &curNtHash);
+		if (!NT_STATUS_IS_OK(nt_status)) {
+			return NT_STATUS_ACCESS_DENIED;
+		}
+	}
+
+	if (curNtHash == NULL) {
 		return NT_STATUS_ACCESS_DENIED;
 	}
 
@@ -371,11 +413,29 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
 					   r->in.secure_channel_type,
 					   &challenge.client_challenge,
 					   &challenge.server_challenge,
-					   mach_pwd,
+					   curNtHash,
 					   r->in.credentials,
 					   r->out.return_credentials,
 					   negotiate_flags);
-	if (!creds) {
+	if (creds == NULL && prevNtHash != NULL) {
+		/*
+		 * We fallback to the previous password for domain trusts.
+		 *
+		 * Note that lpcfg_old_password_allowed_period() doesn't
+		 * apply here.
+		 */
+		creds = netlogon_creds_server_init(mem_ctx,
+						   r->in.account_name,
+						   r->in.computer_name,
+						   r->in.secure_channel_type,
+						   &challenge.client_challenge,
+						   &challenge.server_challenge,
+						   prevNtHash,
+						   r->in.credentials,
+						   r->out.return_credentials,
+						   negotiate_flags);
+	}
+	if (creds == NULL) {
 		return NT_STATUS_ACCESS_DENIED;
 	}
 
-- 
1.9.1


From 3cdfae0967fb9d6cee33dc281d729aeabc668853 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 22 Dec 2014 22:02:25 +0100
Subject: [PATCH 26/28] s4:rpc_server/netlogon: implement
 dcesrv_netr_ServerGetTrustInfo()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail                            |   1 -
 source4/rpc_server/netlogon/dcerpc_netlogon.c | 153 +++++++++++++++++++++++++-
 2 files changed, 152 insertions(+), 2 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index 3314d5d..2a570a0 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -96,7 +96,6 @@
 ^samba4.rpc.netlogon.*.GetPassword
 ^samba4.rpc.netlogon.*.GetTrustPasswords
 ^samba4.rpc.netlogon.*.DatabaseRedo
-^samba4.rpc.netlogon.*.ServerGetTrustInfo
 ^samba4.base.charset.*.Testing partial surrogate
 ^samba4.*.base.maximum_allowed		# broken until we implement NTCREATEX_OPTIONS_BACKUP_INTENT
 .*net.api.delshare.*				# DelShare isn't implemented yet
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 9ed287f..a85ccb9 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -2712,7 +2712,158 @@ static NTSTATUS dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state *
 static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
 		       struct netr_ServerGetTrustInfo *r)
 {
-	DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+	struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+	struct netlogon_creds_CredentialState *creds = NULL;
+	struct ldb_context *sam_ctx = NULL;
+	const char * const attrs[] = {
+		"unicodePwd",
+		"sAMAccountName",
+		"userAccountControl",
+		NULL
+	};
+	struct ldb_message **res = NULL;
+	struct samr_Password *curNtHash = NULL, *prevNtHash = NULL;
+	NTSTATUS nt_status;
+	int ret;
+	const char *asid = NULL;
+	uint32_t uac = 0;
+	const char *aname = NULL;
+	struct ldb_message *tdo_msg = NULL;
+	const char * const tdo_attrs[] = {
+		"trustAuthIncoming",
+		"trustAttributes",
+		NULL
+	};
+	struct netr_TrustInfo *trust_info = NULL;
+
+	ZERO_STRUCTP(r->out.new_owf_password);
+	ZERO_STRUCTP(r->out.old_owf_password);
+
+	nt_status = dcesrv_netr_creds_server_step_check(dce_call,
+							mem_ctx,
+							r->in.computer_name,
+							r->in.credential,
+							r->out.return_authenticator,
+							&creds);
+	if (!NT_STATUS_IS_OK(nt_status)) {
+		return nt_status;
+	}
+
+	/* TODO: check r->in.server_name is our name */
+
+	if (strcasecmp_m(r->in.account_name, creds->account_name) != 0) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	if (r->in.secure_channel_type != creds->secure_channel_type) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	if (strcasecmp_m(r->in.computer_name, creds->computer_name) != 0) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+				lp_ctx, system_session(lp_ctx), 0);
+	if (sam_ctx == NULL) {
+		return NT_STATUS_INVALID_SYSTEM_SERVICE;
+	}
+
+	asid = ldap_encode_ndr_dom_sid(mem_ctx, creds->sid);
+	if (asid == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	ret = gendb_search(sam_ctx, mem_ctx, NULL, &res, attrs,
+			   "(&(objectClass=user)(objectSid=%s))",
+			   asid);
+	if (ret != 1) {
+		return NT_STATUS_ACCOUNT_DISABLED;
+	}
+
+	switch (creds->secure_channel_type) {
+	case SEC_CHAN_DNS_DOMAIN:
+	case SEC_CHAN_DOMAIN:
+		uac = ldb_msg_find_attr_as_uint(res[0], "userAccountControl", 0);
+
+		if (uac & UF_ACCOUNTDISABLE) {
+			return NT_STATUS_ACCOUNT_DISABLED;
+		}
+
+		if (!(uac & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+			return NT_STATUS_ACCOUNT_DISABLED;
+		}
+
+		aname = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
+		if (aname == NULL) {
+			return NT_STATUS_ACCOUNT_DISABLED;
+		}
+
+		nt_status = dsdb_trust_search_tdo_by_type(sam_ctx,
+						SEC_CHAN_DOMAIN, aname,
+						tdo_attrs, mem_ctx, &tdo_msg);
+		if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+			return NT_STATUS_ACCOUNT_DISABLED;
+		}
+		if (!NT_STATUS_IS_OK(nt_status)) {
+			return nt_status;
+		}
+
+		nt_status = dsdb_trust_get_incoming_passwords(tdo_msg, mem_ctx,
+							      &curNtHash,
+							      &prevNtHash);
+		if (!NT_STATUS_IS_OK(nt_status)) {
+			return nt_status;
+		}
+
+		trust_info = talloc_zero(mem_ctx, struct netr_TrustInfo);
+		if (trust_info == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		trust_info->count = 1;
+		trust_info->data = talloc_array(trust_info, uint32_t,
+						trust_info->count);
+		if (trust_info->data == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		trust_info->data[0] = ldb_msg_find_attr_as_uint(tdo_msg,
+							"trustAttributes",
+							0);
+		break;
+
+	default:
+		nt_status = samdb_result_passwords_no_lockout(mem_ctx, lp_ctx,
+							      res[0],
+							      NULL, &curNtHash);
+		if (!NT_STATUS_IS_OK(nt_status)) {
+			return nt_status;
+		}
+
+		prevNtHash = talloc(mem_ctx, struct samr_Password);
+		if (prevNtHash == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		E_md4hash("", prevNtHash->hash);
+		break;
+	}
+
+	if (curNtHash != NULL) {
+		*r->out.new_owf_password = *curNtHash;
+		netlogon_creds_des_encrypt(creds, r->out.new_owf_password);
+	}
+	if (prevNtHash != NULL) {
+		*r->out.old_owf_password = *prevNtHash;
+		netlogon_creds_des_encrypt(creds, r->out.old_owf_password);
+	}
+
+	if (trust_info != NULL) {
+		*r->out.trust_info = trust_info;
+	}
+
+	return NT_STATUS_OK;
 }
 
 /*
-- 
1.9.1


From 198822c9cdca4de13c918369357e1199bd387b27 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 9 Mar 2015 13:19:06 +0100
Subject: [PATCH 27/28] s4:rpc_server/netlogon: implement
 dcesrv_netr_ServerTrustPasswordsGet()

We just need to call dcesrv_netr_ServerGetTrustInfo() and ignore trust_info.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail                            |  1 -
 source4/rpc_server/netlogon/dcerpc_netlogon.c | 26 +++++++++++++++++++++++++-
 2 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index 2a570a0..53276c8 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -94,7 +94,6 @@
 ^samba4.rpc.netlogon.*.NetrEnumerateTrustedDomains
 ^samba4.rpc.netlogon.*.NetrEnumerateTrustedDomainsEx
 ^samba4.rpc.netlogon.*.GetPassword
-^samba4.rpc.netlogon.*.GetTrustPasswords
 ^samba4.rpc.netlogon.*.DatabaseRedo
 ^samba4.base.charset.*.Testing partial surrogate
 ^samba4.*.base.maximum_allowed		# broken until we implement NTCREATEX_OPTIONS_BACKUP_INTENT
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index a85ccb9..bab4723 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -2520,13 +2520,37 @@ static WERROR dcesrv_netr_DsrDeregisterDNSHostRecords(struct dcesrv_call_state *
 }
 
 
+static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+		       struct netr_ServerGetTrustInfo *r);
+
 /*
   netr_ServerTrustPasswordsGet
 */
 static NTSTATUS dcesrv_netr_ServerTrustPasswordsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
 		       struct netr_ServerTrustPasswordsGet *r)
 {
-	DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+	struct netr_ServerGetTrustInfo r2 = {};
+	struct netr_TrustInfo *_ti = NULL;
+	NTSTATUS status;
+
+	r2.in.server_name = r->in.server_name;
+	r2.in.account_name = r->in.account_name;
+	r2.in.secure_channel_type = r->in.secure_channel_type;
+	r2.in.computer_name = r->in.computer_name;
+	r2.in.credential = r->in.credential;
+
+	r2.out.return_authenticator = r->out.return_authenticator;
+	r2.out.new_owf_password = r->out.new_owf_password;
+	r2.out.old_owf_password = r->out.old_owf_password;
+	r2.out.trust_info = &_ti;
+
+	status = dcesrv_netr_ServerGetTrustInfo(dce_call, mem_ctx, &r2);
+
+	r->out.return_authenticator = r2.out.return_authenticator;
+	r->out.new_owf_password = r2.out.new_owf_password;
+	r->out.old_owf_password = r2.out.old_owf_password;
+
+	return status;
 }
 
 
-- 
1.9.1


From bd24f3b477ff5aa0892f04059870e51bfb22165a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 3 Feb 2015 18:30:36 +0100
Subject: [PATCH 28/28] s4:rpc_server/lsa: implement
 dcesrv_lsa_lsaRQueryForestTrustInformation()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/dcesrv_lsa.c | 76 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 75 insertions(+), 1 deletion(-)

diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c
index 0dbe9d1..a09a69a 100644
--- a/source4/rpc_server/lsa/dcesrv_lsa.c
+++ b/source4/rpc_server/lsa/dcesrv_lsa.c
@@ -4143,7 +4143,81 @@ static NTSTATUS dcesrv_lsa_LSARUNREGISTERAUDITEVENT(struct dcesrv_call_state *dc
 static NTSTATUS dcesrv_lsa_lsaRQueryForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
 		       struct lsa_lsaRQueryForestTrustInformation *r)
 {
-	DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+	struct dcesrv_handle *h = NULL;
+	struct lsa_policy_state *p_state = NULL;
+	int forest_level = DS_DOMAIN_FUNCTION_2000;
+	const char * const trust_attrs[] = {
+		"securityIdentifier",
+		"flatName",
+		"trustPartner",
+		"trustAttributes",
+		"trustDirection",
+		"trustType",
+		"msDS-TrustForestTrustInfo",
+		NULL
+	};
+	struct ldb_message *trust_tdo_msg = NULL;
+	struct lsa_TrustDomainInfoInfoEx *trust_tdo = NULL;
+	struct ForestTrustInfo *trust_fti = NULL;
+	struct lsa_ForestTrustInformation *trust_lfti = NULL;
+	NTSTATUS status;
+
+	DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+	p_state = h->data;
+
+	if (strcmp(p_state->domain_dns, p_state->forest_dns)) {
+		return NT_STATUS_INVALID_DOMAIN_STATE;
+	}
+
+	forest_level = dsdb_forest_functional_level(p_state->sam_ldb);
+	if (forest_level < DS_DOMAIN_FUNCTION_2003) {
+		return NT_STATUS_INVALID_DOMAIN_STATE;
+	}
+
+	if (r->in.trusted_domain_name->string == NULL) {
+		return NT_STATUS_NO_SUCH_DOMAIN;
+	}
+
+	status = dsdb_trust_search_tdo(p_state->sam_ldb,
+				       r->in.trusted_domain_name->string,
+				       r->in.trusted_domain_name->string,
+				       trust_attrs, mem_ctx, &trust_tdo_msg);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+		return NT_STATUS_NO_SUCH_DOMAIN;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	status = dsdb_trust_parse_tdo_info(mem_ctx, trust_tdo_msg, &trust_tdo);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	if (!(trust_tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	if (r->in.highest_record_type >= LSA_FOREST_TRUST_RECORD_TYPE_LAST) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	status = dsdb_trust_parse_forest_info(mem_ctx,
+					      trust_tdo_msg,
+					      &trust_fti);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	status = dsdb_trust_forest_info_to_lsa(mem_ctx, trust_fti,
+					       &trust_lfti);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	*r->out.forest_trust_info = trust_lfti;
+	return NT_STATUS_OK;
 }
 
 #define DNS_CMP_MATCH 0
-- 
1.9.1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20150409/5f25ce2f/attachment-0001.pgp>


More information about the samba-technical mailing list