[PATCHES] Handle expired sessions in winbindd

Christof Schmitt cs at samba.org
Thu Jan 7 23:11:37 UTC 2016


A SMB session from winbind to the DC can expire any time, when trying to
connect to a pipe or when issuing a RPC call. Depending on which
codepath receives the corresponding error code (SESSION_EXPIRED or
IO_DEVICE_ERROR for RPC calls), the error is surfaced to the winbindd
client, and can e.g. fail a SESSION_SETUP in smbd. This happened
recently in a member server that is seeing many short-lived SMB
connections and occassionally some of the getpwnam calls to winbindd
fail due to the expired sessions.

The attached patches catch the error and retry the same request on a new
connection. The first patch is a hack to use the admember selftest
environment for some testing. I was not sure of the best approach of
getting some test coverage here. Maybe change the config of admember to
use short-lived tickets, or create a new admember2 environment that uses
a short ticket lifetime.

Christof
-------------- next part --------------
From cb4baad6baab60a6c211d698981dccb8b705ca96 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Tue, 22 Dec 2015 12:47:55 -0700
Subject: [PATCH 1/8] DONOTPUSH: Hack admember selftest to have winbind receive SESSION_EXPIRED

---
 lib/param/util.c          |   20 +++++++++-----------
 selftest/target/Samba4.pm |   10 ++++++----
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/lib/param/util.c b/lib/param/util.c
index 7e4232d..7a805e9 100644
--- a/lib/param/util.c
+++ b/lib/param/util.c
@@ -275,17 +275,15 @@ void lpcfg_default_kdc_policy(struct loadparm_context *lp_ctx,
 				time_t *usr_tkt_lifetime,
 				time_t *renewal_lifetime)
 {
-	long val;
+	*svc_tkt_lifetime = lpcfg_parm_long(lp_ctx, NULL,
+					    "kdc", "service ticket lifetime",
+					    10 * 60 * 60);
 
-	val = lpcfg_parm_long(lp_ctx, NULL,
-				"kdc", "service ticket lifetime", 10);
-	*svc_tkt_lifetime = val * 60 * 60;
+	*usr_tkt_lifetime = lpcfg_parm_long(lp_ctx, NULL,
+					    "kdc", "user ticket lifetime",
+					    10 * 60 * 60);
 
-	val = lpcfg_parm_long(lp_ctx, NULL,
-				"kdc", "user ticket lifetime", 10);
-	*usr_tkt_lifetime = val * 60 * 60;
-
-	val = lpcfg_parm_long(lp_ctx, NULL,
-				"kdc", "renewal lifetime", 24 * 7);
-	*renewal_lifetime = val * 60 * 60;
+	*renewal_lifetime = lpcfg_parm_long(lp_ctx, NULL,
+					    "kdc", "renewal lifetime",
+					    24 * 7 * 60 * 60);
 }
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index fbefda7..5396c5b 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -1816,7 +1816,7 @@ sub provision_ad_dc($$)
 	create mask = 755
 	dos filemode = yes
 
-        dcerpc endpoint servers = -winreg -srvsvc
+        dcerpc endpoint servers = -winreg -srvsvc -epmapper
 
 	printcap name = /dev/null
 
@@ -1833,6 +1833,8 @@ sub provision_ad_dc($$)
 	queue resume command = $bindir_abs/vlp tdbfile=$lockdir/vlp.tdb queueresume %p
 	lpq cache time = 0
 	print notify backchannel = yes
+	winbind max domain connections = 5
+	kdc:service ticket lifetime = 30
 ";
 
 	my $extra_smbconf_shares = "
@@ -2089,10 +2091,10 @@ sub setup_env($$$)
 	} elsif ($envname eq "chgdcpass") {
 		return $self->setup_chgdcpass("$path/chgdcpass", $self->{vars}->{chgdcpass});
 	} elsif ($envname eq "ad_member") {
-		if (not defined($self->{vars}->{ad_dc_ntvfs})) {
-			$self->setup_ad_dc_ntvfs("$path/ad_dc_ntvfs");
+		if (not defined($self->{vars}->{ad_dc})) {
+			$self->setup_ad_dc("$path/ad_dc");
 		}
-		return $target3->setup_admember("$path/ad_member", $self->{vars}->{ad_dc_ntvfs}, 29);
+		return $target3->setup_admember("$path/ad_member", $self->{vars}->{ad_dc}, 29);
 	} elsif ($envname eq "ad_dc") {
 		return $self->setup_ad_dc("$path/ad_dc");
 	} elsif ($envname eq "ad_dc_no_nss") {
-- 
1.7.1


From 312e842f1a71de6f244c26588cfe6ba5b0547072 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Tue, 5 Jan 2016 13:39:25 -0700
Subject: [PATCH 2/8] winbindd: Reset connection for expired session before reconnecting

A RPC call on a expired SMB2 session returns IO_DEVICE_ERROR. In this
case, reset the connection before issuing the same call
again.

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source3/winbindd/winbindd_reconnect.c |   39 ++++++++++++++++++++------------
 1 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/source3/winbindd/winbindd_reconnect.c b/source3/winbindd/winbindd_reconnect.c
index e45f9b1..9442f34 100644
--- a/source3/winbindd/winbindd_reconnect.c
+++ b/source3/winbindd/winbindd_reconnect.c
@@ -27,7 +27,8 @@
 
 extern struct winbindd_methods msrpc_methods;
 
-static bool reconnect_need_retry(NTSTATUS status)
+static bool reconnect_need_retry(NTSTATUS status,
+				 struct winbindd_domain *domain)
 {
 	if (NT_STATUS_IS_OK(status)) {
 		return false;
@@ -69,6 +70,14 @@ static bool reconnect_need_retry(NTSTATUS status)
 		return false;
 	}
 
+	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR)) {
+		/*
+		 * RPC call sent on expired session, needs
+		 * reauthentication.
+		 */
+		invalidate_cm_connection(domain);
+	}
+
 	return true;
 }
 
@@ -83,7 +92,7 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
 	result = msrpc_methods.query_user_list(domain, mem_ctx,
 					       num_entries, info);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.query_user_list(domain, mem_ctx,
 						       num_entries, info);
 	return result;
@@ -100,7 +109,7 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
 	result = msrpc_methods.enum_dom_groups(domain, mem_ctx,
 					       num_entries, info);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.enum_dom_groups(domain, mem_ctx,
 						       num_entries, info);
 	return result;
@@ -118,7 +127,7 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
 	result = msrpc_methods.enum_local_groups(domain, mem_ctx,
 						 num_entries, info);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.enum_local_groups(domain, mem_ctx,
 							 num_entries, info);
 
@@ -139,7 +148,7 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain,
 	result = msrpc_methods.name_to_sid(domain, mem_ctx, domain_name, name,
 					   flags, sid, type);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.name_to_sid(domain, mem_ctx,
 						   domain_name, name, flags,
 						   sid, type);
@@ -162,7 +171,7 @@ static NTSTATUS sid_to_name(struct winbindd_domain *domain,
 	result = msrpc_methods.sid_to_name(domain, mem_ctx, sid,
 					   domain_name, name, type);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.sid_to_name(domain, mem_ctx, sid,
 						   domain_name, name, type);
 
@@ -183,7 +192,7 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 	result = msrpc_methods.rids_to_names(domain, mem_ctx, sid,
 					     rids, num_rids,
 					     domain_name, names, types);
-	if (reconnect_need_retry(result)) {
+	if (reconnect_need_retry(result, domain)) {
 		result = msrpc_methods.rids_to_names(domain, mem_ctx, sid,
 						     rids, num_rids,
 						     domain_name, names,
@@ -204,7 +213,7 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
 	result = msrpc_methods.query_user(domain, mem_ctx, user_sid,
 					  user_info);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.query_user(domain, mem_ctx, user_sid,
 						  user_info);
 
@@ -223,7 +232,7 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
 						 user_sid, num_groups,
 						 user_gids);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.lookup_usergroups(domain, mem_ctx,
 							 user_sid, num_groups,
 							 user_gids);
@@ -243,7 +252,7 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
 						  num_aliases,
 						  alias_rids);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.lookup_useraliases(domain, mem_ctx,
 							  num_sids, sids,
 							  num_aliases,
@@ -268,7 +277,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
 					       sid_mem, names,
 					       name_types);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.lookup_groupmem(domain, mem_ctx,
 						       group_sid, type,
 						       num_names,
@@ -285,7 +294,7 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32_t *seq)
 
 	result = msrpc_methods.sequence_number(domain, seq);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.sequence_number(domain, seq);
 
 	return result;
@@ -300,7 +309,7 @@ static NTSTATUS lockout_policy(struct winbindd_domain *domain,
 
 	result = msrpc_methods.lockout_policy(domain, mem_ctx, policy);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.lockout_policy(domain, mem_ctx, policy);
 
 	return result;
@@ -315,7 +324,7 @@ static NTSTATUS password_policy(struct winbindd_domain *domain,
  
 	result = msrpc_methods.password_policy(domain, mem_ctx, policy);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.password_policy(domain, mem_ctx, policy);
 	
 	return result;
@@ -330,7 +339,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
 
 	result = msrpc_methods.trusted_domains(domain, mem_ctx, trusts);
 
-	if (reconnect_need_retry(result))
+	if (reconnect_need_retry(result, domain))
 		result = msrpc_methods.trusted_domains(domain, mem_ctx,
 						       trusts);
 
-- 
1.7.1


From 278cff5eb99af42b046a162f0db8f6dbbdcd52ad Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Tue, 5 Jan 2016 14:37:30 -0700
Subject: [PATCH 3/8] winbindd: Add retry also for ADS method calls

RPC calls can return IO_DEVICE_ERROR on expired SMB2 sessions. Retrying
on a new connections avoids surfacing this errors to winbindd clients.

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source3/winbindd/winbindd_cache.c         |    4 +-
 source3/winbindd/winbindd_ndr.c           |    3 +
 source3/winbindd/winbindd_proto.h         |    4 +
 source3/winbindd/winbindd_reconnect.c     |    3 +-
 source3/winbindd/winbindd_reconnect_ads.c |  324 +++++++++++++++++++++++++++++
 source3/wscript_build                     |    1 +
 6 files changed, 335 insertions(+), 4 deletions(-)
 create mode 100644 source3/winbindd/winbindd_reconnect_ads.c

diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
index ae9d11f..cf3ed71 100644
--- a/source3/winbindd/winbindd_cache.c
+++ b/source3/winbindd/winbindd_cache.c
@@ -46,7 +46,7 @@
 
 extern struct winbindd_methods reconnect_methods;
 #ifdef HAVE_ADS
-extern struct winbindd_methods ads_methods;
+extern struct winbindd_methods reconnect_ads_methods;
 #endif
 extern struct winbindd_methods builtin_passdb_methods;
 extern struct winbindd_methods sam_passdb_methods;
@@ -168,7 +168,7 @@ static struct winbind_cache *get_cache(struct winbindd_domain *domain)
 		    && domain->active_directory
 		    && !lp_winbind_rpc_only()) {
 			DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
-			domain->backend = &ads_methods;
+			domain->backend = &reconnect_ads_methods;
 		} else {
 #endif	/* HAVE_ADS */
 			DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
diff --git a/source3/winbindd/winbindd_ndr.c b/source3/winbindd/winbindd_ndr.c
index 37b7e02..029e883 100644
--- a/source3/winbindd/winbindd_ndr.c
+++ b/source3/winbindd/winbindd_ndr.c
@@ -75,6 +75,7 @@ void ndr_print_winbindd_cm_conn(struct ndr_print *ndr,
 
 #ifdef HAVE_ADS
 extern struct winbindd_methods ads_methods;
+extern struct winbindd_methods reconnect_ads_methods;
 #endif
 extern struct winbindd_methods msrpc_methods;
 extern struct winbindd_methods builtin_passdb_methods;
@@ -100,6 +101,8 @@ void ndr_print_winbindd_methods(struct ndr_print *ndr,
 #ifdef HAVE_ADS
 	} else if (r == &ads_methods) {
 		ndr_print_string(ndr, name, "ads_methods");
+	} else if (r == &reconnect_ads_methods) {
+		ndr_print_string(ndr, name, "reconnect_ads_methods");
 #endif
 	} else if (r == &builtin_passdb_methods) {
 		ndr_print_string(ndr, name, "builtin_passdb_methods");
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 9920a3f..6e50718 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -933,4 +933,8 @@ ADS_STATUS ads_idmap_cached_connection(ADS_STRUCT **adsp, const char *dom_name);
 
 /* The following definitions come from winbindd/winbindd_irpc.c  */
 NTSTATUS wb_irpc_register(void);
+
+/* The following definitions come from winbindd/winbindd_reconnect.c  */
+bool reconnect_need_retry(NTSTATUS status, struct winbindd_domain *domain);
+
 #endif /*  _WINBINDD_PROTO_H_  */
diff --git a/source3/winbindd/winbindd_reconnect.c b/source3/winbindd/winbindd_reconnect.c
index 9442f34..f7dd805 100644
--- a/source3/winbindd/winbindd_reconnect.c
+++ b/source3/winbindd/winbindd_reconnect.c
@@ -27,8 +27,7 @@
 
 extern struct winbindd_methods msrpc_methods;
 
-static bool reconnect_need_retry(NTSTATUS status,
-				 struct winbindd_domain *domain)
+bool reconnect_need_retry(NTSTATUS status, struct winbindd_domain *domain)
 {
 	if (NT_STATUS_IS_OK(status)) {
 		return false;
diff --git a/source3/winbindd/winbindd_reconnect_ads.c b/source3/winbindd/winbindd_reconnect_ads.c
new file mode 100644
index 0000000..dc93181
--- /dev/null
+++ b/source3/winbindd/winbindd_reconnect_ads.c
@@ -0,0 +1,324 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Wrapper around winbindd_ads.c to centralize retry logic.
+   Copyright (C) Christof Schmitt 2016
+
+   Based on winbindd_reconnect.c
+   Copyright (C) Volker Lendecke 2005
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#ifdef HAVE_ADS
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern struct winbindd_methods ads_methods;
+
+/* List all users */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+				TALLOC_CTX *mem_ctx,
+				uint32_t *num_entries,
+				struct wbint_userinfo **info)
+{
+	NTSTATUS result;
+
+	result = ads_methods.query_user_list(domain, mem_ctx,
+					     num_entries, info);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.query_user_list(domain, mem_ctx,
+						     num_entries, info);
+	}
+
+	return result;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+				TALLOC_CTX *mem_ctx,
+				uint32_t *num_entries,
+				struct wb_acct_info **info)
+{
+	NTSTATUS result;
+
+	result = ads_methods.enum_dom_groups(domain, mem_ctx,
+					     num_entries, info);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.enum_dom_groups(domain, mem_ctx,
+						     num_entries, info);
+	}
+
+	return result;
+}
+
+/* List all domain groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+				  TALLOC_CTX *mem_ctx,
+				  uint32_t *num_entries,
+				  struct wb_acct_info **info)
+{
+	NTSTATUS result;
+
+	result = ads_methods.enum_local_groups(domain, mem_ctx,
+					       num_entries, info);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.enum_local_groups(domain, mem_ctx,
+						       num_entries, info);
+	}
+
+	return result;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+			    TALLOC_CTX *mem_ctx,
+			    const char *domain_name,
+			    const char *name,
+			    uint32_t flags,
+			    struct dom_sid *sid,
+			    enum lsa_SidType *type)
+{
+	NTSTATUS result;
+
+	result = ads_methods.name_to_sid(domain, mem_ctx, domain_name, name,
+					 flags, sid, type);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.name_to_sid(domain, mem_ctx,
+						 domain_name, name, flags,
+						 sid, type);
+	}
+
+	return result;
+}
+
+/*
+  convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+			    TALLOC_CTX *mem_ctx,
+			    const struct dom_sid *sid,
+			    char **domain_name,
+			    char **name,
+			    enum lsa_SidType *type)
+{
+	NTSTATUS result;
+
+	result = ads_methods.sid_to_name(domain, mem_ctx, sid,
+					 domain_name, name, type);
+
+	if (reconnect_need_retry(result, domain))
+		result = ads_methods.sid_to_name(domain, mem_ctx, sid,
+						 domain_name, name, type);
+
+	return result;
+}
+
+static NTSTATUS rids_to_names(struct winbindd_domain *domain,
+			      TALLOC_CTX *mem_ctx,
+			      const struct dom_sid *sid,
+			      uint32_t *rids,
+			      size_t num_rids,
+			      char **domain_name,
+			      char ***names,
+			      enum lsa_SidType **types)
+{
+	NTSTATUS result;
+
+	result = ads_methods.rids_to_names(domain, mem_ctx, sid,
+					   rids, num_rids,
+					   domain_name, names, types);
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.rids_to_names(domain, mem_ctx, sid,
+						   rids, num_rids, domain_name,
+						   names, types);
+	}
+
+	return result;
+}
+
+/* Lookup user information from a rid or username. */
+static NTSTATUS query_user(struct winbindd_domain *domain,
+			   TALLOC_CTX *mem_ctx,
+			   const struct dom_sid *user_sid,
+			   struct wbint_userinfo *user_info)
+{
+	NTSTATUS result;
+
+	result = ads_methods.query_user(domain, mem_ctx, user_sid, user_info);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.query_user(domain, mem_ctx, user_sid,
+						user_info);
+	}
+
+	return result;
+}
+
+/* Lookup groups a user is a member of.  I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+				  TALLOC_CTX *mem_ctx,
+				  const struct dom_sid *user_sid,
+				  uint32_t *num_groups,
+				  struct dom_sid **user_gids)
+{
+	NTSTATUS result;
+
+	result = ads_methods.lookup_usergroups(domain, mem_ctx, user_sid,
+					       num_groups, user_gids);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.lookup_usergroups(domain, mem_ctx,
+						       user_sid, num_groups,
+						       user_gids);
+	}
+
+	return result;
+}
+
+static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
+				   TALLOC_CTX *mem_ctx,
+				   uint32_t num_sids,
+				   const struct dom_sid *sids,
+				   uint32_t *num_aliases, uint32_t **alias_rids)
+{
+	NTSTATUS result;
+
+	result = ads_methods.lookup_useraliases(domain, mem_ctx, num_sids, sids,
+						num_aliases, alias_rids);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.lookup_useraliases(domain, mem_ctx,
+							num_sids, sids,
+							num_aliases,
+							alias_rids);
+	}
+
+	return result;
+}
+
+/* Lookup group membership given a rid.   */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+				TALLOC_CTX *mem_ctx,
+				const struct dom_sid *group_sid,
+				enum lsa_SidType type,
+				uint32_t *num_names,
+				struct dom_sid **sid_mem, char ***names,
+				uint32_t **name_types)
+{
+	NTSTATUS result;
+
+	result = ads_methods.lookup_groupmem(domain, mem_ctx, group_sid, type,
+					     num_names, sid_mem, names,
+					     name_types);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.lookup_groupmem(domain, mem_ctx, group_sid,
+						     type, num_names, sid_mem,
+						     names, name_types);
+	}
+
+	return result;
+}
+
+/* find the sequence number for a domain */
+static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32_t *seq)
+{
+	NTSTATUS result;
+
+	result = ads_methods.sequence_number(domain, seq);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.sequence_number(domain, seq);
+	}
+
+	return result;
+}
+
+/* find the lockout policy of a domain */
+static NTSTATUS lockout_policy(struct winbindd_domain *domain,
+			       TALLOC_CTX *mem_ctx,
+			       struct samr_DomInfo12 *policy)
+{
+	NTSTATUS result;
+
+	result = ads_methods.lockout_policy(domain, mem_ctx, policy);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.lockout_policy(domain, mem_ctx, policy);
+	}
+
+	return result;
+}
+
+/* find the password policy of a domain */
+static NTSTATUS password_policy(struct winbindd_domain *domain,
+				TALLOC_CTX *mem_ctx,
+				struct samr_DomInfo1 *policy)
+{
+	NTSTATUS result;
+
+	result = ads_methods.password_policy(domain, mem_ctx, policy);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.password_policy(domain, mem_ctx, policy);
+	}
+
+	return result;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+				TALLOC_CTX *mem_ctx,
+				struct netr_DomainTrustList *trusts)
+{
+	NTSTATUS result;
+
+	result = ads_methods.trusted_domains(domain, mem_ctx, trusts);
+
+	if (reconnect_need_retry(result, domain)) {
+		result = ads_methods.trusted_domains(domain, mem_ctx, trusts);
+	}
+
+	return result;
+}
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods reconnect_ads_methods = {
+	True,
+	query_user_list,
+	enum_dom_groups,
+	enum_local_groups,
+	name_to_sid,
+	sid_to_name,
+	rids_to_names,
+	query_user,
+	lookup_usergroups,
+	lookup_useraliases,
+	lookup_groupmem,
+	sequence_number,
+	lockout_policy,
+	password_policy,
+	trusted_domains,
+};
+
+#endif
diff --git a/source3/wscript_build b/source3/wscript_build
index d40dd7e..87b0910 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -904,6 +904,7 @@ bld.SAMBA3_BINARY('winbindd/winbindd',
                  winbindd/winbindd_msrpc.c
                  winbindd/winbindd_rpc.c
                  winbindd/winbindd_reconnect.c
+                 winbindd/winbindd_reconnect_ads.c
                  winbindd/winbindd_ads.c
                  winbindd/winbindd_samr.c
                  winbindd/winbindd_dual.c
-- 
1.7.1


From 59e7e496069708ea7aa2ae273719afe08944930c Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Tue, 5 Jan 2016 14:42:09 -0700
Subject: [PATCH 4/8] winbindd: Remove double retry from some ADS methods

The retry through the new reconnect_ads layer is enough. This structure
also makes the distinction between retry layer and actual methods call a
bit clearer.

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source3/winbindd/winbindd_ads.c |   26 ++++++++++++--------------
 1 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
index b373be5..a9a23db 100644
--- a/source3/winbindd/winbindd_ads.c
+++ b/source3/winbindd/winbindd_ads.c
@@ -38,6 +38,7 @@
 #define DBGC_CLASS DBGC_WINBIND
 
 extern struct winbindd_methods reconnect_methods;
+extern struct winbindd_methods msrpc_methods;
 
 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
 
@@ -563,9 +564,8 @@ static NTSTATUS name_to_sid(struct winbindd_domain *domain,
 			    struct dom_sid *sid,
 			    enum lsa_SidType *type)
 {
-	return reconnect_methods.name_to_sid(domain, mem_ctx,
-					     domain_name, name, flags,
-					     sid, type);
+	return msrpc_methods.name_to_sid(domain, mem_ctx, domain_name, name,
+					 flags, sid, type);
 }
 
 /* convert a domain SID to a user or group name - use rpc methods */
@@ -576,8 +576,8 @@ static NTSTATUS sid_to_name(struct winbindd_domain *domain,
 			    char **name,
 			    enum lsa_SidType *type)
 {
-	return reconnect_methods.sid_to_name(domain, mem_ctx, sid,
-					     domain_name, name, type);
+	return msrpc_methods.sid_to_name(domain, mem_ctx, sid,
+					 domain_name, name, type);
 }
 
 /* convert a list of rids to names - use rpc methods */
@@ -590,9 +590,9 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 			      char ***names,
 			      enum lsa_SidType **types)
 {
-	return reconnect_methods.rids_to_names(domain, mem_ctx, sid,
-					       rids, num_rids,
-					       domain_name, names, types);
+	return msrpc_methods.rids_to_names(domain, mem_ctx, sid,
+					   rids, num_rids,
+					   domain_name, names, types);
 }
 
 /* If you are looking for "dn_lookup": Yes, it used to be here!
@@ -1142,10 +1142,8 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
 				   uint32_t num_sids, const struct dom_sid *sids,
 				   uint32_t *num_aliases, uint32_t **alias_rids)
 {
-	return reconnect_methods.lookup_useraliases(domain, mem_ctx,
-						    num_sids, sids,
-						    num_aliases,
-						    alias_rids);
+	return msrpc_methods.lookup_useraliases(domain, mem_ctx, num_sids, sids,
+						num_aliases, alias_rids);
 }
 
 static NTSTATUS add_primary_group_members(
@@ -1527,7 +1525,7 @@ static NTSTATUS lockout_policy(struct winbindd_domain *domain,
 			       TALLOC_CTX *mem_ctx,
 			       struct samr_DomInfo12 *policy)
 {
-	return reconnect_methods.lockout_policy(domain, mem_ctx, policy);
+	return msrpc_methods.lockout_policy(domain, mem_ctx, policy);
 }
 
 /* find the password policy of a domain - use rpc methods */
@@ -1535,7 +1533,7 @@ static NTSTATUS password_policy(struct winbindd_domain *domain,
 				TALLOC_CTX *mem_ctx,
 				struct samr_DomInfo1 *policy)
 {
-	return reconnect_methods.password_policy(domain, mem_ctx, policy);
+	return msrpc_methods.password_policy(domain, mem_ctx, policy);
 }
 
 /* get a list of trusted domains */
-- 
1.7.1


From 0dee804a166a9d8b25b20dbdfff6ef56d7110cbd Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Wed, 6 Jan 2016 14:15:30 -0700
Subject: [PATCH 5/8] winbindd: Retry on expired session in cm_connect_lsa

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source3/winbindd/winbindd_cm.c |   48 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 48 insertions(+), 0 deletions(-)

diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c
index 93a2dde..35fd89a 100644
--- a/source3/winbindd/winbindd_cm.c
+++ b/source3/winbindd/winbindd_cm.c
@@ -2960,7 +2960,9 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 	NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
 	struct netlogon_creds_cli_context *p_creds;
 	struct cli_credentials *creds = NULL;
+	bool retry = false; /* allow one retry attempt for expired session */
 
+retry:
 	result = init_dc_connection_rpc(domain, false);
 	if (!NT_STATUS_IS_OK(result))
 		return result;
@@ -2995,6 +2997,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 		 smbXcli_conn_remote_name(conn->cli->conn),
 		 creds,
 		 &conn->lsa_pipe);
+
+	if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+	    && !retry) {
+		invalidate_cm_connection(domain);
+		retry = true;
+		goto retry;
+	}
+
 	if (!NT_STATUS_IS_OK(result)) {
 		DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
 			  "domain %s using NTLMSSP authenticated pipe: user "
@@ -3012,6 +3022,13 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 	result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
 					SEC_FLAG_MAXIMUM_ALLOWED,
 					&conn->lsa_policy);
+	if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+		invalidate_cm_connection(domain);
+		TALLOC_FREE(conn->lsa_pipe);
+		retry = true;
+		goto retry;
+	}
+
 	if (NT_STATUS_IS_OK(result)) {
 		goto done;
 	}
@@ -3047,6 +3064,13 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 		(conn->cli, &ndr_table_lsarpc, NCACN_NP,
 		 creds, p_creds, &conn->lsa_pipe);
 
+	if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+	    && !retry) {
+		invalidate_cm_connection(domain);
+		retry = true;
+		goto retry;
+	}
+
 	if (!NT_STATUS_IS_OK(result)) {
 		DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
 			  "domain %s using schannel. Error was %s\n",
@@ -3059,6 +3083,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 	result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
 					SEC_FLAG_MAXIMUM_ALLOWED,
 					&conn->lsa_policy);
+
+	if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+		invalidate_cm_connection(domain);
+		TALLOC_FREE(conn->lsa_pipe);
+		retry = true;
+		goto retry;
+	}
+
 	if (NT_STATUS_IS_OK(result)) {
 		goto done;
 	}
@@ -3083,6 +3115,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 	result = cli_rpc_pipe_open_noauth(conn->cli,
 					  &ndr_table_lsarpc,
 					  &conn->lsa_pipe);
+
+	if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+	    && !retry) {
+		invalidate_cm_connection(domain);
+		retry = true;
+		goto retry;
+	}
+
 	if (!NT_STATUS_IS_OK(result)) {
 		goto done;
 	}
@@ -3090,6 +3130,14 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 	result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
 					SEC_FLAG_MAXIMUM_ALLOWED,
 					&conn->lsa_policy);
+
+	if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+		invalidate_cm_connection(domain);
+		TALLOC_FREE(conn->lsa_pipe);
+		retry = true;
+		goto retry;
+	}
+
  done:
 	if (!NT_STATUS_IS_OK(result)) {
 		invalidate_cm_connection(domain);
-- 
1.7.1


From e9c6311a416ad90a6a943ca31e6c8d8d60b04eb5 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Thu, 7 Jan 2016 15:03:22 -0700
Subject: [PATCH 6/8] winbindd: Retry on expired session in cm_connect_sam

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source3/winbindd/winbindd_cm.c |   48 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 48 insertions(+), 0 deletions(-)

diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c
index 35fd89a..c0891ce 100644
--- a/source3/winbindd/winbindd_cm.c
+++ b/source3/winbindd/winbindd_cm.c
@@ -2683,6 +2683,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 	NTSTATUS status, result;
 	struct netlogon_creds_cli_context *p_creds;
 	struct cli_credentials *creds = NULL;
+	bool retry = false; /* allow one retry attempt for expired session */
 
 	if (sid_check_is_our_sam(&domain->sid)) {
 		if (domain->rodc == false || need_rw_dc == false) {
@@ -2690,6 +2691,7 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 		}
 	}
 
+retry:
 	status = init_dc_connection_rpc(domain, need_rw_dc);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
@@ -2733,6 +2735,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 					      smbXcli_conn_remote_name(conn->cli->conn),
 					      creds,
 					      &conn->samr_pipe);
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+	    && !retry) {
+		invalidate_cm_connection(domain);
+		retry = true;
+		goto retry;
+	}
+
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(10,("cm_connect_sam: failed to connect to SAMR "
 			  "pipe for domain %s using NTLMSSP "
@@ -2753,6 +2763,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 				      SEC_FLAG_MAXIMUM_ALLOWED,
 				      &conn->sam_connect_handle,
 				      &result);
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+		invalidate_cm_connection(domain);
+		TALLOC_FREE(conn->samr_pipe);
+		retry = true;
+		goto retry;
+	}
+
 	if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
 		goto open_domain;
 	}
@@ -2790,6 +2808,13 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 		(conn->cli, &ndr_table_samr, NCACN_NP,
 		 creds, p_creds, &conn->samr_pipe);
 
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+	    && !retry) {
+		invalidate_cm_connection(domain);
+		retry = true;
+		goto retry;
+	}
+
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for "
 			  "domain %s using schannel. Error was %s\n",
@@ -2804,6 +2829,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 				      SEC_FLAG_MAXIMUM_ALLOWED,
 				      &conn->sam_connect_handle,
 				      &result);
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+		invalidate_cm_connection(domain);
+		TALLOC_FREE(conn->samr_pipe);
+		retry = true;
+		goto retry;
+	}
+
 	if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
 		goto open_domain;
 	}
@@ -2830,6 +2863,13 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 	status = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr,
 					  &conn->samr_pipe);
 
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+	    && !retry) {
+		invalidate_cm_connection(domain);
+		retry = true;
+		goto retry;
+	}
+
 	if (!NT_STATUS_IS_OK(status)) {
 		goto done;
 	}
@@ -2839,6 +2879,14 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 				      SEC_FLAG_MAXIMUM_ALLOWED,
 				      &conn->sam_connect_handle,
 				      &result);
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+		invalidate_cm_connection(domain);
+		TALLOC_FREE(conn->samr_pipe);
+		retry = true;
+		goto retry;
+	}
+
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(10,("cm_connect_sam: rpccli_samr_Connect2 failed "
 			  "for domain %s Error was %s\n",
-- 
1.7.1


From 61703c97deb1c6c125c564a01dddbf1a9b12605f Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Tue, 5 Jan 2016 15:10:45 -0700
Subject: [PATCH 7/8] winbindd: Retry on expired session in cm_connect_netlogon

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source3/winbindd/winbindd_cm.c |    8 ++++++++
 1 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c
index c0891ce..f593d24 100644
--- a/source3/winbindd/winbindd_cm.c
+++ b/source3/winbindd/winbindd_cm.c
@@ -3406,6 +3406,14 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
 	}
 
 	status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+		/*
+		 * SMB2 session expired, needs reauthentication. Drop
+		 * connection and retry.
+		 */
+		invalidate_cm_connection(domain);
+		status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+	}
 
 	return status;
 }
-- 
1.7.1


From a97c31b39b1f0a5f7a81fe8a2de1023edce3af9d Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Thu, 7 Jan 2016 13:27:49 -0700
Subject: [PATCH 8/8] Revert "winbind: Retry after SESSION_EXPIRED error in ping-dc"

This reverts commit a2670f15dea27c10e3827216adf572f9c3894f85.

cm_connect_netlogon now handles the retry for an expired session.

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source3/winbindd/winbindd_dual_srv.c |    8 --------
 1 files changed, 0 insertions(+), 8 deletions(-)

diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index 44e4842..cdd9bbd 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -741,14 +741,6 @@ NTSTATUS _wbint_PingDc(struct pipes_struct *p, struct wbint_PingDc *r)
 
 reconnect:
 	status = cm_connect_netlogon(domain, &netlogon_pipe);
-	if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
-		/*
-		 * Retry to open new connection with new kerberos ticket.
-		 */
-		invalidate_cm_connection(domain);
-		status = cm_connect_netlogon(domain, &netlogon_pipe);
-	}
-
 	reset_cm_connection_on_error(domain, status);
         if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(3, ("could not open handle to NETLOGON pipe: %s\n",
-- 
1.7.1



More information about the samba-technical mailing list