test: Test winbind idmap_ad ticket expiry behaviour
authorVolker Lendecke <vl@samba.org>
Thu, 13 Aug 2020 12:59:58 +0000 (14:59 +0200)
committerKarolin Seeger <kseeger@samba.org>
Mon, 31 Aug 2020 08:12:23 +0000 (08:12 +0000)
We need to make sure that winbind's idmap_ad deals fine with an
expired krb ticket used to connect to AD via LDAP. In a customer
situation we have seen the RFC4511 section 4.4.1 unsolicited ldap exop
response coming through, but the TCP disconnect that Windows seems to
do after that did not make it. Winbind deals fine with a TCP
disconnect, but right now it does not handle just the section 4.4.1
response properly: It completely hangs.

This test requests a ticket valid for 5 seconds and makes the LDAP
server postpone the TCP disconnect after the ticket expiry for 10
seconds. The tests that winbind reacts to the ticket expiry exop
response by making sure in this situation the wbinfo call running into
the issue takes less than 8 seconds. If it did not look at the expiry
exop response, it would take more than 10 seconds.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=14465
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
(cherry picked from commit a4ecd112e7754ab25bcae749594952a28c4c8905)

nsswitch/tests/test_ticket_expiry.sh [new file with mode: 0755]
selftest/knownfail.d/ticket_expiry [new file with mode: 0644]
selftest/target/Samba3.pm
selftest/target/Samba4.pm
source3/selftest/tests.py

diff --git a/nsswitch/tests/test_ticket_expiry.sh b/nsswitch/tests/test_ticket_expiry.sh
new file mode 100755 (executable)
index 0000000..3b98b0f
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+# Test winbind ad backend behaviour when the kerberos ticket expires
+
+if [ $# -ne 1 ]; then
+    echo Usage: $0 DOMAIN
+    exit 1
+fi
+
+DOMAIN="$1"
+
+wbinfo="$VALGRIND $BINDIR/wbinfo"
+net="$VALGRIND $BINDIR/net"
+
+failed=0
+
+. `dirname $0`/../../testprogs/blackbox/subunit.sh
+
+DOMAIN_SID=$($wbinfo -n "$DOMAIN/" | cut -f 1 -d " ")
+if [ $? -ne 0 ] ; then
+    echo "Could not find domain SID" | subunit_fail_test "test_idmap_ad"
+    exit 1
+fi
+ADMINS_SID="$DOMAIN_SID-512"
+
+# Previous tests might have put in a mapping
+$net cache del IDMAP/SID2XID/"$ADMINS_SID"
+
+# Trigger a winbind ad connection with a 5-second ticket lifetime,
+# see the smb.conf for the ad_member_idmap_ad environment we're in
+#
+# We expect failure here because there are no mappings in AD. In this
+# test we are only interested in the winbind LDAP connection as such,
+# we don't really care whether idmap_ad works fine. This is done in
+# different tests. And a negative lookup also triggers the LDAP
+# connection.
+
+testit_expect_failure "Deleting0 IDMAP/SID2XID/$ADMINS_SID" $net cache del IDMAP/SID2XID/"$ADMINS_SID" ||
+    failed=$(expr $failed + 1)
+
+testit_expect_failure "Expecting failure1, no mapping in AD" $wbinfo --sid-to-gid "$ADMINS_SID" ||
+    failed=$(expr $failed + 1)
+
+testit "Deleting1 IDMAP/SID2XID/$ADMINS_SID" $net cache del IDMAP/SID2XID/"$ADMINS_SID" ||
+    failed=$(expr $failed + 1)
+
+# allow our kerberos ticket to expire
+testit "Sleeping for 6 seconds" sleep 6 || failed=$(expr $failed + 1)
+
+# Try again, check how long it took to recover from ticket expiry
+#
+# On the LDAP connection two things happen: First we get an
+# unsolicited exop response telling us the network session was
+# abandoned, and secondly the LDAP server will kill the TCP
+# connection. Our ldap server is configured to defer the TCP
+# disconnect by 10 seconds. We need to make sure that winbind already
+# reacts to the unsolicited exop reply, discarding the connection. The
+# only way is to make sure the following wbinfo does not take too
+# long.
+
+# We need to do the test command in this funny way as on gitlab we're
+# using the bash builtin
+
+START=$(date +%s)
+testit_expect_failure "Expecting failure2, no mapping in AD" $wbinfo --sid-to-gid "$ADMINS_SID" ||
+    failed=$(expr $failed + 1)
+END=$(date +%s)
+DURATION=$(expr $END - $START)
+testit "timeout DURATION[$DURATION] < 8" test "$DURATION" -le 8 ||
+    failed=$(expr $failed + 1)
+
+testit "Deleting2 IDMAP/SID2XID/$ADMINS_SID" $net cache del IDMAP/SID2XID/"$ADMINS_SID" ||
+    failed=$(expr $failed + 1)
+
+exit $failed
diff --git a/selftest/knownfail.d/ticket_expiry b/selftest/knownfail.d/ticket_expiry
new file mode 100644 (file)
index 0000000..04e508a
--- /dev/null
@@ -0,0 +1 @@
+idmap_ad.ticket_expiry.timeout.DURATION.*\(ad_member_idmap_ad:local\)
index a15979199f0b8793b1016fbbe12d51707904d10c..c22c567cd65407d5d4597db7e405458efb15e2f2 100755 (executable)
@@ -728,6 +728,7 @@ sub setup_ad_member_idmap_ad
        idmap config $dcvars->{DOMAIN} : range = 2000000-2999999
        idmap config $dcvars->{TRUST_DOMAIN} : backend = ad
        idmap config $dcvars->{TRUST_DOMAIN} : range = 2000000-2999999
+       gensec_gssapi:requested_life_time = 5
 ";
 
        my $ret = $self->provision($prefix, $dcvars->{DOMAIN},
index a599099c59db177671e0dbd1db11f231f5141667..4758a5513d1402663b3a0e478e98bb1c87cf79d0 100755 (executable)
@@ -1709,7 +1709,11 @@ sub provision_fl2008r2dc($$$)
        my ($self, $prefix, $dcvars) = @_;
 
        print "PROVISIONING DC WITH FOREST LEVEL 2008r2...\n";
-        my $extra_conf_options = "ldap server require strong auth = no";
+        my $extra_conf_options = "
+       ldap server require strong auth = no
+        # delay by 10 seconds, 10^7 usecs
+       ldap_server:delay_expire_disconnect = 10000
+";
        my $extra_provision_options = ["--use-ntvfs", "--base-schema=2008_R2"];
        my $ret = $self->provision($prefix,
                                   "domain controller",
index 817fc783062aa7a88a898cb4b6e1a4d3ff89e22c..430a7e327f2a2c5a1effed544a96340165c57e07 100755 (executable)
@@ -719,6 +719,11 @@ for t in tests:
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
 
+plantestsuite(
+    "idmap_ad.ticket_expiry",
+    "ad_member_idmap_ad:local",
+    [os.path.join(samba3srcdir, "../nsswitch/tests/test_ticket_expiry.sh"),
+     '$DOMAIN'])
 
 test = 'rpc.lsa.lookupsids'
 auth_options = ["", "ntlm", "spnego", "spnego,ntlm", "spnego,smb1", "spnego,smb2"]