[Patches] AD DC and lsa lookup sids/names for trusts

Stefan Metzmacher metze at samba.org
Mon Feb 19 21:51:50 UTC 2018


Hi,

here're some patches, which are currently being reviewed by Ralph.

They allow transitive forwarding of lsa lookup sids/names to trusted
domains and forests, when acting as AD DC.

The most important reason to have this is, that Samba domain member
servers rely on lsa lookup sids/names to work for a successful
authentication.

The bug references need to we updated...

The meta bug is https://bugzilla.samba.org/show_bug.cgi?id=13287

metze
-------------- next part --------------
From 31a00fa81fa46d798bf39a1499b604b41d608775 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 23 Jan 2018 14:34:45 +0100
Subject: [PATCH 01/30] winbindd: don't split the rid for SID_NAME_DOMAIN sids
 in wb_lookupsids

Bug 1:

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/winbindd/wb_lookupsids.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/source3/winbindd/wb_lookupsids.c b/source3/winbindd/wb_lookupsids.c
index f2b2768..6a4ad68 100644
--- a/source3/winbindd/wb_lookupsids.c
+++ b/source3/winbindd/wb_lookupsids.c
@@ -545,7 +545,9 @@ static void wb_lookupsids_single_done(struct tevent_req *subreq)
 		 */
 
 		sid_copy(&src_domain_sid, &state->sids[res_sid_index]);
-		sid_split_rid(&src_domain_sid, &src_rid);
+		if (type != SID_NAME_DOMAIN) {
+			sid_split_rid(&src_domain_sid, &src_rid);
+		}
 
 		src_domain.name.string = domain_name;
 		src_domain.sid = &src_domain_sid;
-- 
1.9.1


From b9cd6dba3220c204f1e5a8bac3f3bd2581c6bbbf Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 23 Jan 2018 23:52:59 +0100
Subject: [PATCH 02/30] winbindd: initialize type = SID_NAME_UNKNOWN in
 wb_lookupsids_single_done()

We check for !NT_STATUS_LOOKUP_ERR(), but wb_lookupsid_recv()
only initializes the results together with NT_STATUS_OK.

Bug 2: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/winbindd/wb_lookupsids.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/winbindd/wb_lookupsids.c b/source3/winbindd/wb_lookupsids.c
index 6a4ad68..bd90c43 100644
--- a/source3/winbindd/wb_lookupsids.c
+++ b/source3/winbindd/wb_lookupsids.c
@@ -513,7 +513,7 @@ static void wb_lookupsids_single_done(struct tevent_req *subreq)
 		req, struct wb_lookupsids_state);
 	const char *domain_name = NULL;
 	const char *name = NULL;
-	enum lsa_SidType type;
+	enum lsa_SidType type = SID_NAME_UNKNOWN;
 	uint32_t res_sid_index;
 	uint32_t src_rid;
 
-- 
1.9.1


From 8e2ef9599b2a652b3716f5eb6ffa252b21fa9659 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 2 Feb 2018 12:07:11 +0100
Subject: [PATCH 03/30] s3:cli_lsarpc: use talloc_zero_array() in
 dcerpc_lsa_lookup_sids_generic()

It just feels better for such a complex function.

Bug 3: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/rpc_client/cli_lsarpc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source3/rpc_client/cli_lsarpc.c b/source3/rpc_client/cli_lsarpc.c
index 65c6ca0..f9ff969 100644
--- a/source3/rpc_client/cli_lsarpc.c
+++ b/source3/rpc_client/cli_lsarpc.c
@@ -370,19 +370,19 @@ NTSTATUS dcerpc_lsa_lookup_sids_generic(struct dcerpc_binding_handle *h,
 	bool have_unmapped = false;
 
 	if (num_sids) {
-		if (!(domains = talloc_array(mem_ctx, char *, num_sids))) {
+		if (!(domains = talloc_zero_array(mem_ctx, char *, num_sids))) {
 			DEBUG(0, ("rpccli_lsa_lookup_sids(): out of memory\n"));
 			status = NT_STATUS_NO_MEMORY;
 			goto fail;
 		}
 
-		if (!(names = talloc_array(mem_ctx, char *, num_sids))) {
+		if (!(names = talloc_zero_array(mem_ctx, char *, num_sids))) {
 			DEBUG(0, ("rpccli_lsa_lookup_sids(): out of memory\n"));
 			status = NT_STATUS_NO_MEMORY;
 			goto fail;
 		}
 
-		if (!(types = talloc_array(mem_ctx, enum lsa_SidType, num_sids))) {
+		if (!(types = talloc_zero_array(mem_ctx, enum lsa_SidType, num_sids))) {
 			DEBUG(0, ("rpccli_lsa_lookup_sids(): out of memory\n"));
 			status = NT_STATUS_NO_MEMORY;
 			goto fail;
-- 
1.9.1


From 22052aa207ffa5d4adb2de454d214ea85a1f7b8d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 2 Feb 2018 12:07:11 +0100
Subject: [PATCH 04/30] s3:cli_lsarpc: use talloc_zero_array() in
 dcerpc_lsa_lookup_names_generic()

It just feels better for such a complex function.

Bug 3: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/rpc_client/cli_lsarpc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source3/rpc_client/cli_lsarpc.c b/source3/rpc_client/cli_lsarpc.c
index f9ff969..6d318f8 100644
--- a/source3/rpc_client/cli_lsarpc.c
+++ b/source3/rpc_client/cli_lsarpc.c
@@ -635,20 +635,20 @@ NTSTATUS dcerpc_lsa_lookup_names_generic(struct dcerpc_binding_handle *h,
 	}
 
 	if (num_names) {
-		if (!((*sids = talloc_array(mem_ctx, struct dom_sid, num_names)))) {
+		if (!((*sids = talloc_zero_array(mem_ctx, struct dom_sid, num_names)))) {
 			DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
 			*presult = NT_STATUS_NO_MEMORY;
 			goto done;
 		}
 
-		if (!((*types = talloc_array(mem_ctx, enum lsa_SidType, num_names)))) {
+		if (!((*types = talloc_zero_array(mem_ctx, enum lsa_SidType, num_names)))) {
 			DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
 			*presult = NT_STATUS_NO_MEMORY;
 			goto done;
 		}
 
 		if (dom_names != NULL) {
-			*dom_names = talloc_array(mem_ctx, const char *, num_names);
+			*dom_names = talloc_zero_array(mem_ctx, const char *, num_names);
 			if (*dom_names == NULL) {
 				DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n"));
 				*presult = NT_STATUS_NO_MEMORY;
-- 
1.9.1


From 2cae3dd2a6f1f096428a79844a66cc52a54c78a4 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 23 Jan 2018 23:52:37 +0100
Subject: [PATCH 05/30] winbindd: make use of talloc_zero_array() in
 wb_lookupsids*()

It just feels better for such a complex function.

Bug 3: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/winbindd/wb_lookupsids.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/source3/winbindd/wb_lookupsids.c b/source3/winbindd/wb_lookupsids.c
index bd90c43..af02a0c 100644
--- a/source3/winbindd/wb_lookupsids.c
+++ b/source3/winbindd/wb_lookupsids.c
@@ -124,7 +124,7 @@ struct tevent_req *wb_lookupsids_send(TALLOC_CTX *mem_ctx,
 	state->sids = sids;
 	state->num_sids = num_sids;
 
-	state->single_sids = talloc_array(state, uint32_t, num_sids);
+	state->single_sids = talloc_zero_array(state, uint32_t, num_sids);
 	if (tevent_req_nomem(state->single_sids, req)) {
 		return tevent_req_post(req, ev);
 	}
@@ -133,7 +133,7 @@ struct tevent_req *wb_lookupsids_send(TALLOC_CTX *mem_ctx,
 	if (tevent_req_nomem(state->res_domains, req)) {
 		return tevent_req_post(req, ev);
 	}
-	state->res_domains->domains = talloc_array(
+	state->res_domains->domains = talloc_zero_array(
 		state->res_domains, struct lsa_DomainInfo, num_sids);
 	if (tevent_req_nomem(state->res_domains->domains, req)) {
 		return tevent_req_post(req, ev);
@@ -143,7 +143,7 @@ struct tevent_req *wb_lookupsids_send(TALLOC_CTX *mem_ctx,
 	if (tevent_req_nomem(state->res_names, req)) {
 		return tevent_req_post(req, ev);
 	}
-	state->res_names->names = talloc_array(
+	state->res_names->names = talloc_zero_array(
 		state->res_names, struct lsa_TranslatedName, num_sids);
 	if (tevent_req_nomem(state->res_names->names, req)) {
 		return tevent_req_post(req, ev);
@@ -371,13 +371,13 @@ static struct wb_lookupsids_domain *wb_lookupsids_get_domain(
 	domain = &domains[num_domains];
 	domain->domain = wb_domain;
 
-	domain->sids.sids = talloc_array(domains, struct lsa_SidPtr, num_sids);
+	domain->sids.sids = talloc_zero_array(domains, struct lsa_SidPtr, num_sids);
 	if (domains->sids.sids == NULL) {
 		goto fail;
 	}
 	domain->sids.num_sids = 0;
 
-	domain->sid_indexes = talloc_array(domains, uint32_t, num_sids);
+	domain->sid_indexes = talloc_zero_array(domains, uint32_t, num_sids);
 	if (domain->sid_indexes == NULL) {
 		TALLOC_FREE(domain->sids.sids);
 		goto fail;
-- 
1.9.1


From c864c41b64c85e716196793adecb62eec32f71ac Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 26 Jan 2018 00:38:32 +0100
Subject: [PATCH 06/30] s4:torture: zero initialize variables in
 test_LookupSidsReply()

This avoids crashes if the server returns unexpected results.  The test
should just report the failure in that case.

Bug 4: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source4/torture/rpc/lsa_lookup.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/source4/torture/rpc/lsa_lookup.c b/source4/torture/rpc/lsa_lookup.c
index 59decda..f641827 100644
--- a/source4/torture/rpc/lsa_lookup.c
+++ b/source4/torture/rpc/lsa_lookup.c
@@ -329,9 +329,9 @@ bool torture_rpc_lsa_lookup(struct torture_context *torture)
 static bool test_LookupSidsReply(struct torture_context *tctx,
 				 struct dcerpc_pipe *p)
 {
-	struct policy_handle *handle;
+	struct policy_handle *handle = NULL;
 
-	struct dom_sid **sids;
+	struct dom_sid **sids = NULL;
 	uint32_t num_sids = 1;
 
 	struct lsa_LookupSids r;
@@ -346,6 +346,10 @@ static bool test_LookupSidsReply(struct torture_context *tctx,
 	struct dcerpc_binding_handle *b = p->binding_handle;
 	enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding);
 
+	ZERO_STRUCT(r);
+	ZERO_STRUCT(sidarray);
+	ZERO_STRUCT(names);
+
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		torture_comment(tctx,
 				"test_LookupSidsReply is only available "
@@ -359,7 +363,7 @@ static bool test_LookupSidsReply(struct torture_context *tctx,
 
 	dom_admin_sid = talloc_asprintf(tctx, "%s-%d", dom_sid, 512);
 
-	sids = talloc_array(tctx, struct dom_sid *, num_sids);
+	sids = talloc_zero_array(tctx, struct dom_sid *, num_sids);
 
 	sids[0] = dom_sid_parse_talloc(tctx, dom_admin_sid);
 
@@ -367,7 +371,7 @@ static bool test_LookupSidsReply(struct torture_context *tctx,
 	names.names = NULL;
 
 	sidarray.num_sids = num_sids;
-	sidarray.sids = talloc_array(tctx, struct lsa_SidPtr, num_sids);
+	sidarray.sids = talloc_zero_array(tctx, struct lsa_SidPtr, num_sids);
 
 	for (i=0; i<num_sids; i++) {
 		sidarray.sids[i].sid = sids[i];
-- 
1.9.1


From 421cf4a128fd1d80850a7af74a3fa9aa870aff88 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 10 Feb 2018 23:54:33 +0100
Subject: [PATCH 07/30] nsswitch: fix double free errors in nsstest.c

We need to zero out static pointers on free.

Bug 5: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 nsswitch/nsstest.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/nsswitch/nsstest.c b/nsswitch/nsstest.c
index 4b3d0a4..6d92806 100644
--- a/nsswitch/nsstest.c
+++ b/nsswitch/nsstest.c
@@ -21,6 +21,8 @@
 #include "replace.h"
 #include "nsswitch/nsstest.h"
 
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
+
 static const char *so_path = "/lib/libnss_winbind.so";
 static const char *nss_name = "winbind";
 static int nss_errno;
@@ -48,10 +50,10 @@ static void *find_fn(const char *name)
 	if (!res) {
 		printf("Can't find function %s\n", s);
 		total_errors++;
-		free(s);
+		SAFE_FREE(s);
 		return NULL;
 	}
-	free(s);
+	SAFE_FREE(s);
 	return res;
 }
 
@@ -194,12 +196,12 @@ again:
 		goto again;
 	}
 	if (status == NSS_STATUS_NOTFOUND) {
-		free(buf);
+		SAFE_FREE(buf);
 		return NULL;
 	}
 	if (status != NSS_STATUS_SUCCESS) {
 		report_nss_error("getgrent", status);
-		free(buf);
+		SAFE_FREE(buf);
 		return NULL;
 	}
 	return &grp;
@@ -232,12 +234,12 @@ again:
 		goto again;
 	}
 	if (status == NSS_STATUS_NOTFOUND) {
-		free(buf);
+		SAFE_FREE(buf);
 		return NULL;
 	}
 	if (status != NSS_STATUS_SUCCESS) {
 		report_nss_error("getgrnam", status);
-		free(buf);
+		SAFE_FREE(buf);
 		return NULL;
 	}
 	return &grp;
@@ -271,12 +273,12 @@ again:
 		goto again;
 	}
 	if (status == NSS_STATUS_NOTFOUND) {
-		free(buf);
+		SAFE_FREE(buf);
 		return NULL;
 	}
 	if (status != NSS_STATUS_SUCCESS) {
 		report_nss_error("getgrgid", status);
-		free(buf);
+		SAFE_FREE(buf);
 		return NULL;
 	}
 	return &grp;
-- 
1.9.1


From 48dc7dd6bb500b94a945a438407a696169ccbc59 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 25 Jan 2018 18:04:29 +0100
Subject: [PATCH 08/30] traffic_packets.py: let Lookup{Sids,Names}() work
 against a sane server

In order to resolve predefined sids or names we need to use
level = LSA_LOOKUP_NAMES_ALL (1).

Bug 6: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 python/samba/emulate/traffic_packets.py | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/python/samba/emulate/traffic_packets.py b/python/samba/emulate/traffic_packets.py
index 185d1d5..688c935 100644
--- a/python/samba/emulate/traffic_packets.py
+++ b/python/samba/emulate/traffic_packets.py
@@ -384,7 +384,7 @@ def packet_lsarpc_14(packet, conversation, context):
     sids  = lsa.TransSidArray()
     names = [lsa.String("This Organization"),
              lsa.String("Digest Authentication")]
-    level = 5
+    level = lsa.LSA_LOOKUP_NAMES_ALL
     count = 0
     c.LookupNames(pol_handle, names, sids, level, count)
     return True
@@ -406,7 +406,7 @@ def packet_lsarpc_15(packet, conversation, context):
     sids.sids = [sid]
     sids.num_sids = 1
     names = lsa.TransNameArray()
-    level = 5
+    level = lsa.LSA_LOOKUP_NAMES_ALL
     count = 0
 
     c.LookupSids(pol_handle, sids, names, level, count)
@@ -464,10 +464,10 @@ def packet_lsarpc_76(packet, conversation, context):
     sids.sids = [sid]
     sids.num_sids = 1
     names = lsa.TransNameArray2()
-    level = 5
+    level = lsa.LSA_LOOKUP_NAMES_ALL
     count = 0
-    lookup_options = 0
-    client_revision = 2
+    lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES
+    client_revision = lsa.LSA_CLIENT_REVISION_2
     c.LookupSids3(sids, names, level, count, lookup_options, client_revision)
     return True
 
@@ -478,10 +478,10 @@ def packet_lsarpc_77(packet, conversation, context):
     sids  = lsa.TransSidArray3()
     names = [lsa.String("This Organization"),
              lsa.String("Digest Authentication")]
-    level = 5
+    level = lsa.LSA_LOOKUP_NAMES_ALL
     count = 0
-    lookup_options = 0
-    client_revision = 2
+    lookup_options = lsa.LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES
+    client_revision = lsa.LSA_CLIENT_REVISION_2
     c.LookupNames4(names, sids, level, count, lookup_options, client_revision)
     return True
 
-- 
1.9.1


From af213d7c0c6b5eda5dc424c109f28900ac3d8b42 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 2 Feb 2018 21:06:38 +0100
Subject: [PATCH 09/30] provision: fix the 'dnsdomain' for the local sam of a
 domain member

A member has a local AD database, which should not use the 'dnsdomain'
as the one on domain controllers.

Bug 7: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 python/samba/provision/__init__.py | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/python/samba/provision/__init__.py b/python/samba/provision/__init__.py
index 2a926bb..558587c 100644
--- a/python/samba/provision/__init__.py
+++ b/python/samba/provision/__init__.py
@@ -654,6 +654,22 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     if domain == realm and not domain_names_forced:
         raise ProvisioningError("guess_names: Realm '%s' must not be equal to short domain name '%s'!" % (realm, domain))
 
+    if serverrole != "active directory domain controller":
+        #
+        # This is the code path for a domain member
+        # where we provision the database as if we where
+        # on a domain controller, so we should not use
+        # the same dnsdomain as the domain controllers
+        # of our primary domain.
+        #
+        # This will be important if we start doing
+        # SID/name filtering and reject the local
+        # sid and names if they come from a domain
+        # controller.
+        #
+        realm = netbiosname
+        dnsdomain = netbiosname.lower()
+
     if rootdn is None:
        rootdn = domaindn
 
-- 
1.9.1


From 144690213c6d74f871770eb08c97bd37ba715b09 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 25 Jan 2018 11:24:25 +0100
Subject: [PATCH 10/30] rpcclient: add lookupsids_level command

Bug 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Ralph Boehme <slow at samba.org>
---
 source3/rpcclient/cmd_lsarpc.c | 91 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/source3/rpcclient/cmd_lsarpc.c b/source3/rpcclient/cmd_lsarpc.c
index 2fa04a2..1109f03e 100644
--- a/source3/rpcclient/cmd_lsarpc.c
+++ b/source3/rpcclient/cmd_lsarpc.c
@@ -452,6 +452,96 @@ static NTSTATUS cmd_lsa_lookup_sids(struct rpc_pipe_client *cli, TALLOC_CTX *mem
 	return status;
 }
 
+static NTSTATUS cmd_lsa_lookup_sids_level(struct rpc_pipe_client *cli,
+					  TALLOC_CTX *mem_ctx, int argc,
+					  const char **argv)
+{
+	struct policy_handle pol;
+	NTSTATUS status, result;
+	struct dom_sid *sids;
+	char **domains;
+	char **names;
+	enum lsa_SidType *types;
+	int i, level;
+	struct dcerpc_binding_handle *b = cli->binding_handle;
+
+	if (argc < 3) {
+		printf("Usage: %s [level] [sid1 [sid2 [...]]]\n", argv[0]);
+		return NT_STATUS_OK;
+	}
+
+	status = rpccli_lsa_open_policy(cli, mem_ctx, True,
+				     SEC_FLAG_MAXIMUM_ALLOWED,
+				     &pol);
+
+	if (!NT_STATUS_IS_OK(status))
+		goto done;
+
+	level = atoi(argv[1]);
+
+	/* Convert arguments to sids */
+
+	sids = talloc_array(mem_ctx, struct dom_sid, argc - 2);
+
+	if (!sids) {
+		printf("could not allocate memory for %d sids\n", argc - 2);
+		goto done;
+	}
+
+	for (i = 0; i < argc - 2; i++)
+		if (!string_to_sid(&sids[i], argv[i + 2])) {
+			status = NT_STATUS_INVALID_SID;
+			goto done;
+		}
+
+	/* Lookup the SIDs */
+
+	status = dcerpc_lsa_lookup_sids_generic(cli->binding_handle,
+						mem_ctx,
+						&pol,
+						argc - 2,
+						sids,
+						level,
+						&domains,
+						&names,
+						&types,
+						false,
+						&result);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto done;
+	}
+	status = result;
+
+	if (!NT_STATUS_IS_OK(status) && NT_STATUS_V(status) !=
+	    NT_STATUS_V(STATUS_SOME_UNMAPPED))
+		goto done;
+
+	status = NT_STATUS_OK;
+
+	/* Print results */
+
+	for (i = 0; i < (argc - 2); i++) {
+		fstring sid_str;
+
+		sid_to_fstring(sid_str, &sids[i]);
+		if (types[i] == SID_NAME_DOMAIN) {
+			printf("%s %s (%d)\n", sid_str,
+			       domains[i] ? domains[i] : "*unknown*",
+			       types[i]);
+		} else {
+			printf("%s %s\\%s (%d)\n", sid_str,
+			       domains[i] ? domains[i] : "*unknown*",
+			       names[i] ? names[i] : "*unknown*",
+			       types[i]);
+		}
+	}
+
+	dcerpc_lsa_Close(b, mem_ctx, &pol, &result);
+
+ done:
+	return status;
+}
+
 /* Resolve a list of SIDs to a list of names */
 
 static NTSTATUS cmd_lsa_lookup_sids3(struct rpc_pipe_client *cli,
@@ -2279,6 +2369,7 @@ struct cmd_set lsarpc_commands[] = {
 	{ "lsaquery", 	         RPC_RTYPE_NTSTATUS, cmd_lsa_query_info_policy,  NULL, &ndr_table_lsarpc, NULL, "Query info policy",                    "" },
 	{ "lookupsids",          RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_sids,        NULL, &ndr_table_lsarpc, NULL, "Convert SIDs to names",                "" },
 	{ "lookupsids3",         RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_sids3,       NULL, &ndr_table_lsarpc, NULL, "Convert SIDs to names",                "" },
+	{ "lookupsids_level",    RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_sids_level,  NULL, &ndr_table_lsarpc, NULL, "Convert SIDs to names",                "" },
 	{ "lookupnames",         RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_names,       NULL, &ndr_table_lsarpc, NULL, "Convert names to SIDs",                "" },
 	{ "lookupnames4",        RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_names4,      NULL, &ndr_table_lsarpc, NULL, "Convert names to SIDs",                "" },
 	{ "lookupnames_level",   RPC_RTYPE_NTSTATUS, cmd_lsa_lookup_names_level, NULL, &ndr_table_lsarpc, NULL, "Convert names to SIDs",                "" },
-- 
1.9.1


From 398ac79e1967ae96ab79e9c6484d85a4cee2ed62 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:56:00 +0100
Subject: [PATCH 11/30] s4:rpc_server/lsa: use
 LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES/LSA_CLIENT_REVISION_1 in compat code

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index f54b118..d036141 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -797,8 +797,8 @@ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
 	r2.in.names    = NULL;
 	r2.in.level    = r->in.level;
 	r2.in.count    = r->in.count;
-	r2.in.lookup_options = 0;
-	r2.in.client_revision = 0;
+	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+	r2.in.client_revision = LSA_CLIENT_REVISION_1;
 	r2.out.count   = r->out.count;
 	r2.out.names   = NULL;
 	r2.out.domains = r->out.domains;
@@ -1124,8 +1124,8 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 	r2.in.sids      = NULL;
 	r2.in.level     = r->in.level;
 	r2.in.count     = r->in.count;
-	r2.in.lookup_options = 0;
-	r2.in.client_revision = 0;
+	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+	r2.in.client_revision = LSA_CLIENT_REVISION_1;
 	r2.out.count    = r->out.count;
 	r2.out.domains	= r->out.domains;
 
-- 
1.9.1


From 23e98a4ec676f9fd8bfc105af14ec7a4507c6916 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:56:00 +0100
Subject: [PATCH 12/30] s4:rpc_server/lsa: make sure
 dcesrv_lsa_LookupSids_common() gets prepared [ref] pointers

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index d036141..c2931c1 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -596,13 +596,16 @@ static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 	NTSTATUS status = NT_STATUS_OK;
 	uint32_t i;
 
+	*r->out.domains = NULL;
+	r->out.names->count = 0;
+	r->out.names->names = NULL;
+	*r->out.count = 0;
+
 	if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
 	    r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	*r->out.domains = NULL;
-
 	/* NOTE: the WSPP test suite tries SIDs with invalid revision numbers,
 	   and expects NT_STATUS_INVALID_PARAMETER back - we just treat it as 
 	   an unknown SID. We could add a SID validator here. (tridge) 
@@ -615,13 +618,6 @@ static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 	}
 	*r->out.domains = domains;
 
-	r->out.names = talloc_zero(mem_ctx,  struct lsa_TransNameArray2);
-	if (r->out.names == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	*r->out.count = 0;
-
 	r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName2, 
 					     r->in.sids->num_sids);
 	if (r->out.names->names == NULL) {
@@ -739,6 +735,11 @@ NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call,
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
+	*r->out.domains = NULL;
+	r->out.names->count = 0;
+	r->out.names->names = NULL;
+	*r->out.count = 0;
+
 	status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
 					     0, /* we skip access checks */
 					     &policy_state);
@@ -790,17 +791,28 @@ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
+	*r->out.domains = NULL;
+	r->out.names->count = 0;
+	r->out.names->names = NULL;
+	*r->out.count = 0;
+
 	ZERO_STRUCT(r2);
 
 	r2.in.handle   = r->in.handle;
 	r2.in.sids     = r->in.sids;
-	r2.in.names    = NULL;
+	r2.in.names    = talloc_zero(mem_ctx, struct lsa_TransNameArray2);
+	if (r2.in.names == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
 	r2.in.level    = r->in.level;
 	r2.in.count    = r->in.count;
 	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
 	r2.in.client_revision = LSA_CLIENT_REVISION_1;
 	r2.out.count   = r->out.count;
-	r2.out.names   = NULL;
+	r2.out.names   = talloc_zero(mem_ctx, struct lsa_TransNameArray2);
+	if (r2.out.names == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
 	r2.out.domains = r->out.domains;
 
 	status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2);
-- 
1.9.1


From abe5dea2092c7a8ff9a09946f1a211d6ddc31acd Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:56:00 +0100
Subject: [PATCH 13/30] s4:rpc_server/lsa: expect prepared [ref] pointers in
 dcesrv_lsa_LookupSids_common()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index c2931c1..8c10014 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -853,26 +853,22 @@ static NTSTATUS dcesrv_lsa_LookupNames_common(struct dcesrv_call_state *dce_call
 	struct lsa_RefDomainList *domains;
 	uint32_t i;
 
+	*r->out.domains = NULL;
+	r->out.sids->count = 0;
+	r->out.sids->sids = NULL;
+	*r->out.count = 0;
+
 	if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
 	    r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	*r->out.domains = NULL;
-
-	domains = talloc_zero(mem_ctx,  struct lsa_RefDomainList);
+	domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
 	if (domains == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 	*r->out.domains = domains;
 
-	r->out.sids = talloc_zero(mem_ctx,  struct lsa_TransSidArray3);
-	if (r->out.sids == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	*r->out.count = 0;
-
 	r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid3, 
 					   r->in.num_names);
 	if (r->out.sids->sids == NULL) {
@@ -979,6 +975,11 @@ NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
+	*r->out.domains = NULL;
+	r->out.sids->count = 0;
+	r->out.sids->sids = NULL;
+	*r->out.count = 0;
+
 	status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
 					     0, /* we skip access checks */
 					     &policy_state);
-- 
1.9.1


From 3e9380502c23b3d2817103ae5b4352ccf434166e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:56:00 +0100
Subject: [PATCH 14/30] s4:rpc_server/lsa: make sure dcesrv_lsa_LookupNames2()
 gets prepared [ref] pointers

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 30 +++++++++++++++++++-----------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 8c10014..219e301 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -1035,10 +1035,13 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
-	*r->out.domains = NULL;
-
 	DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
 
+	*r->out.domains = NULL;
+	r->out.sids->count = 0;
+	r->out.sids->sids = NULL;
+	*r->out.count = 0;
+
 	if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
 	    r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
 		return NT_STATUS_INVALID_PARAMETER;
@@ -1046,19 +1049,12 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 
 	state = h->data;
 
-	domains = talloc_zero(mem_ctx,  struct lsa_RefDomainList);
+	domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
 	if (domains == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 	*r->out.domains = domains;
 
-	r->out.sids = talloc_zero(mem_ctx,  struct lsa_TransSidArray2);
-	if (r->out.sids == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	*r->out.count = 0;
-
 	r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid2, 
 					   r->in.num_names);
 	if (r->out.sids->sids == NULL) {
@@ -1129,17 +1125,29 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
+	*r->out.domains = NULL;
+	r->out.sids->count = 0;
+	r->out.sids->sids = NULL;
+	*r->out.count = 0;
+
 	ZERO_STRUCT(r2);
 
 	r2.in.handle    = r->in.handle;
 	r2.in.num_names = r->in.num_names;
 	r2.in.names     = r->in.names;
-	r2.in.sids      = NULL;
+	r2.in.sids      = talloc_zero(mem_ctx, struct lsa_TransSidArray2);
+	if (r2.in.sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
 	r2.in.level     = r->in.level;
 	r2.in.count     = r->in.count;
 	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
 	r2.in.client_revision = LSA_CLIENT_REVISION_1;
 	r2.out.count    = r->out.count;
+	r2.out.sids     = talloc_zero(mem_ctx, struct lsa_TransSidArray2);
+	if (r2.out.sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
 	r2.out.domains	= r->out.domains;
 
 	status = dcesrv_lsa_LookupNames2(dce_call, mem_ctx, &r2);
-- 
1.9.1


From ddbd180c27afd6ee8b7f95e508671d7ec31b6c0a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:56:00 +0100
Subject: [PATCH 15/30] s4:rpc_server/lsa: remove unused 'status' variable in
 dcesrv_lsa_LookupSids_common()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 219e301..a8a136b 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -593,7 +593,6 @@ static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 					     struct lsa_LookupSids2 *r)
 {
 	struct lsa_RefDomainList *domains = NULL;
-	NTSTATUS status = NT_STATUS_OK;
 	uint32_t i;
 
 	*r->out.domains = NULL;
@@ -641,14 +640,12 @@ static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 
 		if (sid_str == NULL) {
 			r->out.names->names[i].name.string = "(SIDERROR)";
-			status = STATUS_SOME_UNMAPPED;
 			continue;
 		}
 
 		status2 = dcesrv_lsa_lookup_sid(state, mem_ctx, sid, sid_str, 
 						&authority_name, &name, &rtype);
 		if (!NT_STATUS_IS_OK(status2)) {
-			status = STATUS_SOME_UNMAPPED;
 			continue;
 		}
 
@@ -675,7 +672,7 @@ static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 		return STATUS_SOME_UNMAPPED;
 	}
 
-	return status;
+	return NT_STATUS_OK;
 }
 
 /*
-- 
1.9.1


From 2bf332ac6ffd9138e0579a22743a661ff55448ff Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:56:00 +0100
Subject: [PATCH 16/30] s4:rpc_server/lsa: simplify [ref] pointer handling in
 dcesrv_lsa_LookupSids()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index a8a136b..400b521 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -793,6 +793,13 @@ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
 	r->out.names->names = NULL;
 	*r->out.count = 0;
 
+	r->out.names->names = talloc_zero_array(r->out.names,
+						struct lsa_TranslatedName,
+						r->in.sids->num_sids);
+	if (r->out.names->names == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
 	ZERO_STRUCT(r2);
 
 	r2.in.handle   = r->in.handle;
@@ -816,27 +823,13 @@ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
 	/* we deliberately don't check for error from the above,
 	   as even on error we are supposed to return the names  */
 
-	r->out.domains = r2.out.domains;
-	if (!r2.out.names) {
-		r->out.names = NULL;
-		return status;
-	}
-
-	r->out.names = talloc(mem_ctx, struct lsa_TransNameArray);
-	if (r->out.names == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	r->out.names->count = r2.out.names->count;
-	r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName, 
-					     r->out.names->count);
-	if (r->out.names->names == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	for (i=0;i<r->out.names->count;i++) {
+	SMB_ASSERT(r2.out.names->count <= r->in.sids->num_sids);
+	for (i=0;i<r2.out.names->count;i++) {
 		r->out.names->names[i].sid_type    = r2.out.names->names[i].sid_type;
 		r->out.names->names[i].name.string = r2.out.names->names[i].name.string;
 		r->out.names->names[i].sid_index   = r2.out.names->names[i].sid_index;
 	}
+	r->out.names->count = r2.out.names->count;
 
 	return status;
 }
-- 
1.9.1


From 3bea1de259c884b2091e2c1b6aa14d39eca3b1a5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:56:00 +0100
Subject: [PATCH 17/30] s4:rpc_server/lsa: simplify [ref] pointer handling in
 dcesrv_lsa_LookupNames()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 24 ++++++++++--------------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 400b521..defcef5 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -1120,6 +1120,13 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 	r->out.sids->sids = NULL;
 	*r->out.count = 0;
 
+	r->out.sids->sids = talloc_zero_array(r->out.sids,
+					      struct lsa_TranslatedSid,
+					      r->in.num_names);
+	if (r->out.sids->sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
 	ZERO_STRUCT(r2);
 
 	r2.in.handle    = r->in.handle;
@@ -1141,25 +1148,14 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 	r2.out.domains	= r->out.domains;
 
 	status = dcesrv_lsa_LookupNames2(dce_call, mem_ctx, &r2);
-	if (r2.out.sids == NULL) {
-		return status;
-	}
 
-	r->out.sids = talloc(mem_ctx, struct lsa_TransSidArray);
-	if (r->out.sids == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	r->out.sids->count = r2.out.sids->count;
-	r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid, 
-					   r->out.sids->count);
-	if (r->out.sids->sids == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	for (i=0;i<r->out.sids->count;i++) {
+	SMB_ASSERT(r2.out.sids->count <= r->in.num_names);
+	for (i=0;i<r2.out.sids->count;i++) {
 		r->out.sids->sids[i].sid_type    = r2.out.sids->sids[i].sid_type;
 		r->out.sids->sids[i].rid         = r2.out.sids->sids[i].rid;
 		r->out.sids->sids[i].sid_index   = r2.out.sids->sids[i].sid_index;
 	}
+	r->out.sids->count = r2.out.sids->count;
 
 	return status;
 }
-- 
1.9.1


From 3a439c71d3809520fbfe49638f33dfad65356534 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 22 Jan 2018 09:27:49 +0100
Subject: [PATCH 18/30] s4:rpc_server/lsa: rename 'state' variable to
 'policy_state' in dcesrv_lsa_LookupSids_common()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index defcef5..378aff0 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -589,7 +589,7 @@ static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state, TALLOC_CTX
 
 static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 					     TALLOC_CTX *mem_ctx,
-					     struct lsa_policy_state *state,
+					     struct lsa_policy_state *policy_state,
 					     struct lsa_LookupSids2 *r)
 {
 	struct lsa_RefDomainList *domains = NULL;
@@ -643,15 +643,15 @@ static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 			continue;
 		}
 
-		status2 = dcesrv_lsa_lookup_sid(state, mem_ctx, sid, sid_str, 
+		status2 = dcesrv_lsa_lookup_sid(policy_state, mem_ctx, sid, sid_str,
 						&authority_name, &name, &rtype);
 		if (!NT_STATUS_IS_OK(status2)) {
 			continue;
 		}
 
 		/* set up the authority table */
-		status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype, 
-						    authority_name, sid, 
+		status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx, rtype,
+						    authority_name, sid,
 						    domains, &sid_index);
 		if (!NT_STATUS_IS_OK(status2)) {
 			continue;
-- 
1.9.1


From 0e32d94bdeb3458456802369ca612ede48e38acb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 22 Jan 2018 09:27:49 +0100
Subject: [PATCH 19/30] s4:rpc_server/lsa: rename 'state' variable to
 'policy_state' in dcesrv_lsa_LookupSids2()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 378aff0..65c664e 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -684,20 +684,20 @@ NTSTATUS dcesrv_lsa_LookupSids2(struct dcesrv_call_state *dce_call,
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_policy_state *state;
-	struct dcesrv_handle *h;
+	struct lsa_policy_state *policy_state = NULL;
+	struct dcesrv_handle *policy_handle = NULL;
 
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
-	DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
 
-	state = h->data;
+	policy_state = policy_handle->data;
 
 	return dcesrv_lsa_LookupSids_common(dce_call,
 					    mem_ctx,
-					    state,
+					    policy_state,
 					    r);
 }
 
-- 
1.9.1


From ed79c17e60b8c9240fec43da6fd8caa718f52760 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 22 Jan 2018 09:27:49 +0100
Subject: [PATCH 20/30] s4:rpc_server/lsa: rename 'state' variable to
 'policy_state' in dcesrv_lsa_LookupNames2()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 65c664e..cefc48f 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -1015,8 +1015,8 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_policy_state *state;
-	struct dcesrv_handle *h;
+	struct lsa_policy_state *policy_state = NULL;
+	struct dcesrv_handle *policy_handle = NULL;
 	uint32_t i;
 	struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
 	struct lsa_RefDomainList *domains;
@@ -1025,7 +1025,9 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
-	DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+	policy_state = policy_handle->data;
 
 	*r->out.domains = NULL;
 	r->out.sids->count = 0;
@@ -1037,8 +1039,6 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	state = h->data;
-
 	domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
 	if (domains == NULL) {
 		return NT_STATUS_NO_MEMORY;
@@ -1069,14 +1069,17 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 		r->out.sids->sids[i].sid_index   = 0xFFFFFFFF;
 		r->out.sids->sids[i].unknown     = 0;
 
-		status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, state, mem_ctx, name,
-						 &authority_name, &sid, &rtype, &rid);
+		status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx,
+						 policy_state, mem_ctx, name,
+						 &authority_name, &sid, &rtype,
+						 &rid);
 		if (!NT_STATUS_IS_OK(status2)) {
 			continue;
 		}
 
-		status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype, authority_name, 
-						    sid, domains, &sid_index);
+		status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx,
+						    rtype, authority_name, sid,
+						    domains, &sid_index);
 		if (!NT_STATUS_IS_OK(status2)) {
 			continue;
 		}
-- 
1.9.1


From f7aaff63134338254058662e20e71732ba3b4803 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 22 Jan 2018 20:21:14 +0100
Subject: [PATCH 21/30] s4:rpc_server/lsa: base dcesrv_lsa_LookupNames() on
 dcesrv_lsa_LookupNames_common()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 43 +++++++++++++++++++++++++++++++------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index cefc48f..1e9b4c6 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -1110,7 +1110,9 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_LookupNames2 r2;
+	struct lsa_policy_state *policy_state = NULL;
+	struct dcesrv_handle *policy_handle = NULL;
+	struct lsa_LookupNames3 r2;
 	NTSTATUS status;
 	uint32_t i;
 
@@ -1118,6 +1120,10 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
+	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+	policy_state = policy_handle->data;
+
 	*r->out.domains = NULL;
 	r->out.sids->count = 0;
 	r->out.sids->sids = NULL;
@@ -1135,7 +1141,7 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 	r2.in.handle    = r->in.handle;
 	r2.in.num_names = r->in.num_names;
 	r2.in.names     = r->in.names;
-	r2.in.sids      = talloc_zero(mem_ctx, struct lsa_TransSidArray2);
+	r2.in.sids      = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
 	if (r2.in.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
@@ -1144,19 +1150,42 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
 	r2.in.client_revision = LSA_CLIENT_REVISION_1;
 	r2.out.count    = r->out.count;
-	r2.out.sids     = talloc_zero(mem_ctx, struct lsa_TransSidArray2);
+	r2.out.sids     = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
 	if (r2.out.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 	r2.out.domains	= r->out.domains;
 
-	status = dcesrv_lsa_LookupNames2(dce_call, mem_ctx, &r2);
+	status = dcesrv_lsa_LookupNames_common(dce_call,
+					       mem_ctx,
+					       policy_state,
+					       &r2);
 
 	SMB_ASSERT(r2.out.sids->count <= r->in.num_names);
 	for (i=0;i<r2.out.sids->count;i++) {
-		r->out.sids->sids[i].sid_type    = r2.out.sids->sids[i].sid_type;
-		r->out.sids->sids[i].rid         = r2.out.sids->sids[i].rid;
-		r->out.sids->sids[i].sid_index   = r2.out.sids->sids[i].sid_index;
+		struct lsa_TranslatedSid3 *s3 =
+			&r2.out.sids->sids[i];
+		struct lsa_TranslatedSid *s =
+			&r->out.sids->sids[i];
+
+		s->sid_type = s3->sid_type;
+		if (s3->sid_type == SID_NAME_DOMAIN) {
+			s->rid = UINT32_MAX;
+		} else if (s3->flags & 0x00000004) {
+			s->rid = UINT32_MAX;
+		} else if (s3->sid == NULL) {
+			/*
+			 * MS-LSAT 3.1.4.7 - rid zero is considered
+			 * equivalent to sid NULL - so we should return
+			 * 0 rid for unmapped entries
+			 */
+			s->rid = 0;
+		} else {
+			s->rid = 0;
+			dom_sid_split_rid(NULL, s3->sid,
+					  NULL, &s->rid);
+		}
+		s->sid_index = s3->sid_index;
 	}
 	r->out.sids->count = r2.out.sids->count;
 
-- 
1.9.1


From 17e1fdc193c4d2d5d71ddf68b426648ec57a4d6f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 22 Jan 2018 20:21:14 +0100
Subject: [PATCH 22/30] s4:rpc_server/lsa: base dcesrv_lsa_LookupNames2() on
 dcesrv_lsa_LookupNames_common()

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 107 +++++++++++++++++-------------------
 1 file changed, 51 insertions(+), 56 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 1e9b4c6..f8deae1 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -1017,9 +1017,9 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
 	struct lsa_policy_state *policy_state = NULL;
 	struct dcesrv_handle *policy_handle = NULL;
+	struct lsa_LookupNames3 r2;
+	NTSTATUS status;
 	uint32_t i;
-	struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
-	struct lsa_RefDomainList *domains;
 
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
@@ -1034,72 +1034,67 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 	r->out.sids->sids = NULL;
 	*r->out.count = 0;
 
-	if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
-	    r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
-		return NT_STATUS_INVALID_PARAMETER;
+	r->out.sids->sids = talloc_zero_array(r->out.sids,
+					      struct lsa_TranslatedSid2,
+					      r->in.num_names);
+	if (r->out.sids->sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
 	}
 
-	domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
-	if (domains == NULL) {
+	ZERO_STRUCT(r2);
+
+	r2.in.handle    = r->in.handle;
+	r2.in.num_names = r->in.num_names;
+	r2.in.names     = r->in.names;
+	r2.in.sids      = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
+	if (r2.in.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	*r->out.domains = domains;
-
-	r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid2, 
-					   r->in.num_names);
-	if (r->out.sids->sids == NULL) {
+	r2.in.level     = r->in.level;
+	r2.in.count     = r->in.count;
+	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+	r2.in.client_revision = LSA_CLIENT_REVISION_1;
+	r2.out.count    = r->out.count;
+	r2.out.sids     = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
+	if (r2.out.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
+	r2.out.domains	= r->out.domains;
 
-	for (i=0;i<r->in.num_names;i++) {
-		const char *name = r->in.names[i].string;
-		const char *authority_name;
-		struct dom_sid *sid;
-		uint32_t sid_index, rid=0;
-		enum lsa_SidType rtype;
-		NTSTATUS status2;
-
-		r->out.sids->count++;
-
-		r->out.sids->sids[i].sid_type    = SID_NAME_UNKNOWN;
-		/* MS-LSAT 3.1.4.7 - rid zero is considered equivalent
-		   to sid NULL - so we should return 0 rid for
-		   unmapped entries */
-		r->out.sids->sids[i].rid         = 0;
-		r->out.sids->sids[i].sid_index   = 0xFFFFFFFF;
-		r->out.sids->sids[i].unknown     = 0;
+	status = dcesrv_lsa_LookupNames_common(dce_call,
+					       mem_ctx,
+					       policy_state,
+					       &r2);
 
-		status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx,
-						 policy_state, mem_ctx, name,
-						 &authority_name, &sid, &rtype,
-						 &rid);
-		if (!NT_STATUS_IS_OK(status2)) {
-			continue;
-		}
+	SMB_ASSERT(r2.out.sids->count <= r->in.num_names);
+	for (i=0;i<r2.out.sids->count;i++) {
+		struct lsa_TranslatedSid3 *s3 =
+			&r2.out.sids->sids[i];
+		struct lsa_TranslatedSid2 *s2 =
+			&r->out.sids->sids[i];
 
-		status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx,
-						    rtype, authority_name, sid,
-						    domains, &sid_index);
-		if (!NT_STATUS_IS_OK(status2)) {
-			continue;
+		s2->sid_type = s3->sid_type;
+		if (s3->sid_type == SID_NAME_DOMAIN) {
+			s2->rid = UINT32_MAX;
+		} else if (s3->flags & 0x00000004) {
+			s2->rid = UINT32_MAX;
+		} else if (s3->sid == NULL) {
+			/*
+			 * MS-LSAT 3.1.4.7 - rid zero is considered
+			 * equivalent to sid NULL - so we should return
+			 * 0 rid for unmapped entries
+			 */
+			s2->rid = 0;
+		} else {
+			s2->rid = 0;
+			dom_sid_split_rid(NULL, s3->sid,
+					  NULL, &s2->rid);
 		}
-
-		r->out.sids->sids[i].sid_type    = rtype;
-		r->out.sids->sids[i].rid         = rid;
-		r->out.sids->sids[i].sid_index   = sid_index;
-		r->out.sids->sids[i].unknown     = 0;
-
-		(*r->out.count)++;
-	}
-	
-	if (*r->out.count == 0) {
-		return NT_STATUS_NONE_MAPPED;
-	}
-	if (*r->out.count != r->in.num_names) {
-		return STATUS_SOME_UNMAPPED;
+		s2->sid_index = s3->sid_index;
 	}
+	r->out.sids->count = r2.out.sids->count;
 
-	return NT_STATUS_OK;
+	return status;
 }
 
 /* 
-- 
1.9.1


From 6bcf640610fa1c7884b100a633d254e22d205084 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 19 Jan 2018 13:42:40 +0100
Subject: [PATCH 23/30] s4:rpc_server/lsa: prepare dcesrv_lsa_LookupSids* for
 async processing

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 215 ++++++++++++++++++++++++++----------
 1 file changed, 157 insertions(+), 58 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index f8deae1..487f6eb 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -587,11 +587,27 @@ static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state, TALLOC_CTX
 	return NT_STATUS_OK;
 }
 
-static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
-					     TALLOC_CTX *mem_ctx,
-					     struct lsa_policy_state *policy_state,
-					     struct lsa_LookupSids2 *r)
+struct dcesrv_lsa_LookupSids_base_state {
+	struct dcesrv_call_state *dce_call;
+
+	TALLOC_CTX *mem_ctx;
+
+	struct lsa_policy_state *policy_state;
+
+	struct lsa_LookupSids3 r;
+
+	struct {
+		struct lsa_LookupSids *l;
+		struct lsa_LookupSids2 *l2;
+		struct lsa_LookupSids3 *l3;
+	} _r;
+};
+
+static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_base_state *state)
 {
+	struct lsa_policy_state *policy_state = state->policy_state;
+	TALLOC_CTX *mem_ctx = state->mem_ctx;
+	struct lsa_LookupSids3 *r = &state->r;
 	struct lsa_RefDomainList *domains = NULL;
 	uint32_t i;
 
@@ -675,6 +691,45 @@ static NTSTATUS dcesrv_lsa_LookupSids_common(struct dcesrv_call_state *dce_call,
 	return NT_STATUS_OK;
 }
 
+static void dcesrv_lsa_LookupSids_base_map(
+	struct dcesrv_lsa_LookupSids_base_state *state)
+{
+	if (state->_r.l3 != NULL) {
+		struct lsa_LookupSids3 *r = state->_r.l3;
+
+		r->out.result = state->r.out.result;
+		return;
+	}
+
+	if (state->_r.l2 != NULL) {
+		struct lsa_LookupSids2 *r = state->_r.l2;
+
+		r->out.result = state->r.out.result;
+		return;
+	}
+
+	if (state->_r.l != NULL) {
+		struct lsa_LookupSids *r = state->_r.l;
+		uint32_t i;
+
+		r->out.result = state->r.out.result;
+
+		SMB_ASSERT(state->r.out.names->count <= r->in.sids->num_sids);
+		for (i = 0; i < state->r.out.names->count; i++) {
+			struct lsa_TranslatedName2 *n2 =
+				&state->r.out.names->names[i];
+			struct lsa_TranslatedName *n =
+				&r->out.names->names[i];
+
+			n->sid_type = n2->sid_type;
+			n->name = n2->name;
+			n->sid_index = n2->sid_index;
+		}
+		r->out.names->count = state->r.out.names->count;
+		return;
+	}
+}
+
 /*
   lsa_LookupSids2
 */
@@ -684,8 +739,9 @@ NTSTATUS dcesrv_lsa_LookupSids2(struct dcesrv_call_state *dce_call,
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_policy_state *policy_state = NULL;
+	struct dcesrv_lsa_LookupSids_base_state *state = NULL;
 	struct dcesrv_handle *policy_handle = NULL;
+	NTSTATUS status;
 
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
@@ -693,12 +749,43 @@ NTSTATUS dcesrv_lsa_LookupSids2(struct dcesrv_call_state *dce_call,
 
 	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
 
-	policy_state = policy_handle->data;
+	*r->out.domains = NULL;
+	r->out.names->count = 0;
+	r->out.names->names = NULL;
+	*r->out.count = 0;
+
+	state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->dce_call = dce_call;
+	state->mem_ctx = mem_ctx;
+
+	state->policy_state = policy_handle->data;
+
+	state->r.in.sids = r->in.sids;
+	state->r.in.level = r->in.level;
+	state->r.in.lookup_options = r->in.lookup_options;
+	state->r.in.client_revision = r->in.client_revision;
+	state->r.in.names = r->in.names;
+	state->r.in.count = r->in.count;
+	state->r.out.domains = r->out.domains;
+	state->r.out.names = r->out.names;
+	state->r.out.count = r->out.count;
+
+	state->_r.l2 = r;
 
-	return dcesrv_lsa_LookupSids_common(dce_call,
-					    mem_ctx,
-					    policy_state,
-					    r);
+	status = dcesrv_lsa_LookupSids_base_call(state);
+
+	if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+		return status;
+	}
+
+	state->r.out.result = status;
+	dcesrv_lsa_LookupSids_base_map(state);
+	TALLOC_FREE(state);
+	return status;
 }
 
 
@@ -715,8 +802,7 @@ NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call,
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
 	const struct dcesrv_auth *auth = &dce_call->conn->auth_state;
-	struct lsa_policy_state *policy_state;
-	struct lsa_LookupSids2 q;
+	struct dcesrv_lsa_LookupSids_base_state *state = NULL;
 	NTSTATUS status;
 
 	if (transport != NCACN_IP_TCP) {
@@ -737,37 +823,42 @@ NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call,
 	r->out.names->names = NULL;
 	*r->out.count = 0;
 
-	status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+	state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->dce_call = dce_call;
+	state->mem_ctx = mem_ctx;
+
+	status = dcesrv_lsa_get_policy_state(state->dce_call, mem_ctx,
 					     0, /* we skip access checks */
-					     &policy_state);
+					     &state->policy_state);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
 
-	ZERO_STRUCT(q);
+	state->r.in.sids = r->in.sids;
+	state->r.in.level = r->in.level;
+	state->r.in.lookup_options = r->in.lookup_options;
+	state->r.in.client_revision = r->in.client_revision;
+	state->r.in.names = r->in.names;
+	state->r.in.count = r->in.count;
+	state->r.out.domains = r->out.domains;
+	state->r.out.names = r->out.names;
+	state->r.out.count = r->out.count;
 
-	q.in.handle   = NULL;
-	q.in.sids     = r->in.sids;
-	q.in.names    = r->in.names;
-	q.in.level    = r->in.level;
-	q.in.count    = r->in.count;
-	q.in.lookup_options = r->in.lookup_options;
-	q.in.client_revision = r->in.client_revision;
-	q.out.count   = r->out.count;
-	q.out.names   = r->out.names;
-	q.out.domains = r->out.domains;
+	state->_r.l3 = r;
 
-	status = dcesrv_lsa_LookupSids_common(dce_call,
-					      mem_ctx,
-					      policy_state,
-					      &q);
-
-	talloc_free(policy_state);
+	status = dcesrv_lsa_LookupSids_base_call(state);
 
-	r->out.count = q.out.count;
-	r->out.names = q.out.names;
-	r->out.domains = q.out.domains;
+	if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+		return status;
+	}
 
+	state->r.out.result = status;
+	dcesrv_lsa_LookupSids_base_map(state);
+	TALLOC_FREE(state);
 	return status;
 }
 
@@ -780,14 +871,16 @@ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_LookupSids2 r2;
+	struct dcesrv_lsa_LookupSids_base_state *state = NULL;
+	struct dcesrv_handle *policy_handle = NULL;
 	NTSTATUS status;
-	uint32_t i;
 
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
 	}
 
+	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
 	*r->out.domains = NULL;
 	r->out.names->count = 0;
 	r->out.names->names = NULL;
@@ -800,37 +893,43 @@ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	ZERO_STRUCT(r2);
+	state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupSids_base_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->dce_call = dce_call;
+	state->mem_ctx = mem_ctx;
+
+	state->policy_state = policy_handle->data;
 
-	r2.in.handle   = r->in.handle;
-	r2.in.sids     = r->in.sids;
-	r2.in.names    = talloc_zero(mem_ctx, struct lsa_TransNameArray2);
-	if (r2.in.names == NULL) {
+	state->r.in.sids = r->in.sids;
+	state->r.in.level = r->in.level;
+	state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+	state->r.in.client_revision = LSA_CLIENT_REVISION_1;
+	state->r.in.names = talloc_zero(state, struct lsa_TransNameArray2);
+	if (state->r.in.names == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	r2.in.level    = r->in.level;
-	r2.in.count    = r->in.count;
-	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
-	r2.in.client_revision = LSA_CLIENT_REVISION_1;
-	r2.out.count   = r->out.count;
-	r2.out.names   = talloc_zero(mem_ctx, struct lsa_TransNameArray2);
-	if (r2.out.names == NULL) {
+	state->r.in.count = r->in.count;
+	state->r.out.domains = r->out.domains;
+	state->r.out.names = talloc_zero(state, struct lsa_TransNameArray2);
+	if (state->r.out.names == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	r2.out.domains = r->out.domains;
+	state->r.out.count = r->out.count;
 
-	status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2);
-	/* we deliberately don't check for error from the above,
-	   as even on error we are supposed to return the names  */
+	state->_r.l = r;
 
-	SMB_ASSERT(r2.out.names->count <= r->in.sids->num_sids);
-	for (i=0;i<r2.out.names->count;i++) {
-		r->out.names->names[i].sid_type    = r2.out.names->names[i].sid_type;
-		r->out.names->names[i].name.string = r2.out.names->names[i].name.string;
-		r->out.names->names[i].sid_index   = r2.out.names->names[i].sid_index;
+	status = dcesrv_lsa_LookupSids_base_call(state);
+
+	if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+		return status;
 	}
-	r->out.names->count = r2.out.names->count;
 
+	state->r.out.result = status;
+	dcesrv_lsa_LookupSids_base_map(state);
+	TALLOC_FREE(state);
 	return status;
 }
 
-- 
1.9.1


From f74cffd11d1caa52a81f22338ecde67a4b32ab6f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 19 Jan 2018 13:42:40 +0100
Subject: [PATCH 24/30] s4:rpc_server/lsa: prepare dcesrv_lsa_LookupNames* for
 async processing

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 390 +++++++++++++++++++++++-------------
 1 file changed, 249 insertions(+), 141 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 487f6eb..a18f7b2 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -933,13 +933,32 @@ NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *m
 	return status;
 }
 
-static NTSTATUS dcesrv_lsa_LookupNames_common(struct dcesrv_call_state *dce_call,
-					      TALLOC_CTX *mem_ctx,
-					      struct lsa_policy_state *policy_state,
-					      struct lsa_LookupNames3 *r)
+struct dcesrv_lsa_LookupNames_base_state {
+	struct dcesrv_call_state *dce_call;
+
+	TALLOC_CTX *mem_ctx;
+
+	struct lsa_policy_state *policy_state;
+
+	struct lsa_LookupNames4 r;
+
+	struct {
+		struct lsa_LookupNames *l;
+		struct lsa_LookupNames2 *l2;
+		struct lsa_LookupNames3 *l3;
+		struct lsa_LookupNames4 *l4;
+	} _r;
+};
+
+
+static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_base_state *state)
 {
-	struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
-	struct lsa_RefDomainList *domains;
+	struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx;
+	struct dcesrv_call_state *dce_call = state->dce_call;
+	struct lsa_policy_state *policy_state = state->policy_state;
+	TALLOC_CTX *mem_ctx = state->mem_ctx;
+	struct lsa_LookupNames4 *r = &state->r;
+	struct lsa_RefDomainList *domains = NULL;
 	uint32_t i;
 
 	*r->out.domains = NULL;
@@ -1009,6 +1028,97 @@ static NTSTATUS dcesrv_lsa_LookupNames_common(struct dcesrv_call_state *dce_call
 	return NT_STATUS_OK;
 }
 
+static void dcesrv_lsa_LookupNames_base_map(
+	struct dcesrv_lsa_LookupNames_base_state *state)
+{
+	if (state->_r.l4 != NULL) {
+		struct lsa_LookupNames4 *r = state->_r.l4;
+
+		r->out.result = state->r.out.result;
+		return;
+	}
+
+	if (state->_r.l3 != NULL) {
+		struct lsa_LookupNames3 *r = state->_r.l3;
+
+		r->out.result = state->r.out.result;
+		return;
+	}
+
+	if (state->_r.l2 != NULL) {
+		struct lsa_LookupNames2 *r = state->_r.l2;
+		uint32_t i;
+
+		r->out.result = state->r.out.result;
+
+		SMB_ASSERT(state->r.out.sids->count <= r->in.num_names);
+		for (i = 0; i < state->r.out.sids->count; i++) {
+			const struct lsa_TranslatedSid3 *s3 =
+				&state->r.out.sids->sids[i];
+			struct lsa_TranslatedSid2 *s2 =
+				&r->out.sids->sids[i];
+
+			s2->sid_type = s3->sid_type;
+			if (s3->sid_type == SID_NAME_DOMAIN) {
+				s2->rid = UINT32_MAX;
+			} else if (s3->flags & 0x00000004) {
+				s2->rid = UINT32_MAX;
+			} else if (s3->sid == NULL) {
+				/*
+				 * MS-LSAT 3.1.4.7 - rid zero is considered
+				 * equivalent to sid NULL - so we should return
+				 * 0 rid for unmapped entries
+				 */
+				s2->rid = 0;
+			} else {
+				s2->rid = 0;
+				dom_sid_split_rid(NULL, s3->sid,
+						  NULL, &s2->rid);
+			}
+			s2->sid_index = s3->sid_index;
+			s2->unknown = s3->flags;
+		}
+		r->out.sids->count = state->r.out.sids->count;
+		return;
+	}
+
+	if (state->_r.l != NULL) {
+		struct lsa_LookupNames *r = state->_r.l;
+		uint32_t i;
+
+		r->out.result = state->r.out.result;
+
+		SMB_ASSERT(state->r.out.sids->count <= r->in.num_names);
+		for (i = 0; i < state->r.out.sids->count; i++) {
+			struct lsa_TranslatedSid3 *s3 =
+				&state->r.out.sids->sids[i];
+			struct lsa_TranslatedSid *s =
+				&r->out.sids->sids[i];
+
+			s->sid_type = s3->sid_type;
+			if (s3->sid_type == SID_NAME_DOMAIN) {
+				s->rid = UINT32_MAX;
+			} else if (s3->flags & 0x00000004) {
+				s->rid = UINT32_MAX;
+			} else if (s3->sid == NULL) {
+				/*
+				 * MS-LSAT 3.1.4.7 - rid zero is considered
+				 * equivalent to sid NULL - so we should return
+				 * 0 rid for unmapped entries
+				 */
+				s->rid = 0;
+			} else {
+				s->rid = 0;
+				dom_sid_split_rid(NULL, s3->sid,
+						  NULL, &s->rid);
+			}
+			s->sid_index = s3->sid_index;
+		}
+		r->out.sids->count = state->r.out.sids->count;
+		return;
+	}
+}
+
 /*
   lsa_LookupNames3
 */
@@ -1018,8 +1128,9 @@ NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call,
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_policy_state *policy_state;
-	struct dcesrv_handle *policy_handle;
+	struct dcesrv_lsa_LookupNames_base_state *state = NULL;
+	struct dcesrv_handle *policy_handle = NULL;
+	NTSTATUS status;
 
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
@@ -1027,12 +1138,44 @@ NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call,
 
 	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
 
-	policy_state = policy_handle->data;
+	*r->out.domains = NULL;
+	r->out.sids->count = 0;
+	r->out.sids->sids = NULL;
+	*r->out.count = 0;
+
+	state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->dce_call = dce_call;
+	state->mem_ctx = mem_ctx;
+
+	state->policy_state = policy_handle->data;
+
+	state->r.in.num_names = r->in.num_names;
+	state->r.in.names = r->in.names;
+	state->r.in.level = r->in.level;
+	state->r.in.lookup_options = r->in.lookup_options;
+	state->r.in.client_revision = r->in.client_revision;
+	state->r.in.sids = r->in.sids;
+	state->r.in.count = r->in.count;
+	state->r.out.domains = r->out.domains;
+	state->r.out.sids = r->out.sids;
+	state->r.out.count = r->out.count;
+
+	state->_r.l3 = r;
+
+	status = dcesrv_lsa_LookupNames_base_call(state);
 
-	return dcesrv_lsa_LookupNames_common(dce_call,
-					     mem_ctx,
-					     policy_state,
-					     r);
+	if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+		return status;
+	}
+
+	state->r.out.result = status;
+	dcesrv_lsa_LookupNames_base_map(state);
+	TALLOC_FREE(state);
+	return status;
 }
 
 /* 
@@ -1047,8 +1190,7 @@ NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
 	const struct dcesrv_auth *auth = &dce_call->conn->auth_state;
-	struct lsa_policy_state *policy_state;
-	struct lsa_LookupNames3 q;
+	struct dcesrv_lsa_LookupNames_base_state *state = NULL;
 	NTSTATUS status;
 
 	if (transport != NCACN_IP_TCP) {
@@ -1069,39 +1211,43 @@ NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX
 	r->out.sids->sids = NULL;
 	*r->out.count = 0;
 
-	status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx,
+	state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->dce_call = dce_call;
+	state->mem_ctx = mem_ctx;
+
+	status = dcesrv_lsa_get_policy_state(state->dce_call, state,
 					     0, /* we skip access checks */
-					     &policy_state);
+					     &state->policy_state);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
 
-	ZERO_STRUCT(q);
-
-	q.in.handle = NULL;
-	q.in.num_names = r->in.num_names;
-	q.in.names = r->in.names;
-	q.in.level = r->in.level;
-	q.in.sids = r->in.sids;
-	q.in.count = r->in.count;
-	q.in.lookup_options = r->in.lookup_options;
-	q.in.client_revision = r->in.client_revision;
-
-	q.out.count = r->out.count;
-	q.out.sids = r->out.sids;
-	q.out.domains = r->out.domains;
+	state->r.in.num_names = r->in.num_names;
+	state->r.in.names = r->in.names;
+	state->r.in.level = r->in.level;
+	state->r.in.lookup_options = r->in.lookup_options;
+	state->r.in.client_revision = r->in.client_revision;
+	state->r.in.sids = r->in.sids;
+	state->r.in.count = r->in.count;
+	state->r.out.domains = r->out.domains;
+	state->r.out.sids = r->out.sids;
+	state->r.out.count = r->out.count;
 
-	status = dcesrv_lsa_LookupNames_common(dce_call,
-					       mem_ctx,
-					       policy_state,
-					       &q);
+	state->_r.l4 = r;
 
-	talloc_free(policy_state);
+	status = dcesrv_lsa_LookupNames_base_call(state);
 
-	r->out.count = q.out.count;
-	r->out.sids = q.out.sids;
-	r->out.domains = q.out.domains;
+	if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+		return status;
+	}
 
+	state->r.out.result = status;
+	dcesrv_lsa_LookupNames_base_map(state);
+	TALLOC_FREE(state);
 	return status;
 }
 
@@ -1114,11 +1260,9 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_policy_state *policy_state = NULL;
+	struct dcesrv_lsa_LookupNames_base_state *state = NULL;
 	struct dcesrv_handle *policy_handle = NULL;
-	struct lsa_LookupNames3 r2;
 	NTSTATUS status;
-	uint32_t i;
 
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
@@ -1126,8 +1270,6 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 
 	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
 
-	policy_state = policy_handle->data;
-
 	*r->out.domains = NULL;
 	r->out.sids->count = 0;
 	r->out.sids->sids = NULL;
@@ -1140,59 +1282,44 @@ NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	ZERO_STRUCT(r2);
+	state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->dce_call = dce_call;
+	state->mem_ctx = mem_ctx;
+
+	state->policy_state = policy_handle->data;
 
-	r2.in.handle    = r->in.handle;
-	r2.in.num_names = r->in.num_names;
-	r2.in.names     = r->in.names;
-	r2.in.sids      = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
-	if (r2.in.sids == NULL) {
+	state->r.in.num_names = r->in.num_names;
+	state->r.in.names = r->in.names;
+	state->r.in.level = r->in.level;
+	state->r.in.lookup_options = r->in.lookup_options;
+	state->r.in.client_revision = r->in.client_revision;
+	state->r.in.sids = talloc_zero(state, struct lsa_TransSidArray3);
+	if (state->r.in.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	r2.in.level     = r->in.level;
-	r2.in.count     = r->in.count;
-	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
-	r2.in.client_revision = LSA_CLIENT_REVISION_1;
-	r2.out.count    = r->out.count;
-	r2.out.sids     = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
-	if (r2.out.sids == NULL) {
+	state->r.in.count = r->in.count;
+	state->r.out.domains = r->out.domains;
+	state->r.out.sids = talloc_zero(state, struct lsa_TransSidArray3);
+	if (state->r.out.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	r2.out.domains	= r->out.domains;
-
-	status = dcesrv_lsa_LookupNames_common(dce_call,
-					       mem_ctx,
-					       policy_state,
-					       &r2);
-
-	SMB_ASSERT(r2.out.sids->count <= r->in.num_names);
-	for (i=0;i<r2.out.sids->count;i++) {
-		struct lsa_TranslatedSid3 *s3 =
-			&r2.out.sids->sids[i];
-		struct lsa_TranslatedSid2 *s2 =
-			&r->out.sids->sids[i];
-
-		s2->sid_type = s3->sid_type;
-		if (s3->sid_type == SID_NAME_DOMAIN) {
-			s2->rid = UINT32_MAX;
-		} else if (s3->flags & 0x00000004) {
-			s2->rid = UINT32_MAX;
-		} else if (s3->sid == NULL) {
-			/*
-			 * MS-LSAT 3.1.4.7 - rid zero is considered
-			 * equivalent to sid NULL - so we should return
-			 * 0 rid for unmapped entries
-			 */
-			s2->rid = 0;
-		} else {
-			s2->rid = 0;
-			dom_sid_split_rid(NULL, s3->sid,
-					  NULL, &s2->rid);
-		}
-		s2->sid_index = s3->sid_index;
+	state->r.out.count = r->out.count;
+
+	state->_r.l2 = r;
+
+	status = dcesrv_lsa_LookupNames_base_call(state);
+
+	if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+		return status;
 	}
-	r->out.sids->count = r2.out.sids->count;
 
+	state->r.out.result = status;
+	dcesrv_lsa_LookupNames_base_map(state);
+	TALLOC_FREE(state);
 	return status;
 }
 
@@ -1204,11 +1331,9 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(dce_call->conn->endpoint->ep_description);
-	struct lsa_policy_state *policy_state = NULL;
+	struct dcesrv_lsa_LookupNames_base_state *state = NULL;
 	struct dcesrv_handle *policy_handle = NULL;
-	struct lsa_LookupNames3 r2;
 	NTSTATUS status;
-	uint32_t i;
 
 	if (transport != NCACN_NP && transport != NCALRPC) {
 		DCESRV_FAULT(DCERPC_FAULT_ACCESS_DENIED);
@@ -1216,8 +1341,6 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 
 	DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
 
-	policy_state = policy_handle->data;
-
 	*r->out.domains = NULL;
 	r->out.sids->count = 0;
 	r->out.sids->sids = NULL;
@@ -1230,59 +1353,44 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	ZERO_STRUCT(r2);
+	state = talloc_zero(mem_ctx, struct dcesrv_lsa_LookupNames_base_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->dce_call = dce_call;
+	state->mem_ctx = mem_ctx;
+
+	state->policy_state = policy_handle->data;
 
-	r2.in.handle    = r->in.handle;
-	r2.in.num_names = r->in.num_names;
-	r2.in.names     = r->in.names;
-	r2.in.sids      = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
-	if (r2.in.sids == NULL) {
+	state->r.in.num_names = r->in.num_names;
+	state->r.in.names = r->in.names;
+	state->r.in.level = r->in.level;
+	state->r.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
+	state->r.in.client_revision = LSA_CLIENT_REVISION_1;
+	state->r.in.sids = talloc_zero(state, struct lsa_TransSidArray3);
+	if (state->r.in.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	r2.in.level     = r->in.level;
-	r2.in.count     = r->in.count;
-	r2.in.lookup_options = LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES;
-	r2.in.client_revision = LSA_CLIENT_REVISION_1;
-	r2.out.count    = r->out.count;
-	r2.out.sids     = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
-	if (r2.out.sids == NULL) {
+	state->r.in.count = r->in.count;
+	state->r.out.domains = r->out.domains;
+	state->r.out.sids = talloc_zero(state, struct lsa_TransSidArray3);
+	if (state->r.out.sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	r2.out.domains	= r->out.domains;
-
-	status = dcesrv_lsa_LookupNames_common(dce_call,
-					       mem_ctx,
-					       policy_state,
-					       &r2);
-
-	SMB_ASSERT(r2.out.sids->count <= r->in.num_names);
-	for (i=0;i<r2.out.sids->count;i++) {
-		struct lsa_TranslatedSid3 *s3 =
-			&r2.out.sids->sids[i];
-		struct lsa_TranslatedSid *s =
-			&r->out.sids->sids[i];
-
-		s->sid_type = s3->sid_type;
-		if (s3->sid_type == SID_NAME_DOMAIN) {
-			s->rid = UINT32_MAX;
-		} else if (s3->flags & 0x00000004) {
-			s->rid = UINT32_MAX;
-		} else if (s3->sid == NULL) {
-			/*
-			 * MS-LSAT 3.1.4.7 - rid zero is considered
-			 * equivalent to sid NULL - so we should return
-			 * 0 rid for unmapped entries
-			 */
-			s->rid = 0;
-		} else {
-			s->rid = 0;
-			dom_sid_split_rid(NULL, s3->sid,
-					  NULL, &s->rid);
-		}
-		s->sid_index = s3->sid_index;
+	state->r.out.count = r->out.count;
+
+	state->_r.l = r;
+
+	status = dcesrv_lsa_LookupNames_base_call(state);
+
+	if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+		return status;
 	}
-	r->out.sids->count = r2.out.sids->count;
 
+	state->r.out.result = status;
+	dcesrv_lsa_LookupNames_base_map(state);
+	TALLOC_FREE(state);
 	return status;
 }
 
-- 
1.9.1


From 1db21cb95a3fae8984443c866ddd2fb213c5e910 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 16 Feb 2018 01:14:00 +0100
Subject: [PATCH 25/30] s4:dsdb: add dsdb_trust_domain_by_{sid,name}()

This gets the lsa_ForestTrustDomainInfo for the searched
domain as well as the lsa_TrustDomainInfoInfoEx for the
direct trust (which might be the same for external trust or
the forest root domain).

BUG 8:...

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

diff --git a/source4/dsdb/common/util_trusts.c b/source4/dsdb/common/util_trusts.c
index 1534829..7dcbea2 100644
--- a/source4/dsdb/common/util_trusts.c
+++ b/source4/dsdb/common/util_trusts.c
@@ -2822,6 +2822,9 @@ struct dsdb_trust_routing_domain {
 	struct dsdb_trust_routing_domain *prev, *next;
 
 	struct lsa_TrustDomainInfoInfoEx *tdo;
+
+	struct lsa_ForestTrustDomainInfo di;
+
 	struct lsa_ForestTrustInformation *fti;
 };
 
@@ -2881,6 +2884,10 @@ NTSTATUS dsdb_trust_routing_table_load(struct ldb_context *sam_ctx,
 		return status;
 	}
 
+	d->di.domain_sid = d->tdo->sid;
+	d->di.netbios_domain_name.string = d->tdo->netbios_name.string;
+	d->di.dns_domain_name.string = d->tdo->domain_name.string;
+
 	if (root_trust_tdo != NULL) {
 		root_direction_tdo = root_trust_tdo;
 	} else if (trust_parent_tdo != NULL) {
@@ -2923,6 +2930,10 @@ NTSTATUS dsdb_trust_routing_table_load(struct ldb_context *sam_ctx,
 			return status;
 		}
 
+		d->di.domain_sid = d->tdo->sid;
+		d->di.netbios_domain_name.string = d->tdo->netbios_name.string;
+		d->di.dns_domain_name.string = d->tdo->domain_name.string;
+
 		DLIST_ADD_END(table->domains, d);
 
 		if (d->tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
@@ -3197,3 +3208,214 @@ const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_routing_by_name(
 
 	return NULL;
 }
+
+const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_domain_by_sid(
+		const struct dsdb_trust_routing_table *table,
+		const struct dom_sid *sid,
+		const struct lsa_ForestTrustDomainInfo **pdi)
+{
+	const struct dsdb_trust_routing_domain *d = NULL;
+
+	if (pdi != NULL) {
+		*pdi = NULL;
+	}
+
+	if (sid == NULL) {
+		return NULL;
+	}
+
+	for (d = table->domains; d != NULL; d = d->next) {
+		bool transitive = false;
+		uint32_t i;
+
+		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 (d->tdo->trust_type != LSA_TRUST_TYPE_UPLEVEL) {
+			transitive = false;
+		}
+
+		if (!transitive || d->fti == NULL) {
+			bool match = false;
+
+			match = dom_sid_equal(d->di.domain_sid, sid);
+			if (match) {
+				/*
+				 * exact match, it's the domain itself.
+				 */
+				if (pdi != NULL) {
+					*pdi = &d->di;
+				}
+				return d->tdo;
+			}
+			continue;
+		}
+
+		for (i = 0; i < d->fti->count; i++ ) {
+			const struct lsa_ForestTrustRecord *f = d->fti->entries[i];
+			const struct lsa_ForestTrustDomainInfo *di = NULL;
+			const struct dom_sid *fti_sid = NULL;
+			bool match = false;
+
+			if (f == NULL) {
+				/* broken record */
+				continue;
+			}
+
+			if (f->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+				continue;
+			}
+
+			if (f->flags & LSA_SID_DISABLED_MASK) {
+				/*
+				 * any flag disables the entry.
+				 */
+				continue;
+			}
+
+			di = &f->forest_trust_data.domain_info;
+			fti_sid = di->domain_sid;
+			if (fti_sid == NULL) {
+				/* broken record */
+				continue;
+			}
+
+			match = dom_sid_equal(fti_sid, sid);
+			if (match) {
+				/*
+				 * exact match, it's a domain in the forest.
+				 */
+				if (pdi != NULL) {
+					*pdi = di;
+				}
+				return d->tdo;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+const struct lsa_TrustDomainInfoInfoEx *dsdb_trust_domain_by_name(
+		const struct dsdb_trust_routing_table *table,
+		const char *name,
+		const struct lsa_ForestTrustDomainInfo **pdi)
+{
+	const struct dsdb_trust_routing_domain *d = NULL;
+
+	if (pdi != NULL) {
+		*pdi = NULL;
+	}
+
+	if (name == NULL) {
+		return NULL;
+	}
+
+	for (d = table->domains; d != NULL; d = d->next) {
+		bool transitive = false;
+		uint32_t i;
+
+		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 (d->tdo->trust_type != LSA_TRUST_TYPE_UPLEVEL) {
+			transitive = false;
+		}
+
+		if (!transitive || d->fti == NULL) {
+			bool match = false;
+
+			match = strequal_m(d->di.netbios_domain_name.string,
+					   name);
+			if (match) {
+				/*
+				 * exact match for netbios name,
+				 * it's the domain itself.
+				 */
+				if (pdi != NULL) {
+					*pdi = &d->di;
+				}
+				return d->tdo;
+			}
+			match = strequal_m(d->di.dns_domain_name.string,
+					   name);
+			if (match) {
+				/*
+				 * exact match for dns name,
+				 * it's the domain itself.
+				 */
+				if (pdi != NULL) {
+					*pdi = &d->di;
+				}
+				return d->tdo;
+			}
+			continue;
+		}
+
+		for (i = 0; i < d->fti->count; i++ ) {
+			const struct lsa_ForestTrustRecord *f = d->fti->entries[i];
+			const struct lsa_ForestTrustDomainInfo *di = NULL;
+			bool match = false;
+
+			if (f == NULL) {
+				/* broken record */
+				continue;
+			}
+
+			if (f->type != LSA_FOREST_TRUST_DOMAIN_INFO) {
+				continue;
+			}
+			di = &f->forest_trust_data.domain_info;
+
+			if (!(f->flags & LSA_NB_DISABLED_MASK)) {
+				match = strequal_m(di->netbios_domain_name.string,
+						   name);
+				if (match) {
+					/*
+					 * exact match for netbios name,
+					 * it's a domain in the forest.
+					 */
+					if (pdi != NULL) {
+						*pdi = di;
+					}
+					return d->tdo;
+				}
+			}
+
+			if (!(f->flags & LSA_TLN_DISABLED_MASK)) {
+				match = strequal_m(di->dns_domain_name.string,
+						   name);
+				if (match) {
+					/*
+					 * exact match for dns name,
+					 * it's a domain in the forest.
+					 */
+					if (pdi != NULL) {
+						*pdi = di;
+					}
+					return d->tdo;
+				}
+			}
+		}
+	}
+
+	return NULL;
+}
-- 
1.9.1


From 6dfb2f3ea448466e6c84cf836f06c10ddb76b843 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 20 Mar 2017 12:55:44 +0100
Subject: [PATCH 26/30] libcli/security: add
 dom_sid_lookup_predefined_{sid,name}()

This basically implements [MS-LSAT] 3.1.1.1.1 Predefined Translation Database
and Corresponding View.

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 libcli/security/dom_sid.h  |  13 ++
 libcli/security/util_sid.c | 499 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 512 insertions(+)

diff --git a/libcli/security/dom_sid.h b/libcli/security/dom_sid.h
index bdcec94..6c3225e 100644
--- a/libcli/security/dom_sid.h
+++ b/libcli/security/dom_sid.h
@@ -62,6 +62,19 @@ extern const struct dom_sid global_sid_Unix_NFS_Groups;
 extern const struct dom_sid global_sid_Unix_NFS_Mode;
 extern const struct dom_sid global_sid_Unix_NFS_Other;
 
+enum lsa_SidType;
+
+NTSTATUS dom_sid_lookup_predefined_name(const char *name,
+					const struct dom_sid **sid,
+					enum lsa_SidType *type,
+					const struct dom_sid **authority_sid,
+					const char **authority_name);
+NTSTATUS dom_sid_lookup_predefined_sid(const struct dom_sid *sid,
+				       const char **name,
+				       enum lsa_SidType *type,
+				       const struct dom_sid **authority_sid,
+				       const char **authority_name);
+
 int dom_sid_compare_auth(const struct dom_sid *sid1,
 			 const struct dom_sid *sid2);
 int dom_sid_compare(const struct dom_sid *sid1, const struct dom_sid *sid2);
diff --git a/libcli/security/util_sid.c b/libcli/security/util_sid.c
index e84cfb4..4e4a8fa 100644
--- a/libcli/security/util_sid.c
+++ b/libcli/security/util_sid.c
@@ -434,3 +434,502 @@ bool is_null_sid(const struct dom_sid *sid)
 	const struct dom_sid null_sid = {0};
 	return dom_sid_equal(sid, &null_sid);
 }
+
+/*
+ * See [MS-LSAT] 3.1.1.1.1 Predefined Translation Database and Corresponding View
+ */
+struct predefined_name_mapping {
+	const char *name;
+	enum lsa_SidType type;
+	struct dom_sid sid;
+};
+
+struct predefined_domain_mapping {
+	const char *domain;
+	struct dom_sid sid;
+	size_t num_names;
+	const struct predefined_name_mapping *names;
+};
+
+/* S-1-${AUTHORITY} */
+#define _SID0(authority) \
+	{ 1, 0, {0,0,0,0,0,authority}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+/* S-1-${AUTHORITY}-${SUB1} */
+#define _SID1(authority,sub1) \
+	{ 1, 1, {0,0,0,0,0,authority}, {sub1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+/* S-1-${AUTHORITY}-${SUB1}-${SUB2} */
+#define _SID2(authority,sub1,sub2) \
+	{ 1, 2, {0,0,0,0,0,authority}, {sub1,sub2,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+
+/*
+ * S-1-0
+ */
+static const struct predefined_name_mapping predefined_names_S_1_0[] = {
+	{
+		.name = "NULL SID",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(0, 0), /* S-1-0-0 */
+	},
+};
+
+/*
+ * S-1-1
+ */
+static const struct predefined_name_mapping predefined_names_S_1_1[] = {
+	{
+		.name = "Everyone",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(1, 0), /* S-1-1-0 */
+	},
+};
+
+/*
+ * S-1-2
+ */
+static const struct predefined_name_mapping predefined_names_S_1_2[] = {
+	{
+		.name = "LOCAL",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(2, 0), /* S-1-2-0 */
+	},
+};
+
+/*
+ * S-1-3
+ */
+static const struct predefined_name_mapping predefined_names_S_1_3[] = {
+	{
+		.name = "CREATOR OWNER",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(3, 0), /* S-1-3-0 */
+	},
+	{
+		.name = "CREATOR GROUP",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(3, 1), /* S-1-3-1 */
+	},
+	{
+		.name = "CREATOR OWNER SERVER",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(3, 0), /* S-1-3-2 */
+	},
+	{
+		.name = "CREATOR GROUP SERVER",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(3, 1), /* S-1-3-3 */
+	},
+	{
+		.name = "OWNER RIGHTS",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(3, 4), /* S-1-3-4 */
+	},
+};
+
+/*
+ * S-1-5 only 'NT Pseudo Domain'
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5p[] = {
+	{
+		.name = "NT Pseudo Domain",
+		.type = SID_NAME_DOMAIN,
+		.sid = _SID0(5), /* S-1-5 */
+	},
+};
+
+/*
+ * S-1-5 'NT AUTHORITY'
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5a[] = {
+	{
+		.name = "DIALUP",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 1), /* S-1-5-1 */
+	},
+	{
+		.name = "NETWORK",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 2), /* S-1-5-2 */
+	},
+	{
+		.name = "BATCH",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 3), /* S-1-5-3 */
+	},
+	{
+		.name = "INTERACTIVE",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 4), /* S-1-5-4 */
+	},
+	{
+		.name = "SERVICE",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 6), /* S-1-5-6 */
+	},
+	{
+		.name = "ANONYMOUS LOGON",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 7), /* S-1-5-7 */
+	},
+	{
+		.name = "PROXY",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 8), /* S-1-5-8 */
+	},
+	{
+		.name = "ENTERPRISE DOMAIN CONTROLLERS",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 9), /* S-1-5-9 */
+	},
+	{
+		.name = "SELF",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 10), /* S-1-5-10 */
+	},
+	{
+		.name = "Authenticated Users",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 11), /* S-1-5-11 */
+	},
+	{
+		.name = "RESTRICTED",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 12), /* S-1-5-12 */
+	},
+	{
+		.name = "TERMINAL SERVER USER",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 13), /* S-1-5-13 */
+	},
+	{
+		.name = "REMOTE INTERACTIVE LOGON",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 14), /* S-1-5-14 */
+	},
+	{
+		.name = "This Organization",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 15), /* S-1-5-15 */
+	},
+	{
+		.name = "IUSR",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 17), /* S-1-5-17 */
+	},
+	{
+		.name = "SYSTEM",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 18), /* S-1-5-18 */
+	},
+	{
+		.name = "LOCAL SERVICE",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 19), /* S-1-5-19 */
+	},
+	{
+		.name = "NETWORK SERVICE",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 20), /* S-1-5-20 */
+	},
+	{
+		.name = "WRITE RESTRICTED",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 33), /* S-1-5-33 */
+	},
+	{
+		.name = "Other Organization",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID1(5, 1000), /* S-1-5-1000 */
+	},
+};
+
+/*
+ * S-1-5-32
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5_32[] = {
+	{
+		.name = "BUILTIN",
+		.type = SID_NAME_DOMAIN,
+		.sid = _SID1(5, 32), /* S-1-5-32 */
+	},
+};
+
+/*
+ * S-1-5-64
+ */
+static const struct predefined_name_mapping predefined_names_S_1_5_64[] = {
+	{
+		.name = "NTLM Authentication",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID2(5, 64, 10), /* S-1-5-64-10 */
+	},
+	{
+		.name = "SChannel Authentication",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID2(5, 64, 14), /* S-1-5-64-14 */
+	},
+	{
+		.name = "Digest Authentication",
+		.type = SID_NAME_WKN_GRP,
+		.sid = _SID2(5, 64, 21), /* S-1-5-64-21 */
+	},
+};
+
+/*
+ * S-1-7
+ */
+static const struct predefined_name_mapping predefined_names_S_1_7[] = {
+	{
+		.name = "Internet$",
+		.type = SID_NAME_DOMAIN,
+		.sid = _SID0(7), /* S-1-7 */
+	},
+};
+
+/*
+ * S-1-16
+ */
+static const struct predefined_name_mapping predefined_names_S_1_16[] = {
+	{
+		.name = "Mandatory Label",
+		.type = SID_NAME_DOMAIN,
+		.sid = _SID0(16), /* S-1-16 */
+	},
+	{
+		.name = "Untrusted Mandatory Level",
+		.type = SID_NAME_LABEL,
+		.sid = _SID1(16, 0), /* S-1-16-0 */
+	},
+	{
+		.name = "Low Mandatory Level",
+		.type = SID_NAME_LABEL,
+		.sid = _SID1(16, 4096), /* S-1-16-4096 */
+	},
+	{
+		.name = "Medium Mandatory Level",
+		.type = SID_NAME_LABEL,
+		.sid = _SID1(16, 8192), /* S-1-16-8192 */
+	},
+	{
+		.name = "High Mandatory Level",
+		.type = SID_NAME_LABEL,
+		.sid = _SID1(16, 12288), /* S-1-16-12288 */
+	},
+	{
+		.name = "System Mandatory Level",
+		.type = SID_NAME_LABEL,
+		.sid = _SID1(16, 16384), /* S-1-16-16384 */
+	},
+	{
+		.name = "Protected Process Mandatory Level",
+		.type = SID_NAME_LABEL,
+		.sid = _SID1(16, 20480), /* S-1-16-20480 */
+	},
+};
+
+static const struct predefined_domain_mapping predefined_domains[] = {
+	{
+		.domain = "",
+		.sid = _SID0(0), /* S-1-0 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_0),
+		.names = predefined_names_S_1_0,
+	},
+	{
+		.domain = "",
+		.sid = _SID0(1), /* S-1-1 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_1),
+		.names = predefined_names_S_1_1,
+	},
+	{
+		.domain = "",
+		.sid = _SID0(2), /* S-1-2 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_2),
+		.names = predefined_names_S_1_2,
+	},
+	{
+		.domain = "",
+		.sid = _SID0(3), /* S-1-3 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_3),
+		.names = predefined_names_S_1_3,
+	},
+	{
+		.domain = "",
+		.sid = _SID0(3), /* S-1-3 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_3),
+		.names = predefined_names_S_1_3,
+	},
+	/*
+	 * S-1-5 is split here
+	 *
+	 * 'NT Pseudo Domain' has precedence before 'NT AUTHORITY'.
+	 *
+	 * In a LookupSids with multiple sids e.g. S-1-5 and S-1-5-7
+	 * the domain section (struct lsa_DomainInfo) gets
+	 * 'NT Pseudo Domain' with S-1-5. If asked in reversed order
+	 * S-1-5-7 and then S-1-5, you get struct lsa_DomainInfo
+	 * with 'NT AUTHORITY' and S-1-5.
+	 */
+	{
+		.domain = "NT Pseudo Domain",
+		.sid = _SID0(5), /* S-1-5 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_5p),
+		.names = predefined_names_S_1_5p,
+	},
+	{
+		.domain = "NT AUTHORITY",
+		.sid = _SID0(5), /* S-1-5 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_5a),
+		.names = predefined_names_S_1_5a,
+	},
+	{
+		.domain = "BUILTIN",
+		.sid = _SID1(5, 32), /* S-1-5-32 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_5_32),
+		.names = predefined_names_S_1_5_32,
+	},
+	/*
+	 * 'NT AUTHORITY' again with S-1-5-64 this time
+	 */
+	{
+		.domain = "NT AUTHORITY",
+		.sid = _SID1(5, 64), /* S-1-5-64 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_5_64),
+		.names = predefined_names_S_1_5_64,
+	},
+	{
+		.domain = "Internet$",
+		.sid = _SID0(7), /* S-1-7 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_7),
+		.names = predefined_names_S_1_7,
+	},
+	{
+		.domain = "Mandatory Label",
+		.sid = _SID0(16), /* S-1-16 */
+		.num_names = ARRAY_SIZE(predefined_names_S_1_16),
+		.names = predefined_names_S_1_16,
+	},
+};
+
+NTSTATUS dom_sid_lookup_predefined_name(const char *name,
+					const struct dom_sid **sid,
+					enum lsa_SidType *type,
+					const struct dom_sid **authority_sid,
+					const char **authority_name)
+{
+	size_t di;
+	const char *domain = "";
+	size_t domain_len = 0;
+	const char *p;
+	bool match;
+
+	*sid = NULL;
+	*type = SID_NAME_UNKNOWN;
+	*authority_sid = NULL;
+	*authority_name = NULL;
+
+	if (name == NULL) {
+		name = "";
+	}
+
+	p = strchr(name, '\\');
+	if (p != NULL) {
+		domain = name;
+		domain_len = PTR_DIFF(p, domain);
+		name = p + 1;
+	}
+
+	match = strequal(name, "");
+	if (match) {
+		/*
+		 * Strange, but that's what W2012R2 does.
+		 */
+		name = "BUILTIN";
+	}
+
+	for (di = 0; di < ARRAY_SIZE(predefined_domains); di++) {
+		const struct predefined_domain_mapping *d =
+			&predefined_domains[di];
+		size_t ni;
+
+		if (domain_len != 0) {
+			int cmp;
+
+			cmp = strncasecmp(d->domain, domain, domain_len);
+			if (cmp != 0) {
+				continue;
+			}
+		}
+
+		for (ni = 0; ni < d->num_names; ni++) {
+			const struct predefined_name_mapping *n =
+				&d->names[ni];
+
+			match = strequal(n->name, name);
+			if (!match) {
+				continue;
+			}
+
+			*sid = &n->sid;
+			*type = n->type;
+			*authority_sid = &d->sid;
+			*authority_name = d->domain;
+			return NT_STATUS_OK;
+		}
+	}
+
+	return NT_STATUS_NONE_MAPPED;
+}
+
+NTSTATUS dom_sid_lookup_predefined_sid(const struct dom_sid *sid,
+				       const char **name,
+				       enum lsa_SidType *type,
+				       const struct dom_sid **authority_sid,
+				       const char **authority_name)
+{
+	size_t di;
+	bool match_domain = false;
+
+	*name = NULL;
+	*type = SID_NAME_UNKNOWN;
+	*authority_sid = NULL;
+	*authority_name = NULL;
+
+	if (sid == NULL) {
+		return NT_STATUS_INVALID_SID;
+	}
+
+	for (di = 0; di < ARRAY_SIZE(predefined_domains); di++) {
+		const struct predefined_domain_mapping *d =
+			&predefined_domains[di];
+		size_t ni;
+		int cmp;
+
+		cmp = dom_sid_compare_auth(&d->sid, sid);
+		if (cmp != 0) {
+			continue;
+		}
+
+		match_domain = true;
+
+		for (ni = 0; ni < d->num_names; ni++) {
+			const struct predefined_name_mapping *n =
+				&d->names[ni];
+
+			cmp = dom_sid_compare(&n->sid, sid);
+			if (cmp != 0) {
+				continue;
+			}
+
+			*name = n->name;
+			*type = n->type;
+			*authority_sid = &d->sid;
+			*authority_name = d->domain;
+			return NT_STATUS_OK;
+		}
+	}
+
+	if (!match_domain) {
+		return NT_STATUS_INVALID_SID;
+	}
+
+	return NT_STATUS_NONE_MAPPED;
+}
-- 
1.9.1


From 58ac12197ddba0d15aa742dcceed97ceb2ba1c57 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 15 Feb 2018 10:30:28 +0100
Subject: [PATCH 27/30] test_trust_ntlm.sh: add lookup name tests

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail.d/s3-lsa-server    |  1 +
 selftest/knownfail.d/s4-lsa-server    |  2 +
 testprogs/blackbox/test_trust_ntlm.sh | 77 +++++++++++++++++++++++++++--------
 3 files changed, 62 insertions(+), 18 deletions(-)
 create mode 100644 selftest/knownfail.d/s3-lsa-server
 create mode 100644 selftest/knownfail.d/s4-lsa-server

diff --git a/selftest/knownfail.d/s3-lsa-server b/selftest/knownfail.d/s3-lsa-server
new file mode 100644
index 0000000..de1244f
--- /dev/null
+++ b/selftest/knownfail.d/s3-lsa-server
@@ -0,0 +1 @@
+^samba4.blackbox.trust_ntlm.Test08.rpcclient.lookupnames.with.ADDOM.SAMBA.EXAMPLE.COM\(ad_member:local\)
diff --git a/selftest/knownfail.d/s4-lsa-server b/selftest/knownfail.d/s4-lsa-server
new file mode 100644
index 0000000..86365e2
--- /dev/null
+++ b/selftest/knownfail.d/s4-lsa-server
@@ -0,0 +1,2 @@
+^samba4.blackbox.trust_ntlm.Test07.rpcclient.lookupnames.with.ADDOMAIN.*fl20
+^samba4.blackbox.trust_ntlm.Test08.rpcclient.lookupnames.with.ADDOM.SAMBA.EXAMPLE.COM.*fl20
diff --git a/testprogs/blackbox/test_trust_ntlm.sh b/testprogs/blackbox/test_trust_ntlm.sh
index 44946bb..101303e 100755
--- a/testprogs/blackbox/test_trust_ntlm.sh
+++ b/testprogs/blackbox/test_trust_ntlm.sh
@@ -36,19 +36,35 @@ unc="//$SERVER/tmp"
 . `dirname $0`/subunit.sh
 . `dirname $0`/common_test_fns.inc
 
-CREDS="$DOMAIN\\$USERNAME%$PASSWORD"
-WBCREDS="$DOMAIN/$USERNAME%$PASSWORD"
+DNAME="$DOMAIN"
+NAME="$DNAME\\$USERNAME"
+WBNAME="$DNAME/$USERNAME"
+CREDS="$NAME%$PASSWORD"
+WBCREDS="$WBNAME%$PASSWORD"
 EXPCREDS="Account Name: $USERNAME, Authority Name: $DOMAIN"
+EXPSID="(User: 1)"
+EXPDSID="(Domain: 3)"
 test_rpcclient_grep "Test01 rpcclient getusername with $CREDS" getusername "$SERVER" "$EXPCREDS" -U$CREDS || failed=`expr $failed + 1`
 test_smbclient "Test01 smbclient with $CREDS" 'ls' "$unc" -U$CREDS || failed=`expr $failed + 1`
 testit "Test01 wbinfo -a with $WBCREDS" $VALGRIND $wbinfo -a $WBCREDS || failed=`expr $failed + 1`
-
-CREDS="$REALM\\$USERNAME%$PASSWORD"
-WBCREDS="$REALM/$USERNAME%$PASSWORD"
+test_rpcclient_grep "Test01 rpcclient lookupnames with $NAME" "lookupnames_level 1 '$NAME'" "$SERVER" "$EXPSID" -U$CREDS || failed=`expr $failed + 1`
+testit "Test01 wbinfo -n with $WBNAME" $VALGRIND $wbinfo -n "$WBNAME" || failed=`expr $failed + 1`
+test_rpcclient_grep "Test01 rpcclient lookupnames with $DNAME" "lookupnames_level 1 '$DNAME'" "$SERVER" "$EXPDSID" -U$CREDS || failed=`expr $failed + 1`
+
+DNAME="$REALM"
+NAME="$DNAME\\$USERNAME"
+WBNAME="$DNAME/$USERNAME"
+CREDS="$NAME%$PASSWORD"
+WBCREDS="$WBNAME%$PASSWORD"
 EXPCREDS="Account Name: $USERNAME, Authority Name: $DOMAIN"
+EXPSID="(User: 1)"
+EXPDSID="(Domain: 3)"
 test_rpcclient_grep "Test02 rpcclient getusername with $CREDS" getusername "$SERVER" "$EXPCREDS" -U$CREDS || failed=`expr $failed + 1`
 test_smbclient "Test02 smbclient with $CREDS" 'ls' "$unc" -U$CREDS || failed=`expr $failed + 1`
 testit "Test02 wbinfo -a with $WBCREDS" $VALGRIND $wbinfo -a $WBCREDS || failed=`expr $failed + 1`
+test_rpcclient_grep "Test02 rpcclient lookupnames with $NAME" "lookupnames_level 1 '$NAME'" "$SERVER" "$EXPSID" -U$CREDS || failed=`expr $failed + 1`
+testit "Test02 wbinfo -n with $WBNAME" $VALGRIND $wbinfo -n "$WBNAME" || failed=`expr $failed + 1`
+test_rpcclient_grep "Test02 rpcclient lookupnames with $DNAME" "lookupnames_level 1 '$DNAME'" "$SERVER" "$EXPDSID" -U$CREDS || failed=`expr $failed + 1`
 
 CREDS="$USERNAME@$DOMAIN%$PASSWORD"
 WBCREDS="$USERNAME@$DOMAIN%$PASSWORD"
@@ -86,12 +102,20 @@ else
 	#testit "Test04 wbinfo -a with $WBCREDS" $VALGRIND $wbinfo -a $WBCREDS || failed=`expr $failed + 1`
 fi
 
-CREDS="UNKNOWNDOMAIN\\$USERNAME%$PASSWORD"
-WBCREDS="UNKNOWNDOMAIN/$USERNAME%$PASSWORD"
+DNAME="UNKNOWNDOMAIN"
+NAME="$DNAME\\$USERNAME"
+WBNAME="$DNAME/$USERNAME"
+CREDS="$NAME%$PASSWORD"
+WBCREDS="$WBNAME%$PASSWORD"
 EXPCREDS="Account Name: $USERNAME, Authority Name: $DOMAIN"
+EXPSID="NT_STATUS_NONE_MAPPED"
+EXPDSID="NT_STATUS_NONE_MAPPED"
 test_rpcclient_grep "Test05 rpcclient getusername with $CREDS" getusername "$SERVER" "$EXPCREDS" -U$CREDS || failed=`expr $failed + 1`
 test_smbclient "Test05 smbclient with $CREDS" 'ls' "$unc" -U$CREDS || failed=`expr $failed + 1`
 testit_expect_failure "Fail05 wbinfo -a with $WBCREDS" $VALGRIND $wbinfo -a $WBCREDS || failed=`expr $failed + 1`
+test_rpcclient_expect_failure_grep "Test05 rpcclient lookupnames with $NAME" "lookupnames_level 1 '$NAME'" "$SERVER" "$EXPSID" -U$CREDS || failed=`expr $failed + 1`
+testit_expect_failure "Test05 wbinfo -n with $WBNAME" $VALGRIND $wbinfo -n "$WBNAME" || failed=`expr $failed + 1`
+test_rpcclient_expect_failure_grep "Test05 rpcclient lookupnames with $DNAME" "lookupnames_level 1 '$DNAME'" "$SERVER" "$EXPDSID" -U$CREDS || failed=`expr $failed + 1`
 
 CREDS="$TRUST_DOMAIN\\$USERNAME%$PASSWORD"
 WBCREDS="$TRUST_DOMAIN/$USERNAME%$PASSWORD"
@@ -100,19 +124,35 @@ test_rpcclient_expect_failure_grep "Fail06 rpcclient getusername with $CREDS" ge
 test_smbclient_expect_failure "Fail06 smbclient with $CREDS" 'ls' "$unc" -U$CREDS && failed=`expr $failed + 1`
 testit_expect_failure "Fail06 wbinfo -a with $WBCREDS" $VALGRIND $wbinfo -a $WBCREDS && failed=`expr $failed + 1`
 
-CREDS="$TRUST_DOMAIN\\$TRUST_USERNAME%$TRUST_PASSWORD"
-WBCREDS="$TRUST_DOMAIN/$TRUST_USERNAME%$TRUST_PASSWORD"
+DNAME="$TRUST_DOMAIN"
+NAME="$DNAME\\$TRUST_USERNAME"
+WBNAME="$DNAME/$TRUST_USERNAME"
+CREDS="$NAME%$TRUST_PASSWORD"
+WBCREDS="$WBNAME%$TRUST_PASSWORD"
 EXPCREDS="Account Name: $TRUST_USERNAME, Authority Name: $TRUST_DOMAIN"
+EXPSID="(User: 1)"
+EXPDSID="(Domain: 3)"
 test_rpcclient_grep "Test07 rpcclient getusername with $CREDS" getusername "$SERVER" "$EXPCREDS" -U$CREDS || failed=`expr $failed + 1`
 test_smbclient "Test07 smbclient with $CREDS" 'ls' "$unc" -U$CREDS || failed=`expr $failed + 1`
 testit "Test07 wbinfo -a with $WBCREDS" $VALGRIND $wbinfo -a $WBCREDS || failed=`expr $failed + 1`
-
-CREDS="$TRUST_REALM\\$TRUST_USERNAME%$TRUST_PASSWORD"
-WBCREDS="$TRUST_REALM/$TRUST_USERNAME%$TRUST_PASSWORD"
+test_rpcclient_grep "Test07 rpcclient lookupnames with $NAME" "lookupnames_level 1 '$NAME'" "$SERVER" "$EXPSID" -U$CREDS || failed=`expr $failed + 1`
+testit "Test07 wbinfo -n with $WBNAME" $VALGRIND $wbinfo -n "$WBNAME" || failed=`expr $failed + 1`
+test_rpcclient_grep "Test07 rpcclient lookupnames with $DNAME" "lookupnames_level 1 '$DNAME'" "$SERVER" "$EXPDSID" -U$CREDS || failed=`expr $failed + 1`
+
+DNAME="$TRUST_REALM"
+NAME="$DNAME\\$TRUST_USERNAME"
+WBNAME="$DNAME/$TRUST_USERNAME"
+CREDS="$NAME%$TRUST_PASSWORD"
+WBCREDS="$WBNAME%$TRUST_PASSWORD"
 EXPCREDS="Account Name: $TRUST_USERNAME, Authority Name: $TRUST_DOMAIN"
+EXPSID="(User: 1)"
+EXPDSID="(Domain: 3)"
 test_rpcclient_grep "Test08 rpcclient getusername with $CREDS" getusername "$SERVER" "$EXPCREDS" -U$CREDS || failed=`expr $failed + 1`
 test_smbclient "Test08 smbclient with $CREDS" 'ls' "$unc" -U$CREDS || failed=`expr $failed + 1`
 testit "Test08 wbinfo -a with $WBCREDS" $VALGRIND $wbinfo -a $WBCREDS || failed=`expr $failed + 1`
+test_rpcclient_grep "Test08 rpcclient lookupnames with $NAME" "lookupnames_level 1 '$NAME'" "$SERVER" "$EXPSID" -U$CREDS || failed=`expr $failed + 1`
+testit "Test08 wbinfo -n with $WBNAME" $VALGRIND $wbinfo -n "$WBNAME" || failed=`expr $failed + 1`
+test_rpcclient_grep "Test08 rpcclient lookupnames with $DNAME" "lookupnames_level 1 '$DNAME'" "$SERVER" "$EXPDSID" -U$CREDS || failed=`expr $failed + 1`
 
 CREDS="$TRUST_USERNAME@$TRUST_DOMAIN%$TRUST_PASSWORD"
 WBCREDS="$TRUST_USERNAME@$TRUST_DOMAIN%$TRUST_PASSWORD"
@@ -154,11 +194,12 @@ fi
 
 lowerrealm=$(echo $TRUST_REALM | tr '[A-Z]' '[a-z]')
 
-if test x$TYPE = x"forest"; then
-
-fi
-
-if test x$UNTRUSTED = x"yes"; then
+#if test x$TYPE = x"forest"; then
+#
+#fi
+#
+#if test x$UNTRUSTED = x"yes"; then
+#
+#fi
 
-fi
 exit $failed
-- 
1.9.1


From 6bfdcdbfe5d2a0ec4c19634a349e28b8c75a5fdf Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 23 Jan 2018 00:52:50 +0100
Subject: [PATCH 28/30] s4:rpc_server/lsa: rewrite lookup sids/names code to
 honor the given lookup level

[MS-LSAT] 2.2.16 LSAP_LOOKUP_LEVEL defines the which views each level should
consult.

Up to now we support some wellknown sids, the builtin domain and our
account domain, but all levels query all views.

This commit implements 3 views (predefined, builtin, account domain)
+ a dummy winbind view (which will later be used to implement the
gc, forest and trust views)..

Depending on the level we select the required views.

This might not be perfect in all details, but it's enough
to pass all existing tests, which already revealed bugs
during the development of this patch.

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/lsa/lsa_lookup.c | 1412 +++++++++++++++++++++--------------
 1 file changed, 855 insertions(+), 557 deletions(-)

diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index a18f7b2..2e0b8af 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -21,473 +21,160 @@
 */
 
 #include "rpc_server/lsa/lsa.h"
+#include "libds/common/roles.h"
 #include "libds/common/flag_mapping.h"
 
-static const struct {
-	const char *domain;
+struct dcesrv_lsa_TranslatedItem {
+	enum lsa_SidType type;
+	const struct dom_sid *sid;
 	const char *name;
-	const char *sid;
-	enum lsa_SidType rtype;
-} well_known[] = {
-	{
-		.name = "EVERYONE",
-		.sid = SID_WORLD,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.name = "CREATOR OWNER",
-		.sid = SID_CREATOR_OWNER,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.name = "CREATOR GROUP",
-		.sid = SID_CREATOR_GROUP,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.name = "Owner Rights",
-		.sid = SID_OWNER_RIGHTS,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Dialup",
-		.sid = SID_NT_DIALUP,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Network",
-		.sid = SID_NT_NETWORK,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Batch",
-		.sid = SID_NT_BATCH,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Interactive",
-		.sid = SID_NT_INTERACTIVE,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Service",
-		.sid = SID_NT_SERVICE,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "ANONYMOUS LOGON",
-		.sid = SID_NT_ANONYMOUS,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Proxy",
-		.sid = SID_NT_PROXY,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "ServerLogon",
-		.sid = SID_NT_ENTERPRISE_DCS,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Self",
-		.sid = SID_NT_SELF,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Authenticated Users",
-		.sid = SID_NT_AUTHENTICATED_USERS,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Restricted",
-		.sid = SID_NT_RESTRICTED,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Terminal Server User",
-		.sid = SID_NT_TERMINAL_SERVER_USERS,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Remote Interactive Logon",
-		.sid = SID_NT_REMOTE_INTERACTIVE,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "This Organization",
-		.sid = SID_NT_THIS_ORGANISATION,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "SYSTEM",
-		.sid = SID_NT_SYSTEM,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Local Service",
-		.sid = SID_NT_LOCAL_SERVICE,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Network Service",
-		.sid = SID_NT_NETWORK_SERVICE,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Digest Authentication",
-		.sid = SID_NT_DIGEST_AUTHENTICATION,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Enterprise Domain Controllers",
-		.sid = SID_NT_ENTERPRISE_DCS,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "NTLM Authentication",
-		.sid = SID_NT_NTLM_AUTHENTICATION,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "Other Organization",
-		.sid = SID_NT_OTHER_ORGANISATION,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "SChannel Authentication",
-		.sid = SID_NT_SCHANNEL_AUTHENTICATION,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.domain = "NT AUTHORITY",
-		.name = "IUSR",
-		.sid = SID_NT_IUSR,
-		.rtype = SID_NAME_WKN_GRP,
-	},
-	{
-		.sid = NULL,
-	}
+	const char *authority_name;
+	const struct dom_sid *authority_sid;
+	uint32_t flags;
+	bool done;
+	struct {
+		const char *domain; /* only $DOMAIN\ */
+		const char *namespace; /* $NAMESPACE\ or @$NAMESPACE */
+		const char *principal; /* \$PRINCIPAL or $PRIN at IPAL */
+		const char *sid; /* "S-1-5-21-9000-8000-7000-6000" */
+		const char *rid; /* "00001770" */
+	} hints;
 };
 
-static NTSTATUS lookup_well_known_names(TALLOC_CTX *mem_ctx, const char *domain,
-					const char *name, const char **authority_name, 
-					struct dom_sid **sid, enum lsa_SidType *rtype)
-{
-	unsigned int i;
-	for (i=0; well_known[i].sid; i++) {
-		if (domain) {
-			if (strcasecmp_m(domain, well_known[i].domain) == 0
-			    && strcasecmp_m(name, well_known[i].name) == 0) {
-				*authority_name = well_known[i].domain;
-				*sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
-				*rtype = well_known[i].rtype;
-				return NT_STATUS_OK;
-			}
-		} else {
-			if (strcasecmp_m(name, well_known[i].name) == 0) {
-				*authority_name = well_known[i].domain;
-				*sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
-				*rtype = well_known[i].rtype;
-				return NT_STATUS_OK;
-			}
-		}
-	}
-	return NT_STATUS_NOT_FOUND;	
-}
+struct dcesrv_lsa_LookupSids_base_state;
+struct dcesrv_lsa_LookupNames_base_state;
 
-static NTSTATUS lookup_well_known_sids(TALLOC_CTX *mem_ctx, 
-				       const char *sid_str, const char **authority_name, 
-				       const char **name, enum lsa_SidType *rtype) 
-{
-	unsigned int i;
-	for (i=0; well_known[i].sid; i++) {
-		if (strcasecmp_m(sid_str, well_known[i].sid) == 0) {
-			*authority_name = well_known[i].domain;
-			*name = well_known[i].name;
-			*rtype = well_known[i].rtype;
-			return NT_STATUS_OK;
-		}
-	}
-	return NT_STATUS_NOT_FOUND;	
-}
+struct dcesrv_lsa_Lookup_view {
+	const char *name;
+	NTSTATUS (*lookup_sid)(struct dcesrv_lsa_LookupSids_base_state *state,
+			       struct dcesrv_lsa_TranslatedItem *item);
+	NTSTATUS (*lookup_name)(struct dcesrv_lsa_LookupNames_base_state *state,
+				struct dcesrv_lsa_TranslatedItem *item);
+};
+
+struct dcesrv_lsa_Lookup_view_table {
+	const char *name;
+	size_t count;
+	const struct dcesrv_lsa_Lookup_view **array;
+};
+
+static const struct dcesrv_lsa_Lookup_view_table *dcesrv_lsa_view_table(
+	enum lsa_LookupNamesLevel level);
 
 /*
   lookup a SID for 1 name
 */
-static NTSTATUS dcesrv_lsa_lookup_name(struct tevent_context *ev_ctx, 
-				       struct loadparm_context *lp_ctx,
-				       struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
-				       const char *name, const char **authority_name, 
-				       struct dom_sid **sid, enum lsa_SidType *rtype,
-				       uint32_t *rid)
+static NTSTATUS dcesrv_lsa_lookup_name(struct lsa_policy_state *state,
+				       TALLOC_CTX *mem_ctx,
+				       const char *domain_name,
+				       const struct dom_sid *domain_sid,
+				       struct ldb_dn *domain_dn,
+				       const char *principal,
+				       const struct dom_sid **p_sid,
+				       enum lsa_SidType *p_type)
 {
-	int ret, i;
-	uint32_t atype;
-	struct ldb_message **res;
 	const char * const attrs[] = { "objectSid", "sAMAccountType", NULL};
-	const char *p;
-	const char *domain;
-	const char *username;
-	struct ldb_dn *domain_dn;
-	struct dom_sid *domain_sid;
+	struct ldb_message **res = NULL;
+	const char *nt4_account = NULL;
+	char *encoded_account = NULL;
+	const char *at = NULL;
 	NTSTATUS status;
+	const struct dom_sid *sid = NULL;
+	uint32_t atype;
+	enum lsa_SidType type;
+	bool match = false;
+	int ret;
+
+	if (principal == NULL && principal[0] == '\0') {
+		return NT_STATUS_NONE_MAPPED;
+	}
+
+	at = strchr(principal, '@');
+	if (at != NULL) {
+		const char *nt4_domain = NULL;
 
-	p = strchr_m(name, '\\');
-	if (p != NULL) {
-		domain = talloc_strndup(mem_ctx, name, p-name);
-		if (!domain) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		username = p + 1;
-	} else if (strchr_m(name, '@')) {
 		status = crack_name_to_nt4_name(mem_ctx,
 						state->sam_ldb,
 						DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
-						name, &domain, &username);
+						principal,
+						&nt4_domain,
+						&nt4_account);
 		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(3, ("Failed to crack name %s into an NT4 name: %s\n", name, nt_errstr(status)));
+			DEBUG(3, ("Failed to crack name %s into an NT4 name: %s\n",
+				  principal, nt_errstr(status)));
 			return status;
 		}
-	} else {
-		domain = NULL;
-		username = name;
-	}
-	
-	if (!domain) {
-		/* Look up table of well known names */
-		status = lookup_well_known_names(mem_ctx, NULL, username, authority_name, sid, rtype);
-		if (NT_STATUS_IS_OK(status)) {
-			dom_sid_split_rid(NULL, *sid, NULL, rid);
-			return NT_STATUS_OK;
-		}
 
-		if (username == NULL) {
-			*authority_name = NAME_BUILTIN;
-			*sid = dom_sid_parse_talloc(mem_ctx, SID_BUILTIN);
-			if (*sid == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*rtype = SID_NAME_DOMAIN;
-			*rid = 0xFFFFFFFF;
-			return NT_STATUS_OK;
-		}
-
-		if (strcasecmp_m(username, NAME_NT_AUTHORITY) == 0) { 
-			*authority_name = NAME_NT_AUTHORITY;
-			*sid =  dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
-			if (*sid == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*rtype = SID_NAME_DOMAIN;
-			dom_sid_split_rid(NULL, *sid, NULL, rid);
-			return NT_STATUS_OK;
+		match = strequal(nt4_domain, domain_name);
+		if (!match) {
+			/*
+			 * TODO: handle multiple domains in a forest.
+			 */
+			return NT_STATUS_NONE_MAPPED;
 		}
-		if (strcasecmp_m(username, NAME_BUILTIN) == 0) { 
-			*authority_name = NAME_BUILTIN;
-			*sid = dom_sid_parse_talloc(mem_ctx, SID_BUILTIN);
-			if (*sid == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*rtype = SID_NAME_DOMAIN;
-			*rid = 0xFFFFFFFF;
-			return NT_STATUS_OK;
-		}
-		if (strcasecmp_m(username, state->domain_dns) == 0) { 
-			*authority_name = talloc_strdup(mem_ctx,
-							state->domain_name);
-			if (*authority_name == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*sid =  dom_sid_dup(mem_ctx, state->domain_sid);
-			if (*sid == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*rtype = SID_NAME_DOMAIN;
-			*rid = 0xFFFFFFFF;
-			return NT_STATUS_OK;
-		}
-		if (strcasecmp_m(username, state->domain_name) == 0) { 
-			*authority_name = talloc_strdup(mem_ctx,
-							state->domain_name);
-			if (*authority_name == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*sid =  dom_sid_dup(mem_ctx, state->domain_sid);
-			if (*sid == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*rtype = SID_NAME_DOMAIN;
-			*rid = 0xFFFFFFFF;
-			return NT_STATUS_OK;
-		}
-
-		/* Perhaps this is a well known user? */
-		name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_NT_AUTHORITY, username);
-		if (!name) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype, rid);
-		if (NT_STATUS_IS_OK(status)) {
-			return status;
-		}
-
-		/* Perhaps this is a BUILTIN user? */
-		name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_BUILTIN, username);
-		if (!name) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype, rid);
-		if (NT_STATUS_IS_OK(status)) {
-			return status;
-		}
-
-		/* OK, I give up - perhaps we need to assume the user is in our domain? */
-		name = talloc_asprintf(mem_ctx, "%s\\%s", state->domain_name, username);
-		if (!name) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype, rid);
-		if (NT_STATUS_IS_OK(status)) {
-			return status;
-		}
-
-		return STATUS_SOME_UNMAPPED;
-	} else if (strcasecmp_m(domain, NAME_NT_AUTHORITY) == 0) {
-		if (!*username) {
-			*authority_name = NAME_NT_AUTHORITY;
-			*sid = dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
-			if (*sid == NULL) {
-				return NT_STATUS_NO_MEMORY;
-			}
-			*rtype = SID_NAME_DOMAIN;
-			dom_sid_split_rid(NULL, *sid, NULL, rid);
-			return NT_STATUS_OK;
-		}
-
-		/* Look up table of well known names */
-		status = lookup_well_known_names(mem_ctx, domain, username, authority_name, 
-						 sid, rtype);
-		if (NT_STATUS_IS_OK(status)) {
-			dom_sid_split_rid(NULL, *sid, NULL, rid);
-		}
-		return status;
-	} else if (strcasecmp_m(domain, NAME_BUILTIN) == 0) {
-		*authority_name = NAME_BUILTIN;
-		domain_dn = state->builtin_dn;
-	} else if (strcasecmp_m(domain, state->domain_dns) == 0) { 
-		*authority_name = talloc_strdup(mem_ctx,
-						state->domain_name);
-		if (*authority_name == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		domain_dn = state->domain_dn;
-	} else if (strcasecmp_m(domain, state->domain_name) == 0) { 
-		*authority_name = talloc_strdup(mem_ctx,
-						state->domain_name);
-		if (*authority_name == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		domain_dn = state->domain_dn;
 	} else {
-		/* Not local, need to ask winbind in future */
-		return STATUS_SOME_UNMAPPED;
+		nt4_account = principal;
 	}
 
-	ret = gendb_search_dn(state->sam_ldb, mem_ctx, domain_dn, &res, attrs);
-	if (ret != 1) {
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
-	}
-	domain_sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
-	if (domain_sid == NULL) {
-		return NT_STATUS_INVALID_SID;
+	encoded_account = ldb_binary_encode_string(mem_ctx, nt4_account);
+	if (encoded_account == NULL) {
+		return NT_STATUS_NO_MEMORY;
 	}
 
-	if (!*username) {
-		*sid = domain_sid;
-		*rtype = SID_NAME_DOMAIN;
-		*rid = 0xFFFFFFFF;
-		return NT_STATUS_OK;
-	}
-	
 	ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs, 
 			   "(&(sAMAccountName=%s)(objectSid=*))", 
-			   ldb_binary_encode_string(mem_ctx, username));
+			   encoded_account);
+	TALLOC_FREE(encoded_account);
 	if (ret < 0) {
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+		return NT_STATUS_INTERNAL_DB_ERROR;
+	}
+	if (ret == 0) {
+		return NT_STATUS_NONE_MAPPED;
+	}
+	if (ret > 1) {
+		status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+		DBG_ERR("nt4_account[%s] found %d times (principal[%s]) - %s\n",
+			nt4_account, ret, principal, nt_errstr(status));
+		return status;
 	}
 
-	for (i=0; i < ret; i++) {
-		*sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
-		if (*sid == NULL) {
-			return NT_STATUS_INVALID_SID;
-		}
-
-		/* Check that this is in the domain */
-		if (!dom_sid_in_domain(domain_sid, *sid)) {
-			continue;
-		}
-
-		atype = ldb_msg_find_attr_as_uint(res[i], "sAMAccountType", 0);
-			
-		*rtype = ds_atype_map(atype);
-		if (*rtype == SID_NAME_UNKNOWN) {
-			return STATUS_SOME_UNMAPPED;
-		}
+	sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+	if (sid == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
 
-		dom_sid_split_rid(NULL, *sid, NULL, rid);
-		return NT_STATUS_OK;
+	/* Check that this is in the domain */
+	match = dom_sid_in_domain(domain_sid, sid);
+	if (!match) {
+		return NT_STATUS_NONE_MAPPED;
 	}
 
-	/* need to check for an allocated sid */
+	atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
+	type = ds_atype_map(atype);
+	if (type == SID_NAME_UNKNOWN) {
+		return NT_STATUS_NONE_MAPPED;
+	}
 
-	return NT_STATUS_INVALID_SID;
+	*p_sid = sid;
+	*p_type = type;
+	return NT_STATUS_OK;
 }
 
 
 /*
   add to the lsa_RefDomainList for LookupSids and LookupNames
 */
-static NTSTATUS dcesrv_lsa_authority_list(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx, 
-					  enum lsa_SidType rtype,
-					  const char *authority_name,
-					  struct dom_sid *sid, 
+static NTSTATUS dcesrv_lsa_authority_list(const char *authority_name,
+					  const struct dom_sid *authority_sid,
 					  struct lsa_RefDomainList *domains,
 					  uint32_t *sid_index)
 {
-	struct dom_sid *authority_sid;
 	uint32_t i;
 
-	if (rtype != SID_NAME_DOMAIN) {
-		authority_sid = dom_sid_dup(mem_ctx, sid);
-		if (authority_sid == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		authority_sid->num_auths--;
-	} else {
-		authority_sid = sid;
+	*sid_index = UINT32_MAX;
+
+	if (authority_name == NULL) {
+		return NT_STATUS_OK;
 	}
 
 	/* see if we've already done this authority name */
@@ -505,85 +192,77 @@ static NTSTATUS dcesrv_lsa_authority_list(struct lsa_policy_state *state, TALLOC
 	if (domains->domains == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	domains->domains[i].name.string = authority_name;
-	domains->domains[i].sid         = authority_sid;
+	domains->domains[i].name.string = talloc_strdup(domains->domains,
+							authority_name);
+	if (domains->domains[i].name.string == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	domains->domains[i].sid         = dom_sid_dup(domains->domains,
+						      authority_sid);
+	if (domains->domains[i].sid == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
 	domains->count++;
 	domains->max_size = LSA_REF_DOMAIN_LIST_MULTIPLIER * domains->count;
 	*sid_index = i;
-	
+
 	return NT_STATUS_OK;
 }
 
 /*
   lookup a name for 1 SID
 */
-static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
-				      struct dom_sid *sid, const char *sid_str,
-				      const char **authority_name, 
-				      const char **name, enum lsa_SidType *rtype)
+static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state,
+				      TALLOC_CTX *mem_ctx,
+				      const char *domain_name,
+				      const struct dom_sid *domain_sid,
+				      struct ldb_dn *domain_dn,
+				      const struct dom_sid *sid,
+				      const char **p_name,
+				      enum lsa_SidType *p_type)
 {
-	NTSTATUS status;
-	int ret;
+	const char * const attrs[] = { "sAMAccountName", "sAMAccountType", NULL};
+	struct ldb_message **res = NULL;
+	char *encoded_sid = NULL;
+	const char *name = NULL;
 	uint32_t atype;
-	struct ldb_message **res;
-	struct ldb_dn *domain_dn;
-	const char * const attrs[] = { "sAMAccountName", "sAMAccountType", "cn", NULL};
-
-	status = lookup_well_known_sids(mem_ctx, sid_str, authority_name, name, rtype);
-	if (NT_STATUS_IS_OK(status)) {
-		return status;
-	}
-
-	if (dom_sid_equal(state->domain_sid, sid)) {
-		*authority_name = talloc_strdup(mem_ctx, state->domain_name);
-		if (*authority_name == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		*name = NULL;
-		*rtype = SID_NAME_DOMAIN;
-		return NT_STATUS_OK;
-	}
-
-	if (dom_sid_in_domain(state->domain_sid, sid)) {
-		*authority_name = talloc_strdup(mem_ctx, state->domain_name);
-		if (*authority_name == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		domain_dn = state->domain_dn;
-	} else if (dom_sid_in_domain(state->builtin_sid, sid)) {
-		*authority_name = NAME_BUILTIN;
-		domain_dn = state->builtin_dn;
-	} else {
-		/* Not well known, our domain or built in */
-
-		/* In future, we must look at SID histories, and at trusted domains via winbind */
+	enum lsa_SidType type;
+	int ret;
 
-		return NT_STATUS_NOT_FOUND;
+	encoded_sid = ldap_encode_ndr_dom_sid(mem_ctx, sid);
+	if (encoded_sid == NULL) {
+		return NT_STATUS_NO_MEMORY;
 	}
 
-	/* need to re-add a check for an allocated sid */
-
 	ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs, 
-			   "objectSid=%s", ldap_encode_ndr_dom_sid(mem_ctx, sid));
-	if ((ret < 0) || (ret > 1)) {
-		return NT_STATUS_INTERNAL_DB_CORRUPTION;
+			   "(&(objectSid=%s)(sAMAccountName=*))", encoded_sid);
+	TALLOC_FREE(encoded_sid);
+	if (ret < 0) {
+		return NT_STATUS_INTERNAL_DB_ERROR;
 	}
 	if (ret == 0) {
-		return NT_STATUS_NOT_FOUND;
+		return NT_STATUS_NONE_MAPPED;
+	}
+	if (ret > 1) {
+		NTSTATUS status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+		DBG_ERR("sid[%s] found %d times - %s\n",
+			dom_sid_string(mem_ctx, sid), ret, nt_errstr(status));
+		return status;
 	}
 
-	*name = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
-	if (!*name) {
-		*name = ldb_msg_find_attr_as_string(res[0], "cn", NULL);
-		if (!*name) {
-			*name = talloc_strdup(mem_ctx, sid_str);
-			NT_STATUS_HAVE_NO_MEMORY(*name);
-		}
+	name = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
+	if (name == NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
 	}
 
 	atype = ldb_msg_find_attr_as_uint(res[0], "sAMAccountType", 0);
-	*rtype = ds_atype_map(atype);
+	type = ds_atype_map(atype);
+	if (type == SID_NAME_UNKNOWN) {
+		return NT_STATUS_NONE_MAPPED;
+	}
 
+	*p_name = name;
+	*p_type = type;
 	return NT_STATUS_OK;
 }
 
@@ -596,6 +275,11 @@ struct dcesrv_lsa_LookupSids_base_state {
 
 	struct lsa_LookupSids3 r;
 
+	const struct dcesrv_lsa_Lookup_view_table *view_table;
+	struct dcesrv_lsa_TranslatedItem *items;
+
+	struct dsdb_trust_routing_table *routing_table;
+
 	struct {
 		struct lsa_LookupSids *l;
 		struct lsa_LookupSids2 *l2;
@@ -603,12 +287,15 @@ struct dcesrv_lsa_LookupSids_base_state {
 	} _r;
 };
 
+static NTSTATUS dcesrv_lsa_LookupSids_base_finish(
+	struct dcesrv_lsa_LookupSids_base_state *state);
+static void dcesrv_lsa_LookupSids_base_map(
+	struct dcesrv_lsa_LookupSids_base_state *state);
+
 static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_base_state *state)
 {
-	struct lsa_policy_state *policy_state = state->policy_state;
-	TALLOC_CTX *mem_ctx = state->mem_ctx;
 	struct lsa_LookupSids3 *r = &state->r;
-	struct lsa_RefDomainList *domains = NULL;
+	uint32_t v;
 	uint32_t i;
 
 	*r->out.domains = NULL;
@@ -616,69 +303,119 @@ static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_bas
 	r->out.names->names = NULL;
 	*r->out.count = 0;
 
-	if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
-	    r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
+	state->view_table = dcesrv_lsa_view_table(r->in.level);
+	if (state->view_table == NULL) {
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	/* NOTE: the WSPP test suite tries SIDs with invalid revision numbers,
-	   and expects NT_STATUS_INVALID_PARAMETER back - we just treat it as 
-	   an unknown SID. We could add a SID validator here. (tridge) 
-	   MS-DTYP 2.4.2
-	*/
-
-	domains = talloc_zero(r->out.domains,  struct lsa_RefDomainList);
-	if (domains == NULL) {
+	*r->out.domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
+	if (*r->out.domains == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	*r->out.domains = domains;
 
-	r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName2, 
-					     r->in.sids->num_sids);
+	r->out.names->names = talloc_zero_array(r->out.names,
+						struct lsa_TranslatedName2,
+						r->in.sids->num_sids);
 	if (r->out.names->names == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 
+	state->items = talloc_zero_array(state,
+					 struct dcesrv_lsa_TranslatedItem,
+					 r->in.sids->num_sids);
+	if (state->items == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
 	for (i=0;i<r->in.sids->num_sids;i++) {
-		struct dom_sid *sid = r->in.sids->sids[i].sid;
-		char *sid_str = dom_sid_string(mem_ctx, sid);
-		const char *name, *authority_name;
-		enum lsa_SidType rtype;
-		uint32_t sid_index;
-		NTSTATUS status2;
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+		uint32_t rid = 0;
 
-		r->out.names->count++;
+		if (r->in.sids->sids[i].sid == NULL) {
+			return NT_STATUS_INVALID_PARAMETER;
+		}
 
-		r->out.names->names[i].sid_type    = SID_NAME_UNKNOWN;
-		r->out.names->names[i].name.string = sid_str;
-		r->out.names->names[i].sid_index   = 0xFFFFFFFF;
-		r->out.names->names[i].unknown     = 0;
+		item->type = SID_NAME_UNKNOWN;
+		item->sid = r->in.sids->sids[i].sid;
 
-		if (sid_str == NULL) {
-			r->out.names->names[i].name.string = "(SIDERROR)";
-			continue;
+		item->hints.sid = dom_sid_string(state->items, item->sid);
+		if (item->hints.sid == NULL) {
+			return NT_STATUS_NO_MEMORY;
 		}
 
-		status2 = dcesrv_lsa_lookup_sid(policy_state, mem_ctx, sid, sid_str,
-						&authority_name, &name, &rtype);
-		if (!NT_STATUS_IS_OK(status2)) {
-			continue;
+		dom_sid_split_rid(state->items, item->sid, NULL, &rid);
+		item->hints.rid = talloc_asprintf(state->items,
+						  "%08X", (unsigned)rid);
+		if (item->hints.rid == NULL) {
+			return NT_STATUS_NO_MEMORY;
 		}
+	}
 
-		/* set up the authority table */
-		status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx, rtype,
-						    authority_name, sid,
-						    domains, &sid_index);
-		if (!NT_STATUS_IS_OK(status2)) {
-			continue;
+	for (v=0; v < state->view_table->count; v++) {
+		const struct dcesrv_lsa_Lookup_view *view =
+			state->view_table->array[v];
+
+		for (i=0; i < r->in.sids->num_sids; i++) {
+			struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+			NTSTATUS status;
+
+			if (item->done) {
+				continue;
+			}
+
+			status = view->lookup_sid(state, item);
+			if (NT_STATUS_IS_OK(status)) {
+				item->done = true;
+			} else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+				status = NT_STATUS_OK;
+			} else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+				status = NT_STATUS_OK;
+			}
+			if (!NT_STATUS_IS_OK(status)) {
+				return status;
+			}
 		}
+	}
+
+	return dcesrv_lsa_LookupSids_base_finish(state);
+}
+
+static NTSTATUS dcesrv_lsa_LookupSids_base_finish(
+	struct dcesrv_lsa_LookupSids_base_state *state)
+{
+	struct lsa_LookupSids3 *r = &state->r;
+	uint32_t i;
 
-		r->out.names->names[i].sid_type    = rtype;
-		r->out.names->names[i].name.string = name;
+	for (i=0;i<r->in.sids->num_sids;i++) {
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+		NTSTATUS status;
+		uint32_t sid_index = UINT32_MAX;
+
+		status = dcesrv_lsa_authority_list(item->authority_name,
+						   item->authority_sid,
+						   *r->out.domains,
+						   &sid_index);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
+		}
+
+		if (item->name == NULL && r->in.level == LSA_LOOKUP_NAMES_ALL) {
+			if (sid_index == UINT32_MAX) {
+				item->name = item->hints.sid;
+			} else {
+				item->name = item->hints.rid;
+			}
+		}
+
+		r->out.names->names[i].sid_type    = item->type;
+		r->out.names->names[i].name.string = item->name;
 		r->out.names->names[i].sid_index   = sid_index;
-		r->out.names->names[i].unknown     = 0;
+		r->out.names->names[i].unknown     = item->flags;
 
-		(*r->out.count)++;
+		r->out.names->count++;
+		if (item->type != SID_NAME_UNKNOWN) {
+			(*r->out.count)++;
+		}
 	}
 
 	if (*r->out.count == 0) {
@@ -942,6 +679,11 @@ struct dcesrv_lsa_LookupNames_base_state {
 
 	struct lsa_LookupNames4 r;
 
+	const struct dcesrv_lsa_Lookup_view_table *view_table;
+	struct dcesrv_lsa_TranslatedItem *items;
+
+	struct dsdb_trust_routing_table *routing_table;
+
 	struct {
 		struct lsa_LookupNames *l;
 		struct lsa_LookupNames2 *l2;
@@ -950,15 +692,16 @@ struct dcesrv_lsa_LookupNames_base_state {
 	} _r;
 };
 
+static NTSTATUS dcesrv_lsa_LookupNames_base_finish(
+	struct dcesrv_lsa_LookupNames_base_state *state);
+static void dcesrv_lsa_LookupNames_base_map(
+	struct dcesrv_lsa_LookupNames_base_state *state);
 
 static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_base_state *state)
 {
-	struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx;
-	struct dcesrv_call_state *dce_call = state->dce_call;
-	struct lsa_policy_state *policy_state = state->policy_state;
-	TALLOC_CTX *mem_ctx = state->mem_ctx;
 	struct lsa_LookupNames4 *r = &state->r;
-	struct lsa_RefDomainList *domains = NULL;
+	enum lsa_LookupOptions invalid_lookup_options = 0;
+	uint32_t v;
 	uint32_t i;
 
 	*r->out.domains = NULL;
@@ -966,58 +709,148 @@ static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_b
 	r->out.sids->sids = NULL;
 	*r->out.count = 0;
 
-	if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
-	    r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
+	if (r->in.level != LSA_LOOKUP_NAMES_ALL) {
+		invalid_lookup_options |=
+			LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL;
+	}
+	if (r->in.lookup_options & invalid_lookup_options) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	state->view_table = dcesrv_lsa_view_table(r->in.level);
+	if (state->view_table == NULL) {
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
-	if (domains == NULL) {
+	*r->out.domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
+	if (*r->out.domains == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	*r->out.domains = domains;
 
-	r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid3, 
-					   r->in.num_names);
+	r->out.sids->sids = talloc_zero_array(r->out.sids,
+					      struct lsa_TranslatedSid3,
+					      r->in.num_names);
 	if (r->out.sids->sids == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 
+	state->items = talloc_zero_array(state,
+					 struct dcesrv_lsa_TranslatedItem,
+					 r->in.num_names);
+	if (state->items == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
 	for (i=0;i<r->in.num_names;i++) {
-		const char *name = r->in.names[i].string;
-		const char *authority_name;
-		struct dom_sid *sid;
-		uint32_t sid_index, rid;
-		enum lsa_SidType rtype;
-		NTSTATUS status2;
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+		char *p = NULL;
+
+		item->type = SID_NAME_UNKNOWN;
+		item->name = r->in.names[i].string;
+		/*
+		 * Note: that item->name can be NULL!
+		 *
+		 * See test_LookupNames_NULL() in
+		 * source4/torture/rpc/lsa.c
+		 *
+		 * nt4 returns NT_STATUS_NONE_MAPPED with sid_type
+		 * SID_NAME_UNKNOWN, rid 0, and sid_index -1;
+		 *
+		 * w2k3/w2k8 return NT_STATUS_OK with sid_type
+		 * SID_NAME_DOMAIN, rid -1 and sid_index 0 and BUILTIN domain
+		 */
+		if (item->name == NULL) {
+			continue;
+		}
 
-		r->out.sids->count++;
+		item->hints.principal = item->name;
+		p = strchr(item->name, '\\');
+		if (p != NULL && p != item->name) {
+			item->hints.domain = talloc_strndup(state->items,
+							    item->name,
+							    p - item->name);
+			if (item->hints.domain == NULL) {
+				return NT_STATUS_NO_MEMORY;
+			}
+			item->hints.namespace = item->hints.domain;
+			p++;
+			if (p[0] == '\0') {
+				/*
+				 * This is just 'BUILTIN\'.
+				 */
+				item->hints.principal = NULL;
+			} else {
+				item->hints.principal = p;
+			}
+		}
+		if (item->hints.domain == NULL) {
+			p = strchr(item->name, '@');
+			if (p != NULL && p != item->name && p[1] != '\0') {
+				item->hints.namespace = p + 1;
+			}
+		}
+	}
 
-		r->out.sids->sids[i].sid_type    = SID_NAME_UNKNOWN;
-		r->out.sids->sids[i].sid         = NULL;
-		r->out.sids->sids[i].sid_index   = 0xFFFFFFFF;
-		r->out.sids->sids[i].flags       = 0;
+	for (v=0; v < state->view_table->count; v++) {
+		const struct dcesrv_lsa_Lookup_view *view =
+			state->view_table->array[v];
 
-		status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, policy_state, mem_ctx, name,
-						 &authority_name, &sid, &rtype, &rid);
-		if (!NT_STATUS_IS_OK(status2) || sid->num_auths == 0) {
-			continue;
+		for (i=0; i < r->in.num_names; i++) {
+			struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+			NTSTATUS status;
+
+			if (item->done) {
+				continue;
+			}
+
+			status = view->lookup_name(state, item);
+			if (NT_STATUS_IS_OK(status)) {
+				item->done = true;
+			} else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+				status = NT_STATUS_OK;
+			} else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+				status = NT_STATUS_OK;
+			}
+			if (!NT_STATUS_IS_OK(status)) {
+				return status;
+			}
 		}
+	}
 
-		status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx, rtype, authority_name, 
-						    sid, domains, &sid_index);
-		if (!NT_STATUS_IS_OK(status2)) {
-			continue;
+	return dcesrv_lsa_LookupNames_base_finish(state);
+}
+
+static NTSTATUS dcesrv_lsa_LookupNames_base_finish(
+	struct dcesrv_lsa_LookupNames_base_state *state)
+{
+	struct lsa_LookupNames4 *r = &state->r;
+	uint32_t i;
+
+	for (i=0;i<r->in.num_names;i++) {
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+		NTSTATUS status;
+		uint32_t sid_index = UINT32_MAX;
+
+		status = dcesrv_lsa_authority_list(item->authority_name,
+						   item->authority_sid,
+						   *r->out.domains,
+						   &sid_index);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
 		}
 
-		r->out.sids->sids[i].sid_type    = rtype;
-		r->out.sids->sids[i].sid         = sid;
-		r->out.sids->sids[i].sid_index   = sid_index;
-		r->out.sids->sids[i].flags       = 0;
+		r->out.sids->sids[i].sid_type  = item->type;
+		r->out.sids->sids[i].sid       = discard_const_p(struct dom_sid,
+								 item->sid);
+		r->out.sids->sids[i].sid_index = sid_index;
+		r->out.sids->sids[i].flags     = item->flags;
 
-		(*r->out.count)++;
+		r->out.sids->count++;
+		if (item->type != SID_NAME_UNKNOWN) {
+			(*r->out.count)++;
+		}
 	}
-	
+
 	if (*r->out.count == 0) {
 		return NT_STATUS_NONE_MAPPED;
 	}
@@ -1394,3 +1227,468 @@ NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *
 	return status;
 }
 
+static NTSTATUS dcesrv_lsa_lookup_name_predefined(
+		struct dcesrv_lsa_LookupNames_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	NTSTATUS status;
+
+	status = dom_sid_lookup_predefined_name(item->name,
+						&item->sid,
+						&item->type,
+						&item->authority_sid,
+						&item->authority_name);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		return status;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_predefined(
+		struct dcesrv_lsa_LookupSids_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	NTSTATUS status;
+
+	status = dom_sid_lookup_predefined_sid(item->sid,
+					       &item->name,
+					       &item->type,
+					       &item->authority_sid,
+					       &item->authority_name);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		return status;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_predefined = {
+	.name = "Predefined",
+	.lookup_sid = dcesrv_lsa_lookup_sid_predefined,
+	.lookup_name = dcesrv_lsa_lookup_name_predefined,
+};
+
+static NTSTATUS dcesrv_lsa_lookup_name_builtin(
+		struct dcesrv_lsa_LookupNames_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	struct lsa_policy_state *policy_state = state->policy_state;
+	NTSTATUS status;
+	bool is_builtin = false;
+
+	if (item->name == NULL) {
+		/*
+		 * This should not be mapped.
+		 */
+		return NT_STATUS_OK;
+	}
+
+	/*
+	 * The predefined view already handled the BUILTIN domain.
+	 *
+	 * Now we just need to find the principal.
+	 *
+	 * We only allow 'BUILTIN\something' and
+	 * not 'something at BUILTIN.
+	 *
+	 * And we try out best for just 'something'.
+	 */
+	is_builtin = strequal(item->hints.domain, NAME_BUILTIN);
+	if (!is_builtin && item->hints.domain != NULL) {
+		return NT_STATUS_NONE_MAPPED;
+	}
+
+	status = dcesrv_lsa_lookup_name(state->policy_state,
+					state->mem_ctx,
+					NAME_BUILTIN,
+					policy_state->builtin_sid,
+					policy_state->builtin_dn,
+					item->hints.principal,
+					&item->sid,
+					&item->type);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		if (!is_builtin) {
+			return NT_STATUS_NONE_MAPPED;
+		}
+		/*
+		 * We know we're authoritive
+		 */
+		status = NT_STATUS_OK;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	item->authority_name = NAME_BUILTIN;
+	item->authority_sid = policy_state->builtin_sid;
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_builtin(
+		struct dcesrv_lsa_LookupSids_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	struct lsa_policy_state *policy_state = state->policy_state;
+	NTSTATUS status;
+	bool is_builtin = false;
+
+	/*
+	 * The predefined view already handled the BUILTIN domain.
+	 *
+	 * Now we just need to find the principal.
+	 */
+	is_builtin = dom_sid_in_domain(policy_state->builtin_sid, item->sid);
+	if (!is_builtin) {
+		return NT_STATUS_NONE_MAPPED;
+	}
+
+	status = dcesrv_lsa_lookup_sid(state->policy_state,
+				       state->mem_ctx,
+				       NAME_BUILTIN,
+				       policy_state->builtin_sid,
+				       policy_state->builtin_dn,
+				       item->sid,
+				       &item->name,
+				       &item->type);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		/*
+		 * We know we're authoritive
+		 */
+		status = NT_STATUS_OK;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	item->authority_name = NAME_BUILTIN;
+	item->authority_sid = policy_state->builtin_sid;
+	return NT_STATUS_OK;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_builtin = {
+	.name = "Builtin",
+	.lookup_sid = dcesrv_lsa_lookup_sid_builtin,
+	.lookup_name = dcesrv_lsa_lookup_name_builtin,
+};
+
+static NTSTATUS dcesrv_lsa_lookup_name_account(
+		struct dcesrv_lsa_LookupNames_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	struct lsa_policy_state *policy_state = state->policy_state;
+	struct loadparm_context *lp_ctx = state->dce_call->conn->dce_ctx->lp_ctx;
+	struct lsa_LookupNames4 *r = &state->r;
+	NTSTATUS status;
+	int role;
+	bool (*is_local_match_fn)(struct loadparm_context *, const char *) = NULL;
+	bool is_domain = false;
+	bool try_lookup = false;
+	const char *check_domain_name = NULL;
+
+	role = lpcfg_server_role(lp_ctx);
+	if (role == ROLE_ACTIVE_DIRECTORY_DC) {
+		is_local_match_fn = lpcfg_is_my_domain_or_realm;
+	} else {
+		is_local_match_fn = lpcfg_is_myname;
+	}
+
+	if (item->name == NULL) {
+		/*
+		 * This should not be mapped.
+		 */
+		return NT_STATUS_OK;
+	}
+
+	if (item->hints.domain != NULL && item->hints.principal == NULL) {
+		/*
+		 * This is 'DOMAIN\'.
+		 */
+		check_domain_name = item->hints.domain;
+	} else {
+		/*
+		 * This is just 'DOMAIN'.
+		 */
+		check_domain_name = item->name;
+	}
+	is_domain = is_local_match_fn(lp_ctx, check_domain_name);
+	if (is_domain) {
+		item->type = SID_NAME_DOMAIN;
+		item->sid = policy_state->domain_sid;
+		item->authority_name = policy_state->domain_name;
+		item->authority_sid = policy_state->domain_sid;
+		return NT_STATUS_OK;
+	}
+
+	if (r->in.lookup_options & LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL) {
+		if (item->hints.domain != item->hints.namespace) {
+			/*
+			 * This means the client asked for an UPN,
+			 * and it should not be mapped.
+			 */
+			return NT_STATUS_OK;
+		}
+	}
+
+	if (item->hints.namespace != NULL) {
+		is_domain = is_local_match_fn(lp_ctx, item->hints.namespace);
+		try_lookup = is_domain;
+	} else {
+		try_lookup = true;
+	}
+
+	if (!try_lookup) {
+		struct dcesrv_lsa_TranslatedItem tmp;
+
+		tmp = *item;
+		status = dom_sid_lookup_predefined_name(item->hints.namespace,
+							&tmp.sid,
+							&tmp.type,
+							&tmp.authority_sid,
+							&tmp.authority_name);
+		if (NT_STATUS_IS_OK(status)) {
+			/*
+			 * It should not be handled by us.
+			 */
+			return NT_STATUS_NONE_MAPPED;
+		}
+		if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+			return status;
+		}
+	}
+
+	if (!try_lookup) {
+		const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+		const struct lsa_ForestTrustDomainInfo *di = NULL;
+
+		if (state->routing_table == NULL) {
+			status = dsdb_trust_routing_table_load(policy_state->sam_ldb,
+							       state,
+							       &state->routing_table);
+			if (!NT_STATUS_IS_OK(status)) {
+				return status;
+			}
+		}
+
+		tdo = dsdb_trust_domain_by_name(state->routing_table,
+						item->hints.namespace,
+						&di);
+		if (tdo == NULL) {
+			/*
+			 * The name is not resolvable at all...
+			 */
+			return NT_STATUS_OK;
+		}
+
+		if (!(tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
+			/*
+			 * The name is not resolvable here
+			 */
+			return NT_STATUS_NONE_MAPPED;
+		}
+
+		/*
+		 * TODO: handle multiple domains in a forest together with
+		 * LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY
+		 */
+		is_domain = true;
+		try_lookup = true;
+	}
+
+	if (!try_lookup) {
+		/*
+		 * It should not be handled by us.
+		 */
+		return NT_STATUS_NONE_MAPPED;
+	}
+
+	/*
+	 * TODO: handle multiple domains in our forest.
+	 */
+
+	status = dcesrv_lsa_lookup_name(state->policy_state,
+					state->mem_ctx,
+					policy_state->domain_name,
+					policy_state->domain_sid,
+					policy_state->domain_dn,
+					item->hints.principal,
+					&item->sid,
+					&item->type);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		if (!is_domain) {
+			return NT_STATUS_NONE_MAPPED;
+		}
+		/*
+		 * We know we're authoritive
+		 */
+		status = NT_STATUS_OK;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	item->authority_name = policy_state->domain_name;
+	item->authority_sid = policy_state->domain_sid;
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_account(
+		struct dcesrv_lsa_LookupSids_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	struct lsa_policy_state *policy_state = state->policy_state;
+	NTSTATUS status;
+	bool is_domain;
+
+	is_domain = dom_sid_equal(policy_state->domain_sid, item->sid);
+	if (is_domain) {
+		item->type = SID_NAME_DOMAIN;
+		item->name = policy_state->domain_name;
+		item->authority_name = policy_state->domain_name;
+		item->authority_sid = policy_state->domain_sid;
+		return NT_STATUS_OK;
+	}
+	is_domain = dom_sid_in_domain(policy_state->domain_sid, item->sid);
+	if (!is_domain) {
+		return NT_STATUS_NONE_MAPPED;
+	}
+
+	status = dcesrv_lsa_lookup_sid(state->policy_state,
+				       state->mem_ctx,
+				       policy_state->domain_name,
+				       policy_state->domain_sid,
+				       policy_state->domain_dn,
+				       item->sid,
+				       &item->name,
+				       &item->type);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		/*
+		 * We know we're authoritive
+		 */
+		status = NT_STATUS_OK;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	item->authority_name = policy_state->domain_name;
+	item->authority_sid = policy_state->domain_sid;
+	return NT_STATUS_OK;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_account = {
+	.name = "Account",
+	.lookup_sid = dcesrv_lsa_lookup_sid_account,
+	.lookup_name = dcesrv_lsa_lookup_name_account,
+};
+
+static NTSTATUS dcesrv_lsa_lookup_name_winbind(
+		struct dcesrv_lsa_LookupNames_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	return NT_STATUS_NONE_MAPPED;
+}
+
+static NTSTATUS dcesrv_lsa_lookup_sid_winbind(
+		struct dcesrv_lsa_LookupSids_base_state *state,
+		struct dcesrv_lsa_TranslatedItem *item)
+{
+	return NT_STATUS_NONE_MAPPED;
+}
+
+static const struct dcesrv_lsa_Lookup_view view_winbind = {
+	.name = "Winbind",
+	.lookup_sid = dcesrv_lsa_lookup_sid_winbind,
+	.lookup_name = dcesrv_lsa_lookup_name_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_all_views[] = {
+	&view_predefined,
+	&view_builtin,
+	&view_account,
+	&view_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_all = {
+	.name = "LSA_LOOKUP_NAMES_ALL",
+	.count = ARRAY_SIZE(table_all_views),
+	.array = table_all_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_domains_views[] = {
+	&view_account,
+	&view_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_domains = {
+	.name = "LSA_LOOKUP_NAMES_DOMAINS_ONLY",
+	.count = ARRAY_SIZE(table_domains_views),
+	.array = table_domains_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_primary_views[] = {
+	&view_account,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_primary = {
+	.name = "LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY",
+	.count = ARRAY_SIZE(table_primary_views),
+	.array = table_primary_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view *table_remote_views[] = {
+	&view_winbind,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_gc = {
+	.name = "LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY",
+	.count = ARRAY_SIZE(table_domains_views),
+	.array = table_domains_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_xreferral = {
+	.name = "LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY",
+	.count = ARRAY_SIZE(table_remote_views),
+	.array = table_remote_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_xresolve = {
+	.name = "LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2",
+	.count = ARRAY_SIZE(table_domains_views),
+	.array = table_domains_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table table_rodc = {
+	.name = "LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC",
+	.count = ARRAY_SIZE(table_remote_views),
+	.array = table_remote_views,
+};
+
+static const struct dcesrv_lsa_Lookup_view_table *dcesrv_lsa_view_table(
+	enum lsa_LookupNamesLevel level)
+{
+	switch (level) {
+	case LSA_LOOKUP_NAMES_ALL:
+		return &table_all;
+	case LSA_LOOKUP_NAMES_DOMAINS_ONLY:
+		return &table_domains;
+	case LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY:
+		return &table_primary;
+	case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY:
+		return &table_gc;
+	case LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY:
+		return &table_xreferral;
+	case LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2:
+		return &table_xresolve;
+	case LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC:
+		return &table_rodc;
+	}
+
+	return NULL;
+}
-- 
1.9.1


From 851afb821c73eea6fd374c65218bd786f366864e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 23 Jan 2018 13:19:37 +0100
Subject: [PATCH 29/30] winbindd: implement
 wb_irpc_lsa_{LookupNames4,LookupSids3}()

This will be used by the LSA Server on an AD DC to request remote views
from trusts.

In future we should implement wb_lookupnames_send/recv similar to
wb_lookupsids_send/recv, but for now using wb_lookupname_send/recv in a loop
works as a first step.

We also need to make use of req->in.level and req->in.client_revision
once we want to support more than one domain within our own forest.

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/winbindd/winbindd_irpc.c | 408 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 408 insertions(+)

diff --git a/source3/winbindd/winbindd_irpc.c b/source3/winbindd/winbindd_irpc.c
index 4101469..e03312e 100644
--- a/source3/winbindd/winbindd_irpc.c
+++ b/source3/winbindd/winbindd_irpc.c
@@ -25,6 +25,10 @@
 #include "librpc/gen_ndr/ndr_winbind_c.h"
 #include "source4/lib/messaging/irpc.h"
 #include "librpc/gen_ndr/ndr_winbind.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/dom_sid.h"
+#include "passdb/lookup_sid.h" /* only for LOOKUP_NAME_NO_NSS flag */
 
 struct wb_irpc_forward_state {
 	struct irpc_message *msg;
@@ -330,6 +334,398 @@ static NTSTATUS wb_irpc_SendToSam(struct irpc_message *msg,
 					domain, IRPC_CALL_TIMEOUT);
 }
 
+struct wb_irpc_lsa_LookupSids3_state {
+	struct irpc_message *msg;
+	struct lsa_LookupSids3 *req;
+};
+
+static void wb_irpc_lsa_LookupSids3_done(struct tevent_req *subreq);
+
+static NTSTATUS wb_irpc_lsa_LookupSids3_call(struct irpc_message *msg,
+					     struct lsa_LookupSids3 *req)
+{
+	struct wb_irpc_lsa_LookupSids3_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+	struct dom_sid *sids = NULL;
+	uint32_t i;
+
+	state = talloc_zero(msg, struct wb_irpc_lsa_LookupSids3_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->msg = msg;
+	state->req = req;
+
+	state->req->out.domains = talloc_zero(state->msg,
+					struct lsa_RefDomainList *);
+	if (state->req->out.domains == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	state->req->out.names = talloc_zero(state->msg,
+					    struct lsa_TransNameArray2);
+	if (state->req->out.names == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	state->req->out.count = talloc_zero(state->msg, uint32_t);
+	if (state->req->out.count == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->req->out.names->names = talloc_zero_array(state->msg,
+						struct lsa_TranslatedName2,
+						req->in.sids->num_sids);
+	if (state->req->out.names->names == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	sids = talloc_zero_array(state, struct dom_sid,
+				 req->in.sids->num_sids);
+	if (sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i = 0; i < req->in.sids->num_sids; i++) {
+		if (req->in.sids->sids[i].sid == NULL) {
+			return NT_STATUS_REQUEST_NOT_ACCEPTED;
+		}
+
+		sids[i] = *req->in.sids->sids[i].sid;
+	}
+
+	subreq = wb_lookupsids_send(msg,
+				    server_event_context(),
+				    sids, req->in.sids->num_sids);
+	if (subreq == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	tevent_req_set_callback(subreq, wb_irpc_lsa_LookupSids3_done, state);
+	msg->defer_reply = true;
+
+	return NT_STATUS_OK;
+}
+
+static void wb_irpc_lsa_LookupSids3_done(struct tevent_req *subreq)
+{
+	struct wb_irpc_lsa_LookupSids3_state *state =
+		tevent_req_callback_data(subreq,
+		struct wb_irpc_lsa_LookupSids3_state);
+	struct lsa_RefDomainList *domains = NULL;
+	struct lsa_TransNameArray *names = NULL;
+	NTSTATUS status;
+	uint32_t i;
+
+	status = wb_lookupsids_recv(subreq, state->msg,
+				    &domains, &names);
+	TALLOC_FREE(subreq);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,("RPC callback failed for %s - %s\n",
+			 __func__, nt_errstr(status)));
+		irpc_send_reply(state->msg, status);
+		return;
+	}
+
+	if (names->count > state->req->in.sids->num_sids) {
+		status = NT_STATUS_INTERNAL_ERROR;
+		DEBUG(0,("RPC callback failed for %s - %s\n",
+			 __func__, nt_errstr(status)));
+		irpc_send_reply(state->msg, status);
+		return;
+	}
+
+	*state->req->out.domains = domains;
+	for (i = 0; i < names->count; i++) {
+		struct lsa_TranslatedName2 *n2 =
+			&state->req->out.names->names[i];
+
+		n2->sid_type = names->names[i].sid_type;
+		n2->name = names->names[i].name;
+		n2->sid_index = names->names[i].sid_index;
+		n2->unknown = 0;
+
+		if (n2->sid_type != SID_NAME_UNKNOWN) {
+			(*state->req->out.count)++;
+		}
+	}
+	state->req->out.names->count = names->count;
+
+	if (*state->req->out.count == 0) {
+		state->req->out.result = NT_STATUS_NONE_MAPPED;
+	} else if (*state->req->out.count != names->count) {
+		state->req->out.result = NT_STATUS_SOME_NOT_MAPPED;
+	} else {
+		state->req->out.result = NT_STATUS_OK;
+	}
+
+	irpc_send_reply(state->msg, NT_STATUS_OK);
+	return;
+}
+
+struct wb_irpc_lsa_LookupNames4_name {
+	void *state;
+	uint32_t idx;
+	const char *domain;
+	char *name;
+	struct dom_sid sid;
+	enum lsa_SidType type;
+	struct dom_sid *authority_sid;
+};
+
+struct wb_irpc_lsa_LookupNames4_state {
+	struct irpc_message *msg;
+	struct lsa_LookupNames4 *req;
+	struct wb_irpc_lsa_LookupNames4_name *names;
+	uint32_t num_pending;
+	uint32_t num_domain_sids;
+	struct dom_sid *domain_sids;
+};
+
+static void wb_irpc_lsa_LookupNames4_done(struct tevent_req *subreq);
+
+static NTSTATUS wb_irpc_lsa_LookupNames4_call(struct irpc_message *msg,
+					      struct lsa_LookupNames4 *req)
+{
+	struct wb_irpc_lsa_LookupNames4_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+	uint32_t i;
+
+
+	state = talloc_zero(msg, struct wb_irpc_lsa_LookupNames4_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->msg = msg;
+	state->req = req;
+
+	state->req->out.domains = talloc_zero(state->msg,
+					struct lsa_RefDomainList *);
+	if (state->req->out.domains == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	state->req->out.sids = talloc_zero(state->msg,
+					   struct lsa_TransSidArray3);
+	if (state->req->out.sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	state->req->out.count = talloc_zero(state->msg, uint32_t);
+	if (state->req->out.count == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->req->out.sids->sids = talloc_zero_array(state->msg,
+						struct lsa_TranslatedSid3,
+						req->in.num_names);
+	if (state->req->out.sids->sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state->names = talloc_zero_array(state,
+					 struct wb_irpc_lsa_LookupNames4_name,
+					 req->in.num_names);
+	if (state->names == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i = 0; i < req->in.num_names; i++) {
+		struct wb_irpc_lsa_LookupNames4_name *nstate =
+			&state->names[i];
+		char *p = NULL;
+
+		if (req->in.names[i].string == NULL) {
+			DBG_ERR("%s: name[%s] NT_STATUS_REQUEST_NOT_ACCEPTED.\n",
+				__location__, req->in.names[i].string);
+			return NT_STATUS_REQUEST_NOT_ACCEPTED;
+		}
+		nstate->state = state;
+		nstate->idx = i;
+		nstate->name = talloc_strdup(state->names,
+					     req->in.names[i].string);
+		if (nstate->name == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+		nstate->type = SID_NAME_UNKNOWN;
+
+		/* cope with the name being a fully qualified name */
+		p = strchr(nstate->name, '\\');
+		if (p != NULL) {
+			*p = 0;
+			nstate->domain = nstate->name;
+			nstate->name = p+1;
+		} else if ((p = strchr(nstate->name, '@')) != NULL) {
+			/* upn */
+			nstate->domain = p + 1;
+			*p = 0;
+		} else {
+			/*
+			 * TODO: select the domain based on
+			 * req->in.level and req->in.client_revision
+			 *
+			 * For now we don't allow this.
+			 */
+			DBG_ERR("%s: name[%s] NT_STATUS_REQUEST_NOT_ACCEPTED.\n",
+				__location__, nstate->name);
+			return NT_STATUS_REQUEST_NOT_ACCEPTED;
+		}
+
+		subreq = wb_lookupname_send(msg,
+					    server_event_context(),
+					    nstate->domain,
+					    nstate->name,
+					    LOOKUP_NAME_NO_NSS);
+		if (subreq == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+		tevent_req_set_callback(subreq,
+					wb_irpc_lsa_LookupNames4_done,
+					nstate);
+		state->num_pending++;
+	}
+
+	msg->defer_reply = true;
+
+	return NT_STATUS_OK;
+}
+
+static void wb_irpc_lsa_LookupNames4_domains_done(struct tevent_req *subreq);
+
+static void wb_irpc_lsa_LookupNames4_done(struct tevent_req *subreq)
+{
+	struct wb_irpc_lsa_LookupNames4_name *nstate =
+		(struct wb_irpc_lsa_LookupNames4_name *)
+		tevent_req_callback_data_void(subreq);
+	struct wb_irpc_lsa_LookupNames4_state *state =
+		talloc_get_type_abort(nstate->state,
+		struct wb_irpc_lsa_LookupNames4_state);
+	NTSTATUS status;
+
+	SMB_ASSERT(state->num_pending > 0);
+	state->num_pending--;
+	status = wb_lookupname_recv(subreq, &nstate->sid, &nstate->type);
+	TALLOC_FREE(subreq);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,("RPC callback failed for %s - %s\n",
+			 __func__, nt_errstr(status)));
+		irpc_send_reply(state->msg, status);
+		return;
+	}
+
+	status = dom_sid_split_rid(state, &nstate->sid,
+				   &nstate->authority_sid, NULL);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("dom_sid_split_rid(%s) failed - %s\n",
+			 sid_string_dbg(&nstate->sid), nt_errstr(status));
+		irpc_send_reply(state->msg, status);
+		return;
+	}
+
+	status = add_sid_to_array_unique(state,
+					 nstate->authority_sid,
+					 &state->domain_sids,
+					 &state->num_domain_sids);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("add_sid_to_array_unique(%s) failed - %s\n",
+			 sid_string_dbg(nstate->authority_sid), nt_errstr(status));
+		irpc_send_reply(state->msg, status);
+		return;
+	}
+
+	if (state->num_pending > 0) {
+		/*
+		 * wait for more...
+		 */
+		return;
+	}
+
+	/*
+	 * Now resolve all domains back to a name
+	 * to get a good lsa_RefDomainList
+	 */
+	subreq = wb_lookupsids_send(state,
+				    server_event_context(),
+				    state->domain_sids,
+				    state->num_domain_sids);
+	if (subreq == NULL) {
+		status = NT_STATUS_NO_MEMORY;
+		DBG_ERR("wb_lookupsids_send - %s\n",
+			nt_errstr(status));
+		irpc_send_reply(state->msg, status);
+		return;
+	}
+	tevent_req_set_callback(subreq,
+				wb_irpc_lsa_LookupNames4_domains_done,
+				state);
+
+	return;
+}
+
+static void wb_irpc_lsa_LookupNames4_domains_done(struct tevent_req *subreq)
+{
+	struct wb_irpc_lsa_LookupNames4_state *state =
+		tevent_req_callback_data(subreq,
+		struct wb_irpc_lsa_LookupNames4_state);
+	struct lsa_RefDomainList *domains = NULL;
+	struct lsa_TransNameArray *names = NULL;
+	NTSTATUS status;
+	uint32_t i;
+
+	status = wb_lookupsids_recv(subreq, state->msg,
+				    &domains, &names);
+	TALLOC_FREE(subreq);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,("RPC callback failed for %s - %s\n",
+			 __func__, nt_errstr(status)));
+		irpc_send_reply(state->msg, status);
+		return;
+	}
+
+	*state->req->out.domains = domains;
+	for (i = 0; i < state->req->in.num_names; i++) {
+		struct wb_irpc_lsa_LookupNames4_name *nstate =
+			&state->names[i];
+		struct lsa_TranslatedSid3 *s3 =
+			&state->req->out.sids->sids[i];
+		uint32_t di;
+
+		s3->sid_type = nstate->type;
+		if (s3->sid_type != SID_NAME_UNKNOWN) {
+			s3->sid = &nstate->sid;
+		} else {
+			s3->sid = NULL;
+		}
+		s3->sid_index = UINT32_MAX;
+		for (di = 0; di < domains->count; di++) {
+			bool match;
+
+			if (domains->domains[di].sid == NULL) {
+				continue;
+			}
+
+			match = dom_sid_equal(nstate->authority_sid,
+					      domains->domains[di].sid);
+			if (match) {
+				s3->sid_index = di;
+				break;
+			}
+		}
+		if (s3->sid_type != SID_NAME_UNKNOWN) {
+			(*state->req->out.count)++;
+		}
+	}
+	state->req->out.sids->count = state->req->in.num_names;
+
+	if (*state->req->out.count == 0) {
+		state->req->out.result = NT_STATUS_NONE_MAPPED;
+	} else if (*state->req->out.count != state->req->in.num_names) {
+		state->req->out.result = NT_STATUS_SOME_NOT_MAPPED;
+	} else {
+		state->req->out.result = NT_STATUS_OK;
+	}
+
+	irpc_send_reply(state->msg, NT_STATUS_OK);
+	return;
+}
+
 NTSTATUS wb_irpc_register(void)
 {
 	NTSTATUS status;
@@ -361,6 +757,18 @@ NTSTATUS wb_irpc_register(void)
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
+	status = IRPC_REGISTER(winbind_imessaging_context(),
+			       lsarpc, LSA_LOOKUPSIDS3,
+			       wb_irpc_lsa_LookupSids3_call, NULL);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	status = IRPC_REGISTER(winbind_imessaging_context(),
+			       lsarpc, LSA_LOOKUPNAMES4,
+			       wb_irpc_lsa_LookupNames4_call, NULL);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
 
 	return NT_STATUS_OK;
 }
-- 
1.9.1


From 9c5d3b763e6f15e283129c96ec365420229288b9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 19 Jan 2018 13:42:40 +0100
Subject: [PATCH 30/30] s4:rpc_server/lsa: implement forwarding
 lsa_Lookup{Sids,Names}() requests to winbindd

This might not be perfect yet, but it's enough to allow names from trusted
forests/domain to be resolved, which is very important for samba based
domain members.

BUG 8: ...

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail.d/s4-lsa-server  |   2 -
 source4/rpc_server/lsa/lsa_lookup.c | 527 +++++++++++++++++++++++++++++++++++-
 2 files changed, 525 insertions(+), 4 deletions(-)
 delete mode 100644 selftest/knownfail.d/s4-lsa-server

diff --git a/selftest/knownfail.d/s4-lsa-server b/selftest/knownfail.d/s4-lsa-server
deleted file mode 100644
index 86365e2..0000000
--- a/selftest/knownfail.d/s4-lsa-server
+++ /dev/null
@@ -1,2 +0,0 @@
-^samba4.blackbox.trust_ntlm.Test07.rpcclient.lookupnames.with.ADDOMAIN.*fl20
-^samba4.blackbox.trust_ntlm.Test08.rpcclient.lookupnames.with.ADDOM.SAMBA.EXAMPLE.COM.*fl20
diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
index 2e0b8af..ff224c5 100644
--- a/source4/rpc_server/lsa/lsa_lookup.c
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -23,6 +23,8 @@
 #include "rpc_server/lsa/lsa.h"
 #include "libds/common/roles.h"
 #include "libds/common/flag_mapping.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
 
 struct dcesrv_lsa_TranslatedItem {
 	enum lsa_SidType type;
@@ -31,6 +33,7 @@ struct dcesrv_lsa_TranslatedItem {
 	const char *authority_name;
 	const struct dom_sid *authority_sid;
 	uint32_t flags;
+	uint32_t wb_idx;
 	bool done;
 	struct {
 		const char *domain; /* only $DOMAIN\ */
@@ -281,6 +284,15 @@ struct dcesrv_lsa_LookupSids_base_state {
 	struct dsdb_trust_routing_table *routing_table;
 
 	struct {
+		struct dcerpc_binding_handle *irpc_handle;
+		struct lsa_SidArray sids;
+		struct lsa_RefDomainList *domains;
+		struct lsa_TransNameArray2 names;
+		uint32_t count;
+		NTSTATUS result;
+	} wb;
+
+	struct {
 		struct lsa_LookupSids *l;
 		struct lsa_LookupSids2 *l2;
 		struct lsa_LookupSids3 *l3;
@@ -291,10 +303,12 @@ static NTSTATUS dcesrv_lsa_LookupSids_base_finish(
 	struct dcesrv_lsa_LookupSids_base_state *state);
 static void dcesrv_lsa_LookupSids_base_map(
 	struct dcesrv_lsa_LookupSids_base_state *state);
+static void dcesrv_lsa_LookupSids_base_done(struct tevent_req *subreq);
 
 static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_base_state *state)
 {
 	struct lsa_LookupSids3 *r = &state->r;
+	struct tevent_req *subreq = NULL;
 	uint32_t v;
 	uint32_t i;
 
@@ -377,7 +391,47 @@ static NTSTATUS dcesrv_lsa_LookupSids_base_call(struct dcesrv_lsa_LookupSids_bas
 		}
 	}
 
-	return dcesrv_lsa_LookupSids_base_finish(state);
+	if (state->wb.irpc_handle == NULL) {
+		return dcesrv_lsa_LookupSids_base_finish(state);
+	}
+
+	state->wb.sids.sids = talloc_zero_array(state, struct lsa_SidPtr,
+						r->in.sids->num_sids);
+	if (state->wb.sids.sids == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i=0; i < r->in.sids->num_sids; i++) {
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+
+		if (item->done) {
+			continue;
+		}
+
+		item->wb_idx = state->wb.sids.num_sids;
+		state->wb.sids.sids[item->wb_idx] = r->in.sids->sids[i];
+		state->wb.sids.num_sids++;
+	}
+
+	subreq = dcerpc_lsa_LookupSids3_send(state,
+					     state->dce_call->event_ctx,
+					     state->wb.irpc_handle,
+					     &state->wb.sids,
+					     &state->wb.domains,
+					     &state->wb.names,
+					     state->r.in.level,
+					     &state->wb.count,
+					     state->r.in.lookup_options,
+					     state->r.in.client_revision);
+	if (subreq == NULL) {
+		return NT_STATUS_NO_MEMORY;;
+	}
+	state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+	tevent_req_set_callback(subreq,
+				dcesrv_lsa_LookupSids_base_done,
+				state);
+
+	return NT_STATUS_OK;
 }
 
 static NTSTATUS dcesrv_lsa_LookupSids_base_finish(
@@ -467,6 +521,91 @@ static void dcesrv_lsa_LookupSids_base_map(
 	}
 }
 
+static void dcesrv_lsa_LookupSids_base_done(struct tevent_req *subreq)
+{
+	struct dcesrv_lsa_LookupSids_base_state *state =
+		tevent_req_callback_data(subreq,
+		struct dcesrv_lsa_LookupSids_base_state);
+	struct dcesrv_call_state *dce_call = state->dce_call;
+	NTSTATUS status;
+	uint32_t i;
+
+	status = dcerpc_lsa_LookupSids3_recv(subreq, state->mem_ctx,
+					     &state->wb.result);
+	TALLOC_FREE(subreq);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+		DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+			 nt_errstr(status)));
+		goto finished;
+	} else if (!NT_STATUS_IS_OK(status)) {
+		state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+		DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+			 nt_errstr(status)));
+		goto finished;
+	}
+
+	status = state->wb.result;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		status = NT_STATUS_OK;
+	} else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+		status = NT_STATUS_OK;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		goto finished;
+	}
+
+	for (i=0; i < state->r.in.sids->num_sids; i++) {
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+		struct lsa_TranslatedName2 *s2 = NULL;
+		struct lsa_DomainInfo *d = NULL;
+
+		if (item->done) {
+			continue;
+		}
+
+		if (item->wb_idx >= state->wb.names.count) {
+			status = NT_STATUS_INTERNAL_ERROR;
+			goto finished;
+		}
+
+		s2 = &state->wb.names.names[item->wb_idx];
+
+		item->type = s2->sid_type;
+		item->name = s2->name.string;
+		item->flags = s2->unknown;
+
+		if (s2->sid_index == UINT32_MAX) {
+			continue;
+		}
+
+		if (state->wb.domains == NULL) {
+			status = NT_STATUS_INTERNAL_ERROR;
+			goto finished;
+		}
+
+		if (s2->sid_index >= state->wb.domains->count) {
+			status = NT_STATUS_INTERNAL_ERROR;
+			goto finished;
+		}
+
+		d = &state->wb.domains->domains[s2->sid_index];
+
+		item->authority_name = d->name.string;
+		item->authority_sid = d->sid;
+	}
+
+	status = dcesrv_lsa_LookupSids_base_finish(state);
+ finished:
+	state->r.out.result = status;
+	dcesrv_lsa_LookupSids_base_map(state);
+	TALLOC_FREE(state);
+
+	status = dcesrv_reply(dce_call);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+	}
+}
+
 /*
   lsa_LookupSids2
 */
@@ -685,6 +824,16 @@ struct dcesrv_lsa_LookupNames_base_state {
 	struct dsdb_trust_routing_table *routing_table;
 
 	struct {
+		struct dcerpc_binding_handle *irpc_handle;
+		uint32_t num_names;
+		struct lsa_String *names;
+		struct lsa_RefDomainList *domains;
+		struct lsa_TransSidArray3 sids;
+		uint32_t count;
+		NTSTATUS result;
+	} wb;
+
+	struct {
 		struct lsa_LookupNames *l;
 		struct lsa_LookupNames2 *l2;
 		struct lsa_LookupNames3 *l3;
@@ -696,11 +845,13 @@ static NTSTATUS dcesrv_lsa_LookupNames_base_finish(
 	struct dcesrv_lsa_LookupNames_base_state *state);
 static void dcesrv_lsa_LookupNames_base_map(
 	struct dcesrv_lsa_LookupNames_base_state *state);
+static void dcesrv_lsa_LookupNames_base_done(struct tevent_req *subreq);
 
 static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_base_state *state)
 {
 	struct lsa_LookupNames4 *r = &state->r;
 	enum lsa_LookupOptions invalid_lookup_options = 0;
+	struct tevent_req *subreq = NULL;
 	uint32_t v;
 	uint32_t i;
 
@@ -817,7 +968,48 @@ static NTSTATUS dcesrv_lsa_LookupNames_base_call(struct dcesrv_lsa_LookupNames_b
 		}
 	}
 
-	return dcesrv_lsa_LookupNames_base_finish(state);
+	if (state->wb.irpc_handle == NULL) {
+		return dcesrv_lsa_LookupNames_base_finish(state);
+	}
+
+	state->wb.names = talloc_zero_array(state, struct lsa_String,
+					    r->in.num_names);
+	if (state->wb.names == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	for (i=0;i<r->in.num_names;i++) {
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+
+		if (item->done) {
+			continue;
+		}
+
+		item->wb_idx = state->wb.num_names;
+		state->wb.names[item->wb_idx] = r->in.names[i];
+		state->wb.num_names++;
+	}
+
+	subreq = dcerpc_lsa_LookupNames4_send(state,
+					      state->dce_call->event_ctx,
+					      state->wb.irpc_handle,
+					      state->wb.num_names,
+					      state->wb.names,
+					      &state->wb.domains,
+					      &state->wb.sids,
+					      state->r.in.level,
+					      &state->wb.count,
+					      state->r.in.lookup_options,
+					      state->r.in.client_revision);
+	if (subreq == NULL) {
+		return NT_STATUS_NO_MEMORY;;
+	}
+	state->dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+	tevent_req_set_callback(subreq,
+				dcesrv_lsa_LookupNames_base_done,
+				state);
+
+	return NT_STATUS_OK;
 }
 
 static NTSTATUS dcesrv_lsa_LookupNames_base_finish(
@@ -952,6 +1144,91 @@ static void dcesrv_lsa_LookupNames_base_map(
 	}
 }
 
+static void dcesrv_lsa_LookupNames_base_done(struct tevent_req *subreq)
+{
+	struct dcesrv_lsa_LookupNames_base_state *state =
+		tevent_req_callback_data(subreq,
+		struct dcesrv_lsa_LookupNames_base_state);
+	struct dcesrv_call_state *dce_call = state->dce_call;
+	NTSTATUS status;
+	uint32_t i;
+
+	status = dcerpc_lsa_LookupNames4_recv(subreq, state->mem_ctx,
+					      &state->wb.result);
+	TALLOC_FREE(subreq);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+		DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+			 nt_errstr(status)));
+		goto finished;
+	} else if (!NT_STATUS_IS_OK(status)) {
+		state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+		DEBUG(0,(__location__ ": IRPC callback failed %s\n",
+			 nt_errstr(status)));
+		goto finished;
+	}
+
+	status = state->wb.result;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		status = NT_STATUS_OK;
+	} else if (NT_STATUS_EQUAL(status, NT_STATUS_SOME_NOT_MAPPED)) {
+		status = NT_STATUS_OK;
+	}
+	if (!NT_STATUS_IS_OK(status)) {
+		goto finished;
+	}
+
+	for (i=0; i < state->r.in.num_names;i++) {
+		struct dcesrv_lsa_TranslatedItem *item = &state->items[i];
+		struct lsa_TranslatedSid3 *s3 = NULL;
+		struct lsa_DomainInfo *d = NULL;
+
+		if (item->done) {
+			continue;
+		}
+
+		if (item->wb_idx >= state->wb.sids.count) {
+			status = NT_STATUS_INTERNAL_ERROR;
+			goto finished;
+		}
+
+		s3 = &state->wb.sids.sids[item->wb_idx];
+
+		item->type = s3->sid_type;
+		item->sid = s3->sid;
+		item->flags = s3->flags;
+
+		if (s3->sid_index == UINT32_MAX) {
+			continue;
+		}
+
+		if (state->wb.domains == NULL) {
+			status = NT_STATUS_INTERNAL_ERROR;
+			goto finished;
+		}
+
+		if (s3->sid_index >= state->wb.domains->count) {
+			status = NT_STATUS_INTERNAL_ERROR;
+			goto finished;
+		}
+
+		d = &state->wb.domains->domains[s3->sid_index];
+
+		item->authority_name = d->name.string;
+		item->authority_sid = d->sid;
+	}
+
+	status = dcesrv_lsa_LookupNames_base_finish(state);
+ finished:
+	state->r.out.result = status;
+	dcesrv_lsa_LookupNames_base_map(state);
+	TALLOC_FREE(state);
+
+	status = dcesrv_reply(dce_call);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0,(__location__ ": dcesrv_reply() failed - %s\n", nt_errstr(status)));
+	}
+}
+
 /*
   lsa_LookupNames3
 */
@@ -1592,6 +1869,142 @@ static NTSTATUS dcesrv_lsa_lookup_name_winbind(
 		struct dcesrv_lsa_LookupNames_base_state *state,
 		struct dcesrv_lsa_TranslatedItem *item)
 {
+	struct lsa_LookupNames4 *r = &state->r;
+	const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+	const struct lsa_ForestTrustDomainInfo *di = NULL;
+	NTSTATUS status;
+	const char *check_domain_name = NULL;
+	bool expect_domain = false;
+
+	if (item->name == NULL) {
+		/*
+		 * This should not be mapped.
+		 */
+		return NT_STATUS_OK;
+	}
+
+	if (item->hints.domain != NULL && item->hints.principal == NULL) {
+		/*
+		 * This is 'DOMAIN\'.
+		 */
+		check_domain_name = item->hints.domain;
+		expect_domain = true;
+	} else if (item->hints.namespace != NULL) {
+		/*
+		 * This is 'DOMAIN\someone'
+		 * or 'someone at DOMAIN'
+		 */
+		check_domain_name = item->hints.namespace;
+	} else {
+		/*
+		 * This is just 'DOMAIN'.
+		 */
+		check_domain_name = item->name;
+		expect_domain = true;
+	}
+
+	if (state->routing_table == NULL) {
+		struct lsa_policy_state *policy_state = state->policy_state;
+
+		status = dsdb_trust_routing_table_load(policy_state->sam_ldb,
+						       state,
+						       &state->routing_table);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
+		}
+	}
+
+	tdo = dsdb_trust_domain_by_name(state->routing_table,
+					check_domain_name,
+					&di);
+	if (tdo == NULL) {
+		/*
+		 * The name is not resolvable at all...
+		 *
+		 * And for now we don't send unqualified names
+		 * to winbindd, as we don't handle them
+		 * there yet.
+		 *
+		 * TODO: how should that work within
+		 * winbindd?
+		 */
+		return NT_STATUS_OK;
+	}
+
+	if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+		/*
+		 * The name should have been resolved in the account view.
+		 *
+		 * TODO: handle multiple domains in a forest...
+		 */
+		return NT_STATUS_OK;
+	}
+
+	if (expect_domain) {
+		const char *name = NULL;
+		const struct dom_sid *sid = NULL;
+
+		name = talloc_strdup(state->mem_ctx,
+				     di->netbios_domain_name.string);
+		if (name == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+		sid = dom_sid_dup(state->mem_ctx,
+				  di->domain_sid);
+		if (sid == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+		item->type = SID_NAME_DOMAIN;
+		item->sid = sid;
+		item->authority_name = name;
+		item->authority_sid = sid;
+		return NT_STATUS_OK;
+	}
+
+	if (r->in.lookup_options & LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES_LOCAL) {
+		if (item->hints.namespace == NULL) {
+			/*
+			 * We should not try to resolve isolated names
+			 * remotely.
+			 */
+			return NT_STATUS_OK;
+		}
+	}
+
+	/*
+	 * We know at least the domain part of the name exists.
+	 *
+	 * For now the rest handled within winbindd.
+	 *
+	 * In future we can optimize it based on
+	 * r->in.level.
+	 *
+	 * We can also try to resolve SID_NAME_DOMAIN
+	 * just based on the routing table.
+	 */
+
+	if (state->wb.irpc_handle != NULL) {
+		/*
+		 * already called...
+		 */
+		return NT_STATUS_NONE_MAPPED;
+	}
+
+	state->wb.irpc_handle = irpc_binding_handle_by_name(state,
+					state->dce_call->msg_ctx,
+					"winbind_server",
+					&ndr_table_lsarpc);
+	if (state->wb.irpc_handle == NULL) {
+		DEBUG(0,("Failed to get binding_handle for winbind_server task\n"));
+		state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+		return NT_STATUS_INVALID_SYSTEM_SERVICE;
+	}
+
+	/*
+	 * 60 seconds timeout should be enough
+	 */
+	dcerpc_binding_handle_set_timeout(state->wb.irpc_handle, 60);
+
 	return NT_STATUS_NONE_MAPPED;
 }
 
@@ -1599,6 +2012,116 @@ static NTSTATUS dcesrv_lsa_lookup_sid_winbind(
 		struct dcesrv_lsa_LookupSids_base_state *state,
 		struct dcesrv_lsa_TranslatedItem *item)
 {
+	const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+	const struct lsa_ForestTrustDomainInfo *di = NULL;
+	struct dcesrv_lsa_TranslatedItem tmp;
+	struct dom_sid domain_sid = {0,};
+	NTSTATUS status;
+	bool match;
+
+	/*
+	 * Verify the sid is not INVALID.
+	 */
+	tmp = *item;
+	status = dom_sid_lookup_predefined_sid(tmp.sid,
+					       &tmp.name,
+					       &tmp.type,
+					       &tmp.authority_sid,
+					       &tmp.authority_name);
+	if (NT_STATUS_IS_OK(status)) {
+		status = NT_STATUS_NONE_MAPPED;
+	}
+	if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+		/*
+		 * Typically INVALID_SID
+		 */
+		return status;
+	}
+
+	if (state->routing_table == NULL) {
+		struct lsa_policy_state *policy_state = state->policy_state;
+
+		status = dsdb_trust_routing_table_load(policy_state->sam_ldb,
+						       state,
+						       &state->routing_table);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
+		}
+	}
+
+	domain_sid = *item->sid;
+	if (domain_sid.num_auths == 5) {
+		sid_split_rid(&domain_sid, NULL);
+	}
+
+	tdo = dsdb_trust_domain_by_sid(state->routing_table,
+				       &domain_sid, &di);
+	if (tdo == NULL) {
+		/*
+		 * The sid is not resolvable at all...
+		 */
+		return NT_STATUS_OK;
+	}
+
+	if (tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+		/*
+		 * The name should have been resolved in the account view.
+		 *
+		 * TODO: handle multiple domains in a forest...
+		 */
+		return NT_STATUS_OK;
+	}
+
+	match = dom_sid_equal(di->domain_sid, item->sid);
+	if (match) {
+		const char *name = NULL;
+
+		name = talloc_strdup(state->mem_ctx,
+				     di->netbios_domain_name.string);
+		if (name == NULL) {
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		item->type = SID_NAME_DOMAIN;
+		item->name = name;
+		item->authority_name = name;
+		item->authority_sid = item->sid;
+		return NT_STATUS_OK;
+	}
+
+	/*
+	 * We know at least the domain part of the sid exists.
+	 *
+	 * For now the rest handled within winbindd.
+	 *
+	 * In future we can optimize it based on
+	 * r->in.level.
+	 *
+	 * We can also try to resolve SID_NAME_DOMAIN
+	 * just based on the routing table.
+	 */
+	if (state->wb.irpc_handle != NULL) {
+		/*
+		 * already called...
+		 */
+		return NT_STATUS_NONE_MAPPED;
+	}
+
+	state->wb.irpc_handle = irpc_binding_handle_by_name(state,
+					state->dce_call->msg_ctx,
+					"winbind_server",
+					&ndr_table_lsarpc);
+	if (state->wb.irpc_handle == NULL) {
+		DEBUG(0,("Failed to get binding_handle for winbind_server task\n"));
+		state->dce_call->fault_code = DCERPC_FAULT_CANT_PERFORM;
+		return NT_STATUS_INVALID_SYSTEM_SERVICE;
+	}
+
+	/*
+	 * 60 seconds timeout should be enough
+	 */
+	dcerpc_binding_handle_set_timeout(state->wb.irpc_handle, 60);
+
 	return NT_STATUS_NONE_MAPPED;
 }
 
-- 
1.9.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20180219/641dcadd/signature-0001.sig>


More information about the samba-technical mailing list