[SCM] Samba Shared Repository - branch master updated

David Mulder dmulder at samba.org
Wed Apr 21 21:41:01 UTC 2021


The branch, master has been updated
       via  34a6575ab95 samba-tool: Use s3 net join for member join
       via  d01a588c207 python: glue function for detecting if selftest is enabled
       via  e5a32d4a01b python: Test s3 net join and leave
       via  bbfdd6322fa s3: Add s3 net python bindings
      from  0f29b8c2fee samba-tool: add dns zoneoptions for aging control

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 34a6575ab95ba50878b8261d8fdd5dfef6727fc3
Author: David Mulder <dmulder at suse.com>
Date:   Tue Oct 27 08:28:06 2020 -0600

    samba-tool: Use s3 net join for member join
    
    The s4 member join code has been broken for some
    time. Modify samba-tool to instead use the
    working s3 member join code.
    
    Signed-off-by: David Mulder <dmulder at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): David Mulder <dmulder at samba.org>
    Autobuild-Date(master): Wed Apr 21 21:40:13 UTC 2021 on sn-devel-184

commit d01a588c20723090b4d672d869c89a4397bce3c0
Author: David Mulder <dmulder at suse.com>
Date:   Fri Mar 19 12:31:42 2021 -0600

    python: glue function for detecting if selftest is enabled
    
    Signed-off-by: David Mulder <dmulder at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit e5a32d4a01b4df7078cf38e52ac4c465baa802f6
Author: David Mulder <dmulder at suse.com>
Date:   Mon Oct 26 15:13:50 2020 -0600

    python: Test s3 net join and leave
    
    Signed-off-by: David Mulder <dmulder at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit bbfdd6322fa33ccd583a475ceff70e2fd2a95872
Author: David Mulder <dmulder at suse.com>
Date:   Wed Oct 21 09:40:32 2020 -0600

    s3: Add s3 net python bindings
    
    This adds python bindings for the s3 net ads
    join and leave commands.
    
    Signed-off-by: David Mulder <dmulder at samba.org>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 python/pyglue.c                            |  12 +
 python/samba/__init__.py                   |   1 +
 python/samba/netcmd/domain.py              |  43 +++-
 python/samba/tests/s3_net_join.py          |  80 +++++++
 selftest/target/Samba4.pm                  |   6 +-
 {source4 => source3}/param/pyparam.h       |  11 +-
 {source4 => source3}/param/pyparam_util.c  |  43 ++--
 source3/param/wscript_build                |  12 +-
 source3/utils/py_net.c                     | 349 +++++++++++++++++++++++++++++
 {source4/libnet => source3/utils}/py_net.h |   6 +-
 source3/utils/wscript_build                |   8 +
 source4/selftest/tests.py                  |   3 +
 12 files changed, 534 insertions(+), 40 deletions(-)
 create mode 100644 python/samba/tests/s3_net_join.py
 copy {source4 => source3}/param/pyparam.h (85%)
 copy {source4 => source3}/param/pyparam_util.c (75%)
 create mode 100644 source3/utils/py_net.c
 copy {source4/libnet => source3/utils}/py_net.h (84%)


Changeset truncated at 500 lines:

diff --git a/python/pyglue.c b/python/pyglue.c
index ed79df10be2..1947373974a 100644
--- a/python/pyglue.c
+++ b/python/pyglue.c
@@ -256,6 +256,16 @@ static PyObject *py_is_ad_dc_built(PyObject *self,
 #endif
 }
 
+static PyObject *py_is_selftest_enabled(PyObject *self,
+                PyObject *Py_UNUSED(ignored))
+{
+#ifdef ENABLE_SELFTEST
+	Py_RETURN_TRUE;
+#else
+	Py_RETURN_FALSE;
+#endif
+}
+
 /*
   return the list of interface IPs we have configured
   takes an loadparm context, returns a list of IPs in string form
@@ -448,6 +458,8 @@ static PyMethodDef py_misc_methods[] = {
 		"Generate random bytes with specified length." },
 	{ "is_ad_dc_built", (PyCFunction)py_is_ad_dc_built, METH_NOARGS,
 		"is Samba built with AD DC?" },
+	{ "is_selftest_enabled", (PyCFunction)py_is_selftest_enabled,
+		METH_NOARGS, "is Samba built with selftest enabled?" },
 	{0}
 };
 
diff --git a/python/samba/__init__.py b/python/samba/__init__.py
index cabae4eaf1f..449e4826ffb 100644
--- a/python/samba/__init__.py
+++ b/python/samba/__init__.py
@@ -382,6 +382,7 @@ strstr_m = _glue.strstr_m
 is_ntvfs_fileserver_built = _glue.is_ntvfs_fileserver_built
 is_heimdal_built = _glue.is_heimdal_built
 is_ad_dc_built = _glue.is_ad_dc_built
+is_selftest_enabled = _glue.is_selftest_enabled
 
 NTSTATUSError = _glue.NTSTATUSError
 HRESULTError = _glue.HRESULTError
diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py
index ffabe8098c0..3c1e8956982 100644
--- a/python/samba/netcmd/domain.py
+++ b/python/samba/netcmd/domain.py
@@ -71,6 +71,8 @@ from samba.upgrade import upgrade_from_samba3
 from samba.drs_utils import drsuapi_connect
 from samba import remove_dc, arcfour_encrypt, string_to_byte_array
 from samba.auth_util import system_session_unix
+from samba.net_s3 import Net as s3_Net
+from samba.param import default_path
 
 from samba.dsdb import (
     DS_DOMAIN_FUNCTION_2000,
@@ -628,6 +630,12 @@ class cmd_domain_join(Command):
             action="store_true")
     ]
 
+    selftest_options = [
+        Option("--experimental-s4-member", action="store_true",
+               help="Perform member joins using the s4 Net join_member. "
+                    "Don't choose this unless you know what you're doing")
+    ]
+
     takes_options = []
     takes_options.extend(common_join_options)
     takes_options.extend(common_provision_join_options)
@@ -635,12 +643,15 @@ class cmd_domain_join(Command):
     if samba.is_ntvfs_fileserver_built():
         takes_options.extend(ntvfs_options)
 
+    if samba.is_selftest_enabled():
+        takes_options.extend(selftest_options)
+
     takes_args = ["domain", "role?"]
 
     def run(self, domain, role=None, sambaopts=None, credopts=None,
             versionopts=None, server=None, site=None, targetdir=None,
             domain_critical_only=False, machinepass=None,
-            use_ntvfs=False, dns_backend=None,
+            use_ntvfs=False, experimental_s4_member=False, dns_backend=None,
             quiet=False, verbose=False,
             plaintext_secrets=False,
             backend_store=None, backend_store_size=None):
@@ -656,9 +667,33 @@ class cmd_domain_join(Command):
             role = role.upper()
 
         if role is None or role == "MEMBER":
-            (join_password, sid, domain_name) = net.join_member(
-                domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
-                machinepass=machinepass)
+            if experimental_s4_member:
+                (join_password, sid, domain_name) = net.join_member(
+                    domain, netbios_name, LIBNET_JOIN_AUTOMATIC,
+                    machinepass=machinepass)
+            else:
+                lp.set('realm', domain)
+                if lp.get('workgroup') == 'WORKGROUP':
+                    lp.set('workgroup', net.finddc(domain=domain,
+                        flags=(nbt.NBT_SERVER_LDAP |
+                               nbt.NBT_SERVER_DS)).domain_name)
+                lp.set('server role', 'member server')
+                smb_conf = lp.configfile if lp.configfile else default_path()
+                with tempfile.NamedTemporaryFile(delete=False,
+                        dir=os.path.dirname(smb_conf)) as f:
+                    lp.dump(False, f.name)
+                    if os.path.exists(smb_conf):
+                        mode = os.stat(smb_conf).st_mode
+                        os.chmod(f.name, mode)
+                    os.rename(f.name, smb_conf)
+                s3_lp = s3param.get_context()
+                s3_lp.load(smb_conf)
+                if machinepass is None:
+                    machinepass = samba.generate_random_machine_password(14, 40)
+                s3_net = s3_Net(creds, s3_lp, server=server)
+                (sid, domain_name) = s3_net.join_member(netbios_name,
+                                                        machinepass=machinepass,
+                                                        debug=verbose)
 
             self.errf.write("Joined domain %s (%s)\n" % (domain_name, sid))
         elif role == "DC":
diff --git a/python/samba/tests/s3_net_join.py b/python/samba/tests/s3_net_join.py
new file mode 100644
index 00000000000..dd691e4a116
--- /dev/null
+++ b/python/samba/tests/s3_net_join.py
@@ -0,0 +1,80 @@
+# Unix SMB/CIFS implementation.
+#
+# Copyright (C) David Mulder <dmulder at samba.org> 2020
+#
+# 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/>.
+#
+
+"""
+Confirm that net_s3.join_member works
+"""
+
+import samba.tests
+import os
+from samba.net_s3 import Net as s3_Net
+from samba.credentials import DONT_USE_KERBEROS
+from samba.samba3 import param as s3param
+from samba import WERRORError
+
+
+def rm(rmdir):
+    for f in os.listdir(rmdir):
+        if os.path.isdir(os.path.join(rmdir, f)):
+            rm(os.path.join(rmdir, f))
+            os.rmdir(os.path.join(rmdir, f))
+        else:
+            os.unlink(os.path.join(rmdir, f))
+
+class NetS3JoinTests(samba.tests.TestCaseInTempDir):
+
+    def setUp(self):
+        super(NetS3JoinTests, self).setUp()
+        self.realm = os.environ["REALM"]
+        self.domain = os.environ["DOMAIN"]
+        self.server = os.environ["SERVER"]
+        self.lp = self.get_loadparm()
+
+    def tearDown(self):
+        super(NetS3JoinTests, self).tearDown()
+
+    def test_net_join(self):
+        netbios_name = "NetJoinTest"
+        machinepass  = "abcdefghij"
+        creds = self.insta_creds(template=self.get_credentials(),
+                                 kerberos_state=DONT_USE_KERBEROS)
+        s3_lp = s3param.get_context()
+        s3_lp.load(self.lp.configfile)
+
+        s3_lp.set('realm', self.realm)
+        s3_lp.set('workgroup', self.domain)
+        s3_lp.set("private dir", self.tempdir)
+        s3_lp.set("lock dir", self.tempdir)
+        s3_lp.set("state directory", self.tempdir)
+        s3_lp.set('server role', 'member server')
+        net = s3_Net(creds, s3_lp, server=self.server)
+
+        try:
+            (domain_sid, domain_name) = net.join_member(netbios_name,
+                                                        machinepass=machinepass)
+        except WERRORError as e:
+            self.fail('Join failed: %s' % e.args[1])
+            raise
+
+        try:
+            ret = net.leave()
+        except WERRORError as e:
+            self.fail('Leave failed: %s' % e.args[1])
+            raise
+        self.assertTrue(ret, 'Leave failed!')
+        rm(self.tempdir)
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 61e9eb61422..20cc2949189 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -1336,7 +1336,7 @@ server min protocol = LANMAN1
 
 	my $samba_tool =  Samba::bindir_path($self, "samba-tool");
 	my $cmd = $self->get_cmd_env_vars($ret);
-	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} member";
+	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member member";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD}";
 
@@ -1401,7 +1401,7 @@ sub provision_rpc_proxy($$$)
 
 	# The joind runs in the context of the rpc_proxy/member for now
 	my $cmd = $self->get_cmd_env_vars($ret);
-	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} member";
+	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member member";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD}";
 
@@ -1481,7 +1481,7 @@ sub provision_promoted_dc($$$)
 
 	my $samba_tool =  Samba::bindir_path($self, "samba-tool");
 	my $cmd = $self->get_cmd_env_vars($ret);
-	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} MEMBER --realm=$dcvars->{REALM}";
+	$cmd .= "$samba_tool domain join $ret->{CONFIGURATION} $dcvars->{REALM} --experimental-s4-member MEMBER --realm=$dcvars->{REALM}";
 	$cmd .= " -U$dcvars->{DC_USERNAME}\%$dcvars->{DC_PASSWORD}";
 	$cmd .= " --machinepass=machine$ret->{PASSWORD}";
 
diff --git a/source4/param/pyparam.h b/source3/param/pyparam.h
similarity index 85%
copy from source4/param/pyparam.h
copy to source3/param/pyparam.h
index e8944f5fa19..8a40a9b7b62 100644
--- a/source4/param/pyparam.h
+++ b/source3/param/pyparam.h
@@ -1,18 +1,18 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    Samba utility functions
-   Copyright (C) Jelmer Vernooij <jelmer at samba.org> 2008
-   
+   Copyright (C) David Mulder <dmulder at samba.org> 2021
+
    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/>.
 */
@@ -23,6 +23,5 @@
 #include "param/param.h"
 
 _PUBLIC_ struct loadparm_context *lpcfg_from_py_object(TALLOC_CTX *mem_ctx, PyObject *py_obj);
-_PUBLIC_ struct loadparm_context *py_default_loadparm_context(TALLOC_CTX *mem_ctx);
 
 #endif /* _PYPARAM_H_ */
diff --git a/source4/param/pyparam_util.c b/source3/param/pyparam_util.c
similarity index 75%
copy from source4/param/pyparam_util.c
copy to source3/param/pyparam_util.c
index 9b9dcd22c90..67ab4945d6a 100644
--- a/source4/param/pyparam_util.c
+++ b/source3/param/pyparam_util.c
@@ -1,54 +1,56 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    Samba utility functions
-   Copyright (C) Jelmer Vernooij <jelmer at samba.org> 2007-2008
-   
+   Copyright (C) David Mulder <dmulder at samba.org> 2021
+
    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 <Python.h>
-#include "py3compat.h"
 #include "includes.h"
 #include "param/param.h"
 #include "param/pyparam.h"
 #include "param/loadparm.h"
+#include "param/s3_param.h"
 #include <pytalloc.h>
 
+#define PyErr_FromString(str) Py_BuildValue("(s)", discard_const_p(char, str))
 #define PyLoadparmContext_AsLoadparmContext(obj) pytalloc_get_type(obj, struct loadparm_context)
 
 _PUBLIC_ struct loadparm_context *lpcfg_from_py_object(TALLOC_CTX *mem_ctx, PyObject *py_obj)
 {
-	struct loadparm_context *lp_ctx;
 	PyObject *param_mod;
 	PyTypeObject *lp_type;
 	bool is_lpobj;
+	const struct loadparm_s3_helpers *s3_context;
+	struct loadparm_context *s4_context;
 
-	if (PyUnicode_Check(py_obj)) {
-		lp_ctx = loadparm_init_global(false);
-		if (lp_ctx == NULL) {
+	if (py_obj == Py_None) {
+		s3_context = loadparm_s3_helpers();
+
+		s4_context = loadparm_init_s3(mem_ctx, s3_context);
+		if (s4_context == NULL) {
+			PyErr_NoMemory();
 			return NULL;
 		}
-		if (!lpcfg_load(lp_ctx, PyUnicode_AsUTF8(py_obj))) {
-			PyErr_Format(PyExc_RuntimeError, "Unable to load %s", 
-				     PyUnicode_AsUTF8(py_obj));
+
+		if (!lpcfg_load_default(s4_context)) {
+			PyErr_FromString("Failed to load defaults\n");
 			return NULL;
 		}
-		return lp_ctx;
-	}
 
-	if (py_obj == Py_None) {
-		return loadparm_init_global(true);
+		return s4_context;
 	}
 
 	param_mod = PyImport_ImportModule("samba.param");
@@ -72,10 +74,3 @@ _PUBLIC_ struct loadparm_context *lpcfg_from_py_object(TALLOC_CTX *mem_ctx, PyOb
 	PyErr_SetNone(PyExc_TypeError);
 	return NULL;
 }
-
-struct loadparm_context *py_default_loadparm_context(TALLOC_CTX *mem_ctx)
-{
-	return loadparm_init_global(true);
-}
-
-
diff --git a/source3/param/wscript_build b/source3/param/wscript_build
index 8a374c466c0..22faf5cdcce 100644
--- a/source3/param/wscript_build
+++ b/source3/param/wscript_build
@@ -15,12 +15,22 @@ bld.SAMBA_GENERATOR('s3_param_proto_h',
                     rule='${PYTHON} ${SRC[0].abspath(env)} --file ${SRC[1].abspath(env)} --output ${TGT} --mode=S3PROTO')
 
 pytalloc_util = bld.pyembed_libname('pytalloc-util')
+pyparam_util = bld.pyembed_libname('pyparam3_util')
+libpython = bld.pyembed_libname('LIBPYTHON')
+
 bld.SAMBA3_PYTHON('pys3param',
                   source='pyparam.c',
-                  deps='smbconf',
+                  deps='smbconf %s' % pyparam_util,
                   public_deps=' '.join(['samba-hostconfig', pytalloc_util, 'talloc']),
                   realname='samba/samba3/param.so')
 
+bld.SAMBA3_SUBSYSTEM(pyparam_util,
+                source='pyparam_util.c',
+                deps='%s samba-hostconfig %s' % (libpython, pytalloc_util),
+                pyext=True,
+                enabled=bld.PYTHON_BUILD_IS_ENABLED()
+                )
+
 bld.SAMBA3_SUBSYSTEM('param_service',
                      source='service.c',
                      deps = 'USER_UTIL smbconf PRINTING')
diff --git a/source3/utils/py_net.c b/source3/utils/py_net.c
new file mode 100644
index 00000000000..1d9b1d783c6
--- /dev/null
+++ b/source3/utils/py_net.c
@@ -0,0 +1,349 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba python bindings to s3 libnet library
+
+   Copyright (C) David Mulder <dmulder at samba.org>
+
+   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 <Python.h>
+#include "includes.h"
+#include <pytalloc.h>
+#include "python/modules.h"
+#include "python/py3compat.h"
+#include "rpc_client/rpc_client.h"
+#include <sys/socket.h>
+#include "net.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/pycredentials.h"
+#include "lib/cmdline_contexts.h"
+#include "param/loadparm.h"
+#include "param/s3_param.h"
+#include "param/pyparam.h"
+#include "py_net.h"
+#include "librpc/gen_ndr/libnet_join.h"
+#include "libnet/libnet_join.h"
+#include "libcli/security/dom_sid.h"
+#include "dynconfig/dynconfig.h"
+
+static WERROR check_ads_config(struct loadparm_context *lp_ctx)
+{
+	if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_MEMBER ) {
+		d_printf(_("Host is not configured as a member server.\n"));
+		return WERR_INVALID_DOMAIN_ROLE;
+	}
+
+	if (strlen(lpcfg_netbios_name(lp_ctx)) > 15) {
+		d_printf(_("Our netbios name can be at most 15 chars long, "
+			   "\"%s\" is %u chars long\n"), lpcfg_netbios_name(lp_ctx),
+			 (unsigned int)strlen(lpcfg_netbios_name(lp_ctx)));
+		return WERR_INVALID_COMPUTERNAME;
+	}
+
+	if ( lpcfg_security(lp_ctx) == SEC_ADS && !*lpcfg_realm(lp_ctx)) {
+		d_fprintf(stderr, _("realm must be set in in %s for ADS "
+			  "join to succeed.\n"), get_dyn_CONFIGFILE());
+		return WERR_INVALID_PARAMETER;
+	}
+
+	return WERR_OK;
+}
+
+static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+	struct libnet_JoinCtx *r = NULL;
+	WERROR werr;
+	PyObject *result;
+	TALLOC_CTX *mem_ctx;
+	bool modify_config = lp_config_backend_is_registry();
+	const char *kwnames[] = { "dnshostname", "createupn", "createcomputer",
+				  "osName", "osVer", "osServicePack",
+				  "machinepass", "debug", NULL };
+
+	mem_ctx = talloc_new(self->mem_ctx);
+	if (mem_ctx == NULL) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	werr = libnet_init_JoinCtx(mem_ctx, &r);
+	if (!W_ERROR_IS_OK(werr)) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sssssssp:Join",
+					 discard_const_p(char *, kwnames),
+					 &r->in.dnshostname,
+					 &r->in.upn,
+					 &r->in.account_ou,
+					 &r->in.os_name,
+					 &r->in.os_version,
+					 &r->in.os_servicepack,
+					 &r->in.machine_password,
+					 &r->in.debug)) {
+		talloc_free(mem_ctx);


-- 
Samba Shared Repository



More information about the samba-cvs mailing list