17966 lines
562 KiB
Diff
17966 lines
562 KiB
Diff
From 3c29fc78029e1274f931e171c9e04c19ad0182c1 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Thu, 17 Aug 2023 01:05:54 +0300
|
|
Subject: [PATCH 001/122] gp: Support more global trust directories
|
|
|
|
In addition to the SUSE global trust directory, add support for RHEL and
|
|
Debian-based distributions (including Ubuntu).
|
|
|
|
To determine the correct directory to use, we iterate over the variants
|
|
and stop at the first which is a directory.
|
|
|
|
In case none is found, fallback to the first option which will produce a
|
|
warning as it did previously.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit a1b285e485c0b5a8747499bdbbb9f3f4fc025b2f)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 12 +++++++++++-
|
|
1 file changed, 11 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 312c8ddf467..1b90ab46e90 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -45,10 +45,12 @@ cert_wrap = b"""
|
|
-----BEGIN CERTIFICATE-----
|
|
%s
|
|
-----END CERTIFICATE-----"""
|
|
-global_trust_dir = '/etc/pki/trust/anchors'
|
|
endpoint_re = '(https|HTTPS)://(?P<server>[a-zA-Z0-9.-]+)/ADPolicyProvider' + \
|
|
'_CEP_(?P<auth>[a-zA-Z]+)/service.svc/CEP'
|
|
|
|
+global_trust_dirs = ['/etc/pki/trust/anchors', # SUSE
|
|
+ '/etc/pki/ca-trust/source/anchors', # RHEL/Fedora
|
|
+ '/usr/local/share/ca-certificates'] # Debian/Ubuntu
|
|
|
|
def octet_string_to_objectGUID(data):
|
|
"""Convert an octet string to an objectGUID."""
|
|
@@ -249,12 +251,20 @@ def getca(ca, url, trust_dir):
|
|
return root_certs
|
|
|
|
|
|
+def find_global_trust_dir():
|
|
+ """Return the global trust dir using known paths from various Linux distros."""
|
|
+ for trust_dir in global_trust_dirs:
|
|
+ if os.path.isdir(trust_dir):
|
|
+ return trust_dir
|
|
+ return global_trust_dirs[0]
|
|
+
|
|
def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
"""Install the root certificate chain."""
|
|
data = dict({'files': [], 'templates': []}, **ca)
|
|
url = 'http://%s/CertSrv/mscep/mscep.dll/pkiclient.exe?' % ca['hostname']
|
|
root_certs = getca(ca, url, trust_dir)
|
|
data['files'].extend(root_certs)
|
|
+ global_trust_dir = find_global_trust_dir()
|
|
for src in root_certs:
|
|
# Symlink the certs to global trust dir
|
|
dst = os.path.join(global_trust_dir, os.path.basename(src))
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 063606e8ec83a58972df47eb561ab267f8937ba4 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Thu, 17 Aug 2023 01:09:28 +0300
|
|
Subject: [PATCH 002/122] gp: Support update-ca-trust helper
|
|
|
|
This is used on RHEL/Fedora instead of update-ca-certificates. They
|
|
behave similarly so it's enough to change the command name.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit fa80d1d86439749c44e60cf9075e84dc9ed3c268)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 6 +++++-
|
|
1 file changed, 5 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 1b90ab46e90..cefdafa21b2 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -258,6 +258,10 @@ def find_global_trust_dir():
|
|
return trust_dir
|
|
return global_trust_dirs[0]
|
|
|
|
+def update_ca_command():
|
|
+ """Return the command to update the CA trust store."""
|
|
+ return which('update-ca-certificates') or which('update-ca-trust')
|
|
+
|
|
def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
"""Install the root certificate chain."""
|
|
data = dict({'files': [], 'templates': []}, **ca)
|
|
@@ -283,7 +287,7 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
# already exists. Ignore the FileExistsError. Preserve the
|
|
# existing symlink in the unapply data.
|
|
data['files'].append(dst)
|
|
- update = which('update-ca-certificates')
|
|
+ update = update_ca_command()
|
|
if update is not None:
|
|
Popen([update]).wait()
|
|
# Setup Certificate Auto Enrollment
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 3b548bf280ca59ef12a7af10a9131813067a850a Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Fri, 11 Aug 2023 18:46:42 +0300
|
|
Subject: [PATCH 003/122] gp: Change root cert extension suffix
|
|
|
|
On Ubuntu, certificates must end in '.crt' in order to be considered by
|
|
the `update-ca-certificates` helper.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit bce3a89204545dcab5fb39a712590f6e166f997b)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 3 ++-
|
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index cefdafa21b2..c562722906b 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -241,7 +241,8 @@ def getca(ca, url, trust_dir):
|
|
certs = load_der_pkcs7_certificates(r.content)
|
|
for i in range(0, len(certs)):
|
|
cert = certs[i].public_bytes(Encoding.PEM)
|
|
- dest = '%s.%d' % (root_cert, i)
|
|
+ filename, extension = root_cert.rsplit('.', 1)
|
|
+ dest = '%s.%d.%s' % (filename, i, extension)
|
|
with open(dest, 'wb') as w:
|
|
w.write(cert)
|
|
root_certs.append(dest)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7592ed5032836dc43f657f66607a0a4661edcdb4 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Fri, 18 Aug 2023 17:06:43 +0300
|
|
Subject: [PATCH 004/122] gp: Test with binary content for certificate data
|
|
|
|
This fails all GPO-related tests that call `gpupdate --rsop`.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit 1ef722cf66f9ec99f52939f1cfca031c5fe1ad70)
|
|
---
|
|
python/samba/tests/gpo.py | 8 ++++----
|
|
selftest/knownfail.d/gpo | 13 +++++++++++++
|
|
2 files changed, 17 insertions(+), 4 deletions(-)
|
|
create mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
|
|
index e4b75cc62a4..963f873f755 100644
|
|
--- a/python/samba/tests/gpo.py
|
|
+++ b/python/samba/tests/gpo.py
|
|
@@ -6783,14 +6783,14 @@ class GPOTests(tests.TestCase):
|
|
ldb.add({'dn': certa_dn,
|
|
'objectClass': 'certificationAuthority',
|
|
'authorityRevocationList': ['XXX'],
|
|
- 'cACertificate': 'XXX',
|
|
+ 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
'certificateRevocationList': ['XXX'],
|
|
})
|
|
# Write the dummy pKIEnrollmentService
|
|
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
|
|
ldb.add({'dn': enroll_dn,
|
|
'objectClass': 'pKIEnrollmentService',
|
|
- 'cACertificate': 'XXXX',
|
|
+ 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
'certificateTemplates': ['Machine'],
|
|
'dNSHostName': hostname,
|
|
})
|
|
@@ -7201,14 +7201,14 @@ class GPOTests(tests.TestCase):
|
|
ldb.add({'dn': certa_dn,
|
|
'objectClass': 'certificationAuthority',
|
|
'authorityRevocationList': ['XXX'],
|
|
- 'cACertificate': 'XXX',
|
|
+ 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
'certificateRevocationList': ['XXX'],
|
|
})
|
|
# Write the dummy pKIEnrollmentService
|
|
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
|
|
ldb.add({'dn': enroll_dn,
|
|
'objectClass': 'pKIEnrollmentService',
|
|
- 'cACertificate': 'XXXX',
|
|
+ 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
'certificateTemplates': ['Machine'],
|
|
'dNSHostName': hostname,
|
|
})
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
new file mode 100644
|
|
index 00000000000..0aad59607c2
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/gpo
|
|
@@ -0,0 +1,13 @@
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_user_centrify_crontab_ext
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_user_scripts_ext
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_rsop
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_access
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_files
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_issue
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_motd
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_openssh
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_startup_scripts
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_sudoers
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_symlink
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_advanced_gp_cert_auto_enroll_ext
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7f7b235bda9e85c5ea330e52e734d1113a884571 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Wed, 16 Aug 2023 12:20:11 +0300
|
|
Subject: [PATCH 005/122] gp: Convert CA certificates to base64
|
|
|
|
I don't know whether this applies universally, but in our case the
|
|
contents of `es['cACertificate'][0]` are binary, so cleanly converting
|
|
to a string fails with the following:
|
|
|
|
'utf-8' codec can't decode byte 0x82 in position 1: invalid start byte
|
|
|
|
We found a fix to be encoding the certificate to base64 when
|
|
constructing the CA list.
|
|
|
|
Section 4.4.5.2 of MS-CAESO also suggests that the content of
|
|
`cACertificate` is binary (OCTET string).
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit 157335ee93eb866f9b6a47486a5668d6e76aced5)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 5 ++---
|
|
selftest/knownfail.d/gpo | 13 -------------
|
|
2 files changed, 2 insertions(+), 16 deletions(-)
|
|
delete mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index c562722906b..c8b5368c16a 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -158,7 +158,7 @@ def fetch_certification_authorities(ldb):
|
|
for es in res:
|
|
data = { 'name': get_string(es['cn'][0]),
|
|
'hostname': get_string(es['dNSHostName'][0]),
|
|
- 'cACertificate': get_string(es['cACertificate'][0])
|
|
+ 'cACertificate': get_string(base64.b64encode(es['cACertificate'][0]))
|
|
}
|
|
result.append(data)
|
|
return result
|
|
@@ -176,8 +176,7 @@ def fetch_template_attrs(ldb, name, attrs=None):
|
|
return {'msPKI-Minimal-Key-Size': ['2048']}
|
|
|
|
def format_root_cert(cert):
|
|
- cert = base64.b64encode(cert.encode())
|
|
- return cert_wrap % re.sub(b"(.{64})", b"\\1\n", cert, 0, re.DOTALL)
|
|
+ return cert_wrap % re.sub(b"(.{64})", b"\\1\n", cert.encode(), 0, re.DOTALL)
|
|
|
|
def find_cepces_submit():
|
|
certmonger_dirs = [os.environ.get("PATH"), '/usr/lib/certmonger',
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
deleted file mode 100644
|
|
index 0aad59607c2..00000000000
|
|
--- a/selftest/knownfail.d/gpo
|
|
+++ /dev/null
|
|
@@ -1,13 +0,0 @@
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_user_centrify_crontab_ext
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_user_scripts_ext
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_rsop
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_access
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_files
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_issue
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_motd
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_openssh
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_startup_scripts
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_sudoers
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_vgp_symlink
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_advanced_gp_cert_auto_enroll_ext
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 49cc74015a603e80048a38fe635cd1ac28938ee4 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Fri, 18 Aug 2023 17:16:23 +0300
|
|
Subject: [PATCH 006/122] gp: Test adding new cert templates enforces changes
|
|
|
|
Ensure that cepces-submit reporting additional templates and re-applying
|
|
will enforce the updated policy.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit 2d6943a864405f324c467e8c3464c31ac08457b0)
|
|
---
|
|
python/samba/tests/bin/cepces-submit | 3 +-
|
|
python/samba/tests/gpo.py | 48 ++++++++++++++++++++++++++++
|
|
selftest/knownfail.d/gpo | 2 ++
|
|
3 files changed, 52 insertions(+), 1 deletion(-)
|
|
create mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/tests/bin/cepces-submit b/python/samba/tests/bin/cepces-submit
|
|
index 668682a9f58..de63164692b 100755
|
|
--- a/python/samba/tests/bin/cepces-submit
|
|
+++ b/python/samba/tests/bin/cepces-submit
|
|
@@ -14,4 +14,5 @@ if __name__ == "__main__":
|
|
assert opts.auth == 'Kerberos'
|
|
if 'CERTMONGER_OPERATION' in os.environ and \
|
|
os.environ['CERTMONGER_OPERATION'] == 'GET-SUPPORTED-TEMPLATES':
|
|
- print('Machine') # Report a Machine template
|
|
+ templates = os.environ.get('CEPCES_SUBMIT_SUPPORTED_TEMPLATES', 'Machine').split(',')
|
|
+ print('\n'.join(templates)) # Report the requested templates
|
|
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
|
|
index 963f873f755..e75c411bde7 100644
|
|
--- a/python/samba/tests/gpo.py
|
|
+++ b/python/samba/tests/gpo.py
|
|
@@ -6812,6 +6812,23 @@ class GPOTests(tests.TestCase):
|
|
self.assertTrue(os.path.exists(machine_crt),
|
|
'Machine key was not generated')
|
|
|
|
+ # Subsequent apply should react to new certificate templates
|
|
+ os.environ['CEPCES_SUBMIT_SUPPORTED_TEMPLATES'] = 'Machine,Workstation'
|
|
+ self.addCleanup(os.environ.pop, 'CEPCES_SUBMIT_SUPPORTED_TEMPLATES')
|
|
+ ext.process_group_policy([], gpos, dname, dname)
|
|
+ self.assertTrue(os.path.exists(ca_crt),
|
|
+ 'Root CA certificate was not requested')
|
|
+ self.assertTrue(os.path.exists(machine_crt),
|
|
+ 'Machine certificate was not requested')
|
|
+ self.assertTrue(os.path.exists(machine_crt),
|
|
+ 'Machine key was not generated')
|
|
+ workstation_crt = os.path.join(dname, '%s.Workstation.crt' % ca_cn)
|
|
+ self.assertTrue(os.path.exists(workstation_crt),
|
|
+ 'Workstation certificate was not requested')
|
|
+ workstation_key = os.path.join(dname, '%s.Workstation.key' % ca_cn)
|
|
+ self.assertTrue(os.path.exists(workstation_crt),
|
|
+ 'Workstation key was not generated')
|
|
+
|
|
# Verify RSOP does not fail
|
|
ext.rsop([g for g in gpos if g.name == guid][0])
|
|
|
|
@@ -6829,11 +6846,17 @@ class GPOTests(tests.TestCase):
|
|
'Machine certificate was not removed')
|
|
self.assertFalse(os.path.exists(machine_crt),
|
|
'Machine key was not removed')
|
|
+ self.assertFalse(os.path.exists(workstation_crt),
|
|
+ 'Workstation certificate was not removed')
|
|
+ self.assertFalse(os.path.exists(workstation_crt),
|
|
+ 'Workstation key was not removed')
|
|
out, _ = Popen(['getcert', 'list-cas'], stdout=PIPE).communicate()
|
|
self.assertNotIn(get_bytes(ca_cn), out, 'CA was not removed')
|
|
out, _ = Popen(['getcert', 'list'], stdout=PIPE).communicate()
|
|
self.assertNotIn(b'Machine', out,
|
|
'Machine certificate not removed')
|
|
+ self.assertNotIn(b'Workstation', out,
|
|
+ 'Workstation certificate not removed')
|
|
|
|
# Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
|
|
ldb.delete(certa_dn)
|
|
@@ -7233,6 +7256,25 @@ class GPOTests(tests.TestCase):
|
|
self.assertTrue(os.path.exists(machine_crt),
|
|
'Machine key was not generated')
|
|
|
|
+ # Subsequent apply should react to new certificate templates
|
|
+ os.environ['CEPCES_SUBMIT_SUPPORTED_TEMPLATES'] = 'Machine,Workstation'
|
|
+ self.addCleanup(os.environ.pop, 'CEPCES_SUBMIT_SUPPORTED_TEMPLATES')
|
|
+ ext.process_group_policy([], gpos, dname, dname)
|
|
+ for ca in ca_list:
|
|
+ self.assertTrue(os.path.exists(ca_crt),
|
|
+ 'Root CA certificate was not requested')
|
|
+ self.assertTrue(os.path.exists(machine_crt),
|
|
+ 'Machine certificate was not requested')
|
|
+ self.assertTrue(os.path.exists(machine_crt),
|
|
+ 'Machine key was not generated')
|
|
+
|
|
+ workstation_crt = os.path.join(dname, '%s.Workstation.crt' % ca)
|
|
+ self.assertTrue(os.path.exists(workstation_crt),
|
|
+ 'Workstation certificate was not requested')
|
|
+ workstation_key = os.path.join(dname, '%s.Workstation.key' % ca)
|
|
+ self.assertTrue(os.path.exists(workstation_crt),
|
|
+ 'Workstation key was not generated')
|
|
+
|
|
# Verify RSOP does not fail
|
|
ext.rsop([g for g in gpos if g.name == guid][0])
|
|
|
|
@@ -7250,12 +7292,18 @@ class GPOTests(tests.TestCase):
|
|
'Machine certificate was not removed')
|
|
self.assertFalse(os.path.exists(machine_crt),
|
|
'Machine key was not removed')
|
|
+ self.assertFalse(os.path.exists(workstation_crt),
|
|
+ 'Workstation certificate was not removed')
|
|
+ self.assertFalse(os.path.exists(workstation_crt),
|
|
+ 'Workstation key was not removed')
|
|
out, _ = Popen(['getcert', 'list-cas'], stdout=PIPE).communicate()
|
|
for ca in ca_list:
|
|
self.assertNotIn(get_bytes(ca), out, 'CA was not removed')
|
|
out, _ = Popen(['getcert', 'list'], stdout=PIPE).communicate()
|
|
self.assertNotIn(b'Machine', out,
|
|
'Machine certificate not removed')
|
|
+ self.assertNotIn(b'Workstation', out,
|
|
+ 'Workstation certificate not removed')
|
|
|
|
# Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
|
|
ldb.delete(certa_dn)
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
new file mode 100644
|
|
index 00000000000..4edc1dce730
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/gpo
|
|
@@ -0,0 +1,2 @@
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_advanced_gp_cert_auto_enroll_ext
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4c0906bd79f030e591701234bc54bc749a42d686 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Wed, 16 Aug 2023 12:37:17 +0300
|
|
Subject: [PATCH 007/122] gp: Template changes should invalidate cache
|
|
|
|
If certificate templates are added or removed, the autoenroll extension
|
|
should react to this and reapply the policy. Previously this wasn't
|
|
taken into account.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit 2a6ae997f2464b12b72b5314fa80d9784fb0f6c1)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 15 ++++++++++-----
|
|
selftest/knownfail.d/gpo | 2 --
|
|
2 files changed, 10 insertions(+), 7 deletions(-)
|
|
delete mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index c8b5368c16a..8233713e8ad 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -262,6 +262,11 @@ def update_ca_command():
|
|
"""Return the command to update the CA trust store."""
|
|
return which('update-ca-certificates') or which('update-ca-trust')
|
|
|
|
+def changed(new_data, old_data):
|
|
+ """Return True if any key present in both dicts has changed."""
|
|
+ return any((new_data[k] != old_data[k] if k in old_data else False) \
|
|
+ for k in new_data.keys())
|
|
+
|
|
def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
"""Install the root certificate chain."""
|
|
data = dict({'files': [], 'templates': []}, **ca)
|
|
@@ -351,12 +356,12 @@ class gp_cert_auto_enroll_ext(gp_pol_ext, gp_applier):
|
|
# If the policy has changed, unapply, then apply new policy
|
|
old_val = self.cache_get_attribute_value(guid, attribute)
|
|
old_data = json.loads(old_val) if old_val is not None else {}
|
|
- if all([(ca[k] == old_data[k] if k in old_data else False) \
|
|
- for k in ca.keys()]) or \
|
|
- self.cache_get_apply_state() == GPOSTATE.ENFORCE:
|
|
+ templates = ['%s.%s' % (ca['name'], t.decode()) for t in get_supported_templates(ca['hostname'])]
|
|
+ new_data = { 'templates': templates, **ca }
|
|
+ if changed(new_data, old_data) or self.cache_get_apply_state() == GPOSTATE.ENFORCE:
|
|
self.unapply(guid, attribute, old_val)
|
|
- # If policy is already applied, skip application
|
|
- if old_val is not None and \
|
|
+ # If policy is already applied and unchanged, skip application
|
|
+ if old_val is not None and not changed(new_data, old_data) and \
|
|
self.cache_get_apply_state() != GPOSTATE.ENFORCE:
|
|
return
|
|
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
deleted file mode 100644
|
|
index 4edc1dce730..00000000000
|
|
--- a/selftest/knownfail.d/gpo
|
|
+++ /dev/null
|
|
@@ -1,2 +0,0 @@
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_advanced_gp_cert_auto_enroll_ext
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e61f30dc2518d5a1c239f090baea4a309307f3f8 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Fri, 18 Aug 2023 17:26:59 +0300
|
|
Subject: [PATCH 008/122] gp: Test disabled enrollment unapplies policy
|
|
|
|
For this we need to stage a Registry.pol file with certificate
|
|
autoenrollment enabled, but with checkboxes unticked.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit ee814f7707a8ddef2657212cd6d31799501b7bb3)
|
|
---
|
|
python/samba/tests/gpo.py | 54 +++++++++++++++++++++++++++++++++++++++
|
|
selftest/knownfail.d/gpo | 1 +
|
|
2 files changed, 55 insertions(+)
|
|
create mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
|
|
index e75c411bde7..580f3568de8 100644
|
|
--- a/python/samba/tests/gpo.py
|
|
+++ b/python/samba/tests/gpo.py
|
|
@@ -281,6 +281,28 @@ b"""
|
|
</PolFile>
|
|
"""
|
|
|
|
+auto_enroll_unchecked_reg_pol = \
|
|
+b"""
|
|
+<?xml version="1.0" encoding="utf-8"?>
|
|
+<PolFile num_entries="3" signature="PReg" version="1">
|
|
+ <Entry type="4" type_name="REG_DWORD">
|
|
+ <Key>Software\Policies\Microsoft\Cryptography\AutoEnrollment</Key>
|
|
+ <ValueName>AEPolicy</ValueName>
|
|
+ <Value>0</Value>
|
|
+ </Entry>
|
|
+ <Entry type="4" type_name="REG_DWORD">
|
|
+ <Key>Software\Policies\Microsoft\Cryptography\AutoEnrollment</Key>
|
|
+ <ValueName>OfflineExpirationPercent</ValueName>
|
|
+ <Value>10</Value>
|
|
+ </Entry>
|
|
+ <Entry type="1" type_name="REG_SZ">
|
|
+ <Key>Software\Policies\Microsoft\Cryptography\AutoEnrollment</Key>
|
|
+ <ValueName>OfflineExpirationStoreNames</ValueName>
|
|
+ <Value>MY</Value>
|
|
+ </Entry>
|
|
+</PolFile>
|
|
+"""
|
|
+
|
|
advanced_enroll_reg_pol = \
|
|
b"""
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
@@ -6836,6 +6858,38 @@ class GPOTests(tests.TestCase):
|
|
ret = rsop(self.lp)
|
|
self.assertEqual(ret, 0, 'gpupdate --rsop failed!')
|
|
|
|
+ # Remove policy by staging pol file with auto-enroll unchecked
|
|
+ parser.load_xml(etree.fromstring(auto_enroll_unchecked_reg_pol.strip()))
|
|
+ ret = stage_file(reg_pol, ndr_pack(parser.pol_file))
|
|
+ self.assertTrue(ret, 'Could not create the target %s' % reg_pol)
|
|
+ ext.process_group_policy([], gpos, dname, dname)
|
|
+ self.assertFalse(os.path.exists(ca_crt),
|
|
+ 'Root CA certificate was not removed')
|
|
+ self.assertFalse(os.path.exists(machine_crt),
|
|
+ 'Machine certificate was not removed')
|
|
+ self.assertFalse(os.path.exists(machine_crt),
|
|
+ 'Machine key was not removed')
|
|
+ self.assertFalse(os.path.exists(workstation_crt),
|
|
+ 'Workstation certificate was not removed')
|
|
+ self.assertFalse(os.path.exists(workstation_crt),
|
|
+ 'Workstation key was not removed')
|
|
+
|
|
+ # Reapply policy by staging the enabled pol file
|
|
+ parser.load_xml(etree.fromstring(auto_enroll_reg_pol.strip()))
|
|
+ ret = stage_file(reg_pol, ndr_pack(parser.pol_file))
|
|
+ self.assertTrue(ret, 'Could not create the target %s' % reg_pol)
|
|
+ ext.process_group_policy([], gpos, dname, dname)
|
|
+ self.assertTrue(os.path.exists(ca_crt),
|
|
+ 'Root CA certificate was not requested')
|
|
+ self.assertTrue(os.path.exists(machine_crt),
|
|
+ 'Machine certificate was not requested')
|
|
+ self.assertTrue(os.path.exists(machine_crt),
|
|
+ 'Machine key was not generated')
|
|
+ self.assertTrue(os.path.exists(workstation_crt),
|
|
+ 'Workstation certificate was not requested')
|
|
+ self.assertTrue(os.path.exists(workstation_crt),
|
|
+ 'Workstation key was not generated')
|
|
+
|
|
# Remove policy
|
|
gp_db = store.get_gplog(machine_creds.get_username())
|
|
del_gpos = get_deleted_gpos_list(gp_db, [])
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
new file mode 100644
|
|
index 00000000000..83bc9f0ac1f
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/gpo
|
|
@@ -0,0 +1 @@
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7757b9b48546d71e19798d1260da97780caa99c3 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Wed, 16 Aug 2023 12:33:59 +0300
|
|
Subject: [PATCH 009/122] gp: Send list of keys instead of dict to remove
|
|
|
|
`cache_get_all_attribute_values` returns a dict whereas we need to pass
|
|
a list of keys to `remove`. These will be interpolated in the gpdb search.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
|
|
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
|
|
Autobuild-Date(master): Mon Aug 28 03:01:22 UTC 2023 on atb-devel-224
|
|
|
|
(cherry picked from commit 7dc181757c76b881ceaf1915ebb0bfbcf5aca83a)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 2 +-
|
|
selftest/knownfail.d/gpo | 1 -
|
|
2 files changed, 1 insertion(+), 2 deletions(-)
|
|
delete mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 8233713e8ad..64c35782ae8 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -415,7 +415,7 @@ class gp_cert_auto_enroll_ext(gp_pol_ext, gp_applier):
|
|
# remove any existing policy
|
|
ca_attrs = \
|
|
self.cache_get_all_attribute_values(gpo.name)
|
|
- self.clean(gpo.name, remove=ca_attrs)
|
|
+ self.clean(gpo.name, remove=list(ca_attrs.keys()))
|
|
|
|
def __read_cep_data(self, guid, ldb, end_point_information,
|
|
trust_dir, private_dir):
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
deleted file mode 100644
|
|
index 83bc9f0ac1f..00000000000
|
|
--- a/selftest/knownfail.d/gpo
|
|
+++ /dev/null
|
|
@@ -1 +0,0 @@
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4e9b2e6409c5764ec0e66cc6c90b08e70f702e7c Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Tue, 9 Jan 2024 08:50:01 +0100
|
|
Subject: [PATCH 010/122] python:gp: Print a nice message if cepces-submit
|
|
can't be found
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15552
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
(cherry picked from commit 8eb42425a8eb1b30ca0e94dfc01d8175ae5cde4b)
|
|
|
|
Autobuild-User(v4-19-test): Jule Anger <janger@samba.org>
|
|
Autobuild-Date(v4-19-test): Mon Jan 15 11:11:31 UTC 2024 on atb-devel-224
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 24 ++++++++++++----------
|
|
1 file changed, 13 insertions(+), 11 deletions(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 64c35782ae8..08d1a7348cd 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -185,17 +185,19 @@ def find_cepces_submit():
|
|
|
|
def get_supported_templates(server):
|
|
cepces_submit = find_cepces_submit()
|
|
- if os.path.exists(cepces_submit):
|
|
- env = os.environ
|
|
- env['CERTMONGER_OPERATION'] = 'GET-SUPPORTED-TEMPLATES'
|
|
- p = Popen([cepces_submit, '--server=%s' % server, '--auth=Kerberos'],
|
|
- env=env, stdout=PIPE, stderr=PIPE)
|
|
- out, err = p.communicate()
|
|
- if p.returncode != 0:
|
|
- data = { 'Error': err.decode() }
|
|
- log.error('Failed to fetch the list of supported templates.', data)
|
|
- return out.strip().split()
|
|
- return []
|
|
+ if not cepces_submit or not os.path.exists(cepces_submit):
|
|
+ log.error('Failed to find cepces-submit')
|
|
+ return []
|
|
+
|
|
+ env = os.environ
|
|
+ env['CERTMONGER_OPERATION'] = 'GET-SUPPORTED-TEMPLATES'
|
|
+ p = Popen([cepces_submit, '--server=%s' % server, '--auth=Kerberos'],
|
|
+ env=env, stdout=PIPE, stderr=PIPE)
|
|
+ out, err = p.communicate()
|
|
+ if p.returncode != 0:
|
|
+ data = {'Error': err.decode()}
|
|
+ log.error('Failed to fetch the list of supported templates.', data)
|
|
+ return out.strip().split()
|
|
|
|
|
|
def getca(ca, url, trust_dir):
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From fb3aefff51c02cf8ba3f8dfeb7d3f971e8d4902a Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Mon, 8 Jan 2024 18:05:08 +0200
|
|
Subject: [PATCH 011/122] gpo: Test certificate policy without NDES
|
|
|
|
As of 8231eaf856b, the NDES feature is no longer required on Windows, as
|
|
cert auto-enroll can use the certificate from the LDAP request.
|
|
|
|
However, 157335ee93e changed the implementation to convert the LDAP
|
|
certificate to base64 due to it failing to cleanly convert to a string.
|
|
|
|
Because of insufficient test coverage I missed handling the part where
|
|
NDES is disabled or not reachable and the LDAP certificate was imported.
|
|
The call to load_der_x509_certificate now fails with an error because it
|
|
expects binary data, yet it receives a base64 encoded string.
|
|
|
|
This adds a test to confirm the issue.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15557
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit 0d1ff69936f18ea729fc11fbbb1569a833302572)
|
|
---
|
|
python/samba/tests/gpo.py | 126 ++++++++++++++++++++++++++++++++++++--
|
|
selftest/knownfail.d/gpo | 1 +
|
|
2 files changed, 121 insertions(+), 6 deletions(-)
|
|
create mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
|
|
index 580f3568de8..a78af17dba4 100644
|
|
--- a/python/samba/tests/gpo.py
|
|
+++ b/python/samba/tests/gpo.py
|
|
@@ -102,17 +102,21 @@ def dummy_certificate():
|
|
|
|
# Dummy requests structure for Certificate Auto Enrollment
|
|
class dummy_requests(object):
|
|
- @staticmethod
|
|
- def get(url=None, params=None):
|
|
+ class exceptions(object):
|
|
+ ConnectionError = Exception
|
|
+
|
|
+ def __init__(self, want_exception=False):
|
|
+ self.want_exception = want_exception
|
|
+
|
|
+ def get(self, url=None, params=None):
|
|
+ if self.want_exception:
|
|
+ raise self.exceptions.ConnectionError
|
|
+
|
|
dummy = requests.Response()
|
|
dummy._content = dummy_certificate()
|
|
dummy.headers = {'Content-Type': 'application/x-x509-ca-cert'}
|
|
return dummy
|
|
|
|
- class exceptions(object):
|
|
- ConnectionError = Exception
|
|
-cae.requests = dummy_requests
|
|
-
|
|
realm = os.environ.get('REALM')
|
|
policies = realm + '/POLICIES'
|
|
realm = realm.lower()
|
|
@@ -6764,6 +6768,114 @@ class GPOTests(tests.TestCase):
|
|
# Unstage the Registry.pol file
|
|
unstage_file(reg_pol)
|
|
|
|
+ def test_gp_cert_auto_enroll_ext_without_ndes(self):
|
|
+ local_path = self.lp.cache_path('gpo_cache')
|
|
+ guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}'
|
|
+ reg_pol = os.path.join(local_path, policies, guid,
|
|
+ 'MACHINE/REGISTRY.POL')
|
|
+ cache_dir = self.lp.get('cache directory')
|
|
+ store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
|
|
+
|
|
+ machine_creds = Credentials()
|
|
+ machine_creds.guess(self.lp)
|
|
+ machine_creds.set_machine_account()
|
|
+
|
|
+ # Initialize the group policy extension
|
|
+ cae.requests = dummy_requests(want_exception=True)
|
|
+ ext = cae.gp_cert_auto_enroll_ext(self.lp, machine_creds,
|
|
+ machine_creds.get_username(), store)
|
|
+
|
|
+ gpos = get_gpo_list(self.server, machine_creds, self.lp,
|
|
+ machine_creds.get_username())
|
|
+
|
|
+ # Stage the Registry.pol file with test data
|
|
+ parser = GPPolParser()
|
|
+ parser.load_xml(etree.fromstring(auto_enroll_reg_pol.strip()))
|
|
+ ret = stage_file(reg_pol, ndr_pack(parser.pol_file))
|
|
+ self.assertTrue(ret, 'Could not create the target %s' % reg_pol)
|
|
+
|
|
+ # Write the dummy CA entry, Enrollment Services, and Templates Entries
|
|
+ admin_creds = Credentials()
|
|
+ admin_creds.set_username(os.environ.get('DC_USERNAME'))
|
|
+ admin_creds.set_password(os.environ.get('DC_PASSWORD'))
|
|
+ admin_creds.set_realm(os.environ.get('REALM'))
|
|
+ hostname = get_dc_hostname(machine_creds, self.lp)
|
|
+ url = 'ldap://%s' % hostname
|
|
+ ldb = Ldb(url=url, session_info=system_session(),
|
|
+ lp=self.lp, credentials=admin_creds)
|
|
+ # Write the dummy CA
|
|
+ confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
|
|
+ ca_cn = '%s-CA' % hostname.replace('.', '-')
|
|
+ certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
|
|
+ ldb.add({'dn': certa_dn,
|
|
+ 'objectClass': 'certificationAuthority',
|
|
+ 'authorityRevocationList': ['XXX'],
|
|
+ 'cACertificate': dummy_certificate(),
|
|
+ 'certificateRevocationList': ['XXX'],
|
|
+ })
|
|
+ # Write the dummy pKIEnrollmentService
|
|
+ enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
|
|
+ ldb.add({'dn': enroll_dn,
|
|
+ 'objectClass': 'pKIEnrollmentService',
|
|
+ 'cACertificate': dummy_certificate(),
|
|
+ 'certificateTemplates': ['Machine'],
|
|
+ 'dNSHostName': hostname,
|
|
+ })
|
|
+ # Write the dummy pKICertificateTemplate
|
|
+ template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
|
|
+ ldb.add({'dn': template_dn,
|
|
+ 'objectClass': 'pKICertificateTemplate',
|
|
+ })
|
|
+
|
|
+ with TemporaryDirectory() as dname:
|
|
+ try:
|
|
+ ext.process_group_policy([], gpos, dname, dname)
|
|
+ except Exception as e:
|
|
+ self.fail(str(e))
|
|
+
|
|
+ ca_crt = os.path.join(dname, '%s.crt' % ca_cn)
|
|
+ self.assertTrue(os.path.exists(ca_crt),
|
|
+ 'Root CA certificate was not requested')
|
|
+ machine_crt = os.path.join(dname, '%s.Machine.crt' % ca_cn)
|
|
+ self.assertTrue(os.path.exists(machine_crt),
|
|
+ 'Machine certificate was not requested')
|
|
+ machine_key = os.path.join(dname, '%s.Machine.key' % ca_cn)
|
|
+ self.assertTrue(os.path.exists(machine_key),
|
|
+ 'Machine key was not generated')
|
|
+
|
|
+ # Verify RSOP does not fail
|
|
+ ext.rsop([g for g in gpos if g.name == guid][0])
|
|
+
|
|
+ # Check that a call to gpupdate --rsop also succeeds
|
|
+ ret = rsop(self.lp)
|
|
+ self.assertEqual(ret, 0, 'gpupdate --rsop failed!')
|
|
+
|
|
+ # Remove policy
|
|
+ gp_db = store.get_gplog(machine_creds.get_username())
|
|
+ del_gpos = get_deleted_gpos_list(gp_db, [])
|
|
+ ext.process_group_policy(del_gpos, [], dname)
|
|
+ self.assertFalse(os.path.exists(ca_crt),
|
|
+ 'Root CA certificate was not removed')
|
|
+ self.assertFalse(os.path.exists(machine_crt),
|
|
+ 'Machine certificate was not removed')
|
|
+ self.assertFalse(os.path.exists(machine_key),
|
|
+ 'Machine key was not removed')
|
|
+ out, _ = Popen(['getcert', 'list-cas'], stdout=PIPE).communicate()
|
|
+ self.assertNotIn(get_bytes(ca_cn), out, 'CA was not removed')
|
|
+ out, _ = Popen(['getcert', 'list'], stdout=PIPE).communicate()
|
|
+ self.assertNotIn(b'Machine', out,
|
|
+ 'Machine certificate not removed')
|
|
+ self.assertNotIn(b'Workstation', out,
|
|
+ 'Workstation certificate not removed')
|
|
+
|
|
+ # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
|
|
+ ldb.delete(certa_dn)
|
|
+ ldb.delete(enroll_dn)
|
|
+ ldb.delete(template_dn)
|
|
+
|
|
+ # Unstage the Registry.pol file
|
|
+ unstage_file(reg_pol)
|
|
+
|
|
def test_gp_cert_auto_enroll_ext(self):
|
|
local_path = self.lp.cache_path('gpo_cache')
|
|
guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}'
|
|
@@ -6777,6 +6889,7 @@ class GPOTests(tests.TestCase):
|
|
machine_creds.set_machine_account()
|
|
|
|
# Initialize the group policy extension
|
|
+ cae.requests = dummy_requests()
|
|
ext = cae.gp_cert_auto_enroll_ext(self.lp, machine_creds,
|
|
machine_creds.get_username(), store)
|
|
|
|
@@ -7241,6 +7354,7 @@ class GPOTests(tests.TestCase):
|
|
machine_creds.set_machine_account()
|
|
|
|
# Initialize the group policy extension
|
|
+ cae.requests = dummy_requests()
|
|
ext = cae.gp_cert_auto_enroll_ext(self.lp, machine_creds,
|
|
machine_creds.get_username(), store)
|
|
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
new file mode 100644
|
|
index 00000000000..f1e590bc7d8
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/gpo
|
|
@@ -0,0 +1 @@
|
|
+^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext_without_ndes
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 1a9af36177c7491687c75df151474bb10285f00e Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Thu, 18 Jan 2024 20:23:24 +0200
|
|
Subject: [PATCH 012/122] gpo: Decode base64 root cert before importing
|
|
|
|
The reasoning behind this is described in the previous commit message,
|
|
but essentially this should either be wrapped in certificate blocks and
|
|
imported as PEM, or converted back to binary and imported as DER.
|
|
|
|
I've opted for the latter since it's how it used to work before it
|
|
regressed in 157335ee93e.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15557
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit 3f3ddfa699a33c2c8a59f7fb9ee044bb2a6e0e06)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 5 +++--
|
|
selftest/knownfail.d/gpo | 1 -
|
|
2 files changed, 3 insertions(+), 3 deletions(-)
|
|
delete mode 100644 selftest/knownfail.d/gpo
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 08d1a7348cd..cd5e54f1110 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -217,10 +217,11 @@ def getca(ca, url, trust_dir):
|
|
' installed or not configured.')
|
|
if 'cACertificate' in ca:
|
|
log.warn('Installing the server certificate only.')
|
|
+ der_certificate = base64.b64decode(ca['cACertificate'])
|
|
try:
|
|
- cert = load_der_x509_certificate(ca['cACertificate'])
|
|
+ cert = load_der_x509_certificate(der_certificate)
|
|
except TypeError:
|
|
- cert = load_der_x509_certificate(ca['cACertificate'],
|
|
+ cert = load_der_x509_certificate(der_certificate,
|
|
default_backend())
|
|
cert_data = cert.public_bytes(Encoding.PEM)
|
|
with open(root_cert, 'wb') as w:
|
|
diff --git a/selftest/knownfail.d/gpo b/selftest/knownfail.d/gpo
|
|
deleted file mode 100644
|
|
index f1e590bc7d8..00000000000
|
|
--- a/selftest/knownfail.d/gpo
|
|
+++ /dev/null
|
|
@@ -1 +0,0 @@
|
|
-^samba.tests.gpo.samba.tests.gpo.GPOTests.test_gp_cert_auto_enroll_ext_without_ndes
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From f5fc88f9ae255f4dc135580f0fa4a02f5addc390 Mon Sep 17 00:00:00 2001
|
|
From: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Date: Fri, 19 Jan 2024 11:36:19 +0200
|
|
Subject: [PATCH 013/122] gpo: Do not get templates list on first run
|
|
|
|
This is a visual fix and has no impact on functionality apart from
|
|
cleaner log messages.
|
|
|
|
The point of this is to get the list of supported templates in order to
|
|
compute a diff between the current applied templates and the updated
|
|
list, so we are able to unapply and reapply the policy in case there are
|
|
differences.
|
|
|
|
However this code path is executed on first applies as well, at which
|
|
point the root CA is not yet set up. This causes the
|
|
`get_supported_templates` call to fail, which is not a hard failure but
|
|
still pollutes the logs. In this case it's safe to avoid executing the
|
|
command as the policy will be applied regardless.
|
|
|
|
Signed-off-by: Gabriel Nagy <gabriel.nagy@canonical.com>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Mon Jan 22 16:48:57 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 8579340fc540633c13c017d896034904a8dbd55c)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 3 ++-
|
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index cd5e54f1110..559c903e1a2 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -359,7 +359,8 @@ class gp_cert_auto_enroll_ext(gp_pol_ext, gp_applier):
|
|
# If the policy has changed, unapply, then apply new policy
|
|
old_val = self.cache_get_attribute_value(guid, attribute)
|
|
old_data = json.loads(old_val) if old_val is not None else {}
|
|
- templates = ['%s.%s' % (ca['name'], t.decode()) for t in get_supported_templates(ca['hostname'])]
|
|
+ templates = ['%s.%s' % (ca['name'], t.decode()) for t in get_supported_templates(ca['hostname'])] \
|
|
+ if old_val is not None else []
|
|
new_data = { 'templates': templates, **ca }
|
|
if changed(new_data, old_data) or self.cache_get_apply_state() == GPOSTATE.ENFORCE:
|
|
self.unapply(guid, attribute, old_val)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e8a6219181f2af87813b53fd09684650c1aa6f90 Mon Sep 17 00:00:00 2001
|
|
From: David Mulder <dmulder@samba.org>
|
|
Date: Fri, 5 Jan 2024 08:47:07 -0700
|
|
Subject: [PATCH 014/122] gp: Skip site GP list if no site is found
|
|
|
|
[MS-GPOL] 3.2.5.1.4 Site Search says if the site
|
|
search returns ERROR_NO_SITENAME, the GP site
|
|
search should be skipped.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15548
|
|
|
|
Signed-off-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Tue Jan 23 11:20:35 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit f05b61b4991e7f51bd184d76a79f8b50114a0ff3)
|
|
---
|
|
python/samba/gp/gpclass.py | 30 ++++++++++++++++++------------
|
|
1 file changed, 18 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/python/samba/gp/gpclass.py b/python/samba/gp/gpclass.py
|
|
index 617ef79350c..babd8f90748 100644
|
|
--- a/python/samba/gp/gpclass.py
|
|
+++ b/python/samba/gp/gpclass.py
|
|
@@ -866,19 +866,25 @@ def get_gpo_list(dc_hostname, creds, lp, username):
|
|
|
|
# (S)ite
|
|
if gpo_list_machine:
|
|
- site_dn = site_dn_for_machine(samdb, dc_hostname, lp, creds, username)
|
|
-
|
|
try:
|
|
- log.debug("get_gpo_list: query SITE: [%s] for GPOs" % site_dn)
|
|
- gp_link = get_gpo_link(samdb, site_dn)
|
|
- except ldb.LdbError as e:
|
|
- (enum, estr) = e.args
|
|
- log.debug(estr)
|
|
- else:
|
|
- add_gplink_to_gpo_list(samdb, gpo_list, forced_gpo_list,
|
|
- site_dn, gp_link,
|
|
- gpo.GP_LINK_SITE,
|
|
- add_only_forced_gpos, token)
|
|
+ site_dn = site_dn_for_machine(samdb, dc_hostname, lp, creds, username)
|
|
+
|
|
+ try:
|
|
+ log.debug("get_gpo_list: query SITE: [%s] for GPOs" % site_dn)
|
|
+ gp_link = get_gpo_link(samdb, site_dn)
|
|
+ except ldb.LdbError as e:
|
|
+ (enum, estr) = e.args
|
|
+ log.debug(estr)
|
|
+ else:
|
|
+ add_gplink_to_gpo_list(samdb, gpo_list, forced_gpo_list,
|
|
+ site_dn, gp_link,
|
|
+ gpo.GP_LINK_SITE,
|
|
+ add_only_forced_gpos, token)
|
|
+ except ldb.LdbError:
|
|
+ # [MS-GPOL] 3.2.5.1.4 Site Search: If the method returns
|
|
+ # ERROR_NO_SITENAME, the remainder of this message MUST be skipped
|
|
+ # and the protocol sequence MUST continue at GPO Search
|
|
+ pass
|
|
|
|
# (L)ocal
|
|
gpo_list.insert(0, gpo.GROUP_POLICY_OBJECT("Local Policy",
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d0d1a890d6f2466691fa4ee663232ee0bd1c3776 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 22 Jan 2024 14:14:30 +0100
|
|
Subject: [PATCH 015/122] python:gp: Avoid path check for cepces-submit
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
find_cepces_submit() uses which(), which returns None if not found.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15559
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
(cherry picked from commit 6a9630eff624643fd725219775784e68d967d04c)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 559c903e1a2..7325d5132cf 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -185,7 +185,7 @@ def find_cepces_submit():
|
|
|
|
def get_supported_templates(server):
|
|
cepces_submit = find_cepces_submit()
|
|
- if not cepces_submit or not os.path.exists(cepces_submit):
|
|
+ if not cepces_submit:
|
|
log.error('Failed to find cepces-submit')
|
|
return []
|
|
|
|
@@ -301,7 +301,7 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
# Setup Certificate Auto Enrollment
|
|
getcert = which('getcert')
|
|
cepces_submit = find_cepces_submit()
|
|
- if getcert is not None and os.path.exists(cepces_submit):
|
|
+ if getcert is not None and cepces_submit is not None:
|
|
p = Popen([getcert, 'add-ca', '-c', ca['name'], '-e',
|
|
'%s --server=%s --auth=%s' % (cepces_submit,
|
|
ca['hostname'], auth)],
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7f6c9a4945635c6eb8ada2255bd0febbf0f4e540 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 22 Jan 2024 14:07:47 +0100
|
|
Subject: [PATCH 016/122] python:gp: Improve logging for certificate enrollment
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15559
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
(cherry picked from commit 6d5507e05050690cd4c56f3f97f5fb7de0338b87)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 11 ++++++++++-
|
|
1 file changed, 10 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 7325d5132cf..a25a9678587 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -274,6 +274,9 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
"""Install the root certificate chain."""
|
|
data = dict({'files': [], 'templates': []}, **ca)
|
|
url = 'http://%s/CertSrv/mscep/mscep.dll/pkiclient.exe?' % ca['hostname']
|
|
+
|
|
+ log.info("Try to get root or server certificates")
|
|
+
|
|
root_certs = getca(ca, url, trust_dir)
|
|
data['files'].extend(root_certs)
|
|
global_trust_dir = find_global_trust_dir()
|
|
@@ -283,6 +286,7 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
try:
|
|
os.symlink(src, dst)
|
|
data['files'].append(dst)
|
|
+ log.info("Created symlink: %s -> %s" % (src, dst))
|
|
except PermissionError:
|
|
log.warn('Failed to symlink root certificate to the'
|
|
' admin trust anchors')
|
|
@@ -295,9 +299,14 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
# already exists. Ignore the FileExistsError. Preserve the
|
|
# existing symlink in the unapply data.
|
|
data['files'].append(dst)
|
|
+
|
|
update = update_ca_command()
|
|
+ log.info("Running %s" % (update))
|
|
if update is not None:
|
|
- Popen([update]).wait()
|
|
+ ret = Popen([update]).wait()
|
|
+ if ret != 0:
|
|
+ log.error('Failed to run %s' % (update))
|
|
+
|
|
# Setup Certificate Auto Enrollment
|
|
getcert = which('getcert')
|
|
cepces_submit = find_cepces_submit()
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 5321d5b5bd24d7659743576f2e12a7dc0a93a828 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 22 Jan 2024 15:04:36 +0100
|
|
Subject: [PATCH 017/122] python:gp: Do not print an error, if CA already
|
|
exists
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
We will get an exit status for duplicate in future:
|
|
https://www.pagure.io/certmonger/issue/269
|
|
We can't really fix that right now, as older version of certmonger
|
|
don't support the `-v` option.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15559
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
(cherry picked from commit 728757cd1ff0465967fcbda100254c9312e87c93)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 8 ++++++--
|
|
1 file changed, 6 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index a25a9678587..0b23cd688db 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -318,8 +318,12 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
out, err = p.communicate()
|
|
log.debug(out.decode())
|
|
if p.returncode != 0:
|
|
- data = { 'Error': err.decode(), 'CA': ca['name'] }
|
|
- log.error('Failed to add Certificate Authority', data)
|
|
+ if p.returncode == 2:
|
|
+ log.info('The CA [%s] already exists' % ca['name'])
|
|
+ else:
|
|
+ data = {'Error': err.decode(), 'CA': ca['name']}
|
|
+ log.error('Failed to add Certificate Authority', data)
|
|
+
|
|
supported_templates = get_supported_templates(ca['hostname'])
|
|
for template in supported_templates:
|
|
attrs = fetch_template_attrs(ldb, template)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 6a7a8a4090b8cdb8e71f4ad590260ceeda253ce2 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 22 Jan 2024 15:05:02 +0100
|
|
Subject: [PATCH 018/122] python:gp: Do not print an error if template already
|
|
exists
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
We will get an exit status for duplicate in future:
|
|
https://www.pagure.io/certmonger/issue/269
|
|
We can't really fix that right now, as older version of certmonger
|
|
don't support the `-v` option.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15559
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
(cherry picked from commit 98dc44286ea102ef7701ccdea26bbde32b523a7e)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 8 ++++++--
|
|
1 file changed, 6 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 0b23cd688db..db681cb6f69 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -338,8 +338,12 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
out, err = p.communicate()
|
|
log.debug(out.decode())
|
|
if p.returncode != 0:
|
|
- data = { 'Error': err.decode(), 'Certificate': nickname }
|
|
- log.error('Failed to request certificate', data)
|
|
+ if p.returncode == 2:
|
|
+ log.info('The template [%s] already exists' % (nickname))
|
|
+ else:
|
|
+ data = {'Error': err.decode(), 'Certificate': nickname}
|
|
+ log.error('Failed to request certificate', data)
|
|
+
|
|
data['files'].extend([keyfile, certfile])
|
|
data['templates'].append(nickname)
|
|
if update is not None:
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 43dc3d5d833bc1db885eb45402decd3225a7c946 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 22 Jan 2024 15:05:24 +0100
|
|
Subject: [PATCH 019/122] python:gp: Log an error if update fails
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15559
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
(cherry picked from commit 367756b85a9ac8daaac2326392bcd1373feed3b7)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 4 +++-
|
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index db681cb6f69..c8ad2039dc6 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -347,7 +347,9 @@ def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
data['files'].extend([keyfile, certfile])
|
|
data['templates'].append(nickname)
|
|
if update is not None:
|
|
- Popen([update]).wait()
|
|
+ ret = Popen([update]).wait()
|
|
+ if ret != 0:
|
|
+ log.error('Failed to run %s' % (update))
|
|
else:
|
|
log.warn('certmonger and cepces must be installed for ' +
|
|
'certificate auto enrollment to work')
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d8276d6a098d10f405b8f24c4dfb82af4496607c Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 22 Jan 2024 15:46:24 +0100
|
|
Subject: [PATCH 020/122] python:gp: Improve working of log messages to avoid
|
|
confusion
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
We should not use the word "Failed". We are totally fine if we can't
|
|
connect to NDES in the meantime. This logs:
|
|
|
|
Try to get root or server certificates.
|
|
Unable to install root certificates (requires NDES).
|
|
Installing the server certificate only.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15559
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: David Mulder <dmulder@samba.org>
|
|
Reviewed-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Mon Jan 29 10:37:29 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 1f823424418e814d9dc0785658e2a7d92643dab2)
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 6 ++----
|
|
1 file changed, 2 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index c8ad2039dc6..2b7f7d22c2b 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -209,12 +209,10 @@ def getca(ca, url, trust_dir):
|
|
r = requests.get(url=url, params={'operation': 'GetCACert',
|
|
'message': 'CAIdentifier'})
|
|
except requests.exceptions.ConnectionError:
|
|
- log.warn('Failed to establish a new connection')
|
|
+ log.warn('Could not connect to Network Device Enrollment Service.')
|
|
r = None
|
|
if r is None or r.content == b'' or r.headers['Content-Type'] == 'text/html':
|
|
- log.warn('Failed to fetch the root certificate chain.')
|
|
- log.warn('The Network Device Enrollment Service is either not' +
|
|
- ' installed or not configured.')
|
|
+ log.warn('Unable to fetch root certificates (requires NDES).')
|
|
if 'cACertificate' in ca:
|
|
log.warn('Installing the server certificate only.')
|
|
der_certificate = base64.b64decode(ca['cACertificate'])
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 585357bf0d8889747a2769c2451ee34766087d95 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 29 Jan 2024 17:46:30 +0100
|
|
Subject: [PATCH 021/122] python:gp: Fix logging with gp
|
|
|
|
This allows enable INFO level logging with: `samba-gpupdate -d3`
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15558
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Tue Jan 30 07:18:05 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 145194071b10c4c1857f28fe79c57fd63ffab889)
|
|
---
|
|
python/samba/gp/util/logging.py | 5 +++--
|
|
1 file changed, 3 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/python/samba/gp/util/logging.py b/python/samba/gp/util/logging.py
|
|
index a74a8707d50..c3de32825db 100644
|
|
--- a/python/samba/gp/util/logging.py
|
|
+++ b/python/samba/gp/util/logging.py
|
|
@@ -24,9 +24,10 @@ import gettext
|
|
import random
|
|
import sys
|
|
|
|
-logger = logging.getLogger()
|
|
+logger = logging.getLogger("gp")
|
|
+
|
|
+
|
|
def logger_init(name, log_level):
|
|
- logger = logging.getLogger(name)
|
|
logger.addHandler(logging.StreamHandler(sys.stdout))
|
|
logger.setLevel(logging.CRITICAL)
|
|
if log_level == 1:
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 14ceb0b5f2f954bbabdaf78b8185fc515e3c8294 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Wed, 13 Mar 2024 13:55:41 +0100
|
|
Subject: [PATCH 022/122] docs-xml: Add parameter all_groupmem to idmap_ad
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15605
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit a485d9de2f2d6a9815dcac6addb988a8987e111c)
|
|
---
|
|
docs-xml/manpages/idmap_ad.8.xml | 10 ++++++++++
|
|
1 file changed, 10 insertions(+)
|
|
|
|
diff --git a/docs-xml/manpages/idmap_ad.8.xml b/docs-xml/manpages/idmap_ad.8.xml
|
|
index b364bbfa231..de6d36afe95 100644
|
|
--- a/docs-xml/manpages/idmap_ad.8.xml
|
|
+++ b/docs-xml/manpages/idmap_ad.8.xml
|
|
@@ -100,6 +100,16 @@
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
+ <term>all_groupmem = yes/no</term>
|
|
+ <listitem><para>
|
|
+ If set to <parameter>yes</parameter> winbind will retrieve all
|
|
+ group members for getgrnam(3), getgrgid(3) and getgrent(3) calls,
|
|
+ including those with missing uidNumber.
|
|
+ </para>
|
|
+ <para>Default: no</para>
|
|
+ </listitem>
|
|
+ </varlistentry>
|
|
+ <varlistentry>
|
|
<term>deny ous</term>
|
|
<listitem><para>This parameter is a list of OUs from
|
|
which objects will not be mapped via the ad idmap
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From ac4184c8c3220263cb6f1a46a012533ed1c4e047 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Tue, 12 Mar 2024 13:20:24 +0100
|
|
Subject: [PATCH 023/122] s3:winbindd: Improve performance of lookup_groupmem()
|
|
in idmap_ad
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
The LDAP query of lookup_groupmem() returns all group members from AD
|
|
even those with missing uidNumber. Such group members are useless in
|
|
UNIX environment for idmap_ad backend since there is no uid mapping.
|
|
|
|
'test_user' is member of group "Domanin Users" with 200K members,
|
|
only 20K members have set uidNumber.
|
|
|
|
Without this fix:
|
|
|
|
$ time id test_user
|
|
|
|
real 1m5.946s
|
|
user 0m0.019s
|
|
sys 0m0.012s
|
|
|
|
With this fix:
|
|
|
|
$ time id test_user
|
|
|
|
real 0m3.544s
|
|
user 0m0.004s
|
|
sys 0m0.007s
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15605
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit 5d475d26a3d545f04791a04e85a06b8b192e3fcf)
|
|
---
|
|
source3/winbindd/winbindd_ads.c | 11 +++++++----
|
|
1 file changed, 7 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
|
|
index d7a665abbc6..e625aa6473f 100644
|
|
--- a/source3/winbindd/winbindd_ads.c
|
|
+++ b/source3/winbindd/winbindd_ads.c
|
|
@@ -1037,7 +1037,7 @@ static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
|
|
}
|
|
|
|
static NTSTATUS add_primary_group_members(
|
|
- ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, uint32_t rid,
|
|
+ ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, uint32_t rid, const char *domname,
|
|
char ***all_members, size_t *num_all_members)
|
|
{
|
|
char *filter;
|
|
@@ -1049,10 +1049,13 @@ static NTSTATUS add_primary_group_members(
|
|
char **members;
|
|
size_t num_members;
|
|
ads_control args;
|
|
+ bool all_groupmem = idmap_config_bool(domname, "all_groupmem", false);
|
|
|
|
filter = talloc_asprintf(
|
|
- mem_ctx, "(&(objectCategory=user)(primaryGroupID=%u))",
|
|
- (unsigned)rid);
|
|
+ mem_ctx,
|
|
+ "(&(objectCategory=user)(primaryGroupID=%u)%s)",
|
|
+ (unsigned)rid,
|
|
+ all_groupmem ? "" : "(uidNumber=*)(!(uidNumber=0))");
|
|
if (filter == NULL) {
|
|
goto done;
|
|
}
|
|
@@ -1204,7 +1207,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
|
|
|
|
DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
|
|
|
|
- status = add_primary_group_members(ads, mem_ctx, rid,
|
|
+ status = add_primary_group_members(ads, mem_ctx, rid, domain->name,
|
|
&members, &num_members);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(10, ("%s: add_primary_group_members failed: %s\n",
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d0e2002efcc37055b35c351a6b936e6ab89fad32 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Mon, 25 Mar 2024 22:38:18 +0100
|
|
Subject: [PATCH 024/122] selftest: Add "winbind expand groups = 1" to
|
|
setup_ad_member_idmap_ad
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15605
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(backported from commit 2dab3a331b5511b4f2253f2b3b4513db7e52ea9a)
|
|
---
|
|
selftest/target/Samba3.pm | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
|
|
index 44ac4a5901a..606c65f8ab1 100755
|
|
--- a/selftest/target/Samba3.pm
|
|
+++ b/selftest/target/Samba3.pm
|
|
@@ -1412,6 +1412,7 @@ sub setup_ad_member_idmap_ad
|
|
idmap config $dcvars->{TRUST_DOMAIN} : backend = ad
|
|
idmap config $dcvars->{TRUST_DOMAIN} : range = 2000000-2999999
|
|
gensec_gssapi:requested_life_time = 5
|
|
+ winbind expand groups = 1
|
|
";
|
|
|
|
my $ret = $self->provision(
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 9625b6aed981aa4e70fe11d9d1acdb54db7591a3 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Thu, 14 Mar 2024 15:24:21 +0100
|
|
Subject: [PATCH 025/122] tests: Add a test for "all_groups=no" to
|
|
test_idmap_ad.sh
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15605
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
|
|
Autobuild-User(master): Pavel Filipensky <pfilipensky@samba.org>
|
|
Autobuild-Date(master): Tue Apr 2 13:25:39 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit f8b72aa1f72881989990fabc9f4888968bb81967)
|
|
---
|
|
nsswitch/tests/test_idmap_ad.sh | 22 ++++++++++++++++++++++
|
|
1 file changed, 22 insertions(+)
|
|
|
|
diff --git a/nsswitch/tests/test_idmap_ad.sh b/nsswitch/tests/test_idmap_ad.sh
|
|
index 7ae112ada71..1d4bd395ba9 100755
|
|
--- a/nsswitch/tests/test_idmap_ad.sh
|
|
+++ b/nsswitch/tests/test_idmap_ad.sh
|
|
@@ -94,6 +94,14 @@ gidNumber: 2000001
|
|
unixHomeDirectory: /home/forbidden
|
|
loginShell: /bin/tcsh
|
|
gecos: User in forbidden OU
|
|
+
|
|
+dn: CN=no_posix_id,CN=Users,$BASE_DN
|
|
+changetype: add
|
|
+objectClass: user
|
|
+samaccountName: no_posix_id
|
|
+unixHomeDirectory: /home/no_posix_id
|
|
+loginShell: /bin/sh
|
|
+gecos: User without uidNumber and gidNumber
|
|
EOF
|
|
|
|
#
|
|
@@ -171,6 +179,17 @@ then
|
|
failed=$(($failed + 1))
|
|
fi
|
|
|
|
+#
|
|
+# Test 6: Make sure that with the default "all_groups=no"
|
|
+# the group "domain users" will not show user "no_posix_id"
|
|
+# but will show "SAMBA2008R2/administrator"
|
|
+#
|
|
+
|
|
+dom_users="$DOMAIN/domain users" # Extra step to make sure that all is one word
|
|
+out="$($wbinfo --group-info "$dom_users")"
|
|
+testit_grep_count "no_posix_id1" "no_posix_id" 0 echo "$out" || failed=$(expr $failed + 1)
|
|
+testit_grep "no_posix_id2" "SAMBA2008R2/administrator" echo "$out" || failed=$(expr $failed + 1)
|
|
+
|
|
#
|
|
# Trusted domain test 1: Test uid of Administrator, should be 2500000
|
|
#
|
|
@@ -241,6 +260,9 @@ gidNumber: 2000002
|
|
dn: cn=forbidden,ou=sub,$BASE_DN
|
|
changetype: delete
|
|
|
|
+dn: CN=no_posix_id,CN=Users,$BASE_DN
|
|
+changetype: delete
|
|
+
|
|
dn: ou=sub,$BASE_DN
|
|
changetype: delete
|
|
EOF
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e5890e63c35a4a5af29ae16e6dd734c4a3a304cc Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Tue, 28 May 2024 13:51:53 +0200
|
|
Subject: [PATCH 026/122] s3:libads: Allow get_kdc_ip_string() to lookup the
|
|
KDCs IP
|
|
|
|
Remove the requirement to provide an IP address. We should look up the
|
|
IP of the KDC and use it for the specified realm/workgroup.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15653
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
(cherry picked from commit 28aa0b815baf4668e3df01d52597c40fd430e2fb)
|
|
---
|
|
source3/libads/kerberos.c | 30 +++++++++++++++---------------
|
|
1 file changed, 15 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c
|
|
index 50f4a6de3c6..ddf97c11973 100644
|
|
--- a/source3/libads/kerberos.c
|
|
+++ b/source3/libads/kerberos.c
|
|
@@ -437,23 +437,23 @@ static char *get_kdc_ip_string(char *mem_ctx,
|
|
char *kdc_str = NULL;
|
|
char *canon_sockaddr = NULL;
|
|
|
|
- SMB_ASSERT(pss != NULL);
|
|
-
|
|
- canon_sockaddr = print_canonical_sockaddr_with_port(frame, pss);
|
|
- if (canon_sockaddr == NULL) {
|
|
- goto out;
|
|
- }
|
|
+ if (pss != NULL) {
|
|
+ canon_sockaddr = print_canonical_sockaddr_with_port(frame, pss);
|
|
+ if (canon_sockaddr == NULL) {
|
|
+ goto out;
|
|
+ }
|
|
|
|
- kdc_str = talloc_asprintf(frame,
|
|
- "\t\tkdc = %s\n",
|
|
- canon_sockaddr);
|
|
- if (kdc_str == NULL) {
|
|
- goto out;
|
|
- }
|
|
+ kdc_str = talloc_asprintf(frame,
|
|
+ "\t\tkdc = %s\n",
|
|
+ canon_sockaddr);
|
|
+ if (kdc_str == NULL) {
|
|
+ goto out;
|
|
+ }
|
|
|
|
- ok = sockaddr_storage_to_samba_sockaddr(&sa, pss);
|
|
- if (!ok) {
|
|
- goto out;
|
|
+ ok = sockaddr_storage_to_samba_sockaddr(&sa, pss);
|
|
+ if (!ok) {
|
|
+ goto out;
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 96a1ecd8db249fa03db60259cf76fdef9c1bd749 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Tue, 28 May 2024 13:53:51 +0200
|
|
Subject: [PATCH 027/122] s3:libads: Do not fail if we don't get an IP passed
|
|
down
|
|
|
|
The IP should be optional and we should look it up if not provided.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15653
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
(cherry picked from commit 9dcc52d2a57314ec9ddaae82b3c49da051d1f1d2)
|
|
---
|
|
source3/libads/kerberos.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c
|
|
index ddf97c11973..f74d8eb567c 100644
|
|
--- a/source3/libads/kerberos.c
|
|
+++ b/source3/libads/kerberos.c
|
|
@@ -704,7 +704,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
|
|
return false;
|
|
}
|
|
|
|
- if (domain == NULL || pss == NULL) {
|
|
+ if (domain == NULL) {
|
|
return false;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4934642b7a7d92c6d81ba25ef6e4b66e3805f708 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Tue, 28 May 2024 13:54:24 +0200
|
|
Subject: [PATCH 028/122] s3:winbind: Fix idmap_ad creating an invalid local
|
|
krb5.conf
|
|
|
|
In case of a trusted domain, we are providing the realm of the primary
|
|
trust but specify the KDC IP of the trusted domain. This leads to
|
|
Kerberos ticket requests to the trusted domain KDC which doesn't know
|
|
about the machine account. However we need a ticket from our primary
|
|
trust KDC.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15653
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
(backported from commit 8989aa47b7493e6b7978c2efc4a40c781e9a2aee)
|
|
---
|
|
source3/winbindd/idmap_ad.c | 11 +++++++++--
|
|
1 file changed, 9 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
|
|
index 5c9fe07db95..b8002825161 100644
|
|
--- a/source3/winbindd/idmap_ad.c
|
|
+++ b/source3/winbindd/idmap_ad.c
|
|
@@ -320,7 +320,10 @@ static NTSTATUS idmap_ad_get_tldap_ctx(TALLOC_CTX *mem_ctx,
|
|
struct tldap_context **pld)
|
|
{
|
|
struct netr_DsRGetDCNameInfo *dcinfo;
|
|
- struct sockaddr_storage dcaddr;
|
|
+ struct sockaddr_storage dcaddr = {
|
|
+ .ss_family = AF_UNSPEC,
|
|
+ };
|
|
+ struct sockaddr_storage *pdcaddr = NULL;
|
|
struct cli_credentials *creds;
|
|
struct loadparm_context *lp_ctx;
|
|
struct tldap_context *ld;
|
|
@@ -362,9 +365,13 @@ static NTSTATUS idmap_ad_get_tldap_ctx(TALLOC_CTX *mem_ctx,
|
|
* create_local_private_krb5_conf_for_domain() can deal with
|
|
* sitename==NULL
|
|
*/
|
|
+ if (strequal(domname, lp_realm()) || strequal(domname, lp_workgroup()))
|
|
+ {
|
|
+ pdcaddr = &dcaddr;
|
|
+ }
|
|
|
|
ok = create_local_private_krb5_conf_for_domain(
|
|
- lp_realm(), lp_workgroup(), sitename, &dcaddr);
|
|
+ lp_realm(), lp_workgroup(), sitename, pdcaddr);
|
|
TALLOC_FREE(sitename);
|
|
if (!ok) {
|
|
DBG_DEBUG("Could not create private krb5.conf\n");
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From cccc902c64c93db317bf4707d0af5e56b2887286 Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Mon, 22 Jul 2024 12:26:55 +0200
|
|
Subject: [PATCH 029/122] s3:notifyd: Use a watcher per db record
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
This fixes a O(n²) performance regression in notifyd. The problem was
|
|
that we had a watcher per notify instance. This changes the code to have
|
|
a watcher per notify db entry.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14430
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Tue Oct 1 14:22:43 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit af011b987a4ad0d3753d83cc0b8d97ad64ba874a)
|
|
---
|
|
source3/smbd/notifyd/notifyd.c | 214 ++++++++++++++++++-------
|
|
source3/smbd/notifyd/notifyd_db.c | 5 +-
|
|
source3/smbd/notifyd/notifyd_entry.c | 51 ++++--
|
|
source3/smbd/notifyd/notifyd_private.h | 46 ++++--
|
|
4 files changed, 228 insertions(+), 88 deletions(-)
|
|
|
|
diff --git a/source3/smbd/notifyd/notifyd.c b/source3/smbd/notifyd/notifyd.c
|
|
index ca303bd4d51..b368b8390fa 100644
|
|
--- a/source3/smbd/notifyd/notifyd.c
|
|
+++ b/source3/smbd/notifyd/notifyd.c
|
|
@@ -337,6 +337,7 @@ static bool notifyd_apply_rec_change(
|
|
struct messaging_context *msg_ctx)
|
|
{
|
|
struct db_record *rec = NULL;
|
|
+ struct notifyd_watcher watcher = {};
|
|
struct notifyd_instance *instances = NULL;
|
|
size_t num_instances;
|
|
size_t i;
|
|
@@ -344,6 +345,7 @@ static bool notifyd_apply_rec_change(
|
|
TDB_DATA value;
|
|
NTSTATUS status;
|
|
bool ok = false;
|
|
+ bool new_watcher = false;
|
|
|
|
if (pathlen == 0) {
|
|
DBG_WARNING("pathlen==0\n");
|
|
@@ -374,8 +376,12 @@ static bool notifyd_apply_rec_change(
|
|
value = dbwrap_record_get_value(rec);
|
|
|
|
if (value.dsize != 0) {
|
|
- if (!notifyd_parse_entry(value.dptr, value.dsize, NULL,
|
|
- &num_instances)) {
|
|
+ ok = notifyd_parse_entry(value.dptr,
|
|
+ value.dsize,
|
|
+ &watcher,
|
|
+ NULL,
|
|
+ &num_instances);
|
|
+ if (!ok) {
|
|
goto fail;
|
|
}
|
|
}
|
|
@@ -390,8 +396,22 @@ static bool notifyd_apply_rec_change(
|
|
goto fail;
|
|
}
|
|
|
|
- if (value.dsize != 0) {
|
|
- memcpy(instances, value.dptr, value.dsize);
|
|
+ if (num_instances > 0) {
|
|
+ struct notifyd_instance *tmp = NULL;
|
|
+ size_t num_tmp = 0;
|
|
+
|
|
+ ok = notifyd_parse_entry(value.dptr,
|
|
+ value.dsize,
|
|
+ NULL,
|
|
+ &tmp,
|
|
+ &num_tmp);
|
|
+ if (!ok) {
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ memcpy(instances,
|
|
+ tmp,
|
|
+ sizeof(struct notifyd_instance) * num_tmp);
|
|
}
|
|
|
|
for (i=0; i<num_instances; i++) {
|
|
@@ -414,41 +434,106 @@ static bool notifyd_apply_rec_change(
|
|
*instance = (struct notifyd_instance) {
|
|
.client = *client,
|
|
.instance = *chg,
|
|
- .internal_filter = chg->filter,
|
|
- .internal_subdir_filter = chg->subdir_filter
|
|
};
|
|
|
|
num_instances += 1;
|
|
}
|
|
|
|
- if ((instance->instance.filter != 0) ||
|
|
- (instance->instance.subdir_filter != 0)) {
|
|
- int ret;
|
|
+ /*
|
|
+ * Calculate an intersection of the instances filters for the watcher.
|
|
+ */
|
|
+ if (instance->instance.filter > 0) {
|
|
+ uint32_t filter = instance->instance.filter;
|
|
+
|
|
+ if ((watcher.filter & filter) != filter) {
|
|
+ watcher.filter |= filter;
|
|
+
|
|
+ new_watcher = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Calculate an intersection of the instances subdir_filters for the
|
|
+ * watcher.
|
|
+ */
|
|
+ if (instance->instance.subdir_filter > 0) {
|
|
+ uint32_t subdir_filter = instance->instance.subdir_filter;
|
|
|
|
- TALLOC_FREE(instance->sys_watch);
|
|
+ if ((watcher.subdir_filter & subdir_filter) != subdir_filter) {
|
|
+ watcher.subdir_filter |= subdir_filter;
|
|
|
|
- ret = sys_notify_watch(entries, sys_notify_ctx, path,
|
|
- &instance->internal_filter,
|
|
- &instance->internal_subdir_filter,
|
|
- notifyd_sys_callback, msg_ctx,
|
|
- &instance->sys_watch);
|
|
- if (ret != 0) {
|
|
- DBG_WARNING("sys_notify_watch for [%s] returned %s\n",
|
|
- path, strerror(errno));
|
|
+ new_watcher = true;
|
|
}
|
|
}
|
|
|
|
if ((instance->instance.filter == 0) &&
|
|
(instance->instance.subdir_filter == 0)) {
|
|
+ uint32_t tmp_filter = 0;
|
|
+ uint32_t tmp_subdir_filter = 0;
|
|
+
|
|
/* This is a delete request */
|
|
- TALLOC_FREE(instance->sys_watch);
|
|
*instance = instances[num_instances-1];
|
|
num_instances -= 1;
|
|
+
|
|
+ for (i = 0; i < num_instances; i++) {
|
|
+ struct notifyd_instance *tmp = &instances[i];
|
|
+
|
|
+ tmp_filter |= tmp->instance.filter;
|
|
+ tmp_subdir_filter |= tmp->instance.subdir_filter;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * If the filter has changed, register a new watcher with the
|
|
+ * changed filter.
|
|
+ */
|
|
+ if (watcher.filter != tmp_filter ||
|
|
+ watcher.subdir_filter != tmp_subdir_filter)
|
|
+ {
|
|
+ watcher.filter = tmp_filter;
|
|
+ watcher.subdir_filter = tmp_subdir_filter;
|
|
+
|
|
+ new_watcher = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (new_watcher) {
|
|
+ /*
|
|
+ * In case we removed all notify instances, we want to remove
|
|
+ * the watcher. We won't register a new one, if no filters are
|
|
+ * set anymore.
|
|
+ */
|
|
+
|
|
+ TALLOC_FREE(watcher.sys_watch);
|
|
+
|
|
+ watcher.sys_filter = watcher.filter;
|
|
+ watcher.sys_subdir_filter = watcher.subdir_filter;
|
|
+
|
|
+ /*
|
|
+ * Only register a watcher if we have filter.
|
|
+ */
|
|
+ if (watcher.filter != 0 || watcher.subdir_filter != 0) {
|
|
+ int ret = sys_notify_watch(entries,
|
|
+ sys_notify_ctx,
|
|
+ path,
|
|
+ &watcher.sys_filter,
|
|
+ &watcher.sys_subdir_filter,
|
|
+ notifyd_sys_callback,
|
|
+ msg_ctx,
|
|
+ &watcher.sys_watch);
|
|
+ if (ret != 0) {
|
|
+ DBG_WARNING("sys_notify_watch for [%s] "
|
|
+ "returned %s\n",
|
|
+ path,
|
|
+ strerror(errno));
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
DBG_DEBUG("%s has %zu instances\n", path, num_instances);
|
|
|
|
if (num_instances == 0) {
|
|
+ TALLOC_FREE(watcher.sys_watch);
|
|
+
|
|
status = dbwrap_record_delete(rec);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_WARNING("dbwrap_record_delete returned %s\n",
|
|
@@ -456,13 +541,21 @@ static bool notifyd_apply_rec_change(
|
|
goto fail;
|
|
}
|
|
} else {
|
|
- value = make_tdb_data(
|
|
- (uint8_t *)instances,
|
|
- sizeof(struct notifyd_instance) * num_instances);
|
|
+ struct TDB_DATA iov[2] = {
|
|
+ {
|
|
+ .dptr = (uint8_t *)&watcher,
|
|
+ .dsize = sizeof(struct notifyd_watcher),
|
|
+ },
|
|
+ {
|
|
+ .dptr = (uint8_t *)instances,
|
|
+ .dsize = sizeof(struct notifyd_instance) *
|
|
+ num_instances,
|
|
+ },
|
|
+ };
|
|
|
|
- status = dbwrap_record_store(rec, value, 0);
|
|
+ status = dbwrap_record_storev(rec, iov, ARRAY_SIZE(iov), 0);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- DBG_WARNING("dbwrap_record_store returned %s\n",
|
|
+ DBG_WARNING("dbwrap_record_storev returned %s\n",
|
|
nt_errstr(status));
|
|
goto fail;
|
|
}
|
|
@@ -706,12 +799,18 @@ static void notifyd_trigger_parser(TDB_DATA key, TDB_DATA data,
|
|
.when = tstate->msg->when };
|
|
struct iovec iov[2];
|
|
size_t path_len = key.dsize;
|
|
+ struct notifyd_watcher watcher = {};
|
|
struct notifyd_instance *instances = NULL;
|
|
size_t num_instances = 0;
|
|
size_t i;
|
|
+ bool ok;
|
|
|
|
- if (!notifyd_parse_entry(data.dptr, data.dsize, &instances,
|
|
- &num_instances)) {
|
|
+ ok = notifyd_parse_entry(data.dptr,
|
|
+ data.dsize,
|
|
+ &watcher,
|
|
+ &instances,
|
|
+ &num_instances);
|
|
+ if (!ok) {
|
|
DBG_DEBUG("Could not parse notifyd_entry\n");
|
|
return;
|
|
}
|
|
@@ -734,9 +833,11 @@ static void notifyd_trigger_parser(TDB_DATA key, TDB_DATA data,
|
|
|
|
if (tstate->covered_by_sys_notify) {
|
|
if (tstate->recursive) {
|
|
- i_filter = instance->internal_subdir_filter;
|
|
+ i_filter = watcher.sys_subdir_filter &
|
|
+ instance->instance.subdir_filter;
|
|
} else {
|
|
- i_filter = instance->internal_filter;
|
|
+ i_filter = watcher.sys_filter &
|
|
+ instance->instance.filter;
|
|
}
|
|
} else {
|
|
if (tstate->recursive) {
|
|
@@ -1142,46 +1243,39 @@ static int notifyd_add_proxy_syswatches(struct db_record *rec,
|
|
struct db_context *db = dbwrap_record_get_db(rec);
|
|
TDB_DATA key = dbwrap_record_get_key(rec);
|
|
TDB_DATA value = dbwrap_record_get_value(rec);
|
|
- struct notifyd_instance *instances = NULL;
|
|
- size_t num_instances = 0;
|
|
- size_t i;
|
|
+ struct notifyd_watcher watcher = {};
|
|
char path[key.dsize+1];
|
|
bool ok;
|
|
+ int ret;
|
|
|
|
memcpy(path, key.dptr, key.dsize);
|
|
path[key.dsize] = '\0';
|
|
|
|
- ok = notifyd_parse_entry(value.dptr, value.dsize, &instances,
|
|
- &num_instances);
|
|
+ /* This is a remote database, we just need the watcher. */
|
|
+ ok = notifyd_parse_entry(value.dptr, value.dsize, &watcher, NULL, NULL);
|
|
if (!ok) {
|
|
DBG_WARNING("Could not parse notifyd entry for %s\n", path);
|
|
return 0;
|
|
}
|
|
|
|
- for (i=0; i<num_instances; i++) {
|
|
- struct notifyd_instance *instance = &instances[i];
|
|
- uint32_t filter = instance->instance.filter;
|
|
- uint32_t subdir_filter = instance->instance.subdir_filter;
|
|
- int ret;
|
|
+ watcher.sys_watch = NULL;
|
|
+ watcher.sys_filter = watcher.filter;
|
|
+ watcher.sys_subdir_filter = watcher.subdir_filter;
|
|
|
|
- /*
|
|
- * This is a remote database. Pointers that we were
|
|
- * given don't make sense locally. Initialize to NULL
|
|
- * in case sys_notify_watch fails.
|
|
- */
|
|
- instances[i].sys_watch = NULL;
|
|
-
|
|
- ret = state->sys_notify_watch(
|
|
- db, state->sys_notify_ctx, path,
|
|
- &filter, &subdir_filter,
|
|
- notifyd_sys_callback, state->msg_ctx,
|
|
- &instance->sys_watch);
|
|
- if (ret != 0) {
|
|
- DBG_WARNING("inotify_watch returned %s\n",
|
|
- strerror(errno));
|
|
- }
|
|
+ ret = state->sys_notify_watch(db,
|
|
+ state->sys_notify_ctx,
|
|
+ path,
|
|
+ &watcher.filter,
|
|
+ &watcher.subdir_filter,
|
|
+ notifyd_sys_callback,
|
|
+ state->msg_ctx,
|
|
+ &watcher.sys_watch);
|
|
+ if (ret != 0) {
|
|
+ DBG_WARNING("inotify_watch returned %s\n", strerror(errno));
|
|
}
|
|
|
|
+ memcpy(value.dptr, &watcher, sizeof(struct notifyd_watcher));
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1189,21 +1283,17 @@ static int notifyd_db_del_syswatches(struct db_record *rec, void *private_data)
|
|
{
|
|
TDB_DATA key = dbwrap_record_get_key(rec);
|
|
TDB_DATA value = dbwrap_record_get_value(rec);
|
|
- struct notifyd_instance *instances = NULL;
|
|
- size_t num_instances = 0;
|
|
- size_t i;
|
|
+ struct notifyd_watcher watcher = {};
|
|
bool ok;
|
|
|
|
- ok = notifyd_parse_entry(value.dptr, value.dsize, &instances,
|
|
- &num_instances);
|
|
+ ok = notifyd_parse_entry(value.dptr, value.dsize, &watcher, NULL, NULL);
|
|
if (!ok) {
|
|
DBG_WARNING("Could not parse notifyd entry for %.*s\n",
|
|
(int)key.dsize, (char *)key.dptr);
|
|
return 0;
|
|
}
|
|
- for (i=0; i<num_instances; i++) {
|
|
- TALLOC_FREE(instances[i].sys_watch);
|
|
- }
|
|
+ TALLOC_FREE(watcher.sys_watch);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/source3/smbd/notifyd/notifyd_db.c b/source3/smbd/notifyd/notifyd_db.c
|
|
index 18228619e9a..7dc3cd58081 100644
|
|
--- a/source3/smbd/notifyd/notifyd_db.c
|
|
+++ b/source3/smbd/notifyd/notifyd_db.c
|
|
@@ -40,7 +40,10 @@ static bool notifyd_parse_db_parser(TDB_DATA key, TDB_DATA value,
|
|
memcpy(path, key.dptr, key.dsize);
|
|
path[key.dsize] = 0;
|
|
|
|
- ok = notifyd_parse_entry(value.dptr, value.dsize, &instances,
|
|
+ ok = notifyd_parse_entry(value.dptr,
|
|
+ value.dsize,
|
|
+ NULL,
|
|
+ &instances,
|
|
&num_instances);
|
|
if (!ok) {
|
|
DBG_DEBUG("Could not parse entry for path %s\n", path);
|
|
diff --git a/source3/smbd/notifyd/notifyd_entry.c b/source3/smbd/notifyd/notifyd_entry.c
|
|
index 539010de03a..f3b0e908136 100644
|
|
--- a/source3/smbd/notifyd/notifyd_entry.c
|
|
+++ b/source3/smbd/notifyd/notifyd_entry.c
|
|
@@ -21,22 +21,51 @@
|
|
* Parse an entry in the notifyd_context->entries database
|
|
*/
|
|
|
|
-bool notifyd_parse_entry(
|
|
- uint8_t *buf,
|
|
- size_t buflen,
|
|
- struct notifyd_instance **instances,
|
|
- size_t *num_instances)
|
|
+/**
|
|
+ * @brief Parse a notifyd database entry.
|
|
+ *
|
|
+ * The memory we pass down needs to be aligned. If it isn't aligned we can run
|
|
+ * into obscure errors as we just point into the data buffer.
|
|
+ *
|
|
+ * @param data The data to parse
|
|
+ * @param data_len The length of the data to parse
|
|
+ * @param watcher A pointer to store the watcher data or NULL.
|
|
+ * @param instances A pointer to store the array of notify instances or NULL.
|
|
+ * @param pnum_instances The number of elements in the array. If you just want
|
|
+ * the number of elements pass NULL for the watcher and instances pointers.
|
|
+ *
|
|
+ * @return true on success, false if an error occurred.
|
|
+ */
|
|
+bool notifyd_parse_entry(uint8_t *data,
|
|
+ size_t data_len,
|
|
+ struct notifyd_watcher *watcher,
|
|
+ struct notifyd_instance **instances,
|
|
+ size_t *pnum_instances)
|
|
{
|
|
- if ((buflen % sizeof(struct notifyd_instance)) != 0) {
|
|
- DBG_WARNING("invalid buffer size: %zu\n", buflen);
|
|
+ size_t ilen;
|
|
+
|
|
+ if (data_len < sizeof(struct notifyd_watcher)) {
|
|
return false;
|
|
}
|
|
|
|
- if (instances != NULL) {
|
|
- *instances = (struct notifyd_instance *)buf;
|
|
+ if (watcher != NULL) {
|
|
+ *watcher = *((struct notifyd_watcher *)(uintptr_t)data);
|
|
}
|
|
- if (num_instances != NULL) {
|
|
- *num_instances = buflen / sizeof(struct notifyd_instance);
|
|
+
|
|
+ ilen = data_len - sizeof(struct notifyd_watcher);
|
|
+ if ((ilen % sizeof(struct notifyd_instance)) != 0) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (pnum_instances != NULL) {
|
|
+ *pnum_instances = ilen / sizeof(struct notifyd_instance);
|
|
}
|
|
+ if (instances != NULL) {
|
|
+ /* The (uintptr_t) cast removes a warning from -Wcast-align. */
|
|
+ *instances =
|
|
+ (struct notifyd_instance *)(uintptr_t)
|
|
+ (data + sizeof(struct notifyd_watcher));
|
|
+ }
|
|
+
|
|
return true;
|
|
}
|
|
diff --git a/source3/smbd/notifyd/notifyd_private.h b/source3/smbd/notifyd/notifyd_private.h
|
|
index 36c08f47c54..db8e6e1c005 100644
|
|
--- a/source3/smbd/notifyd/notifyd_private.h
|
|
+++ b/source3/smbd/notifyd/notifyd_private.h
|
|
@@ -20,30 +20,48 @@
|
|
#include "lib/util/server_id.h"
|
|
#include "notifyd.h"
|
|
|
|
+
|
|
/*
|
|
- * notifyd's representation of a notify instance
|
|
+ * Representation of a watcher for a path
|
|
+ *
|
|
+ * This will be stored in the db.
|
|
*/
|
|
-struct notifyd_instance {
|
|
- struct server_id client;
|
|
- struct notify_instance instance;
|
|
-
|
|
- void *sys_watch; /* inotify/fam/etc handle */
|
|
+struct notifyd_watcher {
|
|
+ /*
|
|
+ * This is an intersections of the filter the watcher is listening for.
|
|
+ */
|
|
+ uint32_t filter;
|
|
+ uint32_t subdir_filter;
|
|
|
|
/*
|
|
- * Filters after sys_watch took responsibility of some bits
|
|
+ * Those are inout variables passed to the sys_watcher. The sys_watcher
|
|
+ * will remove the bits it can't handle.
|
|
*/
|
|
- uint32_t internal_filter;
|
|
- uint32_t internal_subdir_filter;
|
|
+ uint32_t sys_filter;
|
|
+ uint32_t sys_subdir_filter;
|
|
+
|
|
+ /* The handle for inotify/fam etc. */
|
|
+ void *sys_watch;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Representation of a notifyd instance
|
|
+ *
|
|
+ * This will be stored in the db.
|
|
+ */
|
|
+struct notifyd_instance {
|
|
+ struct server_id client;
|
|
+ struct notify_instance instance;
|
|
};
|
|
|
|
/*
|
|
* Parse an entry in the notifyd_context->entries database
|
|
*/
|
|
|
|
-bool notifyd_parse_entry(
|
|
- uint8_t *buf,
|
|
- size_t buflen,
|
|
- struct notifyd_instance **instances,
|
|
- size_t *num_instances);
|
|
+bool notifyd_parse_entry(uint8_t *data,
|
|
+ size_t data_len,
|
|
+ struct notifyd_watcher *watcher,
|
|
+ struct notifyd_instance **instances,
|
|
+ size_t *num_instances);
|
|
|
|
#endif
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From b04cb93ee52aac0ce7213d0581d69e852df52d4a Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Mon, 5 Feb 2024 15:03:48 +0100
|
|
Subject: [PATCH 030/122] smbd: simplify handling of failing fstat() after
|
|
unlinking file
|
|
|
|
close_remove_share_mode() already called vfs_stat_fsp(), so we can skip the
|
|
fstat() triggered in fd_close() by fsp->fsp_flags.fstat_before_close being true.
|
|
|
|
This avoids getting an EACCESS error when doing an fstat() on the removed file
|
|
which seems to happen with some FUSE filesystems.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15527
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Jeremy Allison <jra@samba.org>
|
|
(cherry picked from commit 6e6324cff29089a636823786183222a73fe7cb28)
|
|
---
|
|
source3/smbd/close.c | 1 +
|
|
source3/smbd/open.c | 15 +--------------
|
|
2 files changed, 2 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
|
|
index af5e78daa10..e16cb2d3485 100644
|
|
--- a/source3/smbd/close.c
|
|
+++ b/source3/smbd/close.c
|
|
@@ -603,6 +603,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
|
|
*/
|
|
|
|
fsp->fsp_flags.delete_on_close = false;
|
|
+ fsp->fsp_flags.fstat_before_close = false;
|
|
lck_state.reset_delete_on_close = true;
|
|
|
|
done:
|
|
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
|
|
index 3581c4b9173..93c12e00eb0 100644
|
|
--- a/source3/smbd/open.c
|
|
+++ b/source3/smbd/open.c
|
|
@@ -997,20 +997,7 @@ NTSTATUS fd_close(files_struct *fsp)
|
|
if (fsp->fsp_flags.fstat_before_close) {
|
|
status = vfs_stat_fsp(fsp);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- /*
|
|
- * If this is a stream and delete-on-close was set, the
|
|
- * backing object (an xattr from streams_xattr) might
|
|
- * already be deleted so fstat() fails with
|
|
- * NT_STATUS_NOT_FOUND. So if fsp refers to a stream we
|
|
- * ignore the error and only bail for normal files where
|
|
- * an fstat() should still work. NB. We cannot use
|
|
- * fsp_is_alternate_stream(fsp) for this as the base_fsp
|
|
- * has already been closed at this point and so the value
|
|
- * fsp_is_alternate_stream() checks for is already NULL.
|
|
- */
|
|
- if (fsp->fsp_name->stream_name == NULL) {
|
|
- return status;
|
|
- }
|
|
+ return status;
|
|
}
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 29f0c0fb2f1cb0cfc4c615d31e82048b46a2cb0d Mon Sep 17 00:00:00 2001
|
|
From: Noel Power <noel.power@suse.com>
|
|
Date: Tue, 20 Feb 2024 09:26:29 +0000
|
|
Subject: [PATCH 031/122] s3/smbd: If we fail to close file_handle ensure we
|
|
should reset the fd
|
|
|
|
if fsp_flags.fstat_before_close == true then close_file_smb will call
|
|
vfs_stat which can fail. If it does fail then the fd associated
|
|
with the file handle will still be set (and we will hit an assert
|
|
is the file handle destructor) when calling file_free.
|
|
We need to set fd to -1 to avoid that. To achieve that we capture and
|
|
return the vfs_stat_fsp failure status while still processing the rest
|
|
of the fd_close logic.
|
|
|
|
[2024/02/20 09:23:48.454671, 0, pid=9744] ../../source3/smbd/smb2_close.c:226(smbd_smb2_close)
|
|
smbd_smb2_close: close_file[]: NT_STATUS_ACCESS_DENIED
|
|
[2024/02/20 09:23:48.454757, 0, pid=9744] ../../source3/smbd/fd_handle.c:40(fd_handle_destructor)
|
|
PANIC: assert failed at ../../source3/smbd/fd_handle.c(40): (fh->fd == -1) || (fh->fd == AT_FDCWD)
|
|
[2024/02/20 09:23:48.454781, 0, pid=9744] ../../lib/util/fault.c:178(smb_panic_log)
|
|
===============================================================
|
|
[2024/02/20 09:23:48.454804, 0, pid=9744] ../../lib/util/fault.c:185(smb_panic_log)
|
|
INTERNAL ERROR: assert failed: (fh->fd == -1) || (fh->fd == AT_FDCWD) in smbd (smbd[192.168.10) (client [192.168.100.15]) pid 9744 (4.21.0pre1-DEVELOPERBUILD)
|
|
[2024/02/20 09:23:48.454844, 0, pid=9744] ../../lib/util/fault.c:190(smb_panic_log)
|
|
If you are running a recent Samba version, and if you think this problem is not yet fixed in the latest versions, please consider reporting this bug, see https://wiki.samba.org/index.php/Bug_Reporting
|
|
[2024/02/20 09:23:48.454869, 0, pid=9744] ../../lib/util/fault.c:191(smb_panic_log)
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15527
|
|
Signed-off-by: Noel Power <noel.power@suse.com>
|
|
Reviewed-by: Jeremy Allison <jra@samba.org>
|
|
|
|
Autobuild-User(master): Noel Power <npower@samba.org>
|
|
Autobuild-Date(master): Wed Mar 13 10:34:45 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 6ee3f809a54d7b833ff798e68a93ada00a215d4d)
|
|
---
|
|
source3/smbd/open.c | 14 ++++++++------
|
|
1 file changed, 8 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
|
|
index 93c12e00eb0..74be444fef5 100644
|
|
--- a/source3/smbd/open.c
|
|
+++ b/source3/smbd/open.c
|
|
@@ -987,7 +987,7 @@ NTSTATUS fd_openat(const struct files_struct *dirfsp,
|
|
|
|
NTSTATUS fd_close(files_struct *fsp)
|
|
{
|
|
- NTSTATUS status;
|
|
+ NTSTATUS stat_status = NT_STATUS_OK;
|
|
int ret;
|
|
|
|
if (fsp == fsp->conn->cwd_fsp) {
|
|
@@ -995,10 +995,12 @@ NTSTATUS fd_close(files_struct *fsp)
|
|
}
|
|
|
|
if (fsp->fsp_flags.fstat_before_close) {
|
|
- status = vfs_stat_fsp(fsp);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- return status;
|
|
- }
|
|
+ /*
|
|
+ * capture status, if failure
|
|
+ * continue close processing
|
|
+ * and return status
|
|
+ */
|
|
+ stat_status = vfs_stat_fsp(fsp);
|
|
}
|
|
|
|
if (fsp->dptr) {
|
|
@@ -1020,7 +1022,7 @@ NTSTATUS fd_close(files_struct *fsp)
|
|
if (ret == -1) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
- return NT_STATUS_OK;
|
|
+ return stat_status;
|
|
}
|
|
|
|
/****************************************************************************
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From ed138c4d679e8291de18162e1cac65cc9da33b4d Mon Sep 17 00:00:00 2001
|
|
From: Jeremy Allison <jra@samba.org>
|
|
Date: Wed, 15 Jan 2025 10:21:19 -0800
|
|
Subject: [PATCH 032/122] auth: Add missing talloc_free() in error code path.
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15782
|
|
|
|
Signed-off-by: Jeremy Allison <jra@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
|
|
Autobuild-User(master): Günther Deschner <gd@samba.org>
|
|
Autobuild-Date(master): Thu Jan 16 14:32:39 UTC 2025 on atb-devel-224
|
|
|
|
(cherry picked from commit c514ce8dcadcbbf0d86f3038d2be0f9253a76b75)
|
|
---
|
|
auth/kerberos/kerberos_pac.c | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
diff --git a/auth/kerberos/kerberos_pac.c b/auth/kerberos/kerberos_pac.c
|
|
index b914075d85c..196654b36bd 100644
|
|
--- a/auth/kerberos/kerberos_pac.c
|
|
+++ b/auth/kerberos/kerberos_pac.c
|
|
@@ -351,6 +351,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
if (ret) {
|
|
DEBUG(5, ("PAC Decode: Failed to verify the service "
|
|
"signature: %s\n", error_message(ret)));
|
|
+ talloc_free(tmp_ctx);
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From f8a7d7a3e8c3be3c7742c874239766b34c25ef3e Mon Sep 17 00:00:00 2001
|
|
From: Jeremy Allison <jra@samba.org>
|
|
Date: Thu, 16 Jan 2025 16:12:31 -0800
|
|
Subject: [PATCH 033/122] auth: Cleanup exit code paths in
|
|
kerberos_decode_pac().
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
One more memory leak missed and now fixed. tmp_ctx
|
|
must be freed once the pac data is talloc_move'd.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15782
|
|
|
|
Signed-off-by: Jeremy Allison <jra@samba.org>
|
|
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
|
|
Reviewed-by: Christian Ambach <ambi@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
|
|
Autobuild-User(master): Günther Deschner <gd@samba.org>
|
|
Autobuild-Date(master): Fri Jan 17 12:01:47 UTC 2025 on atb-devel-224
|
|
|
|
(cherry picked from commit f9eb0b248da0689c82656f3e482161c45749afb6)
|
|
---
|
|
auth/kerberos/kerberos_pac.c | 88 ++++++++++++++++++------------------
|
|
1 file changed, 43 insertions(+), 45 deletions(-)
|
|
|
|
diff --git a/auth/kerberos/kerberos_pac.c b/auth/kerberos/kerberos_pac.c
|
|
index 196654b36bd..abb096bde1b 100644
|
|
--- a/auth/kerberos/kerberos_pac.c
|
|
+++ b/auth/kerberos/kerberos_pac.c
|
|
@@ -128,7 +128,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
time_t tgs_authtime,
|
|
struct PAC_DATA **pac_data_out)
|
|
{
|
|
- NTSTATUS status;
|
|
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
enum ndr_err_code ndr_err;
|
|
krb5_error_code ret;
|
|
DATA_BLOB modified_pac_blob;
|
|
@@ -164,8 +164,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
kdc_sig_wipe = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA);
|
|
srv_sig_wipe = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA);
|
|
if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_NO_MEMORY;
|
|
+ status = NT_STATUS_NO_MEMORY;
|
|
+ goto out;
|
|
}
|
|
|
|
ndr_err = ndr_pull_struct_blob(&pac_data_blob, pac_data, pac_data,
|
|
@@ -174,15 +174,14 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DEBUG(0,("can't parse the PAC: %s\n",
|
|
nt_errstr(status)));
|
|
- talloc_free(tmp_ctx);
|
|
- return status;
|
|
+ goto out;
|
|
}
|
|
|
|
if (pac_data->num_buffers < 4) {
|
|
/* we need logon_ingo, service_key and kdc_key */
|
|
DEBUG(0,("less than 4 PAC buffers\n"));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
ndr_err = ndr_pull_struct_blob(
|
|
@@ -192,15 +191,14 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DEBUG(0,("can't parse the PAC: %s\n",
|
|
nt_errstr(status)));
|
|
- talloc_free(tmp_ctx);
|
|
- return status;
|
|
+ goto out;
|
|
}
|
|
|
|
if (pac_data_raw->num_buffers < 4) {
|
|
/* we need logon_ingo, service_key and kdc_key */
|
|
DEBUG(0,("less than 4 PAC buffers\n"));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
if (pac_data->num_buffers != pac_data_raw->num_buffers) {
|
|
@@ -208,8 +206,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
DEBUG(0, ("misparse! PAC_DATA has %d buffers while "
|
|
"PAC_DATA_RAW has %d\n", pac_data->num_buffers,
|
|
pac_data_raw->num_buffers));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
for (i=0; i < pac_data->num_buffers; i++) {
|
|
@@ -220,8 +218,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
DEBUG(0, ("misparse! PAC_DATA buffer %d has type "
|
|
"%d while PAC_DATA_RAW has %d\n", i,
|
|
data_buf->type, raw_buf->type));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
switch (data_buf->type) {
|
|
case PAC_TYPE_LOGON_INFO:
|
|
@@ -254,26 +252,26 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
|
|
if (!logon_info) {
|
|
DEBUG(0,("PAC no logon_info\n"));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
if (!logon_name) {
|
|
DEBUG(0,("PAC no logon_name\n"));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
if (!srv_sig_ptr || !srv_sig_blob) {
|
|
DEBUG(0,("PAC no srv_key\n"));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
if (!kdc_sig_ptr || !kdc_sig_blob) {
|
|
DEBUG(0,("PAC no kdc_key\n"));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
/* Find and zero out the signatures,
|
|
@@ -288,8 +286,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DEBUG(0,("can't parse the KDC signature: %s\n",
|
|
nt_errstr(status)));
|
|
- talloc_free(tmp_ctx);
|
|
- return status;
|
|
+ goto out;
|
|
}
|
|
|
|
ndr_err = ndr_pull_struct_blob(
|
|
@@ -299,8 +296,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DEBUG(0,("can't parse the SRV signature: %s\n",
|
|
nt_errstr(status)));
|
|
- talloc_free(tmp_ctx);
|
|
- return status;
|
|
+ goto out;
|
|
}
|
|
|
|
/* Now zero the decoded structure */
|
|
@@ -317,8 +313,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DEBUG(0,("can't repack the KDC signature: %s\n",
|
|
nt_errstr(status)));
|
|
- talloc_free(tmp_ctx);
|
|
- return status;
|
|
+ goto out;
|
|
}
|
|
ndr_err = ndr_push_struct_blob(
|
|
srv_sig_blob, pac_data_raw, srv_sig_wipe,
|
|
@@ -327,8 +322,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DEBUG(0,("can't repack the SRV signature: %s\n",
|
|
nt_errstr(status)));
|
|
- talloc_free(tmp_ctx);
|
|
- return status;
|
|
+ goto out;
|
|
}
|
|
|
|
/* push out the whole structure, but now with zero'ed signatures */
|
|
@@ -339,8 +333,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
status = ndr_map_error2ntstatus(ndr_err);
|
|
DEBUG(0,("can't repack the RAW PAC: %s\n",
|
|
nt_errstr(status)));
|
|
- talloc_free(tmp_ctx);
|
|
- return status;
|
|
+ goto out;
|
|
}
|
|
|
|
if (service_keyblock) {
|
|
@@ -351,8 +344,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
if (ret) {
|
|
DEBUG(5, ("PAC Decode: Failed to verify the service "
|
|
"signature: %s\n", error_message(ret)));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_ACCESS_DENIED;
|
|
+ status = NT_STATUS_ACCESS_DENIED;
|
|
+ goto out;
|
|
}
|
|
|
|
if (krbtgt_keyblock) {
|
|
@@ -362,8 +355,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
if (ret) {
|
|
DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
|
|
smb_get_krb5_error_message(context, ret, tmp_ctx)));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_ACCESS_DENIED;
|
|
+ status = NT_STATUS_ACCESS_DENIED;
|
|
+ goto out;
|
|
}
|
|
}
|
|
}
|
|
@@ -379,8 +372,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
nt_time_string(tmp_ctx, logon_name->logon_time)));
|
|
DEBUG(2, ("PAC Decode: Ticket: %s\n",
|
|
nt_time_string(tmp_ctx, tgs_authtime_nttime)));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_ACCESS_DENIED;
|
|
+ status = NT_STATUS_ACCESS_DENIED;
|
|
+ goto out;
|
|
}
|
|
}
|
|
|
|
@@ -392,8 +385,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
if (ret) {
|
|
DEBUG(2, ("Could not unparse name from ticket to match with name from PAC: [%s]:%s\n",
|
|
logon_name->account_name, error_message(ret)));
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
+ status = NT_STATUS_INVALID_PARAMETER;
|
|
+ goto out;
|
|
}
|
|
|
|
bool_ret = strcmp(client_principal_string, logon_name->account_name) == 0;
|
|
@@ -404,8 +397,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
logon_name->account_name,
|
|
client_principal_string));
|
|
SAFE_FREE(client_principal_string);
|
|
- talloc_free(tmp_ctx);
|
|
- return NT_STATUS_ACCESS_DENIED;
|
|
+ status = NT_STATUS_ACCESS_DENIED;
|
|
+ goto out;
|
|
}
|
|
SAFE_FREE(client_principal_string);
|
|
|
|
@@ -426,10 +419,15 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
|
}
|
|
|
|
if (pac_data_out) {
|
|
- *pac_data_out = talloc_steal(mem_ctx, pac_data);
|
|
+ *pac_data_out = talloc_move(mem_ctx, &pac_data);
|
|
}
|
|
|
|
- return NT_STATUS_OK;
|
|
+ status = NT_STATUS_OK;
|
|
+
|
|
+ out:
|
|
+
|
|
+ TALLOC_FREE(tmp_ctx);
|
|
+ return status;
|
|
}
|
|
|
|
NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 9fd06d5c331f5babaf417cc7339d12854a79fe4b Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 15 Feb 2024 17:29:46 +0100
|
|
Subject: [PATCH 034/122] s3:libsmb/dsgetdcname: use
|
|
NETLOGON_NT_VERSION_AVOID_NT4EMUL
|
|
|
|
In 2024 we always want an active directory response...
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15620
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
(cherry picked from commit 2b66663c75cdb3bc1b6bc5b1736dd9d35b094b42)
|
|
---
|
|
source3/libsmb/dsgetdcname.c | 5 +++++
|
|
1 file changed, 5 insertions(+)
|
|
|
|
diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c
|
|
index 280ccd585b0..6fcaa26810c 100644
|
|
--- a/source3/libsmb/dsgetdcname.c
|
|
+++ b/source3/libsmb/dsgetdcname.c
|
|
@@ -930,6 +930,11 @@ static NTSTATUS process_dc_netbios(TALLOC_CTX *mem_ctx,
|
|
name_type = NBT_NAME_PDC;
|
|
}
|
|
|
|
+ /*
|
|
+ * It's 2024 we always want an AD style response!
|
|
+ */
|
|
+ nt_version |= NETLOGON_NT_VERSION_AVOID_NT4EMUL;
|
|
+
|
|
nt_version |= map_ds_flags_to_nt_version(flags);
|
|
|
|
snprintf(my_acct_name,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 58e28d056f2df0906ee77ccfb9b56e8a764b38b4 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Tue, 7 May 2024 14:53:24 +0000
|
|
Subject: [PATCH 035/122] s3:libsmb: allow store_cldap_reply() to work with a
|
|
ipv6 response
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15642
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
|
|
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
|
|
Autobuild-Date(master): Fri May 10 01:35:18 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 712ffbffc03c7dcd551c1e22815ebe7c0b9b45d2)
|
|
---
|
|
source3/libsmb/dsgetdcname.c | 24 +++++++++++++++++++++++-
|
|
1 file changed, 23 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c
|
|
index 6fcaa26810c..da173e7bbb0 100644
|
|
--- a/source3/libsmb/dsgetdcname.c
|
|
+++ b/source3/libsmb/dsgetdcname.c
|
|
@@ -196,7 +196,29 @@ static NTSTATUS store_cldap_reply(TALLOC_CTX *mem_ctx,
|
|
/* FIXME */
|
|
r->sockaddr_size = 0x10; /* the w32 winsock addr size */
|
|
r->sockaddr.sockaddr_family = 2; /* AF_INET */
|
|
- r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, addr);
|
|
+ if (is_ipaddress_v4(addr)) {
|
|
+ r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, addr);
|
|
+ if (r->sockaddr.pdc_ip == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+ } else {
|
|
+ /*
|
|
+ * ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX will
|
|
+ * fail with an ipv6 address.
|
|
+ *
|
|
+ * This matches windows behaviour in the CLDAP
|
|
+ * response when NETLOGON_NT_VERSION_5EX_WITH_IP
|
|
+ * is used.
|
|
+ *
|
|
+ * Windows returns the ipv4 address of the ipv6
|
|
+ * server interface and falls back to 127.0.0.1
|
|
+ * if there's no ipv4 address.
|
|
+ */
|
|
+ r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, "127.0.0.1");
|
|
+ if (r->sockaddr.pdc_ip == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+ }
|
|
|
|
ndr_err = ndr_push_struct_blob(&blob, mem_ctx, r,
|
|
(ndr_push_flags_fn_t)ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e4d5269b2359c670acdf0cba81248f148ae68c17 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 11 Oct 2024 13:32:22 +0000
|
|
Subject: [PATCH 036/122] s3:libsmb: let discover_dc_netbios() return
|
|
DOMAIN_CONTROLLER_NOT_FOUND
|
|
|
|
We may get NT_STATUS_NOT_FOUND when the name can't be resolved
|
|
and NT_STATUS_INVALID_ADDRESS if the system doesn't have ipv4
|
|
addresses...
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit e47ce1d10b13d8ef165c70984e6e490f4c2a64c2)
|
|
---
|
|
source3/libsmb/dsgetdcname.c | 14 +++++++++++++-
|
|
1 file changed, 13 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c
|
|
index da173e7bbb0..8278959dd7d 100644
|
|
--- a/source3/libsmb/dsgetdcname.c
|
|
+++ b/source3/libsmb/dsgetdcname.c
|
|
@@ -483,7 +483,19 @@ static NTSTATUS discover_dc_netbios(TALLOC_CTX *mem_ctx,
|
|
&count,
|
|
resolve_order);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(10,("discover_dc_netbios: failed to find DC\n"));
|
|
+ NTSTATUS raw_status = status;
|
|
+
|
|
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
+ status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
|
|
+ }
|
|
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_ADDRESS)) {
|
|
+ status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ DBG_DEBUG("failed to find DC for %s: %s => %s\n",
|
|
+ domain_name,
|
|
+ nt_errstr(raw_status),
|
|
+ nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d90d2b0e985913247f43192cb94eec0efb3e9046 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd@samba.org>
|
|
Date: Wed, 2 Jul 2025 21:59:48 +0200
|
|
Subject: [PATCH 037/122] s3-winbindd: Fix internal winbind dsgetdcname calls
|
|
w.r.t. domain name
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
when winbind calls to dsgetdcname internally, make sure to
|
|
prefer the DNS domain name if we have it. Makes DNS lookups much more
|
|
likely to succeed.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15876
|
|
|
|
Guenther
|
|
|
|
Signed-off-by: Guenther Deschner <gd@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
|
|
Autobuild-User(master): Ralph Böhme <slow@samba.org>
|
|
Autobuild-Date(master): Mon Jul 7 10:44:37 UTC 2025 on atb-devel-224
|
|
|
|
(cherry picked from commit 2560c9b3224816ffd371a62103f65b3aca301ad5)
|
|
---
|
|
source3/winbindd/wb_queryuser.c | 17 +++++++++++++----
|
|
source3/winbindd/wb_sids2xids.c | 17 +++++++++++++----
|
|
source3/winbindd/wb_xids2sids.c | 12 +++++++++---
|
|
source3/winbindd/winbindd_dual.c | 6 +++++-
|
|
source3/winbindd/winbindd_proto.h | 1 +
|
|
source3/winbindd/winbindd_util.c | 19 +++++++++++++++++++
|
|
6 files changed, 60 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/source3/winbindd/wb_queryuser.c b/source3/winbindd/wb_queryuser.c
|
|
index c2758f1b76a..db8e946ba71 100644
|
|
--- a/source3/winbindd/wb_queryuser.c
|
|
+++ b/source3/winbindd/wb_queryuser.c
|
|
@@ -289,10 +289,19 @@ static void wb_queryuser_done(struct tevent_req *subreq)
|
|
|
|
if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
|
|
!state->tried_dclookup) {
|
|
- D_DEBUG("GetNssInfo got DOMAIN_CONTROLLER_NOT_FOUND, calling wb_dsgetdcname_send()\n");
|
|
- subreq = wb_dsgetdcname_send(
|
|
- state, state->ev, state->info->domain_name, NULL, NULL,
|
|
- DS_RETURN_DNS_NAME);
|
|
+ const char *domain_name = find_dns_domain_name(
|
|
+ state->info->domain_name);
|
|
+
|
|
+ D_DEBUG("GetNssInfo got DOMAIN_CONTROLLER_NOT_FOUND, calling "
|
|
+ "wb_dsgetdcname_send(%s)\n",
|
|
+ domain_name);
|
|
+
|
|
+ subreq = wb_dsgetdcname_send(state,
|
|
+ state->ev,
|
|
+ domain_name,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ DS_RETURN_DNS_NAME);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
diff --git a/source3/winbindd/wb_sids2xids.c b/source3/winbindd/wb_sids2xids.c
|
|
index f0f6c23fc20..03e5e7e0258 100644
|
|
--- a/source3/winbindd/wb_sids2xids.c
|
|
+++ b/source3/winbindd/wb_sids2xids.c
|
|
@@ -612,13 +612,22 @@ static void wb_sids2xids_done(struct tevent_req *subreq)
|
|
!state->tried_dclookup) {
|
|
|
|
struct lsa_DomainInfo *d;
|
|
+ const char *domain_name = NULL;
|
|
|
|
- D_DEBUG("Domain controller not found. Calling wb_dsgetdcname_send() to get it.\n");
|
|
d = &state->idmap_doms.domains[state->dom_index];
|
|
|
|
- subreq = wb_dsgetdcname_send(
|
|
- state, state->ev, d->name.string, NULL, NULL,
|
|
- DS_RETURN_DNS_NAME);
|
|
+ domain_name = find_dns_domain_name(d->name.string);
|
|
+
|
|
+ D_DEBUG("Domain controller not found. Calling "
|
|
+ "wb_dsgetdcname_send(%s) to get it.\n",
|
|
+ domain_name);
|
|
+
|
|
+ subreq = wb_dsgetdcname_send(state,
|
|
+ state->ev,
|
|
+ domain_name,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ DS_RETURN_DNS_NAME);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
diff --git a/source3/winbindd/wb_xids2sids.c b/source3/winbindd/wb_xids2sids.c
|
|
index 86bd7f9deab..6fcf524d94f 100644
|
|
--- a/source3/winbindd/wb_xids2sids.c
|
|
+++ b/source3/winbindd/wb_xids2sids.c
|
|
@@ -143,9 +143,15 @@ static void wb_xids2sids_dom_done(struct tevent_req *subreq)
|
|
if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
|
|
!state->tried_dclookup) {
|
|
|
|
- subreq = wb_dsgetdcname_send(
|
|
- state, state->ev, state->dom_map->name, NULL, NULL,
|
|
- DS_RETURN_DNS_NAME);
|
|
+ const char *domain_name = find_dns_domain_name(
|
|
+ state->dom_map->name);
|
|
+
|
|
+ subreq = wb_dsgetdcname_send(state,
|
|
+ state->ev,
|
|
+ domain_name,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ DS_RETURN_DNS_NAME);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
|
|
index 36562ab10b8..02a10e41537 100644
|
|
--- a/source3/winbindd/winbindd_dual.c
|
|
+++ b/source3/winbindd/winbindd_dual.c
|
|
@@ -532,6 +532,7 @@ static void wb_domain_request_trigger(struct tevent_req *req,
|
|
struct wb_domain_request_state *state = tevent_req_data(
|
|
req, struct wb_domain_request_state);
|
|
struct winbindd_domain *domain = state->domain;
|
|
+ const char *domain_name = NULL;
|
|
struct tevent_req *subreq = NULL;
|
|
size_t shortest_queue_length;
|
|
|
|
@@ -604,8 +605,11 @@ static void wb_domain_request_trigger(struct tevent_req *req,
|
|
* which is indicated by DS_RETURN_DNS_NAME.
|
|
* For NT4 domains we still get the netbios name.
|
|
*/
|
|
+
|
|
+ domain_name = find_dns_domain_name(state->domain->name);
|
|
+
|
|
subreq = wb_dsgetdcname_send(state, state->ev,
|
|
- state->domain->name,
|
|
+ domain_name,
|
|
NULL, /* domain_guid */
|
|
NULL, /* site_name */
|
|
DS_RETURN_DNS_NAME); /* flags */
|
|
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
|
|
index 9b10f2c061a..4f7dc8a15d6 100644
|
|
--- a/source3/winbindd/winbindd_proto.h
|
|
+++ b/source3/winbindd/winbindd_proto.h
|
|
@@ -567,6 +567,7 @@ bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
|
|
struct dom_sid **sids, uint32_t *num_sids);
|
|
bool parse_xidlist(TALLOC_CTX *mem_ctx, const char *xidstr,
|
|
struct unixid **pxids, uint32_t *pnum_xids);
|
|
+const char *find_dns_domain_name(const char *domain_name);
|
|
|
|
/* The following definitions come from winbindd/winbindd_wins.c */
|
|
|
|
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
|
|
index fe93528787d..eca4116d0c8 100644
|
|
--- a/source3/winbindd/winbindd_util.c
|
|
+++ b/source3/winbindd/winbindd_util.c
|
|
@@ -2181,3 +2181,22 @@ fail:
|
|
TALLOC_FREE(xids);
|
|
return false;
|
|
}
|
|
+
|
|
+/**
|
|
+ * Helper to extract the DNS Domain Name from a struct winbindd_domain
|
|
+ */
|
|
+const char *find_dns_domain_name(const char *domain_name)
|
|
+{
|
|
+ struct winbindd_domain *wbdom = NULL;
|
|
+
|
|
+ wbdom = find_domain_from_name(domain_name);
|
|
+ if (wbdom == NULL) {
|
|
+ return domain_name;
|
|
+ }
|
|
+
|
|
+ if (wbdom->active_directory && wbdom->alt_name != NULL) {
|
|
+ return wbdom->alt_name;
|
|
+ }
|
|
+
|
|
+ return wbdom->name;
|
|
+}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7da6072ce95bca445368f6d0453247c8f92fcdf2 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 9 May 2025 09:38:41 +0200
|
|
Subject: [PATCH 038/122] s3:winbindd: avoid using any netlogon call to get a
|
|
dc name
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15876
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(backported from commit f86a4bf6848ade2db7229d182576db3320c3ece7)
|
|
---
|
|
source3/winbindd/winbindd_cm.c | 145 ---------------------------
|
|
source3/winbindd/winbindd_dual_srv.c | 105 +------------------
|
|
2 files changed, 5 insertions(+), 245 deletions(-)
|
|
|
|
diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c
|
|
index 2ebfb0f6dd8..195259daa43 100644
|
|
--- a/source3/winbindd/winbindd_cm.c
|
|
+++ b/source3/winbindd/winbindd_cm.c
|
|
@@ -475,135 +475,6 @@ static bool cm_is_ipc_credentials(struct cli_credentials *creds)
|
|
return ret;
|
|
}
|
|
|
|
-static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
|
|
- fstring dcname,
|
|
- struct sockaddr_storage *dc_ss,
|
|
- uint32_t request_flags)
|
|
-{
|
|
- struct winbindd_domain *our_domain = NULL;
|
|
- struct rpc_pipe_client *netlogon_pipe = NULL;
|
|
- NTSTATUS result;
|
|
- WERROR werr;
|
|
- TALLOC_CTX *mem_ctx;
|
|
- unsigned int orig_timeout;
|
|
- const char *tmp = NULL;
|
|
- const char *p;
|
|
- struct dcerpc_binding_handle *b;
|
|
-
|
|
- /* Hmmmm. We can only open one connection to the NETLOGON pipe at the
|
|
- * moment.... */
|
|
-
|
|
- if (IS_DC) {
|
|
- return False;
|
|
- }
|
|
-
|
|
- if (domain->primary) {
|
|
- return False;
|
|
- }
|
|
-
|
|
- our_domain = find_our_domain();
|
|
-
|
|
- if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL) {
|
|
- return False;
|
|
- }
|
|
-
|
|
- result = cm_connect_netlogon(our_domain, &netlogon_pipe);
|
|
- if (!NT_STATUS_IS_OK(result)) {
|
|
- talloc_destroy(mem_ctx);
|
|
- return False;
|
|
- }
|
|
-
|
|
- b = netlogon_pipe->binding_handle;
|
|
-
|
|
- /* This call can take a long time - allow the server to time out.
|
|
- 35 seconds should do it. */
|
|
-
|
|
- orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000);
|
|
-
|
|
- if (our_domain->active_directory) {
|
|
- struct netr_DsRGetDCNameInfo *domain_info = NULL;
|
|
-
|
|
- /*
|
|
- * TODO request flags are not respected in the server
|
|
- * (and in some cases, like REQUIRE_PDC, causes an error)
|
|
- */
|
|
- result = dcerpc_netr_DsRGetDCName(b,
|
|
- mem_ctx,
|
|
- our_domain->dcname,
|
|
- domain->name,
|
|
- NULL,
|
|
- NULL,
|
|
- request_flags|DS_RETURN_DNS_NAME,
|
|
- &domain_info,
|
|
- &werr);
|
|
- if (NT_STATUS_IS_OK(result) && W_ERROR_IS_OK(werr)) {
|
|
- tmp = talloc_strdup(
|
|
- mem_ctx, domain_info->dc_unc);
|
|
- if (tmp == NULL) {
|
|
- DEBUG(0, ("talloc_strdup failed\n"));
|
|
- talloc_destroy(mem_ctx);
|
|
- return false;
|
|
- }
|
|
- if (domain->alt_name == NULL) {
|
|
- domain->alt_name = talloc_strdup(domain,
|
|
- domain_info->domain_name);
|
|
- if (domain->alt_name == NULL) {
|
|
- DEBUG(0, ("talloc_strdup failed\n"));
|
|
- talloc_destroy(mem_ctx);
|
|
- return false;
|
|
- }
|
|
- }
|
|
- if (domain->forest_name == NULL) {
|
|
- domain->forest_name = talloc_strdup(domain,
|
|
- domain_info->forest_name);
|
|
- if (domain->forest_name == NULL) {
|
|
- DEBUG(0, ("talloc_strdup failed\n"));
|
|
- talloc_destroy(mem_ctx);
|
|
- return false;
|
|
- }
|
|
- }
|
|
- }
|
|
- } else {
|
|
- result = dcerpc_netr_GetAnyDCName(b, mem_ctx,
|
|
- our_domain->dcname,
|
|
- domain->name,
|
|
- &tmp,
|
|
- &werr);
|
|
- }
|
|
-
|
|
- /* And restore our original timeout. */
|
|
- rpccli_set_timeout(netlogon_pipe, orig_timeout);
|
|
-
|
|
- if (!NT_STATUS_IS_OK(result)) {
|
|
- DEBUG(10,("dcerpc_netr_GetAnyDCName failed: %s\n",
|
|
- nt_errstr(result)));
|
|
- talloc_destroy(mem_ctx);
|
|
- return false;
|
|
- }
|
|
-
|
|
- if (!W_ERROR_IS_OK(werr)) {
|
|
- DEBUG(10,("dcerpc_netr_GetAnyDCName failed: %s\n",
|
|
- win_errstr(werr)));
|
|
- talloc_destroy(mem_ctx);
|
|
- return false;
|
|
- }
|
|
-
|
|
- /* dcerpc_netr_GetAnyDCName gives us a name with \\ */
|
|
- p = strip_hostname(tmp);
|
|
-
|
|
- fstrcpy(dcname, p);
|
|
-
|
|
- talloc_destroy(mem_ctx);
|
|
-
|
|
- DEBUG(10,("dcerpc_netr_GetAnyDCName returned %s\n", dcname));
|
|
-
|
|
- if (!resolve_name(dcname, dc_ss, 0x20, true)) {
|
|
- return False;
|
|
- }
|
|
-
|
|
- return True;
|
|
-}
|
|
-
|
|
/**
|
|
* Helper function to assemble trust password and account name
|
|
*/
|
|
@@ -1279,24 +1150,8 @@ static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
|
|
struct samba_sockaddr *sa_list = NULL;
|
|
size_t salist_size = 0;
|
|
size_t i;
|
|
- bool is_our_domain;
|
|
enum security_types sec = (enum security_types)lp_security();
|
|
|
|
- is_our_domain = strequal(domain->name, lp_workgroup());
|
|
-
|
|
- /* If not our domain, get the preferred DC, by asking our primary DC */
|
|
- if ( !is_our_domain
|
|
- && get_dc_name_via_netlogon(domain, dcname, &ss, request_flags)
|
|
- && add_one_dc_unique(mem_ctx, domain->name, dcname, &ss, dcs,
|
|
- num_dcs) )
|
|
- {
|
|
- char addr[INET6_ADDRSTRLEN];
|
|
- print_sockaddr(addr, sizeof(addr), &ss);
|
|
- DEBUG(10, ("Retrieved DC %s at %s via netlogon\n",
|
|
- dcname, addr));
|
|
- return True;
|
|
- }
|
|
-
|
|
if ((sec == SEC_ADS) && (domain->alt_name != NULL)) {
|
|
char *sitename = NULL;
|
|
|
|
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
|
|
index f0fd18a8fa6..47c68257b12 100644
|
|
--- a/source3/winbindd/winbindd_dual_srv.c
|
|
+++ b/source3/winbindd/winbindd_dual_srv.c
|
|
@@ -662,106 +662,11 @@ NTSTATUS _wbint_QueryUserRidList(struct pipes_struct *p,
|
|
|
|
NTSTATUS _wbint_DsGetDcName(struct pipes_struct *p, struct wbint_DsGetDcName *r)
|
|
{
|
|
- struct winbindd_domain *domain = wb_child_domain();
|
|
- struct rpc_pipe_client *netlogon_pipe;
|
|
- struct netr_DsRGetDCNameInfo *dc_info;
|
|
- NTSTATUS status;
|
|
- WERROR werr;
|
|
- unsigned int orig_timeout;
|
|
- struct dcerpc_binding_handle *b;
|
|
- bool retry = false;
|
|
- bool try_dsrgetdcname = false;
|
|
-
|
|
- if (domain == NULL) {
|
|
- return dsgetdcname(p->mem_ctx, global_messaging_context(),
|
|
- r->in.domain_name, r->in.domain_guid,
|
|
- r->in.site_name ? r->in.site_name : "",
|
|
- r->in.flags,
|
|
- r->out.dc_info);
|
|
- }
|
|
-
|
|
- if (domain->active_directory) {
|
|
- try_dsrgetdcname = true;
|
|
- }
|
|
-
|
|
-reconnect:
|
|
- status = cm_connect_netlogon(domain, &netlogon_pipe);
|
|
-
|
|
- reset_cm_connection_on_error(domain, NULL, status);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(10, ("Can't contact the NETLOGON pipe\n"));
|
|
- return status;
|
|
- }
|
|
-
|
|
- b = netlogon_pipe->binding_handle;
|
|
-
|
|
- /* This call can take a long time - allow the server to time out.
|
|
- 35 seconds should do it. */
|
|
-
|
|
- orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000);
|
|
-
|
|
- if (try_dsrgetdcname) {
|
|
- status = dcerpc_netr_DsRGetDCName(b,
|
|
- p->mem_ctx, domain->dcname,
|
|
- r->in.domain_name, NULL, r->in.domain_guid,
|
|
- r->in.flags, r->out.dc_info, &werr);
|
|
- if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(werr)) {
|
|
- goto done;
|
|
- }
|
|
- if (!retry &&
|
|
- reset_cm_connection_on_error(domain, NULL, status))
|
|
- {
|
|
- retry = true;
|
|
- goto reconnect;
|
|
- }
|
|
- try_dsrgetdcname = false;
|
|
- retry = false;
|
|
- }
|
|
-
|
|
- /*
|
|
- * Fallback to less capable methods
|
|
- */
|
|
-
|
|
- dc_info = talloc_zero(r->out.dc_info, struct netr_DsRGetDCNameInfo);
|
|
- if (dc_info == NULL) {
|
|
- status = NT_STATUS_NO_MEMORY;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- if (r->in.flags & DS_PDC_REQUIRED) {
|
|
- status = dcerpc_netr_GetDcName(b,
|
|
- p->mem_ctx, domain->dcname,
|
|
- r->in.domain_name, &dc_info->dc_unc, &werr);
|
|
- } else {
|
|
- status = dcerpc_netr_GetAnyDCName(b,
|
|
- p->mem_ctx, domain->dcname,
|
|
- r->in.domain_name, &dc_info->dc_unc, &werr);
|
|
- }
|
|
-
|
|
- if (!retry && reset_cm_connection_on_error(domain, b, status)) {
|
|
- retry = true;
|
|
- goto reconnect;
|
|
- }
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(10, ("dcerpc_netr_Get[Any]DCName failed: %s\n",
|
|
- nt_errstr(status)));
|
|
- goto done;
|
|
- }
|
|
- if (!W_ERROR_IS_OK(werr)) {
|
|
- DEBUG(10, ("dcerpc_netr_Get[Any]DCName failed: %s\n",
|
|
- win_errstr(werr)));
|
|
- status = werror_to_ntstatus(werr);
|
|
- goto done;
|
|
- }
|
|
-
|
|
- *r->out.dc_info = dc_info;
|
|
- status = NT_STATUS_OK;
|
|
-
|
|
-done:
|
|
- /* And restore our original timeout. */
|
|
- rpccli_set_timeout(netlogon_pipe, orig_timeout);
|
|
-
|
|
- return status;
|
|
+ return dsgetdcname(p->mem_ctx, global_messaging_context(),
|
|
+ r->in.domain_name, r->in.domain_guid,
|
|
+ r->in.site_name ? r->in.site_name : "",
|
|
+ r->in.flags,
|
|
+ r->out.dc_info);
|
|
}
|
|
|
|
NTSTATUS _wbint_LookupRids(struct pipes_struct *p, struct wbint_LookupRids *r)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From ad54ceadacfbcf0d9c96ad773e50db96003e2c08 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Wed, 23 Jul 2025 15:09:21 +0200
|
|
Subject: [PATCH 039/122] s3:winbindd: Resolve dc name using CLDAP also for
|
|
ROLE_IPA_DC
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
server role ROLE_IPA_DC (introduced in e2d5b4d) needs special handling
|
|
in dcip_check_name(). We should resolve the DC name using:
|
|
- CLDAP in dcip_check_name_ads()
|
|
instead of:
|
|
- NETBIOS in nbt_getdc() that fails if Windows is not providing netbios.
|
|
|
|
The impacted environment has:
|
|
|
|
domain->alt_name = example.com
|
|
domain->active_directory = 1
|
|
security = USER
|
|
server role = ROLE_IPA_DC
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15891
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Pair-programmed-with: Andreas Schneider <asn@samba.org>
|
|
|
|
Reviewed-by: Alexander Bokovoy <ab@samba.org>
|
|
(cherry picked from commit 4921c3304e5e0480e5bb80a757b3f04b3b92c3b1)
|
|
(cherry picked from commit fe8eafc289dfbb6f2b6c706f2a8a68186807d4f8)
|
|
---
|
|
source3/winbindd/winbindd_cm.c | 4 +++-
|
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c
|
|
index 195259daa43..86dbf68f033 100644
|
|
--- a/source3/winbindd/winbindd_cm.c
|
|
+++ b/source3/winbindd/winbindd_cm.c
|
|
@@ -1075,7 +1075,9 @@ static bool dcip_check_name(TALLOC_CTX *mem_ctx,
|
|
|
|
if ((lp_security() == SEC_ADS) && (domain->alt_name != NULL)) {
|
|
is_ad_domain = true;
|
|
- } else if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
|
|
+ } else if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC ||
|
|
+ lp_server_role() == ROLE_IPA_DC)
|
|
+ {
|
|
is_ad_domain = domain->active_directory;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From b73efffbb02903427af2c2cc57171d4848ca11f8 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Mon, 4 Aug 2025 08:35:29 +0200
|
|
Subject: [PATCH 040/122] docs-xml: Make smb.conf 'server role' value
|
|
consistent with ROLE_IPA_DC in libparam
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15891
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Alexander Bokovoy <ab@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit d88268102ade07fab345e04109818d97d8843a14)
|
|
(cherry picked from commit d14fa6eb96a9f296d386ff4864e4f016440f2ac8)
|
|
---
|
|
docs-xml/smbdotconf/security/serverrole.xml | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/docs-xml/smbdotconf/security/serverrole.xml b/docs-xml/smbdotconf/security/serverrole.xml
|
|
index 4ea4e4751ee..40244e125ce 100644
|
|
--- a/docs-xml/smbdotconf/security/serverrole.xml
|
|
+++ b/docs-xml/smbdotconf/security/serverrole.xml
|
|
@@ -78,7 +78,7 @@
|
|
url="http://wiki.samba.org/index.php/Samba4/HOWTO">Samba4
|
|
HOWTO</ulink></para>
|
|
|
|
- <para><anchor id="IPA-DC"/><emphasis>SERVER ROLE = IPA DOMAIN CONTROLLER</emphasis></para>
|
|
+ <para><anchor id="IPA-DC"/><emphasis>SERVER ROLE = IPA PRIMARY DOMAIN CONTROLLER</emphasis></para>
|
|
|
|
<para>This mode of operation runs Samba in a hybrid mode for IPA
|
|
domain controller, providing forest trust to Active Directory.
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 832a4e31630fd441f8ab4325439f90d561cb8fa4 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Mon, 4 Aug 2025 23:26:02 +0200
|
|
Subject: [PATCH 041/122] s3:netlogon: IPA DC is the PDC as well - allow
|
|
ROLE_IPA_DC in _netr_DsRGetForestTrustInformation()
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15891
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Alexander Bokovoy <ab@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit 1dbafcc4e4ff8f39af5ca737b30e9821413dd1f2)
|
|
(cherry picked from commit 00adb3104e745babb2c330fa9c9e324805395edb)
|
|
---
|
|
source3/rpc_server/netlogon/srv_netlog_nt.c | 5 ++++-
|
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/rpc_server/netlogon/srv_netlog_nt.c b/source3/rpc_server/netlogon/srv_netlog_nt.c
|
|
index c5a4b0ef30c..7957d3ab34d 100644
|
|
--- a/source3/rpc_server/netlogon/srv_netlog_nt.c
|
|
+++ b/source3/rpc_server/netlogon/srv_netlog_nt.c
|
|
@@ -2613,7 +2613,10 @@ WERROR _netr_DsRGetForestTrustInformation(struct pipes_struct *p,
|
|
return WERR_INVALID_FLAGS;
|
|
}
|
|
|
|
- if ((r->in.flags & DS_GFTI_UPDATE_TDO) && (lp_server_role() != ROLE_DOMAIN_PDC)) {
|
|
+ if ((r->in.flags & DS_GFTI_UPDATE_TDO) &&
|
|
+ (lp_server_role() != ROLE_DOMAIN_PDC) &&
|
|
+ (lp_server_role() != ROLE_IPA_DC))
|
|
+ {
|
|
p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
|
|
return WERR_NERR_NOTPRIMARY;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 8d5638581dfc539c8524d7a507e8cc8977e827a2 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Mon, 4 Aug 2025 23:28:24 +0200
|
|
Subject: [PATCH 042/122] s3:utils: Allow ROLE_IPA_DC to allow to use Kerberos
|
|
in gensec
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15891
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Alexander Bokovoy <ab@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Tue Aug 5 14:51:51 UTC 2025 on atb-devel-224
|
|
|
|
(cherry picked from commit a4dff82e45308db3ccabac2a55c03d52f04d7b4d)
|
|
|
|
Autobuild-User(v4-22-test): Jule Anger <janger@samba.org>
|
|
Autobuild-Date(v4-22-test): Mon Aug 11 07:53:47 UTC 2025 on atb-devel-224
|
|
|
|
(cherry picked from commit 3364797676624aa9367076a69b2daf73870429ba)
|
|
---
|
|
source3/utils/ntlm_auth.c | 6 +++++-
|
|
1 file changed, 5 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c
|
|
index cff3c53845f..2968ca47734 100644
|
|
--- a/source3/utils/ntlm_auth.c
|
|
+++ b/source3/utils/ntlm_auth.c
|
|
@@ -1341,7 +1341,11 @@ static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
|
|
|
|
cli_credentials_set_conf(server_credentials, lp_ctx);
|
|
|
|
- if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
|
|
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC ||
|
|
+ lp_server_role() == ROLE_IPA_DC ||
|
|
+ lp_security() == SEC_ADS ||
|
|
+ USE_KERBEROS_KEYTAB)
|
|
+ {
|
|
cli_credentials_set_kerberos_state(server_credentials,
|
|
CRED_USE_KERBEROS_DESIRED,
|
|
CRED_SPECIFIED);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 3ef02a381cdc83549506e159ebc457730c06c547 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 22 Jul 2025 19:22:31 +0200
|
|
Subject: [PATCH 043/122] libads: fix get_kdc_ip_string()
|
|
|
|
Correctly handle the interaction between optionally passed in DC via
|
|
pss and DC lookup.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15876
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 23f100f67c0586a940e91e9e1e6f42b804401322)
|
|
---
|
|
source3/libads/kerberos.c | 11 ++++++++---
|
|
1 file changed, 8 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c
|
|
index f74d8eb567c..f324321c87b 100644
|
|
--- a/source3/libads/kerberos.c
|
|
+++ b/source3/libads/kerberos.c
|
|
@@ -523,10 +523,12 @@ static char *get_kdc_ip_string(char *mem_ctx,
|
|
DBG_DEBUG("%zu additional KDCs to test\n", num_dcs);
|
|
if (num_dcs == 0) {
|
|
/*
|
|
- * We do not have additional KDCs, but we have the one passed
|
|
- * in via `pss`. So just use that one and leave.
|
|
+ * We do not have additional KDCs, but if we have one passed
|
|
+ * in via `pss` just use that one, otherwise fail
|
|
*/
|
|
- result = talloc_move(mem_ctx, &kdc_str);
|
|
+ if (pss != NULL) {
|
|
+ result = talloc_move(mem_ctx, &kdc_str);
|
|
+ }
|
|
goto out;
|
|
}
|
|
|
|
@@ -567,6 +569,9 @@ static char *get_kdc_ip_string(char *mem_ctx,
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
|
|
"%s\n", nt_errstr(status)));
|
|
+ if (pss != NULL) {
|
|
+ result = talloc_move(mem_ctx, &kdc_str);
|
|
+ }
|
|
goto out;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From b0dbc167f85deabff2af5b18bc201e8db0d3b97d Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 22 Jul 2025 19:16:14 +0200
|
|
Subject: [PATCH 044/122] winbindd: use find_domain_from_name_noinit() in
|
|
find_dns_domain_name()
|
|
|
|
Avoid triggering a connection to a DC of a trusted domain.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15876
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 9ad2e59a464bb472da2071c61a254547b6497625)
|
|
---
|
|
source3/winbindd/winbindd_util.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
|
|
index eca4116d0c8..3a7a9114988 100644
|
|
--- a/source3/winbindd/winbindd_util.c
|
|
+++ b/source3/winbindd/winbindd_util.c
|
|
@@ -2189,7 +2189,7 @@ const char *find_dns_domain_name(const char *domain_name)
|
|
{
|
|
struct winbindd_domain *wbdom = NULL;
|
|
|
|
- wbdom = find_domain_from_name(domain_name);
|
|
+ wbdom = find_domain_from_name_noinit(domain_name);
|
|
if (wbdom == NULL) {
|
|
return domain_name;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 1961f54ce07f7dc3cfcae5c00b96b39109f08b3a Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 19 Dec 2023 11:11:55 +0100
|
|
Subject: [PATCH 045/122] vfs_default: allow disabling /proc/fds and
|
|
RESOLVE_NO_SYMLINK at compile time
|
|
|
|
This will be used in CI to have a gitlab runner without all modern Linux
|
|
features we make use of as part of path processing:
|
|
|
|
- O_PATH
|
|
- openat2() with RESOLVE_NO_SYMLINKS
|
|
- somehow safely reopen an O_PATH file handle
|
|
|
|
That gives what a classix UNIX like AIX or Solaris offers feature wise.
|
|
|
|
Other OSes support other combinations of those features, but we leave the
|
|
exersize of possibly adding more runners supporting those combinations to the
|
|
reader.
|
|
|
|
The following list shows which features are available and used by Samba on a few
|
|
OSes:
|
|
|
|
| O_PATH | RESOLVE_NO_SYMLINKS | Safe reopen | CI covered
|
|
--------|----------------|---------------------|----------------------------
|
|
| Supported Used | Supported Used | Supported Used |
|
|
============================================================================
|
|
Linux | + + | + + | + + | +
|
|
FreeBSD | + + | + [1] - | + [2] - | -
|
|
AIX | - - | - - | - - | +
|
|
|
|
[1] via open() flag O_RESOLVE_BENEATH
|
|
[2] via open() flag O_EMPTY_PATH
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
(cherry picked from commit 5c2f96442a25a1725809a28b3719afbc0bd01830)
|
|
---
|
|
source3/modules/vfs_default.c | 6 ++++++
|
|
1 file changed, 6 insertions(+)
|
|
|
|
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
|
|
index 1d4b9b1a840..8d78831492f 100644
|
|
--- a/source3/modules/vfs_default.c
|
|
+++ b/source3/modules/vfs_default.c
|
|
@@ -52,6 +52,9 @@ static int vfswrap_connect(vfs_handle_struct *handle, const char *service, const
|
|
bool bval;
|
|
|
|
handle->conn->have_proc_fds = sys_have_proc_fds();
|
|
+#ifdef DISABLE_PROC_FDS
|
|
+ handle->conn->have_proc_fds = false;
|
|
+#endif
|
|
|
|
/*
|
|
* assume the kernel will support openat2(),
|
|
@@ -70,6 +73,9 @@ static int vfswrap_connect(vfs_handle_struct *handle, const char *service, const
|
|
handle->conn->open_how_resolve |=
|
|
VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS;
|
|
}
|
|
+#ifdef DISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS
|
|
+ handle->conn->open_how_resolve &= ~VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS;
|
|
+#endif
|
|
|
|
return 0; /* Return >= 0 for success */
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 26de62a2a968dd5b73af296251b26112cdd533e5 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 19 Dec 2023 11:12:49 +0100
|
|
Subject: [PATCH 046/122] CI: disable /proc/fds and RESOLVE_NO_SYMLINK in
|
|
samba-no-opath-build runner
|
|
|
|
This is a more sensible combination of missing Linux specific features:
|
|
|
|
- O_PATH
|
|
- openat2() with RESOLVE_NO_SYMLINKS
|
|
- somehow safely reopen an O_PATH file handle
|
|
|
|
Currently only O_PATH is disabled for these jobs, but that doesn't really match
|
|
and know OS.
|
|
|
|
The following list shows which features are available and used by Samba on a few
|
|
OSes:
|
|
|
|
| O_PATH | RESOLVE_NO_SYMLINKS | Safe reopen | CI covered
|
|
--------|----------------|---------------------|----------------------------
|
|
| Supported Used | Supported Used | Supported Used |
|
|
============================================================================
|
|
Linux | + + | + + | + + | +
|
|
FreeBSD | + + | + [1] - | + [2] - | -
|
|
AIX | - - | - - | - - | +
|
|
|
|
So by also disabling RESOLVE_NO_SYMLINKS and Safe Reopen, we cover classic UNIX
|
|
systems like AIX.
|
|
|
|
[1] via open() flag O_RESOLVE_BENEATH
|
|
[2] via open() flag O_EMPTY_PATH
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
(cherry picked from commit 62cbe145c7e500c4759ed2005c78bd5056c87f43)
|
|
---
|
|
script/autobuild.py | 2 +-
|
|
selftest/skip.opath-required | 6 ++++++
|
|
2 files changed, 7 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/script/autobuild.py b/script/autobuild.py
|
|
index e074c39d3c0..85043032d73 100755
|
|
--- a/script/autobuild.py
|
|
+++ b/script/autobuild.py
|
|
@@ -296,7 +296,7 @@ tasks = {
|
|
"samba-no-opath-build": {
|
|
"git-clone-required": True,
|
|
"sequence": [
|
|
- ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1' ./configure.developer --without-ad-dc " + samba_configure_params),
|
|
+ ("configure", "ADDITIONAL_CFLAGS='-DDISABLE_OPATH=1 -DDISABLE_VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS=1 -DDISABLE_PROC_FDS=1' ./configure.developer --without-ad-dc " + samba_configure_params),
|
|
("make", "make -j"),
|
|
("check-clean-tree", CLEAN_SOURCE_TREE_CMD),
|
|
("chmod-R-a-w", "chmod -R a-w ."),
|
|
diff --git a/selftest/skip.opath-required b/selftest/skip.opath-required
|
|
index c3a13f5ec6e..67764a0b027 100644
|
|
--- a/selftest/skip.opath-required
|
|
+++ b/selftest/skip.opath-required
|
|
@@ -14,3 +14,9 @@
|
|
# available this works fine. So for now restrict testing posix
|
|
# extensions to environments where we have O_PATH around
|
|
^samba.tests.smb1posix
|
|
+
|
|
+# These don't work without /proc/fd support
|
|
+^samba3.blackbox.test_symlink_traversal.*\(fileserver\)
|
|
+^samba3.blackbox.shadow_copy_torture.*\(fileserver\)
|
|
+^samba3.blackbox.virus_scanner.*\(fileserver:local\)
|
|
+^samba3.blackbox.shadow_copy2.*\(fileserver.*\)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 2c27aae5a4c8d7368dc142fb2be36919296d2a02 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 2 Jan 2024 12:49:14 +0100
|
|
Subject: [PATCH 047/122] smbd: pass symlink target path to
|
|
safe_symlink_target_path()
|
|
|
|
Moves processing the symlink error response to the caller
|
|
filename_convert_dirfsp(). Prepares for using this in
|
|
non_widelink_open(), where it will replace symlink_target_below_conn()
|
|
with the same functionality.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
(back-ported from commit 0515dded4ddb49e5570ae7df51126af1a2d643de)
|
|
---
|
|
source3/include/proto.h | 5 +++
|
|
source3/smbd/filename.c | 72 +++++++++++++++++++----------------------
|
|
2 files changed, 38 insertions(+), 39 deletions(-)
|
|
|
|
diff --git a/source3/include/proto.h b/source3/include/proto.h
|
|
index 8eed81d8f2e..13240033bf1 100644
|
|
--- a/source3/include/proto.h
|
|
+++ b/source3/include/proto.h
|
|
@@ -719,6 +719,11 @@ struct smb_filename *synthetic_smb_fname(TALLOC_CTX *mem_ctx,
|
|
const SMB_STRUCT_STAT *psbuf,
|
|
NTTIME twrp,
|
|
uint32_t flags);
|
|
+NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
|
|
+ const char *connectpath,
|
|
+ const char *target,
|
|
+ size_t unparsed,
|
|
+ char **_relative);
|
|
NTSTATUS filename_convert_dirfsp(
|
|
TALLOC_CTX *ctx,
|
|
connection_struct *conn,
|
|
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
|
|
index 8693dcf1153..45fb90381e2 100644
|
|
--- a/source3/smbd/filename.c
|
|
+++ b/source3/smbd/filename.c
|
|
@@ -942,44 +942,34 @@ static char *symlink_target_path(
|
|
return ret;
|
|
}
|
|
|
|
-static NTSTATUS safe_symlink_target_path(
|
|
- TALLOC_CTX *mem_ctx,
|
|
- const char *connectpath,
|
|
- const char *name_in,
|
|
- const char *substitute,
|
|
- size_t unparsed,
|
|
- char **_name_out)
|
|
+NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
|
|
+ const char *connectpath,
|
|
+ const char *target,
|
|
+ size_t unparsed,
|
|
+ char **_relative)
|
|
{
|
|
- char *target = NULL;
|
|
char *abs_target = NULL;
|
|
char *abs_target_canon = NULL;
|
|
const char *relative = NULL;
|
|
- char *name_out = NULL;
|
|
- NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
bool in_share;
|
|
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
|
|
- target = symlink_target_path(mem_ctx, name_in, substitute, unparsed);
|
|
- if (target == NULL) {
|
|
- goto fail;
|
|
- }
|
|
-
|
|
- DBG_DEBUG("name_in: %s, substitute: %s, unparsed: %zu, target=%s\n",
|
|
- name_in,
|
|
- substitute,
|
|
- unparsed,
|
|
- target);
|
|
+ DBG_DEBUG("connectpath [%s] target [%s] unparsed [%zu]\n",
|
|
+ connectpath, target, unparsed);
|
|
|
|
if (target[0] == '/') {
|
|
- abs_target = target;
|
|
+ abs_target = talloc_strdup(mem_ctx, target);
|
|
} else {
|
|
- abs_target = talloc_asprintf(
|
|
- target, "%s/%s", connectpath, target);
|
|
- if (abs_target == NULL) {
|
|
- goto fail;
|
|
- }
|
|
+ abs_target = talloc_asprintf(mem_ctx,
|
|
+ "%s/%s",
|
|
+ connectpath,
|
|
+ target);
|
|
+ }
|
|
+ if (abs_target == NULL) {
|
|
+ goto fail;
|
|
}
|
|
|
|
- abs_target_canon = canonicalize_absolute_path(target, abs_target);
|
|
+ abs_target_canon = canonicalize_absolute_path(abs_target, abs_target);
|
|
if (abs_target_canon == NULL) {
|
|
goto fail;
|
|
}
|
|
@@ -994,15 +984,14 @@ static NTSTATUS safe_symlink_target_path(
|
|
goto fail;
|
|
}
|
|
|
|
- name_out = talloc_strdup(mem_ctx, relative);
|
|
- if (name_out == NULL) {
|
|
+ *_relative = talloc_strdup(mem_ctx, relative);
|
|
+ if (*_relative == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
status = NT_STATUS_OK;
|
|
- *_name_out = name_out;
|
|
fail:
|
|
- TALLOC_FREE(target);
|
|
+ TALLOC_FREE(abs_target);
|
|
return status;
|
|
}
|
|
|
|
@@ -1438,6 +1427,7 @@ NTSTATUS filename_convert_dirfsp(
|
|
size_t unparsed = 0;
|
|
NTSTATUS status;
|
|
char *target = NULL;
|
|
+ char *safe_target = NULL;
|
|
size_t symlink_redirects = 0;
|
|
|
|
next:
|
|
@@ -1476,17 +1466,21 @@ next:
|
|
* resolve all symlinks locally.
|
|
*/
|
|
|
|
- status = safe_symlink_target_path(
|
|
- mem_ctx,
|
|
- conn->connectpath,
|
|
- name_in,
|
|
- substitute,
|
|
- unparsed,
|
|
- &target);
|
|
+ target = symlink_target_path(mem_ctx, name_in, substitute, unparsed);
|
|
+ if (target == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+
|
|
+ status = safe_symlink_target_path(mem_ctx,
|
|
+ conn->connectpath,
|
|
+ target,
|
|
+ unparsed,
|
|
+ &safe_target);
|
|
+ TALLOC_FREE(target);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
- name_in = target;
|
|
+ name_in = safe_target;
|
|
|
|
symlink_redirects += 1;
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 99d7e841d4e18f760c137530bbed0dea6115311a Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 2 Jan 2024 13:25:25 +0100
|
|
Subject: [PATCH 048/122] smbd: add a directory argument to
|
|
safe_symlink_target_path()
|
|
|
|
Existing caller passes NULL, no change in behaviour. Prepares for
|
|
replacing symlink_target_below_conn() in open.c.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
(cherry picked from commit fc80c72d658a41fe4d93b24b793b52c91b350175)
|
|
---
|
|
source3/include/proto.h | 1 +
|
|
source3/smbd/filename.c | 15 ++++++++++++++-
|
|
2 files changed, 15 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/include/proto.h b/source3/include/proto.h
|
|
index 13240033bf1..15c5839caf8 100644
|
|
--- a/source3/include/proto.h
|
|
+++ b/source3/include/proto.h
|
|
@@ -721,6 +721,7 @@ struct smb_filename *synthetic_smb_fname(TALLOC_CTX *mem_ctx,
|
|
uint32_t flags);
|
|
NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
|
|
const char *connectpath,
|
|
+ const char *dir,
|
|
const char *target,
|
|
size_t unparsed,
|
|
char **_relative);
|
|
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
|
|
index 45fb90381e2..55a49e0ba93 100644
|
|
--- a/source3/smbd/filename.c
|
|
+++ b/source3/smbd/filename.c
|
|
@@ -944,6 +944,7 @@ static char *symlink_target_path(
|
|
|
|
NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
|
|
const char *connectpath,
|
|
+ const char *dir,
|
|
const char *target,
|
|
size_t unparsed,
|
|
char **_relative)
|
|
@@ -959,10 +960,21 @@ NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
|
|
|
|
if (target[0] == '/') {
|
|
abs_target = talloc_strdup(mem_ctx, target);
|
|
- } else {
|
|
+ } else if (dir == NULL) {
|
|
+ abs_target = talloc_asprintf(mem_ctx,
|
|
+ "%s/%s",
|
|
+ connectpath,
|
|
+ target);
|
|
+ } else if (dir[0] == '/') {
|
|
abs_target = talloc_asprintf(mem_ctx,
|
|
"%s/%s",
|
|
+ dir,
|
|
+ target);
|
|
+ } else {
|
|
+ abs_target = talloc_asprintf(mem_ctx,
|
|
+ "%s/%s/%s",
|
|
connectpath,
|
|
+ dir,
|
|
target);
|
|
}
|
|
if (abs_target == NULL) {
|
|
@@ -1473,6 +1485,7 @@ next:
|
|
|
|
status = safe_symlink_target_path(mem_ctx,
|
|
conn->connectpath,
|
|
+ NULL,
|
|
target,
|
|
unparsed,
|
|
&safe_target);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 5041a6fa5cdfd21bf697249d900ea5c107d355a2 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 2 Jan 2024 14:34:26 +0100
|
|
Subject: [PATCH 049/122] smbd: use safe_symlink_target_path() in
|
|
symlink_target_below_conn()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
(cherry picked from commit 1965fc77b3852a0593e13897af08f5304a1ce3a2)
|
|
---
|
|
selftest/skip.opath-required | 2 -
|
|
source3/smbd/open.c | 73 +++++++-----------------------------
|
|
2 files changed, 14 insertions(+), 61 deletions(-)
|
|
|
|
diff --git a/selftest/skip.opath-required b/selftest/skip.opath-required
|
|
index 67764a0b027..9c6ba481cdf 100644
|
|
--- a/selftest/skip.opath-required
|
|
+++ b/selftest/skip.opath-required
|
|
@@ -16,7 +16,5 @@
|
|
^samba.tests.smb1posix
|
|
|
|
# These don't work without /proc/fd support
|
|
-^samba3.blackbox.test_symlink_traversal.*\(fileserver\)
|
|
^samba3.blackbox.shadow_copy_torture.*\(fileserver\)
|
|
^samba3.blackbox.virus_scanner.*\(fileserver:local\)
|
|
-^samba3.blackbox.shadow_copy2.*\(fileserver.*\)
|
|
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
|
|
index 74be444fef5..6582bd60245 100644
|
|
--- a/source3/smbd/open.c
|
|
+++ b/source3/smbd/open.c
|
|
@@ -571,7 +571,6 @@ out:
|
|
static NTSTATUS symlink_target_below_conn(
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *connection_path,
|
|
- size_t connection_path_len,
|
|
struct files_struct *fsp,
|
|
struct files_struct *dirfsp,
|
|
struct smb_filename *symlink_name,
|
|
@@ -579,9 +578,7 @@ static NTSTATUS symlink_target_below_conn(
|
|
{
|
|
char *target = NULL;
|
|
char *absolute = NULL;
|
|
- const char *relative = NULL;
|
|
NTSTATUS status;
|
|
- bool ok;
|
|
|
|
if (fsp_get_pathref_fd(fsp) != -1) {
|
|
/*
|
|
@@ -594,69 +591,28 @@ static NTSTATUS symlink_target_below_conn(
|
|
talloc_tos(), dirfsp, symlink_name, &target);
|
|
}
|
|
|
|
+ status = safe_symlink_target_path(talloc_tos(),
|
|
+ connection_path,
|
|
+ dirfsp->fsp_name->base_name,
|
|
+ target,
|
|
+ 0,
|
|
+ &absolute);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- DBG_DEBUG("readlink_talloc failed: %s\n", nt_errstr(status));
|
|
+ DBG_DEBUG("safe_symlink_target_path() failed: %s\n",
|
|
+ nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
- if (target[0] != '/') {
|
|
- char *tmp = talloc_asprintf(
|
|
- talloc_tos(),
|
|
- "%s/%s/%s",
|
|
- connection_path,
|
|
- dirfsp->fsp_name->base_name,
|
|
- target);
|
|
-
|
|
- TALLOC_FREE(target);
|
|
-
|
|
- if (tmp == NULL) {
|
|
- return NT_STATUS_NO_MEMORY;
|
|
- }
|
|
- target = tmp;
|
|
- }
|
|
-
|
|
- DBG_DEBUG("redirecting to %s\n", target);
|
|
-
|
|
- absolute = canonicalize_absolute_path(talloc_tos(), target);
|
|
- TALLOC_FREE(target);
|
|
-
|
|
- if (absolute == NULL) {
|
|
- return NT_STATUS_NO_MEMORY;
|
|
- }
|
|
-
|
|
- /*
|
|
- * We're doing the "below connection_path" here because it's
|
|
- * cheap. It might be that we get a symlink out of the share,
|
|
- * pointing to yet another symlink getting us back into the
|
|
- * share. If we need that, we would have to remove the check
|
|
- * here.
|
|
- */
|
|
- ok = subdir_of(
|
|
- connection_path,
|
|
- connection_path_len,
|
|
- absolute,
|
|
- &relative);
|
|
- if (!ok) {
|
|
- DBG_NOTICE("Bad access attempt: %s is a symlink "
|
|
- "outside the share path\n"
|
|
- "conn_rootdir =%s\n"
|
|
- "resolved_name=%s\n",
|
|
- symlink_name->base_name,
|
|
- connection_path,
|
|
- absolute);
|
|
- TALLOC_FREE(absolute);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
- }
|
|
-
|
|
- if (relative[0] == '\0') {
|
|
+ if (absolute[0] == '\0') {
|
|
/*
|
|
* special case symlink to share root: "." is our
|
|
* share root filename
|
|
*/
|
|
- absolute[0] = '.';
|
|
- absolute[1] = '\0';
|
|
- } else {
|
|
- memmove(absolute, relative, strlen(relative)+1);
|
|
+ TALLOC_FREE(absolute);
|
|
+ absolute = talloc_strdup(talloc_tos(), ".");
|
|
+ if (absolute == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
}
|
|
|
|
*_target = absolute;
|
|
@@ -834,7 +790,6 @@ again:
|
|
status = symlink_target_below_conn(
|
|
talloc_tos(),
|
|
connpath,
|
|
- connpath_len,
|
|
fsp,
|
|
discard_const_p(files_struct, dirfsp),
|
|
smb_fname_rel,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From f2fc99f0c7d441115a486413f345c0226a00b38b Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Mon, 18 Dec 2023 12:35:58 +0100
|
|
Subject: [PATCH 050/122] smbd: use dirfsp and atname in open_directory()
|
|
|
|
On systems without /proc/fd support this avoid the expensive chdir()
|
|
logic in non_widelink_open(). open_file_ntcreate() already passes
|
|
dirfsp and atname to reopen_from_fsp(), it was just missed in the
|
|
conversion.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
|
|
Autobuild-User(master): Volker Lendecke <vl@samba.org>
|
|
Autobuild-Date(master): Mon Jan 22 12:00:56 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 2713023250f15cf9971d88620cab9dd4afd0dc73)
|
|
|
|
Autobuild-User(v4-19-test): Jule Anger <janger@samba.org>
|
|
Autobuild-Date(v4-19-test): Mon Jan 29 11:59:41 UTC 2024 on atb-devel-224
|
|
---
|
|
source3/smbd/open.c | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
|
|
index 6582bd60245..b9849f82396 100644
|
|
--- a/source3/smbd/open.c
|
|
+++ b/source3/smbd/open.c
|
|
@@ -4865,8 +4865,8 @@ static NTSTATUS open_directory(connection_struct *conn,
|
|
|
|
if (access_mask & need_fd_access) {
|
|
status = reopen_from_fsp(
|
|
- fsp->conn->cwd_fsp,
|
|
- fsp->fsp_name,
|
|
+ parent_dir_fname->fsp,
|
|
+ smb_fname_atname,
|
|
fsp,
|
|
O_RDONLY | O_DIRECTORY,
|
|
0,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7d102268ebbebf6fc723a43485a82f72069d00ee Mon Sep 17 00:00:00 2001
|
|
From: Volker Lendecke <vl@samba.org>
|
|
Date: Fri, 16 Dec 2022 16:35:00 +0100
|
|
Subject: [PATCH 051/122] smbd: Return open_symlink_err from
|
|
filename_convert_dirfsp_nosymlink()
|
|
|
|
Don't lose information returned from openat_pathref_fsp_nosymlink()
|
|
|
|
Signed-off-by: Volker Lendecke <vl@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(backported from commit c81d1d3fe4e3aeb2761dc539e8fb87d2ad862e5f)
|
|
Related to: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
---
|
|
source3/smbd/filename.c | 77 +++++++++++++++++------------------------
|
|
1 file changed, 32 insertions(+), 45 deletions(-)
|
|
|
|
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
|
|
index 55a49e0ba93..9fd85af992a 100644
|
|
--- a/source3/smbd/filename.c
|
|
+++ b/source3/smbd/filename.c
|
|
@@ -944,37 +944,35 @@ static char *symlink_target_path(
|
|
|
|
NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
|
|
const char *connectpath,
|
|
- const char *dir,
|
|
- const char *target,
|
|
+ const char *name_in,
|
|
+ const char *substitute,
|
|
size_t unparsed,
|
|
char **_relative)
|
|
{
|
|
+ char *target = NULL;
|
|
char *abs_target = NULL;
|
|
char *abs_target_canon = NULL;
|
|
const char *relative = NULL;
|
|
bool in_share;
|
|
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
|
|
+ target = symlink_target_path(mem_ctx,
|
|
+ name_in,
|
|
+ substitute,
|
|
+ unparsed);
|
|
+ if (target == NULL) {
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
DBG_DEBUG("connectpath [%s] target [%s] unparsed [%zu]\n",
|
|
connectpath, target, unparsed);
|
|
|
|
if (target[0] == '/') {
|
|
- abs_target = talloc_strdup(mem_ctx, target);
|
|
- } else if (dir == NULL) {
|
|
- abs_target = talloc_asprintf(mem_ctx,
|
|
- "%s/%s",
|
|
- connectpath,
|
|
- target);
|
|
- } else if (dir[0] == '/') {
|
|
- abs_target = talloc_asprintf(mem_ctx,
|
|
- "%s/%s",
|
|
- dir,
|
|
- target);
|
|
+ abs_target = target;
|
|
} else {
|
|
- abs_target = talloc_asprintf(mem_ctx,
|
|
- "%s/%s/%s",
|
|
+ abs_target = talloc_asprintf(target,
|
|
+ "%s/%s",
|
|
connectpath,
|
|
- dir,
|
|
target);
|
|
}
|
|
if (abs_target == NULL) {
|
|
@@ -1019,8 +1017,7 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|
NTTIME twrp,
|
|
struct files_struct **_dirfsp,
|
|
struct smb_filename **_smb_fname,
|
|
- char **_substitute,
|
|
- size_t *_unparsed)
|
|
+ struct open_symlink_err **_symlink_err)
|
|
{
|
|
struct smb_filename *smb_dirname = NULL;
|
|
struct smb_filename *smb_fname_rel = NULL;
|
|
@@ -1142,11 +1139,8 @@ static NTSTATUS filename_convert_dirfsp_nosymlink(
|
|
|
|
SMB_ASSERT(name_in_len >= dirname_len);
|
|
|
|
- *_substitute = talloc_move(
|
|
- mem_ctx,
|
|
- &symlink_err->reparse->substitute_name);
|
|
- *_unparsed = symlink_err->unparsed +
|
|
- (name_in_len - dirname_len);
|
|
+ symlink_err->unparsed += (name_in_len - dirname_len);
|
|
+ *_symlink_err = symlink_err;
|
|
|
|
goto fail;
|
|
}
|
|
@@ -1435,10 +1429,9 @@ NTSTATUS filename_convert_dirfsp(
|
|
struct files_struct **_dirfsp,
|
|
struct smb_filename **_smb_fname)
|
|
{
|
|
- char *substitute = NULL;
|
|
- size_t unparsed = 0;
|
|
+ struct open_symlink_err *symlink_err = NULL;
|
|
NTSTATUS status;
|
|
- char *target = NULL;
|
|
+ char *substitute = NULL;
|
|
char *safe_target = NULL;
|
|
size_t symlink_redirects = 0;
|
|
|
|
@@ -1447,16 +1440,14 @@ next:
|
|
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
|
|
- status = filename_convert_dirfsp_nosymlink(
|
|
- mem_ctx,
|
|
- conn,
|
|
- name_in,
|
|
- ucf_flags,
|
|
- twrp,
|
|
- _dirfsp,
|
|
- _smb_fname,
|
|
- &substitute,
|
|
- &unparsed);
|
|
+ status = filename_convert_dirfsp_nosymlink(mem_ctx,
|
|
+ conn,
|
|
+ name_in,
|
|
+ ucf_flags,
|
|
+ twrp,
|
|
+ _dirfsp,
|
|
+ _smb_fname,
|
|
+ &symlink_err);
|
|
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
|
|
return status;
|
|
@@ -1477,19 +1468,15 @@ next:
|
|
* UCF_POSIX_PATHNAMES set to cause the client to
|
|
* resolve all symlinks locally.
|
|
*/
|
|
-
|
|
- target = symlink_target_path(mem_ctx, name_in, substitute, unparsed);
|
|
- if (target == NULL) {
|
|
- return NT_STATUS_NO_MEMORY;
|
|
- }
|
|
+ substitute = symlink_err->reparse->substitute_name;
|
|
|
|
status = safe_symlink_target_path(mem_ctx,
|
|
conn->connectpath,
|
|
- NULL,
|
|
- target,
|
|
- unparsed,
|
|
+ name_in,
|
|
+ substitute,
|
|
+ symlink_err->unparsed,
|
|
&safe_target);
|
|
- TALLOC_FREE(target);
|
|
+ TALLOC_FREE(symlink_err);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From edaabc3d53fddd9e2fa6168c8bf01ebfbf229657 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 25 Apr 2024 15:24:57 +0200
|
|
Subject: [PATCH 052/122] s3/lib: add next helper variable in server_id_watch_*
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit d76edcd48437715c7541b5b1e6a56245c25f460b)
|
|
---
|
|
source3/lib/server_id_watch.c | 14 ++++++++++----
|
|
1 file changed, 10 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/source3/lib/server_id_watch.c b/source3/lib/server_id_watch.c
|
|
index f0189e0e896..50b35f27b3e 100644
|
|
--- a/source3/lib/server_id_watch.c
|
|
+++ b/source3/lib/server_id_watch.c
|
|
@@ -27,6 +27,7 @@
|
|
struct server_id_watch_state {
|
|
struct tevent_context *ev;
|
|
struct server_id pid;
|
|
+ struct timeval start;
|
|
};
|
|
|
|
static void server_id_watch_waited(struct tevent_req *subreq);
|
|
@@ -37,6 +38,7 @@ struct tevent_req *server_id_watch_send(TALLOC_CTX *mem_ctx,
|
|
{
|
|
struct tevent_req *req, *subreq;
|
|
struct server_id_watch_state *state;
|
|
+ struct timeval next;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct server_id_watch_state);
|
|
if (req == NULL) {
|
|
@@ -44,14 +46,15 @@ struct tevent_req *server_id_watch_send(TALLOC_CTX *mem_ctx,
|
|
}
|
|
state->ev = ev;
|
|
state->pid = pid;
|
|
+ state->start = tevent_timeval_current();
|
|
|
|
if (!serverid_exists(&state->pid)) {
|
|
tevent_req_done(req);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
- subreq = tevent_wakeup_send(
|
|
- state, ev, tevent_timeval_current_ofs(0, 500000));
|
|
+ next = tevent_timeval_add(&state->start, 0, 500000);
|
|
+ subreq = tevent_wakeup_send(state, ev, next);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
@@ -66,6 +69,8 @@ static void server_id_watch_waited(struct tevent_req *subreq)
|
|
subreq, struct tevent_req);
|
|
struct server_id_watch_state *state = tevent_req_data(
|
|
req, struct server_id_watch_state);
|
|
+ struct timeval now;
|
|
+ struct timeval next;
|
|
bool ok;
|
|
|
|
ok = tevent_wakeup_recv(subreq);
|
|
@@ -80,8 +85,9 @@ static void server_id_watch_waited(struct tevent_req *subreq)
|
|
return;
|
|
}
|
|
|
|
- subreq = tevent_wakeup_send(
|
|
- state, state->ev, tevent_timeval_current_ofs(0, 500000));
|
|
+ now = tevent_timeval_current();
|
|
+ next = tevent_timeval_add(&now, 0, 500000);
|
|
+ subreq = tevent_wakeup_send(state, state->ev, next);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From c25f1811c2ccaa2d5cc8005597fb9979aa1102ee Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 4 Apr 2024 12:31:05 +0200
|
|
Subject: [PATCH 053/122] s3/lib: add option "serverid watch:debug = yes" to
|
|
print kernel stack of hanging process
|
|
|
|
We only do if sys_have_proc_fds() returns true, so it's most likely
|
|
linux...
|
|
|
|
Enabled by default with log level 10...
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 5c57e840527432c4b1a7ec94894939022a9e9622)
|
|
---
|
|
source3/lib/server_id_watch.c | 62 +++++++++++++++++++++++++++++++++--
|
|
1 file changed, 59 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/source3/lib/server_id_watch.c b/source3/lib/server_id_watch.c
|
|
index 50b35f27b3e..c372ec8c431 100644
|
|
--- a/source3/lib/server_id_watch.c
|
|
+++ b/source3/lib/server_id_watch.c
|
|
@@ -17,17 +17,18 @@
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
-#include "replace.h"
|
|
-#include <tevent.h>
|
|
-#include <talloc.h>
|
|
+#include "includes.h"
|
|
#include "serverid.h"
|
|
#include "server_id_watch.h"
|
|
+#include "lib/util/server_id.h"
|
|
#include "lib/util/tevent_unix.h"
|
|
|
|
struct server_id_watch_state {
|
|
struct tevent_context *ev;
|
|
struct server_id pid;
|
|
struct timeval start;
|
|
+ struct timeval warn;
|
|
+ bool debug;
|
|
};
|
|
|
|
static void server_id_watch_waited(struct tevent_req *subreq);
|
|
@@ -47,6 +48,12 @@ struct tevent_req *server_id_watch_send(TALLOC_CTX *mem_ctx,
|
|
state->ev = ev;
|
|
state->pid = pid;
|
|
state->start = tevent_timeval_current();
|
|
+ state->warn = tevent_timeval_add(&state->start, 10, 0);
|
|
+
|
|
+ state->debug = lp_parm_bool(GLOBAL_SECTION_SNUM,
|
|
+ "serverid watch",
|
|
+ "debug",
|
|
+ CHECK_DEBUGLVL(DBGLVL_DEBUG));
|
|
|
|
if (!serverid_exists(&state->pid)) {
|
|
tevent_req_done(req);
|
|
@@ -86,6 +93,55 @@ static void server_id_watch_waited(struct tevent_req *subreq)
|
|
}
|
|
|
|
now = tevent_timeval_current();
|
|
+
|
|
+ if (!state->debug) {
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
+ if (timeval_compare(&state->warn, &now) == -1) {
|
|
+ double duration = timeval_elapsed2(&state->start, &now);
|
|
+ char proc_path[64] = { 0, };
|
|
+ char *kstack = NULL;
|
|
+ struct server_id_buf buf;
|
|
+ const char *pid = server_id_str_buf(state->pid, &buf);
|
|
+ int ret;
|
|
+
|
|
+ state->warn = tevent_timeval_add(&now, 10, 0);
|
|
+
|
|
+ if (!procid_is_local(&state->pid) || !sys_have_proc_fds()) {
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n",
|
|
+ pid, duration);
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
+ ret = snprintf(proc_path,
|
|
+ ARRAY_SIZE(proc_path),
|
|
+ "/proc/%" PRIu64 "/stack",
|
|
+ state->pid.pid);
|
|
+ if (ret < 0) {
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n"
|
|
+ "snprintf failed\n",
|
|
+ pid, duration);
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
+ become_root();
|
|
+ kstack = file_load(proc_path, NULL, 0, state);
|
|
+ unbecome_root();
|
|
+ if (kstack == NULL) {
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n"
|
|
+ "file_load [%s] failed\n",
|
|
+ pid, duration, proc_path);
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n"
|
|
+ "%s:\n%s",
|
|
+ pid, duration, proc_path, kstack);
|
|
+ TALLOC_FREE(kstack);
|
|
+ }
|
|
+
|
|
+next:
|
|
next = tevent_timeval_add(&now, 0, 500000);
|
|
subreq = tevent_wakeup_send(state, state->ev, next);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 23dbf8f0317810d65e716a3c9b947c7a6549cb46 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 25 Apr 2024 15:17:08 +0200
|
|
Subject: [PATCH 054/122] s3/lib: add option "serverid watch:debug script"
|
|
|
|
This takes just PID and NODE:PID on a cluster.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 7add7dbf1aee13b4d9ab70d1a5312c8ff30d9e00)
|
|
---
|
|
source3/lib/server_id_watch.c | 52 +++++++++++++++++++++++++++++++++++
|
|
1 file changed, 52 insertions(+)
|
|
|
|
diff --git a/source3/lib/server_id_watch.c b/source3/lib/server_id_watch.c
|
|
index c372ec8c431..8ddf9c6b1c8 100644
|
|
--- a/source3/lib/server_id_watch.c
|
|
+++ b/source3/lib/server_id_watch.c
|
|
@@ -100,6 +100,7 @@ static void server_id_watch_waited(struct tevent_req *subreq)
|
|
|
|
if (timeval_compare(&state->warn, &now) == -1) {
|
|
double duration = timeval_elapsed2(&state->start, &now);
|
|
+ const char *cmd = NULL;
|
|
char proc_path[64] = { 0, };
|
|
char *kstack = NULL;
|
|
struct server_id_buf buf;
|
|
@@ -108,6 +109,57 @@ static void server_id_watch_waited(struct tevent_req *subreq)
|
|
|
|
state->warn = tevent_timeval_add(&now, 10, 0);
|
|
|
|
+ cmd = lp_parm_const_string(GLOBAL_SECTION_SNUM,
|
|
+ "serverid watch",
|
|
+ "debug script",
|
|
+ NULL);
|
|
+ if (cmd != NULL) {
|
|
+ char *cmdstr = NULL;
|
|
+ char *output = NULL;
|
|
+ int fd;
|
|
+
|
|
+ /*
|
|
+ * Note in a cluster setup pid will be
|
|
+ * a NOTE:PID like '1:3978365'
|
|
+ *
|
|
+ * Without clustering it is just '3978365'
|
|
+ */
|
|
+ cmdstr = talloc_asprintf(state, "%s %s", cmd, pid);
|
|
+ if (cmdstr == NULL) {
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n"
|
|
+ "talloc_asprintf failed\n",
|
|
+ pid, duration);
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
+ become_root();
|
|
+ ret = smbrun(cmdstr, &fd, NULL);
|
|
+ unbecome_root();
|
|
+ if (ret != 0) {
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n"
|
|
+ "smbrun('%s') failed\n",
|
|
+ pid, duration, cmdstr);
|
|
+ TALLOC_FREE(cmdstr);
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
+ output = fd_load(fd, NULL, 0, state);
|
|
+ close(fd);
|
|
+ if (output == NULL) {
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n"
|
|
+ "fd_load() of smbrun('%s') failed\n",
|
|
+ pid, duration, cmdstr);
|
|
+ TALLOC_FREE(cmdstr);
|
|
+ goto next;
|
|
+ }
|
|
+ DBG_ERR("Process %s hanging for %f seconds?\n"
|
|
+ "%s returned:\n%s",
|
|
+ pid, duration, cmdstr, output);
|
|
+ TALLOC_FREE(cmdstr);
|
|
+ TALLOC_FREE(output);
|
|
+ goto next;
|
|
+ }
|
|
+
|
|
if (!procid_is_local(&state->pid) || !sys_have_proc_fds()) {
|
|
DBG_ERR("Process %s hanging for %f seconds?\n",
|
|
pid, duration);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 59975168627e4bfbd2e75a611cb8cb13019a7df3 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Fri, 5 Apr 2024 12:15:28 +0200
|
|
Subject: [PATCH 055/122] smbd: log share_mode_watch_recv() errors as errors
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit b45e78871aadca6ae33475bee890736838f44219)
|
|
---
|
|
source3/smbd/open.c | 5 +++--
|
|
1 file changed, 3 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
|
|
index b9849f82396..da129119c7f 100644
|
|
--- a/source3/smbd/open.c
|
|
+++ b/source3/smbd/open.c
|
|
@@ -2969,8 +2969,9 @@ static void defer_open_done(struct tevent_req *req)
|
|
status = share_mode_watch_recv(req, NULL, NULL);
|
|
TALLOC_FREE(req);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(5, ("dbwrap_watched_watch_recv returned %s\n",
|
|
- nt_errstr(status)));
|
|
+ DBG_ERR("share_mode_watch_recv() returned %s, "
|
|
+ "rescheduling mid %" PRIu64 "\n",
|
|
+ nt_errstr(status), state->mid);
|
|
/*
|
|
* Even if it failed, retry anyway. TODO: We need a way to
|
|
* tell a re-scheduled open about that error.
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e619b72fe1b9c36963c452c1d102009b28e8e289 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 4 Apr 2024 19:18:19 +0200
|
|
Subject: [PATCH 056/122] smbd: add option "smbd lease break:debug hung procs"
|
|
|
|
By enabling this a process sending a lease break message to another process
|
|
holding a lease will start watching that process and if that process didn't
|
|
process the lease break within 10 seconds (cf server_id_watch_waited()), we log
|
|
a kernel stack backtrace of that process.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit d8613d7ee23c4e990285a387eb9ac2eeefff9749)
|
|
---
|
|
source3/smbd/open.c | 110 ++++++++++++++++++++++++++++++++++++++++----
|
|
1 file changed, 102 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
|
|
index da129119c7f..4cc5190f690 100644
|
|
--- a/source3/smbd/open.c
|
|
+++ b/source3/smbd/open.c
|
|
@@ -38,6 +38,7 @@
|
|
#include "serverid.h"
|
|
#include "messages.h"
|
|
#include "source3/lib/dbwrap/dbwrap_watch.h"
|
|
+#include "source3/lib/server_id_watch.h"
|
|
#include "locking/leases_db.h"
|
|
#include "librpc/gen_ndr/ndr_leases_db.h"
|
|
#include "lib/util/time_basic.h"
|
|
@@ -2472,6 +2473,10 @@ static int map_lease_type_to_oplock(uint32_t lease_type)
|
|
return result;
|
|
}
|
|
|
|
+struct blocker_debug_state {
|
|
+ size_t num_blockers;
|
|
+};
|
|
+
|
|
struct delay_for_oplock_state {
|
|
struct files_struct *fsp;
|
|
const struct smb2_lease *lease;
|
|
@@ -2483,8 +2488,22 @@ struct delay_for_oplock_state {
|
|
bool have_other_lease;
|
|
uint32_t total_lease_types;
|
|
bool delay;
|
|
+ struct blocker_debug_state *blocker_debug_state;
|
|
};
|
|
|
|
+static int blocker_debug_state_destructor(struct blocker_debug_state *state)
|
|
+{
|
|
+ if (state->num_blockers == 0) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ DBG_DEBUG("blocker_debug_state [%p] num_blockers [%zu]\n",
|
|
+ state, state->num_blockers);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void delay_for_oplock_fn_watch_done(struct tevent_req *subreq);
|
|
+
|
|
static bool delay_for_oplock_fn(
|
|
struct share_mode_entry *e,
|
|
bool *modified,
|
|
@@ -2497,6 +2516,8 @@ static bool delay_for_oplock_fn(
|
|
uint32_t e_lease_type = SMB2_LEASE_NONE;
|
|
uint32_t break_to;
|
|
bool lease_is_breaking = false;
|
|
+ struct tevent_req *subreq = NULL;
|
|
+ struct server_id_buf idbuf = {};
|
|
|
|
if (e_is_lease) {
|
|
NTSTATUS status;
|
|
@@ -2636,9 +2657,56 @@ static bool delay_for_oplock_fn(
|
|
state->delay = true;
|
|
}
|
|
|
|
+ if (!state->delay) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (state->blocker_debug_state == NULL) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ subreq = server_id_watch_send(state->blocker_debug_state,
|
|
+ fsp->conn->sconn->ev_ctx,
|
|
+ e->pid);
|
|
+ if (subreq == NULL) {
|
|
+ DBG_ERR("server_id_watch_send(%s) returned NULL\n",
|
|
+ server_id_str_buf(e->pid, &idbuf));
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ tevent_req_set_callback(subreq,
|
|
+ delay_for_oplock_fn_watch_done,
|
|
+ state->blocker_debug_state);
|
|
+
|
|
+ state->blocker_debug_state->num_blockers++;
|
|
+
|
|
+ DBG_DEBUG("Starting to watch pid [%s] state [%p] num_blockers [%zu]\n",
|
|
+ server_id_str_buf(e->pid, &idbuf),
|
|
+ state->blocker_debug_state,
|
|
+ state->blocker_debug_state->num_blockers);
|
|
+
|
|
return false;
|
|
};
|
|
|
|
+static void delay_for_oplock_fn_watch_done(struct tevent_req *subreq)
|
|
+{
|
|
+ struct blocker_debug_state *blocker_debug_state = tevent_req_callback_data(
|
|
+ subreq, struct blocker_debug_state);
|
|
+ struct server_id pid = {};
|
|
+ struct server_id_buf idbuf = {};
|
|
+ int ret;
|
|
+
|
|
+ ret = server_id_watch_recv(subreq, &pid);
|
|
+ if (ret != 0) {
|
|
+ DBG_ERR("server_id_watch_recv failed %s\n", strerror(ret));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ DBG_DEBUG("state [%p] server_id_watch_recv() returned pid [%s] exited\n",
|
|
+ blocker_debug_state,
|
|
+ server_id_str_buf(pid, &idbuf));
|
|
+}
|
|
+
|
|
static NTSTATUS delay_for_oplock(files_struct *fsp,
|
|
int oplock_request,
|
|
const struct smb2_lease *lease,
|
|
@@ -2647,7 +2715,8 @@ static NTSTATUS delay_for_oplock(files_struct *fsp,
|
|
uint32_t create_disposition,
|
|
bool first_open_attempt,
|
|
int *poplock_type,
|
|
- uint32_t *pgranted)
|
|
+ uint32_t *pgranted,
|
|
+ struct blocker_debug_state **blocker_debug_state)
|
|
{
|
|
struct delay_for_oplock_state state = {
|
|
.fsp = fsp,
|
|
@@ -2693,6 +2762,22 @@ static NTSTATUS delay_for_oplock(files_struct *fsp,
|
|
goto grant;
|
|
}
|
|
|
|
+ if (lp_parm_bool(GLOBAL_SECTION_SNUM,
|
|
+ "smbd lease break",
|
|
+ "debug hung procs",
|
|
+ false))
|
|
+ {
|
|
+ state.blocker_debug_state = talloc_zero(fsp,
|
|
+ struct blocker_debug_state);
|
|
+ if (state.blocker_debug_state == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+ talloc_steal(talloc_tos(), state.blocker_debug_state);
|
|
+
|
|
+ talloc_set_destructor(state.blocker_debug_state,
|
|
+ blocker_debug_state_destructor);
|
|
+ }
|
|
+
|
|
state.delay_mask = have_sharing_violation ?
|
|
SMB2_LEASE_HANDLE : SMB2_LEASE_WRITE;
|
|
|
|
@@ -2714,6 +2799,7 @@ static NTSTATUS delay_for_oplock(files_struct *fsp,
|
|
}
|
|
|
|
if (state.delay) {
|
|
+ *blocker_debug_state = state.blocker_debug_state;
|
|
return NT_STATUS_RETRY;
|
|
}
|
|
|
|
@@ -2827,7 +2913,8 @@ static NTSTATUS handle_share_mode_lease(
|
|
const struct smb2_lease *lease,
|
|
bool first_open_attempt,
|
|
int *poplock_type,
|
|
- uint32_t *pgranted)
|
|
+ uint32_t *pgranted,
|
|
+ struct blocker_debug_state **blocker_debug_state)
|
|
{
|
|
bool sharing_violation = false;
|
|
NTSTATUS status;
|
|
@@ -2868,7 +2955,8 @@ static NTSTATUS handle_share_mode_lease(
|
|
create_disposition,
|
|
first_open_attempt,
|
|
poplock_type,
|
|
- pgranted);
|
|
+ pgranted,
|
|
+ blocker_debug_state);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
@@ -2903,7 +2991,8 @@ static void defer_open_done(struct tevent_req *req);
|
|
static void defer_open(struct share_mode_lock *lck,
|
|
struct timeval timeout,
|
|
struct smb_request *req,
|
|
- struct file_id id)
|
|
+ struct file_id id,
|
|
+ struct blocker_debug_state **blocker_debug_state)
|
|
{
|
|
struct deferred_open_record *open_rec = NULL;
|
|
struct timeval abs_timeout;
|
|
@@ -2947,6 +3036,8 @@ static void defer_open(struct share_mode_lock *lck,
|
|
}
|
|
tevent_req_set_callback(watch_req, defer_open_done, watch_state);
|
|
|
|
+ talloc_move(watch_req, blocker_debug_state);
|
|
+
|
|
ok = tevent_req_set_endtime(watch_req, req->sconn->ev_ctx, abs_timeout);
|
|
if (!ok) {
|
|
exit_server("tevent_req_set_endtime failed");
|
|
@@ -3229,7 +3320,8 @@ static bool open_match_attributes(connection_struct *conn,
|
|
|
|
static void schedule_defer_open(struct share_mode_lock *lck,
|
|
struct file_id id,
|
|
- struct smb_request *req)
|
|
+ struct smb_request *req,
|
|
+ struct blocker_debug_state **blocker_debug_state)
|
|
{
|
|
/* This is a relative time, added to the absolute
|
|
request_time value to get the absolute timeout time.
|
|
@@ -3253,7 +3345,7 @@ static void schedule_defer_open(struct share_mode_lock *lck,
|
|
return;
|
|
}
|
|
|
|
- defer_open(lck, timeout, req, id);
|
|
+ defer_open(lck, timeout, req, id, blocker_debug_state);
|
|
}
|
|
|
|
/****************************************************************************
|
|
@@ -3315,6 +3407,7 @@ static NTSTATUS check_and_store_share_mode(
|
|
int oplock_type = NO_OPLOCK;
|
|
uint32_t granted_lease = 0;
|
|
const struct smb2_lease_key *lease_key = NULL;
|
|
+ struct blocker_debug_state *blocker_debug_state = NULL;
|
|
bool delete_on_close;
|
|
bool ok;
|
|
|
|
@@ -3337,9 +3430,10 @@ static NTSTATUS check_and_store_share_mode(
|
|
lease,
|
|
first_open_attempt,
|
|
&oplock_type,
|
|
- &granted_lease);
|
|
+ &granted_lease,
|
|
+ &blocker_debug_state);
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
|
|
- schedule_defer_open(lck, fsp->file_id, req);
|
|
+ schedule_defer_open(lck, fsp->file_id, req, &blocker_debug_state);
|
|
return NT_STATUS_SHARING_VIOLATION;
|
|
}
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e6a0d821ba28839728371ca94bb364dd6865b5dd Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Wed, 20 Mar 2024 14:27:27 +0100
|
|
Subject: [PATCH 057/122] smbd: move trace_state variable behind tv variable
|
|
|
|
Next commit adds timestamp variables to trace_state that want to be initialized
|
|
with the current time, so moving behind tv we can then just reuse tv for that.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 679e12aee2f0c283a6f9b9c6008c549a6ca9633e)
|
|
---
|
|
source3/smbd/smb2_process.c | 8 ++++----
|
|
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/source3/smbd/smb2_process.c b/source3/smbd/smb2_process.c
|
|
index fbbe4ef3992..188eaa14839 100644
|
|
--- a/source3/smbd/smb2_process.c
|
|
+++ b/source3/smbd/smb2_process.c
|
|
@@ -1783,10 +1783,6 @@ void smbd_process(struct tevent_context *ev_ctx,
|
|
int sock_fd,
|
|
bool interactive)
|
|
{
|
|
- struct smbd_tevent_trace_state trace_state = {
|
|
- .ev = ev_ctx,
|
|
- .frame = talloc_stackframe(),
|
|
- };
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
struct smbXsrv_client *client = NULL;
|
|
@@ -1797,6 +1793,10 @@ void smbd_process(struct tevent_context *ev_ctx,
|
|
int ret;
|
|
NTSTATUS status;
|
|
struct timeval tv = timeval_current();
|
|
+ struct smbd_tevent_trace_state trace_state = {
|
|
+ .ev = ev_ctx,
|
|
+ .frame = talloc_stackframe(),
|
|
+ };
|
|
NTTIME now = timeval_to_nttime(&tv);
|
|
char *chroot_dir = NULL;
|
|
int rc;
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 15276d7645255ddddf2a3bf6b7a429e3d40ec9b7 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Wed, 20 Mar 2024 14:28:43 +0100
|
|
Subject: [PATCH 058/122] smbd: add option "smbd:debug events" for tevent
|
|
handling duration threshold warnings
|
|
|
|
Can be used to enable printing an error message if tevent event handlers ran
|
|
longer then three seconds. Also logs a message with a loglevel of 3 if there
|
|
were no events at hall.
|
|
|
|
Enabled by default with 'log level = 10' or
|
|
'smbd profiling level = on'...
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 90d776cb18395ed804f0ab4fd13ef571fc0ad827)
|
|
---
|
|
source3/smbd/smb2_process.c | 64 +++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 64 insertions(+)
|
|
|
|
diff --git a/source3/smbd/smb2_process.c b/source3/smbd/smb2_process.c
|
|
index 188eaa14839..dbe91132f7f 100644
|
|
--- a/source3/smbd/smb2_process.c
|
|
+++ b/source3/smbd/smb2_process.c
|
|
@@ -1692,8 +1692,36 @@ struct smbd_tevent_trace_state {
|
|
struct tevent_context *ev;
|
|
TALLOC_CTX *frame;
|
|
SMBPROFILE_BASIC_ASYNC_STATE(profile_idle);
|
|
+ struct timeval before_wait_tv;
|
|
+ struct timeval after_wait_tv;
|
|
};
|
|
|
|
+static inline void smbd_tevent_trace_callback_before_wait(
|
|
+ struct smbd_tevent_trace_state *state)
|
|
+{
|
|
+ struct timeval now = timeval_current();
|
|
+ struct timeval diff;
|
|
+
|
|
+ diff = tevent_timeval_until(&state->after_wait_tv, &now);
|
|
+ if (diff.tv_sec > 3) {
|
|
+ DBG_ERR("Handling event took %ld seconds!\n", (long)diff.tv_sec);
|
|
+ }
|
|
+ state->before_wait_tv = now;
|
|
+}
|
|
+
|
|
+static inline void smbd_tevent_trace_callback_after_wait(
|
|
+ struct smbd_tevent_trace_state *state)
|
|
+{
|
|
+ struct timeval now = timeval_current();
|
|
+ struct timeval diff;
|
|
+
|
|
+ diff = tevent_timeval_until(&state->before_wait_tv, &now);
|
|
+ if (diff.tv_sec > 30) {
|
|
+ DBG_NOTICE("No event for %ld seconds!\n", (long)diff.tv_sec);
|
|
+ }
|
|
+ state->after_wait_tv = now;
|
|
+}
|
|
+
|
|
static inline void smbd_tevent_trace_callback_before_loop_once(
|
|
struct smbd_tevent_trace_state *state)
|
|
{
|
|
@@ -1729,6 +1757,30 @@ static void smbd_tevent_trace_callback(enum tevent_trace_point point,
|
|
errno = 0;
|
|
}
|
|
|
|
+static void smbd_tevent_trace_callback_debug(enum tevent_trace_point point,
|
|
+ void *private_data)
|
|
+{
|
|
+ struct smbd_tevent_trace_state *state =
|
|
+ (struct smbd_tevent_trace_state *)private_data;
|
|
+
|
|
+ switch (point) {
|
|
+ case TEVENT_TRACE_BEFORE_WAIT:
|
|
+ smbd_tevent_trace_callback_before_wait(state);
|
|
+ break;
|
|
+ case TEVENT_TRACE_AFTER_WAIT:
|
|
+ smbd_tevent_trace_callback_after_wait(state);
|
|
+ break;
|
|
+ case TEVENT_TRACE_BEFORE_LOOP_ONCE:
|
|
+ smbd_tevent_trace_callback_before_loop_once(state);
|
|
+ break;
|
|
+ case TEVENT_TRACE_AFTER_LOOP_ONCE:
|
|
+ smbd_tevent_trace_callback_after_loop_once(state);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ errno = 0;
|
|
+}
|
|
+
|
|
static void smbd_tevent_trace_callback_profile(enum tevent_trace_point point,
|
|
void *private_data)
|
|
{
|
|
@@ -1737,6 +1789,7 @@ static void smbd_tevent_trace_callback_profile(enum tevent_trace_point point,
|
|
|
|
switch (point) {
|
|
case TEVENT_TRACE_BEFORE_WAIT:
|
|
+ smbd_tevent_trace_callback_before_wait(state);
|
|
if (!smbprofile_dump_pending()) {
|
|
/*
|
|
* If there's no dump pending
|
|
@@ -1749,6 +1802,7 @@ static void smbd_tevent_trace_callback_profile(enum tevent_trace_point point,
|
|
SMBPROFILE_BASIC_ASYNC_START(idle, profile_p, state->profile_idle);
|
|
break;
|
|
case TEVENT_TRACE_AFTER_WAIT:
|
|
+ smbd_tevent_trace_callback_after_wait(state);
|
|
SMBPROFILE_BASIC_ASYNC_END(state->profile_idle);
|
|
if (!smbprofile_dump_pending()) {
|
|
/*
|
|
@@ -1796,7 +1850,13 @@ void smbd_process(struct tevent_context *ev_ctx,
|
|
struct smbd_tevent_trace_state trace_state = {
|
|
.ev = ev_ctx,
|
|
.frame = talloc_stackframe(),
|
|
+ .before_wait_tv = tv,
|
|
+ .after_wait_tv = tv,
|
|
};
|
|
+ bool debug = lp_parm_bool(GLOBAL_SECTION_SNUM,
|
|
+ "smbd",
|
|
+ "debug events",
|
|
+ CHECK_DEBUGLVL(DBGLVL_DEBUG));
|
|
NTTIME now = timeval_to_nttime(&tv);
|
|
char *chroot_dir = NULL;
|
|
int rc;
|
|
@@ -2041,6 +2101,10 @@ void smbd_process(struct tevent_context *ev_ctx,
|
|
tevent_set_trace_callback(ev_ctx,
|
|
smbd_tevent_trace_callback_profile,
|
|
&trace_state);
|
|
+ } else if (debug) {
|
|
+ tevent_set_trace_callback(ev_ctx,
|
|
+ smbd_tevent_trace_callback_debug,
|
|
+ &trace_state);
|
|
} else {
|
|
tevent_set_trace_callback(ev_ctx,
|
|
smbd_tevent_trace_callback,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4631b9d60a874db10dbdd52406d0094a7dbd1356 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 26 Aug 2024 14:11:02 +0200
|
|
Subject: [PATCH 059/122] vfs_error_inject: add 'error_inject:durable_reconnect
|
|
= st_ex_nlink'
|
|
|
|
This allows to simulate durable reconnect failures because the stat
|
|
information of the file changed.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 692ed832dfff61ad1c9b646b5c8d6f85f25efb99)
|
|
---
|
|
source3/modules/vfs_error_inject.c | 76 ++++++++++++++++++++++++++++++
|
|
1 file changed, 76 insertions(+)
|
|
|
|
diff --git a/source3/modules/vfs_error_inject.c b/source3/modules/vfs_error_inject.c
|
|
index 529504fd8d5..dcf0de0a2d9 100644
|
|
--- a/source3/modules/vfs_error_inject.c
|
|
+++ b/source3/modules/vfs_error_inject.c
|
|
@@ -19,6 +19,7 @@
|
|
|
|
#include "includes.h"
|
|
#include "smbd/smbd.h"
|
|
+#include "librpc/gen_ndr/ndr_open_files.h"
|
|
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS DBGC_VFS
|
|
@@ -204,11 +205,86 @@ static int vfs_error_inject_unlinkat(struct vfs_handle_struct *handle,
|
|
return -1;
|
|
}
|
|
|
|
+static NTSTATUS vfs_error_inject_durable_reconnect(struct vfs_handle_struct *handle,
|
|
+ struct smb_request *smb1req,
|
|
+ struct smbXsrv_open *op,
|
|
+ const DATA_BLOB old_cookie,
|
|
+ TALLOC_CTX *mem_ctx,
|
|
+ struct files_struct **fsp,
|
|
+ DATA_BLOB *new_cookie)
|
|
+{
|
|
+ const char *vfs_func = "durable_reconnect";
|
|
+ const char *err_str = NULL;
|
|
+ NTSTATUS status;
|
|
+ enum ndr_err_code ndr_err;
|
|
+ struct vfs_default_durable_cookie cookie;
|
|
+ DATA_BLOB modified_cookie = data_blob_null;
|
|
+
|
|
+ err_str = lp_parm_const_string(SNUM(handle->conn),
|
|
+ "error_inject",
|
|
+ vfs_func,
|
|
+ NULL);
|
|
+ if (err_str == NULL) {
|
|
+ return SMB_VFS_NEXT_DURABLE_RECONNECT(handle,
|
|
+ smb1req,
|
|
+ op,
|
|
+ old_cookie,
|
|
+ mem_ctx,
|
|
+ fsp,
|
|
+ new_cookie);
|
|
+ }
|
|
+
|
|
+ ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
|
|
+ (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
|
|
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
+ status = ndr_map_error2ntstatus(ndr_err);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
|
|
+ return NT_STATUS_INVALID_PARAMETER;
|
|
+ }
|
|
+
|
|
+ if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
|
|
+ return NT_STATUS_INVALID_PARAMETER;
|
|
+ }
|
|
+
|
|
+ if (strequal(err_str, "st_ex_nlink")) {
|
|
+ cookie.stat_info.st_ex_nlink += 1;
|
|
+ } else {
|
|
+ DBG_ERR("Unknown error inject %s requested "
|
|
+ "for vfs function %s\n", err_str, vfs_func);
|
|
+ return SMB_VFS_NEXT_DURABLE_RECONNECT(handle,
|
|
+ smb1req,
|
|
+ op,
|
|
+ old_cookie,
|
|
+ mem_ctx,
|
|
+ fsp,
|
|
+ new_cookie);
|
|
+ }
|
|
+
|
|
+ ndr_err = ndr_push_struct_blob(&modified_cookie, talloc_tos(), &cookie,
|
|
+ (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
|
|
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
+ status = ndr_map_error2ntstatus(ndr_err);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ return SMB_VFS_NEXT_DURABLE_RECONNECT(handle,
|
|
+ smb1req,
|
|
+ op,
|
|
+ modified_cookie,
|
|
+ mem_ctx,
|
|
+ fsp,
|
|
+ new_cookie);
|
|
+}
|
|
+
|
|
static struct vfs_fn_pointers vfs_error_inject_fns = {
|
|
.chdir_fn = vfs_error_inject_chdir,
|
|
.pwrite_fn = vfs_error_inject_pwrite,
|
|
.openat_fn = vfs_error_inject_openat,
|
|
.unlinkat_fn = vfs_error_inject_unlinkat,
|
|
+ .durable_reconnect_fn = vfs_error_inject_durable_reconnect,
|
|
};
|
|
|
|
static_decl_vfs;
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From c8e88652163cc56b1f9fb0926a140c81e6b7ec94 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 26 Aug 2024 14:42:02 +0200
|
|
Subject: [PATCH 060/122] s4:torture/smb2: add
|
|
smb2.durable-v2-regressions.durable_v2_reconnect_bug15624
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit ef4ef04e7f83b1029446ff8b5fc5fdf4ab33edbd)
|
|
---
|
|
selftest/skip | 1 +
|
|
source4/torture/smb2/durable_v2_open.c | 118 +++++++++++++++++++++++++
|
|
source4/torture/smb2/smb2.c | 2 +
|
|
3 files changed, 121 insertions(+)
|
|
|
|
diff --git a/selftest/skip b/selftest/skip
|
|
index e808367c00d..056c54ea287 100644
|
|
--- a/selftest/skip
|
|
+++ b/selftest/skip
|
|
@@ -149,3 +149,4 @@ bench # don't run benchmarks in our selftest
|
|
^samba.tests.reparsepoints.*
|
|
^samba.tests.smb2symlink.*
|
|
^samba3.blackbox.open-eintr.*
|
|
+smb2.durable-v2-regressions # Only used in blackbox tests
|
|
diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
|
|
index 9b9af11124c..7447dd287a4 100644
|
|
--- a/source4/torture/smb2/durable_v2_open.c
|
|
+++ b/source4/torture/smb2/durable_v2_open.c
|
|
@@ -2355,6 +2355,112 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * basic test for doing a durable open
|
|
+ * tcp disconnect, reconnect, do a durable reopen (succeeds)
|
|
+ */
|
|
+static bool test_durable_v2_reconnect_bug15624(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ NTSTATUS status;
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ char fname[256];
|
|
+ struct smb2_handle _h;
|
|
+ struct smb2_handle *h = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid = GUID_random();
|
|
+ struct smbcli_options options;
|
|
+ uint64_t previous_session_id;
|
|
+ uint8_t b = 0;
|
|
+ bool ret = true;
|
|
+ bool ok;
|
|
+
|
|
+ if (!torture_setting_bool(tctx, "bug15624", false)) {
|
|
+ torture_comment(tctx,
|
|
+ "share requires:\n"
|
|
+ "'vfs objects = error_inject'\n"
|
|
+ "'error_inject:durable_reconnect=st_ex_nlink'\n"
|
|
+ "test requires:\n"
|
|
+ "'--option=torture:bug15624=yes'\n");
|
|
+ torture_skip(tctx, "'--option=torture:bug15624=yes' missing");
|
|
+ }
|
|
+
|
|
+ options = tree->session->transport->options;
|
|
+ previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname,
|
|
+ sizeof(fname),
|
|
+ "durable_v2_reconnect_bug15624_%s.dat",
|
|
+ generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree, fname);
|
|
+
|
|
+ smb2_oplock_create_share(&io, fname,
|
|
+ smb2_util_share_access(""),
|
|
+ smb2_util_oplock_level("b"));
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid;
|
|
+ io.in.timeout = 0;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ _h = io.out.file.handle;
|
|
+ h = &_h;
|
|
+ CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+
|
|
+ status = smb2_util_write(tree, *h, &b, 0, 1);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ /* disconnect, leaving the durable open */
|
|
+ TALLOC_FREE(tree);
|
|
+ h = NULL;
|
|
+
|
|
+ ok = torture_smb2_connection_ext(tctx, previous_session_id,
|
|
+ &options, &tree);
|
|
+ torture_assert_goto(tctx, ok, ret, done, "couldn't reconnect, bailing\n");
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = &_h;
|
|
+ io.in.create_guid = create_guid;
|
|
+
|
|
+ /*
|
|
+ * This assumes 'error_inject:durable_reconnect = st_ex_nlink'
|
|
+ * will cause the durable reconnect to fail...
|
|
+ * in order to have a regression test for the dead lock.
|
|
+ */
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+
|
|
+ /*
|
|
+ * With the regression this will fail with
|
|
+ * a timeout...
|
|
+ */
|
|
+ status = smb2_util_unlink(tree2, fname);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+done:
|
|
+ if (h != NULL) {
|
|
+ smb2_util_close(tree, *h);
|
|
+ }
|
|
+ TALLOC_FREE(tree);
|
|
+
|
|
+ smb2_util_unlink(tree2, fname);
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
struct torture_suite *torture_smb2_durable_v2_delay_init(TALLOC_CTX *ctx)
|
|
{
|
|
struct torture_suite *suite =
|
|
@@ -2369,3 +2475,15 @@ struct torture_suite *torture_smb2_durable_v2_delay_init(TALLOC_CTX *ctx)
|
|
|
|
return suite;
|
|
}
|
|
+
|
|
+struct torture_suite *torture_smb2_durable_v2_regressions_init(TALLOC_CTX *ctx)
|
|
+{
|
|
+ struct torture_suite *suite =
|
|
+ torture_suite_create(ctx, "durable-v2-regressions");
|
|
+
|
|
+ torture_suite_add_2smb2_test(suite,
|
|
+ "durable_v2_reconnect_bug15624",
|
|
+ test_durable_v2_reconnect_bug15624);
|
|
+
|
|
+ return suite;
|
|
+}
|
|
diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c
|
|
index 5b6477e47bc..9cf7f5da78b 100644
|
|
--- a/source4/torture/smb2/smb2.c
|
|
+++ b/source4/torture/smb2/smb2.c
|
|
@@ -170,6 +170,8 @@ NTSTATUS torture_smb2_init(TALLOC_CTX *ctx)
|
|
torture_smb2_durable_v2_open_init(suite));
|
|
torture_suite_add_suite(suite,
|
|
torture_smb2_durable_v2_delay_init(suite));
|
|
+ torture_suite_add_suite(suite,
|
|
+ torture_smb2_durable_v2_regressions_init(suite));
|
|
torture_suite_add_suite(suite, torture_smb2_dir_init(suite));
|
|
torture_suite_add_suite(suite, torture_smb2_lease_init(suite));
|
|
torture_suite_add_suite(suite, torture_smb2_compound_init(suite));
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 56a3aaf95c44052b19b61115686c71d5b7dbab4a Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 26 Aug 2024 14:42:12 +0200
|
|
Subject: [PATCH 061/122] s3:tests: let test_durable_handle_reconnect.sh run
|
|
smb2.durable-v2-regressions.durable_v2_reconnect_bug15624
|
|
|
|
This demonstrates the dead lock after a durable reconnect failed
|
|
because the stat info changed, the file can't be accessed anymore
|
|
as we leak the incomplete share mode entry in a still running
|
|
process.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit 14875448ca06a3a28800343a3a326f1a66bccec0)
|
|
---
|
|
.../samba3.blackbox.durable_v2_delay | 1 +
|
|
.../tests/test_durable_handle_reconnect.sh | 18 ++++++++++++++++++
|
|
2 files changed, 19 insertions(+)
|
|
create mode 100644 selftest/knownfail.d/samba3.blackbox.durable_v2_delay
|
|
|
|
diff --git a/selftest/knownfail.d/samba3.blackbox.durable_v2_delay b/selftest/knownfail.d/samba3.blackbox.durable_v2_delay
|
|
new file mode 100644
|
|
index 00000000000..88e29960797
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/samba3.blackbox.durable_v2_delay
|
|
@@ -0,0 +1 @@
|
|
+^samba3.blackbox.durable_v2_delay.durable-v2-regressions.durable_v2_reconnect_bug15624
|
|
diff --git a/source3/script/tests/test_durable_handle_reconnect.sh b/source3/script/tests/test_durable_handle_reconnect.sh
|
|
index 0ab32974824..fd5c156956f 100755
|
|
--- a/source3/script/tests/test_durable_handle_reconnect.sh
|
|
+++ b/source3/script/tests/test_durable_handle_reconnect.sh
|
|
@@ -33,4 +33,22 @@ testit "durable_v2_delay.durable_v2_reconnect_delay_msec" $VALGRIND \
|
|
|
|
rm $delay_inject_conf
|
|
|
|
+error_inject_conf=$(dirname $SMB_CONF_PATH)/error_inject.conf
|
|
+
|
|
+cat > $error_inject_conf << _EOF
|
|
+ kernel share modes = no
|
|
+ kernel oplocks = no
|
|
+ posix locking = no
|
|
+ error_inject:durable_reconnect = st_ex_nlink
|
|
+_EOF
|
|
+
|
|
+testit "durable-v2-regressions.durable_v2_reconnect_bug15624" \
|
|
+ $VALGRIND $BINDIR/smbtorture //$SERVER_IP/error_inject \
|
|
+ -U$USERNAME%$PASSWORD \
|
|
+ --option=torture:bug15624=yes \
|
|
+ smb2.durable-v2-regressions.durable_v2_reconnect_bug15624 ||
|
|
+ failed=$(expr $failed + 1)
|
|
+
|
|
+rm $error_inject_conf
|
|
+
|
|
testok $0 $failed
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d8f01885145ecfce15f2507fdcc625442db1738c Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 9 Apr 2024 14:52:44 +0200
|
|
Subject: [PATCH 062/122] smbd: consolidate DH reconnect failure code
|
|
|
|
No change in behaviour, except that we now
|
|
also call fd_close() if vfs_default_durable_cookie()
|
|
failed.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
(cherry picked from commit a91457f97c98fcec1ed062514c364271af1df669)
|
|
---
|
|
source3/smbd/durable.c | 142 ++++++++++++++---------------------------
|
|
1 file changed, 47 insertions(+), 95 deletions(-)
|
|
|
|
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
|
|
index b21c223b2e4..50075ddd3f7 100644
|
|
--- a/source3/smbd/durable.c
|
|
+++ b/source3/smbd/durable.c
|
|
@@ -624,22 +624,22 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
ok = share_mode_forall_entries(lck, durable_reconnect_fn, &e);
|
|
if (!ok) {
|
|
DBG_WARNING("share_mode_forall_entries failed\n");
|
|
- TALLOC_FREE(lck);
|
|
- return NT_STATUS_INTERNAL_DB_ERROR;
|
|
+ status = NT_STATUS_INTERNAL_DB_ERROR;
|
|
+ goto fail;
|
|
}
|
|
|
|
if (e.pid.pid == 0) {
|
|
DBG_WARNING("Did not find a unique valid share mode entry\n");
|
|
- TALLOC_FREE(lck);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
if (!server_id_is_disconnected(&e.pid)) {
|
|
DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
|
|
"reconnect for handle that was not marked "
|
|
"disconnected (e.g. smbd or cluster node died)\n"));
|
|
- TALLOC_FREE(lck);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
if (e.share_file_id != op->global->open_persistent_id) {
|
|
@@ -648,8 +648,8 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
"(e.g. another client had opened the file)\n",
|
|
e.share_file_id,
|
|
op->global->open_persistent_id);
|
|
- TALLOC_FREE(lck);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
|
|
@@ -658,8 +658,8 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
|
|
"share[%s] is not writeable anymore\n",
|
|
lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
|
|
- TALLOC_FREE(lck);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
/*
|
|
@@ -670,8 +670,7 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
|
|
"new fsp: %s\n", nt_errstr(status)));
|
|
- TALLOC_FREE(lck);
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
fh_set_private_options(fsp->fh, e.private_options);
|
|
@@ -714,9 +713,8 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
*/
|
|
if (!GUID_equal(fsp_client_guid(fsp),
|
|
&e.client_guid)) {
|
|
- TALLOC_FREE(lck);
|
|
- file_free(smb1req, fsp);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
status = leases_db_get(
|
|
@@ -730,9 +728,7 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
&lease_version, /* lease_version */
|
|
&epoch); /* epoch */
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- TALLOC_FREE(lck);
|
|
- file_free(smb1req, fsp);
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
fsp->lease = find_fsp_lease(
|
|
@@ -742,9 +738,8 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
lease_version,
|
|
epoch);
|
|
if (fsp->lease == NULL) {
|
|
- TALLOC_FREE(lck);
|
|
- file_free(smb1req, fsp);
|
|
- return NT_STATUS_NO_MEMORY;
|
|
+ status = NT_STATUS_NO_MEMORY;
|
|
+ goto fail;
|
|
}
|
|
}
|
|
|
|
@@ -760,12 +755,10 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
|
|
status = fsp_set_smb_fname(fsp, smb_fname);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- TALLOC_FREE(lck);
|
|
- file_free(smb1req, fsp);
|
|
DEBUG(0, ("vfs_default_durable_reconnect: "
|
|
"fsp_set_smb_fname failed: %s\n",
|
|
nt_errstr(status)));
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
op->compat = fsp;
|
|
@@ -780,11 +773,8 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
fh_get_gen_id(fsp->fh));
|
|
if (!ok) {
|
|
DBG_DEBUG("Could not set new share_mode_entry values\n");
|
|
- TALLOC_FREE(lck);
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return NT_STATUS_INTERNAL_ERROR;
|
|
+ status = NT_STATUS_INTERNAL_ERROR;
|
|
+ goto fail;
|
|
}
|
|
|
|
ok = brl_reconnect_disconnected(fsp);
|
|
@@ -793,11 +783,7 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
DEBUG(1, ("vfs_default_durable_reconnect: "
|
|
"failed to reopen brlocks: %s\n",
|
|
nt_errstr(status)));
|
|
- TALLOC_FREE(lck);
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
/*
|
|
@@ -813,13 +799,9 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
|
|
status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, &how);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- TALLOC_FREE(lck);
|
|
DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
|
|
"file: %s\n", nt_errstr(status)));
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
/*
|
|
@@ -833,48 +815,22 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
|
|
ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
|
|
if (ret == -1) {
|
|
- NTSTATUS close_status;
|
|
status = map_nt_error_from_unix_common(errno);
|
|
DEBUG(1, ("Unable to fstat stream: %s => %s\n",
|
|
smb_fname_str_dbg(smb_fname),
|
|
nt_errstr(status)));
|
|
- close_status = fd_close(fsp);
|
|
- if (!NT_STATUS_IS_OK(close_status)) {
|
|
- DBG_ERR("fd_close failed (%s) - leaking file "
|
|
- "descriptor\n", nt_errstr(close_status));
|
|
- }
|
|
- TALLOC_FREE(lck);
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
|
|
- NTSTATUS close_status = fd_close(fsp);
|
|
- if (!NT_STATUS_IS_OK(close_status)) {
|
|
- DBG_ERR("fd_close failed (%s) - leaking file "
|
|
- "descriptor\n", nt_errstr(close_status));
|
|
- }
|
|
- TALLOC_FREE(lck);
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
|
|
if (!file_id_equal(&cookie.id, &file_id)) {
|
|
- NTSTATUS close_status = fd_close(fsp);
|
|
- if (!NT_STATUS_IS_OK(close_status)) {
|
|
- DBG_ERR("fd_close failed (%s) - leaking file "
|
|
- "descriptor\n", nt_errstr(close_status));
|
|
- }
|
|
- TALLOC_FREE(lck);
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
(void)fdos_mode(fsp);
|
|
@@ -883,42 +839,21 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
&fsp->fsp_name->st,
|
|
fsp_str_dbg(fsp));
|
|
if (!ok) {
|
|
- NTSTATUS close_status = fd_close(fsp);
|
|
- if (!NT_STATUS_IS_OK(close_status)) {
|
|
- DBG_ERR("fd_close failed (%s) - leaking file "
|
|
- "descriptor\n", nt_errstr(close_status));
|
|
- }
|
|
- TALLOC_FREE(lck);
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ goto fail;
|
|
}
|
|
|
|
status = set_file_oplock(fsp);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- NTSTATUS close_status = fd_close(fsp);
|
|
- if (!NT_STATUS_IS_OK(close_status)) {
|
|
- DBG_ERR("fd_close failed (%s) - leaking file "
|
|
- "descriptor\n", nt_errstr(close_status));
|
|
- }
|
|
- TALLOC_FREE(lck);
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- TALLOC_FREE(lck);
|
|
DEBUG(1, ("vfs_default_durable_reconnect: "
|
|
"vfs_default_durable_cookie - %s\n",
|
|
nt_errstr(status)));
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
- return status;
|
|
+ goto fail;
|
|
}
|
|
|
|
smb1req->chain_fsp = fsp;
|
|
@@ -935,4 +870,21 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
*new_cookie = new_cookie_blob;
|
|
|
|
return NT_STATUS_OK;
|
|
+
|
|
+fail:
|
|
+ if (fsp != NULL && fsp_get_pathref_fd(fsp) != -1) {
|
|
+ NTSTATUS close_status;
|
|
+ close_status = fd_close(fsp);
|
|
+ if (!NT_STATUS_IS_OK(close_status)) {
|
|
+ DBG_ERR("fd_close failed (%s), leaking fd\n",
|
|
+ nt_errstr(close_status));
|
|
+ }
|
|
+ }
|
|
+ TALLOC_FREE(lck);
|
|
+ if (fsp != NULL) {
|
|
+ op->compat = NULL;
|
|
+ fsp->op = NULL;
|
|
+ file_free(smb1req, fsp);
|
|
+ }
|
|
+ return status;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From b248ddd3dd7193ba44c9ad86488dd180a25e3774 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 9 Apr 2024 14:53:32 +0200
|
|
Subject: [PATCH 063/122] smbd: remove just created sharemode entry in the
|
|
error codepaths
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Without this we leave stale sharemode entries around that can lead to all sorts
|
|
of havoc.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15624
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Guenther Deschner <gd@samba.org>
|
|
|
|
Autobuild-User(master): Günther Deschner <gd@samba.org>
|
|
Autobuild-Date(master): Thu Sep 19 19:36:19 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 2ff3b9bc0d254a63a913ff9084de3d794fee27d0)
|
|
---
|
|
selftest/knownfail.d/samba3.blackbox.durable_v2_delay | 1 -
|
|
source3/smbd/durable.c | 8 ++++++++
|
|
2 files changed, 8 insertions(+), 1 deletion(-)
|
|
delete mode 100644 selftest/knownfail.d/samba3.blackbox.durable_v2_delay
|
|
|
|
diff --git a/selftest/knownfail.d/samba3.blackbox.durable_v2_delay b/selftest/knownfail.d/samba3.blackbox.durable_v2_delay
|
|
deleted file mode 100644
|
|
index 88e29960797..00000000000
|
|
--- a/selftest/knownfail.d/samba3.blackbox.durable_v2_delay
|
|
+++ /dev/null
|
|
@@ -1 +0,0 @@
|
|
-^samba3.blackbox.durable_v2_delay.durable-v2-regressions.durable_v2_reconnect_bug15624
|
|
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
|
|
index 50075ddd3f7..98d0d403e30 100644
|
|
--- a/source3/smbd/durable.c
|
|
+++ b/source3/smbd/durable.c
|
|
@@ -538,6 +538,7 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
enum ndr_err_code ndr_err;
|
|
struct vfs_default_durable_cookie cookie;
|
|
DATA_BLOB new_cookie_blob = data_blob_null;
|
|
+ bool have_share_mode_entry = false;
|
|
|
|
*result = NULL;
|
|
*new_cookie = data_blob_null;
|
|
@@ -776,6 +777,7 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto fail;
|
|
}
|
|
+ have_share_mode_entry = true;
|
|
|
|
ok = brl_reconnect_disconnected(fsp);
|
|
if (!ok) {
|
|
@@ -872,6 +874,12 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
return NT_STATUS_OK;
|
|
|
|
fail:
|
|
+ if (fsp != NULL && have_share_mode_entry) {
|
|
+ /*
|
|
+ * Something is screwed up, delete the sharemode entry.
|
|
+ */
|
|
+ del_share_mode(lck, fsp);
|
|
+ }
|
|
if (fsp != NULL && fsp_get_pathref_fd(fsp) != -1) {
|
|
NTSTATUS close_status;
|
|
close_status = fd_close(fsp);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 67ff429e41004899e514d893e80332de79ca2bab Mon Sep 17 00:00:00 2001
|
|
From: Earl Chew <earl_chew@yahoo.com>
|
|
Date: Sun, 17 Dec 2023 08:37:33 -0800
|
|
Subject: [PATCH 064/122] Augment library_flags() to return libraries
|
|
|
|
Extend library_flags() to return the libraries provided by
|
|
pkg-config --libs.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15623
|
|
|
|
Signed-off-by: Earl Chew <earl_chew@yahoo.com>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
(cherry picked from commit 363c33185779141fdfbda695997d548939a0251f)
|
|
---
|
|
buildtools/wafsamba/samba3.py | 8 ++++----
|
|
buildtools/wafsamba/samba_autoconf.py | 22 +++++++++++++---------
|
|
buildtools/wafsamba/samba_deps.py | 5 ++---
|
|
lib/util/charset/wscript_configure | 2 +-
|
|
4 files changed, 20 insertions(+), 17 deletions(-)
|
|
|
|
diff --git a/buildtools/wafsamba/samba3.py b/buildtools/wafsamba/samba3.py
|
|
index 227ee27705d..ba0783f0d22 100644
|
|
--- a/buildtools/wafsamba/samba3.py
|
|
+++ b/buildtools/wafsamba/samba3.py
|
|
@@ -45,25 +45,25 @@ def s3_fix_kwargs(bld, kwargs):
|
|
'../bin/default/third_party/heimdal/lib/asn1' ]
|
|
|
|
if bld.CONFIG_SET('USING_SYSTEM_TDB'):
|
|
- (tdb_includes, tdb_ldflags, tdb_cpppath) = library_flags(bld, 'tdb')
|
|
+ (tdb_includes, tdb_ldflags, tdb_cpppath, tdb_libs) = library_flags(bld, 'tdb')
|
|
extra_includes += tdb_cpppath
|
|
else:
|
|
extra_includes += [ '../lib/tdb/include' ]
|
|
|
|
if bld.CONFIG_SET('USING_SYSTEM_TEVENT'):
|
|
- (tevent_includes, tevent_ldflags, tevent_cpppath) = library_flags(bld, 'tevent')
|
|
+ (tevent_includes, tevent_ldflags, tevent_cpppath, tevent_libs) = library_flags(bld, 'tevent')
|
|
extra_includes += tevent_cpppath
|
|
else:
|
|
extra_includes += [ '../lib/tevent' ]
|
|
|
|
if bld.CONFIG_SET('USING_SYSTEM_TALLOC'):
|
|
- (talloc_includes, talloc_ldflags, talloc_cpppath) = library_flags(bld, 'talloc')
|
|
+ (talloc_includes, talloc_ldflags, talloc_cpppath, talloc_libs) = library_flags(bld, 'talloc')
|
|
extra_includes += talloc_cpppath
|
|
else:
|
|
extra_includes += [ '../lib/talloc' ]
|
|
|
|
if bld.CONFIG_SET('USING_SYSTEM_POPT'):
|
|
- (popt_includes, popt_ldflags, popt_cpppath) = library_flags(bld, 'popt')
|
|
+ (popt_includes, popt_ldflags, popt_cpppath, popt_libs) = library_flags(bld, 'popt')
|
|
extra_includes += popt_cpppath
|
|
else:
|
|
extra_includes += [ '../lib/popt' ]
|
|
diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py
|
|
index 34fd5fab2c0..d3b6503c5ca 100644
|
|
--- a/buildtools/wafsamba/samba_autoconf.py
|
|
+++ b/buildtools/wafsamba/samba_autoconf.py
|
|
@@ -91,7 +91,7 @@ def CHECK_HEADER(conf, h, add_headers=False, lib=None):
|
|
conf.env.hlist.append(h)
|
|
return True
|
|
|
|
- (ccflags, ldflags, cpppath) = library_flags(conf, lib)
|
|
+ (ccflags, ldflags, cpppath, libs) = library_flags(conf, lib)
|
|
|
|
hdrs = hlist_to_string(conf, headers=h)
|
|
if lib is None:
|
|
@@ -435,7 +435,7 @@ def CHECK_CODE(conf, code, define,
|
|
|
|
uselib = TO_LIST(lib)
|
|
|
|
- (ccflags, ldflags, cpppath) = library_flags(conf, uselib)
|
|
+ (ccflags, ldflags, cpppath, libs) = library_flags(conf, uselib)
|
|
|
|
includes = TO_LIST(includes)
|
|
includes.extend(cpppath)
|
|
@@ -569,21 +569,24 @@ Build.BuildContext.CONFIG_SET = CONFIG_SET
|
|
Build.BuildContext.CONFIG_GET = CONFIG_GET
|
|
|
|
|
|
-def library_flags(self, libs):
|
|
+def library_flags(self, library):
|
|
'''work out flags from pkg_config'''
|
|
ccflags = []
|
|
ldflags = []
|
|
cpppath = []
|
|
- for lib in TO_LIST(libs):
|
|
+ libs = []
|
|
+ for lib in TO_LIST(library):
|
|
# note that we do not add the -I and -L in here, as that is added by the waf
|
|
# core. Adding it here would just change the order that it is put on the link line
|
|
# which can cause system paths to be added before internal libraries
|
|
extra_ccflags = TO_LIST(getattr(self.env, 'CFLAGS_%s' % lib.upper(), []))
|
|
extra_ldflags = TO_LIST(getattr(self.env, 'LDFLAGS_%s' % lib.upper(), []))
|
|
extra_cpppath = TO_LIST(getattr(self.env, 'CPPPATH_%s' % lib.upper(), []))
|
|
+ extra_libs = TO_LIST(getattr(self.env, 'LIB_%s' % lib.upper(), []))
|
|
ccflags.extend(extra_ccflags)
|
|
ldflags.extend(extra_ldflags)
|
|
cpppath.extend(extra_cpppath)
|
|
+ libs.extend(extra_libs)
|
|
|
|
extra_cpppath = TO_LIST(getattr(self.env, 'INCLUDES_%s' % lib.upper(), []))
|
|
cpppath.extend(extra_cpppath)
|
|
@@ -593,11 +596,12 @@ def library_flags(self, libs):
|
|
ccflags = unique_list(ccflags)
|
|
ldflags = unique_list(ldflags)
|
|
cpppath = unique_list(cpppath)
|
|
- return (ccflags, ldflags, cpppath)
|
|
+ libs = unique_list(libs)
|
|
+ return (ccflags, ldflags, cpppath, libs)
|
|
|
|
|
|
@conf
|
|
-def CHECK_LIB(conf, libs, mandatory=False, empty_decl=True, set_target=True, shlib=False):
|
|
+def CHECK_LIB(conf, library, mandatory=False, empty_decl=True, set_target=True, shlib=False):
|
|
'''check if a set of libraries exist as system libraries
|
|
|
|
returns the sublist of libs that do exist as a syslib or []
|
|
@@ -611,13 +615,13 @@ int foo()
|
|
}
|
|
'''
|
|
ret = []
|
|
- liblist = TO_LIST(libs)
|
|
- for lib in liblist[:]:
|
|
+ liblist = TO_LIST(library)
|
|
+ for lib in liblist:
|
|
if GET_TARGET_TYPE(conf, lib) == 'SYSLIB':
|
|
ret.append(lib)
|
|
continue
|
|
|
|
- (ccflags, ldflags, cpppath) = library_flags(conf, lib)
|
|
+ (ccflags, ldflags, cpppath, libs) = library_flags(conf, lib)
|
|
if shlib:
|
|
res = conf.check(features='c cshlib', fragment=fragment, lib=lib, uselib_store=lib, cflags=ccflags, ldflags=ldflags, uselib=lib.upper(), mandatory=False)
|
|
else:
|
|
diff --git a/buildtools/wafsamba/samba_deps.py b/buildtools/wafsamba/samba_deps.py
|
|
index 66adf40307e..5b428295b86 100644
|
|
--- a/buildtools/wafsamba/samba_deps.py
|
|
+++ b/buildtools/wafsamba/samba_deps.py
|
|
@@ -83,9 +83,8 @@ def build_dependencies(self):
|
|
self.add_objects = list(self.final_objects)
|
|
|
|
# extra link flags from pkg_config
|
|
- libs = self.final_syslibs.copy()
|
|
-
|
|
- (cflags, ldflags, cpppath) = library_flags(self, list(libs))
|
|
+ (cflags, ldflags, cpppath, libs) = library_flags(
|
|
+ self, list(self.final_syslibs.copy()))
|
|
new_ldflags = getattr(self, 'samba_ldflags', [])[:]
|
|
new_ldflags.extend(ldflags)
|
|
self.ldflags = new_ldflags
|
|
diff --git a/lib/util/charset/wscript_configure b/lib/util/charset/wscript_configure
|
|
index 9c27fc664f0..58858f69b31 100644
|
|
--- a/lib/util/charset/wscript_configure
|
|
+++ b/lib/util/charset/wscript_configure
|
|
@@ -8,7 +8,7 @@
|
|
# managed to link when specifying -liconv a executable even if there is no
|
|
# libiconv.so or libiconv.a
|
|
|
|
-conf.CHECK_LIB(libs="iconv", shlib=True)
|
|
+conf.CHECK_LIB("iconv", shlib=True)
|
|
|
|
#HP-UX can use libiconv as an add-on package, which has #define iconv_open libiconv_open
|
|
if (conf.CHECK_FUNCS_IN('iconv_open', 'iconv', checklibc=False, headers='iconv.h') or
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From a4f79d7fb725fab47bda53b9482c1ee301a8393a Mon Sep 17 00:00:00 2001
|
|
From: Earl Chew <earl_chew@yahoo.com>
|
|
Date: Sat, 16 Dec 2023 17:47:09 -0800
|
|
Subject: [PATCH 065/122] Improve CHECK_LIB interaction with CHECK_PKG
|
|
|
|
When checking for shared libraries, only name the target library
|
|
if it was not previously discoverd by pkg-config --libs and now
|
|
available from uselib_store. This avoids using both sources of
|
|
information which results in the library being named twice on
|
|
the command line.
|
|
|
|
Once the library is confirmed by CHECK_LIB, append the library if
|
|
not already present, to avoid dropping libraries that were
|
|
previously discovered by CHECK_PKG.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15623
|
|
|
|
Signed-off-by: Earl Chew <earl_chew@yahoo.com>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
(cherry picked from commit 0c983bd0095d4fb20ef8b42f5efb740393073862)
|
|
---
|
|
buildtools/wafsamba/samba_autoconf.py | 12 ++++++++++--
|
|
1 file changed, 10 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py
|
|
index d3b6503c5ca..b1d2f761095 100644
|
|
--- a/buildtools/wafsamba/samba_autoconf.py
|
|
+++ b/buildtools/wafsamba/samba_autoconf.py
|
|
@@ -623,7 +623,12 @@ int foo()
|
|
|
|
(ccflags, ldflags, cpppath, libs) = library_flags(conf, lib)
|
|
if shlib:
|
|
- res = conf.check(features='c cshlib', fragment=fragment, lib=lib, uselib_store=lib, cflags=ccflags, ldflags=ldflags, uselib=lib.upper(), mandatory=False)
|
|
+ # Avoid repeating the library if it is already named by
|
|
+ # pkg-config --libs.
|
|
+ kw = {}
|
|
+ if lib not in libs:
|
|
+ kw['lib'] = lib
|
|
+ res = conf.check(features='c cshlib', fragment=fragment, uselib_store=lib, cflags=ccflags, ldflags=ldflags, uselib=lib.upper(), mandatory=False, **kw)
|
|
else:
|
|
res = conf.check(lib=lib, uselib_store=lib, cflags=ccflags, ldflags=ldflags, uselib=lib.upper(), mandatory=False)
|
|
|
|
@@ -637,7 +642,10 @@ int foo()
|
|
SET_TARGET_TYPE(conf, lib, 'EMPTY')
|
|
else:
|
|
conf.define('HAVE_LIB%s' % lib.upper().replace('-','_').replace('.','_'), 1)
|
|
- conf.env['LIB_' + lib.upper()] = lib
|
|
+ # To avoid losing information from pkg-config, append the library
|
|
+ # only it is not already present.
|
|
+ if lib not in libs:
|
|
+ conf.env.append_value('LIB_' + lib.upper(), lib)
|
|
if set_target:
|
|
conf.SET_TARGET_TYPE(lib, 'SYSLIB')
|
|
ret.append(lib)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 2b4f5a62eac69e12ecd9a1e3919ea4a8b3d40820 Mon Sep 17 00:00:00 2001
|
|
From: Earl Chew <earl_chew@yahoo.com>
|
|
Date: Sat, 16 Dec 2023 08:48:36 -0800
|
|
Subject: [PATCH 066/122] Combine ICU libraries icu-i18n and icu-uc into a
|
|
single dependency
|
|
|
|
Rather than probing for icu-i18n, icu-uc, and icudata libraries
|
|
separately, only probe for icu-i18n, and icu-uc, as direct dependencies
|
|
This avoids overlinking with icudata, and allows the package
|
|
to build even when ICU is not installed as a system library.
|
|
|
|
RN: Only use icu-i18n and icu-uc to express ICU dependency
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15623
|
|
|
|
Signed-off-by: Earl Chew <earl_chew@yahoo.com>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
(cherry picked from commit 05807488fd340751ee976c5f8a367013ff94843e)
|
|
---
|
|
lib/util/charset/iconv.c | 8 ++++----
|
|
lib/util/charset/wscript_build | 3 ++-
|
|
lib/util/charset/wscript_configure | 17 +++++++----------
|
|
3 files changed, 13 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/lib/util/charset/iconv.c b/lib/util/charset/iconv.c
|
|
index 30e705ee119..3234f92bc55 100644
|
|
--- a/lib/util/charset/iconv.c
|
|
+++ b/lib/util/charset/iconv.c
|
|
@@ -26,7 +26,7 @@
|
|
#include "lib/util/charset/charset.h"
|
|
#include "lib/util/charset/charset_proto.h"
|
|
|
|
-#ifdef HAVE_ICU_I18N
|
|
+#ifdef HAVE_ICUI18N
|
|
#include <unicode/ustring.h>
|
|
#include <unicode/utrans.h>
|
|
#endif
|
|
@@ -168,7 +168,7 @@ static size_t sys_iconv(void *cd,
|
|
}
|
|
#endif
|
|
|
|
-#ifdef HAVE_ICU_I18N
|
|
+#ifdef HAVE_ICUI18N
|
|
static size_t sys_uconv(void *cd,
|
|
const char **inbuf,
|
|
size_t *inbytesleft,
|
|
@@ -334,7 +334,7 @@ static bool is_utf16(const char *name)
|
|
|
|
static int smb_iconv_t_destructor(smb_iconv_t hwd)
|
|
{
|
|
-#ifdef HAVE_ICU_I18N
|
|
+#ifdef HAVE_ICUI18N
|
|
/*
|
|
* This has to come first, as the cd_direct member won't be an iconv
|
|
* handle and must not be passed to iconv_close().
|
|
@@ -418,7 +418,7 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode,
|
|
}
|
|
#endif
|
|
|
|
-#ifdef HAVE_ICU_I18N
|
|
+#ifdef HAVE_ICUI18N
|
|
if (strcasecmp(fromcode, "UTF8-NFD") == 0 &&
|
|
strcasecmp(tocode, "UTF8-NFC") == 0)
|
|
{
|
|
diff --git a/lib/util/charset/wscript_build b/lib/util/charset/wscript_build
|
|
index c69a17170ad..3af90a0ad57 100644
|
|
--- a/lib/util/charset/wscript_build
|
|
+++ b/lib/util/charset/wscript_build
|
|
@@ -6,7 +6,8 @@ bld.SAMBA_SUBSYSTEM('ICONV_WRAPPER',
|
|
weird.c
|
|
charset_macosxfs.c
|
|
''',
|
|
- public_deps='iconv replace talloc ' + bld.env['icu-libs'])
|
|
+ deps=bld.env['icu-libs'],
|
|
+ public_deps='iconv replace talloc')
|
|
|
|
bld.SAMBA_SUBSYSTEM('charset',
|
|
public_headers='charset.h',
|
|
diff --git a/lib/util/charset/wscript_configure b/lib/util/charset/wscript_configure
|
|
index 58858f69b31..c49b55a4fd4 100644
|
|
--- a/lib/util/charset/wscript_configure
|
|
+++ b/lib/util/charset/wscript_configure
|
|
@@ -37,15 +37,12 @@ conf.CHECK_CODE('''
|
|
lib='iconv',
|
|
headers='errno.h iconv.h')
|
|
|
|
-if conf.CHECK_CFG(package='icu-i18n',
|
|
+if conf.CHECK_CFG(package='icu-i18n icu-uc',
|
|
args='--cflags --libs',
|
|
- msg='Checking for icu-i18n',
|
|
- uselib_store='ICU_I18N'):
|
|
- for lib in conf.env['LIB_ICU_I18N']:
|
|
- conf.CHECK_LIB(lib, shlib=True, mandatory=True)
|
|
- conf.env['icu-libs'] = ' '.join(conf.env['LIB_ICU_I18N'])
|
|
- if not conf.CHECK_HEADERS('unicode/ustring.h'):
|
|
- conf.fatal('Found libicu, but unicode/ustring.h is missing')
|
|
+ msg='Checking for icu-i18n icu-uc',
|
|
+ uselib_store='ICUI18N'):
|
|
+ conf.env['icu-libs'] = 'icui18n'
|
|
+ conf.CHECK_LIB(conf.env['icu-libs'], shlib=True, mandatory=True)
|
|
+ if not conf.CHECK_HEADERS('unicode/ustring.h', lib='icui18n'):
|
|
+ conf.fatal('Found icui18n, but unicode/ustring.h is missing')
|
|
conf.DEFINE('HAVE_UTF8_NORMALISATION', 1)
|
|
-else:
|
|
- conf.env['icu-libs'] = ''
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 8e5968634b263c20ad71c75e839abb217614b567 Mon Sep 17 00:00:00 2001
|
|
From: Earl Chew <earl_chew@yahoo.com>
|
|
Date: Fri, 10 May 2024 19:46:28 -0700
|
|
Subject: [PATCH 067/122] Restore empty string default for conf.env['icu-libs']
|
|
|
|
The reworked ICU libraries configuration code used [] as
|
|
default for conf.env['icu-libs']. This breaks dependency analysis
|
|
in samba_deps.py because SAMBA_SUBSYSTEM() expects deps to be
|
|
a string.
|
|
|
|
Signed-off-by: Earl Chew <earl_chew@yahoo.com>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Tue May 14 14:44:06 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 68a1200f66e9008ca0a739b37b48c49453ca9d83)
|
|
---
|
|
lib/util/charset/wscript_configure | 2 ++
|
|
1 file changed, 2 insertions(+)
|
|
|
|
diff --git a/lib/util/charset/wscript_configure b/lib/util/charset/wscript_configure
|
|
index c49b55a4fd4..adae44eab5e 100644
|
|
--- a/lib/util/charset/wscript_configure
|
|
+++ b/lib/util/charset/wscript_configure
|
|
@@ -46,3 +46,5 @@ if conf.CHECK_CFG(package='icu-i18n icu-uc',
|
|
if not conf.CHECK_HEADERS('unicode/ustring.h', lib='icui18n'):
|
|
conf.fatal('Found icui18n, but unicode/ustring.h is missing')
|
|
conf.DEFINE('HAVE_UTF8_NORMALISATION', 1)
|
|
+else:
|
|
+ conf.env['icu-libs'] = ''
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 88a29be0ed6cf611eb812c0729d2ee61be07a3a3 Mon Sep 17 00:00:00 2001
|
|
From: Earl Chew <earl_chew@yahoo.com>
|
|
Date: Fri, 27 Sep 2024 06:50:31 -0700
|
|
Subject: [PATCH 068/122] Describe implication of upstream ICU-22610
|
|
|
|
Add commentary to link commit 86c7688 (MR !3447) to the upstream
|
|
fix for ICU-22610 in case there is subsequent breakage.
|
|
|
|
Signed-off-by: Earl Chew <earl_chew@yahoo.com>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
|
|
|
|
Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
|
|
Autobuild-Date(master): Fri Nov 8 00:20:38 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit 1655413f1246147db9b34d4684a64dac49cf5f0c)
|
|
---
|
|
lib/util/charset/wscript_configure | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/lib/util/charset/wscript_configure b/lib/util/charset/wscript_configure
|
|
index adae44eab5e..451f7f7bca3 100644
|
|
--- a/lib/util/charset/wscript_configure
|
|
+++ b/lib/util/charset/wscript_configure
|
|
@@ -37,6 +37,10 @@ conf.CHECK_CODE('''
|
|
lib='iconv',
|
|
headers='errno.h iconv.h')
|
|
|
|
+# Since commit 86c7688 (MR !3447), the required ICU libraries are discovered
|
|
+# as a single group. This had the benefit of working around ICU-22610, and also
|
|
+# works with the fix that was merged to ICU main in commit 199bc827.
|
|
+
|
|
if conf.CHECK_CFG(package='icu-i18n icu-uc',
|
|
args='--cflags --libs',
|
|
msg='Checking for icu-i18n icu-uc',
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 72c6766af2ac55854b816147a277404d98b1de9a Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 8 Jan 2026 11:55:18 +0100
|
|
Subject: [PATCH 069/122] smbd: add a directory argument to
|
|
safe_symlink_target_path()
|
|
|
|
Existing caller passes NULL, no change in behaviour. Prepares for
|
|
replacing symlink_target_below_conn() in open.c.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15549
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Volker Lendecke <vl@samba.org>
|
|
(backported from commit fc80c72d658a41fe4d93b24b793b52c91b350175)
|
|
|
|
Backport changes:
|
|
- The v4-19 branch has a different safe_symlink_target_path() signature
|
|
that takes (name_in, substitute) instead of (dir, target), due to
|
|
commit 7d102268ebb being backported out of order.
|
|
- Adapted the function to use the new (dir, target) signature matching
|
|
proto.h, which already had the correct declaration.
|
|
- Updated filename_convert_dirfsp() to call symlink_target_path() first
|
|
to compute the target path, then pass dir=NULL to safe_symlink_target_path().
|
|
- This fixes symlink resolution for relative symlinks in subdirectories
|
|
(e.g., subdir/link -> ../file) by correctly building the absolute path
|
|
as connectpath/dir/target instead of just connectpath/target.
|
|
---
|
|
source3/smbd/filename.c | 52 +++++++++++++++++++++++++++--------------
|
|
1 file changed, 34 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c
|
|
index 9fd85af992a..f6e9ed6aae0 100644
|
|
--- a/source3/smbd/filename.c
|
|
+++ b/source3/smbd/filename.c
|
|
@@ -944,35 +944,40 @@ static char *symlink_target_path(
|
|
|
|
NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
|
|
const char *connectpath,
|
|
- const char *name_in,
|
|
- const char *substitute,
|
|
+ const char *dir,
|
|
+ const char *target,
|
|
size_t unparsed,
|
|
char **_relative)
|
|
{
|
|
- char *target = NULL;
|
|
char *abs_target = NULL;
|
|
char *abs_target_canon = NULL;
|
|
const char *relative = NULL;
|
|
bool in_share;
|
|
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
|
|
- target = symlink_target_path(mem_ctx,
|
|
- name_in,
|
|
- substitute,
|
|
- unparsed);
|
|
- if (target == NULL) {
|
|
- goto fail;
|
|
- }
|
|
-
|
|
- DBG_DEBUG("connectpath [%s] target [%s] unparsed [%zu]\n",
|
|
- connectpath, target, unparsed);
|
|
+ DBG_DEBUG("connectpath [%s] dir [%s] target [%s] unparsed [%zu]\n",
|
|
+ connectpath,
|
|
+ dir != NULL ? dir : "",
|
|
+ target,
|
|
+ unparsed);
|
|
|
|
if (target[0] == '/') {
|
|
- abs_target = target;
|
|
- } else {
|
|
- abs_target = talloc_asprintf(target,
|
|
+ abs_target = talloc_strdup(mem_ctx, target);
|
|
+ } else if (dir == NULL) {
|
|
+ abs_target = talloc_asprintf(mem_ctx,
|
|
+ "%s/%s",
|
|
+ connectpath,
|
|
+ target);
|
|
+ } else if (dir[0] == '/') {
|
|
+ abs_target = talloc_asprintf(mem_ctx,
|
|
"%s/%s",
|
|
+ dir,
|
|
+ target);
|
|
+ } else {
|
|
+ abs_target = talloc_asprintf(mem_ctx,
|
|
+ "%s/%s/%s",
|
|
connectpath,
|
|
+ dir,
|
|
target);
|
|
}
|
|
if (abs_target == NULL) {
|
|
@@ -1432,6 +1437,7 @@ NTSTATUS filename_convert_dirfsp(
|
|
struct open_symlink_err *symlink_err = NULL;
|
|
NTSTATUS status;
|
|
char *substitute = NULL;
|
|
+ char *target = NULL;
|
|
char *safe_target = NULL;
|
|
size_t symlink_redirects = 0;
|
|
|
|
@@ -1470,13 +1476,23 @@ next:
|
|
*/
|
|
substitute = symlink_err->reparse->substitute_name;
|
|
|
|
+ target = symlink_target_path(mem_ctx,
|
|
+ name_in,
|
|
+ substitute,
|
|
+ symlink_err->unparsed);
|
|
+ if (target == NULL) {
|
|
+ TALLOC_FREE(symlink_err);
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+
|
|
status = safe_symlink_target_path(mem_ctx,
|
|
conn->connectpath,
|
|
- name_in,
|
|
- substitute,
|
|
+ NULL,
|
|
+ target,
|
|
symlink_err->unparsed,
|
|
&safe_target);
|
|
TALLOC_FREE(symlink_err);
|
|
+ TALLOC_FREE(target);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 9b8c2d3abe56b53b4ac7dfb6af927a889580ae7f Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org>
|
|
Date: Mon, 19 Jan 2026 14:33:52 +0100
|
|
Subject: [PATCH 070/122] s3:libads: Reset ads->config.flags in
|
|
ads_disconnect()
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
This is doing the same thing in ads_disconnect() as commit
|
|
a26f535 Clear previous CLDAP ping flags when reusing the ADS_STRUCT
|
|
did in ads_current_time()
|
|
|
|
In this case we:
|
|
|
|
1) found cached ADS_STRUCT which already has ads->config.flags set:
|
|
|
|
lookup_groupmem()
|
|
ads_cached_connection()
|
|
ads_cached_connection_reuse()
|
|
|
|
2) started search which immediately timeouts (the cached conn. was dead)
|
|
|
|
ads_do_search_retry_internal()
|
|
ldap_search_with_timeout() - IO_TIMEOUT
|
|
|
|
3) Retry loop finds a new DC and tries to connect
|
|
|
|
ads_do_search_retry_internal()
|
|
ads_disconnect()
|
|
ads_find_dc()
|
|
ads_try_connect()
|
|
netlogon_pings()
|
|
check_cldap_reply_required_flags()
|
|
|
|
4) check_cldap_reply_required_flags() fails since ads->config.flags
|
|
(stored possibly long time ago) contain:
|
|
|
|
NBT_SERVER_CLOSEST 0x00000080
|
|
which is misinterpreted as:
|
|
DS_PDC_REQUIRED 0x00000080
|
|
|
|
the newly found DC is not PDC (we asked for DS_ONLY_LDAP_NEEDED)
|
|
and since previous DC had NBT_SERVER_CLOSEST we want DS_PDC_REQUIRED
|
|
and fail.
|
|
|
|
We should anyway avoid mixing independent namespaces NBT_* and DS_*
|
|
in the same flag.
|
|
Next commit will do that.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15972
|
|
|
|
Signed-off-by: Pavel Filipenský <pfilipensky@samba.org>
|
|
Reviewed-by: Andreas Schneider <asn@samba.org>
|
|
(cherry picked from commit 9f3a35991feb01a8d2c2b69fa0b914bbc637a809)
|
|
---
|
|
source3/libads/ldap.c | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
diff --git a/source3/libads/ldap.c b/source3/libads/ldap.c
|
|
index cc00753ff74..625377fa2cc 100644
|
|
--- a/source3/libads/ldap.c
|
|
+++ b/source3/libads/ldap.c
|
|
@@ -1068,6 +1068,7 @@ void ads_disconnect(ADS_STRUCT *ads)
|
|
if (ads->ldap_wrap_data.mem_ctx) {
|
|
talloc_free(ads->ldap_wrap_data.mem_ctx);
|
|
}
|
|
+ ads->config.flags = 0;
|
|
ads_zero_ldap(ads);
|
|
ZERO_STRUCT(ads->ldap_wrap_data);
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 1e1c43cc946f1947835570907064b4ee3aaa3ee7 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 30 Aug 2024 14:22:24 +0200
|
|
Subject: [PATCH 071/122] s4:torture/smb2: improve error handling in
|
|
durable_open.c
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit e65e1326a0214a7dfff75ea1e528e82c8fc64517)
|
|
---
|
|
source4/torture/smb2/durable_open.c | 39 ++++++++---------------------
|
|
1 file changed, 11 insertions(+), 28 deletions(-)
|
|
|
|
diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
|
|
index f56c55811ac..39b6efad567 100644
|
|
--- a/source4/torture/smb2/durable_open.c
|
|
+++ b/source4/torture/smb2/durable_open.c
|
|
@@ -28,34 +28,17 @@
|
|
#include "torture/smb2/proto.h"
|
|
#include "../libcli/smb/smbXcli_base.h"
|
|
|
|
-#define CHECK_VAL(v, correct) do { \
|
|
- if ((v) != (correct)) { \
|
|
- torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
|
|
- __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
|
|
- ret = false; \
|
|
- }} while (0)
|
|
-
|
|
-#define CHECK_NOT_VAL(v, incorrect) do { \
|
|
- if ((v) == (incorrect)) { \
|
|
- torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
|
|
- __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \
|
|
- ret = false; \
|
|
- }} while (0)
|
|
-
|
|
-#define CHECK_NOT_NULL(p) do { \
|
|
- if ((p) == NULL) { \
|
|
- torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \
|
|
- __location__, #p); \
|
|
- ret = false; \
|
|
- }} while (0)
|
|
-
|
|
-#define CHECK_STATUS(status, correct) do { \
|
|
- if (!NT_STATUS_EQUAL(status, correct)) { \
|
|
- torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
|
|
- nt_errstr(status), nt_errstr(correct)); \
|
|
- ret = false; \
|
|
- goto done; \
|
|
- }} while (0)
|
|
+#define CHECK_VAL(v, correct) \
|
|
+ torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__)
|
|
+
|
|
+#define CHECK_NOT_VAL(v, incorrect) \
|
|
+ torture_assert_u64_not_equal_goto(tctx, v, incorrect, ret, done, __location__)
|
|
+
|
|
+#define CHECK_NOT_NULL(p) \
|
|
+ torture_assert_not_null_goto(tctx, p, ret, done, __location__)
|
|
+
|
|
+#define CHECK_STATUS(status, correct) \
|
|
+ torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__)
|
|
|
|
#define CHECK_CREATED(__io, __created, __attribute) \
|
|
do { \
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 9149916376570a97ee0d94e9ed64751796da5053 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 30 Aug 2024 14:22:24 +0200
|
|
Subject: [PATCH 072/122] s4:torture/smb2: improve error handling in
|
|
durable_v2_open.c
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 9b2417c2f04857709c25e3665cd783a68edf0cf2)
|
|
---
|
|
source4/torture/smb2/durable_v2_open.c | 19 +++++--------------
|
|
1 file changed, 5 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
|
|
index 7447dd287a4..7eed4327b52 100644
|
|
--- a/source4/torture/smb2/durable_v2_open.c
|
|
+++ b/source4/torture/smb2/durable_v2_open.c
|
|
@@ -27,20 +27,11 @@
|
|
#include "torture/smb2/proto.h"
|
|
#include "librpc/ndr/libndr.h"
|
|
|
|
-#define CHECK_VAL(v, correct) do { \
|
|
- if ((v) != (correct)) { \
|
|
- torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
|
|
- __location__, #v, (int)v, (int)correct); \
|
|
- ret = false; \
|
|
- }} while (0)
|
|
-
|
|
-#define CHECK_STATUS(status, correct) do { \
|
|
- if (!NT_STATUS_EQUAL(status, correct)) { \
|
|
- torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
|
|
- nt_errstr(status), nt_errstr(correct)); \
|
|
- ret = false; \
|
|
- goto done; \
|
|
- }} while (0)
|
|
+#define CHECK_VAL(v, correct) \
|
|
+ torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__)
|
|
+
|
|
+#define CHECK_STATUS(status, correct) \
|
|
+ torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__)
|
|
|
|
#define CHECK_CREATED(__io, __created, __attribute) \
|
|
do { \
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 986cc4d97eba6d3807170f66f6b3664f395a6e3e Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 30 Aug 2024 17:38:02 +0200
|
|
Subject: [PATCH 073/122] s4:torture/smb2: add smb2.durable-open.lock-noW-lease
|
|
|
|
This demonstrates that a W lease is required for a
|
|
durable handle to be durable when it has byte range locks.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 1cc1586d84a65046ab7804f17297c6964bb76c23)
|
|
---
|
|
selftest/knownfail.d/smb2.durable.lock | 1 +
|
|
source4/torture/smb2/durable_open.c | 97 +++++++++++++++++++++++++-
|
|
2 files changed, 97 insertions(+), 1 deletion(-)
|
|
create mode 100644 selftest/knownfail.d/smb2.durable.lock
|
|
|
|
diff --git a/selftest/knownfail.d/smb2.durable.lock b/selftest/knownfail.d/smb2.durable.lock
|
|
new file mode 100644
|
|
index 00000000000..3e3bd80ba46
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/smb2.durable.lock
|
|
@@ -0,0 +1 @@
|
|
+^samba3.smb2.durable-open.lock-noW-lease
|
|
diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
|
|
index 39b6efad567..cc78c0aff66 100644
|
|
--- a/source4/torture/smb2/durable_open.c
|
|
+++ b/source4/torture/smb2/durable_open.c
|
|
@@ -2151,7 +2151,7 @@ static bool test_durable_open_lock_oplock(struct torture_context *tctx,
|
|
}
|
|
|
|
/*
|
|
- Open, take BRL, disconnect, reconnect.
|
|
+ Open(RWH), take BRL, disconnect, reconnect.
|
|
*/
|
|
static bool test_durable_open_lock_lease(struct torture_context *tctx,
|
|
struct smb2_tree *tree)
|
|
@@ -2249,6 +2249,100 @@ static bool test_durable_open_lock_lease(struct torture_context *tctx,
|
|
return ret;
|
|
}
|
|
|
|
+/*
|
|
+ Open(RH), take BRL, disconnect, fails reconnect without W LEASE
|
|
+*/
|
|
+static bool test_durable_open_lock_noW_lease(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ struct smb2_create io;
|
|
+ struct smb2_lease ls;
|
|
+ struct smb2_handle h = {{0}};
|
|
+ struct smb2_lock lck;
|
|
+ struct smb2_lock_element el[2];
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ bool ret = true;
|
|
+ uint64_t lease;
|
|
+ uint32_t caps;
|
|
+ struct smbcli_options options;
|
|
+
|
|
+ options = tree->session->transport->options;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Choose a random name and random lease in case the state is left a
|
|
+ * little funky.
|
|
+ */
|
|
+ lease = random();
|
|
+ snprintf(fname, 256, "durable_open_lease_noW_lock_%s.dat", generate_random_str(tctx, 8));
|
|
+
|
|
+ /* Clean slate */
|
|
+ smb2_util_unlink(tree, fname);
|
|
+
|
|
+ /* Create with lease */
|
|
+
|
|
+ smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
|
|
+ smb2_util_lease_state("RH"));
|
|
+ io.in.durable_open = true;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h = io.out.file.handle;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+
|
|
+ CHECK_VAL(io.out.durable_open, true);
|
|
+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
|
|
+ CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
|
|
+ CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
|
|
+ CHECK_VAL(io.out.lease_response.lease_state,
|
|
+ SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
|
|
+
|
|
+ ZERO_STRUCT(lck);
|
|
+ ZERO_STRUCT(el);
|
|
+ lck.in.locks = el;
|
|
+ lck.in.lock_count = 0x0001;
|
|
+ lck.in.lock_sequence = 0x00000000;
|
|
+ lck.in.file.handle = h;
|
|
+ el[0].offset = 0;
|
|
+ el[0].length = 1;
|
|
+ el[0].reserved = 0x00000000;
|
|
+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
|
|
+ status = smb2_lock(tree, &lck);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ /* Disconnect/Reconnect. */
|
|
+ talloc_free(tree);
|
|
+ tree = NULL;
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_handle = &h;
|
|
+ io.in.lease_request = &ls;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+ h = io.out.file.handle;
|
|
+
|
|
+ done:
|
|
+ smb2_util_close(tree, h);
|
|
+ smb2_util_unlink(tree, fname);
|
|
+ talloc_free(tree);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/**
|
|
* Open with a RH lease, disconnect, open in another tree, reconnect.
|
|
*
|
|
@@ -2823,6 +2917,7 @@ struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
|
|
torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
|
|
torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
|
|
torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
|
|
+ torture_suite_add_1smb2_test(suite, "lock-noW-lease", test_durable_open_lock_noW_lease);
|
|
torture_suite_add_2smb2_test(suite, "open2-lease",
|
|
test_durable_open_open2_lease);
|
|
torture_suite_add_2smb2_test(suite, "open2-oplock",
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From cdffe7d7a0b6919fe405fbf4b536a6f33d33363f Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 30 Aug 2024 17:38:02 +0200
|
|
Subject: [PATCH 074/122] s4:torture/smb2: add
|
|
smb2.durable-v2-open.lock-{oplock,lease,noW-lease}
|
|
|
|
This demonstrates that a W lease is required for a
|
|
durable handle to be durable when it has byte range locks.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 8884d617310b47375e38c0386433c5e183703454)
|
|
---
|
|
selftest/knownfail.d/smb2.durable.lock | 1 +
|
|
source4/torture/smb2/durable_v2_open.c | 336 +++++++++++++++++++++++++
|
|
2 files changed, 337 insertions(+)
|
|
|
|
diff --git a/selftest/knownfail.d/smb2.durable.lock b/selftest/knownfail.d/smb2.durable.lock
|
|
index 3e3bd80ba46..16273fb4ad4 100644
|
|
--- a/selftest/knownfail.d/smb2.durable.lock
|
|
+++ b/selftest/knownfail.d/smb2.durable.lock
|
|
@@ -1 +1,2 @@
|
|
^samba3.smb2.durable-open.lock-noW-lease
|
|
+^samba3.smb2.durable-v2-open.lock-noW-lease
|
|
diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
|
|
index 7eed4327b52..685ef80c0cc 100644
|
|
--- a/source4/torture/smb2/durable_v2_open.c
|
|
+++ b/source4/torture/smb2/durable_v2_open.c
|
|
@@ -41,6 +41,30 @@
|
|
CHECK_VAL((__io)->out.reserved2, 0); \
|
|
} while(0)
|
|
|
|
+#define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \
|
|
+ do { \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \
|
|
+ if (__oplevel) { \
|
|
+ CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
|
|
+ } else { \
|
|
+ CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
|
|
+ } \
|
|
+ \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
|
|
+ if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \
|
|
+ } \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
|
|
+ CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \
|
|
+ } while(0)
|
|
+
|
|
static struct {
|
|
int count;
|
|
struct smb2_close cl;
|
|
@@ -1731,6 +1755,315 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+/*
|
|
+ Open(BATCH), take BRL, disconnect, reconnect.
|
|
+*/
|
|
+static bool test_durable_v2_open_lock_oplock(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid = GUID_random();
|
|
+ struct smb2_handle h = {{0}};
|
|
+ struct smb2_lock lck;
|
|
+ struct smb2_lock_element el[2];
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options;
|
|
+
|
|
+ options = tree->session->transport->options;
|
|
+
|
|
+ snprintf(fname, 256, "durable_v2_open_lock_oplock_%s.dat", generate_random_str(tctx, 8));
|
|
+
|
|
+ /* Clean slate */
|
|
+ smb2_util_unlink(tree, fname);
|
|
+
|
|
+ /* Create with lease */
|
|
+
|
|
+ smb2_oplock_create_share(&io, fname,
|
|
+ smb2_util_share_access(""),
|
|
+ smb2_util_oplock_level("b"));
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h = io.out.file.handle;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
|
|
+
|
|
+ ZERO_STRUCT(lck);
|
|
+ ZERO_STRUCT(el);
|
|
+ lck.in.locks = el;
|
|
+ lck.in.lock_count = 0x0001;
|
|
+ lck.in.lock_sequence = 0x00000000;
|
|
+ lck.in.file.handle = h;
|
|
+ el[0].offset = 0;
|
|
+ el[0].length = 1;
|
|
+ el[0].reserved = 0x00000000;
|
|
+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
|
|
+ status = smb2_lock(tree, &lck);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ /* Disconnect/Reconnect. */
|
|
+ talloc_free(tree);
|
|
+ tree = NULL;
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = &h;
|
|
+ io.in.create_guid = create_guid;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h = io.out.file.handle;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
|
|
+
|
|
+ lck.in.file.handle = h;
|
|
+ el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
|
|
+ status = smb2_lock(tree, &lck);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ done:
|
|
+ smb2_util_close(tree, h);
|
|
+ smb2_util_unlink(tree, fname);
|
|
+ talloc_free(tree);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ Open(RWH), take BRL, disconnect, reconnect.
|
|
+*/
|
|
+static bool test_durable_v2_open_lock_lease(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ struct smb2_create io;
|
|
+ struct smb2_lease ls;
|
|
+ struct GUID create_guid = GUID_random();
|
|
+ struct smb2_handle h = {{0}};
|
|
+ struct smb2_lock lck;
|
|
+ struct smb2_lock_element el[2];
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ bool ret = true;
|
|
+ uint64_t lease;
|
|
+ uint32_t caps;
|
|
+ struct smbcli_options options;
|
|
+
|
|
+ options = tree->session->transport->options;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Choose a random name and random lease in case the state is left a
|
|
+ * little funky.
|
|
+ */
|
|
+ lease = random();
|
|
+ snprintf(fname, 256, "durable_v2_open_lock_lease_%s.dat", generate_random_str(tctx, 8));
|
|
+
|
|
+ /* Clean slate */
|
|
+ smb2_util_unlink(tree, fname);
|
|
+
|
|
+ /* Create with lease */
|
|
+
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h = io.out.file.handle;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ ZERO_STRUCT(lck);
|
|
+ ZERO_STRUCT(el);
|
|
+ lck.in.locks = el;
|
|
+ lck.in.lock_count = 0x0001;
|
|
+ lck.in.lock_sequence = 0x00000000;
|
|
+ lck.in.file.handle = h;
|
|
+ el[0].offset = 0;
|
|
+ el[0].length = 1;
|
|
+ el[0].reserved = 0x00000000;
|
|
+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
|
|
+ status = smb2_lock(tree, &lck);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ /* Disconnect/Reconnect. */
|
|
+ talloc_free(tree);
|
|
+ tree = NULL;
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = &h;
|
|
+ io.in.create_guid = create_guid;
|
|
+ io.in.lease_request_v2 = &ls;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h = io.out.file.handle;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ lck.in.file.handle = h;
|
|
+ el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
|
|
+ status = smb2_lock(tree, &lck);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ done:
|
|
+ smb2_util_close(tree, h);
|
|
+ smb2_util_unlink(tree, fname);
|
|
+ talloc_free(tree);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ Open(RH), take BRL, disconnect, fails reconnect without W LEASE
|
|
+*/
|
|
+static bool test_durable_v2_open_lock_noW_lease(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ struct smb2_create io;
|
|
+ struct smb2_lease ls;
|
|
+ struct GUID create_guid = GUID_random();
|
|
+ struct smb2_handle h = {{0}};
|
|
+ struct smb2_lock lck;
|
|
+ struct smb2_lock_element el[2];
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ bool ret = true;
|
|
+ uint64_t lease;
|
|
+ uint32_t caps;
|
|
+ struct smbcli_options options;
|
|
+
|
|
+ options = tree->session->transport->options;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Choose a random name and random lease in case the state is left a
|
|
+ * little funky.
|
|
+ */
|
|
+ lease = random();
|
|
+ snprintf(fname, 256, "durable_v2_open_lock_noW_lease_%s.dat", generate_random_str(tctx, 8));
|
|
+
|
|
+ /* Clean slate */
|
|
+ smb2_util_unlink(tree, fname);
|
|
+
|
|
+ /* Create with lease */
|
|
+
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h = io.out.file.handle;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ ZERO_STRUCT(lck);
|
|
+ ZERO_STRUCT(el);
|
|
+ lck.in.locks = el;
|
|
+ lck.in.lock_count = 0x0001;
|
|
+ lck.in.lock_sequence = 0x00000000;
|
|
+ lck.in.file.handle = h;
|
|
+ el[0].offset = 0;
|
|
+ el[0].length = 1;
|
|
+ el[0].reserved = 0x00000000;
|
|
+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
|
|
+ status = smb2_lock(tree, &lck);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ /* Disconnect/Reconnect. */
|
|
+ talloc_free(tree);
|
|
+ tree = NULL;
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = &h;
|
|
+ io.in.create_guid = create_guid;
|
|
+ io.in.lease_request_v2 = &ls;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+
|
|
+ done:
|
|
+ smb2_util_close(tree, h);
|
|
+ smb2_util_unlink(tree, fname);
|
|
+ talloc_free(tree);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/**
|
|
* Test durable request / reconnect with AppInstanceId
|
|
*/
|
|
@@ -2157,6 +2490,9 @@ struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx)
|
|
torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
|
|
torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
|
|
torture_suite_add_1smb2_test(suite, "durable-v2-setinfo", test_durable_v2_setinfo);
|
|
+ torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_v2_open_lock_oplock);
|
|
+ torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_v2_open_lock_lease);
|
|
+ torture_suite_add_1smb2_test(suite, "lock-noW-lease", test_durable_v2_open_lock_noW_lease);
|
|
torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
|
|
torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
|
|
torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d770866bbdd9ba70f896eb3b75bba6baa01fdf33 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 30 Aug 2024 18:10:16 +0200
|
|
Subject: [PATCH 075/122] s3:smbd: only store durable handles with byte range
|
|
locks when having WRITE lease
|
|
|
|
This simplifies the reconnect assumptions, when we want to allow
|
|
more than one durable handle on a file for multiple clients with
|
|
READ+HANDLE leases.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 0893ae88180137d44f17196234f657d362543ff5)
|
|
---
|
|
selftest/knownfail.d/smb2.durable.lock | 2 --
|
|
source3/smbd/durable.c | 6 ++++++
|
|
2 files changed, 6 insertions(+), 2 deletions(-)
|
|
delete mode 100644 selftest/knownfail.d/smb2.durable.lock
|
|
|
|
diff --git a/selftest/knownfail.d/smb2.durable.lock b/selftest/knownfail.d/smb2.durable.lock
|
|
deleted file mode 100644
|
|
index 16273fb4ad4..00000000000
|
|
--- a/selftest/knownfail.d/smb2.durable.lock
|
|
+++ /dev/null
|
|
@@ -1,2 +0,0 @@
|
|
-^samba3.smb2.durable-open.lock-noW-lease
|
|
-^samba3.smb2.durable-v2-open.lock-noW-lease
|
|
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
|
|
index 98d0d403e30..b7fa53e7555 100644
|
|
--- a/source3/smbd/durable.c
|
|
+++ b/source3/smbd/durable.c
|
|
@@ -173,6 +173,12 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
+ if (fsp->current_lock_count != 0 &&
|
|
+ (fsp_lease_type(fsp) & SMB2_LEASE_WRITE) == 0)
|
|
+ {
|
|
+ return NT_STATUS_NOT_SUPPORTED;
|
|
+ }
|
|
+
|
|
/*
|
|
* For now let it be simple and do not keep
|
|
* delete on close files durable open
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 14ec1065ac307f5c59e292ac8f805104f9c10884 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Wed, 28 Aug 2024 16:48:27 +0200
|
|
Subject: [PATCH 076/122] s4:torture/smb2: add
|
|
smb2.durable-v2-open.{[non]stat[RH]-and,two-same,two-different}-lease
|
|
|
|
These show that it's possible to have durable handles in addition
|
|
of stat opens, as well as multiple durable opens with RH leases.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 77c7741f39a0a9789bede7c4722bd3f35d4af3fd)
|
|
---
|
|
.../knownfail.d/smb2.durable-v2-open.bug15649 | 2 +
|
|
.../knownfail.d/smb2.durable-v2-open.bug15651 | 3 +
|
|
source4/torture/smb2/durable_v2_open.c | 784 ++++++++++++++++++
|
|
3 files changed, 789 insertions(+)
|
|
create mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15649
|
|
create mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
|
|
diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15649 b/selftest/knownfail.d/smb2.durable-v2-open.bug15649
|
|
new file mode 100644
|
|
index 00000000000..748b6c3150e
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15649
|
|
@@ -0,0 +1,2 @@
|
|
+^samba3.smb2.durable-v2-open.stat-and-lease
|
|
+^samba3.smb2.durable-v2-open.nonstat-and-lease
|
|
diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 b/selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
new file mode 100644
|
|
index 00000000000..1bb0a70d9a0
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
@@ -0,0 +1,3 @@
|
|
+^samba3.smb2.durable-v2-open.statRH-and-lease
|
|
+^samba3.smb2.durable-v2-open.two-same-lease
|
|
+^samba3.smb2.durable-v2-open.two-different-lease
|
|
diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
|
|
index 685ef80c0cc..e86b1955092 100644
|
|
--- a/source4/torture/smb2/durable_v2_open.c
|
|
+++ b/source4/torture/smb2/durable_v2_open.c
|
|
@@ -26,6 +26,7 @@
|
|
#include "torture/torture.h"
|
|
#include "torture/smb2/proto.h"
|
|
#include "librpc/ndr/libndr.h"
|
|
+#include "lease_break_handler.h"
|
|
|
|
#define CHECK_VAL(v, correct) \
|
|
torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__)
|
|
@@ -2064,6 +2065,784 @@ static bool test_durable_v2_open_lock_noW_lease(struct torture_context *tctx,
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * 1. stat open (without lease) => h1
|
|
+ * 2. durable open with RWH => h2
|
|
+ * 3. disconnect
|
|
+ * 4. reconnect
|
|
+ * 5. durable reconnect RWH => h2
|
|
+ */
|
|
+static bool test_durable_v2_open_stat_and_lease(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls;
|
|
+ uint64_t lease_key;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ smb2_generic_create(&io, NULL, false /* dir */, fname,
|
|
+ FILE_OPEN_IF, 0, 0, 0);
|
|
+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
+ io.in.desired_access |= SEC_FILE_WRITE_ATTRIBUTE;
|
|
+ io.in.desired_access |= SEC_STD_SYNCHRONIZE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
|
|
+
|
|
+ lease_key = random();
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease_key, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+ h1 = NULL;
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h2;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.lease_request_v2 = &ls;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree1 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree1, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. non stat open (without a lease) => h1
|
|
+ * 2. durable open with RWH => h2 => RH
|
|
+ * 3. disconnect
|
|
+ * 4. reconnect
|
|
+ * 5. durable reconnect RH => h2
|
|
+ */
|
|
+static bool test_durable_v2_open_nonstat_and_lease(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls;
|
|
+ uint64_t lease_key;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ smb2_generic_create(&io, NULL, false /* dir */, fname,
|
|
+ FILE_OPEN_IF, 0, 0, 0);
|
|
+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
+ io.in.desired_access |= SEC_FILE_WRITE_ATTRIBUTE;
|
|
+ io.in.desired_access |= SEC_STD_SYNCHRONIZE;
|
|
+ /*
|
|
+ * SEC_STD_READ_CONTROL means we no longer
|
|
+ * have a stat open that would allow a RWH lease
|
|
+ */
|
|
+ io.in.desired_access |= SEC_STD_READ_CONTROL;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
|
|
+
|
|
+ lease_key = random();
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease_key, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+ h1 = NULL;
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h2;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.lease_request_v2 = &ls;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree1 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree1, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. stat open with RH lease => h1
|
|
+ * 2. durable open with RWH => h2 => RH
|
|
+ * 3. disconnect
|
|
+ * 4. reconnect
|
|
+ * 5. durable reconnect RH => h2
|
|
+ */
|
|
+static bool test_durable_v2_open_statRH_and_lease(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls;
|
|
+ uint64_t lease_key;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ smb2_generic_create(&io, NULL, false /* dir */, fname,
|
|
+ FILE_OPEN_IF, 0, 0, 0);
|
|
+ lease_key = random();
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease_key, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ lease_key = random();
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease_key, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+ h1 = NULL;
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h2;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.lease_request_v2 = &ls;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree1 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree1, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1(RWH) => h1
|
|
+ * 2. durable open with L1(RWH) => h2
|
|
+ * 3. disconnect
|
|
+ * 4. reconnect
|
|
+ * 5. durable reconnect L1(RWH) => h1
|
|
+ * 6. durable reconnect L1(RWH) => h2
|
|
+ */
|
|
+static bool test_durable_v2_open_two_same_lease(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1 = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls;
|
|
+ uint64_t lease_key;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key = random();
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease_key, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
|
|
+ lease_key, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.lease_request_v2 = &ls;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h2;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.lease_request_v2 = &ls;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key,
|
|
+ 0, 0, ls.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1 = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree1 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree1, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1(RH) => h1
|
|
+ * 2. durable open with L2(RH) => h2
|
|
+ * 3. disconnect
|
|
+ * 4. reconnect
|
|
+ * 5. durable reconnect L1(RH) => h1
|
|
+ * 6. durable reconnect L2(RH) => h2
|
|
+ */
|
|
+static bool test_durable_v2_open_two_different_leases(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1 = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls1;
|
|
+ uint64_t lease_key1;
|
|
+ struct smb2_lease ls2;
|
|
+ uint64_t lease_key2;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1 = random();
|
|
+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname,
|
|
+ lease_key1, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1,
|
|
+ 0, 0, ls1.lease_epoch);
|
|
+
|
|
+ lease_key2 = random();
|
|
+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname,
|
|
+ lease_key2, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls2.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.lease_request_v2 = &ls1;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1,
|
|
+ 0, 0, ls1.lease_epoch);
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h2;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.lease_request_v2 = &ls2;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1 = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree1 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree1, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/**
|
|
* Test durable request / reconnect with AppInstanceId
|
|
*/
|
|
@@ -2493,6 +3272,11 @@ struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx)
|
|
torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_v2_open_lock_oplock);
|
|
torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_v2_open_lock_lease);
|
|
torture_suite_add_1smb2_test(suite, "lock-noW-lease", test_durable_v2_open_lock_noW_lease);
|
|
+ torture_suite_add_1smb2_test(suite, "stat-and-lease", test_durable_v2_open_stat_and_lease);
|
|
+ torture_suite_add_1smb2_test(suite, "nonstat-and-lease", test_durable_v2_open_nonstat_and_lease);
|
|
+ torture_suite_add_1smb2_test(suite, "statRH-and-lease", test_durable_v2_open_statRH_and_lease);
|
|
+ torture_suite_add_1smb2_test(suite, "two-same-lease", test_durable_v2_open_two_same_lease);
|
|
+ torture_suite_add_1smb2_test(suite, "two-different-lease", test_durable_v2_open_two_different_leases);
|
|
torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
|
|
torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
|
|
torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7c395c25b4e2d0365ef8c99404e5e9e88f0adf2d Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Wed, 4 Sep 2024 18:18:43 +0200
|
|
Subject: [PATCH 077/122] s4:torture/smb2: add
|
|
smb2.durable-v2-open.{keep,purge}-disconnected-* tests
|
|
|
|
These demonstrate which durables handles are kept and which are purged
|
|
because of various opens, writes or renames.
|
|
|
|
smb2.durable-v2-open.keep-disconnected-rh-with-stat-open
|
|
smb2.durable-v2-open.keep-disconnected-rh-with-rh-open
|
|
smb2.durable-v2-open.keep-disconnected-rh-with-rwh-open
|
|
smb2.durable-v2-open.keep-disconnected-rwh-with-stat-open
|
|
|
|
smb2.durable-v2-open.purge-disconnected-rwh-with-rwh-open
|
|
smb2.durable-v2-open.purge-disconnected-rwh-with-rh-open
|
|
smb2.durable-v2-open.purge-disconnected-rh-with-share-none-open
|
|
smb2.durable-v2-open.purge-disconnected-rh-with-write
|
|
smb2.durable-v2-open.purge-disconnected-rh-with-rename
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15708
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 9e98cd5c7a180521026b0d73a330bdaf2c8af73a)
|
|
---
|
|
.../knownfail.d/smb2.durable-v2-open.bug15651 | 2 +
|
|
.../knownfail.d/smb2.durable-v2-open.bug15708 | 7 +
|
|
source4/torture/smb2/durable_v2_open.c | 1851 +++++++++++++++++
|
|
3 files changed, 1860 insertions(+)
|
|
create mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15708
|
|
|
|
diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 b/selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
index 1bb0a70d9a0..1702a3a6580 100644
|
|
--- a/selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
@@ -1,3 +1,5 @@
|
|
^samba3.smb2.durable-v2-open.statRH-and-lease
|
|
^samba3.smb2.durable-v2-open.two-same-lease
|
|
^samba3.smb2.durable-v2-open.two-different-lease
|
|
+^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-stat-open
|
|
+^samba3.smb2.durable-v2-open.keep-disconnected-rwh-with-stat-open
|
|
diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15708 b/selftest/knownfail.d/smb2.durable-v2-open.bug15708
|
|
new file mode 100644
|
|
index 00000000000..3a6380c6d65
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15708
|
|
@@ -0,0 +1,7 @@
|
|
+#
|
|
+# https://bugzilla.samba.org/show_bug.cgi?id=15708 is not fixed
|
|
+# yet, it requires some complex changes within handle_share_mode_lease()
|
|
+# merging logic of open_mode_check() and delay_for_oplock()...
|
|
+#
|
|
+^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-rh-open
|
|
+^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-rwh-open
|
|
diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c
|
|
index e86b1955092..104796e76ad 100644
|
|
--- a/source4/torture/smb2/durable_v2_open.c
|
|
+++ b/source4/torture/smb2/durable_v2_open.c
|
|
@@ -2843,6 +2843,1848 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * 1. durable open with L1A(RH) on tree1 => h1a
|
|
+ * 1. durable open with L1B(RH) on tree1 => h1b
|
|
+ * 2. disconnect tree1
|
|
+ * 3. stat open on tree2 => h2
|
|
+ * 4. reconnect tree1
|
|
+ * 5. durable reconnect L1A(RH) => h1a
|
|
+ * 6. durable reconnect L1B(RH) => h1a
|
|
+ */
|
|
+static bool test_durable_v2_open_keep_disconnected_rh_with_stat_open(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1a;
|
|
+ struct smb2_handle *h1a = NULL;
|
|
+ struct smb2_handle _h1b;
|
|
+ struct smb2_handle *h1b = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1a = GUID_random();
|
|
+ struct GUID create_guid1b = GUID_random();
|
|
+ struct smb2_lease ls1a;
|
|
+ uint64_t lease_key1a;
|
|
+ struct smb2_lease ls1b;
|
|
+ uint64_t lease_key1b;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1a = random();
|
|
+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname,
|
|
+ lease_key1a, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1a.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ lease_key1b = random();
|
|
+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname,
|
|
+ lease_key1b, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ smb2_generic_create(&io, NULL, false /* dir */, fname,
|
|
+ FILE_OPEN_IF, 0, 0, 0);
|
|
+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1a;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.lease_request_v2 = &ls1a;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1b;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.lease_request_v2 = &ls1b;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1a);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1a = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1b);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1b = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree2, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1a != NULL) {
|
|
+ smb2_util_close(tree1, *h1a);
|
|
+ }
|
|
+ if (tree1 != NULL && h1b != NULL) {
|
|
+ smb2_util_close(tree1, *h1b);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1A(RH) on tree1 => h1a
|
|
+ * 1. durable open with L1B(RH) on tree1 => h1b
|
|
+ * 2. disconnect tree1
|
|
+ * 3. durable open with L2(RH) on tree2 => h2
|
|
+ * 4. reconnect tree1
|
|
+ * 5. durable reconnect L1A(RH) => h1a
|
|
+ * 6. durable reconnect L1B(RH) => h1a
|
|
+ */
|
|
+static bool test_durable_v2_open_keep_disconnected_rh_with_rh_open(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1a;
|
|
+ struct smb2_handle *h1a = NULL;
|
|
+ struct smb2_handle _h1b;
|
|
+ struct smb2_handle *h1b = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1a = GUID_random();
|
|
+ struct GUID create_guid1b = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls1a;
|
|
+ uint64_t lease_key1a;
|
|
+ struct smb2_lease ls1b;
|
|
+ uint64_t lease_key1b;
|
|
+ struct smb2_lease ls2;
|
|
+ uint64_t lease_key2;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1a = random();
|
|
+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname,
|
|
+ lease_key1a, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1a.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ lease_key1b = random();
|
|
+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname,
|
|
+ lease_key1b, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ lease_key2 = random();
|
|
+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname,
|
|
+ lease_key2, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls2.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1a;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.lease_request_v2 = &ls1a;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1b;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.lease_request_v2 = &ls1b;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1a);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1a = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1b);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1b = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree2, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1a != NULL) {
|
|
+ smb2_util_close(tree1, *h1a);
|
|
+ }
|
|
+ if (tree1 != NULL && h1b != NULL) {
|
|
+ smb2_util_close(tree1, *h1b);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1A(RH) on tree1 => h1a
|
|
+ * 1. durable open with L1B(RH) on tree1 => h1b
|
|
+ * 2. disconnect tree1
|
|
+ * 3. durable open with L2(RWH) on tree2 => h2 => RH
|
|
+ * 4. reconnect tree1
|
|
+ * 5. durable reconnect L1A(RH) => h1a
|
|
+ * 6. durable reconnect L1B(RH) => h1a
|
|
+ */
|
|
+static bool test_durable_v2_open_keep_disconnected_rh_with_rwh_open(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1a;
|
|
+ struct smb2_handle *h1a = NULL;
|
|
+ struct smb2_handle _h1b;
|
|
+ struct smb2_handle *h1b = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1a = GUID_random();
|
|
+ struct GUID create_guid1b = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls1a;
|
|
+ uint64_t lease_key1a;
|
|
+ struct smb2_lease ls1b;
|
|
+ uint64_t lease_key1b;
|
|
+ struct smb2_lease ls2;
|
|
+ uint64_t lease_key2;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1a = random();
|
|
+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname,
|
|
+ lease_key1a, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1a.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ lease_key1b = random();
|
|
+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname,
|
|
+ lease_key1b, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ lease_key2 = random();
|
|
+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname,
|
|
+ lease_key2, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls2.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1a;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.lease_request_v2 = &ls1a;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1b;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.lease_request_v2 = &ls1b;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1a);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1a = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1b);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1b = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree2, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1a != NULL) {
|
|
+ smb2_util_close(tree1, *h1a);
|
|
+ }
|
|
+ if (tree1 != NULL && h1b != NULL) {
|
|
+ smb2_util_close(tree1, *h1b);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1(RWH) on tree1 => h1
|
|
+ * 2. disconnect tree1
|
|
+ * 3. stat open on tree2 => h2
|
|
+ * 4. reconnect tree1
|
|
+ * 5. durable reconnect L1(RWH) => h1
|
|
+ */
|
|
+static bool test_durable_v2_open_keep_disconnected_rwh_with_stat_open(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1 = GUID_random();
|
|
+ struct smb2_lease ls1;
|
|
+ uint64_t lease_key1;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1 = random();
|
|
+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname,
|
|
+ lease_key1, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1,
|
|
+ 0, 0, ls1.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ smb2_generic_create(&io, NULL, false /* dir */, fname,
|
|
+ FILE_OPEN_IF, 0, 0, 0);
|
|
+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.lease_request_v2 = &ls1;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1,
|
|
+ 0, 0, ls1.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1 = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree2, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1(RWH) on tree1 => h1
|
|
+ * 2. disconnect tree1
|
|
+ * 3. durable open with L2(RWH) on tree2 => h2
|
|
+ * 4. reconnect tree1
|
|
+ * 5. durable reconnect L1(RH) => h1 => not found
|
|
+ */
|
|
+static bool test_durable_v2_open_purge_disconnected_rwh_with_rwh_open(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1 = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls1;
|
|
+ uint64_t lease_key1;
|
|
+ struct smb2_lease ls2;
|
|
+ uint64_t lease_key2;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1 = random();
|
|
+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname,
|
|
+ lease_key1, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1,
|
|
+ 0, 0, ls1.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ lease_key2 = random();
|
|
+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname,
|
|
+ lease_key2, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls2.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.lease_request_v2 = &ls1;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+ ls1.lease_state = smb2_util_lease_state("RH");
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+
|
|
+ status = smb2_util_close(tree2, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1(RWH) on tree1 => h1
|
|
+ * 2. disconnect tree1
|
|
+ * 3. durable open with L2(RH) on tree2 => h2
|
|
+ * 4. reconnect tree1
|
|
+ * 5. durable reconnect L1(RH) => h1 => not found
|
|
+ */
|
|
+static bool test_durable_v2_open_purge_disconnected_rwh_with_rh_open(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1;
|
|
+ struct smb2_handle *h1 = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1 = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls1;
|
|
+ uint64_t lease_key1;
|
|
+ struct smb2_lease ls2;
|
|
+ uint64_t lease_key2;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1 = random();
|
|
+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname,
|
|
+ lease_key1, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RWH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1 = io.out.file.handle;
|
|
+ h1 = &_h1;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1,
|
|
+ 0, 0, ls1.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ lease_key2 = random();
|
|
+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname,
|
|
+ lease_key2, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls2.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1;
|
|
+ io.in.create_guid = create_guid1;
|
|
+ io.in.lease_request_v2 = &ls1;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+ ls1.lease_state = smb2_util_lease_state("RH");
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+
|
|
+ status = smb2_util_close(tree2, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1 != NULL) {
|
|
+ smb2_util_close(tree1, *h1);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1A(RH) on tree1 => h1a
|
|
+ * 2. durable open with L1B(RH) on tree1 => h1b
|
|
+ * 3. disconnect tree1
|
|
+ * 4. open with SHARE_NONE on tree2 => h2
|
|
+ * 5. reconnect tree1
|
|
+ * 6. durable reconnect L1A(RH) => not found
|
|
+ * 7. durable reconnect L1B(RH) => not found
|
|
+ */
|
|
+static bool test_durable_v2_open_purge_disconnected_rh_with_share_none_open(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1a;
|
|
+ struct smb2_handle *h1a = NULL;
|
|
+ struct smb2_handle _h1b;
|
|
+ struct smb2_handle *h1b = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1a = GUID_random();
|
|
+ struct GUID create_guid1b = GUID_random();
|
|
+ struct smb2_lease ls1a;
|
|
+ uint64_t lease_key1a;
|
|
+ struct smb2_lease ls1b;
|
|
+ uint64_t lease_key1b;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options1;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options1 = tree1->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1a = random();
|
|
+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname,
|
|
+ lease_key1a, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1a.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ lease_key1b = random();
|
|
+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname,
|
|
+ lease_key1b, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree1);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ smb2_generic_create_share(&io, &ls1a, false /* dir */, fname,
|
|
+ NTCREATEX_DISP_OPEN_IF,
|
|
+ FILE_SHARE_NONE,
|
|
+ SMB2_OPLOCK_LEVEL_NONE, 0, 0);
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, false);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1a;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.lease_request_v2 = &ls1a;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+ h1a = NULL;
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h1b;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.lease_request_v2 = &ls1b;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+ h1b = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree2, *h2);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h2 = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1a != NULL) {
|
|
+ smb2_util_close(tree1, *h1a);
|
|
+ }
|
|
+ if (tree1 != NULL && h1b != NULL) {
|
|
+ smb2_util_close(tree1, *h1b);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1A(RH) on tree1 => h1a
|
|
+ * 2. durable open with L1B(RH) on tree1 => h1b
|
|
+ * 3. durable open with L2(RH) on tree2 => h2
|
|
+ * 4. disconnect tree2
|
|
+ * 5.1 write to h1a
|
|
+ * 5.2 lease break to NONE for L1B (ack requested, but ignored)
|
|
+ * 6. reconnect tree2
|
|
+ * 7. durable reconnect L2(RH) => h2 => not found
|
|
+ * 8. close h1a
|
|
+ * 9. durable open with L1A(RWH) on tree1 => h1a only RH
|
|
+ */
|
|
+static bool test_durable_v2_open_purge_disconnected_rh_with_write(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[256];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1a;
|
|
+ struct smb2_handle *h1a = NULL;
|
|
+ struct smb2_handle _h1b;
|
|
+ struct smb2_handle *h1b = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1a = GUID_random();
|
|
+ struct GUID create_guid1b = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls1a;
|
|
+ uint64_t lease_key1a;
|
|
+ struct smb2_lease ls1b;
|
|
+ uint64_t lease_key1b;
|
|
+ struct smb2_lease ls2;
|
|
+ uint64_t lease_key2;
|
|
+ struct smb2_write wrt;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options2;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options2 = tree2->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 256, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+
|
|
+ lease_key1a = random();
|
|
+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname,
|
|
+ lease_key1a, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1a.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ lease_key1b = random();
|
|
+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname,
|
|
+ lease_key1b, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ lease_key2 = random();
|
|
+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname,
|
|
+ lease_key2, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls2.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+ lease_break_info.lease_skip_ack = true;
|
|
+
|
|
+ ZERO_STRUCT(wrt);
|
|
+ wrt.in.file.handle = *h1a;
|
|
+ wrt.in.offset = 0;
|
|
+ wrt.in.data = data_blob_string_const("data");
|
|
+ status = smb2_write(tree1, &wrt);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_BREAK_INFO_V2(tree1->session->transport,
|
|
+ "RH", "", lease_key1b, ls1b.lease_epoch);
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options2, &tree2)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h2;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.lease_request_v2 = &ls2;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+ h2 = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1a);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1a = NULL;
|
|
+
|
|
+ /*
|
|
+ * Now there's only lease_key2 with state NONE
|
|
+ *
|
|
+ * And that means an additional open still
|
|
+ * only gets RH...
|
|
+ */
|
|
+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname,
|
|
+ lease_key1a, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RHW"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
|
|
+ CHECK_VAL(io.out.size, wrt.in.data.length);
|
|
+ CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1a.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1a);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1a = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1b);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1b = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1a != NULL) {
|
|
+ smb2_util_close(tree1, *h1a);
|
|
+ }
|
|
+ if (tree1 != NULL && h1b != NULL) {
|
|
+ smb2_util_close(tree1, *h1b);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 1. durable open with L1A(RH) on tree1 => h1a
|
|
+ * 2. durable open with L1B(RH) on tree1 => h1b
|
|
+ * 3. durable open with L2(RH) on tree2 => h2
|
|
+ * 4. disconnect tree2
|
|
+ * 5.1 rename h1a
|
|
+ * 5.2 lease break to R for L1B (ack requested, and required)
|
|
+ * 6. reconnect tree2
|
|
+ * 7. durable reconnect L2(RH) => h2 => not found
|
|
+ */
|
|
+static bool test_durable_v2_open_purge_disconnected_rh_with_rename(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
|
|
+ NTSTATUS status;
|
|
+ char fname[128];
|
|
+ char fname_renamed[140];
|
|
+ struct smb2_handle dh;
|
|
+ struct smb2_handle _h1a;
|
|
+ struct smb2_handle *h1a = NULL;
|
|
+ struct smb2_handle _h1b;
|
|
+ struct smb2_handle *h1b = NULL;
|
|
+ struct smb2_handle _h2;
|
|
+ struct smb2_handle *h2 = NULL;
|
|
+ struct smb2_create io;
|
|
+ struct GUID create_guid1a = GUID_random();
|
|
+ struct GUID create_guid1b = GUID_random();
|
|
+ struct GUID create_guid2 = GUID_random();
|
|
+ struct smb2_lease ls1a;
|
|
+ uint64_t lease_key1a;
|
|
+ struct smb2_lease ls1b;
|
|
+ uint64_t lease_key1b;
|
|
+ struct smb2_lease ls2;
|
|
+ uint64_t lease_key2;
|
|
+ union smb_setfileinfo sinfo;
|
|
+ bool ret = true;
|
|
+ struct smbcli_options options2;
|
|
+ uint32_t caps;
|
|
+
|
|
+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
|
|
+ if (!(caps & SMB2_CAP_LEASING)) {
|
|
+ torture_skip(tctx, "leases are not supported");
|
|
+ }
|
|
+
|
|
+ options2 = tree2->session->transport->options;
|
|
+
|
|
+ tree1->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree1->session->transport->lease.private_data = tree1;
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+
|
|
+ smb2_deltree(tree1, __func__);
|
|
+ status = torture_smb2_testdir(tree1, __func__, &dh);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testdir failed\n");
|
|
+ smb2_util_close(tree1, dh);
|
|
+
|
|
+ /* Choose a random name in case the state is left a little funky. */
|
|
+ snprintf(fname, 128, "%s\\file_%s.dat",
|
|
+ __func__, generate_random_str(tctx, 8));
|
|
+ snprintf(fname_renamed, 140, "%s.renamed", fname);
|
|
+
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_util_unlink(tree1, fname_renamed);
|
|
+
|
|
+ lease_key1a = random();
|
|
+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname,
|
|
+ lease_key1a, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1a;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1a = io.out.file.handle;
|
|
+ h1a = &_h1a;
|
|
+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1a.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a,
|
|
+ 0, 0, ls1a.lease_epoch);
|
|
+
|
|
+ lease_key1b = random();
|
|
+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname,
|
|
+ lease_key1b, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid1b;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree1, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h1b = io.out.file.handle;
|
|
+ h1b = &_h1b;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b,
|
|
+ 0, 0, ls1b.lease_epoch);
|
|
+
|
|
+ lease_key2 = random();
|
|
+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname,
|
|
+ lease_key2, 0, /* parent lease key */
|
|
+ smb2_util_lease_state("RH"), 0 /* lease epoch */);
|
|
+ io.in.durable_open = false;
|
|
+ io.in.durable_open_v2 = true;
|
|
+ io.in.persistent_open = false;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.timeout = UINT32_MAX;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ _h2 = io.out.file.handle;
|
|
+ h2 = &_h2;
|
|
+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
|
|
+ CHECK_VAL(io.out.durable_open, false);
|
|
+ CHECK_VAL(io.out.durable_open_v2, true);
|
|
+ CHECK_VAL(io.out.persistent_open, false);
|
|
+ CHECK_VAL(io.out.timeout, 300*1000);
|
|
+ ls2.lease_epoch += 1;
|
|
+ CHECK_LEASE_V2(&io, "RH", true, lease_key2,
|
|
+ 0, 0, ls2.lease_epoch);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ /* disconnect, reconnect and then do durable reopen */
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ ZERO_STRUCT(sinfo);
|
|
+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
|
|
+ sinfo.rename_information.in.file.handle = *h1a;
|
|
+ sinfo.rename_information.in.overwrite = 0;
|
|
+ sinfo.rename_information.in.root_fid = 0;
|
|
+ sinfo.rename_information.in.new_name = fname_renamed;
|
|
+ status = smb2_setinfo_file(tree1, &sinfo);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+
|
|
+ ls1b.lease_epoch += 1;
|
|
+ CHECK_BREAK_INFO_V2(tree1->session->transport,
|
|
+ "RH", "R", lease_key1b, ls1b.lease_epoch);
|
|
+ torture_reset_lease_break_info(tctx, &lease_break_info);
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+ if (!torture_smb2_connection_ext(tctx, 0, &options2, &tree2)) {
|
|
+ torture_warning(tctx, "couldn't reconnect, bailing\n");
|
|
+ ret = false;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ tree2->session->transport->lease.handler = torture_lease_handler;
|
|
+ tree2->session->transport->lease.private_data = tree2;
|
|
+
|
|
+ ZERO_STRUCT(io);
|
|
+ io.in.fname = fname;
|
|
+ io.in.durable_open_v2 = false;
|
|
+ io.in.durable_handle_v2 = h2;
|
|
+ io.in.create_guid = create_guid2;
|
|
+ io.in.lease_request_v2 = &ls2;
|
|
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
|
|
+
|
|
+ status = smb2_create(tree2, mem_ctx, &io);
|
|
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
|
+ h2 = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1a);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1a = NULL;
|
|
+
|
|
+ status = smb2_util_close(tree1, *h1b);
|
|
+ CHECK_STATUS(status, NT_STATUS_OK);
|
|
+ h1b = NULL;
|
|
+
|
|
+ CHECK_NO_BREAK(tctx);
|
|
+
|
|
+done:
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_keepalive(tree1->session->transport);
|
|
+ }
|
|
+ if (tree2 != NULL) {
|
|
+ smb2_keepalive(tree2->session->transport);
|
|
+ }
|
|
+ if (tree1 != NULL && h1a != NULL) {
|
|
+ smb2_util_close(tree1, *h1a);
|
|
+ }
|
|
+ if (tree1 != NULL && h1b != NULL) {
|
|
+ smb2_util_close(tree1, *h1b);
|
|
+ }
|
|
+ if (tree2 != NULL && h2 != NULL) {
|
|
+ smb2_util_close(tree2, *h2);
|
|
+ }
|
|
+
|
|
+ if (tree1 != NULL) {
|
|
+ smb2_util_unlink(tree1, fname);
|
|
+ smb2_util_unlink(tree1, fname_renamed);
|
|
+ smb2_deltree(tree1, __func__);
|
|
+
|
|
+ TALLOC_FREE(tree1);
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(tree2);
|
|
+
|
|
+ talloc_free(mem_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/**
|
|
* Test durable request / reconnect with AppInstanceId
|
|
*/
|
|
@@ -3277,6 +5119,15 @@ struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx)
|
|
torture_suite_add_1smb2_test(suite, "statRH-and-lease", test_durable_v2_open_statRH_and_lease);
|
|
torture_suite_add_1smb2_test(suite, "two-same-lease", test_durable_v2_open_two_same_lease);
|
|
torture_suite_add_1smb2_test(suite, "two-different-lease", test_durable_v2_open_two_different_leases);
|
|
+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rh-with-stat-open", test_durable_v2_open_keep_disconnected_rh_with_stat_open);
|
|
+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rh-with-rh-open", test_durable_v2_open_keep_disconnected_rh_with_rh_open);
|
|
+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rh-with-rwh-open", test_durable_v2_open_keep_disconnected_rh_with_rwh_open);
|
|
+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rwh-with-stat-open", test_durable_v2_open_keep_disconnected_rwh_with_stat_open);
|
|
+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rwh-with-rwh-open", test_durable_v2_open_purge_disconnected_rwh_with_rwh_open);
|
|
+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rwh-with-rh-open", test_durable_v2_open_purge_disconnected_rwh_with_rh_open);
|
|
+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rh-with-share-none-open", test_durable_v2_open_purge_disconnected_rh_with_share_none_open);
|
|
+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rh-with-write", test_durable_v2_open_purge_disconnected_rh_with_write);
|
|
+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rh-with-rename", test_durable_v2_open_purge_disconnected_rh_with_rename);
|
|
torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
|
|
torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
|
|
torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 9b8a74aac964841fce8d032487678513a438274a Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 29 Aug 2024 20:20:23 +0200
|
|
Subject: [PATCH 078/122] s3:smbd: let durable_reconnect_fn already check for a
|
|
disconnected handle with the correct file_id
|
|
|
|
We'll soon allow more than one disconnected durable handle, so
|
|
we need to find the correct one instead of assuming only a single
|
|
one.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit 2869bd1a507e7376f0bb0ec68ed4e045b043cfdb)
|
|
---
|
|
source3/smbd/durable.c | 29 +++++++++++++++++++++--------
|
|
1 file changed, 21 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
|
|
index b7fa53e7555..bf3cbed0a2c 100644
|
|
--- a/source3/smbd/durable.c
|
|
+++ b/source3/smbd/durable.c
|
|
@@ -506,19 +506,33 @@ static bool vfs_default_durable_reconnect_check_stat(
|
|
return true;
|
|
}
|
|
|
|
+struct durable_reconnect_state {
|
|
+ struct smbXsrv_open *op;
|
|
+ struct share_mode_entry *e;
|
|
+};
|
|
+
|
|
static bool durable_reconnect_fn(
|
|
struct share_mode_entry *e,
|
|
bool *modified,
|
|
void *private_data)
|
|
{
|
|
- struct share_mode_entry *dst_e = private_data;
|
|
+ struct durable_reconnect_state *state = private_data;
|
|
+ uint64_t id = state->op->global->open_persistent_id;
|
|
+
|
|
+ if (e->share_file_id != id) {
|
|
+ return false; /* Look at potential other entries */
|
|
+ }
|
|
|
|
- if (dst_e->pid.pid != 0) {
|
|
+ if (!server_id_is_disconnected(&e->pid)) {
|
|
+ return false; /* Look at potential other entries */
|
|
+ }
|
|
+
|
|
+ if (state->e->share_file_id == id) {
|
|
DBG_INFO("Found more than one entry, invalidating previous\n");
|
|
- dst_e->pid.pid = 0;
|
|
+ *state->e = (struct share_mode_entry) { .pid = { .pid = 0, }};
|
|
return true; /* end the loop through share mode entries */
|
|
}
|
|
- *dst_e = *e;
|
|
+ *state->e = *e;
|
|
return false; /* Look at potential other entries */
|
|
}
|
|
|
|
@@ -533,7 +547,8 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
struct share_mode_lock *lck;
|
|
- struct share_mode_entry e;
|
|
+ struct share_mode_entry e = { .pid = { .pid = 0, }};
|
|
+ struct durable_reconnect_state rstate = { .op = op, .e = &e, };
|
|
struct files_struct *fsp = NULL;
|
|
NTSTATUS status;
|
|
bool ok;
|
|
@@ -626,9 +641,7 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
- e = (struct share_mode_entry) { .pid.pid = 0 };
|
|
-
|
|
- ok = share_mode_forall_entries(lck, durable_reconnect_fn, &e);
|
|
+ ok = share_mode_forall_entries(lck, durable_reconnect_fn, &rstate);
|
|
if (!ok) {
|
|
DBG_WARNING("share_mode_forall_entries failed\n");
|
|
status = NT_STATUS_INTERNAL_DB_ERROR;
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 2aa438545f3d31dfb2be6c054e3dcbd2c4707caa Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 29 Aug 2024 18:43:14 +0200
|
|
Subject: [PATCH 079/122] s3:smbd: allow reset_share_mode_entry() to handle
|
|
more than one durable handle
|
|
|
|
This means that multiple durable handles with RH leases can
|
|
co-exist now... Before only the last remaining durable handle
|
|
was able to pass the SMB_VFS_DURABLE_DISCONNECT() step.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit b1e5f5d8d2852b66ca4c858d14d367ffe228a88d)
|
|
---
|
|
.../knownfail.d/smb2.durable-v2-open.bug15651 | 5 -
|
|
source3/locking/share_mode_lock.c | 315 ++++++++++++++++--
|
|
2 files changed, 293 insertions(+), 27 deletions(-)
|
|
delete mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
|
|
diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 b/selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
deleted file mode 100644
|
|
index 1702a3a6580..00000000000
|
|
--- a/selftest/knownfail.d/smb2.durable-v2-open.bug15651
|
|
+++ /dev/null
|
|
@@ -1,5 +0,0 @@
|
|
-^samba3.smb2.durable-v2-open.statRH-and-lease
|
|
-^samba3.smb2.durable-v2-open.two-same-lease
|
|
-^samba3.smb2.durable-v2-open.two-different-lease
|
|
-^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-stat-open
|
|
-^samba3.smb2.durable-v2-open.keep-disconnected-rwh-with-stat-open
|
|
diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
|
|
index 3fc7d56562a..4bbccdcd3bd 100644
|
|
--- a/source3/locking/share_mode_lock.c
|
|
+++ b/source3/locking/share_mode_lock.c
|
|
@@ -2703,16 +2703,25 @@ bool reset_share_mode_entry(
|
|
struct share_mode_data *d = NULL;
|
|
TDB_DATA key = locking_key(&id);
|
|
struct locking_tdb_data *ltdb = NULL;
|
|
- struct share_mode_entry e;
|
|
+ struct share_mode_entry e = { .pid.pid = 0 };
|
|
struct share_mode_entry_buf e_buf;
|
|
+ size_t old_idx;
|
|
+ size_t new_idx;
|
|
+ bool found;
|
|
NTSTATUS status;
|
|
- int cmp;
|
|
bool ret = false;
|
|
bool ok;
|
|
+ struct file_id_buf id_buf;
|
|
+ struct server_id_buf pid_buf1;
|
|
+ struct server_id_buf pid_buf2;
|
|
+ size_t low_idx1, low_idx2, low_num;
|
|
+ size_t mid_idx1, mid_idx2, mid_num;
|
|
+ size_t high_idx1, high_idx2, high_num;
|
|
+ TDB_DATA dbufs[4];
|
|
+ size_t num_dbufs = 0;
|
|
|
|
status = share_mode_lock_access_private_data(lck, &d);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- struct file_id_buf id_buf;
|
|
/* Any error recovery possible here ? */
|
|
DBG_ERR("share_mode_lock_access_private_data() failed for "
|
|
"%s - %s\n",
|
|
@@ -2728,29 +2737,54 @@ bool reset_share_mode_entry(
|
|
return false;
|
|
}
|
|
|
|
- if (ltdb->num_share_entries != 1) {
|
|
- DBG_DEBUG("num_share_modes=%zu\n", ltdb->num_share_entries);
|
|
- goto done;
|
|
- }
|
|
+ DBG_DEBUG("%s - num_share_modes=%zu\n",
|
|
+ file_id_str_buf(id, &id_buf),
|
|
+ ltdb->num_share_entries);
|
|
|
|
- ok = share_mode_entry_get(ltdb->share_entries, &e);
|
|
- if (!ok) {
|
|
- DBG_WARNING("share_mode_entry_get failed\n");
|
|
+ new_idx = share_mode_entry_find(
|
|
+ ltdb->share_entries,
|
|
+ ltdb->num_share_entries,
|
|
+ new_pid,
|
|
+ new_share_file_id,
|
|
+ &e,
|
|
+ &found);
|
|
+ if (found) {
|
|
+ DBG_ERR("%s - num_share_modes=%zu "
|
|
+ "found NEW[%s][%"PRIu64"]\n",
|
|
+ file_id_str_buf(id, &id_buf),
|
|
+ ltdb->num_share_entries,
|
|
+ server_id_str_buf(new_pid, &pid_buf2),
|
|
+ new_share_file_id);
|
|
goto done;
|
|
}
|
|
|
|
- cmp = share_mode_entry_cmp(
|
|
- old_pid, old_share_file_id, e.pid, e.share_file_id);
|
|
- if (cmp != 0) {
|
|
- struct server_id_buf tmp1, tmp2;
|
|
- DBG_WARNING("Expected pid=%s, file_id=%"PRIu64", "
|
|
- "got pid=%s, file_id=%"PRIu64"\n",
|
|
- server_id_str_buf(old_pid, &tmp1),
|
|
- old_share_file_id,
|
|
- server_id_str_buf(e.pid, &tmp2),
|
|
- e.share_file_id);
|
|
+ old_idx = share_mode_entry_find(
|
|
+ ltdb->share_entries,
|
|
+ ltdb->num_share_entries,
|
|
+ old_pid,
|
|
+ old_share_file_id,
|
|
+ &e,
|
|
+ &found);
|
|
+ if (!found) {
|
|
+ DBG_WARNING("%s - num_share_modes=%zu "
|
|
+ "OLD[%s][%"PRIu64"] not found\n",
|
|
+ file_id_str_buf(id, &id_buf),
|
|
+ ltdb->num_share_entries,
|
|
+ server_id_str_buf(old_pid, &pid_buf1),
|
|
+ old_share_file_id);
|
|
goto done;
|
|
}
|
|
+ DBG_DEBUG("%s - num_share_modes=%zu "
|
|
+ "OLD[%s][%"PRIu64"] => idx=%zu "
|
|
+ "NEW[%s][%"PRIu64"] => idx=%zu\n",
|
|
+ file_id_str_buf(id, &id_buf),
|
|
+ ltdb->num_share_entries,
|
|
+ server_id_str_buf(old_pid, &pid_buf1),
|
|
+ old_share_file_id,
|
|
+ old_idx,
|
|
+ server_id_str_buf(new_pid, &pid_buf2),
|
|
+ new_share_file_id,
|
|
+ new_idx);
|
|
|
|
e.pid = new_pid;
|
|
if (new_mid != UINT64_MAX) {
|
|
@@ -2764,11 +2798,248 @@ bool reset_share_mode_entry(
|
|
goto done;
|
|
}
|
|
|
|
- ltdb->share_entries = e_buf.buf;
|
|
+ /*
|
|
+ * The logic to remove the existing
|
|
+ * entry and add the new one at the
|
|
+ * same time is a bit complex because
|
|
+ * we need to keep the entries sorted.
|
|
+ *
|
|
+ * The following examples should catch
|
|
+ * the corner cases and show that
|
|
+ * the {low,mid,high}_{idx1,num} are
|
|
+ * correctly calculated and the new
|
|
+ * entry is put before or after the mid
|
|
+ * elements...
|
|
+ *
|
|
+ * 1.
|
|
+ * 0
|
|
+ * 1
|
|
+ * 2 <- old_idx
|
|
+ * new_idx -> 3
|
|
+ * 3
|
|
+ * 4
|
|
+ *
|
|
+ * low_idx1 = 0;
|
|
+ * low_idx2 = MIN(old_idx, new_idx); => 2
|
|
+ * low_num = low_idx2 - low_idx1; => 2
|
|
+ *
|
|
+ * if (new < old) => new; => no
|
|
+ *
|
|
+ * mid_idx1 = MIN(old_idx+1, new_idx); => 3
|
|
+ * mid_idx2 = MAX(old_idx, new_idx); => 3
|
|
+ * mid_num = mid_idx2 - mid_idx1; => 0
|
|
+ *
|
|
+ * if (new >= old) => new; => yes
|
|
+ *
|
|
+ * high_idx1 = MAX(old_idx+1, new_idx); => 3
|
|
+ * high_idx2 = num_share_entries; => 5
|
|
+ * high_num = high_idx2 - high_idx1 = 2
|
|
+ *
|
|
+ * 2.
|
|
+ * 0
|
|
+ * 1
|
|
+ * new_idx -> 2
|
|
+ * 2 <- old_idx
|
|
+ * 3
|
|
+ * 4
|
|
+ *
|
|
+ * low_idx1 = 0;
|
|
+ * low_idx2 = MIN(old_idx, new_idx); => 2
|
|
+ * low_num = low_idx2 - low_idx1; => 2
|
|
+ *
|
|
+ * if (new < old) => new; => no
|
|
+ *
|
|
+ * mid_idx1 = MIN(old_idx+1, new_idx); => 2
|
|
+ * mid_idx2 = MAX(old_idx, new_idx); => 2
|
|
+ * mid_num = mid_idx2 - mid_idx1; => 0
|
|
+ *
|
|
+ * if (new >= old) => new; => yes
|
|
+ *
|
|
+ * high_idx1 = MAX(old_idx+1, new_idx); => 3
|
|
+ * high_idx2 = num_share_entries; => 5
|
|
+ * high_num = high_idx2 - high_idx1 = 2
|
|
+ *
|
|
+ * 3.
|
|
+ * 0
|
|
+ * 1 <- old_idx
|
|
+ * 2
|
|
+ * new_idx -> 3
|
|
+ * 3
|
|
+ * 4
|
|
+ *
|
|
+ * low_idx1 = 0;
|
|
+ * low_idx2 = MIN(old_idx, new_idx); => 1
|
|
+ * low_num = low_idx2 - low_idx1; => 1
|
|
+ *
|
|
+ * if (new < old) => new; => no
|
|
+ *
|
|
+ * mid_idx1 = MIN(old_idx+1, new_idx); => 2
|
|
+ * mid_idx2 = MAX(old_idx, new_idx); => 3
|
|
+ * mid_num = mid_idx2 - mid_idx1; => 1
|
|
+ *
|
|
+ * if (new >= old) => new; => yes
|
|
+ *
|
|
+ * high_idx1 = MAX(old_idx+1, new_idx); => 3
|
|
+ * high_idx2 = num_share_entries; => 5
|
|
+ * high_num = high_idx2 - high_idx1 = 2
|
|
+ *
|
|
+ * 4.
|
|
+ * 0
|
|
+ * new_idx -> 1
|
|
+ * 1
|
|
+ * 2
|
|
+ * 3 <- old_idx
|
|
+ * 4
|
|
+ *
|
|
+ * low_idx1 = 0;
|
|
+ * low_idx2 = MIN(old_idx, new_idx); => 1
|
|
+ * low_num = low_idx2 - low_idx1; => 1
|
|
+ *
|
|
+ * if (new < old) => new; => yes
|
|
+ *
|
|
+ * mid_idx1 = MIN(old_idx+1, new_idx); => 1
|
|
+ * mid_idx2 = MAX(old_idx, new_idx); => 3
|
|
+ * mid_num = mid_idx2 - mid_idx1; => 2
|
|
+ *
|
|
+ * if (new >= old) => new; => no
|
|
+ *
|
|
+ * high_idx1 = MAX(old_idx+1, new_idx); => 4
|
|
+ * high_idx2 = num_share_entries; => 5
|
|
+ * high_num = high_idx2 - high_idx1 = 1
|
|
+ *
|
|
+ * 5.
|
|
+ * new_idx -> 0
|
|
+ * 0
|
|
+ * 1
|
|
+ * 2
|
|
+ * 3
|
|
+ * 4 <- old_idx
|
|
+ *
|
|
+ * low_idx1 = 0;
|
|
+ * low_idx2 = MIN(old_idx, new_idx); => 0
|
|
+ * low_num = low_idx2 - low_idx1; => 0
|
|
+ *
|
|
+ * if (new < old) => new; => yes
|
|
+ *
|
|
+ * mid_idx1 = MIN(old_idx+1, new_idx); => 0
|
|
+ * mid_idx2 = MAX(old_idx, new_idx); => 4
|
|
+ * mid_num = mid_idx2 - mid_idx1; => 4
|
|
+ *
|
|
+ * if (new >= old) => new; => no
|
|
+ *
|
|
+ * high_idx1 = MAX(old_idx+1, new_idx); => 5
|
|
+ * high_idx2 = num_share_entries; => 5
|
|
+ * high_num = high_idx2 - high_idx1 = 0
|
|
+ *
|
|
+ * 6.
|
|
+ * new_idx -> 0
|
|
+ * 0 <- old_idx
|
|
+ *
|
|
+ * low_idx1 = 0;
|
|
+ * low_idx2 = MIN(old_idx, new_idx); => 0
|
|
+ * low_num = low_idx2 - low_idx1; => 0
|
|
+ *
|
|
+ * if (new < old) => new; => no
|
|
+ *
|
|
+ * mid_idx1 = MIN(old_idx+1, new_idx); => 0
|
|
+ * mid_idx2 = MAX(old_idx, new_idx); => 0
|
|
+ * mid_num = mid_idx2 - mid_idx1; => 0
|
|
+ *
|
|
+ * if (new >= old) => new; => yes
|
|
+ *
|
|
+ * high_idx1 = MAX(old_idx+1, new_idx); => 1
|
|
+ * high_idx2 = num_share_entries; => 1
|
|
+ * high_num = high_idx2 - high_idx1 = 0
|
|
+ *
|
|
+ * 7.
|
|
+ * 0 <- old_idx
|
|
+ * new_idx -> 1
|
|
+ *
|
|
+ * low_idx1 = 0;
|
|
+ * low_idx2 = MIN(old_idx, new_idx); => 0
|
|
+ * low_num = low_idx2 - low_idx1; => 0
|
|
+ *
|
|
+ * if (new < old) => new; => no
|
|
+ *
|
|
+ * mid_idx1 = MIN(old_idx+1, new_idx); => 1
|
|
+ * mid_idx2 = MAX(old_idx, new_idx); => 1
|
|
+ * mid_num = mid_idx2 - mid_idx1; => 0
|
|
+ *
|
|
+ * if (new >= old) => new; => yes
|
|
+ *
|
|
+ * high_idx1 = MAX(old_idx+1, new_idx); => 1
|
|
+ * high_idx2 = num_share_entries; => 1
|
|
+ * high_num = high_idx2 - high_idx1 = 0
|
|
+ */
|
|
+ low_idx1 = 0;
|
|
+ low_idx2 = MIN(old_idx, new_idx);
|
|
+ low_num = low_idx2 - low_idx1;
|
|
+ mid_idx1 = MIN(old_idx+1, new_idx);
|
|
+ mid_idx2 = MAX(old_idx, new_idx);
|
|
+ mid_num = mid_idx2 - mid_idx1;
|
|
+ high_idx1 = MAX(old_idx+1, new_idx);
|
|
+ high_idx2 = ltdb->num_share_entries;
|
|
+ high_num = high_idx2 - high_idx1;
|
|
+
|
|
+ if (low_num != 0) {
|
|
+ dbufs[num_dbufs] = (TDB_DATA) {
|
|
+ .dptr = discard_const_p(uint8_t, ltdb->share_entries) +
|
|
+ low_idx1 * SHARE_MODE_ENTRY_SIZE,
|
|
+ .dsize = low_num * SHARE_MODE_ENTRY_SIZE,
|
|
+ };
|
|
+ num_dbufs += 1;
|
|
+ }
|
|
+
|
|
+ if (new_idx < old_idx) {
|
|
+ dbufs[num_dbufs] = (TDB_DATA) {
|
|
+ .dptr = e_buf.buf, .dsize = SHARE_MODE_ENTRY_SIZE,
|
|
+ };
|
|
+ num_dbufs += 1;
|
|
+ }
|
|
+
|
|
+ if (mid_num != 0) {
|
|
+ dbufs[num_dbufs] = (TDB_DATA) {
|
|
+ .dptr = discard_const_p(uint8_t, ltdb->share_entries) +
|
|
+ mid_idx1 * SHARE_MODE_ENTRY_SIZE,
|
|
+ .dsize = mid_num * SHARE_MODE_ENTRY_SIZE,
|
|
+ };
|
|
+ num_dbufs += 1;
|
|
+ }
|
|
+
|
|
+ if (new_idx >= old_idx) {
|
|
+ dbufs[num_dbufs] = (TDB_DATA) {
|
|
+ .dptr = e_buf.buf, .dsize = SHARE_MODE_ENTRY_SIZE,
|
|
+ };
|
|
+ num_dbufs += 1;
|
|
+ }
|
|
+
|
|
+ if (high_num != 0) {
|
|
+ dbufs[num_dbufs] = (TDB_DATA) {
|
|
+ .dptr = discard_const_p(uint8_t, ltdb->share_entries) +
|
|
+ high_idx1 * SHARE_MODE_ENTRY_SIZE,
|
|
+ .dsize = high_num * SHARE_MODE_ENTRY_SIZE,
|
|
+ };
|
|
+ num_dbufs += 1;
|
|
+ }
|
|
|
|
+ {
|
|
+ size_t i;
|
|
+ for (i=0; i<num_dbufs; i++) {
|
|
+ DBG_DEBUG("dbufs[%zu]=(%p, %zu)\n",
|
|
+ i,
|
|
+ dbufs[i].dptr,
|
|
+ dbufs[i].dsize);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * We completely rewrite the entries...
|
|
+ */
|
|
+ ltdb->share_entries = NULL;
|
|
+ ltdb->num_share_entries = 0;
|
|
d->modified = true;
|
|
|
|
- status = share_mode_data_ltdb_store(d, key, ltdb, NULL, 0);
|
|
+ status = share_mode_data_ltdb_store(d, key, ltdb, dbufs, num_dbufs);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_ERR("share_mode_data_ltdb_store failed: %s\n",
|
|
nt_errstr(status));
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 2eca89f51181b452cc86277f45db234e8b00db74 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 30 Aug 2024 14:16:12 +0200
|
|
Subject: [PATCH 080/122] s3:smbd: avoid false positives for got_oplock and
|
|
have_other_lease in delay_for_oplock_fn
|
|
|
|
stat opens should not cause a oplock/lease downgrade if
|
|
they don't have a lease attached to itself.
|
|
|
|
Note that opens broken to NONE still count if they are
|
|
non-stat opens...
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
|
|
Autobuild-User(master): Stefan Metzmacher <metze@samba.org>
|
|
Autobuild-Date(master): Thu Oct 10 13:59:18 UTC 2024 on atb-devel-224
|
|
|
|
(cherry picked from commit dd5b9e08c7a98c54b62d3b097c75faa09cd17da7)
|
|
|
|
Autobuild-User(v4-21-test): Jule Anger <janger@samba.org>
|
|
Autobuild-Date(v4-21-test): Mon Oct 14 11:09:14 UTC 2024 on atb-devel-224
|
|
---
|
|
selftest/knownfail | 1 -
|
|
.../knownfail.d/smb2.durable-v2-open.bug15649 | 2 --
|
|
source3/smbd/open.c | 26 ++++++++++++++-----
|
|
3 files changed, 20 insertions(+), 9 deletions(-)
|
|
delete mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15649
|
|
|
|
diff --git a/selftest/knownfail b/selftest/knownfail
|
|
index 4e34effbbd1..2da5cbd6dfd 100644
|
|
--- a/selftest/knownfail
|
|
+++ b/selftest/knownfail
|
|
@@ -219,7 +219,6 @@
|
|
^samba3.smb2.compound.interim2 # wrong return code (STATUS_CANCELLED)
|
|
^samba3.smb2.compound.aio.interim2 # wrong return code (STATUS_CANCELLED)
|
|
^samba3.smb2.lock.*replay_broken_windows # This tests the windows behaviour
|
|
-^samba3.smb2.lease.statopen3
|
|
^samba3.smb2.lease.unlink # we currently do not downgrade RH lease to R after unlink
|
|
^samba4.smb2.ioctl.compress_notsup.*\(ad_dc_ntvfs\)
|
|
^samba3.raw.session.*reauth2 # maybe fix this?
|
|
diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15649 b/selftest/knownfail.d/smb2.durable-v2-open.bug15649
|
|
deleted file mode 100644
|
|
index 748b6c3150e..00000000000
|
|
--- a/selftest/knownfail.d/smb2.durable-v2-open.bug15649
|
|
+++ /dev/null
|
|
@@ -1,2 +0,0 @@
|
|
-^samba3.smb2.durable-v2-open.stat-and-lease
|
|
-^samba3.smb2.durable-v2-open.nonstat-and-lease
|
|
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
|
|
index 4cc5190f690..2ccccb9eb75 100644
|
|
--- a/source3/smbd/open.c
|
|
+++ b/source3/smbd/open.c
|
|
@@ -2485,7 +2485,7 @@ struct delay_for_oplock_state {
|
|
bool first_open_attempt;
|
|
bool got_handle_lease;
|
|
bool got_oplock;
|
|
- bool have_other_lease;
|
|
+ bool disallow_write_lease;
|
|
uint32_t total_lease_types;
|
|
bool delay;
|
|
struct blocker_debug_state *blocker_debug_state;
|
|
@@ -2593,15 +2593,27 @@ static bool delay_for_oplock_fn(
|
|
}
|
|
|
|
if (!state->got_oplock &&
|
|
+ (e->op_type != NO_OPLOCK) &&
|
|
(e->op_type != LEASE_OPLOCK) &&
|
|
!share_entry_stale_pid(e)) {
|
|
state->got_oplock = true;
|
|
}
|
|
|
|
- if (!state->have_other_lease &&
|
|
+ /*
|
|
+ * Two things prevent a write lease
|
|
+ * to be granted:
|
|
+ *
|
|
+ * 1. Any oplock or lease (even broken to NONE)
|
|
+ * 2. An open with an access mask other than
|
|
+ * FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES
|
|
+ * or SYNCHRONIZE_ACCESS
|
|
+ */
|
|
+ if (!state->disallow_write_lease &&
|
|
+ (e->op_type != NO_OPLOCK || !is_oplock_stat_open(e->access_mask)) &&
|
|
!is_same_lease(fsp, e, lease) &&
|
|
- !share_entry_stale_pid(e)) {
|
|
- state->have_other_lease = true;
|
|
+ !share_entry_stale_pid(e))
|
|
+ {
|
|
+ state->disallow_write_lease = true;
|
|
}
|
|
|
|
if (e_is_lease && is_lease_stat_open(fsp->access_mask)) {
|
|
@@ -2835,9 +2847,11 @@ grant:
|
|
granted &= ~SMB2_LEASE_READ;
|
|
}
|
|
|
|
- if (state.have_other_lease) {
|
|
+ if (state.disallow_write_lease) {
|
|
/*
|
|
- * Can grant only one writer
|
|
+ * Can grant only a write lease
|
|
+ * if there are no other leases
|
|
+ * and no other non-stat opens.
|
|
*/
|
|
granted &= ~SMB2_LEASE_WRITE;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 11fc816defc6e5d381c8ce8834dbf03fac4a2378 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 9 Jan 2025 08:57:17 +0100
|
|
Subject: [PATCH 081/122] dbwrap: check for option "tdb_hash_size:DBNAME.tdb"
|
|
in db_open()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 7eb135c42d530a16e80e165d9e8e99d920797f12)
|
|
---
|
|
source3/lib/dbwrap/dbwrap_open.c | 5 +++++
|
|
1 file changed, 5 insertions(+)
|
|
|
|
diff --git a/source3/lib/dbwrap/dbwrap_open.c b/source3/lib/dbwrap/dbwrap_open.c
|
|
index 52c8a94aeff..91556f22819 100644
|
|
--- a/source3/lib/dbwrap/dbwrap_open.c
|
|
+++ b/source3/lib/dbwrap/dbwrap_open.c
|
|
@@ -80,6 +80,11 @@ struct db_context *db_open(TALLOC_CTX *mem_ctx,
|
|
base = name;
|
|
}
|
|
|
|
+ hash_size = lp_parm_int(GLOBAL_SECTION_SNUM,
|
|
+ "tdb_hash_size",
|
|
+ base,
|
|
+ hash_size);
|
|
+
|
|
if (tdb_flags & TDB_CLEAR_IF_FIRST) {
|
|
bool try_readonly = false;
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 8d960286aa04b28b2e97fe7bfddb487c3694bef6 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 9 Jan 2025 12:27:43 +0100
|
|
Subject: [PATCH 082/122] smbtorture: add test "open-brlock-deadlock"
|
|
|
|
smbtorture reproducer for bug 15767. As it needs a very specific setup that
|
|
can't easily be done in selftest, the test is only executed when manually called
|
|
with
|
|
|
|
--option=torture:open_brlock_deadlock_timemout=SEC
|
|
|
|
To prepare the setup for the test set:
|
|
|
|
tdb_hash_size:locking.tdb = 1
|
|
tdb_hash_size:brlock.tdb = 1
|
|
|
|
and remove both tdb from disk which is needed so the TDBs get recreated with the
|
|
new hash_size.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 7c60498cee7dca5770d4d1f623c472d585ae9cae)
|
|
---
|
|
source4/torture/smb2/lock.c | 283 ++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 283 insertions(+)
|
|
|
|
diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c
|
|
index eac0d557fc3..e5cf61a471a 100644
|
|
--- a/source4/torture/smb2/lock.c
|
|
+++ b/source4/torture/smb2/lock.c
|
|
@@ -3465,6 +3465,288 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+struct open_brlock_deadlock_state {
|
|
+ bool stop;
|
|
+ bool ok;
|
|
+ struct torture_context *tctx;
|
|
+ struct smb2_tree *tree1;
|
|
+ struct smb2_tree *tree2;
|
|
+ struct smb2_create cr;
|
|
+ struct smb2_request *cr_req;
|
|
+ struct smb2_close cl;
|
|
+ struct smb2_request *cl_req;
|
|
+
|
|
+ struct smb2_lock_element el;
|
|
+ struct smb2_lock lock;
|
|
+ struct smb2_request *lock_req;
|
|
+};
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_opened(struct smb2_request *req);
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_open(
|
|
+ struct open_brlock_deadlock_state *state)
|
|
+{
|
|
+ if (state->stop) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ state->cr_req = smb2_create_send(state->tree1, &state->cr);
|
|
+ torture_assert_goto(state->tctx, state->cr_req != NULL, state->ok, failed,
|
|
+ "smb2_create_send failed\n");
|
|
+
|
|
+ state->cr_req->async.fn = test_open_brlock_deadlock_loop_opened;
|
|
+ state->cr_req->async.private_data = state;
|
|
+ return;
|
|
+failed:
|
|
+ state->stop = true;
|
|
+}
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_close(
|
|
+ struct open_brlock_deadlock_state *state);
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_opened(struct smb2_request *req)
|
|
+{
|
|
+ struct open_brlock_deadlock_state *state = req->async.private_data;
|
|
+ TALLOC_CTX *frame = talloc_stackframe();
|
|
+ NTSTATUS status;
|
|
+
|
|
+ status = smb2_create_recv(req, frame, &state->cr);
|
|
+ torture_assert_ntstatus_ok_goto(state->tctx, status,
|
|
+ state->ok, failed, __location__);
|
|
+ state->cr_req = NULL;
|
|
+
|
|
+ TALLOC_FREE(frame);
|
|
+ test_open_brlock_deadlock_loop_close(state);
|
|
+ return;
|
|
+
|
|
+failed:
|
|
+ state->stop = true;
|
|
+ TALLOC_FREE(frame);
|
|
+}
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_closed(struct smb2_request *req);
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_close(
|
|
+ struct open_brlock_deadlock_state *state)
|
|
+{
|
|
+ if (state->stop) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ state->cl.in.file = state->cr.out.file;
|
|
+ state->cl_req = smb2_close_send(state->tree1, &state->cl);
|
|
+ torture_assert_goto(state->tctx, state->cl_req != NULL, state->ok, failed,
|
|
+ "smb2_create_send failed\n");
|
|
+
|
|
+ state->cl_req->async.fn = test_open_brlock_deadlock_loop_closed;
|
|
+ state->cl_req->async.private_data = state;
|
|
+ return;
|
|
+failed:
|
|
+ state->stop = true;
|
|
+}
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_closed(struct smb2_request *req)
|
|
+{
|
|
+ struct open_brlock_deadlock_state *state = req->async.private_data;
|
|
+ TALLOC_CTX *frame = talloc_stackframe();
|
|
+ NTSTATUS status;
|
|
+
|
|
+ status = smb2_close_recv(req, &state->cl);
|
|
+ torture_assert_ntstatus_ok_goto(state->tctx, status,
|
|
+ state->ok, failed, __location__);
|
|
+ state->cl_req = NULL;
|
|
+
|
|
+ TALLOC_FREE(frame);
|
|
+ test_open_brlock_deadlock_loop_open(state);
|
|
+ return;
|
|
+
|
|
+failed:
|
|
+ state->stop = true;
|
|
+ TALLOC_FREE(frame);
|
|
+}
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_locked(struct smb2_request *req);
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_lock(
|
|
+ struct open_brlock_deadlock_state *state)
|
|
+{
|
|
+ if (state->stop) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ state->el.flags = SMB2_LOCK_FLAG_EXCLUSIVE;
|
|
+
|
|
+ state->lock_req = smb2_lock_send(state->tree2, &state->lock);
|
|
+ torture_assert_goto(state->tctx, state->lock_req != NULL,
|
|
+ state->ok, failed,
|
|
+ "smb2_create_send failed\n");
|
|
+
|
|
+ state->lock_req->async.fn = test_open_brlock_deadlock_loop_locked;
|
|
+ state->lock_req->async.private_data = state;
|
|
+ return;
|
|
+failed:
|
|
+ state->stop = true;
|
|
+}
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_unlock(
|
|
+ struct open_brlock_deadlock_state *state);
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_locked(struct smb2_request *req)
|
|
+{
|
|
+ struct open_brlock_deadlock_state *state = req->async.private_data;
|
|
+ TALLOC_CTX *frame = talloc_stackframe();
|
|
+ NTSTATUS status;
|
|
+
|
|
+ status = smb2_lock_recv(req, &state->lock);
|
|
+ torture_assert_ntstatus_ok_goto(state->tctx, status,
|
|
+ state->ok, failed, __location__);
|
|
+ state->lock_req = NULL;
|
|
+
|
|
+ TALLOC_FREE(frame);
|
|
+ test_open_brlock_deadlock_loop_unlock(state);
|
|
+ return;
|
|
+
|
|
+failed:
|
|
+ state->stop = true;
|
|
+ TALLOC_FREE(frame);
|
|
+}
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_unlocked(struct smb2_request *req);
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_unlock(
|
|
+ struct open_brlock_deadlock_state *state)
|
|
+{
|
|
+ if (state->stop) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ state->el.flags = SMB2_LOCK_FLAG_UNLOCK;
|
|
+
|
|
+ state->lock_req = smb2_lock_send(state->tree2, &state->lock);
|
|
+ torture_assert_goto(state->tctx, state->lock_req != NULL,
|
|
+ state->ok, failed,
|
|
+ "smb2_create_send failed\n");
|
|
+
|
|
+ state->lock_req->async.fn = test_open_brlock_deadlock_loop_unlocked;
|
|
+ state->lock_req->async.private_data = state;
|
|
+ return;
|
|
+failed:
|
|
+ state->stop = true;
|
|
+}
|
|
+
|
|
+static void test_open_brlock_deadlock_loop_unlocked(struct smb2_request *req)
|
|
+{
|
|
+ struct open_brlock_deadlock_state *state = req->async.private_data;
|
|
+ TALLOC_CTX *frame = talloc_stackframe();
|
|
+ NTSTATUS status;
|
|
+
|
|
+ status = smb2_lock_recv(req, &state->lock);
|
|
+ torture_assert_ntstatus_ok_goto(state->tctx, status,
|
|
+ state->ok, failed, __location__);
|
|
+ state->lock_req = NULL;
|
|
+
|
|
+ TALLOC_FREE(frame);
|
|
+ test_open_brlock_deadlock_loop_lock(state);
|
|
+ return;
|
|
+
|
|
+failed:
|
|
+ state->stop = true;
|
|
+ TALLOC_FREE(frame);
|
|
+}
|
|
+
|
|
+
|
|
+static void test_open_brlock_deadlock_timeout(struct tevent_context *ev,
|
|
+ struct tevent_timer *te,
|
|
+ struct timeval current_time,
|
|
+ void *private_data)
|
|
+{
|
|
+ struct open_brlock_deadlock_state *state = private_data;
|
|
+ state->stop = true;
|
|
+}
|
|
+
|
|
+static bool test_open_brlock_deadlock(struct torture_context *tctx,
|
|
+ struct smb2_tree *tree1,
|
|
+ struct smb2_tree *tree2)
|
|
+{
|
|
+ int timeout_sec = torture_setting_int(tctx, "open_brlock_deadlock_timemout", 0);
|
|
+ const char *fname1 = BASEDIR "\\test_open_brlock_deadlock1.txt";
|
|
+ const char *fname2 = BASEDIR "\\test_open_brlock_deadlock2.txt";
|
|
+ struct open_brlock_deadlock_state state;
|
|
+ struct smb2_handle h = {};
|
|
+ uint8_t buf[200];
|
|
+ struct tevent_timer *te = NULL;
|
|
+ NTSTATUS status;
|
|
+ bool ret = true;
|
|
+
|
|
+ if (timeout_sec == 0) {
|
|
+ torture_skip_goto(tctx, done, "Test skipped, pass '--option=torture:open_brlock_deadlock_timemout=SEC' to run\n");
|
|
+ }
|
|
+
|
|
+ state = (struct open_brlock_deadlock_state) {
|
|
+ .tctx = tctx,
|
|
+ .tree1 = tree1,
|
|
+ .tree2 = tree2,
|
|
+ };
|
|
+
|
|
+ ret = smb2_util_setup_dir(tctx, tree1, BASEDIR);
|
|
+ torture_assert_goto(tctx, ret, ret, done,
|
|
+ "smb2_util_setup_dir failed");
|
|
+
|
|
+ status = torture_smb2_testfile(tree1, fname1, &h);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testfile failed");
|
|
+ status = smb2_util_close(tree1, h);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "smb2_util_close failed");
|
|
+
|
|
+ status = torture_smb2_testfile(tree2, fname2, &h);
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "torture_smb2_testfile failed");
|
|
+
|
|
+ status = smb2_util_write(tree2, h, buf, 0, ARRAY_SIZE(buf));
|
|
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
|
|
+ "smb2_util_write failed");
|
|
+
|
|
+ state.cr = (struct smb2_create) {
|
|
+ .in.desired_access = SEC_FILE_READ_DATA,
|
|
+ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
|
|
+ .in.create_disposition = NTCREATEX_DISP_OPEN,
|
|
+ .in.fname = fname1,
|
|
+ };
|
|
+
|
|
+ state.el = (struct smb2_lock_element) {
|
|
+ .length = 1,
|
|
+ .offset = 0,
|
|
+ };
|
|
+ state.lock = (struct smb2_lock) {
|
|
+ .in.locks = &state.el,
|
|
+ .in.lock_count = 1,
|
|
+ .in.file.handle = h,
|
|
+ };
|
|
+
|
|
+ te = tevent_add_timer(tctx->ev,
|
|
+ tctx,
|
|
+ timeval_current_ofs(timeout_sec, 0),
|
|
+ test_open_brlock_deadlock_timeout,
|
|
+ &state);
|
|
+ torture_assert_goto(tctx, te != NULL, ret, done, __location__);
|
|
+
|
|
+ test_open_brlock_deadlock_loop_open(&state);
|
|
+ test_open_brlock_deadlock_loop_lock(&state);
|
|
+
|
|
+ while (!state.stop) {
|
|
+ int rc = tevent_loop_once(tctx->ev);
|
|
+ torture_assert_int_equal(tctx, rc, 0, "tevent_loop_once");
|
|
+ }
|
|
+
|
|
+done:
|
|
+ if (!smb2_util_handle_empty(h)) {
|
|
+ smb2_util_close(tree2, h);
|
|
+ }
|
|
+ smb2_deltree(tree1, BASEDIR);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
/* basic testing of SMB2 locking
|
|
*/
|
|
struct torture_suite *torture_smb2_lock_init(TALLOC_CTX *ctx)
|
|
@@ -3506,6 +3788,7 @@ struct torture_suite *torture_smb2_lock_init(TALLOC_CTX *ctx)
|
|
torture_suite_add_1smb2_test(suite, "replay_smb3_specification_multi",
|
|
test_replay_smb3_specification_multi);
|
|
torture_suite_add_1smb2_test(suite, "ctdb-delrec-deadlock", test_deadlock);
|
|
+ torture_suite_add_2smb2_test(suite, "open-brlock-deadlock", test_open_brlock_deadlock);
|
|
|
|
suite->description = talloc_strdup(suite, "SMB2-LOCK tests");
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 241beffc5dee532a6f9a013d3f2d60e5dc7fb472 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 6 Jan 2025 15:59:27 +0100
|
|
Subject: [PATCH 083/122] s3/brlock: split out brl_get_locks_readonly_parse()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Pair-Programmed-With: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 94e7cbcc32b73e4d56e7209e04d22d4270a6eb5b)
|
|
---
|
|
source3/locking/brlock.c | 43 ++++++++++++++++++++++++++--------------
|
|
1 file changed, 28 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index 905da049c58..a1713418d65 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -1859,29 +1859,18 @@ static void brl_get_locks_readonly_parser(TDB_DATA key, TDB_DATA data,
|
|
*state->br_lock = br_lck;
|
|
}
|
|
|
|
-struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
|
|
+static struct byte_range_lock *brl_get_locks_readonly_parse(TALLOC_CTX *mem_ctx,
|
|
+ files_struct *fsp)
|
|
{
|
|
struct byte_range_lock *br_lock = NULL;
|
|
struct brl_get_locks_readonly_state state;
|
|
NTSTATUS status;
|
|
|
|
- DEBUG(10, ("seqnum=%d, fsp->brlock_seqnum=%d\n",
|
|
- dbwrap_get_seqnum(brlock_db), fsp->brlock_seqnum));
|
|
-
|
|
- if ((fsp->brlock_rec != NULL)
|
|
- && (dbwrap_get_seqnum(brlock_db) == fsp->brlock_seqnum)) {
|
|
- /*
|
|
- * We have cached the brlock_rec and the database did not
|
|
- * change.
|
|
- */
|
|
- return fsp->brlock_rec;
|
|
- }
|
|
-
|
|
/*
|
|
* Parse the record fresh from the database
|
|
*/
|
|
|
|
- state.mem_ctx = fsp;
|
|
+ state.mem_ctx = mem_ctx;
|
|
state.br_lock = &br_lock;
|
|
|
|
status = dbwrap_parse_record(
|
|
@@ -1894,7 +1883,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
|
|
/*
|
|
* No locks on this file. Return an empty br_lock.
|
|
*/
|
|
- br_lock = talloc_zero(fsp, struct byte_range_lock);
|
|
+ br_lock = talloc_zero(mem_ctx, struct byte_range_lock);
|
|
if (br_lock == NULL) {
|
|
return NULL;
|
|
}
|
|
@@ -1912,6 +1901,30 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
|
|
br_lock->modified = false;
|
|
br_lock->record = NULL;
|
|
|
|
+ return br_lock;
|
|
+}
|
|
+
|
|
+struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
|
|
+{
|
|
+ struct byte_range_lock *br_lock = NULL;
|
|
+
|
|
+ DEBUG(10, ("seqnum=%d, fsp->brlock_seqnum=%d\n",
|
|
+ dbwrap_get_seqnum(brlock_db), fsp->brlock_seqnum));
|
|
+
|
|
+ if ((fsp->brlock_rec != NULL)
|
|
+ && (dbwrap_get_seqnum(brlock_db) == fsp->brlock_seqnum)) {
|
|
+ /*
|
|
+ * We have cached the brlock_rec and the database did not
|
|
+ * change.
|
|
+ */
|
|
+ return fsp->brlock_rec;
|
|
+ }
|
|
+
|
|
+ br_lock = brl_get_locks_readonly_parse(fsp, fsp);
|
|
+ if (br_lock == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
/*
|
|
* Cache the brlock struct, invalidated when the dbwrap_seqnum
|
|
* changes. See beginning of this routine.
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e0acfc837633b184e933a2a60cdf294229767f9a Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 6 Jan 2025 17:07:11 +0100
|
|
Subject: [PATCH 084/122] s3/brlock: add brl_req_set()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Ralph Boehme <slow@samba.org>
|
|
(cherry picked from commit c9c04c7d75dee0c3e6e843b581624a3852042057)
|
|
---
|
|
source3/locking/brlock.c | 9 +++++++++
|
|
source3/locking/proto.h | 3 +++
|
|
2 files changed, 12 insertions(+)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index a1713418d65..9629c68c3c8 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -86,6 +86,15 @@ struct files_struct *brl_fsp(struct byte_range_lock *brl)
|
|
return brl->fsp;
|
|
}
|
|
|
|
+
|
|
+void brl_req_set(struct byte_range_lock *br_lck,
|
|
+ TALLOC_CTX *req_mem_ctx,
|
|
+ const struct GUID *req_guid)
|
|
+{
|
|
+ br_lck->req_mem_ctx = req_mem_ctx;
|
|
+ br_lck->req_guid = req_guid;
|
|
+}
|
|
+
|
|
TALLOC_CTX *brl_req_mem_ctx(const struct byte_range_lock *brl)
|
|
{
|
|
if (brl->req_mem_ctx == NULL) {
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index 7fc177d7aa6..3413596baed 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -32,6 +32,9 @@ void brl_shutdown(void);
|
|
|
|
unsigned int brl_num_locks(const struct byte_range_lock *brl);
|
|
struct files_struct *brl_fsp(struct byte_range_lock *brl);
|
|
+void brl_req_set(struct byte_range_lock *br_lck,
|
|
+ TALLOC_CTX *req_mem_ctx,
|
|
+ const struct GUID *req_guid);
|
|
TALLOC_CTX *brl_req_mem_ctx(const struct byte_range_lock *brl);
|
|
const struct GUID *brl_req_guid(const struct byte_range_lock *brl);
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 862bec505a66f8c9958f96411fa2f05db8059ed1 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Sat, 1 Feb 2025 10:37:40 +0100
|
|
Subject: [PATCH 085/122] s3/brlock: add share_mode_do_locked_brl()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit e17fb732c89f8b34de00904383044de3c4f85bd0)
|
|
---
|
|
source3/locking/brlock.c | 103 +++++++++++++++++++++++++++++++++++++++
|
|
source3/locking/proto.h | 8 +++
|
|
2 files changed, 111 insertions(+)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index 9629c68c3c8..cf639451e99 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -34,6 +34,7 @@
|
|
#include "serverid.h"
|
|
#include "messages.h"
|
|
#include "util_tdb.h"
|
|
+#include "source3/locking/share_mode_lock.h"
|
|
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS DBGC_LOCKING
|
|
@@ -2018,3 +2019,105 @@ done:
|
|
talloc_free(frame);
|
|
return ret;
|
|
}
|
|
+
|
|
+struct share_mode_do_locked_brl_state {
|
|
+ share_mode_do_locked_brl_fn_t cb;
|
|
+ void *cb_data;
|
|
+ struct files_struct *fsp;
|
|
+ NTSTATUS status;
|
|
+};
|
|
+
|
|
+static void share_mode_do_locked_brl_fn(struct share_mode_lock *lck,
|
|
+ void *private_data)
|
|
+{
|
|
+ struct share_mode_do_locked_brl_state *state = private_data;
|
|
+ struct byte_range_lock *br_lck = NULL;
|
|
+ TDB_DATA key = make_tdb_data((uint8_t *)&state->fsp->file_id,
|
|
+ sizeof(state->fsp->file_id));
|
|
+
|
|
+ if (lp_locking(state->fsp->conn->params) &&
|
|
+ state->fsp->fsp_flags.can_lock)
|
|
+ {
|
|
+ br_lck = brl_get_locks_readonly_parse(talloc_tos(),
|
|
+ state->fsp);
|
|
+ if (br_lck == NULL) {
|
|
+ state->status = NT_STATUS_NO_MEMORY;
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ state->cb(lck, br_lck, state->cb_data);
|
|
+
|
|
+ if (br_lck == NULL || !br_lck->modified) {
|
|
+ TALLOC_FREE(br_lck);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ br_lck->record = dbwrap_fetch_locked(brlock_db, br_lck, key);
|
|
+ if (br_lck->record == NULL) {
|
|
+ DBG_ERR("Could not lock byte range lock entry for '%s'\n",
|
|
+ fsp_str_dbg(state->fsp));
|
|
+ TALLOC_FREE(br_lck);
|
|
+ state->status = NT_STATUS_INTERNAL_DB_ERROR;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ byte_range_lock_flush(br_lck);
|
|
+ share_mode_wakeup_waiters(br_lck->fsp->file_id);
|
|
+ TALLOC_FREE(br_lck);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Run cb with a glock'ed locking.tdb record, providing both a share_mode_lock
|
|
+ * and a br_lck object. An initial read-only, but upgradable, br_lck object is
|
|
+ * fetched from brlock.tdb while holding the glock on the locking.tdb record.
|
|
+ *
|
|
+ * This function only ever hold one low-level TDB chainlock at a time on either
|
|
+ * locking.tdb or brlock.tdb, so it can't run afoul any lock order violations.
|
|
+ *
|
|
+ * Note that br_lck argument in the callback might be NULL in case lp_locking()
|
|
+ * is disabled, the fsp doesn't allow locking or is a directory, so either
|
|
+ * the caller or the callback have to check for this.
|
|
+ */
|
|
+NTSTATUS share_mode_do_locked_brl(files_struct *fsp,
|
|
+ share_mode_do_locked_brl_fn_t cb,
|
|
+ void *cb_data)
|
|
+{
|
|
+ static bool recursion_guard;
|
|
+ TALLOC_CTX *frame = NULL;
|
|
+ struct share_mode_do_locked_brl_state state = {
|
|
+ .fsp = fsp,
|
|
+ .cb = cb,
|
|
+ .cb_data = cb_data,
|
|
+ };
|
|
+ NTSTATUS status;
|
|
+
|
|
+ SMB_ASSERT(!recursion_guard);
|
|
+
|
|
+ /* silently return ok on print files as we don't do locking there */
|
|
+ if (fsp->print_file) {
|
|
+ return NT_STATUS_OK;
|
|
+ }
|
|
+
|
|
+ frame = talloc_stackframe();
|
|
+
|
|
+ recursion_guard = true;
|
|
+ status = share_mode_do_locked_vfs_allowed(
|
|
+ fsp->file_id,
|
|
+ share_mode_do_locked_brl_fn,
|
|
+ &state);
|
|
+ recursion_guard = false;
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_ERR("share_mode_do_locked_vfs_allowed() failed for %s - %s\n",
|
|
+ fsp_str_dbg(fsp), nt_errstr(status));
|
|
+ TALLOC_FREE(frame);
|
|
+ return status;
|
|
+ }
|
|
+ if (!NT_STATUS_IS_OK(state.status)) {
|
|
+ TALLOC_FREE(frame);
|
|
+ return state.status;
|
|
+ }
|
|
+
|
|
+ TALLOC_FREE(frame);
|
|
+ return NT_STATUS_OK;
|
|
+}
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index 3413596baed..c9d769ba53f 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -87,6 +87,14 @@ struct byte_range_lock *brl_get_locks_for_locking(TALLOC_CTX *mem_ctx,
|
|
files_struct *fsp,
|
|
TALLOC_CTX *req_mem_ctx,
|
|
const struct GUID *req_guid);
|
|
+struct share_mode_lock;
|
|
+typedef void (*share_mode_do_locked_brl_fn_t)(
|
|
+ struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck, /* br_lck can be NULL */
|
|
+ void *private_data);
|
|
+NTSTATUS share_mode_do_locked_brl(files_struct *fsp,
|
|
+ share_mode_do_locked_brl_fn_t fn,
|
|
+ void *private_data);
|
|
struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx,
|
|
files_struct *fsp);
|
|
struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d18f6942b66eb7166f8929e9b475d0acbd721cb0 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Wed, 8 Jan 2025 15:43:04 +0100
|
|
Subject: [PATCH 086/122] s3/brlock: don't increment current_lock_count if
|
|
do_lock_fn() failed
|
|
|
|
Also only assign psmblctx and pblocker_pid if the lock request failed.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 3a0c6e99de4377f44bc29766b6ceb79040caed9f)
|
|
---
|
|
source3/locking/locking.c | 20 +++++++++++---------
|
|
1 file changed, 11 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
|
|
index 6ee5987ffda..d796e6ffb7b 100644
|
|
--- a/source3/locking/locking.c
|
|
+++ b/source3/locking/locking.c
|
|
@@ -340,19 +340,21 @@ NTSTATUS do_lock(files_struct *fsp,
|
|
nt_errstr(status));
|
|
return status;
|
|
}
|
|
-
|
|
- if (psmblctx != NULL) {
|
|
- *psmblctx = state.blocker_smblctx;
|
|
- }
|
|
- if (pblocker_pid != NULL) {
|
|
- *pblocker_pid = state.blocker_pid;
|
|
+ if (!NT_STATUS_IS_OK(state.status)) {
|
|
+ DBG_DEBUG("do_lock_fn returned %s\n",
|
|
+ nt_errstr(state.status));
|
|
+ if (psmblctx != NULL) {
|
|
+ *psmblctx = state.blocker_smblctx;
|
|
+ }
|
|
+ if (pblocker_pid != NULL) {
|
|
+ *pblocker_pid = state.blocker_pid;
|
|
+ }
|
|
+ return state.status;
|
|
}
|
|
|
|
- DBG_DEBUG("returning status=%s\n", nt_errstr(state.status));
|
|
-
|
|
increment_current_lock_count(fsp, lock_flav);
|
|
|
|
- return state.status;
|
|
+ return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4a6c36f1d31556e1abcdbf090f76d4c49b261e92 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Wed, 29 Jan 2025 06:13:29 +0100
|
|
Subject: [PATCH 087/122] s3/locking: add brl_set_modified()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 2772f147c9b13cd2160181c4f7905b54ab765054)
|
|
---
|
|
source3/locking/brlock.c | 5 +++++
|
|
source3/locking/proto.h | 1 +
|
|
2 files changed, 6 insertions(+)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index cf639451e99..e0d18800cf1 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -2121,3 +2121,8 @@ NTSTATUS share_mode_do_locked_brl(files_struct *fsp,
|
|
TALLOC_FREE(frame);
|
|
return NT_STATUS_OK;
|
|
}
|
|
+
|
|
+void brl_set_modified(struct byte_range_lock *br_lck, bool modified)
|
|
+{
|
|
+ br_lck->modified = modified;
|
|
+}
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index c9d769ba53f..c74539c8161 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -99,6 +99,7 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx,
|
|
files_struct *fsp);
|
|
struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp);
|
|
bool brl_cleanup_disconnected(struct file_id fid, uint64_t open_persistent_id);
|
|
+void brl_set_modified(struct byte_range_lock *br_lck, bool modified);
|
|
|
|
/* The following definitions come from locking/locking.c */
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 3362898470c1efa07bb9e05f6663a42fef53c784 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Wed, 29 Jan 2025 06:13:44 +0100
|
|
Subject: [PATCH 088/122] smbd: use share_mode_do_locked_brl()
|
|
|
|
Fix a deadlock that can happen if two clients happen to open and byte-range-lock
|
|
two different files whos record in locking.tdb and brlock.tdb happen to sit on
|
|
the same hashchain.
|
|
|
|
The deadlock was introduced by commit
|
|
680c7907325b433856ac1dd916ab63e671fbe4ab. Before, we used share_mode_do_locked()
|
|
in do_lock() which meant we acquired a chainlock on locking.tdb before getting a
|
|
chainlock on brlock.tdb via brl_get_locks_for_locking(), so the TDB chainlock
|
|
order invariant was always uphold.
|
|
|
|
The following race between specific client requests lead to the deadlock.
|
|
|
|
Client A) issues a byte-range-lock request on a file:
|
|
|
|
A1) glock locking.tdb (via _share_mode_do_locked_vfs_allowed())
|
|
A2) chainlock brlock.tdb (via brl_lock())
|
|
A3) attempt to chainlock locking.tdb (via share_mode_g_lock_dump())
|
|
[1]
|
|
|
|
Client B) opens a different (!) file:
|
|
|
|
B1) glock and chainlock locking.tdb (via _share_mode_entry_prepare_lock())
|
|
B2) attempt to chainlock brlock.tdb (via file_has_brlocks())
|
|
[2]
|
|
|
|
The glock from A1 is per record and hence doesn't synchronize with the glock
|
|
from B1 as it is for a different file and hence a different record, subsequently
|
|
A2 and A3 violate the lock order constraint
|
|
|
|
To avoid the chainlock lock order violation in the second client we modify the
|
|
br-lock code to not take the brlock.tdb chainlock from step A2 via
|
|
br_get_locks() for the whole time we process the request. Instead we just fetch
|
|
the br-locks via br_get_locks_readonly(), so when running into
|
|
contend_level2_oplocks_begin_default() to check for leases and looking into
|
|
locking.tdb we don't hold a brlock.tdb chainlock.
|
|
|
|
Or im simpler terms, we only ever take at most one low-level TDB chainlock at a
|
|
time:
|
|
|
|
Byte-range-lock code calls share_mode_do_locked_brl(..., cb_fn, ...):
|
|
1) chainlock locking.tdb
|
|
2) glock locking.tdb (via share_mode_do_locked_vfs_allowed())
|
|
3) chainunlock locking.tdb
|
|
4) share_mode_do_locked_brl_fn() -> brl_get_locks_readonly_parse():
|
|
a) chainlock brlock.tdb
|
|
b) parse record and store in-memory copy
|
|
c) chainunlock brlock.tdb
|
|
5) run cb_fn()
|
|
6) chainlock brlock.tdb:
|
|
a) br_lck->record = dbwrap_fetch_locked(brlock_db, ...)
|
|
b) store modifed br_lck from 5) via byte_range_lock_flush()
|
|
7) chainunlock brlock.tdb
|
|
8) chainlock locking.tdb
|
|
9) gunlock locking.tdb
|
|
10) chainunlock locking.tdb
|
|
|
|
All access to brlock.tdb is synchronized correctly via glocks on the locking.tdb
|
|
record of the file (step 3)), so operations still appear atomic to clients.
|
|
|
|
As a result of using share_mode_do_locked_brl(), the functions do_[un]lock() ->
|
|
brl_[un]lock() now loop over the same br_lck object in memory, avoiding
|
|
repeatedly fetching and storing the locks per loop.
|
|
|
|
[1]
|
|
Full SBT:
|
|
|
|
#0 0x00007fffa0cecbb0 in __pthread_mutex_lock_full () from /lib64/glibc-hwcaps/power9/libpthread-2.28.so
|
|
#1 0x00007fffa0a73cf8 in chain_mutex_lock (m=<optimized out>, m@entry=0x7fff9ae071b0, waitflag=<optimized out>, waitflag@entry=true) at ../../lib/tdb/common/mutex.c:182
|
|
#2 0x00007fffa0a7432c in tdb_mutex_lock (tdb=0x1543ba120, rw=<optimized out>, off=<optimized out>, len=<optimized out>, waitflag=<optimized out>, pret=0x7fffd7df3858) at ../../lib/tdb/common/mutex.c:234
|
|
#3 0x00007fffa0a6812c in fcntl_lock (waitflag=<optimized out>, len=1, off=376608, rw=0, tdb=0x1543ba120) at ../../lib/tdb/common/lock.c:200
|
|
#4 tdb_brlock (tdb=0x1543ba120, rw_type=<optimized out>, offset=<optimized out>, len=1, flags=<optimized out>) at ../../lib/tdb/common/lock.c:200
|
|
#5 0x00007fffa0a68af8 in tdb_nest_lock (flags=<optimized out>, ltype=0, offset=<optimized out>, tdb=0x1543ba120) at ../../lib/tdb/common/lock.c:390
|
|
#6 tdb_nest_lock (tdb=0x1543ba120, offset=<optimized out>, ltype=<optimized out>, flags=<optimized out>) at ../../lib/tdb/common/lock.c:336
|
|
#7 0x00007fffa0a69088 in tdb_lock_list (tdb=0x1543ba120, list=<optimized out>, ltype=<optimized out>, waitflag=<optimized out>) at ../../lib/tdb/common/lock.c:482
|
|
#8 0x00007fffa0a69198 in tdb_lock (tdb=0x1543ba120, list=<optimized out>, ltype=<optimized out>) at ../../lib/tdb/common/lock.c:500
|
|
#9 0x00007fffa0a64b50 in tdb_find_lock_hash (tdb=<optimized out>, tdb@entry=0x1543ba120, key=..., hash=<optimized out>, locktype=<optimized out>, locktype@entry=0, rec=<optimized out>, rec@entry=0x7fffd7df3ab0) at ../../lib/tdb/common/tdb.c:165
|
|
#10 0x00007fffa0a64ed0 in tdb_parse_record (tdb=0x1543ba120, key=..., parser=0x7fffa0e74470 <db_ctdb_ltdb_parser>, private_data=0x7fffd7df3b18) at ../../lib/tdb/common/tdb.c:329
|
|
#11 0x00007fffa0e74cbc in db_ctdb_ltdb_parse (db=<optimized out>, private_data=0x7fffd7df3b70, parser=0x7fffa0e76470 <db_ctdb_parse_record_parser_nonpersistent>, key=...) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:170
|
|
#12 db_ctdb_try_parse_local_record (ctx=ctx@entry=0x1543d4580, key=..., state=state@entry=0x7fffd7df3b70) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1385
|
|
#13 0x00007fffa0e76024 in db_ctdb_parse_record (db=<optimized out>, key=..., parser=0x7fffa1313910 <dbwrap_watched_parse_record_parser>, private_data=0x7fffd7df3c08) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1425
|
|
#14 0x00007fffa0884760 in dbwrap_parse_record (db=<optimized out>, key=..., parser=<optimized out>, private_data=<optimized out>) at ../../lib/dbwrap/dbwrap.c:454
|
|
#15 0x00007fffa1313ab4 in dbwrap_watched_parse_record (db=0x1543a7160, key=..., parser=0x7fffa13187d0 <g_lock_dump_fn>, private_data=0x7fffd7df3ce8) at ../../source3/lib/dbwrap/dbwrap_watch.c:783
|
|
#16 0x00007fffa0884760 in dbwrap_parse_record (db=<optimized out>, key=..., parser=<optimized out>, private_data=<optimized out>) at ../../lib/dbwrap/dbwrap.c:454
|
|
#17 0x00007fffa131c004 in g_lock_dump (ctx=<error reading variable: value has been optimized out>, key=..., fn=0x7fffa14f3d70 <fsp_update_share_mode_flags_fn>, private_data=0x7fffd7df3dd8) at ../../source3/lib/g_lock.c:1653
|
|
#18 0x00007fffa14f434c in share_mode_g_lock_dump (key=..., fn=0x7fffa14f3d70 <fsp_update_share_mode_flags_fn>, private_data=0x7fffd7df3dd8) at ../../source3/locking/share_mode_lock.c:96
|
|
#19 0x00007fffa14f8d44 in fsp_update_share_mode_flags (fsp=0x15433c550) at ../../source3/locking/share_mode_lock.c:1181
|
|
#20 file_has_read_lease (fsp=0x15433c550) at ../../source3/locking/share_mode_lock.c:1207
|
|
#21 0x00007fffa15ccc98 in contend_level2_oplocks_begin_default (type=<optimized out>, fsp=0x15433c550) at ../../source3/smbd/smb2_oplock.c:1282
|
|
#22 smbd_contend_level2_oplocks_begin (fsp=0x15433c550, type=<optimized out>) at ../../source3/smbd/smb2_oplock.c:1338
|
|
#23 0x00007fffa0dd0b54 in contend_level2_oplocks_begin (fsp=<optimized out>, type=<optimized out>) at ../../source3/lib/smbd_shim.c:72
|
|
#24 0x00007fffa14ecfd0 in brl_lock_windows_default (br_lck=0x154421330, plock=0x7fffd7df4250) at ../../source3/locking/brlock.c:457
|
|
#25 0x00007fffa150b70c in vfswrap_brl_lock_windows (handle=<optimized out>, br_lck=<optimized out>, plock=<optimized out>) at ../../source3/modules/vfs_default.c:3424
|
|
#26 0x00007fffa1561910 in smb_vfs_call_brl_lock_windows (handle=<optimized out>, br_lck=<optimized out>, plock=<optimized out>) at ../../source3/smbd/vfs.c:2686
|
|
#27 0x00007fff9c0a7350 in smb_time_audit_brl_lock_windows (handle=<optimized out>, br_lck=0x154421330, plock=0x7fffd7df4250) at ../../source3/modules/vfs_time_audit.c:1740
|
|
#28 0x00007fffa1561910 in smb_vfs_call_brl_lock_windows (handle=<optimized out>, br_lck=<optimized out>, plock=<optimized out>) at ../../source3/smbd/vfs.c:2686
|
|
#29 0x00007fffa14ed410 in brl_lock (br_lck=0x154421330, smblctx=3102281601, pid=..., start=0, size=18446744073709551615, lock_type=<optimized out>, lock_flav=WINDOWS_LOCK, blocker_pid=0x7fffd7df4540, psmblctx=0x7fffd7df4558) at ../../source3/locking/brlock.c:1004
|
|
#30 0x00007fffa14e7b18 in do_lock_fn (lck=<optimized out>, private_data=0x7fffd7df4508) at ../../source3/locking/locking.c:271
|
|
#31 0x00007fffa14fcd94 in _share_mode_do_locked_vfs_allowed (id=..., fn=0x7fffa14e7a60 <do_lock_fn>, private_data=0x7fffd7df4508, location=<optimized out>) at ../../source3/locking/share_mode_lock.c:2927
|
|
#32 0x00007fffa14e918c in do_lock (fsp=0x15433c550, req_mem_ctx=<optimized out>, req_guid=<optimized out>, smblctx=<optimized out>, count=18446744073709551615, offset=0, lock_type=<optimized out>, lock_flav=<optimized out>, pblocker_pid=0x7fffd7df46f0,
|
|
psmblctx=0x7fffd7df46d8) at ../../source3/locking/locking.c:335
|
|
#33 0x00007fffa155381c in smbd_do_locks_try (fsp=0x15433c550, num_locks=<optimized out>, locks=0x1543bc310, blocker_idx=0x7fffd7df46d6, blocking_pid=0x7fffd7df46f0, blocking_smblctx=0x7fffd7df46d8) at ../../source3/smbd/blocking.c:46
|
|
#34 0x00007fffa159dc90 in smbd_smb2_lock_try (req=req@entry=0x1543bc080) at ../../source3/smbd/smb2_lock.c:590
|
|
#35 0x00007fffa159ee8c in smbd_smb2_lock_send (in_locks=<optimized out>, in_lock_count=1, in_lock_sequence=<optimized out>, fsp=0x15433c550, smb2req=0x1543532e0, ev=0x154328120, mem_ctx=0x1543532e0) at ../../source3/smbd/smb2_lock.c:488
|
|
#36 smbd_smb2_request_process_lock (req=0x1543532e0) at ../../source3/smbd/smb2_lock.c:150
|
|
#37 0x00007fffa158a368 in smbd_smb2_request_dispatch (req=0x1543532e0) at ../../source3/smbd/smb2_server.c:3515
|
|
#38 0x00007fffa158c540 in smbd_smb2_io_handler (fde_flags=<optimized out>, xconn=0x154313f30) at ../../source3/smbd/smb2_server.c:5112
|
|
#39 smbd_smb2_connection_handler (ev=<optimized out>, fde=<optimized out>, flags=<optimized out>, private_data=<optimized out>) at ../../source3/smbd/smb2_server.c:5150
|
|
#40 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x1543670f0, flags=<optimized out>, removed=0x0) at ../../lib/tevent/tevent_fd.c:158
|
|
#41 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4b28, epoll_ev=0x1543b4e80) at ../../lib/tevent/tevent_epoll.c:730
|
|
#42 epoll_event_loop_once (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent_epoll.c:946
|
|
#43 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:110
|
|
#44 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent.c:823
|
|
#45 0x00007fffa1197884 in tevent_common_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:950
|
|
#46 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:141
|
|
#47 0x00007fffa1197978 in _tevent_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:971
|
|
#48 0x00007fffa15737fc in smbd_process (ev_ctx=0x154328120, msg_ctx=<optimized out>, sock_fd=<optimized out>, interactive=<optimized out>) at ../../source3/smbd/smb2_process.c:2158
|
|
#49 0x000000011db5c554 in smbd_accept_connection (ev=0x154328120, fde=<optimized out>, flags=<optimized out>, private_data=<optimized out>) at ../../source3/smbd/server.c:1150
|
|
#50 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x1543ac2d0, flags=<optimized out>, removed=0x0) at ../../lib/tevent/tevent_fd.c:158
|
|
#51 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4f98, epoll_ev=0x154328350) at ../../lib/tevent/tevent_epoll.c:730
|
|
#52 epoll_event_loop_once (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent_epoll.c:946
|
|
#53 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:110
|
|
#54 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent.c:823
|
|
#55 0x00007fffa1197884 in tevent_common_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:950
|
|
#56 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:141
|
|
#57 0x00007fffa1197978 in _tevent_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:971
|
|
#58 0x000000011db58c54 in smbd_parent_loop (parent=<optimized out>, ev_ctx=0x154328120) at ../../source3/smbd/server.c:1499
|
|
#59 main (argc=<optimized out>, argv=<optimized out>) at ../../source3/smbd/server.c:2258
|
|
|
|
[2]
|
|
Full SBT:
|
|
|
|
#0 0x00007fffa0cecbb0 in __pthread_mutex_lock_full () from /lib64/glibc-hwcaps/power9/libpthread-2.28.so
|
|
#1 0x00007fffa0a73cf8 in chain_mutex_lock (m=<optimized out>, m@entry=0x7fff9b3a71b0, waitflag=<optimized out>, waitflag@entry=true) at ../../lib/tdb/common/mutex.c:182
|
|
#2 0x00007fffa0a7432c in tdb_mutex_lock (tdb=0x1543c6900, rw=<optimized out>, off=<optimized out>, len=<optimized out>, waitflag=<optimized out>, pret=0x7fffd7df2e28) at ../../lib/tdb/common/mutex.c:234
|
|
#3 0x00007fffa0a6812c in fcntl_lock (waitflag=<optimized out>, len=1, off=376608, rw=0, tdb=0x1543c6900) at ../../lib/tdb/common/lock.c:200
|
|
#4 tdb_brlock (tdb=0x1543c6900, rw_type=<optimized out>, offset=<optimized out>, len=1, flags=<optimized out>) at ../../lib/tdb/common/lock.c:200
|
|
#5 0x00007fffa0a68af8 in tdb_nest_lock (flags=<optimized out>, ltype=0, offset=<optimized out>, tdb=0x1543c6900) at ../../lib/tdb/common/lock.c:390
|
|
#6 tdb_nest_lock (tdb=0x1543c6900, offset=<optimized out>, ltype=<optimized out>, flags=<optimized out>) at ../../lib/tdb/common/lock.c:336
|
|
#7 0x00007fffa0a69088 in tdb_lock_list (tdb=0x1543c6900, list=<optimized out>, ltype=<optimized out>, waitflag=<optimized out>) at ../../lib/tdb/common/lock.c:482
|
|
#8 0x00007fffa0a69198 in tdb_lock (tdb=0x1543c6900, list=<optimized out>, ltype=<optimized out>) at ../../lib/tdb/common/lock.c:500
|
|
#9 0x00007fffa0a64b50 in tdb_find_lock_hash (tdb=<optimized out>, tdb@entry=0x1543c6900, key=..., hash=<optimized out>, locktype=<optimized out>, locktype@entry=0, rec=<optimized out>, rec@entry=0x7fffd7df3080) at ../../lib/tdb/common/tdb.c:165
|
|
#10 0x00007fffa0a64ed0 in tdb_parse_record (tdb=0x1543c6900, key=..., parser=0x7fffa0e74470 <db_ctdb_ltdb_parser>, private_data=0x7fffd7df30e8) at ../../lib/tdb/common/tdb.c:329
|
|
#11 0x00007fffa0e74cbc in db_ctdb_ltdb_parse (db=<optimized out>, private_data=0x7fffd7df3140, parser=0x7fffa0e76470 <db_ctdb_parse_record_parser_nonpersistent>, key=...) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:170
|
|
#12 db_ctdb_try_parse_local_record (ctx=ctx@entry=0x154328fc0, key=..., state=state@entry=0x7fffd7df3140) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1385
|
|
#13 0x00007fffa0e76024 in db_ctdb_parse_record (db=<optimized out>, key=..., parser=0x7fffa14ec820 <brl_get_locks_readonly_parser>, private_data=0x7fffd7df3218) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1425
|
|
#14 0x00007fffa0884760 in dbwrap_parse_record (db=<optimized out>, key=..., parser=<optimized out>, private_data=<optimized out>) at ../../lib/dbwrap/dbwrap.c:454
|
|
#15 0x00007fffa14ef5bc in brl_get_locks_readonly (fsp=0x1543d01e0) at ../../source3/locking/brlock.c:1884
|
|
#16 0x00007fffa1546968 in file_has_brlocks (fsp=0x1543d01e0) at ../../source3/smbd/open.c:2232
|
|
#17 delay_for_oplock (pgranted=<synthetic pointer>, poplock_type=<synthetic pointer>, first_open_attempt=<optimized out>, create_disposition=1, have_sharing_violation=false, lck=0x7fffd7df3ce8, lease=0x0, oplock_request=0, fsp=0x1543d01e0) at ../../source3/smbd/open.c:2749
|
|
#18 handle_share_mode_lease (pgranted=<synthetic pointer>, poplock_type=<synthetic pointer>, first_open_attempt=<optimized out>, lease=0x0, oplock_request=0, share_access=7, access_mask=131201, create_disposition=1, lck=0x7fffd7df3ce8, fsp=0x1543d01e0) at ../../source3/smbd/open.c:2865
|
|
#19 check_and_store_share_mode (first_open_attempt=<optimized out>, lease=0x0, oplock_request=0, share_access=7, access_mask=131201, create_disposition=1, lck=0x7fffd7df3ce8, req=0x154414800, fsp=0x1543d01e0) at ../../source3/smbd/open.c:3333
|
|
#20 open_ntcreate_lock_add_entry (lck=0x7fffd7df3ce8, keep_locked=0x7fffd7df3ad0, private_data=0x7fffd7df3cc8) at ../../source3/smbd/open.c:3688
|
|
#21 0x00007fffa14f6248 in share_mode_entry_prepare_lock_fn (glck=0x7fffd7df35b8, cb_private=0x7fffd7df3a88) at ../../source3/locking/share_mode_lock.c:2978
|
|
#22 0x00007fffa1317680 in g_lock_lock_cb_run_and_store (cb_state=cb_state@entry=0x7fffd7df35b8) at ../../source3/lib/g_lock.c:597
|
|
#23 0x00007fffa1319df8 in g_lock_lock_simple_fn (rec=0x7fffd7df3798, value=..., private_data=0x7fffd7df39a0) at ../../source3/lib/g_lock.c:1212
|
|
#24 0x00007fffa13160e0 in dbwrap_watched_do_locked_fn (backend_rec=<optimized out>, backend_value=..., private_data=0x7fffd7df3768) at ../../source3/lib/dbwrap/dbwrap_watch.c:458
|
|
#25 0x00007fffa0884e48 in dbwrap_do_locked (db=<optimized out>, key=..., fn=0x7fffa1316080 <dbwrap_watched_do_locked_fn>, private_data=0x7fffd7df3768) at ../../lib/dbwrap/dbwrap.c:602
|
|
#26 0x00007fffa1315274 in dbwrap_watched_do_locked (db=0x1543a7160, key=..., fn=0x7fffa1319ca0 <g_lock_lock_simple_fn>, private_data=0x7fffd7df39a0) at ../../source3/lib/dbwrap/dbwrap_watch.c:480
|
|
#27 0x00007fffa0884d60 in dbwrap_do_locked (db=<optimized out>, key=..., fn=<optimized out>, private_data=<optimized out>) at ../../lib/dbwrap/dbwrap.c:582
|
|
#28 0x00007fffa131b458 in g_lock_lock (ctx=0x1543cc630, key=..., type=<optimized out>, timeout=..., cb_fn=0x7fffa14f6190 <share_mode_entry_prepare_lock_fn>, cb_private=0x7fffd7df3a88) at ../../source3/lib/g_lock.c:1267
|
|
#29 0x00007fffa14fd060 in _share_mode_entry_prepare_lock (prepare_state=0x7fffd7df3cc8, id=..., servicepath=<optimized out>, smb_fname=<optimized out>, old_write_time=<optimized out>, fn=<optimized out>, private_data=0x7fffd7df3cc8, location=0x7fffa165b880 "../../source3/smbd/open.c:4292") at ../../source3/locking/share_mode_lock.c:3033
|
|
#30 0x00007fffa15491e0 in open_file_ntcreate (conn=conn@entry=0x154382050, req=req@entry=0x154414800, access_mask=<optimized out>, access_mask@entry=131201, share_access=share_access@entry=7, create_disposition=create_disposition@entry=1, create_options=create_options@entry=0, new_dos_attributes=<optimized out>, new_dos_attributes@entry=128, oplock_request=oplock_request@entry=0, lease=<optimized out>, lease@entry=0x0, private_flags=<optimized out>, private_flags@entry=0, parent_dir_fname=<optimized out>, smb_fname_atname=<optimized out>, pinfo=<optimized out>, pinfo@entry=0x7fffd7df3f1c, fsp=<optimized out>, fsp@entry=0x1543d01e0) at ../../source3/smbd/open.c:4286
|
|
#31 0x00007fffa154b94c in create_file_unixpath (conn=conn@entry=0x154382050, req=req@entry=0x154414800, dirfsp=dirfsp@entry=0x15439a7f0, smb_fname=smb_fname@entry=0x154416300, access_mask=access_mask@entry=131201, share_access=share_access@entry=7, create_disposition=create_disposition@entry=1, create_options=create_options@entry=0, file_attributes=file_attributes@entry=128, oplock_request=<optimized out>, oplock_request@entry=0, lease=<optimized out>, lease@entry=0x0, allocation_size=allocation_size@entry=0, private_flags=private_flags@entry=0, sd=sd@entry=0x0, ea_list=ea_list@entry=0x0, result=result@entry=0x7fffd7df4168, pinfo=pinfo@entry=0x7fffd7df4160) at ../../source3/smbd/open.c:6290
|
|
#32 0x00007fffa154dfac in create_file_default (conn=0x154382050, req=0x154414800, dirfsp=0x15439a7f0, smb_fname=0x154416300, access_mask=<optimized out>, share_access=<optimized out>, create_disposition=<optimized out>, create_options=<optimized out>, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/smbd/open.c:6609
|
|
#33 0x00007fffa150972c in vfswrap_create_file (handle=<optimized out>, req=<optimized out>, dirfsp=<optimized out>, smb_fname=<optimized out>, access_mask=<optimized out>, share_access=<optimized out>, create_disposition=<optimized out>, create_options=<optimized out>, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/modules/vfs_default.c:776
|
|
#34 0x00007fffa1559cbc in smb_vfs_call_create_file (handle=<optimized out>, req=<optimized out>, dirfsp=<optimized out>, smb_fname=<optimized out>, access_mask=<optimized out>, share_access=<optimized out>, create_disposition=<optimized out>, create_options=<optimized out>, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/smbd/vfs.c:1560
|
|
#35 0x00007fff9c0a9ec4 in smb_time_audit_create_file (handle=0x154426820, req=0x154414800, dirfsp=0x15439a7f0, fname=0x154416300, access_mask=<optimized out>, share_access=<optimized out>, create_disposition=<optimized out>, create_options=<optimized out>, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result_fsp=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/modules/vfs_time_audit.c:634
|
|
#36 0x00007fffa1559cbc in smb_vfs_call_create_file (handle=<optimized out>, req=<optimized out>, dirfsp=<optimized out>, smb_fname=<optimized out>, access_mask=<optimized out>, share_access=<optimized out>, create_disposition=<optimized out>, create_options=<optimized out>, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/smbd/vfs.c:1560
|
|
#37 0x00007fffa1597aa8 in smbd_smb2_create_send (in_context_blobs=..., in_name=0x154413ca0, in_create_options=<optimized out>, in_create_disposition=<optimized out>, in_share_access=<optimized out>, in_file_attributes=<optimized out>, in_desired_access=<optimized out>, in_impersonation_level=<optimized out>, in_oplock_level=<optimized out>, smb2req=0x154413770, ev=0x154328120, mem_ctx=0x154413770) at ../../source3/smbd/smb2_create.c:1115
|
|
#38 smbd_smb2_request_process_create (smb2req=0x154413770) at ../../source3/smbd/smb2_create.c:291
|
|
#39 0x00007fffa158a628 in smbd_smb2_request_dispatch (req=0x154413770) at ../../source3/smbd/smb2_server.c:3485
|
|
#40 0x00007fffa158c540 in smbd_smb2_io_handler (fde_flags=<optimized out>, xconn=0x154313f30) at ../../source3/smbd/smb2_server.c:5112
|
|
#41 smbd_smb2_connection_handler (ev=<optimized out>, fde=<optimized out>, flags=<optimized out>, private_data=<optimized out>) at ../../source3/smbd/smb2_server.c:5150
|
|
#42 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x15435add0, flags=<optimized out>, removed=0x0) at ../../lib/tevent/tevent_fd.c:158
|
|
#43 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4b28, epoll_ev=0x1543b4e80) at ../../lib/tevent/tevent_epoll.c:730
|
|
#44 epoll_event_loop_once (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent_epoll.c:946
|
|
#45 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:110
|
|
#46 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent.c:823
|
|
#47 0x00007fffa1197884 in tevent_common_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:950
|
|
#48 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:141
|
|
#49 0x00007fffa1197978 in _tevent_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:971
|
|
#50 0x00007fffa15737fc in smbd_process (ev_ctx=0x154328120, msg_ctx=<optimized out>, sock_fd=<optimized out>, interactive=<optimized out>) at ../../source3/smbd/smb2_process.c:2158
|
|
#51 0x000000011db5c554 in smbd_accept_connection (ev=0x154328120, fde=<optimized out>, flags=<optimized out>, private_data=<optimized out>) at ../../source3/smbd/server.c:1150
|
|
#52 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x1543ac2d0, flags=<optimized out>, removed=0x0) at ../../lib/tevent/tevent_fd.c:158
|
|
#53 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4f98, epoll_ev=0x154328350) at ../../lib/tevent/tevent_epoll.c:730
|
|
#54 epoll_event_loop_once (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent_epoll.c:946
|
|
#55 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:110
|
|
#56 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent.c:823
|
|
#57 0x00007fffa1197884 in tevent_common_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:950
|
|
#58 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:141
|
|
#59 0x00007fffa1197978 in _tevent_loop_wait (ev=<optimized out>, location=<optimized out>) at ../../lib/tevent/tevent.c:971
|
|
#60 0x000000011db58c54 in smbd_parent_loop (parent=<optimized out>, ev_ctx=0x154328120) at ../../source3/smbd/server.c:1499
|
|
#61 main (argc=<optimized out>, argv=<optimized out>) at ../../source3/smbd/server.c:2258
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
(backported from commit 2eef298ff4c5baf15c7d29c65fb021dbed5b0a93)
|
|
[slow@samba.org: changed argument of share_mode_watch_send()]
|
|
[slow@samba.org: small context change in vfs_fruit]
|
|
---
|
|
source3/locking/locking.c | 109 +++++--------------
|
|
source3/locking/proto.h | 4 +-
|
|
source3/modules/vfs_fruit.c | 91 +++++++++++-----
|
|
source3/smbd/blocking.c | 202 +++++++++++++++++++-----------------
|
|
source3/smbd/proto.h | 19 ++--
|
|
source3/smbd/smb2_lock.c | 77 +++++++++-----
|
|
source3/smbd/smb2_reply.c | 53 ++++++----
|
|
7 files changed, 293 insertions(+), 262 deletions(-)
|
|
|
|
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
|
|
index d796e6ffb7b..25b3bdcf7f7 100644
|
|
--- a/source3/locking/locking.c
|
|
+++ b/source3/locking/locking.c
|
|
@@ -237,52 +237,7 @@ static void decrement_current_lock_count(files_struct *fsp,
|
|
Utility function called by locking requests.
|
|
****************************************************************************/
|
|
|
|
-struct do_lock_state {
|
|
- struct files_struct *fsp;
|
|
- TALLOC_CTX *req_mem_ctx;
|
|
- const struct GUID *req_guid;
|
|
- uint64_t smblctx;
|
|
- uint64_t count;
|
|
- uint64_t offset;
|
|
- enum brl_type lock_type;
|
|
- enum brl_flavour lock_flav;
|
|
-
|
|
- struct server_id blocker_pid;
|
|
- uint64_t blocker_smblctx;
|
|
- NTSTATUS status;
|
|
-};
|
|
-
|
|
-static void do_lock_fn(
|
|
- struct share_mode_lock *lck,
|
|
- void *private_data)
|
|
-{
|
|
- struct do_lock_state *state = private_data;
|
|
- struct byte_range_lock *br_lck = NULL;
|
|
-
|
|
- br_lck = brl_get_locks_for_locking(talloc_tos(),
|
|
- state->fsp,
|
|
- state->req_mem_ctx,
|
|
- state->req_guid);
|
|
- if (br_lck == NULL) {
|
|
- state->status = NT_STATUS_NO_MEMORY;
|
|
- return;
|
|
- }
|
|
-
|
|
- state->status = brl_lock(
|
|
- br_lck,
|
|
- state->smblctx,
|
|
- messaging_server_id(state->fsp->conn->sconn->msg_ctx),
|
|
- state->offset,
|
|
- state->count,
|
|
- state->lock_type,
|
|
- state->lock_flav,
|
|
- &state->blocker_pid,
|
|
- &state->blocker_smblctx);
|
|
-
|
|
- TALLOC_FREE(br_lck);
|
|
-}
|
|
-
|
|
-NTSTATUS do_lock(files_struct *fsp,
|
|
+NTSTATUS do_lock(struct byte_range_lock *br_lck,
|
|
TALLOC_CTX *req_mem_ctx,
|
|
const struct GUID *req_guid,
|
|
uint64_t smblctx,
|
|
@@ -293,22 +248,13 @@ NTSTATUS do_lock(files_struct *fsp,
|
|
struct server_id *pblocker_pid,
|
|
uint64_t *psmblctx)
|
|
{
|
|
- struct do_lock_state state = {
|
|
- .fsp = fsp,
|
|
- .req_mem_ctx = req_mem_ctx,
|
|
- .req_guid = req_guid,
|
|
- .smblctx = smblctx,
|
|
- .count = count,
|
|
- .offset = offset,
|
|
- .lock_type = lock_type,
|
|
- .lock_flav = lock_flav,
|
|
- };
|
|
+ files_struct *fsp = brl_fsp(br_lck);
|
|
+ struct server_id blocker_pid;
|
|
+ uint64_t blocker_smblctx;
|
|
NTSTATUS status;
|
|
|
|
- /* silently return ok on print files as we don't do locking there */
|
|
- if (fsp->print_file) {
|
|
- return NT_STATUS_OK;
|
|
- }
|
|
+ SMB_ASSERT(req_mem_ctx != NULL);
|
|
+ SMB_ASSERT(req_guid != NULL);
|
|
|
|
if (!fsp->fsp_flags.can_lock) {
|
|
if (fsp->fsp_flags.is_directory) {
|
|
@@ -332,25 +278,27 @@ NTSTATUS do_lock(files_struct *fsp,
|
|
fsp_fnum_dbg(fsp),
|
|
fsp_str_dbg(fsp));
|
|
|
|
- status = share_mode_do_locked_vfs_allowed(fsp->file_id,
|
|
- do_lock_fn,
|
|
- &state);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- DBG_DEBUG("share_mode_do_locked returned %s\n",
|
|
- nt_errstr(status));
|
|
- return status;
|
|
- }
|
|
- if (!NT_STATUS_IS_OK(state.status)) {
|
|
- DBG_DEBUG("do_lock_fn returned %s\n",
|
|
- nt_errstr(state.status));
|
|
+ brl_req_set(br_lck, req_mem_ctx, req_guid);
|
|
+ status = brl_lock(br_lck,
|
|
+ smblctx,
|
|
+ messaging_server_id(fsp->conn->sconn->msg_ctx),
|
|
+ offset,
|
|
+ count,
|
|
+ lock_type,
|
|
+ lock_flav,
|
|
+ &blocker_pid,
|
|
+ &blocker_smblctx);
|
|
+ brl_req_set(br_lck, NULL, NULL);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_DEBUG("brl_lock failed: %s\n", nt_errstr(status));
|
|
if (psmblctx != NULL) {
|
|
- *psmblctx = state.blocker_smblctx;
|
|
+ *psmblctx = blocker_smblctx;
|
|
}
|
|
if (pblocker_pid != NULL) {
|
|
- *pblocker_pid = state.blocker_pid;
|
|
+ *pblocker_pid = blocker_pid;
|
|
}
|
|
- return state.status;
|
|
- }
|
|
+ return status;
|
|
+ }
|
|
|
|
increment_current_lock_count(fsp, lock_flav);
|
|
|
|
@@ -361,14 +309,14 @@ NTSTATUS do_lock(files_struct *fsp,
|
|
Utility function called by unlocking requests.
|
|
****************************************************************************/
|
|
|
|
-NTSTATUS do_unlock(files_struct *fsp,
|
|
+NTSTATUS do_unlock(struct byte_range_lock *br_lck,
|
|
uint64_t smblctx,
|
|
uint64_t count,
|
|
uint64_t offset,
|
|
enum brl_flavour lock_flav)
|
|
{
|
|
+ files_struct *fsp = brl_fsp(br_lck);
|
|
bool ok = False;
|
|
- struct byte_range_lock *br_lck = NULL;
|
|
|
|
if (!fsp->fsp_flags.can_lock) {
|
|
return fsp->fsp_flags.is_directory ?
|
|
@@ -387,11 +335,6 @@ NTSTATUS do_unlock(files_struct *fsp,
|
|
fsp_fnum_dbg(fsp),
|
|
fsp_str_dbg(fsp));
|
|
|
|
- br_lck = brl_get_locks(talloc_tos(), fsp);
|
|
- if (!br_lck) {
|
|
- return NT_STATUS_NO_MEMORY;
|
|
- }
|
|
-
|
|
ok = brl_unlock(br_lck,
|
|
smblctx,
|
|
messaging_server_id(fsp->conn->sconn->msg_ctx),
|
|
@@ -399,8 +342,6 @@ NTSTATUS do_unlock(files_struct *fsp,
|
|
count,
|
|
lock_flav);
|
|
|
|
- TALLOC_FREE(br_lck);
|
|
-
|
|
if (!ok) {
|
|
DEBUG(10,("do_unlock: returning ERRlock.\n" ));
|
|
return NT_STATUS_RANGE_NOT_LOCKED;
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index c74539c8161..e332abf34ec 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -120,7 +120,7 @@ NTSTATUS query_lock(files_struct *fsp,
|
|
uint64_t *poffset,
|
|
enum brl_type *plock_type,
|
|
enum brl_flavour lock_flav);
|
|
-NTSTATUS do_lock(files_struct *fsp,
|
|
+NTSTATUS do_lock(struct byte_range_lock *br_lck,
|
|
TALLOC_CTX *req_mem_ctx,
|
|
const struct GUID *req_guid,
|
|
uint64_t smblctx,
|
|
@@ -130,7 +130,7 @@ NTSTATUS do_lock(files_struct *fsp,
|
|
enum brl_flavour lock_flav,
|
|
struct server_id *pblocker_pid,
|
|
uint64_t *psmblctx);
|
|
-NTSTATUS do_unlock(files_struct *fsp,
|
|
+NTSTATUS do_unlock(struct byte_range_lock *br_lck,
|
|
uint64_t smblctx,
|
|
uint64_t count,
|
|
uint64_t offset,
|
|
diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c
|
|
index 49a1723864e..caaf1f73856 100644
|
|
--- a/source3/modules/vfs_fruit.c
|
|
+++ b/source3/modules/vfs_fruit.c
|
|
@@ -622,11 +622,21 @@ static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
|
|
return false;
|
|
}
|
|
|
|
-static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
- files_struct *fsp,
|
|
- uint32_t access_mask,
|
|
- uint32_t share_mode)
|
|
+struct check_access_state {
|
|
+ NTSTATUS status;
|
|
+ files_struct *fsp;
|
|
+ uint32_t access_mask;
|
|
+ uint32_t share_mode;
|
|
+};
|
|
+
|
|
+static void fruit_check_access(struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck,
|
|
+ void *private_data)
|
|
{
|
|
+ struct check_access_state *state = private_data;
|
|
+ files_struct *fsp = state->fsp;
|
|
+ uint32_t access_mask = state->access_mask;
|
|
+ uint32_t share_mode = state->share_mode;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
off_t off;
|
|
bool share_for_read = (share_mode & FILE_SHARE_READ);
|
|
@@ -640,6 +650,14 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
/* FIXME: hardcoded data fork, add resource fork */
|
|
enum apple_fork fork_type = APPLE_FORK_DATA;
|
|
|
|
+ /*
|
|
+ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so
|
|
+ * br_lck has to be there!
|
|
+ */
|
|
+ SMB_ASSERT(br_lck != NULL);
|
|
+
|
|
+ state->status = NT_STATUS_OK;
|
|
+
|
|
DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
|
|
fsp_str_dbg(fsp),
|
|
access_mask & FILE_READ_DATA ? "READ" :"-",
|
|
@@ -647,7 +665,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
share_mode);
|
|
|
|
if (fsp_get_io_fd(fsp) == -1) {
|
|
- return NT_STATUS_OK;
|
|
+ return;
|
|
}
|
|
|
|
/* Read NetATalk opens and deny modes on the file. */
|
|
@@ -670,22 +688,26 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
/* If there are any conflicts - sharing violation. */
|
|
if ((access_mask & FILE_READ_DATA) &&
|
|
netatalk_already_open_with_deny_read) {
|
|
- return NT_STATUS_SHARING_VIOLATION;
|
|
+ state->status = NT_STATUS_SHARING_VIOLATION;
|
|
+ return;
|
|
}
|
|
|
|
if (!share_for_read &&
|
|
netatalk_already_open_for_reading) {
|
|
- return NT_STATUS_SHARING_VIOLATION;
|
|
+ state->status = NT_STATUS_SHARING_VIOLATION;
|
|
+ return;
|
|
}
|
|
|
|
if ((access_mask & FILE_WRITE_DATA) &&
|
|
netatalk_already_open_with_deny_write) {
|
|
- return NT_STATUS_SHARING_VIOLATION;
|
|
+ state->status = NT_STATUS_SHARING_VIOLATION;
|
|
+ return;
|
|
}
|
|
|
|
if (!share_for_write &&
|
|
netatalk_already_open_for_writing) {
|
|
- return NT_STATUS_SHARING_VIOLATION;
|
|
+ state->status = NT_STATUS_SHARING_VIOLATION;
|
|
+ return;
|
|
}
|
|
|
|
if (!(access_mask & FILE_READ_DATA)) {
|
|
@@ -693,15 +715,16 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
* Nothing we can do here, we need read access
|
|
* to set locks.
|
|
*/
|
|
- return NT_STATUS_OK;
|
|
+ return;
|
|
}
|
|
|
|
/* Set NetAtalk locks matching our access */
|
|
if (access_mask & FILE_READ_DATA) {
|
|
off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
|
|
req_guid.time_hi_and_version = __LINE__;
|
|
+
|
|
status = do_lock(
|
|
- fsp,
|
|
+ br_lck,
|
|
talloc_tos(),
|
|
&req_guid,
|
|
fsp->op->global->open_persistent_id,
|
|
@@ -711,17 +734,18 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
POSIX_LOCK,
|
|
NULL,
|
|
NULL);
|
|
-
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- return status;
|
|
+ state->status = status;
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
if (!share_for_read) {
|
|
off = denymode_to_netatalk_brl(fork_type, DENY_READ);
|
|
req_guid.time_hi_and_version = __LINE__;
|
|
+
|
|
status = do_lock(
|
|
- fsp,
|
|
+ br_lck,
|
|
talloc_tos(),
|
|
&req_guid,
|
|
fsp->op->global->open_persistent_id,
|
|
@@ -731,17 +755,18 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
POSIX_LOCK,
|
|
NULL,
|
|
NULL);
|
|
-
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- return status;
|
|
+ state->status = status;
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
if (access_mask & FILE_WRITE_DATA) {
|
|
off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
|
|
req_guid.time_hi_and_version = __LINE__;
|
|
+
|
|
status = do_lock(
|
|
- fsp,
|
|
+ br_lck,
|
|
talloc_tos(),
|
|
&req_guid,
|
|
fsp->op->global->open_persistent_id,
|
|
@@ -751,17 +776,18 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
POSIX_LOCK,
|
|
NULL,
|
|
NULL);
|
|
-
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- return status;
|
|
+ state->status = status;
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
if (!share_for_write) {
|
|
off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
|
|
req_guid.time_hi_and_version = __LINE__;
|
|
+
|
|
status = do_lock(
|
|
- fsp,
|
|
+ br_lck,
|
|
talloc_tos(),
|
|
&req_guid,
|
|
fsp->op->global->open_persistent_id,
|
|
@@ -771,13 +797,11 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
|
|
POSIX_LOCK,
|
|
NULL,
|
|
NULL);
|
|
-
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- return status;
|
|
+ state->status = status;
|
|
+ return;
|
|
}
|
|
}
|
|
-
|
|
- return NT_STATUS_OK;
|
|
}
|
|
|
|
static NTSTATUS check_aapl(vfs_handle_struct *handle,
|
|
@@ -4346,16 +4370,27 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
|
|
}
|
|
|
|
if ((config->locking == FRUIT_LOCKING_NETATALK) &&
|
|
+ lp_locking(fsp->conn->params) &&
|
|
+ fsp->fsp_flags.can_lock &&
|
|
(fsp->op != NULL) &&
|
|
!fsp->fsp_flags.is_pathref)
|
|
{
|
|
- status = fruit_check_access(
|
|
- handle, *result,
|
|
- access_mask,
|
|
- share_access);
|
|
+ struct check_access_state state = (struct check_access_state) {
|
|
+ .fsp = fsp,
|
|
+ .access_mask = access_mask,
|
|
+ .share_mode = share_access,
|
|
+ };
|
|
+
|
|
+ status = share_mode_do_locked_brl(fsp,
|
|
+ fruit_check_access,
|
|
+ &state);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
+ if (!NT_STATUS_IS_OK(state.status)) {
|
|
+ status = state.status;
|
|
+ goto fail;
|
|
+ }
|
|
}
|
|
|
|
return status;
|
|
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c
|
|
index 8b41288bfbf..c9d9aff98b7 100644
|
|
--- a/source3/smbd/blocking.c
|
|
+++ b/source3/smbd/blocking.c
|
|
@@ -29,31 +29,27 @@
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS DBGC_LOCKING
|
|
|
|
-NTSTATUS smbd_do_locks_try(
|
|
- struct files_struct *fsp,
|
|
- uint16_t num_locks,
|
|
- struct smbd_lock_element *locks,
|
|
- uint16_t *blocker_idx,
|
|
- struct server_id *blocking_pid,
|
|
- uint64_t *blocking_smblctx)
|
|
+NTSTATUS smbd_do_locks_try(struct byte_range_lock *br_lck,
|
|
+ struct smbd_do_locks_state *state)
|
|
{
|
|
- NTSTATUS status = NT_STATUS_OK;
|
|
+ bool unlock_ok;
|
|
uint16_t i;
|
|
+ NTSTATUS status = NT_STATUS_OK;
|
|
|
|
- for (i=0; i<num_locks; i++) {
|
|
- struct smbd_lock_element *e = &locks[i];
|
|
+ for (i = 0; i < state->num_locks; i++) {
|
|
+ struct smbd_lock_element *e = &state->locks[i];
|
|
|
|
status = do_lock(
|
|
- fsp,
|
|
- locks, /* req_mem_ctx */
|
|
+ br_lck,
|
|
+ state->locks, /* req_mem_ctx */
|
|
&e->req_guid,
|
|
e->smblctx,
|
|
e->count,
|
|
e->offset,
|
|
e->brltype,
|
|
e->lock_flav,
|
|
- blocking_pid,
|
|
- blocking_smblctx);
|
|
+ &state->blocking_pid,
|
|
+ &state->blocking_smblctx);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
break;
|
|
}
|
|
@@ -63,18 +59,35 @@ NTSTATUS smbd_do_locks_try(
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
- *blocker_idx = i;
|
|
+ state->blocker_idx = i;
|
|
+ unlock_ok = true;
|
|
|
|
/*
|
|
* Undo the locks we successfully got
|
|
*/
|
|
for (i = i-1; i != UINT16_MAX; i--) {
|
|
- struct smbd_lock_element *e = &locks[i];
|
|
- do_unlock(fsp,
|
|
- e->smblctx,
|
|
- e->count,
|
|
- e->offset,
|
|
- e->lock_flav);
|
|
+ struct smbd_lock_element *e = &state->locks[i];
|
|
+ NTSTATUS ulstatus;
|
|
+
|
|
+ ulstatus = do_unlock(br_lck,
|
|
+ e->smblctx,
|
|
+ e->count,
|
|
+ e->offset,
|
|
+ e->lock_flav);
|
|
+ if (!NT_STATUS_IS_OK(ulstatus)) {
|
|
+ DBG_DEBUG("Failed to undo lock flavour %s lock "
|
|
+ "type %s start=%"PRIu64" len=%"PRIu64" "
|
|
+ "requested for file [%s]\n",
|
|
+ lock_flav_name(e->lock_flav),
|
|
+ lock_type_name(e->brltype),
|
|
+ e->offset,
|
|
+ e->count,
|
|
+ fsp_str_dbg(brl_fsp(br_lck)));
|
|
+ unlock_ok = false;
|
|
+ }
|
|
+ }
|
|
+ if (unlock_ok) {
|
|
+ brl_set_modified(br_lck, false);
|
|
}
|
|
|
|
return status;
|
|
@@ -118,13 +131,6 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req);
|
|
static void smbd_smb1_do_locks_retry(struct tevent_req *subreq);
|
|
static void smbd_smb1_blocked_locks_cleanup(
|
|
struct tevent_req *req, enum tevent_req_state req_state);
|
|
-static NTSTATUS smbd_smb1_do_locks_check(
|
|
- struct files_struct *fsp,
|
|
- uint16_t num_locks,
|
|
- struct smbd_lock_element *locks,
|
|
- uint16_t *blocker_idx,
|
|
- struct server_id *blocking_pid,
|
|
- uint64_t *blocking_smblctx);
|
|
|
|
static void smbd_smb1_do_locks_setup_timeout(
|
|
struct smbd_smb1_do_locks_state *state,
|
|
@@ -378,18 +384,35 @@ static NTSTATUS smbd_smb1_do_locks_check_blocked(
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
-static NTSTATUS smbd_smb1_do_locks_check(
|
|
- struct files_struct *fsp,
|
|
- uint16_t num_locks,
|
|
- struct smbd_lock_element *locks,
|
|
- uint16_t *blocker_idx,
|
|
- struct server_id *blocking_pid,
|
|
- uint64_t *blocking_smblctx)
|
|
+static void smbd_smb1_do_locks_try_fn(struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck,
|
|
+ void *private_data)
|
|
{
|
|
+ struct tevent_req *req = talloc_get_type_abort(
|
|
+ private_data, struct tevent_req);
|
|
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
|
|
+ req, struct smbd_smb1_do_locks_state);
|
|
+ struct smbd_do_locks_state brl_state;
|
|
+ struct files_struct *fsp = state->fsp;
|
|
struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs;
|
|
size_t num_blocked = talloc_array_length(blocked);
|
|
- NTSTATUS status;
|
|
+ struct timeval endtime = { 0 };
|
|
+ struct tevent_req *subreq = NULL;
|
|
size_t bi;
|
|
+ NTSTATUS status;
|
|
+ bool ok;
|
|
+ bool expired;
|
|
+
|
|
+ /*
|
|
+ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so
|
|
+ * br_lck has to be there!
|
|
+ */
|
|
+ SMB_ASSERT(br_lck != NULL);
|
|
+
|
|
+ brl_state = (struct smbd_do_locks_state) {
|
|
+ .num_locks = state->num_locks,
|
|
+ .locks = state->locks,
|
|
+ };
|
|
|
|
/*
|
|
* We check the pending/blocked requests
|
|
@@ -404,8 +427,8 @@ static NTSTATUS smbd_smb1_do_locks_check(
|
|
tevent_req_data(blocked[bi],
|
|
struct smbd_smb1_do_locks_state);
|
|
|
|
- if (blocked_state->locks == locks) {
|
|
- SMB_ASSERT(blocked_state->num_locks == num_locks);
|
|
+ if (blocked_state->locks == state->locks) {
|
|
+ SMB_ASSERT(blocked_state->num_locks == state->num_locks);
|
|
|
|
/*
|
|
* We found ourself...
|
|
@@ -416,61 +439,24 @@ static NTSTATUS smbd_smb1_do_locks_check(
|
|
status = smbd_smb1_do_locks_check_blocked(
|
|
blocked_state->num_locks,
|
|
blocked_state->locks,
|
|
- num_locks,
|
|
- locks,
|
|
- blocker_idx,
|
|
- blocking_smblctx);
|
|
+ state->num_locks,
|
|
+ state->locks,
|
|
+ &brl_state.blocker_idx,
|
|
+ &brl_state.blocking_smblctx);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- *blocking_pid = messaging_server_id(
|
|
- fsp->conn->sconn->msg_ctx);
|
|
- return status;
|
|
+ brl_state.blocking_pid = messaging_server_id(
|
|
+ fsp->conn->sconn->msg_ctx);
|
|
+ goto check_retry;
|
|
}
|
|
}
|
|
|
|
- status = smbd_do_locks_try(
|
|
- fsp,
|
|
- num_locks,
|
|
- locks,
|
|
- blocker_idx,
|
|
- blocking_pid,
|
|
- blocking_smblctx);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- return status;
|
|
- }
|
|
-
|
|
- return NT_STATUS_OK;
|
|
-}
|
|
-
|
|
-static void smbd_smb1_do_locks_try(struct tevent_req *req)
|
|
-{
|
|
- struct smbd_smb1_do_locks_state *state = tevent_req_data(
|
|
- req, struct smbd_smb1_do_locks_state);
|
|
- struct files_struct *fsp = state->fsp;
|
|
- struct share_mode_lock *lck;
|
|
- struct timeval endtime = { 0 };
|
|
- struct server_id blocking_pid = { 0 };
|
|
- uint64_t blocking_smblctx = 0;
|
|
- struct tevent_req *subreq = NULL;
|
|
- NTSTATUS status;
|
|
- bool ok;
|
|
- bool expired;
|
|
-
|
|
- lck = get_existing_share_mode_lock(state, fsp->file_id);
|
|
- if (tevent_req_nomem(lck, req)) {
|
|
- DBG_DEBUG("Could not get share mode lock\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- status = smbd_smb1_do_locks_check(
|
|
- fsp,
|
|
- state->num_locks,
|
|
- state->locks,
|
|
- &state->blocker,
|
|
- &blocking_pid,
|
|
- &blocking_smblctx);
|
|
+ status = smbd_do_locks_try(br_lck, &brl_state);
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
goto done;
|
|
}
|
|
+
|
|
+ state->blocker = brl_state.blocker_idx;
|
|
+
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
|
|
/*
|
|
* We got NT_STATUS_RETRY,
|
|
@@ -494,8 +480,8 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req)
|
|
* locking.tdb may cause retries.
|
|
*/
|
|
|
|
- if (blocking_smblctx != UINT64_MAX) {
|
|
- SMB_ASSERT(blocking_smblctx == 0);
|
|
+ if (brl_state.blocking_smblctx != UINT64_MAX) {
|
|
+ SMB_ASSERT(brl_state.blocking_smblctx == 0);
|
|
goto setup_retry;
|
|
}
|
|
|
|
@@ -516,6 +502,8 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req)
|
|
endtime = timeval_current_ofs_msec(state->retry_msecs);
|
|
goto setup_retry;
|
|
}
|
|
+
|
|
+check_retry:
|
|
if (!ERROR_WAS_LOCK_DENIED(status)) {
|
|
goto done;
|
|
}
|
|
@@ -529,7 +517,7 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req)
|
|
smbd_smb1_do_locks_setup_timeout(state, &state->locks[state->blocker]);
|
|
DBG_DEBUG("timeout=%"PRIu32", blocking_smblctx=%"PRIu64"\n",
|
|
state->timeout,
|
|
- blocking_smblctx);
|
|
+ brl_state.blocking_smblctx);
|
|
|
|
/*
|
|
* The client specified timeout expired
|
|
@@ -554,7 +542,7 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req)
|
|
|
|
endtime = state->endtime;
|
|
|
|
- if (blocking_smblctx == UINT64_MAX) {
|
|
+ if (brl_state.blocking_smblctx == UINT64_MAX) {
|
|
struct timeval tmp;
|
|
|
|
smbd_smb1_do_locks_update_polling_msecs(state);
|
|
@@ -568,11 +556,11 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req)
|
|
|
|
setup_retry:
|
|
subreq = share_mode_watch_send(
|
|
- state, state->ev, lck, blocking_pid);
|
|
+ state, state->ev, lck, brl_state.blocking_pid);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
+ status = NT_STATUS_NO_MEMORY;
|
|
goto done;
|
|
}
|
|
- TALLOC_FREE(lck);
|
|
tevent_req_set_callback(subreq, smbd_smb1_do_locks_retry, req);
|
|
|
|
if (timeval_is_zero(&endtime)) {
|
|
@@ -586,10 +574,38 @@ setup_retry:
|
|
}
|
|
return;
|
|
done:
|
|
- TALLOC_FREE(lck);
|
|
smbd_smb1_brl_finish_by_req(req, status);
|
|
}
|
|
|
|
+static void smbd_smb1_do_locks_try(struct tevent_req *req)
|
|
+{
|
|
+ struct smbd_smb1_do_locks_state *state = tevent_req_data(
|
|
+ req, struct smbd_smb1_do_locks_state);
|
|
+ NTSTATUS status;
|
|
+
|
|
+ if (!state->fsp->fsp_flags.can_lock) {
|
|
+ if (state->fsp->fsp_flags.is_directory) {
|
|
+ return smbd_smb1_brl_finish_by_req(req,
|
|
+ NT_STATUS_INVALID_DEVICE_REQUEST);
|
|
+ }
|
|
+ return smbd_smb1_brl_finish_by_req(req,
|
|
+ NT_STATUS_INVALID_HANDLE);
|
|
+ }
|
|
+
|
|
+ if (!lp_locking(state->fsp->conn->params)) {
|
|
+ return smbd_smb1_brl_finish_by_req(req, NT_STATUS_OK);
|
|
+ }
|
|
+
|
|
+ status = share_mode_do_locked_brl(state->fsp,
|
|
+ smbd_smb1_do_locks_try_fn,
|
|
+ req);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ smbd_smb1_brl_finish_by_req(req, status);
|
|
+ return;
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
static void smbd_smb1_do_locks_retry(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
|
|
index 78e1b48be09..b8bb8eb293c 100644
|
|
--- a/source3/smbd/proto.h
|
|
+++ b/source3/smbd/proto.h
|
|
@@ -86,13 +86,18 @@ ssize_t pwrite_fsync_recv(struct tevent_req *req, int *perr);
|
|
|
|
/* The following definitions come from smbd/blocking.c */
|
|
|
|
-NTSTATUS smbd_do_locks_try(
|
|
- struct files_struct *fsp,
|
|
- uint16_t num_locks,
|
|
- struct smbd_lock_element *locks,
|
|
- uint16_t *blocker_idx,
|
|
- struct server_id *blocking_pid,
|
|
- uint64_t *blocking_smblctx);
|
|
+struct smbd_do_locks_state {
|
|
+ uint16_t num_locks;
|
|
+ struct smbd_lock_element *locks;
|
|
+ NTSTATUS status;
|
|
+ uint16_t blocker_idx;
|
|
+ struct server_id blocking_pid;
|
|
+ uint64_t blocking_smblctx;
|
|
+};
|
|
+
|
|
+NTSTATUS smbd_do_locks_try(struct byte_range_lock *br_lck,
|
|
+ struct smbd_do_locks_state *state);
|
|
+
|
|
struct tevent_req *smbd_smb1_do_locks_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
diff --git a/source3/smbd/smb2_lock.c b/source3/smbd/smb2_lock.c
|
|
index c9d810f71ba..14f912bf2da 100644
|
|
--- a/source3/smbd/smb2_lock.c
|
|
+++ b/source3/smbd/smb2_lock.c
|
|
@@ -569,33 +569,32 @@ static void smbd_smb2_lock_update_polling_msecs(
|
|
state->polling_msecs += v_min;
|
|
}
|
|
|
|
-static void smbd_smb2_lock_try(struct tevent_req *req)
|
|
+static void smbd_do_locks_try_fn(struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck,
|
|
+ void *private_data)
|
|
{
|
|
+ struct tevent_req *req = talloc_get_type_abort(
|
|
+ private_data, struct tevent_req);
|
|
struct smbd_smb2_lock_state *state = tevent_req_data(
|
|
req, struct smbd_smb2_lock_state);
|
|
- struct share_mode_lock *lck = NULL;
|
|
- uint16_t blocker_idx;
|
|
- struct server_id blocking_pid = { 0 };
|
|
- uint64_t blocking_smblctx;
|
|
- NTSTATUS status;
|
|
+ struct smbd_do_locks_state brl_state;
|
|
struct tevent_req *subreq = NULL;
|
|
struct timeval endtime = { 0 };
|
|
+ NTSTATUS status;
|
|
|
|
- lck = get_existing_share_mode_lock(
|
|
- talloc_tos(), state->fsp->file_id);
|
|
- if (tevent_req_nomem(lck, req)) {
|
|
- return;
|
|
- }
|
|
+ /*
|
|
+ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so
|
|
+ * br_lck has to be there!
|
|
+ */
|
|
+ SMB_ASSERT(br_lck != NULL);
|
|
|
|
- status = smbd_do_locks_try(
|
|
- state->fsp,
|
|
- state->lock_count,
|
|
- state->locks,
|
|
- &blocker_idx,
|
|
- &blocking_pid,
|
|
- &blocking_smblctx);
|
|
+ brl_state = (struct smbd_do_locks_state) {
|
|
+ .num_locks = state->lock_count,
|
|
+ .locks = state->locks,
|
|
+ };
|
|
+
|
|
+ status = smbd_do_locks_try(br_lck, &brl_state);
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
- TALLOC_FREE(lck);
|
|
tevent_req_done(req);
|
|
return;
|
|
}
|
|
@@ -622,8 +621,8 @@ static void smbd_smb2_lock_try(struct tevent_req *req)
|
|
* locking.tdb may cause retries.
|
|
*/
|
|
|
|
- if (blocking_smblctx != UINT64_MAX) {
|
|
- SMB_ASSERT(blocking_smblctx == 0);
|
|
+ if (brl_state.blocking_smblctx != UINT64_MAX) {
|
|
+ SMB_ASSERT(brl_state.blocking_smblctx == 0);
|
|
goto setup_retry;
|
|
}
|
|
|
|
@@ -658,7 +657,6 @@ static void smbd_smb2_lock_try(struct tevent_req *req)
|
|
status = NT_STATUS_LOCK_NOT_GRANTED;
|
|
}
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
|
|
- TALLOC_FREE(lck);
|
|
tevent_req_nterror(req, status);
|
|
return;
|
|
}
|
|
@@ -670,12 +668,11 @@ static void smbd_smb2_lock_try(struct tevent_req *req)
|
|
state->retry_msecs = 0;
|
|
|
|
if (!state->blocking) {
|
|
- TALLOC_FREE(lck);
|
|
tevent_req_nterror(req, status);
|
|
return;
|
|
}
|
|
|
|
- if (blocking_smblctx == UINT64_MAX) {
|
|
+ if (brl_state.blocking_smblctx == UINT64_MAX) {
|
|
smbd_smb2_lock_update_polling_msecs(state);
|
|
|
|
DBG_DEBUG("Blocked on a posix lock. Retry in %"PRIu32" msecs\n",
|
|
@@ -688,8 +685,7 @@ setup_retry:
|
|
DBG_DEBUG("Watching share mode lock\n");
|
|
|
|
subreq = share_mode_watch_send(
|
|
- state, state->ev, lck, blocking_pid);
|
|
- TALLOC_FREE(lck);
|
|
+ state, state->ev, lck, brl_state.blocking_pid);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
@@ -708,6 +704,35 @@ setup_retry:
|
|
}
|
|
}
|
|
|
|
+static void smbd_smb2_lock_try(struct tevent_req *req)
|
|
+{
|
|
+ struct smbd_smb2_lock_state *state = tevent_req_data(
|
|
+ req, struct smbd_smb2_lock_state);
|
|
+ NTSTATUS status;
|
|
+
|
|
+ if (!state->fsp->fsp_flags.can_lock) {
|
|
+ if (state->fsp->fsp_flags.is_directory) {
|
|
+ tevent_req_nterror(req,
|
|
+ NT_STATUS_INVALID_DEVICE_REQUEST);
|
|
+ return;
|
|
+ }
|
|
+ tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!lp_locking(state->fsp->conn->params)) {
|
|
+ return tevent_req_done(req);
|
|
+ }
|
|
+
|
|
+ status = share_mode_do_locked_brl(state->fsp,
|
|
+ smbd_do_locks_try_fn,
|
|
+ req);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ tevent_req_nterror(req, status);
|
|
+ return;
|
|
+ }
|
|
+}
|
|
+
|
|
static void smbd_smb2_lock_retry(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
diff --git a/source3/smbd/smb2_reply.c b/source3/smbd/smb2_reply.c
|
|
index dfcd05d2cae..c782a624d7f 100644
|
|
--- a/source3/smbd/smb2_reply.c
|
|
+++ b/source3/smbd/smb2_reply.c
|
|
@@ -2113,23 +2113,22 @@ uint64_t get_lock_offset(const uint8_t *data, int data_offset,
|
|
return offset;
|
|
}
|
|
|
|
-struct smbd_do_unlocking_state {
|
|
- struct files_struct *fsp;
|
|
- uint16_t num_ulocks;
|
|
- struct smbd_lock_element *ulocks;
|
|
- NTSTATUS status;
|
|
-};
|
|
-
|
|
-static void smbd_do_unlocking_fn(
|
|
- struct share_mode_lock *lck,
|
|
- void *private_data)
|
|
+static void smbd_do_unlocking_fn(struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck,
|
|
+ void *private_data)
|
|
{
|
|
- struct smbd_do_unlocking_state *state = private_data;
|
|
- struct files_struct *fsp = state->fsp;
|
|
+ struct smbd_do_locks_state *state = private_data;
|
|
+ struct files_struct *fsp = brl_fsp(br_lck);
|
|
uint16_t i;
|
|
|
|
- for (i = 0; i < state->num_ulocks; i++) {
|
|
- struct smbd_lock_element *e = &state->ulocks[i];
|
|
+ /*
|
|
+ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so
|
|
+ * br_lck has to be there!
|
|
+ */
|
|
+ SMB_ASSERT(br_lck != NULL);
|
|
+
|
|
+ for (i = 0; i < state->num_locks; i++) {
|
|
+ struct smbd_lock_element *e = &state->locks[i];
|
|
|
|
DBG_DEBUG("unlock start=%"PRIu64", len=%"PRIu64" for "
|
|
"pid %"PRIu64", file %s\n",
|
|
@@ -2145,7 +2144,7 @@ static void smbd_do_unlocking_fn(
|
|
}
|
|
|
|
state->status = do_unlock(
|
|
- fsp, e->smblctx, e->count, e->offset, e->lock_flav);
|
|
+ br_lck, e->smblctx, e->count, e->offset, e->lock_flav);
|
|
|
|
DBG_DEBUG("do_unlock returned %s\n",
|
|
nt_errstr(state->status));
|
|
@@ -2163,20 +2162,30 @@ NTSTATUS smbd_do_unlocking(struct smb_request *req,
|
|
uint16_t num_ulocks,
|
|
struct smbd_lock_element *ulocks)
|
|
{
|
|
- struct smbd_do_unlocking_state state = {
|
|
- .fsp = fsp,
|
|
- .num_ulocks = num_ulocks,
|
|
- .ulocks = ulocks,
|
|
+ struct smbd_do_locks_state state = {
|
|
+ .num_locks = num_ulocks,
|
|
+ .locks = ulocks,
|
|
};
|
|
NTSTATUS status;
|
|
|
|
DBG_NOTICE("%s num_ulocks=%"PRIu16"\n", fsp_fnum_dbg(fsp), num_ulocks);
|
|
|
|
- status = share_mode_do_locked_vfs_allowed(
|
|
- fsp->file_id, smbd_do_unlocking_fn, &state);
|
|
+ if (!fsp->fsp_flags.can_lock) {
|
|
+ if (fsp->fsp_flags.is_directory) {
|
|
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
|
|
+ }
|
|
+ return NT_STATUS_INVALID_HANDLE;
|
|
+ }
|
|
+
|
|
+ if (!lp_locking(fsp->conn->params)) {
|
|
+ return NT_STATUS_OK;
|
|
+ }
|
|
|
|
+ status = share_mode_do_locked_brl(fsp,
|
|
+ smbd_do_unlocking_fn,
|
|
+ &state);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
- DBG_DEBUG("share_mode_do_locked_vfs_allowed failed: %s\n",
|
|
+ DBG_DEBUG("share_mode_do_locked_brl failed: %s\n",
|
|
nt_errstr(status));
|
|
return status;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e067ece27012e10a5a49cf0f757fcfb37bc97450 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Wed, 8 Jan 2025 12:51:37 +0100
|
|
Subject: [PATCH 089/122] s3/brlock: remove brl_get_locks_for_locking()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Pair-Programmed-With: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 0c4c430c50e15d591a0d871a5f3e59e8be0d0a83)
|
|
---
|
|
source3/locking/brlock.c | 19 -------------------
|
|
source3/locking/proto.h | 4 ----
|
|
2 files changed, 23 deletions(-)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index e0d18800cf1..592792c5303 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -1824,25 +1824,6 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp)
|
|
return br_lck;
|
|
}
|
|
|
|
-struct byte_range_lock *brl_get_locks_for_locking(TALLOC_CTX *mem_ctx,
|
|
- files_struct *fsp,
|
|
- TALLOC_CTX *req_mem_ctx,
|
|
- const struct GUID *req_guid)
|
|
-{
|
|
- struct byte_range_lock *br_lck = NULL;
|
|
-
|
|
- br_lck = brl_get_locks(mem_ctx, fsp);
|
|
- if (br_lck == NULL) {
|
|
- return NULL;
|
|
- }
|
|
- SMB_ASSERT(req_mem_ctx != NULL);
|
|
- br_lck->req_mem_ctx = req_mem_ctx;
|
|
- SMB_ASSERT(req_guid != NULL);
|
|
- br_lck->req_guid = req_guid;
|
|
-
|
|
- return br_lck;
|
|
-}
|
|
-
|
|
struct brl_get_locks_readonly_state {
|
|
TALLOC_CTX *mem_ctx;
|
|
struct byte_range_lock **br_lock;
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index e332abf34ec..44b43c1b1e2 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -83,10 +83,6 @@ int brl_forall(void (*fn)(struct file_id id, struct server_id pid,
|
|
br_off start, br_off size,
|
|
void *private_data),
|
|
void *private_data);
|
|
-struct byte_range_lock *brl_get_locks_for_locking(TALLOC_CTX *mem_ctx,
|
|
- files_struct *fsp,
|
|
- TALLOC_CTX *req_mem_ctx,
|
|
- const struct GUID *req_guid);
|
|
struct share_mode_lock;
|
|
typedef void (*share_mode_do_locked_brl_fn_t)(
|
|
struct share_mode_lock *lck,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 79331346ad6305470c67710e7a6189442ca491f1 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Mon, 27 Jan 2025 15:22:26 +0100
|
|
Subject: [PATCH 090/122] smbd: call locking_close_file() while still holding a
|
|
glock on the locking.tdb record
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 4d680b6c17ee7674b9686aec2b69038f89e1989a)
|
|
---
|
|
source3/smbd/close.c | 26 +++++++++++++++++++-------
|
|
1 file changed, 19 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
|
|
index e16cb2d3485..f36e699c6ea 100644
|
|
--- a/source3/smbd/close.c
|
|
+++ b/source3/smbd/close.c
|
|
@@ -303,6 +303,17 @@ static void close_share_mode_lock_prepare(struct share_mode_lock *lck,
|
|
*/
|
|
*keep_locked = false;
|
|
|
|
+ if (fsp->current_lock_count > 0) {
|
|
+ /*
|
|
+ * Remove the byte-range locks under the glock
|
|
+ */
|
|
+ *keep_locked = true;
|
|
+ }
|
|
+
|
|
+ if (fh_get_refcount(fsp->fh) > 1) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
if (fsp->oplock_type != NO_OPLOCK) {
|
|
ok = remove_share_oplock(lck, fsp);
|
|
if (!ok) {
|
|
@@ -453,6 +464,12 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
|
|
return status;
|
|
}
|
|
|
|
+ locking_close_file(fsp, close_type);
|
|
+
|
|
+ if (fh_get_refcount(fsp->fh) > 1) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
/* Remove the oplock before potentially deleting the file. */
|
|
if (fsp->oplock_type != NO_OPLOCK) {
|
|
release_file_oplock(fsp);
|
|
@@ -901,13 +918,8 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
|
|
the same handle we only have one share mode. Ensure we only remove
|
|
the share mode on the last close. */
|
|
|
|
- if (fh_get_refcount(fsp->fh) == 1) {
|
|
- /* Should we return on error here... ? */
|
|
- tmp = close_remove_share_mode(fsp, close_type);
|
|
- status = ntstatus_keeperror(status, tmp);
|
|
- }
|
|
-
|
|
- locking_close_file(fsp, close_type);
|
|
+ tmp = close_remove_share_mode(fsp, close_type);
|
|
+ status = ntstatus_keeperror(status, tmp);
|
|
|
|
/*
|
|
* Ensure pending modtime is set before closing underlying fd.
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 02708477b64206765976cb03e70c95a4b4daf894 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 30 Jan 2025 17:35:26 +0100
|
|
Subject: [PATCH 091/122] s3/locking: prepare brl_locktest() for upgradable
|
|
read-only locks
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 8f9387ceb5c94c7db92ab342e33c64b858c301b1)
|
|
---
|
|
source3/locking/brlock.c | 5 +++--
|
|
source3/locking/locking.c | 4 ++--
|
|
source3/locking/proto.h | 3 ++-
|
|
3 files changed, 7 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index 592792c5303..08500c4d1ac 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -1283,7 +1283,8 @@ bool brl_unlock(struct byte_range_lock *br_lck,
|
|
****************************************************************************/
|
|
|
|
bool brl_locktest(struct byte_range_lock *br_lck,
|
|
- const struct lock_struct *rw_probe)
|
|
+ const struct lock_struct *rw_probe,
|
|
+ bool upgradable)
|
|
{
|
|
bool ret = True;
|
|
unsigned int i;
|
|
@@ -1296,7 +1297,7 @@ bool brl_locktest(struct byte_range_lock *br_lck,
|
|
* Our own locks don't conflict.
|
|
*/
|
|
if (brl_conflict_other(&locks[i], rw_probe)) {
|
|
- if (br_lck->record == NULL) {
|
|
+ if (!upgradable) {
|
|
/* readonly */
|
|
return false;
|
|
}
|
|
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
|
|
index 25b3bdcf7f7..d8e70a479ad 100644
|
|
--- a/source3/locking/locking.c
|
|
+++ b/source3/locking/locking.c
|
|
@@ -143,7 +143,7 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock)
|
|
if (!br_lck) {
|
|
return true;
|
|
}
|
|
- ret = brl_locktest(br_lck, plock);
|
|
+ ret = brl_locktest(br_lck, plock, false);
|
|
|
|
if (!ret) {
|
|
/*
|
|
@@ -154,7 +154,7 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock)
|
|
if (br_lck == NULL) {
|
|
return true;
|
|
}
|
|
- ret = brl_locktest(br_lck, plock);
|
|
+ ret = brl_locktest(br_lck, plock, true);
|
|
TALLOC_FREE(br_lck);
|
|
}
|
|
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index 44b43c1b1e2..44808171f1a 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -66,7 +66,8 @@ bool brl_unlock(struct byte_range_lock *br_lck,
|
|
bool brl_unlock_windows_default(struct byte_range_lock *br_lck,
|
|
const struct lock_struct *plock);
|
|
bool brl_locktest(struct byte_range_lock *br_lck,
|
|
- const struct lock_struct *rw_probe);
|
|
+ const struct lock_struct *rw_probe,
|
|
+ bool upgradable);
|
|
NTSTATUS brl_lockquery(struct byte_range_lock *br_lck,
|
|
uint64_t *psmblctx,
|
|
struct server_id pid,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 0bf638a66c95a1f8304cb3efa284f2303445f2c0 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Wed, 2 Apr 2025 12:43:15 +0200
|
|
Subject: [PATCH 092/122] smbd: check can_lock in strict_lock_check_default()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 678f28c1af7c160ffdcb0e4baa0a7d4b9906f2e5)
|
|
---
|
|
source3/locking/locking.c | 5 ++++-
|
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
|
|
index d8e70a479ad..9bb0696946e 100644
|
|
--- a/source3/locking/locking.c
|
|
+++ b/source3/locking/locking.c
|
|
@@ -115,7 +115,10 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock)
|
|
return True;
|
|
}
|
|
|
|
- if (!lp_locking(fsp->conn->params) || !strict_locking) {
|
|
+ if (!lp_locking(fsp->conn->params) ||
|
|
+ !strict_locking ||
|
|
+ !fsp->fsp_flags.can_lock)
|
|
+ {
|
|
return True;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 936a9736ba92cf9827591a2d8279ec7953717323 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Thu, 30 Jan 2025 07:40:32 +0100
|
|
Subject: [PATCH 093/122] smbd: use share_mode_do_locked_brl() in
|
|
strict_lock_check_default()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit 56bb20c87a733ab8f7efedd881ea0ecaf51b2ba8)
|
|
---
|
|
source3/locking/locking.c | 43 +++++++++++++++++++++++++++++++++------
|
|
1 file changed, 37 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
|
|
index 9bb0696946e..400b4722379 100644
|
|
--- a/source3/locking/locking.c
|
|
+++ b/source3/locking/locking.c
|
|
@@ -105,10 +105,32 @@ void init_strict_lock_struct(files_struct *fsp,
|
|
plock->lock_flav = lp_posix_cifsu_locktype(fsp);
|
|
}
|
|
|
|
+struct strict_lock_check_state {
|
|
+ bool ret;
|
|
+ files_struct *fsp;
|
|
+ struct lock_struct *plock;
|
|
+};
|
|
+
|
|
+static void strict_lock_check_default_fn(struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck,
|
|
+ void *private_data)
|
|
+{
|
|
+ struct strict_lock_check_state *state = private_data;
|
|
+
|
|
+ /*
|
|
+ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so
|
|
+ * br_lck has to be there!
|
|
+ */
|
|
+ SMB_ASSERT(br_lck != NULL);
|
|
+
|
|
+ state->ret = brl_locktest(br_lck, state->plock, true);
|
|
+}
|
|
+
|
|
bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock)
|
|
{
|
|
struct byte_range_lock *br_lck;
|
|
int strict_locking = lp_strict_locking(fsp->conn->params);
|
|
+ NTSTATUS status;
|
|
bool ret = False;
|
|
|
|
if (plock->size == 0) {
|
|
@@ -147,18 +169,27 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock)
|
|
return true;
|
|
}
|
|
ret = brl_locktest(br_lck, plock, false);
|
|
-
|
|
if (!ret) {
|
|
/*
|
|
* We got a lock conflict. Retry with rw locks to enable
|
|
* autocleanup. This is the slow path anyway.
|
|
*/
|
|
- br_lck = brl_get_locks(talloc_tos(), fsp);
|
|
- if (br_lck == NULL) {
|
|
- return true;
|
|
+
|
|
+ struct strict_lock_check_state state =
|
|
+ (struct strict_lock_check_state) {
|
|
+ .fsp = fsp,
|
|
+ .plock = plock,
|
|
+ };
|
|
+
|
|
+ status = share_mode_do_locked_brl(fsp,
|
|
+ strict_lock_check_default_fn,
|
|
+ &state);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n",
|
|
+ fsp_str_dbg(fsp), nt_errstr(status));
|
|
+ state.ret = false;
|
|
}
|
|
- ret = brl_locktest(br_lck, plock, true);
|
|
- TALLOC_FREE(br_lck);
|
|
+ ret = state.ret;
|
|
}
|
|
|
|
DEBUG(10, ("strict_lock_default: flavour = %s brl start=%ju "
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 66794d5d5c2b930d6afde8103484333f53b2e68c Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 28 Jan 2025 11:19:05 +0100
|
|
Subject: [PATCH 094/122] smbd: use share_mode_do_locked_brl() in
|
|
vfs_default_durable_disconnect()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(backported from commit 393379fc9c726eb781fd1bfb3a70ea2802739aff)
|
|
[slow@samba.org: conflict due to removed delayed write time handling]
|
|
---
|
|
source3/locking/brlock.c | 14 +----
|
|
source3/locking/proto.h | 9 ++-
|
|
source3/smbd/durable.c | 117 ++++++++++++++++++++++++---------------
|
|
3 files changed, 82 insertions(+), 58 deletions(-)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index 08500c4d1ac..b4c8501b4de 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -1459,14 +1459,14 @@ void brl_close_fnum(struct byte_range_lock *br_lck)
|
|
}
|
|
}
|
|
|
|
-bool brl_mark_disconnected(struct files_struct *fsp)
|
|
+bool brl_mark_disconnected(struct files_struct *fsp,
|
|
+ struct byte_range_lock *br_lck)
|
|
{
|
|
uint32_t tid = fsp->conn->cnum;
|
|
uint64_t smblctx;
|
|
uint64_t fnum = fsp->fnum;
|
|
unsigned int i;
|
|
struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx);
|
|
- struct byte_range_lock *br_lck = NULL;
|
|
|
|
if (fsp->op == NULL) {
|
|
return false;
|
|
@@ -1482,11 +1482,6 @@ bool brl_mark_disconnected(struct files_struct *fsp)
|
|
return true;
|
|
}
|
|
|
|
- br_lck = brl_get_locks(talloc_tos(), fsp);
|
|
- if (br_lck == NULL) {
|
|
- return false;
|
|
- }
|
|
-
|
|
for (i=0; i < br_lck->num_locks; i++) {
|
|
struct lock_struct *lock = &br_lck->lock_data[i];
|
|
|
|
@@ -1496,22 +1491,18 @@ bool brl_mark_disconnected(struct files_struct *fsp)
|
|
*/
|
|
|
|
if (lock->context.smblctx != smblctx) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
if (lock->context.tid != tid) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
if (!server_id_equal(&lock->context.pid, &self)) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
if (lock->fnum != fnum) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
@@ -1521,7 +1512,6 @@ bool brl_mark_disconnected(struct files_struct *fsp)
|
|
}
|
|
|
|
br_lck->modified = true;
|
|
- TALLOC_FREE(br_lck);
|
|
return true;
|
|
}
|
|
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index 44808171f1a..d3b4e02bc26 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -75,7 +75,14 @@ NTSTATUS brl_lockquery(struct byte_range_lock *br_lck,
|
|
br_off *psize,
|
|
enum brl_type *plock_type,
|
|
enum brl_flavour lock_flav);
|
|
-bool brl_mark_disconnected(struct files_struct *fsp);
|
|
+
|
|
+struct brl_connectstate {
|
|
+ bool ok;
|
|
+ struct files_struct *fsp;
|
|
+};
|
|
+
|
|
+bool brl_mark_disconnected(struct files_struct *fsp,
|
|
+ struct byte_range_lock *br_lck);
|
|
bool brl_reconnect_disconnected(struct files_struct *fsp);
|
|
void brl_close_fnum(struct byte_range_lock *br_lck);
|
|
int brl_forall(void (*fn)(struct file_id id, struct server_id pid,
|
|
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
|
|
index bf3cbed0a2c..1506d37208a 100644
|
|
--- a/source3/smbd/durable.c
|
|
+++ b/source3/smbd/durable.c
|
|
@@ -133,6 +133,63 @@ NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
+struct durable_disconnect_state {
|
|
+ NTSTATUS status;
|
|
+ struct files_struct *fsp;
|
|
+};
|
|
+
|
|
+static void default_durable_disconnect_fn(struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck,
|
|
+ void *private_data)
|
|
+{
|
|
+ struct durable_disconnect_state *state = private_data;
|
|
+ struct files_struct *fsp = state->fsp;
|
|
+ struct smb_file_time ft;
|
|
+ bool ok;
|
|
+
|
|
+ /* Ensure any pending write time updates are done. */
|
|
+ if (fsp->update_write_time_event) {
|
|
+ fsp_flush_write_time_update(fsp);
|
|
+ }
|
|
+
|
|
+
|
|
+ init_smb_file_time(&ft);
|
|
+
|
|
+ if (fsp->fsp_flags.write_time_forced) {
|
|
+ NTTIME mtime = share_mode_changed_write_time(lck);
|
|
+ ft.mtime = nt_time_to_full_timespec(mtime);
|
|
+ } else if (fsp->fsp_flags.update_write_time_on_close) {
|
|
+ if (is_omit_timespec(&fsp->close_write_time)) {
|
|
+ ft.mtime = timespec_current();
|
|
+ } else {
|
|
+ ft.mtime = fsp->close_write_time;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!is_omit_timespec(&ft.mtime)) {
|
|
+ round_timespec(fsp->conn->ts_res, &ft.mtime);
|
|
+ file_ntimes(fsp->conn, fsp, &ft);
|
|
+ }
|
|
+
|
|
+ ok = mark_share_mode_disconnected(lck, fsp);
|
|
+ if (!ok) {
|
|
+ state->status = NT_STATUS_UNSUCCESSFUL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (br_lck == NULL) {
|
|
+ state->status = NT_STATUS_OK;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ok = brl_mark_disconnected(fsp, br_lck);
|
|
+ if (!ok) {
|
|
+ state->status = NT_STATUS_UNSUCCESSFUL;
|
|
+ return;
|
|
+ }
|
|
+ state->status = NT_STATUS_OK;
|
|
+}
|
|
+
|
|
NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
|
|
const DATA_BLOB old_cookie,
|
|
TALLOC_CTX *mem_ctx,
|
|
@@ -143,8 +200,7 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
|
|
enum ndr_err_code ndr_err;
|
|
struct vfs_default_durable_cookie cookie;
|
|
DATA_BLOB new_cookie_blob = data_blob_null;
|
|
- struct share_mode_lock *lck;
|
|
- bool ok;
|
|
+ struct durable_disconnect_state state;
|
|
|
|
*new_cookie = data_blob_null;
|
|
|
|
@@ -198,52 +254,23 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
- /* Ensure any pending write time updates are done. */
|
|
- if (fsp->update_write_time_event) {
|
|
- fsp_flush_write_time_update(fsp);
|
|
- }
|
|
+ state = (struct durable_disconnect_state) {
|
|
+ .fsp = fsp,
|
|
+ };
|
|
|
|
- /*
|
|
- * The above checks are done in mark_share_mode_disconnected() too
|
|
- * but we want to avoid getting the lock if possible
|
|
- */
|
|
- lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
|
|
- if (lck != NULL) {
|
|
- struct smb_file_time ft;
|
|
-
|
|
- init_smb_file_time(&ft);
|
|
-
|
|
- if (fsp->fsp_flags.write_time_forced) {
|
|
- NTTIME mtime = share_mode_changed_write_time(lck);
|
|
- ft.mtime = nt_time_to_full_timespec(mtime);
|
|
- } else if (fsp->fsp_flags.update_write_time_on_close) {
|
|
- if (is_omit_timespec(&fsp->close_write_time)) {
|
|
- ft.mtime = timespec_current();
|
|
- } else {
|
|
- ft.mtime = fsp->close_write_time;
|
|
- }
|
|
- }
|
|
-
|
|
- if (!is_omit_timespec(&ft.mtime)) {
|
|
- round_timespec(conn->ts_res, &ft.mtime);
|
|
- file_ntimes(conn, fsp, &ft);
|
|
- }
|
|
-
|
|
- ok = mark_share_mode_disconnected(lck, fsp);
|
|
- if (!ok) {
|
|
- TALLOC_FREE(lck);
|
|
- }
|
|
- }
|
|
- if (lck != NULL) {
|
|
- ok = brl_mark_disconnected(fsp);
|
|
- if (!ok) {
|
|
- TALLOC_FREE(lck);
|
|
- }
|
|
+ status = share_mode_do_locked_brl(fsp,
|
|
+ default_durable_disconnect_fn,
|
|
+ &state);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n",
|
|
+ fsp_str_dbg(fsp), nt_errstr(status));
|
|
+ return status;
|
|
}
|
|
- if (lck == NULL) {
|
|
- return NT_STATUS_NOT_SUPPORTED;
|
|
+ if (!NT_STATUS_IS_OK(state.status)) {
|
|
+ DBG_ERR("default_durable_disconnect_fn [%s] failed: %s\n",
|
|
+ fsp_str_dbg(fsp), nt_errstr(state.status));
|
|
+ return state.status;
|
|
}
|
|
- TALLOC_FREE(lck);
|
|
|
|
status = vfs_stat_fsp(fsp);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 70a466aad41c97cdedb01d0a501d3a34c6fe1eb3 Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Wed, 2 Apr 2025 14:52:03 +0200
|
|
Subject: [PATCH 095/122] smbd: use share_mode_do_locked_brl() in
|
|
vfs_default_durable_reconnect()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(backported from commit dc03a06ffcc79d0818ae4a36fe3f2df705144138)
|
|
[slow@samba.org: conflict due to removed delayed write time handling]
|
|
[slow@samba.org: conflict due to filename_convert_dirfsp_rel()]
|
|
---
|
|
source3/locking/brlock.c | 15 +-
|
|
source3/locking/proto.h | 3 +-
|
|
source3/smbd/durable.c | 439 ++++++++++++++++++++-------------------
|
|
3 files changed, 234 insertions(+), 223 deletions(-)
|
|
|
|
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
|
|
index b4c8501b4de..117b3edf05f 100644
|
|
--- a/source3/locking/brlock.c
|
|
+++ b/source3/locking/brlock.c
|
|
@@ -1515,14 +1515,14 @@ bool brl_mark_disconnected(struct files_struct *fsp,
|
|
return true;
|
|
}
|
|
|
|
-bool brl_reconnect_disconnected(struct files_struct *fsp)
|
|
+bool brl_reconnect_disconnected(struct files_struct *fsp,
|
|
+ struct byte_range_lock *br_lck)
|
|
{
|
|
uint32_t tid = fsp->conn->cnum;
|
|
uint64_t smblctx;
|
|
uint64_t fnum = fsp->fnum;
|
|
unsigned int i;
|
|
struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx);
|
|
- struct byte_range_lock *br_lck = NULL;
|
|
|
|
if (fsp->op == NULL) {
|
|
return false;
|
|
@@ -1540,13 +1540,7 @@ bool brl_reconnect_disconnected(struct files_struct *fsp)
|
|
* them instead.
|
|
*/
|
|
|
|
- br_lck = brl_get_locks(talloc_tos(), fsp);
|
|
- if (br_lck == NULL) {
|
|
- return false;
|
|
- }
|
|
-
|
|
if (br_lck->num_locks == 0) {
|
|
- TALLOC_FREE(br_lck);
|
|
return true;
|
|
}
|
|
|
|
@@ -1559,22 +1553,18 @@ bool brl_reconnect_disconnected(struct files_struct *fsp)
|
|
*/
|
|
|
|
if (lock->context.smblctx != smblctx) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
if (lock->context.tid != TID_FIELD_INVALID) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
if (!server_id_is_disconnected(&lock->context.pid)) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
if (lock->fnum != FNUM_FIELD_INVALID) {
|
|
- TALLOC_FREE(br_lck);
|
|
return false;
|
|
}
|
|
|
|
@@ -1585,7 +1575,6 @@ bool brl_reconnect_disconnected(struct files_struct *fsp)
|
|
|
|
fsp->current_lock_count = br_lck->num_locks;
|
|
br_lck->modified = true;
|
|
- TALLOC_FREE(br_lck);
|
|
return true;
|
|
}
|
|
|
|
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
|
|
index d3b4e02bc26..37d382833fc 100644
|
|
--- a/source3/locking/proto.h
|
|
+++ b/source3/locking/proto.h
|
|
@@ -83,7 +83,8 @@ struct brl_connectstate {
|
|
|
|
bool brl_mark_disconnected(struct files_struct *fsp,
|
|
struct byte_range_lock *br_lck);
|
|
-bool brl_reconnect_disconnected(struct files_struct *fsp);
|
|
+bool brl_reconnect_disconnected(struct files_struct *fsp,
|
|
+ struct byte_range_lock *br_lck);
|
|
void brl_close_fnum(struct byte_range_lock *br_lck);
|
|
int brl_forall(void (*fn)(struct file_id id, struct server_id pid,
|
|
enum brl_type lock_type,
|
|
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
|
|
index 1506d37208a..34218d1c596 100644
|
|
--- a/source3/smbd/durable.c
|
|
+++ b/source3/smbd/durable.c
|
|
@@ -563,121 +563,42 @@ static bool durable_reconnect_fn(
|
|
return false; /* Look at potential other entries */
|
|
}
|
|
|
|
-NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
- struct smb_request *smb1req,
|
|
- struct smbXsrv_open *op,
|
|
- const DATA_BLOB old_cookie,
|
|
- TALLOC_CTX *mem_ctx,
|
|
- files_struct **result,
|
|
- DATA_BLOB *new_cookie)
|
|
+struct vfs_default_durable_reconnect_state {
|
|
+ NTSTATUS status;
|
|
+ TALLOC_CTX *mem_ctx;
|
|
+ struct smb_request *smb1req;
|
|
+ struct smbXsrv_open *op;
|
|
+ struct vfs_default_durable_cookie cookie;
|
|
+ struct files_struct *fsp;
|
|
+ DATA_BLOB new_cookie_blob;
|
|
+};
|
|
+
|
|
+static void vfs_default_durable_reconnect_fn(struct share_mode_lock *lck,
|
|
+ struct byte_range_lock *br_lck,
|
|
+ void *private_data)
|
|
{
|
|
+ struct vfs_default_durable_reconnect_state *state = private_data;
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
- struct share_mode_lock *lck;
|
|
+ struct files_struct *fsp = state->fsp;
|
|
struct share_mode_entry e = { .pid = { .pid = 0, }};
|
|
- struct durable_reconnect_state rstate = { .op = op, .e = &e, };
|
|
- struct files_struct *fsp = NULL;
|
|
- NTSTATUS status;
|
|
- bool ok;
|
|
- int ret;
|
|
+ struct durable_reconnect_state rstate = { .op = state->op, .e = &e, };
|
|
struct vfs_open_how how = { .flags = 0, };
|
|
struct file_id file_id;
|
|
- struct smb_filename *smb_fname = NULL;
|
|
- enum ndr_err_code ndr_err;
|
|
- struct vfs_default_durable_cookie cookie;
|
|
- DATA_BLOB new_cookie_blob = data_blob_null;
|
|
bool have_share_mode_entry = false;
|
|
-
|
|
- *result = NULL;
|
|
- *new_cookie = data_blob_null;
|
|
-
|
|
- if (!lp_durable_handles(SNUM(conn))) {
|
|
- return NT_STATUS_NOT_SUPPORTED;
|
|
- }
|
|
-
|
|
- /*
|
|
- * the checks for kernel oplocks
|
|
- * and similar things are done
|
|
- * in the vfs_default_durable_cookie()
|
|
- * call below.
|
|
- */
|
|
-
|
|
- ndr_err = ndr_pull_struct_blob_all(
|
|
- &old_cookie,
|
|
- talloc_tos(),
|
|
- &cookie,
|
|
- (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
|
|
- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
- status = ndr_map_error2ntstatus(ndr_err);
|
|
- return status;
|
|
- }
|
|
-
|
|
- if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
- }
|
|
-
|
|
- if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
|
|
- return NT_STATUS_INVALID_PARAMETER;
|
|
- }
|
|
-
|
|
- if (!cookie.allow_reconnect) {
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
- }
|
|
-
|
|
- if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
- }
|
|
-
|
|
- /* Create an smb_filename with stream_name == NULL. */
|
|
- smb_fname = synthetic_smb_fname(talloc_tos(),
|
|
- cookie.base_name,
|
|
- NULL,
|
|
- NULL,
|
|
- 0,
|
|
- 0);
|
|
- if (smb_fname == NULL) {
|
|
- return NT_STATUS_NO_MEMORY;
|
|
- }
|
|
-
|
|
- ret = SMB_VFS_LSTAT(conn, smb_fname);
|
|
- if (ret == -1) {
|
|
- status = map_nt_error_from_unix_common(errno);
|
|
- DEBUG(1, ("Unable to lstat stream: %s => %s\n",
|
|
- smb_fname_str_dbg(smb_fname),
|
|
- nt_errstr(status)));
|
|
- return status;
|
|
- }
|
|
-
|
|
- if (!S_ISREG(smb_fname->st.st_ex_mode)) {
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
- }
|
|
-
|
|
- file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
|
|
- if (!file_id_equal(&cookie.id, &file_id)) {
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
- }
|
|
-
|
|
- /*
|
|
- * 1. check entry in locking.tdb
|
|
- */
|
|
-
|
|
- lck = get_existing_share_mode_lock(mem_ctx, file_id);
|
|
- if (lck == NULL) {
|
|
- DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
|
|
- "not obtained from db\n"));
|
|
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
- }
|
|
+ int ret;
|
|
+ bool ok;
|
|
|
|
ok = share_mode_forall_entries(lck, durable_reconnect_fn, &rstate);
|
|
if (!ok) {
|
|
DBG_WARNING("share_mode_forall_entries failed\n");
|
|
- status = NT_STATUS_INTERNAL_DB_ERROR;
|
|
+ state->status = NT_STATUS_INTERNAL_DB_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
if (e.pid.pid == 0) {
|
|
DBG_WARNING("Did not find a unique valid share mode entry\n");
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
@@ -685,69 +606,36 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
|
|
"reconnect for handle that was not marked "
|
|
"disconnected (e.g. smbd or cluster node died)\n"));
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
- if (e.share_file_id != op->global->open_persistent_id) {
|
|
+ if (e.share_file_id != state->op->global->open_persistent_id) {
|
|
DBG_INFO("denying durable "
|
|
"share_file_id changed %"PRIu64" != %"PRIu64" "
|
|
"(e.g. another client had opened the file)\n",
|
|
e.share_file_id,
|
|
- op->global->open_persistent_id);
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ state->op->global->open_persistent_id);
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
|
|
- !CAN_WRITE(conn))
|
|
+ !CAN_WRITE(fsp->conn))
|
|
{
|
|
DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
|
|
"share[%s] is not writeable anymore\n",
|
|
- lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
- goto fail;
|
|
- }
|
|
-
|
|
- /*
|
|
- * 2. proceed with opening file
|
|
- */
|
|
-
|
|
- status = fsp_new(conn, conn, &fsp);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
|
|
- "new fsp: %s\n", nt_errstr(status)));
|
|
+ lp_servicename(talloc_tos(), lp_sub, SNUM(fsp->conn))));
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
fh_set_private_options(fsp->fh, e.private_options);
|
|
- fsp->file_id = file_id;
|
|
- fsp->file_pid = smb1req->smbpid;
|
|
- fsp->vuid = smb1req->vuid;
|
|
fsp->open_time = e.time;
|
|
fsp->access_mask = e.access_mask;
|
|
fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0);
|
|
fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
|
|
- fsp->fnum = op->local_id;
|
|
- fsp_set_gen_id(fsp);
|
|
|
|
- /*
|
|
- * TODO:
|
|
- * Do we need to store the modified flag in the DB?
|
|
- */
|
|
- fsp->fsp_flags.modified = false;
|
|
- /*
|
|
- * no durables for directories
|
|
- */
|
|
- fsp->fsp_flags.is_directory = false;
|
|
- /*
|
|
- * For normal files, can_lock == !is_directory
|
|
- */
|
|
- fsp->fsp_flags.can_lock = true;
|
|
- /*
|
|
- * We do not support aio write behind for smb2
|
|
- */
|
|
- fsp->fsp_flags.aio_write_behind = false;
|
|
fsp->oplock_type = e.op_type;
|
|
|
|
if (fsp->oplock_type == LEASE_OPLOCK) {
|
|
@@ -760,21 +648,21 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
*/
|
|
if (!GUID_equal(fsp_client_guid(fsp),
|
|
&e.client_guid)) {
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
- status = leases_db_get(
|
|
+ state->status = leases_db_get(
|
|
&e.client_guid,
|
|
&e.lease_key,
|
|
- &file_id,
|
|
+ &fsp->file_id,
|
|
¤t_state, /* current_state */
|
|
NULL, /* breaking */
|
|
NULL, /* breaking_to_requested */
|
|
NULL, /* breaking_to_required */
|
|
&lease_version, /* lease_version */
|
|
&epoch); /* epoch */
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
+ if (!NT_STATUS_IS_OK(state->status)) {
|
|
goto fail;
|
|
}
|
|
|
|
@@ -785,53 +673,46 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
lease_version,
|
|
epoch);
|
|
if (fsp->lease == NULL) {
|
|
- status = NT_STATUS_NO_MEMORY;
|
|
+ state->status = NT_STATUS_NO_MEMORY;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
- fsp->initial_allocation_size = cookie.initial_allocation_size;
|
|
- fh_set_position_information(fsp->fh, cookie.position_information);
|
|
+ fsp->initial_allocation_size = state->cookie.initial_allocation_size;
|
|
+ fh_set_position_information(fsp->fh, state->cookie.position_information);
|
|
fsp->fsp_flags.update_write_time_triggered =
|
|
- cookie.update_write_time_triggered;
|
|
+ state->cookie.update_write_time_triggered;
|
|
fsp->fsp_flags.update_write_time_on_close =
|
|
- cookie.update_write_time_on_close;
|
|
- fsp->fsp_flags.write_time_forced = cookie.write_time_forced;
|
|
+ state->cookie.update_write_time_on_close;
|
|
+ fsp->fsp_flags.write_time_forced = state->cookie.write_time_forced;
|
|
fsp->close_write_time = nt_time_to_full_timespec(
|
|
- cookie.close_write_time);
|
|
+ state->cookie.close_write_time);
|
|
|
|
- status = fsp_set_smb_fname(fsp, smb_fname);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(0, ("vfs_default_durable_reconnect: "
|
|
- "fsp_set_smb_fname failed: %s\n",
|
|
- nt_errstr(status)));
|
|
- goto fail;
|
|
- }
|
|
-
|
|
- op->compat = fsp;
|
|
- fsp->op = op;
|
|
+ state->op->compat = fsp;
|
|
+ fsp->op = state->op;
|
|
|
|
ok = reset_share_mode_entry(
|
|
lck,
|
|
e.pid,
|
|
e.share_file_id,
|
|
- messaging_server_id(conn->sconn->msg_ctx),
|
|
- smb1req->mid,
|
|
+ messaging_server_id(fsp->conn->sconn->msg_ctx),
|
|
+ state->smb1req->mid,
|
|
fh_get_gen_id(fsp->fh));
|
|
if (!ok) {
|
|
DBG_DEBUG("Could not set new share_mode_entry values\n");
|
|
- status = NT_STATUS_INTERNAL_ERROR;
|
|
+ state->status = NT_STATUS_INTERNAL_ERROR;
|
|
goto fail;
|
|
}
|
|
have_share_mode_entry = true;
|
|
|
|
- ok = brl_reconnect_disconnected(fsp);
|
|
- if (!ok) {
|
|
- status = NT_STATUS_INTERNAL_ERROR;
|
|
- DEBUG(1, ("vfs_default_durable_reconnect: "
|
|
- "failed to reopen brlocks: %s\n",
|
|
- nt_errstr(status)));
|
|
- goto fail;
|
|
+ if (br_lck != NULL) {
|
|
+ ok = brl_reconnect_disconnected(fsp, br_lck);
|
|
+ if (!ok) {
|
|
+ state->status = NT_STATUS_INTERNAL_ERROR;
|
|
+ DBG_ERR("failed to reopen brlocks: %s\n",
|
|
+ nt_errstr(state->status));
|
|
+ goto fail;
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
@@ -845,10 +726,9 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
how.flags = O_RDONLY;
|
|
}
|
|
|
|
- status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, &how);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
|
|
- "file: %s\n", nt_errstr(status)));
|
|
+ state->status = fd_openat(fsp->conn->cwd_fsp, fsp->fsp_name, fsp, &how);
|
|
+ if (!NT_STATUS_IS_OK(state->status)) {
|
|
+ DBG_ERR("failed to open file: %s\n", nt_errstr(state->status));
|
|
goto fail;
|
|
}
|
|
|
|
@@ -863,70 +743,66 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
|
|
ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
|
|
if (ret == -1) {
|
|
- status = map_nt_error_from_unix_common(errno);
|
|
- DEBUG(1, ("Unable to fstat stream: %s => %s\n",
|
|
- smb_fname_str_dbg(smb_fname),
|
|
- nt_errstr(status)));
|
|
+ state->status = map_nt_error_from_unix_common(errno);
|
|
+ DBG_ERR("Unable to fstat stream: %s => %s\n",
|
|
+ fsp_str_dbg(fsp),
|
|
+ nt_errstr(state->status));
|
|
goto fail;
|
|
}
|
|
|
|
if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
- file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
|
|
- if (!file_id_equal(&cookie.id, &file_id)) {
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st);
|
|
+ if (!file_id_equal(&state->cookie.id, &file_id)) {
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
(void)fdos_mode(fsp);
|
|
|
|
- ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
|
|
+ ok = vfs_default_durable_reconnect_check_stat(&state->cookie.stat_info,
|
|
&fsp->fsp_name->st,
|
|
fsp_str_dbg(fsp));
|
|
if (!ok) {
|
|
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
- status = set_file_oplock(fsp);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
+ state->status = set_file_oplock(fsp);
|
|
+ if (!NT_STATUS_IS_OK(state->status)) {
|
|
goto fail;
|
|
}
|
|
|
|
- status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
|
|
- if (!NT_STATUS_IS_OK(status)) {
|
|
- DEBUG(1, ("vfs_default_durable_reconnect: "
|
|
- "vfs_default_durable_cookie - %s\n",
|
|
- nt_errstr(status)));
|
|
+ state->status = vfs_default_durable_cookie(fsp,
|
|
+ state->mem_ctx,
|
|
+ &state->new_cookie_blob);
|
|
+ if (!NT_STATUS_IS_OK(state->status)) {
|
|
+ DBG_ERR("vfs_default_durable_cookie - %s\n",
|
|
+ nt_errstr(state->status));
|
|
goto fail;
|
|
}
|
|
|
|
- smb1req->chain_fsp = fsp;
|
|
- smb1req->smb2req->compat_chain_fsp = fsp;
|
|
-
|
|
- DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
|
|
- fsp_str_dbg(fsp)));
|
|
+ state->smb1req->chain_fsp = fsp;
|
|
+ state->smb1req->smb2req->compat_chain_fsp = fsp;
|
|
|
|
- TALLOC_FREE(lck);
|
|
+ DBG_DEBUG("opened file '%s'\n", fsp_str_dbg(fsp));
|
|
|
|
fsp->fsp_flags.is_fsa = true;
|
|
|
|
- *result = fsp;
|
|
- *new_cookie = new_cookie_blob;
|
|
-
|
|
- return NT_STATUS_OK;
|
|
+ state->status = NT_STATUS_OK;
|
|
+ return;
|
|
|
|
fail:
|
|
- if (fsp != NULL && have_share_mode_entry) {
|
|
+ if (have_share_mode_entry) {
|
|
/*
|
|
* Something is screwed up, delete the sharemode entry.
|
|
*/
|
|
del_share_mode(lck, fsp);
|
|
}
|
|
- if (fsp != NULL && fsp_get_pathref_fd(fsp) != -1) {
|
|
+ if (fsp_get_pathref_fd(fsp) != -1) {
|
|
NTSTATUS close_status;
|
|
close_status = fd_close(fsp);
|
|
if (!NT_STATUS_IS_OK(close_status)) {
|
|
@@ -934,11 +810,156 @@ fail:
|
|
nt_errstr(close_status));
|
|
}
|
|
}
|
|
- TALLOC_FREE(lck);
|
|
- if (fsp != NULL) {
|
|
- op->compat = NULL;
|
|
- fsp->op = NULL;
|
|
- file_free(smb1req, fsp);
|
|
+ state->op->compat = NULL;
|
|
+ fsp->op = NULL;
|
|
+}
|
|
+
|
|
+NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
|
|
+ struct smb_request *smb1req,
|
|
+ struct smbXsrv_open *op,
|
|
+ const DATA_BLOB old_cookie,
|
|
+ TALLOC_CTX *mem_ctx,
|
|
+ files_struct **result,
|
|
+ DATA_BLOB *new_cookie)
|
|
+{
|
|
+ struct vfs_default_durable_reconnect_state state;
|
|
+ struct smb_filename *smb_fname = NULL;
|
|
+ struct file_id file_id;
|
|
+ NTSTATUS status;
|
|
+ enum ndr_err_code ndr_err;
|
|
+ int ret;
|
|
+
|
|
+ *result = NULL;
|
|
+ *new_cookie = data_blob_null;
|
|
+
|
|
+ if (!lp_durable_handles(SNUM(conn))) {
|
|
+ return NT_STATUS_NOT_SUPPORTED;
|
|
+ }
|
|
+
|
|
+ state = (struct vfs_default_durable_reconnect_state) {
|
|
+ .mem_ctx = mem_ctx,
|
|
+ .smb1req = smb1req,
|
|
+ .op = op,
|
|
+ };
|
|
+
|
|
+ /*
|
|
+ * the checks for kernel oplocks
|
|
+ * and similar things are done
|
|
+ * in the vfs_default_durable_cookie()
|
|
+ * call below.
|
|
+ */
|
|
+
|
|
+ ndr_err = ndr_pull_struct_blob_all(
|
|
+ &old_cookie,
|
|
+ talloc_tos(),
|
|
+ &state.cookie,
|
|
+ (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
|
|
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
+ status = ndr_map_error2ntstatus(ndr_err);
|
|
+ return status;
|
|
}
|
|
- return status;
|
|
+
|
|
+ if (strcmp(state.cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
|
|
+ return NT_STATUS_INVALID_PARAMETER;
|
|
+ }
|
|
+
|
|
+ if (state.cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
|
|
+ return NT_STATUS_INVALID_PARAMETER;
|
|
+ }
|
|
+
|
|
+ if (!state.cookie.allow_reconnect) {
|
|
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ if (strcmp(state.cookie.servicepath, conn->connectpath) != 0) {
|
|
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ /* Create an smb_filename with stream_name == NULL. */
|
|
+ smb_fname = synthetic_smb_fname(talloc_tos(),
|
|
+ state.cookie.base_name,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ 0,
|
|
+ 0);
|
|
+ if (smb_fname == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+
|
|
+ ret = SMB_VFS_LSTAT(conn, smb_fname);
|
|
+ if (ret == -1) {
|
|
+ status = map_nt_error_from_unix_common(errno);
|
|
+ DEBUG(1, ("Unable to lstat stream: %s => %s\n",
|
|
+ smb_fname_str_dbg(smb_fname),
|
|
+ nt_errstr(status)));
|
|
+ return status;
|
|
+ }
|
|
+ if (!S_ISREG(smb_fname->st.st_ex_mode)) {
|
|
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
|
|
+ if (!file_id_equal(&state.cookie.id, &file_id)) {
|
|
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ status = fsp_new(conn, conn, &state.fsp);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_ERR("failed to create new fsp: %s\n",
|
|
+ nt_errstr(status));
|
|
+ return status;
|
|
+ }
|
|
+ state.fsp->file_id = file_id;
|
|
+ state.fsp->file_pid = smb1req->smbpid;
|
|
+ state.fsp->vuid = smb1req->vuid;
|
|
+ state.fsp->fnum = op->local_id;
|
|
+ fsp_set_gen_id(state.fsp);
|
|
+
|
|
+ status = fsp_set_smb_fname(state.fsp, smb_fname);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_ERR("fsp_set_smb_fname failed: %s\n",
|
|
+ nt_errstr(status));
|
|
+ file_free(smb1req, state.fsp);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * TODO:
|
|
+ * Do we need to store the modified flag in the DB?
|
|
+ */
|
|
+ state.fsp->fsp_flags.modified = false;
|
|
+ /*
|
|
+ * no durables for directories
|
|
+ */
|
|
+ state.fsp->fsp_flags.is_directory = false;
|
|
+ /*
|
|
+ * For normal files, can_lock == !is_directory
|
|
+ */
|
|
+ state.fsp->fsp_flags.can_lock = true;
|
|
+ /*
|
|
+ * We do not support aio write behind for smb2
|
|
+ */
|
|
+ state.fsp->fsp_flags.aio_write_behind = false;
|
|
+
|
|
+ status = share_mode_do_locked_brl(state.fsp,
|
|
+ vfs_default_durable_reconnect_fn,
|
|
+ &state);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n",
|
|
+ smb_fname_str_dbg(smb_fname), nt_errstr(status));
|
|
+ file_free(smb1req, state.fsp);
|
|
+ return status;
|
|
+ }
|
|
+ if (!NT_STATUS_IS_OK(state.status)) {
|
|
+ DBG_ERR("default_durable_reconnect_fn [%s] failed: %s\n",
|
|
+ smb_fname_str_dbg(smb_fname),
|
|
+ nt_errstr(state.status));
|
|
+ file_free(smb1req, state.fsp);
|
|
+ return state.status;
|
|
+ }
|
|
+
|
|
+ *result = state.fsp;
|
|
+ *new_cookie = state.new_cookie_blob;
|
|
+
|
|
+ return NT_STATUS_OK;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 05df077f6fd9402f2513a9c9a9d55cfbb40971cf Mon Sep 17 00:00:00 2001
|
|
From: Ralph Boehme <slow@samba.org>
|
|
Date: Tue, 28 Jan 2025 14:48:39 +0100
|
|
Subject: [PATCH 096/122] s3:rpc_server/srvsvc: use brl_get_locks_readonly()
|
|
instead of brl_get_locks()
|
|
|
|
No need to keep the record locked longer then needed.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767
|
|
|
|
Signed-off-by: Ralph Boehme <slow@samba.org>
|
|
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
|
(cherry picked from commit c36cc2b6720a2cfe54ce52a500dc499418e27e34)
|
|
---
|
|
source3/rpc_server/srvsvc/srv_srvsvc_nt.c | 12 +++++++++---
|
|
1 file changed, 9 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
|
|
index d6e7bed5949..c07eefdfaad 100644
|
|
--- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
|
|
+++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c
|
|
@@ -191,17 +191,23 @@ static WERROR net_enum_files(TALLOC_CTX *ctx,
|
|
/* need to count the number of locks on a file */
|
|
|
|
for (i=0; i<(*ctr3)->count; i++) {
|
|
- struct files_struct fsp = { .file_id = f_enum_cnt.fids[i], };
|
|
+ struct files_struct *fsp = NULL;
|
|
struct byte_range_lock *brl = NULL;
|
|
|
|
- brl = brl_get_locks(ctx, &fsp);
|
|
+ fsp = talloc_zero(talloc_tos(), struct files_struct);
|
|
+ if (fsp == NULL) {
|
|
+ return WERR_NOT_ENOUGH_MEMORY;
|
|
+ }
|
|
+ fsp->file_id = f_enum_cnt.fids[i];
|
|
+
|
|
+ brl = brl_get_locks_readonly(fsp);
|
|
if (brl == NULL) {
|
|
continue;
|
|
}
|
|
|
|
(*ctr3)->array[i].num_locks = brl_num_locks(brl);
|
|
-
|
|
TALLOC_FREE(brl);
|
|
+ TALLOC_FREE(fsp);
|
|
}
|
|
|
|
return WERR_OK;
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e40fd11548bda2fe4cf178b639f56dd7652675ee Mon Sep 17 00:00:00 2001
|
|
From: Andreas Schneider <asn@samba.org>
|
|
Date: Tue, 24 Mar 2026 15:00:21 +0100
|
|
Subject: [PATCH 097/122] wafsamba: Add -D_FORTIFY_SOURCE=3 when stack
|
|
protector is enabled
|
|
|
|
The capability check in SAMBA_CONFIG_H() already tests that the compiler
|
|
accepts both -Wp,-D_FORTIFY_SOURCE and the stack protector flag
|
|
together, but only the stack protector flag was added to EXTRA_CFLAGS on
|
|
success.
|
|
|
|
The glibc normally silently downgrades to the supported level if the on
|
|
specified is not supported.
|
|
|
|
Note that -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 only sets it if not
|
|
already defined.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16040
|
|
|
|
Signed-off-by: Andreas Schneider <asn@samba.org>
|
|
Reviewed-by: Anoop C S <anoopcs@samba.org>
|
|
|
|
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
|
|
Autobuild-Date(master): Fri Mar 27 08:33:09 UTC 2026 on atb-devel-224
|
|
---
|
|
buildtools/wafsamba/samba_autoconf.py | 7 ++++++-
|
|
script/autobuild.py | 3 ++-
|
|
2 files changed, 8 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/buildtools/wafsamba/samba_autoconf.py b/buildtools/wafsamba/samba_autoconf.py
|
|
index b1d2f761095..d0c9bf110ff 100644
|
|
--- a/buildtools/wafsamba/samba_autoconf.py
|
|
+++ b/buildtools/wafsamba/samba_autoconf.py
|
|
@@ -733,11 +733,16 @@ def SAMBA_CONFIG_H(conf, path=None):
|
|
}
|
|
''',
|
|
execute=0,
|
|
- cflags=[ '-Werror', '-Wp,-D_FORTIFY_SOURCE=2', stack_protect_flag],
|
|
+ cflags=[
|
|
+ '-Werror',
|
|
+ '-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3',
|
|
+ stack_protect_flag
|
|
+ ],
|
|
mandatory=False,
|
|
msg='Checking if compiler accepts %s' % (stack_protect_flag))
|
|
if flag_supported:
|
|
conf.ADD_CFLAGS('%s' % (stack_protect_flag))
|
|
+ conf.ADD_CFLAGS('-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3')
|
|
break
|
|
|
|
flag_supported = conf.check(fragment='''
|
|
diff --git a/script/autobuild.py b/script/autobuild.py
|
|
index 85043032d73..938c7a97fdd 100755
|
|
--- a/script/autobuild.py
|
|
+++ b/script/autobuild.py
|
|
@@ -171,6 +171,7 @@ samba_libs_configure_libs = samba_libs_configure_base + " --bundled-libraries=cm
|
|
samba_libs_configure_bundled_libs = " --bundled-libraries=!talloc,!pytalloc-util,!tdb,!pytdb,!ldb,!pyldb,!pyldb-util,!tevent,!pytevent,!popt"
|
|
samba_libs_configure_samba = samba_libs_configure_base + samba_libs_configure_bundled_libs
|
|
|
|
+samba_o3_cflags = "-O3"
|
|
|
|
def format_option(name, value=None):
|
|
"""Format option as str list."""
|
|
@@ -808,7 +809,7 @@ tasks = {
|
|
"samba-o3": {
|
|
"sequence": [
|
|
("random-sleep", random_sleep(300, 900)),
|
|
- ("configure", "ADDITIONAL_CFLAGS='-O3 -Wp,-D_FORTIFY_SOURCE=2' ./configure.developer --abi-check-disable" + samba_configure_params),
|
|
+ ("configure", "ADDITIONAL_CFLAGS='" + samba_o3_cflags + "' ./configure.developer --abi-check-disable" + samba_configure_params),
|
|
("make", "make -j"),
|
|
("test", make_test(cmd='make test', TESTS="--exclude=selftest/slow-none", include_envs=["none"])),
|
|
("quicktest", make_test(cmd='make quicktest', include_envs=["ad_dc", "ad_dc_smb1", "ad_dc_smb1_done"])),
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e916e33421ea017ebd106d418dfd1a3a4f0fe896 Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Fri, 27 Feb 2026 11:30:40 +1300
|
|
Subject: [PATCH 098/122] CVE-2026-3012: gpo tests: fix test cleanup
|
|
|
|
These tests are going to fail soon but as currently written they do
|
|
not clean up after themselves, erroring instead of failing and causing
|
|
cascading errors in subsequent tests. For now we don't care to make
|
|
the other tests less fragile.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
|
|
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
|
|
---
|
|
python/samba/tests/gpo.py | 42 +++++++++++++++++++++++----------------
|
|
1 file changed, 25 insertions(+), 17 deletions(-)
|
|
|
|
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
|
|
index a78af17dba4..067b92728d4 100644
|
|
--- a/python/samba/tests/gpo.py
|
|
+++ b/python/samba/tests/gpo.py
|
|
@@ -6807,6 +6807,7 @@ class GPOTests(tests.TestCase):
|
|
confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
|
|
ca_cn = '%s-CA' % hostname.replace('.', '-')
|
|
certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
|
|
+ self.addCleanup(ldb.delete, certa_dn)
|
|
ldb.add({'dn': certa_dn,
|
|
'objectClass': 'certificationAuthority',
|
|
'authorityRevocationList': ['XXX'],
|
|
@@ -6815,6 +6816,7 @@ class GPOTests(tests.TestCase):
|
|
})
|
|
# Write the dummy pKIEnrollmentService
|
|
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
|
|
+ self.addCleanup(ldb.delete, enroll_dn)
|
|
ldb.add({'dn': enroll_dn,
|
|
'objectClass': 'pKIEnrollmentService',
|
|
'cACertificate': dummy_certificate(),
|
|
@@ -6823,6 +6825,7 @@ class GPOTests(tests.TestCase):
|
|
})
|
|
# Write the dummy pKICertificateTemplate
|
|
template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
|
|
+ self.addCleanup(ldb.delete, template_dn)
|
|
ldb.add({'dn': template_dn,
|
|
'objectClass': 'pKICertificateTemplate',
|
|
})
|
|
@@ -6868,11 +6871,6 @@ class GPOTests(tests.TestCase):
|
|
self.assertNotIn(b'Workstation', out,
|
|
'Workstation certificate not removed')
|
|
|
|
- # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
|
|
- ldb.delete(certa_dn)
|
|
- ldb.delete(enroll_dn)
|
|
- ldb.delete(template_dn)
|
|
-
|
|
# Unstage the Registry.pol file
|
|
unstage_file(reg_pol)
|
|
|
|
@@ -6883,6 +6881,7 @@ class GPOTests(tests.TestCase):
|
|
'MACHINE/REGISTRY.POL')
|
|
cache_dir = self.lp.get('cache directory')
|
|
store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
|
|
+ self.addCleanup(store.log.close)
|
|
|
|
machine_creds = Credentials()
|
|
machine_creds.guess(self.lp)
|
|
@@ -6915,6 +6914,7 @@ class GPOTests(tests.TestCase):
|
|
confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
|
|
ca_cn = '%s-CA' % hostname.replace('.', '-')
|
|
certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
|
|
+ self.addCleanup(ldb.delete, certa_dn)
|
|
ldb.add({'dn': certa_dn,
|
|
'objectClass': 'certificationAuthority',
|
|
'authorityRevocationList': ['XXX'],
|
|
@@ -6923,6 +6923,7 @@ class GPOTests(tests.TestCase):
|
|
})
|
|
# Write the dummy pKIEnrollmentService
|
|
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
|
|
+ self.addCleanup(ldb.delete, enroll_dn)
|
|
ldb.add({'dn': enroll_dn,
|
|
'objectClass': 'pKIEnrollmentService',
|
|
'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
@@ -6931,12 +6932,16 @@ class GPOTests(tests.TestCase):
|
|
})
|
|
# Write the dummy pKICertificateTemplate
|
|
template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
|
|
+ self.addCleanup(ldb.delete, template_dn)
|
|
ldb.add({'dn': template_dn,
|
|
'objectClass': 'pKICertificateTemplate',
|
|
})
|
|
|
|
with TemporaryDirectory() as dname:
|
|
- ext.process_group_policy([], gpos, dname, dname)
|
|
+ try:
|
|
+ ext.process_group_policy([], gpos, dname, dname)
|
|
+ except Exception as e:
|
|
+ self.fail(f"process_group_policy() raised {e}")
|
|
ca_crt = os.path.join(dname, '%s.crt' % ca_cn)
|
|
self.assertTrue(os.path.exists(ca_crt),
|
|
'Root CA certificate was not requested')
|
|
@@ -7025,11 +7030,6 @@ class GPOTests(tests.TestCase):
|
|
self.assertNotIn(b'Workstation', out,
|
|
'Workstation certificate not removed')
|
|
|
|
- # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
|
|
- ldb.delete(certa_dn)
|
|
- ldb.delete(enroll_dn)
|
|
- ldb.delete(template_dn)
|
|
-
|
|
# Unstage the Registry.pol file
|
|
unstage_file(reg_pol)
|
|
|
|
@@ -7348,6 +7348,7 @@ class GPOTests(tests.TestCase):
|
|
'MACHINE/REGISTRY.POL')
|
|
cache_dir = self.lp.get('cache directory')
|
|
store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
|
|
+ self.addCleanup(store.log.close)
|
|
|
|
machine_creds = Credentials()
|
|
machine_creds.guess(self.lp)
|
|
@@ -7389,6 +7390,8 @@ class GPOTests(tests.TestCase):
|
|
confdn = 'CN=Public Key Services,CN=Services,CN=Configuration,%s' % base_dn
|
|
ca_cn = '%s-CA' % hostname.replace('.', '-')
|
|
certa_dn = 'CN=%s,CN=Certification Authorities,%s' % (ca_cn, confdn)
|
|
+ self.addCleanup(ldb.delete, certa_dn)
|
|
+
|
|
ldb.add({'dn': certa_dn,
|
|
'objectClass': 'certificationAuthority',
|
|
'authorityRevocationList': ['XXX'],
|
|
@@ -7397,6 +7400,7 @@ class GPOTests(tests.TestCase):
|
|
})
|
|
# Write the dummy pKIEnrollmentService
|
|
enroll_dn = 'CN=%s,CN=Enrollment Services,%s' % (ca_cn, confdn)
|
|
+ self.addCleanup(ldb.delete, enroll_dn)
|
|
ldb.add({'dn': enroll_dn,
|
|
'objectClass': 'pKIEnrollmentService',
|
|
'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
@@ -7405,12 +7409,21 @@ class GPOTests(tests.TestCase):
|
|
})
|
|
# Write the dummy pKICertificateTemplate
|
|
template_dn = 'CN=Machine,CN=Certificate Templates,%s' % confdn
|
|
+ try:
|
|
+ ldb.delete(template_dn)
|
|
+ except _ldb.LdbError:
|
|
+ pass
|
|
+
|
|
+ self.addCleanup(ldb.delete, template_dn)
|
|
ldb.add({'dn': template_dn,
|
|
'objectClass': 'pKICertificateTemplate',
|
|
})
|
|
|
|
with TemporaryDirectory() as dname:
|
|
- ext.process_group_policy([], gpos, dname, dname)
|
|
+ try:
|
|
+ ext.process_group_policy([], gpos, dname, dname)
|
|
+ except Exception as e:
|
|
+ self.fail(f"process_group_policy() raised {e}")
|
|
ca_list = [ca_cn, 'example0-com-CA', 'example1-com-CA',
|
|
'example2-com-CA']
|
|
for ca in ca_list:
|
|
@@ -7473,11 +7486,6 @@ class GPOTests(tests.TestCase):
|
|
self.assertNotIn(b'Workstation', out,
|
|
'Workstation certificate not removed')
|
|
|
|
- # Remove the dummy CA, pKIEnrollmentService, and pKICertificateTemplate
|
|
- ldb.delete(certa_dn)
|
|
- ldb.delete(enroll_dn)
|
|
- ldb.delete(template_dn)
|
|
-
|
|
# Unstage the Registry.pol file
|
|
unstage_file(reg_pol)
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 5edabac806ad07c2b77a725fe8fccb9155897d7e Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Mon, 23 Feb 2026 11:01:57 +1300
|
|
Subject: [PATCH 099/122] CVE-2026-3012: do not fetch certificate over http
|
|
|
|
In the case where a certificate was found via HTTP, it was trusted
|
|
without verification and put in the global CA store.
|
|
|
|
There is no means to check the certificate other than by comparing it
|
|
to certificates we may have gathered via LDAP, but in that case there
|
|
is no advantage over just using the LDAP-derived certificates.
|
|
|
|
Using the LDAP certificates was already the fallback case if HTTP
|
|
failed, so we just make it the default.
|
|
|
|
The HTTP fetch depends on the NDES service, which is a variant of
|
|
Simple Certificate Enrolment Protocol (SCEP, RFC8894), but in fact
|
|
Samba implements none of that protocol other than the HTTP fetch. SCEP
|
|
is for clients that are not true domain members. Domain members can
|
|
access to certificates over LDAP. This patch is not reducing SCEP
|
|
client support because Samba never had it.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
|
|
|
|
Reported-by: Arad Inbar, DREAM Security Research Team
|
|
Reported-by: Nir Somech, DREAM Security Research Team
|
|
Reported-by: Ben Grinberg, DREAM Security Research Team
|
|
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 54 ++++------------------
|
|
selftest/knownfail.d/gpo-auto-enrol | 2 +
|
|
2 files changed, 11 insertions(+), 45 deletions(-)
|
|
create mode 100644 selftest/knownfail.d/gpo-auto-enrol
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index 2b7f7d22c2b..f1c55a0dbf1 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -16,7 +16,6 @@
|
|
|
|
import os
|
|
import operator
|
|
-import requests
|
|
from samba.gp.gpclass import gp_pol_ext, gp_applier, GPOSTATE
|
|
from samba import Ldb
|
|
from ldb import SCOPE_SUBTREE, SCOPE_BASE
|
|
@@ -200,58 +199,24 @@ def get_supported_templates(server):
|
|
return out.strip().split()
|
|
|
|
|
|
-def getca(ca, url, trust_dir):
|
|
- """Fetch Certificate Chain from the CA."""
|
|
+def getca(ca, trust_dir):
|
|
+ """Fetch a certificate from LDAP."""
|
|
root_cert = os.path.join(trust_dir, '%s.crt' % ca['name'])
|
|
root_certs = []
|
|
-
|
|
- try:
|
|
- r = requests.get(url=url, params={'operation': 'GetCACert',
|
|
- 'message': 'CAIdentifier'})
|
|
- except requests.exceptions.ConnectionError:
|
|
- log.warn('Could not connect to Network Device Enrollment Service.')
|
|
- r = None
|
|
- if r is None or r.content == b'' or r.headers['Content-Type'] == 'text/html':
|
|
- log.warn('Unable to fetch root certificates (requires NDES).')
|
|
- if 'cACertificate' in ca:
|
|
- log.warn('Installing the server certificate only.')
|
|
- der_certificate = base64.b64decode(ca['cACertificate'])
|
|
- try:
|
|
- cert = load_der_x509_certificate(der_certificate)
|
|
- except TypeError:
|
|
- cert = load_der_x509_certificate(der_certificate,
|
|
- default_backend())
|
|
- cert_data = cert.public_bytes(Encoding.PEM)
|
|
- with open(root_cert, 'wb') as w:
|
|
- w.write(cert_data)
|
|
- root_certs.append(root_cert)
|
|
- return root_certs
|
|
-
|
|
- if r.headers['Content-Type'] == 'application/x-x509-ca-cert':
|
|
- # Older versions of load_der_x509_certificate require a backend param
|
|
+ if 'cACertificate' in ca:
|
|
+ log.warn('Installing the server certificate only.')
|
|
+ der_certificate = base64.b64decode(ca['cACertificate'])
|
|
try:
|
|
- cert = load_der_x509_certificate(r.content)
|
|
+ cert = load_der_x509_certificate(der_certificate)
|
|
except TypeError:
|
|
- cert = load_der_x509_certificate(r.content, default_backend())
|
|
+ cert = load_der_x509_certificate(der_certificate,
|
|
+ default_backend())
|
|
cert_data = cert.public_bytes(Encoding.PEM)
|
|
with open(root_cert, 'wb') as w:
|
|
w.write(cert_data)
|
|
root_certs.append(root_cert)
|
|
- elif r.headers['Content-Type'] == 'application/x-x509-ca-ra-cert':
|
|
- certs = load_der_pkcs7_certificates(r.content)
|
|
- for i in range(0, len(certs)):
|
|
- cert = certs[i].public_bytes(Encoding.PEM)
|
|
- filename, extension = root_cert.rsplit('.', 1)
|
|
- dest = '%s.%d.%s' % (filename, i, extension)
|
|
- with open(dest, 'wb') as w:
|
|
- w.write(cert)
|
|
- root_certs.append(dest)
|
|
- else:
|
|
- log.warn('getca: Wrong (or missing) MIME content type')
|
|
-
|
|
return root_certs
|
|
|
|
-
|
|
def find_global_trust_dir():
|
|
"""Return the global trust dir using known paths from various Linux distros."""
|
|
for trust_dir in global_trust_dirs:
|
|
@@ -271,11 +236,10 @@ def changed(new_data, old_data):
|
|
def cert_enroll(ca, ldb, trust_dir, private_dir, auth='Kerberos'):
|
|
"""Install the root certificate chain."""
|
|
data = dict({'files': [], 'templates': []}, **ca)
|
|
- url = 'http://%s/CertSrv/mscep/mscep.dll/pkiclient.exe?' % ca['hostname']
|
|
|
|
log.info("Try to get root or server certificates")
|
|
|
|
- root_certs = getca(ca, url, trust_dir)
|
|
+ root_certs = getca(ca, trust_dir)
|
|
data['files'].extend(root_certs)
|
|
global_trust_dir = find_global_trust_dir()
|
|
for src in root_certs:
|
|
diff --git a/selftest/knownfail.d/gpo-auto-enrol b/selftest/knownfail.d/gpo-auto-enrol
|
|
new file mode 100644
|
|
index 00000000000..4bf4b8e3c72
|
|
--- /dev/null
|
|
+++ b/selftest/knownfail.d/gpo-auto-enrol
|
|
@@ -0,0 +1,2 @@
|
|
+^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_advanced_gp_cert_auto_enroll_ext\(ad_dc:local\)
|
|
+^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_gp_cert_auto_enroll_ext\(ad_dc:local\)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From b5f1e5a313c5cd2dd4b7c8f21dff0272abc9b904 Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Thu, 26 Feb 2026 14:21:01 +1300
|
|
Subject: [PATCH 100/122] CVE-2026-3012: gp_auto_enrol: skip CAs not found in
|
|
LDAP
|
|
|
|
If a certificate is mentioned in a GPO but is not present as a
|
|
cACertificate attribute on a pKIEnrollmentService object, we have no way
|
|
of obtaining it, so we might as well forget it.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
|
|
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
|
|
---
|
|
python/samba/gp/gp_cert_auto_enroll_ext.py | 10 ++++++++++
|
|
1 file changed, 10 insertions(+)
|
|
|
|
diff --git a/python/samba/gp/gp_cert_auto_enroll_ext.py b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
index f1c55a0dbf1..c2cc73c80f0 100644
|
|
--- a/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
+++ b/python/samba/gp/gp_cert_auto_enroll_ext.py
|
|
@@ -457,11 +457,21 @@ class gp_cert_auto_enroll_ext(gp_pol_ext, gp_applier):
|
|
# This is a basic configuration.
|
|
cas = fetch_certification_authorities(ldb)
|
|
for _ca in cas:
|
|
+ if 'cACertificate' not in _ca:
|
|
+ log.warning(f"ignoring CA '{_ca['name']}' with no "
|
|
+ "cACertificate in LDAP.")
|
|
+ continue
|
|
+
|
|
self.apply(guid, _ca, cert_enroll, _ca, ldb, trust_dir,
|
|
private_dir)
|
|
ca_names.append(_ca['name'])
|
|
# If EndPoint.URI starts with "HTTPS//":
|
|
elif ca['URL'].lower().startswith('https://'):
|
|
+ if 'cACertificate' not in ca:
|
|
+ log.warning(f"ignoring CA '{ca['name']}' "
|
|
+ f"({ca['URL']}) with no "
|
|
+ "cACertificate in LDAP.")
|
|
+ continue
|
|
self.apply(guid, ca, cert_enroll, ca, ldb, trust_dir,
|
|
private_dir, auth=ca['auth'])
|
|
ca_names.append(ca['name'])
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From c9dee6ec8532681652de09f1e411cc25c6cb886f Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Fri, 27 Feb 2026 14:46:04 +1300
|
|
Subject: [PATCH 101/122] CVE-2026-3012: gpo tests should use real certificates
|
|
|
|
Or at least, more real than a short arbitrary byte string, so that
|
|
the certificates can be parsed.
|
|
|
|
This shows that certificate enrolment works via LDAP in the situations
|
|
where we would have fetched them via HTTP.
|
|
|
|
This does not fix the advanced_gp_cert_auto_enroll_ext test which
|
|
wants to install certificates it has no access too. This will not be
|
|
fixed in the security release.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16003
|
|
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Reviewed-by: Jennifer Sutton <jennifersutton@catalyst.net.nz>
|
|
---
|
|
python/samba/tests/gpo.py | 8 ++++----
|
|
selftest/knownfail.d/gpo-auto-enrol | 1 -
|
|
2 files changed, 4 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
|
|
index 067b92728d4..cbf53b88235 100644
|
|
--- a/python/samba/tests/gpo.py
|
|
+++ b/python/samba/tests/gpo.py
|
|
@@ -6918,7 +6918,7 @@ class GPOTests(tests.TestCase):
|
|
ldb.add({'dn': certa_dn,
|
|
'objectClass': 'certificationAuthority',
|
|
'authorityRevocationList': ['XXX'],
|
|
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
+ 'cACertificate': dummy_certificate(),
|
|
'certificateRevocationList': ['XXX'],
|
|
})
|
|
# Write the dummy pKIEnrollmentService
|
|
@@ -6926,7 +6926,7 @@ class GPOTests(tests.TestCase):
|
|
self.addCleanup(ldb.delete, enroll_dn)
|
|
ldb.add({'dn': enroll_dn,
|
|
'objectClass': 'pKIEnrollmentService',
|
|
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
+ 'cACertificate': dummy_certificate(),
|
|
'certificateTemplates': ['Machine'],
|
|
'dNSHostName': hostname,
|
|
})
|
|
@@ -7395,7 +7395,7 @@ class GPOTests(tests.TestCase):
|
|
ldb.add({'dn': certa_dn,
|
|
'objectClass': 'certificationAuthority',
|
|
'authorityRevocationList': ['XXX'],
|
|
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
+ 'cACertificate': dummy_certificate(),
|
|
'certificateRevocationList': ['XXX'],
|
|
})
|
|
# Write the dummy pKIEnrollmentService
|
|
@@ -7403,7 +7403,7 @@ class GPOTests(tests.TestCase):
|
|
self.addCleanup(ldb.delete, enroll_dn)
|
|
ldb.add({'dn': enroll_dn,
|
|
'objectClass': 'pKIEnrollmentService',
|
|
- 'cACertificate': b'0\x82\x03u0\x82\x02]\xa0\x03\x02\x01\x02\x02\x10I',
|
|
+ 'cACertificate': dummy_certificate(),
|
|
'certificateTemplates': ['Machine'],
|
|
'dNSHostName': hostname,
|
|
})
|
|
diff --git a/selftest/knownfail.d/gpo-auto-enrol b/selftest/knownfail.d/gpo-auto-enrol
|
|
index 4bf4b8e3c72..4b787a5ac86 100644
|
|
--- a/selftest/knownfail.d/gpo-auto-enrol
|
|
+++ b/selftest/knownfail.d/gpo-auto-enrol
|
|
@@ -1,2 +1 @@
|
|
^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_advanced_gp_cert_auto_enroll_ext\(ad_dc:local\)
|
|
-^samba\.tests\.gpo\.samba\.tests\.gpo\.GPOTests\.test_gp_cert_auto_enroll_ext\(ad_dc:local\)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 272d18c47aa7c8e1c4a5d7690f8cd2c17c0d4161 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 23 Apr 2026 18:20:15 +0200
|
|
Subject: [PATCH 102/122] CVE-2026-4480/CVE-2026-4408: lib/util: inline
|
|
string_sub2() into string_sub() the only caller
|
|
|
|
This will simplify further changes.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/substitute.c | 20 ++------------------
|
|
1 file changed, 2 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
|
|
index b7b5588da86..26362ca77b2 100644
|
|
--- a/lib/util/substitute.c
|
|
+++ b/lib/util/substitute.c
|
|
@@ -47,10 +47,9 @@
|
|
use of len==0 which was for no length checks to be done.
|
|
**/
|
|
|
|
-static void string_sub2(char *s,const char *pattern, const char *insert, size_t len,
|
|
- bool remove_unsafe_characters, bool replace_once,
|
|
- bool allow_trailing_dollar)
|
|
+void string_sub(char *s, const char *pattern, const char *insert, size_t len)
|
|
{
|
|
+ bool remove_unsafe_characters = true;
|
|
char *p;
|
|
size_t ls, lp, li, i;
|
|
|
|
@@ -79,13 +78,6 @@ static void string_sub2(char *s,const char *pattern, const char *insert, size_t
|
|
for (i=0;i<li;i++) {
|
|
switch (insert[i]) {
|
|
case '$':
|
|
- /* allow a trailing $
|
|
- * (as in machine accounts) */
|
|
- if (allow_trailing_dollar && (i == li - 1 )) {
|
|
- p[i] = insert[i];
|
|
- break;
|
|
- }
|
|
- FALL_THROUGH;
|
|
case '`':
|
|
case '"':
|
|
case '\'':
|
|
@@ -107,17 +99,9 @@ static void string_sub2(char *s,const char *pattern, const char *insert, size_t
|
|
}
|
|
s = p + li;
|
|
ls = ls + li - lp;
|
|
-
|
|
- if (replace_once)
|
|
- break;
|
|
}
|
|
}
|
|
|
|
-void string_sub(char *s,const char *pattern, const char *insert, size_t len)
|
|
-{
|
|
- string_sub2( s, pattern, insert, len, true, false, false );
|
|
-}
|
|
-
|
|
/**
|
|
Similar to string_sub() but allows for any character to be substituted.
|
|
Use with caution!
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4dd5b25a69b7b6ae9576e0da1514aa6bd9b6504d Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 23 Apr 2026 18:20:15 +0200
|
|
Subject: [PATCH 103/122] CVE-2026-4480/CVE-2026-4408: lib/util: remove unused
|
|
talloc_strdup(insert) from talloc_string_sub2()
|
|
|
|
The insert string is not modified, so we do not need to copy it.
|
|
|
|
This will simplify further changes.
|
|
|
|
Review with: git show --patience
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/substitute.c | 57 +++++++++++++++++++------------------------
|
|
1 file changed, 25 insertions(+), 32 deletions(-)
|
|
|
|
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
|
|
index 26362ca77b2..4a0c58ab3a7 100644
|
|
--- a/lib/util/substitute.c
|
|
+++ b/lib/util/substitute.c
|
|
@@ -157,7 +157,7 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
bool replace_once,
|
|
bool allow_trailing_dollar)
|
|
{
|
|
- char *p, *in;
|
|
+ char *p;
|
|
char *s;
|
|
char *string;
|
|
ssize_t ls,lp,li,ld, i;
|
|
@@ -175,22 +175,32 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
|
|
s = string;
|
|
|
|
- in = talloc_strdup(mem_ctx, insert);
|
|
- if (!in) {
|
|
- DEBUG(0, ("talloc_string_sub2: ENOMEM\n"));
|
|
- talloc_free(string);
|
|
- return NULL;
|
|
- }
|
|
ls = (ssize_t)strlen(s);
|
|
lp = (ssize_t)strlen(pattern);
|
|
li = (ssize_t)strlen(insert);
|
|
ld = li - lp;
|
|
|
|
- for (i=0;i<li;i++) {
|
|
- switch (in[i]) {
|
|
+ while ((p = strstr_m(s,pattern))) {
|
|
+ if (ld > 0) {
|
|
+ int offset = PTR_DIFF(s,string);
|
|
+ string = (char *)talloc_realloc_size(mem_ctx, string,
|
|
+ ls + ld + 1);
|
|
+ if (!string) {
|
|
+ DEBUG(0, ("talloc_string_sub: out of "
|
|
+ "memory!\n"));
|
|
+ return NULL;
|
|
+ }
|
|
+ p = string + offset + (p - s);
|
|
+ }
|
|
+ if (li != lp) {
|
|
+ memmove(p+li,p+lp,strlen(p+lp)+1);
|
|
+ }
|
|
+ for (i=0; i<li; i++) {
|
|
+ switch (insert[i]) {
|
|
case '$':
|
|
- /* allow a trailing $
|
|
- * (as in machine accounts) */
|
|
+ /*
|
|
+ * allow a trailing $ (as in machine accounts)
|
|
+ */
|
|
if (allow_trailing_dollar && (i == li - 1 )) {
|
|
break;
|
|
}
|
|
@@ -204,34 +214,18 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
case '\r':
|
|
case '\n':
|
|
if (remove_unsafe_characters) {
|
|
- in[i] = '_';
|
|
- break;
|
|
+ p[i] = '_';
|
|
+ continue;
|
|
}
|
|
|
|
FALL_THROUGH;
|
|
default:
|
|
/* ok */
|
|
break;
|
|
- }
|
|
- }
|
|
-
|
|
- while ((p = strstr_m(s,pattern))) {
|
|
- if (ld > 0) {
|
|
- int offset = PTR_DIFF(s,string);
|
|
- string = (char *)talloc_realloc_size(mem_ctx, string,
|
|
- ls + ld + 1);
|
|
- if (!string) {
|
|
- DEBUG(0, ("talloc_string_sub: out of "
|
|
- "memory!\n"));
|
|
- TALLOC_FREE(in);
|
|
- return NULL;
|
|
}
|
|
- p = string + offset + (p - s);
|
|
- }
|
|
- if (li != lp) {
|
|
- memmove(p+li,p+lp,strlen(p+lp)+1);
|
|
+
|
|
+ p[i] = insert[i];
|
|
}
|
|
- memcpy(p, in, li);
|
|
s = p + li;
|
|
ls += ld;
|
|
|
|
@@ -239,7 +233,6 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
break;
|
|
}
|
|
}
|
|
- TALLOC_FREE(in);
|
|
return string;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From aa8022cabec1ca534b88272c6f4ba2ae22e87c99 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 23 Apr 2026 18:20:15 +0200
|
|
Subject: [PATCH 104/122] CVE-2026-4480/CVE-2026-4408: lib/util: factor out a
|
|
mask_unsafe_character() helper function
|
|
|
|
This moves the logic into a single place and
|
|
makes if more flexible to be used with more
|
|
values than STRING_SUB_UNSAFE_CHARACTERS.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/substitute.c | 109 +++++++++++++++++++++---------------------
|
|
lib/util/substitute.h | 6 ++-
|
|
2 files changed, 60 insertions(+), 55 deletions(-)
|
|
|
|
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
|
|
index 4a0c58ab3a7..b9fe32e993e 100644
|
|
--- a/lib/util/substitute.c
|
|
+++ b/lib/util/substitute.c
|
|
@@ -35,6 +35,33 @@
|
|
* @brief Substitute utilities.
|
|
**/
|
|
|
|
+static inline
|
|
+char mask_unsafe_character(char in,
|
|
+ bool is_last,
|
|
+ bool allow_trailing_dollar,
|
|
+ const char *unsafe_characters,
|
|
+ char safe_out)
|
|
+{
|
|
+ const char *unsafe = NULL;
|
|
+
|
|
+ if (unsafe_characters == NULL) {
|
|
+ return in;
|
|
+ }
|
|
+
|
|
+ /* allow a trailing $ (as in machine accounts) */
|
|
+ if (allow_trailing_dollar && is_last && in == '$') {
|
|
+ return in;
|
|
+ }
|
|
+
|
|
+ unsafe = strchr(unsafe_characters, in);
|
|
+ if (unsafe != NULL) {
|
|
+ return safe_out;
|
|
+ }
|
|
+
|
|
+ /* ok */
|
|
+ return in;
|
|
+}
|
|
+
|
|
/**
|
|
Substitute a string for a pattern in another string. Make sure there is
|
|
enough room!
|
|
@@ -42,14 +69,16 @@
|
|
This routine looks for pattern in s and replaces it with
|
|
insert. It may do multiple replacements or just one.
|
|
|
|
- Any of " ; ' $ or ` in the insert string are replaced with _
|
|
+ Any of STRING_SUB_UNSAFE_CHARACTERS in the insert string are replaced with _
|
|
+
|
|
if len==0 then the string cannot be extended. This is different from the old
|
|
use of len==0 which was for no length checks to be done.
|
|
**/
|
|
|
|
void string_sub(char *s, const char *pattern, const char *insert, size_t len)
|
|
{
|
|
- bool remove_unsafe_characters = true;
|
|
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
|
|
+ char safe_character = '_';
|
|
char *p;
|
|
size_t ls, lp, li, i;
|
|
|
|
@@ -76,26 +105,18 @@ void string_sub(char *s, const char *pattern, const char *insert, size_t len)
|
|
memmove(p+li,p+lp,strlen(p+lp)+1);
|
|
}
|
|
for (i=0;i<li;i++) {
|
|
- switch (insert[i]) {
|
|
- case '$':
|
|
- case '`':
|
|
- case '"':
|
|
- case '\'':
|
|
- case ';':
|
|
- case '%':
|
|
- case '\r':
|
|
- case '\n':
|
|
- if ( remove_unsafe_characters ) {
|
|
- p[i] = '_';
|
|
- /* yes this break should be here
|
|
- * since we want to fall throw if
|
|
- * not replacing unsafe chars */
|
|
- break;
|
|
- }
|
|
- FALL_THROUGH;
|
|
- default:
|
|
- p[i] = insert[i];
|
|
- }
|
|
+ /*
|
|
+ * Without allow_trailing_dollar we don't
|
|
+ * need to calculate is_last...
|
|
+ */
|
|
+ const bool is_last = false;
|
|
+ const bool allow_trailing_dollar = false;
|
|
+
|
|
+ p[i] = mask_unsafe_character(insert[i],
|
|
+ is_last,
|
|
+ allow_trailing_dollar,
|
|
+ unsafe_characters,
|
|
+ safe_character);
|
|
}
|
|
s = p + li;
|
|
ls = ls + li - lp;
|
|
@@ -157,9 +178,11 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
bool replace_once,
|
|
bool allow_trailing_dollar)
|
|
{
|
|
- char *p;
|
|
- char *s;
|
|
- char *string;
|
|
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
|
|
+ const char safe_character = '_';
|
|
+ char *p = NULL,
|
|
+ char *s = NULL;
|
|
+ char *string = NULL;
|
|
ssize_t ls,lp,li,ld, i;
|
|
|
|
if (!insert || !pattern || !*pattern || !src) {
|
|
@@ -195,36 +218,14 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
if (li != lp) {
|
|
memmove(p+li,p+lp,strlen(p+lp)+1);
|
|
}
|
|
- for (i=0; i<li; i++) {
|
|
- switch (insert[i]) {
|
|
- case '$':
|
|
- /*
|
|
- * allow a trailing $ (as in machine accounts)
|
|
- */
|
|
- if (allow_trailing_dollar && (i == li - 1 )) {
|
|
- break;
|
|
- }
|
|
-
|
|
- FALL_THROUGH;
|
|
- case '`':
|
|
- case '"':
|
|
- case '\'':
|
|
- case ';':
|
|
- case '%':
|
|
- case '\r':
|
|
- case '\n':
|
|
- if (remove_unsafe_characters) {
|
|
- p[i] = '_';
|
|
- continue;
|
|
- }
|
|
-
|
|
- FALL_THROUGH;
|
|
- default:
|
|
- /* ok */
|
|
- break;
|
|
- }
|
|
+ for (i=0; i < li; i++) {
|
|
+ bool is_last = (i == li - 1);
|
|
|
|
- p[i] = insert[i];
|
|
+ p[i] = mask_unsafe_character(insert[i],
|
|
+ is_last,
|
|
+ allow_trailing_dollar,
|
|
+ unsafe_characters,
|
|
+ safe_character);
|
|
}
|
|
s = p + li;
|
|
ls += ld;
|
|
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
|
|
index 3134cfcdea5..e1a82859dac 100644
|
|
--- a/lib/util/substitute.h
|
|
+++ b/lib/util/substitute.h
|
|
@@ -26,6 +26,8 @@
|
|
|
|
#include <talloc.h>
|
|
|
|
+#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%\r\n"
|
|
+
|
|
/**
|
|
Substitute a string for a pattern in another string. Make sure there is
|
|
enough room!
|
|
@@ -33,7 +35,9 @@
|
|
This routine looks for pattern in s and replaces it with
|
|
insert. It may do multiple replacements.
|
|
|
|
- Any of " ; ' $ or ` in the insert string are replaced with _
|
|
+ Any of STRING_SUB_UNSAFE_CHARACTERS (see above) in the
|
|
+ insert string are replaced with _
|
|
+
|
|
if len==0 then the string cannot be extended. This is different from the old
|
|
use of len==0 which was for no length checks to be done.
|
|
**/
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 7d209a9a91c0c94b5d0069a83252df686752807d Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 30 Apr 2026 14:48:26 +0200
|
|
Subject: [PATCH 105/122] CVE-2026-4480/CVE-2026-4408: lib/util: split out
|
|
realloc_string_sub_raw()
|
|
|
|
This will allow realloc_string_sub2() to use it in order
|
|
to have the logic in one place only.
|
|
|
|
And it will also allow adjacted callers to be
|
|
more flexible.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/substitute.c | 85 ++++++++++++++++++++++++++++++-------------
|
|
lib/util/substitute.h | 18 +++++++++
|
|
2 files changed, 78 insertions(+), 25 deletions(-)
|
|
|
|
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
|
|
index b9fe32e993e..465aea86605 100644
|
|
--- a/lib/util/substitute.c
|
|
+++ b/lib/util/substitute.c
|
|
@@ -171,32 +171,24 @@ _PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, siz
|
|
* talloc version of string_sub2.
|
|
*/
|
|
|
|
-char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
- const char *pattern,
|
|
- const char *insert,
|
|
- bool remove_unsafe_characters,
|
|
- bool replace_once,
|
|
- bool allow_trailing_dollar)
|
|
+bool realloc_string_sub_raw(char **_string,
|
|
+ const char *pattern,
|
|
+ const char *insert,
|
|
+ bool replace_once,
|
|
+ bool allow_trailing_dollar,
|
|
+ const char *unsafe_characters,
|
|
+ char safe_character)
|
|
{
|
|
- const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
|
|
- const char safe_character = '_';
|
|
- char *p = NULL,
|
|
+ char *p = NULL;
|
|
char *s = NULL;
|
|
char *string = NULL;
|
|
ssize_t ls,lp,li,ld, i;
|
|
|
|
- if (!insert || !pattern || !*pattern || !src) {
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- string = talloc_strdup(mem_ctx, src);
|
|
- if (string == NULL) {
|
|
- DEBUG(0, ("talloc_string_sub2: "
|
|
- "talloc_strdup failed\n"));
|
|
- return NULL;
|
|
+ if (!insert || !pattern || !*pattern || !_string|| !*_string) {
|
|
+ return false;
|
|
}
|
|
|
|
- s = string;
|
|
+ s = string = *_string;
|
|
|
|
ls = (ssize_t)strlen(s);
|
|
lp = (ssize_t)strlen(pattern);
|
|
@@ -205,14 +197,13 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
|
|
while ((p = strstr_m(s,pattern))) {
|
|
if (ld > 0) {
|
|
- int offset = PTR_DIFF(s,string);
|
|
- string = (char *)talloc_realloc_size(mem_ctx, string,
|
|
- ls + ld + 1);
|
|
+ ptrdiff_t offset = PTR_DIFF(s,string);
|
|
+ string = talloc_realloc(NULL, string, char, ls + ld + 1);
|
|
if (!string) {
|
|
- DEBUG(0, ("talloc_string_sub: out of "
|
|
- "memory!\n"));
|
|
- return NULL;
|
|
+ DBG_ERR("out of memory(realloc)!\n");
|
|
+ return false;
|
|
}
|
|
+ *_string = string;
|
|
p = string + offset + (p - s);
|
|
}
|
|
if (li != lp) {
|
|
@@ -234,6 +225,50 @@ char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
break;
|
|
}
|
|
}
|
|
+ return true;
|
|
+}
|
|
+
|
|
+char *talloc_string_sub2(TALLOC_CTX *mem_ctx,
|
|
+ const char *src,
|
|
+ const char *pattern,
|
|
+ const char *insert,
|
|
+ bool remove_unsafe_characters,
|
|
+ bool replace_once,
|
|
+ bool allow_trailing_dollar)
|
|
+{
|
|
+ const char *unsafe_characters = NULL;
|
|
+ char safe_character = '\0';
|
|
+ char *string = NULL;
|
|
+ bool ok;
|
|
+
|
|
+ if (!insert || !pattern || !*pattern || !src) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (remove_unsafe_characters) {
|
|
+ unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
|
|
+ safe_character = '_';
|
|
+ }
|
|
+
|
|
+ string = talloc_strdup(mem_ctx, src);
|
|
+ if (string == NULL) {
|
|
+ DBG_ERR("out of memory, talloc_strdup(src)!\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ ok = realloc_string_sub_raw(&string,
|
|
+ pattern,
|
|
+ insert,
|
|
+ replace_once,
|
|
+ allow_trailing_dollar,
|
|
+ unsafe_characters,
|
|
+ safe_character);
|
|
+ if (!ok) {
|
|
+ TALLOC_FREE(string);
|
|
+ DBG_ERR("out of memory, realloc_string_sub_raw()!\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
return string;
|
|
}
|
|
|
|
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
|
|
index e1a82859dac..041a649fd18 100644
|
|
--- a/lib/util/substitute.h
|
|
+++ b/lib/util/substitute.h
|
|
@@ -51,6 +51,24 @@ void string_sub(char *s,const char *pattern, const char *insert, size_t len);
|
|
**/
|
|
void all_string_sub(char *s,const char *pattern,const char *insert, size_t len);
|
|
|
|
+/*
|
|
+ * If unsafe_characters is NULL all characters are allowed,
|
|
+ * if unsafe_characters is not NULL all characters caught
|
|
+ * by iscntrl() are also replaced by safe_character.
|
|
+ *
|
|
+ * *_string might be reallocated!
|
|
+ *
|
|
+ * On error *_string may still be reallocated and
|
|
+ * may contain partial replacements.
|
|
+ */
|
|
+bool realloc_string_sub_raw(char **_string,
|
|
+ const char *pattern,
|
|
+ const char *insert,
|
|
+ bool replace_once,
|
|
+ bool allow_trailing_dollar,
|
|
+ const char *unsafe_characters,
|
|
+ char safe_character);
|
|
+
|
|
char *talloc_string_sub2(TALLOC_CTX *mem_ctx, const char *src,
|
|
const char *pattern,
|
|
const char *insert,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 362a51adc666ae43b7e4bb24c38a59a2de0493ba Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Wed, 6 May 2026 17:23:39 +0200
|
|
Subject: [PATCH 106/122] CVE-2026-4480/CVE-2026-4408: s3:lib: fix potential
|
|
memory leak in talloc_sub_basic()
|
|
|
|
This makes the code easier to understand...
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/lib/substitute.c | 2 ++
|
|
1 file changed, 2 insertions(+)
|
|
|
|
diff --git a/source3/lib/substitute.c b/source3/lib/substitute.c
|
|
index 40eb15aee04..5121fcaac1c 100644
|
|
--- a/source3/lib/substitute.c
|
|
+++ b/source3/lib/substitute.c
|
|
@@ -317,6 +317,7 @@ char *talloc_sub_basic(TALLOC_CTX *mem_ctx,
|
|
}
|
|
|
|
tmp_ctx = talloc_stackframe();
|
|
+ a_string = talloc_steal(tmp_ctx, a_string);
|
|
|
|
for (s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) {
|
|
|
|
@@ -478,6 +479,7 @@ error:
|
|
TALLOC_FREE(a_string);
|
|
|
|
done:
|
|
+ a_string = talloc_steal(mem_ctx, a_string);
|
|
TALLOC_FREE(tmp_ctx);
|
|
return a_string;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 203717ae88b5a51de3542366dc0ea375cbd7dcf9 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 23 Apr 2026 21:11:27 +0200
|
|
Subject: [PATCH 107/122] CVE-2026-4480/CVE-2026-4408: s3:lib: let
|
|
realloc_string_sub2() use realloc_string_sub_raw()
|
|
|
|
We don't need this logic more than once!
|
|
|
|
But we leave the strange calling convention of
|
|
realloc_string_sub2(), where the caller it
|
|
not allowed to use the passed pointer when
|
|
NULL is returned...
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/lib/substitute_generic.c | 81 ++++++++++----------------------
|
|
1 file changed, 24 insertions(+), 57 deletions(-)
|
|
|
|
diff --git a/source3/lib/substitute_generic.c b/source3/lib/substitute_generic.c
|
|
index 26c5ee761f8..e0639f04eb8 100644
|
|
--- a/source3/lib/substitute_generic.c
|
|
+++ b/source3/lib/substitute_generic.c
|
|
@@ -37,71 +37,38 @@ char *realloc_string_sub2(char *string,
|
|
bool remove_unsafe_characters,
|
|
bool allow_trailing_dollar)
|
|
{
|
|
- char *p, *in;
|
|
- char *s;
|
|
- ssize_t ls,lp,li,ld, i;
|
|
+ const char *unsafe_characters = NULL;
|
|
+ char safe_character = '\0';
|
|
+ bool ok;
|
|
|
|
if (!insert || !pattern || !*pattern || !string || !*string)
|
|
return NULL;
|
|
|
|
- s = string;
|
|
+ if (remove_unsafe_characters) {
|
|
+ unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
|
|
+ safe_character = '_';
|
|
+ }
|
|
|
|
- in = talloc_strdup(talloc_tos(), insert);
|
|
- if (!in) {
|
|
- DEBUG(0, ("realloc_string_sub: out of memory!\n"));
|
|
+ ok = realloc_string_sub_raw(&string,
|
|
+ pattern,
|
|
+ insert,
|
|
+ false, /* replace_once */
|
|
+ allow_trailing_dollar,
|
|
+ unsafe_characters,
|
|
+ safe_character);
|
|
+ if (!ok) {
|
|
+ DBG_ERR("out of memory, realloc_string_sub_raw()!\n");
|
|
+ /*
|
|
+ * The calling convention of realloc_string_sub2()
|
|
+ * is very strange regarding stale string pointers.
|
|
+ *
|
|
+ * It is assumed the given string was allocated
|
|
+ * on talloc_tos(), so we just don't touch
|
|
+ * it at all here...
|
|
+ */
|
|
return NULL;
|
|
}
|
|
- ls = (ssize_t)strlen(s);
|
|
- lp = (ssize_t)strlen(pattern);
|
|
- li = (ssize_t)strlen(insert);
|
|
- ld = li - lp;
|
|
- for (i=0;i<li;i++) {
|
|
- switch (in[i]) {
|
|
- case '$':
|
|
- /* allow a trailing $
|
|
- * (as in machine accounts) */
|
|
- if (allow_trailing_dollar && (i == li - 1 )) {
|
|
- break;
|
|
- }
|
|
- FALL_THROUGH;
|
|
- case '`':
|
|
- case '"':
|
|
- case '\'':
|
|
- case ';':
|
|
- case '%':
|
|
- case '\r':
|
|
- case '\n':
|
|
- if ( remove_unsafe_characters ) {
|
|
- in[i] = '_';
|
|
- break;
|
|
- }
|
|
- FALL_THROUGH;
|
|
- default:
|
|
- /* ok */
|
|
- break;
|
|
- }
|
|
- }
|
|
|
|
- while ((p = strstr_m(s,pattern))) {
|
|
- if (ld > 0) {
|
|
- int offset = PTR_DIFF(s,string);
|
|
- string = talloc_realloc(NULL, string, char, ls + ld + 1);
|
|
- if (!string) {
|
|
- DEBUG(0, ("realloc_string_sub: "
|
|
- "out of memory!\n"));
|
|
- talloc_free(in);
|
|
- return NULL;
|
|
- }
|
|
- p = string + offset + (p - s);
|
|
- }
|
|
- if (li != lp) {
|
|
- memmove(p+li,p+lp,strlen(p+lp)+1);
|
|
- }
|
|
- memcpy(p, in, li);
|
|
- s = p + li;
|
|
- ls += ld;
|
|
- }
|
|
- talloc_free(in);
|
|
return string;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 92fa7a66d53334c1d27c46e3425a5a99d49395d5 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 23 Apr 2026 18:21:08 +0200
|
|
Subject: [PATCH 108/122] CVE-2026-4480/CVE-2026-4408: lib/util: let
|
|
mask_unsafe_character() check all control characters
|
|
|
|
There's no reason to mask only \r and \n.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/substitute.c | 8 +++++++-
|
|
lib/util/substitute.h | 6 +++---
|
|
2 files changed, 10 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
|
|
index 465aea86605..30989927da7 100644
|
|
--- a/lib/util/substitute.c
|
|
+++ b/lib/util/substitute.c
|
|
@@ -22,6 +22,7 @@
|
|
*/
|
|
|
|
#include "replace.h"
|
|
+#include "system/locale.h"
|
|
#include "debug.h"
|
|
#ifndef SAMBA_UTIL_CORE_ONLY
|
|
#include "charset/charset.h"
|
|
@@ -53,6 +54,10 @@ char mask_unsafe_character(char in,
|
|
return in;
|
|
}
|
|
|
|
+ if (iscntrl(in)) {
|
|
+ return safe_out;
|
|
+ }
|
|
+
|
|
unsafe = strchr(unsafe_characters, in);
|
|
if (unsafe != NULL) {
|
|
return safe_out;
|
|
@@ -69,7 +74,8 @@ char mask_unsafe_character(char in,
|
|
This routine looks for pattern in s and replaces it with
|
|
insert. It may do multiple replacements or just one.
|
|
|
|
- Any of STRING_SUB_UNSAFE_CHARACTERS in the insert string are replaced with _
|
|
+ Any of STRING_SUB_UNSAFE_CHARACTERS and any character
|
|
+ caught by calling iscntrl() in the insert string are replaced with _
|
|
|
|
if len==0 then the string cannot be extended. This is different from the old
|
|
use of len==0 which was for no length checks to be done.
|
|
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
|
|
index 041a649fd18..b183d864671 100644
|
|
--- a/lib/util/substitute.h
|
|
+++ b/lib/util/substitute.h
|
|
@@ -26,7 +26,7 @@
|
|
|
|
#include <talloc.h>
|
|
|
|
-#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%\r\n"
|
|
+#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%"
|
|
|
|
/**
|
|
Substitute a string for a pattern in another string. Make sure there is
|
|
@@ -35,8 +35,8 @@
|
|
This routine looks for pattern in s and replaces it with
|
|
insert. It may do multiple replacements.
|
|
|
|
- Any of STRING_SUB_UNSAFE_CHARACTERS (see above) in the
|
|
- insert string are replaced with _
|
|
+ Any of STRING_SUB_UNSAFE_CHARACTERS (see above) and any character
|
|
+ caught by calling iscntrl() in the insert string are replaced with _
|
|
|
|
if len==0 then the string cannot be extended. This is different from the old
|
|
use of len==0 which was for no length checks to be done.
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 31f45a68954ee6597b520faa70dbd8c7f4169c67 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 23 Apr 2026 18:21:08 +0200
|
|
Subject: [PATCH 109/122] CVE-2026-4480/CVE-2026-4408: lib/util: add more
|
|
unsafe characters to STRING_SUB_UNSAFE_CHARACTERS
|
|
|
|
|&<> are unsafe characters for shell processing.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/substitute.h | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
|
|
index b183d864671..41f56c73ba2 100644
|
|
--- a/lib/util/substitute.h
|
|
+++ b/lib/util/substitute.h
|
|
@@ -26,7 +26,7 @@
|
|
|
|
#include <talloc.h>
|
|
|
|
-#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%"
|
|
+#define STRING_SUB_UNSAFE_CHARACTERS "$`\"';%|&<>"
|
|
|
|
/**
|
|
Substitute a string for a pattern in another string. Make sure there is
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 076a15e3f20c8bca24bc92960770737b5b5d55cd Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 8 May 2026 22:33:32 +0200
|
|
Subject: [PATCH 110/122] CVE-2026-4480/CVE-2026-4408: lib/util: let
|
|
log_escape() make use of iscntrl()
|
|
|
|
using iscntrl() also handles 0x7F (DEL).
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/util_str_escape.c | 5 +++--
|
|
1 file changed, 3 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/lib/util/util_str_escape.c b/lib/util/util_str_escape.c
|
|
index ea0fcc2f905..908f70fa47a 100644
|
|
--- a/lib/util/util_str_escape.c
|
|
+++ b/lib/util/util_str_escape.c
|
|
@@ -18,6 +18,7 @@
|
|
*/
|
|
|
|
#include "replace.h"
|
|
+#include "system/locale.h"
|
|
#include "lib/util/debug.h"
|
|
#include "lib/util/util_str_escape.h"
|
|
|
|
@@ -28,7 +29,7 @@
|
|
*/
|
|
static size_t encoded_length(char c)
|
|
{
|
|
- if (c != '\\' && c > 0x1F) {
|
|
+ if (c != '\\' && !iscntrl(c)) {
|
|
return 1;
|
|
} else {
|
|
switch (c) {
|
|
@@ -79,7 +80,7 @@ char *log_escape(TALLOC_CTX *frame, const char *in)
|
|
c = in;
|
|
e = encoded;
|
|
while (*c) {
|
|
- if (*c != '\\' && *c > 0x1F) {
|
|
+ if (*c != '\\' && !iscntrl((unsigned char)(*c))) {
|
|
*e++ = *c++;
|
|
} else {
|
|
switch (*c) {
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4384eeb867ddb35d217b7f6232b89e7c66966fb8 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 7 May 2026 18:10:50 +0200
|
|
Subject: [PATCH 111/122] CVE-2026-4480/CVE-2026-4408: lib/util: add
|
|
talloc_string_sub_{mixed_quoting,unsafe}() helpers
|
|
|
|
This is the basic helper function for the security problems.
|
|
|
|
talloc_string_sub_mixed_quoting() checks for strange quoting
|
|
in smb.conf options.
|
|
|
|
And talloc_string_sub_unsafe() tries to autodetect how the unsafe
|
|
(client controlled value) and masked and single quote it,
|
|
as a fallback for strange quoting a fixed fallback string
|
|
is used and the caller should warn the admin and give
|
|
hints how to fix the configuration.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Pair-Programmed-With: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/substitute.c | 260 ++++++++++++++++++++++++++++++++++++++++++
|
|
lib/util/substitute.h | 17 +++
|
|
2 files changed, 277 insertions(+)
|
|
|
|
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
|
|
index 30989927da7..406d8424be1 100644
|
|
--- a/lib/util/substitute.c
|
|
+++ b/lib/util/substitute.c
|
|
@@ -25,6 +25,8 @@
|
|
#include "system/locale.h"
|
|
#include "debug.h"
|
|
#ifndef SAMBA_UTIL_CORE_ONLY
|
|
+#include "lib/util/fault.h"
|
|
+#include "lib/util/talloc_stack.h"
|
|
#include "charset/charset.h"
|
|
#else
|
|
#include "charset_compat.h"
|
|
@@ -297,3 +299,261 @@ char *talloc_all_string_sub(TALLOC_CTX *ctx,
|
|
return talloc_string_sub2(ctx, src, pattern, insert,
|
|
false, false, false);
|
|
}
|
|
+
|
|
+#ifndef SAMBA_UTIL_CORE_ONLY
|
|
+
|
|
+bool talloc_string_sub_mixed_quoting(const char *full_cmd, char variable_char)
|
|
+{
|
|
+ /*
|
|
+ * Try to make sure talloc_string_sub_unsafe()
|
|
+ * won't return NULL, instead talloc_stackframe_pool()
|
|
+ * would panic
|
|
+ */
|
|
+ size_t cmd_len = full_cmd != NULL ? strlen(full_cmd) : 0;
|
|
+ size_t pool_size = 512 + cmd_len;
|
|
+ TALLOC_CTX *frame = talloc_stackframe_pool(pool_size);
|
|
+ char *cmd = NULL;
|
|
+ bool modified = false;
|
|
+ bool masked = false;
|
|
+ bool mixed_fallback = false;
|
|
+
|
|
+ cmd = talloc_string_sub_unsafe(frame,
|
|
+ full_cmd,
|
|
+ variable_char,
|
|
+ "U", /* unsafe_value */
|
|
+ "'\"%", /* unsafe_characters */
|
|
+ '_', /* safe_character */
|
|
+ "F", /* fallback_value */
|
|
+ &modified,
|
|
+ &masked,
|
|
+ &mixed_fallback);
|
|
+ if (cmd == NULL) {
|
|
+ mixed_fallback = false;
|
|
+ }
|
|
+ TALLOC_FREE(frame);
|
|
+ return mixed_fallback;
|
|
+}
|
|
+
|
|
+char *talloc_string_sub_unsafe(TALLOC_CTX *mem_ctx,
|
|
+ const char *orig_cmd,
|
|
+ char variable_char,
|
|
+ const char *unsafe_value,
|
|
+ const char *unsafe_characters,
|
|
+ char safe_character,
|
|
+ const char *fallback_value,
|
|
+ bool *_modified,
|
|
+ bool *_masked,
|
|
+ bool *_mixed_fallback)
|
|
+{
|
|
+ TALLOC_CTX *frame = talloc_stackframe();
|
|
+ const char variable[3] =
|
|
+ { '%', variable_char, '\0' };
|
|
+ const char variable_s_quoted[5] =
|
|
+ { '\'', '%', variable_char, '\'', '\0' };
|
|
+ const char variable_d_quoted[5] =
|
|
+ { '"', '%', variable_char, '"', '\0' };
|
|
+ char *cmd = NULL;
|
|
+ char *masked_value = NULL;
|
|
+ char *quoted_value = NULL;
|
|
+ bool has_s_quotes;
|
|
+ bool has_d_quotes;
|
|
+ bool has_variable;
|
|
+ bool has_variable_s_quoted;
|
|
+ bool has_variable_d_quoted;
|
|
+ bool modified = false;
|
|
+ bool masked = false;
|
|
+ bool mixed_fallback = false;
|
|
+ bool ok;
|
|
+
|
|
+ /*
|
|
+ * The unsafe_characters argument should contain
|
|
+ * single and double quotes.
|
|
+ * Otherwise We can't safely handle this.
|
|
+ */
|
|
+ SMB_ASSERT(unsafe_characters != NULL);
|
|
+ SMB_ASSERT(strchr(unsafe_characters, '\'') != NULL);
|
|
+ SMB_ASSERT(strchr(unsafe_characters, '"') != NULL);
|
|
+ SMB_ASSERT(strchr(unsafe_characters, '%') != NULL);
|
|
+
|
|
+ cmd = talloc_strdup(mem_ctx, orig_cmd);
|
|
+ if (cmd == NULL) {
|
|
+ TALLOC_FREE(frame);
|
|
+ return NULL;
|
|
+ }
|
|
+ cmd = talloc_steal(frame, cmd);
|
|
+
|
|
+ has_variable = strstr(orig_cmd, variable) != NULL;
|
|
+ if (!has_variable) {
|
|
+ /*
|
|
+ * Nothing to do...
|
|
+ */
|
|
+ goto done;
|
|
+ }
|
|
+ modified = true;
|
|
+
|
|
+ /*
|
|
+ * Replace all unsafe characters as well as control
|
|
+ * characters.
|
|
+ *
|
|
+ * Note that we start with masked_value = "%u"
|
|
+ * and then replace "%u" with unsafe_value,
|
|
+ * as a result we have a masked version of
|
|
+ * unsafe_value.
|
|
+ *
|
|
+ * And don't allow option injected like
|
|
+ *
|
|
+ * '-h value'
|
|
+ * '--help value'
|
|
+ *
|
|
+ */
|
|
+ masked_value = talloc_strdup(frame, variable);
|
|
+ if (masked_value == NULL) {
|
|
+ goto nomem;
|
|
+ }
|
|
+ ok = realloc_string_sub_raw(&masked_value,
|
|
+ variable,
|
|
+ unsafe_value,
|
|
+ false, /* replace_once */
|
|
+ false, /* allow_trailing_dollar */
|
|
+ unsafe_characters,
|
|
+ safe_character);
|
|
+ if (!ok) {
|
|
+ goto nomem;
|
|
+ }
|
|
+ if (masked_value[0] == '-') {
|
|
+ masked_value[0] = safe_character;
|
|
+ }
|
|
+ masked = strcmp(masked_value, unsafe_value) != 0;
|
|
+
|
|
+retry:
|
|
+
|
|
+ has_s_quotes = strchr(cmd, '\'') != NULL;
|
|
+ has_d_quotes = strchr(cmd, '"') != NULL;
|
|
+ has_variable = strstr(cmd, variable) != NULL;
|
|
+ has_variable_s_quoted = strstr(cmd, variable_s_quoted) != NULL;
|
|
+ has_variable_d_quoted = strstr(cmd, variable_d_quoted) != NULL;
|
|
+
|
|
+ if (has_variable_s_quoted) {
|
|
+ /*
|
|
+ * In smb.conf we have something like
|
|
+ *
|
|
+ * some script = /usr/bin/script '%u'
|
|
+ *
|
|
+ * It is safe to replace '%u' (or '%J' etc, depending
|
|
+ * on variable_char) with '<masked_value>' if
|
|
+ * masked_value does not contain single quotes. We
|
|
+ * have checked that.
|
|
+ */
|
|
+
|
|
+ if (quoted_value == NULL) {
|
|
+ quoted_value = talloc_asprintf(frame, "'%s'",
|
|
+ masked_value);
|
|
+ if (quoted_value == NULL) {
|
|
+ goto nomem;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ok = realloc_string_sub_raw(&cmd,
|
|
+ variable_s_quoted,
|
|
+ quoted_value,
|
|
+ false, /* replace_once */
|
|
+ false, /* allow_trailing_dollar */
|
|
+ NULL, /* unsafe_characters */
|
|
+ '\0'); /* safe_character */
|
|
+ if (!ok) {
|
|
+ goto nomem;
|
|
+ }
|
|
+
|
|
+ goto retry;
|
|
+ }
|
|
+
|
|
+ if (has_variable_d_quoted && !has_s_quotes) {
|
|
+ /*
|
|
+ * replace the "%u"
|
|
+ *
|
|
+ * some script = /usr/bin/script "%u"
|
|
+ *
|
|
+ * with '%u' and try the '%u' -> 'variable' substitution
|
|
+ * again.
|
|
+ */
|
|
+
|
|
+ ok = realloc_string_sub_raw(&cmd,
|
|
+ variable_d_quoted,
|
|
+ variable_s_quoted,
|
|
+ false, /* replace_once */
|
|
+ false, /* allow_trailing_dollar */
|
|
+ NULL, /* unsafe_characters */
|
|
+ '\0'); /* safe_character */
|
|
+ if (!ok) {
|
|
+ goto nomem;
|
|
+ }
|
|
+
|
|
+ goto retry;
|
|
+ }
|
|
+
|
|
+ if (has_variable && !has_s_quotes && !has_d_quotes) {
|
|
+ /*
|
|
+ * In this case:
|
|
+ *
|
|
+ * some script = /usr/bin/script %u
|
|
+ *
|
|
+ * we can safely substitute %u -> '%u' and try the
|
|
+ * single quote test again.
|
|
+ */
|
|
+
|
|
+ ok = realloc_string_sub_raw(&cmd,
|
|
+ variable,
|
|
+ variable_s_quoted,
|
|
+ false, /* replace_once */
|
|
+ false, /* allow_trailing_dollar */
|
|
+ NULL, /* unsafe_characters */
|
|
+ '\0'); /* safe_character */
|
|
+ if (!ok) {
|
|
+ goto nomem;
|
|
+ }
|
|
+
|
|
+ goto retry;
|
|
+ }
|
|
+
|
|
+ if (has_variable) {
|
|
+ /*
|
|
+ * There are single or double quotes, but not tightly
|
|
+ * bound around a %u.
|
|
+ *
|
|
+ * Or there's a mix of single and double quotes.
|
|
+ *
|
|
+ * We just use a generic fallback value.
|
|
+ * and let the caller warn about this
|
|
+ * and give the admin a hind to fix the smb.conf
|
|
+ * option.
|
|
+ */
|
|
+ mixed_fallback = true;
|
|
+
|
|
+ ok = realloc_string_sub_raw(&cmd,
|
|
+ variable,
|
|
+ fallback_value,
|
|
+ false, /* replace_once */
|
|
+ false, /* allow_trailing_dollar */
|
|
+ NULL, /* unsafe_characters */
|
|
+ '\0'); /* safe_character */
|
|
+ if (!ok) {
|
|
+ goto nomem;
|
|
+ }
|
|
+ }
|
|
+
|
|
+done:
|
|
+ *_modified = modified;
|
|
+ *_masked = masked;
|
|
+ *_mixed_fallback = mixed_fallback;
|
|
+ cmd = talloc_steal(mem_ctx, cmd);
|
|
+ TALLOC_FREE(frame);
|
|
+ return cmd;
|
|
+
|
|
+nomem:
|
|
+ *_modified = false;
|
|
+ *_masked = false;
|
|
+ *_mixed_fallback = false;
|
|
+ TALLOC_FREE(frame);
|
|
+ return NULL;
|
|
+}
|
|
+#endif /* ! SAMBA_UTIL_CORE_ONLY */
|
|
diff --git a/lib/util/substitute.h b/lib/util/substitute.h
|
|
index 41f56c73ba2..b8205055da1 100644
|
|
--- a/lib/util/substitute.h
|
|
+++ b/lib/util/substitute.h
|
|
@@ -83,4 +83,21 @@ char *talloc_all_string_sub(TALLOC_CTX *ctx,
|
|
const char *src,
|
|
const char *pattern,
|
|
const char *insert);
|
|
+
|
|
+#ifndef SAMBA_UTIL_CORE_ONLY
|
|
+bool talloc_string_sub_mixed_quoting(const char *full_cmd, char variable_char);
|
|
+
|
|
+char *talloc_string_sub_unsafe(TALLOC_CTX *mem_ctx,
|
|
+ const char *orig_cmd,
|
|
+ char variable_char,
|
|
+ const char *unsafe_value,
|
|
+ const char *unsafe_characters,
|
|
+ char safe_character,
|
|
+ const char *fallback_value,
|
|
+ bool *_modified,
|
|
+ bool *_masked,
|
|
+ bool *_mixed_fallback);
|
|
+
|
|
+#endif /* ! SAMBA_UTIL_CORE_ONLY */
|
|
+
|
|
#endif /* _SAMBA_SUBSTITUTE_H_ */
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d4588ae23b22671f3960c396644941d11f538778 Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Sat, 9 May 2026 22:02:47 +1200
|
|
Subject: [PATCH 112/122] CVE-2026-4480/CVE-2026-4408: lib/util: add
|
|
test_string_sub unittests
|
|
|
|
This demonstrates the logic of talloc_string_sub_{mixed_quoting,unsafe}()
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
---
|
|
lib/util/tests/test_string_sub.c | 1044 ++++++++++++++++++++++++++++++
|
|
lib/util/wscript_build | 6 +
|
|
selftest/tests.py | 2 +
|
|
3 files changed, 1052 insertions(+)
|
|
create mode 100644 lib/util/tests/test_string_sub.c
|
|
|
|
diff --git a/lib/util/tests/test_string_sub.c b/lib/util/tests/test_string_sub.c
|
|
new file mode 100644
|
|
index 00000000000..da97c1c936c
|
|
--- /dev/null
|
|
+++ b/lib/util/tests/test_string_sub.c
|
|
@@ -0,0 +1,1044 @@
|
|
+
|
|
+#include <stdarg.h>
|
|
+#include <stddef.h>
|
|
+#include <stdint.h>
|
|
+#include <setjmp.h>
|
|
+#include <sys/stat.h>
|
|
+#include "replace.h"
|
|
+#include <cmocka.h>
|
|
+#include "talloc.h"
|
|
+
|
|
+#include "../substitute.h"
|
|
+
|
|
+/* set _DEBUG_VERBOSE to print more. */
|
|
+#define _DEBUG_VERBOSE
|
|
+
|
|
+#ifdef _DEBUG_VERBOSE
|
|
+#define debug_message(...) print_message(__VA_ARGS__)
|
|
+#else
|
|
+#define debug_message(...) /* debug_message */
|
|
+#endif
|
|
+
|
|
+
|
|
+static int setup_talloc_context(void **state)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
|
|
+ *state = mem_ctx;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int teardown_talloc_context(void **state)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = *state;
|
|
+ TALLOC_FREE(mem_ctx);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct cmd_expansion {
|
|
+ const char *lp_cmd;
|
|
+ const char *username;
|
|
+ const char *result_cmd;
|
|
+ bool modified;
|
|
+ bool masked;
|
|
+ bool mixed_fallback;
|
|
+};
|
|
+
|
|
+static void _test_talloc_string_sub_unsafe(void **state,
|
|
+ struct cmd_expansion expansions[],
|
|
+ size_t n_expansions,
|
|
+ const char *unsafe_characters)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = *state;
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < n_expansions; i++) {
|
|
+ struct cmd_expansion t = expansions[i];
|
|
+ char *result_cmd = NULL;
|
|
+ bool masked;
|
|
+ bool mixed_fallback;
|
|
+ bool modified;
|
|
+ bool flags_correct;
|
|
+ bool mixed;
|
|
+ int cmp;
|
|
+
|
|
+ mixed = talloc_string_sub_mixed_quoting(t.lp_cmd, 'u');
|
|
+
|
|
+ result_cmd = talloc_string_sub_unsafe(mem_ctx,
|
|
+ t.lp_cmd,
|
|
+ 'u',
|
|
+ t.username,
|
|
+ unsafe_characters,
|
|
+ '_',
|
|
+ "FallbackUsername",
|
|
+ &modified,
|
|
+ &masked,
|
|
+ &mixed_fallback);
|
|
+ assert_ptr_not_equal(result_cmd, NULL);
|
|
+ assert_ptr_not_equal(t.result_cmd, NULL);
|
|
+
|
|
+ cmp = strcmp(t.result_cmd, result_cmd);
|
|
+ flags_correct = (modified == t.modified &&
|
|
+ masked == t.masked &&
|
|
+ mixed_fallback == t.mixed_fallback);
|
|
+
|
|
+ if (cmp == 0) {
|
|
+ debug_message("[%zu] «%s» «%s» -> «%s»; AS EXPECTED\n",
|
|
+ i, t.lp_cmd,
|
|
+ t.username,
|
|
+ result_cmd);
|
|
+ } else {
|
|
+ debug_message("[%zu] «%s» «%s»; "
|
|
+ "expected [%zu] «%s» got [%zu] «%s»\033[1;31m BAD! \033[0m\n",
|
|
+ i, t.lp_cmd,
|
|
+ t.username,
|
|
+ strlen(t.result_cmd), t.result_cmd,
|
|
+ strlen(result_cmd), result_cmd);
|
|
+ }
|
|
+ assert_int_equal(cmp, 0);
|
|
+ if (!flags_correct) {
|
|
+ debug_message("[%zu] ", i);
|
|
+#define _FLAG(x) debug_message((t. x == x) ? "%s: %s √; ": \
|
|
+ "%s \033[1;31m expected %s \033[0m; ", \
|
|
+ #x, t.x ? "true": "false");
|
|
+ _FLAG(modified);
|
|
+ _FLAG(masked);
|
|
+ _FLAG(mixed_fallback);
|
|
+ debug_message("\n");
|
|
+ }
|
|
+ assert_int_equal(flags_correct, true);
|
|
+ if (mixed_fallback != mixed) {
|
|
+ debug_message("[%zu] %s mixed \033[1;31m expected %s \033[0m; ",
|
|
+ i, t.lp_cmd,
|
|
+ mixed_fallback ? "true": "false");
|
|
+ }
|
|
+ assert_int_equal(mixed_fallback, mixed);
|
|
+#undef _FLAG
|
|
+ }
|
|
+ debug_message("ALL correct\n");
|
|
+}
|
|
+
|
|
+static void test_talloc_string_sub_unsafe(void **state)
|
|
+{
|
|
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
|
|
+
|
|
+ static struct cmd_expansion expansions[] = {
|
|
+ {
|
|
+ "/bin/echo \"bob'",
|
|
+ "bob",
|
|
+ "/bin/echo \"bob'",
|
|
+ false,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob'",
|
|
+ "/bin/echo 'bob_'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob'''",
|
|
+ "/bin/echo 'bob___'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob\'",
|
|
+ "/bin/echo 'bob_'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u",
|
|
+ "bob bob bob",
|
|
+ "/bin/echo 'FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\"",
|
|
+ " ",
|
|
+ "/bin/echo ' '",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo \"--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob !0",
|
|
+ "/bin/echo \"--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "!0",
|
|
+ "/bin/echo '!0'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob \\",
|
|
+ "/bin/echo \"--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "bob >> x",
|
|
+ "/bin/echo --uu='bob __ x'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '--uu=%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo '--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "bob",
|
|
+ "/bin/echo --uu='bob'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu'=%u'",
|
|
+ "bob",
|
|
+ "/bin/echo --uu'=FallbackUsername'",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu'=%u'",
|
|
+ "`ls`",
|
|
+ "/bin/echo --uu'=FallbackUsername'",
|
|
+ true,
|
|
+ true,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "u%u%u%u%u",
|
|
+ "/bin/echo --uu='u_u_u_u_u'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "$(ls)",
|
|
+ "/bin/echo --uu='_(ls)'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "`ls`",
|
|
+ "/bin/echo --uu='_ls_'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='1' %u",
|
|
+ "`ls`",
|
|
+ "/bin/echo --uu='1' FallbackUsername",
|
|
+ true,
|
|
+ true,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu=\"'%u'\"",
|
|
+ "bob",
|
|
+ "/bin/echo --uu=\"'bob'\"",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u' --yy='%u' '%u' %u",
|
|
+ "bob",
|
|
+ "/bin/echo --uu='bob' --yy='bob' 'bob' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu=%u%u%u'' %user 50%u",
|
|
+ "bob",
|
|
+ "/bin/echo --uu=FallbackUsernameFallbackUsernameFallbackUsername'' FallbackUsernameser 50FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "!!",
|
|
+ "/bin/echo '!!'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ ">xxx",
|
|
+ "/bin/echo '_xxx'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "3",
|
|
+ "/bin/echo '3'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "3$",
|
|
+ "/bin/echo '3_'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "comp$",
|
|
+ "/bin/echo 'comp_'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "3$3",
|
|
+ "/bin/echo '3_3'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "q $3",
|
|
+ "/bin/echo 'q _3'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u",
|
|
+ "q $3",
|
|
+ "/bin/echo 'FallbackUsername",
|
|
+ true,
|
|
+ true,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s '%u' %u",
|
|
+ "āāā",
|
|
+ "/bin/echo -s 'āāā' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s '%u' %u",
|
|
+ "-āāā",
|
|
+ "/bin/echo -s '_āāā' FallbackUsername",
|
|
+ true,
|
|
+ true,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s %u",
|
|
+ "āāā",
|
|
+ "/bin/echo -s 'āāā'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s %u",
|
|
+ "a -a",
|
|
+ "/bin/echo -s 'a -a'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s=%u %u",
|
|
+ "ā -a",
|
|
+ "/bin/echo -s='ā -a' 'ā -a'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s=\"%u %u\"",
|
|
+ "ā -a",
|
|
+ "/bin/echo -s=\"FallbackUsername FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -m='fridge' %u",
|
|
+ "ā -ß",
|
|
+ "/bin/echo -m='fridge' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -m='fridge' %u",
|
|
+ "-ā -a",
|
|
+ "/bin/echo -m='fridge' FallbackUsername",
|
|
+ true,
|
|
+ true,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "-n",
|
|
+ "/bin/echo '_n'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "o'clock",
|
|
+ "/bin/echo 'o_clock'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"bob'",
|
|
+ "bob",
|
|
+ "/bin/echo \"bob'",
|
|
+ false,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\"",
|
|
+ "%u",
|
|
+ "/bin/echo '_u'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"$(ls)\"",
|
|
+ "%u",
|
|
+ "/bin/echo \"$(ls)\"",
|
|
+ false,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "\\",
|
|
+ "/bin/echo '\\'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "\\",
|
|
+ "/bin/echo '\\'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\"",
|
|
+ "\\",
|
|
+ "/bin/echo '\\'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\" %u",
|
|
+ "\\",
|
|
+ "/bin/echo '\\' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u' \"%u\" %u",
|
|
+ "\\",
|
|
+ "/bin/echo '\\' \"FallbackUsername\" FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u' \"%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob' \"FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ };
|
|
+
|
|
+ _test_talloc_string_sub_unsafe(state,
|
|
+ expansions,
|
|
+ ARRAY_SIZE(expansions),
|
|
+ unsafe_characters);
|
|
+}
|
|
+
|
|
+static void test_talloc_string_sub_unsafe_minimal_unsafe_chars(void **state)
|
|
+{
|
|
+ const char *unsafe_characters = "\"'%";
|
|
+
|
|
+ static struct cmd_expansion expansions[] = {
|
|
+ {
|
|
+ "/bin/echo \"bob'",
|
|
+ "bob",
|
|
+ "/bin/echo \"bob'",
|
|
+ false,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob'",
|
|
+ "/bin/echo 'bob_'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob'''",
|
|
+ "/bin/echo 'bob___'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob\'",
|
|
+ "/bin/echo 'bob_'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u",
|
|
+ "bob bob bob",
|
|
+ "/bin/echo 'FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\"",
|
|
+ " ",
|
|
+ "/bin/echo ' '",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo \"--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob !0",
|
|
+ "/bin/echo \"--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "!0",
|
|
+ "/bin/echo '!0'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob \\",
|
|
+ "/bin/echo \"--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "bob >> x",
|
|
+ "/bin/echo --uu='bob >> x'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '--uu=%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo '--uu=FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "bob",
|
|
+ "/bin/echo --uu='bob'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu'=%u'",
|
|
+ "bob",
|
|
+ "/bin/echo --uu'=FallbackUsername'",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu'=%u'",
|
|
+ "`ls`",
|
|
+ "/bin/echo --uu'=FallbackUsername'",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "u%u%u%u%u",
|
|
+ "/bin/echo --uu='u_u_u_u_u'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "$(ls)",
|
|
+ "/bin/echo --uu='$(ls)'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "`ls`",
|
|
+ "/bin/echo --uu='`ls`'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='1' %u",
|
|
+ "`ls`",
|
|
+ "/bin/echo --uu='1' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu=\"'%u'\"",
|
|
+ "bob",
|
|
+ "/bin/echo --uu=\"'bob'\"",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u' --yy='%u' '%u' %u",
|
|
+ "bob",
|
|
+ "/bin/echo --uu='bob' --yy='bob' 'bob' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu=%u%u%u'' %user 50%u",
|
|
+ "bob",
|
|
+ "/bin/echo --uu=FallbackUsernameFallbackUsernameFallbackUsername'' FallbackUsernameser 50FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "!!",
|
|
+ "/bin/echo '!!'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ ">xxx",
|
|
+ "/bin/echo '>xxx'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "3",
|
|
+ "/bin/echo '3'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "3$",
|
|
+ "/bin/echo '3$'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "comp$",
|
|
+ "/bin/echo 'comp$'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "3$3",
|
|
+ "/bin/echo '3$3'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "q $3",
|
|
+ "/bin/echo 'q $3'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u",
|
|
+ "q $3",
|
|
+ "/bin/echo 'FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s '%u' %u",
|
|
+ "āāā",
|
|
+ "/bin/echo -s 'āāā' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s '%u' %u",
|
|
+ "-āāā",
|
|
+ "/bin/echo -s '_āāā' FallbackUsername",
|
|
+ true,
|
|
+ true,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s %u",
|
|
+ "āāā",
|
|
+ "/bin/echo -s 'āāā'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s %u",
|
|
+ "a -a",
|
|
+ "/bin/echo -s 'a -a'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s=%u %u",
|
|
+ "ā -a",
|
|
+ "/bin/echo -s='ā -a' 'ā -a'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s=\"%u %u\"",
|
|
+ "ā -a",
|
|
+ "/bin/echo -s=\"FallbackUsername FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -m='fridge' %u",
|
|
+ "ā -ß",
|
|
+ "/bin/echo -m='fridge' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -m='fridge' %u",
|
|
+ "-ā -a",
|
|
+ "/bin/echo -m='fridge' FallbackUsername",
|
|
+ true,
|
|
+ true,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "-n",
|
|
+ "/bin/echo '_n'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "o'clock",
|
|
+ "/bin/echo 'o_clock'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"bob'",
|
|
+ "bob",
|
|
+ "/bin/echo \"bob'",
|
|
+ false,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\"",
|
|
+ "%u",
|
|
+ "/bin/echo '_u'",
|
|
+ true,
|
|
+ true,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"$(ls)\"",
|
|
+ "%u",
|
|
+ "/bin/echo \"$(ls)\"",
|
|
+ false,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "\\",
|
|
+ "/bin/echo '\\'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "\\",
|
|
+ "/bin/echo '\\'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\"",
|
|
+ "\\",
|
|
+ "/bin/echo '\\'",
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\" %u",
|
|
+ "\\",
|
|
+ "/bin/echo '\\' FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u' \"%u\" %u",
|
|
+ "\\",
|
|
+ "/bin/echo '\\' \"FallbackUsername\" FallbackUsername",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u' \"%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob' \"FallbackUsername\"",
|
|
+ true,
|
|
+ false,
|
|
+ true,
|
|
+ },
|
|
+ };
|
|
+
|
|
+ _test_talloc_string_sub_unsafe(state,
|
|
+ expansions,
|
|
+ ARRAY_SIZE(expansions),
|
|
+ unsafe_characters);
|
|
+}
|
|
+
|
|
+static void test_talloc_string_sub_unsafe_all_mixes(void **state)
|
|
+{
|
|
+ const char *unsafe_characters = STRING_SUB_UNSAFE_CHARACTERS;
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < 32; i++) {
|
|
+ char in[100] = { 0, };
|
|
+ char out[100] = { 0, };
|
|
+ struct cmd_expansion expansions[] = {
|
|
+ {
|
|
+ in,
|
|
+ "bob",
|
|
+ out,
|
|
+ true,
|
|
+ false,
|
|
+ false,
|
|
+ },
|
|
+ };
|
|
+ bool vsq = i & 1;
|
|
+ bool vdq = i & 2;
|
|
+ bool v = i & 4;
|
|
+ bool sq = i & 8;
|
|
+ bool dq = i & 16;
|
|
+ char *inp = in;
|
|
+ char *outp = out;
|
|
+ if (vsq) {
|
|
+ inp = stpcpy(inp, "'%u' ");
|
|
+ outp = stpcpy(outp, "'bob' ");
|
|
+ debug_message("vsq ");
|
|
+ }
|
|
+ if (vdq) {
|
|
+ inp = stpcpy(inp, "\"%u\" ");
|
|
+ outp = stpcpy(outp, (vsq || sq) ? "\"FallbackUsername\" " : "'bob' ");
|
|
+ debug_message("vdq ");
|
|
+ if (vsq || sq) {
|
|
+ expansions[0].mixed_fallback = true;
|
|
+ }
|
|
+ }
|
|
+ if (v) {
|
|
+ inp = stpcpy(inp, "%u ");
|
|
+ outp = stpcpy(outp, (vsq || vdq || sq || dq) ? "FallbackUsername " : "'bob' ");
|
|
+ debug_message("v ");
|
|
+ if (vsq || vdq || sq || dq) {
|
|
+ expansions[0].mixed_fallback = true;
|
|
+ }
|
|
+ }
|
|
+ if (sq) {
|
|
+ inp = stpcpy(inp, "' ");
|
|
+ outp = stpcpy(outp, "' ");
|
|
+ debug_message("sq ");
|
|
+ }
|
|
+ if (dq) {
|
|
+ inp = stpcpy(inp, "\" ");
|
|
+ outp = stpcpy(outp, "\" ");
|
|
+ debug_message("dq ");
|
|
+ }
|
|
+ debug_message("(i: %zu)\n", i);
|
|
+ *inp = '\0';
|
|
+ *outp = '\0';
|
|
+ expansions[0].modified = strcmp(in, out) != 0;
|
|
+
|
|
+ _test_talloc_string_sub_unsafe(state,
|
|
+ expansions,
|
|
+ ARRAY_SIZE(expansions),
|
|
+ unsafe_characters);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+int main(void)
|
|
+{
|
|
+ const struct CMUnitTest tests[] = {
|
|
+ cmocka_unit_test(test_talloc_string_sub_unsafe),
|
|
+ cmocka_unit_test(test_talloc_string_sub_unsafe_minimal_unsafe_chars),
|
|
+ cmocka_unit_test(test_talloc_string_sub_unsafe_all_mixes),
|
|
+ };
|
|
+ if (!isatty(1)) {
|
|
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
|
|
+ }
|
|
+ return cmocka_run_group_tests(tests,
|
|
+ setup_talloc_context,
|
|
+ teardown_talloc_context);
|
|
+}
|
|
diff --git a/lib/util/wscript_build b/lib/util/wscript_build
|
|
index b4fcfeaba07..900751adc7f 100644
|
|
--- a/lib/util/wscript_build
|
|
+++ b/lib/util/wscript_build
|
|
@@ -413,3 +413,9 @@ else:
|
|
deps='cmocka replace talloc stable_sort',
|
|
local_include=False,
|
|
for_selftest=True)
|
|
+
|
|
+ bld.SAMBA3_BINARY('test_string_sub',
|
|
+ source='tests/test_string_sub.c',
|
|
+ deps='''cmocka replace talloc samba-util
|
|
+ ''',
|
|
+ for_selftest=True)
|
|
diff --git a/selftest/tests.py b/selftest/tests.py
|
|
index 2cafe2faa4e..3869d9894ee 100644
|
|
--- a/selftest/tests.py
|
|
+++ b/selftest/tests.py
|
|
@@ -439,6 +439,8 @@ plantestsuite("samba.unittests.sys_rw", "none",
|
|
[os.path.join(bindir(), "default/lib/util/test_sys_rw")])
|
|
plantestsuite("samba.unittests.stable_sort", "none",
|
|
[os.path.join(bindir(), "default/lib/util/test_stable_sort")])
|
|
+plantestsuite("samba.unittests.test_string_sub", "none",
|
|
+ [os.path.join(bindir(), "test_string_sub")])
|
|
plantestsuite("samba.unittests.ntlm_check", "none",
|
|
[os.path.join(bindir(), "default/libcli/auth/test_ntlm_check")])
|
|
plantestsuite("samba.unittests.gnutls", "none",
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From f1430e0324e53727c964baf46bb2bacaf5bd6699 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Sun, 15 Mar 2026 19:15:14 +0100
|
|
Subject: [PATCH 113/122] CVE-2026-4480: s3:printing: mask and/or single quote
|
|
jobname passed as %J to "print command"
|
|
|
|
Fix an unauthenticated remote code execution vulnerability with
|
|
printing set to anything *but* cups and iprint, for example "lprng",
|
|
so that "print command" is executed upon job submission. If the
|
|
client-controlled job name is handed to the "print command" via %J,
|
|
rpcd_spoolssd passes this to the shell without escaping critical
|
|
characters.
|
|
|
|
Using single quotes (directly) around %J, '%J' would avoid the
|
|
problem, we now try to autodetect if we can use '%J' implicitly
|
|
or we fallback to a fixed "__CVE-2026-4480_FallbackJobname__"
|
|
string instead of the client provided jobname.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/printing/print_generic.c | 117 ++++++++++++++++++++++++++-----
|
|
1 file changed, 99 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/source3/printing/print_generic.c b/source3/printing/print_generic.c
|
|
index 8798a4cf34a..c3a6f8b1506 100644
|
|
--- a/source3/printing/print_generic.c
|
|
+++ b/source3/printing/print_generic.c
|
|
@@ -1,23 +1,24 @@
|
|
-/*
|
|
+/*
|
|
Unix SMB/CIFS implementation.
|
|
printing command routines
|
|
Copyright (C) Andrew Tridgell 1992-2000
|
|
-
|
|
+
|
|
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 "lib/util/util_str_escape.h"
|
|
#include "printing.h"
|
|
#include "smbd/proto.h"
|
|
#include "source3/lib/substitute.h"
|
|
@@ -124,7 +125,7 @@ static int generic_job_pause(int snum, struct printjob *pjob)
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
fstring jobstr;
|
|
-
|
|
+
|
|
/* need to pause the spooled entry */
|
|
fstr_sprintf(jobstr, "%d", pjob->sysjob);
|
|
return print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
|
|
@@ -206,6 +207,52 @@ static int generic_queue_get(const char *printer_name,
|
|
return qcount;
|
|
}
|
|
|
|
+static const char *replace_print_cmd_J(TALLOC_CTX *mem_ctx,
|
|
+ const char *orig_cmd,
|
|
+ const char *unsafe_jobname,
|
|
+ const char *fallback_jobname)
|
|
+{
|
|
+ char *cmd = NULL;
|
|
+ bool modified = false;
|
|
+ bool masked = false;
|
|
+ bool mixed_fallback = false;
|
|
+
|
|
+ /*
|
|
+ * This replaces unsafe characters with '_'.
|
|
+ * We also mask forward and backslash here.
|
|
+ *
|
|
+ * Then it replaces %J with an single quoted
|
|
+ * version of the masked jobname or it falls
|
|
+ * back to fallback_jobname is the print command
|
|
+ * uses strange mixed quoting.
|
|
+ */
|
|
+
|
|
+#define JOBNAME_UNSAFE_CHARACTERS \
|
|
+ STRING_SUB_UNSAFE_CHARACTERS "/\\"
|
|
+
|
|
+ cmd = talloc_string_sub_unsafe(mem_ctx,
|
|
+ orig_cmd,
|
|
+ 'J',
|
|
+ unsafe_jobname,
|
|
+ JOBNAME_UNSAFE_CHARACTERS,
|
|
+ '_',
|
|
+ fallback_jobname,
|
|
+ &modified,
|
|
+ &masked,
|
|
+ &mixed_fallback);
|
|
+ if (cmd == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The caller already checked talloc_string_sub_mixed_quoting()
|
|
+ * and warned the admin, so we don't check mixed_fallback
|
|
+ * here
|
|
+ */
|
|
+
|
|
+ return cmd;
|
|
+}
|
|
+
|
|
/****************************************************************************
|
|
Submit a file for printing - called from print_job_end()
|
|
****************************************************************************/
|
|
@@ -221,11 +268,12 @@ static int generic_job_submit(int snum, struct printjob *pjob,
|
|
char *print_directory = NULL;
|
|
char *wd = NULL;
|
|
char *p = NULL;
|
|
- char *jobname = NULL;
|
|
+ const char *print_cmd = NULL;
|
|
TALLOC_CTX *ctx = talloc_tos();
|
|
fstring job_page_count, job_size;
|
|
print_queue_struct *q;
|
|
print_status_struct status;
|
|
+ const char *jobname = "No Document Name";
|
|
|
|
/* we print from the directory path to give the best chance of
|
|
parsing the lpq output */
|
|
@@ -254,24 +302,48 @@ static int generic_job_submit(int snum, struct printjob *pjob,
|
|
return -1;
|
|
}
|
|
|
|
- jobname = talloc_strdup(ctx, pjob->jobname);
|
|
- if (!jobname) {
|
|
- ret = -1;
|
|
- goto out;
|
|
+ if (pjob->jobname[0] != '\0') {
|
|
+ jobname = pjob->jobname;
|
|
}
|
|
- jobname = talloc_string_sub(ctx, jobname, "'", "_");
|
|
- if (!jobname) {
|
|
- ret = -1;
|
|
- goto out;
|
|
+
|
|
+ print_cmd = lp_print_command(snum);
|
|
+ if (print_cmd != NULL) {
|
|
+ const char *invalid_jobname = "__CVE-2026-4480_FallbackJobname__";
|
|
+
|
|
+ if (talloc_string_sub_mixed_quoting(print_cmd, 'J')) {
|
|
+ /*
|
|
+ * The admin used a strange mixture of
|
|
+ * single and double quotes, fallback
|
|
+ * to InvalidDocumentName and warn about
|
|
+ * it, so that the admin can adjust to
|
|
+ * the use single quotes directly around %J,
|
|
+ * e.g. '%J'.
|
|
+ */
|
|
+ jobname = invalid_jobname;
|
|
+ D_WARNING("CVE-2026-4480: printer %s "
|
|
+ "strange quoting in 'print command', "
|
|
+ "falling back to jobname=%s, "
|
|
+ "use testparm to fix the configuration\n",
|
|
+ lp_printername(talloc_tos(), lp_sub, snum),
|
|
+ invalid_jobname);
|
|
+ }
|
|
+
|
|
+ print_cmd = replace_print_cmd_J(ctx,
|
|
+ print_cmd,
|
|
+ jobname,
|
|
+ invalid_jobname);
|
|
+ if (!print_cmd) {
|
|
+ ret = -1;
|
|
+ goto out;
|
|
+ }
|
|
}
|
|
fstr_sprintf(job_page_count, "%d", pjob->page_count);
|
|
fstr_sprintf(job_size, "%zu", pjob->size);
|
|
|
|
/* send it to the system spooler */
|
|
ret = print_run_command(snum, lp_printername(talloc_tos(), lp_sub, snum), True,
|
|
- lp_print_command(snum), NULL,
|
|
+ print_cmd, NULL,
|
|
"%s", p,
|
|
- "%J", jobname,
|
|
"%f", p,
|
|
"%z", job_size,
|
|
"%c", job_page_count,
|
|
@@ -292,9 +364,14 @@ static int generic_job_submit(int snum, struct printjob *pjob,
|
|
int i;
|
|
for (i = 0; i < ret; i++) {
|
|
if (strcmp(q[i].fs_file, p) == 0) {
|
|
+ char *le_jobname =
|
|
+ log_escape(talloc_tos(), jobname);
|
|
+
|
|
pjob->sysjob = q[i].sysjob;
|
|
DEBUG(5, ("new job %u (%s) matches sysjob %d\n",
|
|
- pjob->jobid, jobname, pjob->sysjob));
|
|
+ pjob->jobid, le_jobname, pjob->sysjob));
|
|
+
|
|
+ TALLOC_FREE(le_jobname);
|
|
break;
|
|
}
|
|
}
|
|
@@ -302,8 +379,12 @@ static int generic_job_submit(int snum, struct printjob *pjob,
|
|
ret = 0;
|
|
}
|
|
if (pjob->sysjob == -1) {
|
|
+ char *le_jobname = log_escape(talloc_tos(), jobname);
|
|
+
|
|
DEBUG(2, ("failed to get sysjob for job %u (%s), tracking as "
|
|
- "Unix job\n", pjob->jobid, jobname));
|
|
+ "Unix job\n", pjob->jobid, le_jobname));
|
|
+
|
|
+ TALLOC_FREE(le_jobname);
|
|
}
|
|
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 97cef37691467fc2e910339ed2af49f7531691ac Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 8 May 2026 23:27:35 +0200
|
|
Subject: [PATCH 114/122] CVE-2026-4480: s3:testparm: warn about 'print
|
|
command' %J usage
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/utils/testparm.c | 8 ++++++++
|
|
1 file changed, 8 insertions(+)
|
|
|
|
diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c
|
|
index 96e05a57f47..16ac47f9ff4 100644
|
|
--- a/source3/utils/testparm.c
|
|
+++ b/source3/utils/testparm.c
|
|
@@ -796,6 +796,14 @@ static void do_per_share_checks(int s)
|
|
"parameter is ignored when using CUPS libraries.\n\n",
|
|
lp_servicename(talloc_tos(), lp_sub, s));
|
|
}
|
|
+ if (talloc_string_sub_mixed_quoting(lp_print_command(s), 'J')) {
|
|
+ fprintf(stderr,
|
|
+ "WARNING: Service %s defines a 'print command' "
|
|
+ "with mixed quoting and %%J.\n"
|
|
+ "CVE-2026-4480 changed the way %%J substitution works.\n"
|
|
+ "You should use single quotes (directly) around '%%J'.\n\n",
|
|
+ lp_servicename(talloc_tos(), lp_sub, s));
|
|
+ }
|
|
|
|
vfs_objects = lp_vfs_objects(s);
|
|
if (vfs_objects && str_list_check(vfs_objects, "fruit")) {
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From a8fc9494b7633950c7e027332479e16a59cc646a Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 11 May 2026 14:11:34 +0200
|
|
Subject: [PATCH 115/122] CVE-2026-4480: docs-xml/smbdotconf: clarify '%J' in
|
|
'print command'
|
|
|
|
Admins should use '%J'.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16033
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
docs-xml/smbdotconf/printing/printcommand.xml | 7 +++++--
|
|
1 file changed, 5 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/docs-xml/smbdotconf/printing/printcommand.xml b/docs-xml/smbdotconf/printing/printcommand.xml
|
|
index c84e45f404d..d708287932a 100644
|
|
--- a/docs-xml/smbdotconf/printing/printcommand.xml
|
|
+++ b/docs-xml/smbdotconf/printing/printcommand.xml
|
|
@@ -21,8 +21,11 @@
|
|
<para>%p - the appropriate printer
|
|
name</para>
|
|
|
|
- <para>%J - the job
|
|
- name as transmitted by the client.</para>
|
|
+ <para>%J - the job name as transmitted by the client,
|
|
+ but with dangerous characters being replaced by _.
|
|
+ You should use single quotes (directly) around %J, e.g. '%J',
|
|
+ see CVE-2026-4480 for more details.
|
|
+ </para>
|
|
|
|
<para>%c - The number of printed pages
|
|
of the spooled job (if known).</para>
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From e8a0c5e718caff4b9792aa4758646dcff5cf3fe1 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Thu, 23 Apr 2026 18:56:21 +0200
|
|
Subject: [PATCH 116/122] CVE-2026-4408: lib/util: introduce
|
|
strstr_for_invalid_account_characters()
|
|
|
|
This splits out the logic from samaccountname_bad_chars_check()
|
|
in source4/dsdb/samdb/ldb_modules/samldb.c, this will be used
|
|
in other places soon.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
lib/util/samba_util.h | 9 +++++++++
|
|
lib/util/util_str.c | 38 ++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 47 insertions(+)
|
|
|
|
diff --git a/lib/util/samba_util.h b/lib/util/samba_util.h
|
|
index e672b4b00b2..954110983fe 100644
|
|
--- a/lib/util/samba_util.h
|
|
+++ b/lib/util/samba_util.h
|
|
@@ -300,6 +300,15 @@ _PUBLIC_ bool set_boolean(const char *boolean_string, bool *boolean);
|
|
*/
|
|
_PUBLIC_ bool conv_str_bool(const char * str, bool * val);
|
|
|
|
+/**
|
|
+ * Returns a pointer to the first invalid character in name.
|
|
+ *
|
|
+ * Passing a NULL pointer as name is not allowed!
|
|
+ *
|
|
+ * This returns NULL for a valid account name.
|
|
+ **/
|
|
+_PUBLIC_ const char *strstr_for_invalid_account_characters(const char *name);
|
|
+
|
|
/**
|
|
* Convert a size specification like 16K into an integral number of bytes.
|
|
**/
|
|
diff --git a/lib/util/util_str.c b/lib/util/util_str.c
|
|
index 7c1d15dbeb0..c4eda4f49f3 100644
|
|
--- a/lib/util/util_str.c
|
|
+++ b/lib/util/util_str.c
|
|
@@ -305,3 +305,41 @@ _PUBLIC_ bool set_boolean(const char *boolean_string, bool *boolean)
|
|
}
|
|
return false;
|
|
}
|
|
+
|
|
+_PUBLIC_ const char *strstr_for_invalid_account_characters(const char *name)
|
|
+{
|
|
+ /*
|
|
+ * Return a pointer to the first invalid character in the
|
|
+ * sAMAccountName, or NULL if the whole name is valid.
|
|
+ *
|
|
+ * The rules here are based on
|
|
+ *
|
|
+ * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx
|
|
+ */
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; name[i] != '\0'; i++) {
|
|
+ uint8_t c = name[i];
|
|
+ const char *p = NULL;
|
|
+
|
|
+ if (iscntrl(c)) {
|
|
+ return &name[i];
|
|
+ }
|
|
+
|
|
+ p = strchr("\"[]:;|=+*?<>/\\,", c);
|
|
+ if (p != NULL) {
|
|
+ return &name[i];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (i == 0) {
|
|
+ return &name[i];
|
|
+ }
|
|
+
|
|
+ if (name[i - 1] == '.') {
|
|
+ i -= 1;
|
|
+ return &name[i];
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d37f3cb93d96ec3c92f1d9f8ddf16093b4c18420 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 11 May 2026 20:21:36 +0200
|
|
Subject: [PATCH 117/122] CVE-2026-4408: s3:samr-server: only allow
|
|
_samr_ValidatePassword as DC
|
|
|
|
This is only supported with 'rpc start on demand helpers = no',
|
|
as it needs ncacn_ip_tcp, but we better also restrict it to DCs.
|
|
|
|
Maybe only FreeIPA needs it as NT4 didn't support ncacn_ip_tcp.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/rpc_server/samr/srv_samr_nt.c | 8 ++++++++
|
|
1 file changed, 8 insertions(+)
|
|
|
|
diff --git a/source3/rpc_server/samr/srv_samr_nt.c b/source3/rpc_server/samr/srv_samr_nt.c
|
|
index 2cc168926a7..26d8e408d38 100644
|
|
--- a/source3/rpc_server/samr/srv_samr_nt.c
|
|
+++ b/source3/rpc_server/samr/srv_samr_nt.c
|
|
@@ -7499,6 +7499,14 @@ NTSTATUS _samr_ValidatePassword(struct pipes_struct *p,
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
+ if (lp_server_role() <= ROLE_DOMAIN_MEMBER) {
|
|
+ /*
|
|
+ * We only want this on DCs
|
|
+ */
|
|
+ p->fault_state = DCERPC_FAULT_ACCESS_DENIED;
|
|
+ return NT_STATUS_ACCESS_DENIED;
|
|
+ }
|
|
+
|
|
if (r->in.level < 1 || r->in.level > 3) {
|
|
return NT_STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From d44e016983db65d430dfa0556755fba32e498dd1 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Wed, 18 Mar 2026 12:24:47 +0100
|
|
Subject: [PATCH 118/122] CVE-2026-4408: s3:samr-server: deny, mask and/or
|
|
single quote username to 'check password script'
|
|
|
|
We pass this on to the check password script, prevent remote command
|
|
execution.
|
|
|
|
We now try to autodetect if we could implicitly use '%u' for the
|
|
replacement and fallback to a fixed fallback username.
|
|
|
|
Admins should make use of SAMBA_CPS_ACCOUNT_NAME
|
|
instead of passing '%u' to 'check password script'
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Pair-Programmed-With: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/rpc_server/samr/srv_samr_chgpasswd.c | 110 +++++++++++++++++--
|
|
1 file changed, 101 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/source3/rpc_server/samr/srv_samr_chgpasswd.c b/source3/rpc_server/samr/srv_samr_chgpasswd.c
|
|
index 3373ffa76f6..0a1edb88712 100644
|
|
--- a/source3/rpc_server/samr/srv_samr_chgpasswd.c
|
|
+++ b/source3/rpc_server/samr/srv_samr_chgpasswd.c
|
|
@@ -54,6 +54,7 @@
|
|
#include "passdb.h"
|
|
#include "auth.h"
|
|
#include "lib/util/sys_rw.h"
|
|
+#include "lib/util/util_str_escape.h"
|
|
#include "librpc/rpc/dcerpc_samr.h"
|
|
|
|
#include "lib/crypto/gnutls_helpers.h"
|
|
@@ -1008,27 +1009,118 @@ static bool check_passwd_history(struct samu *sampass, const char *plaintext)
|
|
/***********************************************************
|
|
************************************************************/
|
|
|
|
+static NTSTATUS check_password_complexity_internal(TALLOC_CTX *tosctx,
|
|
+ const char *orig_cmd,
|
|
+ const char *username,
|
|
+ char **cmd_out)
|
|
+{
|
|
+ const char *fallback_username = "__CVE-2026-4408_FallbackUsername__";
|
|
+ const char *inv = NULL;
|
|
+ char *cmd = NULL;
|
|
+ bool modified = false;
|
|
+ bool masked = false;
|
|
+ bool mixed_fallback = false;
|
|
+
|
|
+ *cmd_out = NULL;
|
|
+
|
|
+ if (username == NULL) {
|
|
+ return NT_STATUS_INVALID_USER_PRINCIPAL_NAME;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * This catches invalid characters in account names
|
|
+ * which might be problematic passing to a shell script.
|
|
+ */
|
|
+ inv = strstr_for_invalid_account_characters(username);
|
|
+ if (inv != NULL) {
|
|
+ char *le_username = log_escape(tosctx, username);
|
|
+
|
|
+ DBG_WARNING("username '%s' has invalid or dangerous characters\n",
|
|
+ le_username);
|
|
+
|
|
+ TALLOC_FREE(le_username);
|
|
+
|
|
+ return NT_STATUS_INVALID_USER_PRINCIPAL_NAME;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * This masks the remaining unsafe characters which
|
|
+ * are not already caught by strstr_for_invalid_account_characters()
|
|
+ * with '_'.
|
|
+ *
|
|
+ * Then it replaces %u with an single quoted
|
|
+ * and/or shell escaped version of the masked username.
|
|
+ */
|
|
+ cmd = talloc_string_sub_unsafe(tosctx,
|
|
+ orig_cmd,
|
|
+ 'u',
|
|
+ username,
|
|
+ STRING_SUB_UNSAFE_CHARACTERS,
|
|
+ '_',
|
|
+ fallback_username,
|
|
+ &modified,
|
|
+ &masked,
|
|
+ &mixed_fallback);
|
|
+ if (cmd == NULL) {
|
|
+ return NT_STATUS_NO_MEMORY;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Now warn about unexpected values
|
|
+ */
|
|
+
|
|
+ if (mixed_fallback) {
|
|
+ D_WARNING("CVE-2026-4408: "
|
|
+ "strange quoting in 'check password script', "
|
|
+ "falling back to replace %%u with %s, "
|
|
+ "use testparm to fix the configuration\n",
|
|
+ fallback_username);
|
|
+ D_WARNING("CVE-2026-4408: "
|
|
+ "You should use '%%u', or SAMBA_CPS_ACCOUNT_NAME "
|
|
+ "inside of 'check password script'.\n");
|
|
+ } else if (masked) {
|
|
+ char *le_username = log_escape(tosctx, username);
|
|
+
|
|
+ D_WARNING("CVE-2026-4408: "
|
|
+ "replaced %%u with masked value instead of: %s\n",
|
|
+ le_username);
|
|
+ D_WARNING("CVE-2026-4408: "
|
|
+ "You should use SAMBA_CPS_ACCOUNT_NAME inside "
|
|
+ "'check password script' instead of %%u.\n");
|
|
+
|
|
+ TALLOC_FREE(le_username);
|
|
+ }
|
|
+
|
|
+ *cmd_out = cmd;
|
|
+ return NT_STATUS_OK;
|
|
+}
|
|
+
|
|
+
|
|
NTSTATUS check_password_complexity(const char *username,
|
|
const char *fullname,
|
|
const char *password,
|
|
enum samPwdChangeReason *samr_reject_reason)
|
|
{
|
|
+ int check_ret;
|
|
+ NTSTATUS status;
|
|
TALLOC_CTX *tosctx = talloc_tos();
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
- int check_ret;
|
|
- char *cmd;
|
|
+ const char *orig_cmd = NULL;
|
|
+ char *cmd = NULL;
|
|
|
|
- /* Use external script to check password complexity */
|
|
- if ((lp_check_password_script(tosctx, lp_sub) == NULL)
|
|
- || (*(lp_check_password_script(tosctx, lp_sub)) == '\0')){
|
|
+ orig_cmd = lp_check_password_script(tosctx, lp_sub);
|
|
+ if (orig_cmd == NULL || orig_cmd[0] == '\0') {
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
- cmd = talloc_string_sub(tosctx, lp_check_password_script(tosctx, lp_sub), "%u",
|
|
- username);
|
|
- if (!cmd) {
|
|
- return NT_STATUS_PASSWORD_RESTRICTION;
|
|
+ /* note we don't use 'fullname' or 'password' here */
|
|
+ status = check_password_complexity_internal(tosctx,
|
|
+ orig_cmd,
|
|
+ username,
|
|
+ &cmd);
|
|
+ if (!NT_STATUS_IS_OK(status)) {
|
|
+ return status;
|
|
}
|
|
|
|
check_ret = setenv("SAMBA_CPS_ACCOUNT_NAME", username, 1);
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 778f3b61db7e4c2b617a97b1dfd463cdcf597148 Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Sat, 2 May 2026 22:12:38 +1200
|
|
Subject: [PATCH 119/122] CVE-2026-4408: s3:samr-server: make
|
|
check_password_complexity_internal() non-static, for easier testing
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/rpc_server/samr/srv_samr_chgpasswd.c | 8 ++++----
|
|
source3/rpc_server/samr/srv_samr_util.h | 5 +++++
|
|
2 files changed, 9 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/source3/rpc_server/samr/srv_samr_chgpasswd.c b/source3/rpc_server/samr/srv_samr_chgpasswd.c
|
|
index 0a1edb88712..62536ed7531 100644
|
|
--- a/source3/rpc_server/samr/srv_samr_chgpasswd.c
|
|
+++ b/source3/rpc_server/samr/srv_samr_chgpasswd.c
|
|
@@ -1009,10 +1009,10 @@ static bool check_passwd_history(struct samu *sampass, const char *plaintext)
|
|
/***********************************************************
|
|
************************************************************/
|
|
|
|
-static NTSTATUS check_password_complexity_internal(TALLOC_CTX *tosctx,
|
|
- const char *orig_cmd,
|
|
- const char *username,
|
|
- char **cmd_out)
|
|
+NTSTATUS check_password_complexity_internal(TALLOC_CTX *tosctx,
|
|
+ const char *orig_cmd,
|
|
+ const char *username,
|
|
+ char **cmd_out)
|
|
{
|
|
const char *fallback_username = "__CVE-2026-4408_FallbackUsername__";
|
|
const char *inv = NULL;
|
|
diff --git a/source3/rpc_server/samr/srv_samr_util.h b/source3/rpc_server/samr/srv_samr_util.h
|
|
index 5e839ac77c0..a3a22012858 100644
|
|
--- a/source3/rpc_server/samr/srv_samr_util.h
|
|
+++ b/source3/rpc_server/samr/srv_samr_util.h
|
|
@@ -79,6 +79,11 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
|
|
uchar password_encrypted_with_nt_hash[516],
|
|
const uchar old_nt_hash_encrypted[16],
|
|
enum samPwdChangeReason *reject_reason);
|
|
+
|
|
+NTSTATUS check_password_complexity_internal(TALLOC_CTX *mem_ctx,
|
|
+ const char *_orig_cmd,
|
|
+ const char *username,
|
|
+ char **cmd_out);
|
|
NTSTATUS check_password_complexity(const char *username,
|
|
const char *fullname,
|
|
const char *password,
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 179cacf4d661e83a402c98c1c5b3b321a9a52786 Mon Sep 17 00:00:00 2001
|
|
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Date: Sat, 2 May 2026 22:14:43 +1200
|
|
Subject: [PATCH 120/122] CVE-2026-4408: s3:torture: tests for password
|
|
complexity scripts
|
|
|
|
This tries to demonstrate the new logic for %u in
|
|
'check password script'.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
|
|
|
|
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
---
|
|
selftest/tests.py | 2 +
|
|
source3/torture/test_rpc_samr.c | 358 ++++++++++++++++++++++++++++++++
|
|
source3/torture/wscript_build | 6 +
|
|
3 files changed, 366 insertions(+)
|
|
create mode 100644 source3/torture/test_rpc_samr.c
|
|
|
|
diff --git a/selftest/tests.py b/selftest/tests.py
|
|
index 3869d9894ee..3b48d262f82 100644
|
|
--- a/selftest/tests.py
|
|
+++ b/selftest/tests.py
|
|
@@ -455,6 +455,8 @@ plantestsuite("samba.unittests.test_oLschema2ldif", "none",
|
|
[os.path.join(bindir(), "default/source4/utils/oLschema2ldif/test_oLschema2ldif")])
|
|
plantestsuite("samba.unittests.auth.sam", "none",
|
|
[os.path.join(bindir(), "test_auth_sam")])
|
|
+plantestsuite("samba.unittests.test_rpc_samr", "none",
|
|
+ [os.path.join(bindir(), "test_rpc_samr")])
|
|
if have_heimdal_support and not using_system_gssapi:
|
|
plantestsuite("samba.unittests.auth.heimdal_gensec_unwrap_des", "none",
|
|
[valgrindify(os.path.join(bindir(), "test_heimdal_gensec_unwrap_des"))])
|
|
diff --git a/source3/torture/test_rpc_samr.c b/source3/torture/test_rpc_samr.c
|
|
new file mode 100644
|
|
index 00000000000..8d4f3985246
|
|
--- /dev/null
|
|
+++ b/source3/torture/test_rpc_samr.c
|
|
@@ -0,0 +1,358 @@
|
|
+
|
|
+#include <stdarg.h>
|
|
+#include <stddef.h>
|
|
+#include <stdint.h>
|
|
+#include <setjmp.h>
|
|
+#include <sys/stat.h>
|
|
+#include <cmocka.h>
|
|
+#include "includes.h"
|
|
+#include "talloc.h"
|
|
+#include "libcli/util/ntstatus.h"
|
|
+#include "../librpc/gen_ndr/samr.h"
|
|
+#include "rpc_server/samr/srv_samr_util.h"
|
|
+
|
|
+/* set SAMR_DEBUG_VERBOSE to true to print more. */
|
|
+#define SAMR_DEBUG_VERBOSE true
|
|
+
|
|
+#if SAMR_DEBUG_VERBOSE
|
|
+#define debug_message(...) print_message(__VA_ARGS__)
|
|
+#else
|
|
+#define debug_message(...) /* debug_message */
|
|
+#endif
|
|
+
|
|
+static int setup_talloc_context(void **state)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
|
|
+ *state = mem_ctx;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int teardown_talloc_context(void **state)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = *state;
|
|
+ TALLOC_FREE(mem_ctx);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct cmd_expansion {
|
|
+ const char *lp_cmd;
|
|
+ const char *username;
|
|
+ const char *result_cmd;
|
|
+ NTSTATUS result_code;
|
|
+};
|
|
+
|
|
+static struct cmd_expansion expansions[] = {
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob",
|
|
+ "/bin/echo 'bob'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob'",
|
|
+ "/bin/echo 'bob_'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob\'",
|
|
+ "/bin/echo 'bob_'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob'''",
|
|
+ "/bin/echo 'bob___'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob*",
|
|
+ NULL,
|
|
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "bob\"",
|
|
+ NULL,
|
|
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u",
|
|
+ "bob bob bob",
|
|
+ "/bin/echo '__CVE-2026-4408_FallbackUsername__",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"%u\"",
|
|
+ " ",
|
|
+ "/bin/echo ' '",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo \"--uu=__CVE-2026-4408_FallbackUsername__\"",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob !0",
|
|
+ "/bin/echo \"--uu=__CVE-2026-4408_FallbackUsername__\"",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "!0",
|
|
+ "/bin/echo '!0'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo \"--uu=%u\"",
|
|
+ "bob \\",
|
|
+ NULL,
|
|
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "bob >> x",
|
|
+ NULL,
|
|
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '--uu=%u\"",
|
|
+ "bob",
|
|
+ "/bin/echo '--uu=__CVE-2026-4408_FallbackUsername__\"",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "bob",
|
|
+ "/bin/echo --uu='bob'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu'=%u'",
|
|
+ "bob",
|
|
+ "/bin/echo --uu'=__CVE-2026-4408_FallbackUsername__'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu'=%u'",
|
|
+ "`ls`",
|
|
+ "/bin/echo --uu'=__CVE-2026-4408_FallbackUsername__'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu'=%u'",
|
|
+ "$(ls)",
|
|
+ "/bin/echo --uu'=__CVE-2026-4408_FallbackUsername__'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u'",
|
|
+ "$(ls)",
|
|
+ "/bin/echo --uu='_(ls)'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu=\"'%u'\"",
|
|
+ "bob",
|
|
+ "/bin/echo --uu=\"'bob'\"",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu='%u' --yy='%u' '%u' %u",
|
|
+ "bob",
|
|
+ "/bin/echo --uu='bob' --yy='bob' 'bob' __CVE-2026-4408_FallbackUsername__",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo --uu=%u%u'' %user 50%u",
|
|
+ "bob",
|
|
+ "/bin/echo --uu=__CVE-2026-4408_FallbackUsername____CVE-2026-4408_FallbackUsername__'' __CVE-2026-4408_FallbackUsername__ser 50__CVE-2026-4408_FallbackUsername__",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "!!",
|
|
+ "/bin/echo '!!'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ ">xxx",
|
|
+ NULL,
|
|
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "\\",
|
|
+ NULL,
|
|
+ NT_STATUS_INVALID_USER_PRINCIPAL_NAME
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "3",
|
|
+ "/bin/echo '3'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "3$",
|
|
+ "/bin/echo '3_'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "comp$",
|
|
+ "/bin/echo 'comp_'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "3$3",
|
|
+ "/bin/echo '3_3'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo '%u'",
|
|
+ "q $3",
|
|
+ "/bin/echo 'q _3'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s '%u' %u",
|
|
+ "āāā",
|
|
+ "/bin/echo -s 'āāā' __CVE-2026-4408_FallbackUsername__",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s '%u' %u",
|
|
+ "-āāā",
|
|
+ "/bin/echo -s '_āāā' __CVE-2026-4408_FallbackUsername__",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s %u",
|
|
+ "āāā",
|
|
+ "/bin/echo -s 'āāā'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s %u",
|
|
+ "a -a",
|
|
+ "/bin/echo -s 'a -a'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s=%u %u",
|
|
+ "ā -a",
|
|
+ "/bin/echo -s='ā -a' 'ā -a'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -s=\"%u %u\"",
|
|
+ "ā -a",
|
|
+ "/bin/echo -s=\"__CVE-2026-4408_FallbackUsername__ __CVE-2026-4408_FallbackUsername__\"",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -m='fridge' %u",
|
|
+ "ā -x -ß",
|
|
+ "/bin/echo -m='fridge' __CVE-2026-4408_FallbackUsername__",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo -m='fridge' %u",
|
|
+ "-ā -a",
|
|
+ "/bin/echo -m='fridge' __CVE-2026-4408_FallbackUsername__",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "-n",
|
|
+ "/bin/echo '_n'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+ {
|
|
+ "/bin/echo %u",
|
|
+ "o'clock",
|
|
+ "/bin/echo 'o_clock'",
|
|
+ NT_STATUS_OK
|
|
+ },
|
|
+};
|
|
+
|
|
+static void test_expansions(void **state)
|
|
+{
|
|
+ TALLOC_CTX *mem_ctx = *state;
|
|
+ size_t i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(expansions); i++) {
|
|
+ struct cmd_expansion t = expansions[i];
|
|
+ char *result_cmd = NULL;
|
|
+ NTSTATUS status;
|
|
+
|
|
+ status = check_password_complexity_internal(mem_ctx,
|
|
+ t.lp_cmd,
|
|
+ t.username,
|
|
+ &result_cmd);
|
|
+ if (NT_STATUS_IS_OK(t.result_code) && NT_STATUS_IS_OK(status)) {
|
|
+ int cmp;
|
|
+
|
|
+ cmp = strcmp(t.result_cmd, result_cmd);
|
|
+ if (cmp == 0) {
|
|
+ debug_message("[%zu] «%s» «%s» -> «%s», nstatus %s; AS EXPECTED\n",
|
|
+ i, t.lp_cmd,
|
|
+ t.username,
|
|
+ result_cmd,
|
|
+ nt_errstr(status));
|
|
+ } else {
|
|
+ debug_message("[%zu] «%s» «%s», nstatus %s; "
|
|
+ "expected «%s» got «%s»\033[1;31m BAD! \033[0m\n",
|
|
+ i, t.lp_cmd,
|
|
+ t.username,
|
|
+ nt_errstr(status),
|
|
+ t.result_cmd,
|
|
+ result_cmd);
|
|
+ }
|
|
+ assert_int_equal(cmp, 0);
|
|
+ } else if (NT_STATUS_EQUAL(status, t.result_code)) {
|
|
+ debug_message("[%zu] «%s» «%s», nstatus %s FAILED AS EXPECTED\n",
|
|
+ i, t.lp_cmd,
|
|
+ t.username,
|
|
+ nt_errstr(status));
|
|
+ } else {
|
|
+ debug_message("[%zu] «%s» «%s» -> «%s», nstatus %s; "
|
|
+ "EXPECTED result «%s» ntstatus %s; \033[1;31m BAD! \033[0m\n",
|
|
+ i, t.lp_cmd,
|
|
+ t.username,
|
|
+ result_cmd,
|
|
+ nt_errstr(status),
|
|
+ t.result_cmd,
|
|
+ nt_errstr(t.result_code));
|
|
+ assert_int_equal(true, false);
|
|
+ }
|
|
+ }
|
|
+ debug_message("ALL correct\n");
|
|
+}
|
|
+
|
|
+int main(void)
|
|
+{
|
|
+ const struct CMUnitTest tests[] = {
|
|
+ cmocka_unit_test(test_expansions),
|
|
+ };
|
|
+ if (!isatty(1)) {
|
|
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
|
|
+ }
|
|
+ return cmocka_run_group_tests(tests,
|
|
+ setup_talloc_context,
|
|
+ teardown_talloc_context);
|
|
+}
|
|
diff --git a/source3/torture/wscript_build b/source3/torture/wscript_build
|
|
index 1d2520099e3..d04008b3df1 100644
|
|
--- a/source3/torture/wscript_build
|
|
+++ b/source3/torture/wscript_build
|
|
@@ -133,3 +133,9 @@ bld.SAMBA3_BINARY('vfstest',
|
|
SMBREADLINE
|
|
''',
|
|
for_selftest=True)
|
|
+
|
|
+bld.SAMBA3_BINARY('test_rpc_samr',
|
|
+ source='test_rpc_samr.c',
|
|
+ deps='''RPC_SERVICE cmocka
|
|
+ ''',
|
|
+ for_selftest=True)
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From f13ff6d7d0fafa1ca02e1d024d00f4b35cb5743e Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Fri, 8 May 2026 23:27:35 +0200
|
|
Subject: [PATCH 121/122] CVE-2026-4408: s3:testparm: warn about 'check
|
|
password script' %u usage
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
source3/utils/testparm.c | 12 ++++++++++++
|
|
1 file changed, 12 insertions(+)
|
|
|
|
diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c
|
|
index 16ac47f9ff4..d49e8bcfc64 100644
|
|
--- a/source3/utils/testparm.c
|
|
+++ b/source3/utils/testparm.c
|
|
@@ -276,6 +276,7 @@ static int do_global_checks(void)
|
|
const char *socket_options;
|
|
const struct loadparm_substitution *lp_sub =
|
|
loadparm_s3_global_substitution();
|
|
+ const char *check_pw_script = NULL;
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
@@ -699,6 +700,17 @@ static int do_global_checks(void)
|
|
"CVE-2022-37966\n\n");
|
|
}
|
|
|
|
+ check_pw_script = lp_check_password_script(talloc_tos(), lp_sub);
|
|
+ if (talloc_string_sub_mixed_quoting(check_pw_script, 'u')) {
|
|
+ fprintf(stderr,
|
|
+ "WARNING: You are using 'check password script' "
|
|
+ "with mixed quoting and %%u.\n"
|
|
+ "CVE-2026-4408 changed the way %%u substitution works. \n"
|
|
+ "You should use the SAMBA_CPS_ACCOUNT_NAME "
|
|
+ "environment variable exported to the script, or\n"
|
|
+ "at least use single quotes (directly) around '%%u'.\n\n");
|
|
+ }
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
--
|
|
2.53.0
|
|
|
|
|
|
From 4c48c55544768d5bc5c168c326c8bdbc7ce0de95 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Metzmacher <metze@samba.org>
|
|
Date: Mon, 11 May 2026 13:52:52 +0200
|
|
Subject: [PATCH 122/122] CVE-2026-4408: docs-xml/smbdotconf: clarify '%u' in
|
|
'check password script'
|
|
|
|
Admins should use SAMBA_CPS_ACCOUNT_NAME.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=16034
|
|
|
|
Signed-off-by: Stefan Metzmacher <metze@samba.org>
|
|
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
|
|
---
|
|
docs-xml/smbdotconf/security/checkpasswordscript.xml | 10 ++++++++--
|
|
1 file changed, 8 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/docs-xml/smbdotconf/security/checkpasswordscript.xml b/docs-xml/smbdotconf/security/checkpasswordscript.xml
|
|
index 18aa2c6d290..dd162d89f08 100644
|
|
--- a/docs-xml/smbdotconf/security/checkpasswordscript.xml
|
|
+++ b/docs-xml/smbdotconf/security/checkpasswordscript.xml
|
|
@@ -20,8 +20,8 @@
|
|
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
- SAMBA_CPS_ACCOUNT_NAME is always present and contains the sAMAccountName of user,
|
|
- the is the same as the %u substitutions in the none AD DC case.
|
|
+ SAMBA_CPS_ACCOUNT_NAME is always present and contains the sAMAccountName of user.
|
|
+ It is the same as the '%u' substitutions in the non AD DC case.
|
|
</para></listitem>
|
|
|
|
<listitem><para>
|
|
@@ -33,6 +33,12 @@
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
|
|
+ <para>Even on a non AD DC SAMBA_CPS_ACCOUNT_NAME is the preferred way to access the
|
|
+ account name, as it contains the raw value provided by the client. If that's not
|
|
+ possible you should use single quotes (directly) around %u, e.g. /path/to/somescript '%u',
|
|
+ see CVE-2026-4408 for more details.
|
|
+ </para>
|
|
+
|
|
<para>Note: In the example directory is a sample program called <command moreinfo="none">crackcheck</command>
|
|
that uses cracklib to check the password quality.</para>
|
|
|
|
--
|
|
2.53.0
|
|
|