summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2017-05-22 16:00:08 +0200
committerVolker Lendecke <vl@samba.org>2017-06-15 13:19:15 +0200
commit26932271a85a08a755c81df0138d770b6a3bc3a3 (patch)
treec3101fefbaf0f0c1eb18ebdced70bd71600a1094
parent2c200dd00d8a3969e5dbbdb0598b5073fd3cf65e (diff)
downloadsamba-26932271a85a08a755c81df0138d770b6a3bc3a3.tar.gz
smbd: Claim version in g_lock
Protect smbd against version incompatibilities in a cluster. At first startup smbd locks "samba_version_string" and writes its version string. It then downgrades the lock to a read lock. Subsequent smbds check against the version string and also keep the read lock around. If the version does not match, we try to write our own version. But as there's a read lock, the lock upgrade to write lock will fail due the read lock being around. So as long as there's one smbd with this read lock, no other version of smbd will be able to start. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
-rw-r--r--docs-xml/smbdotconf/misc/allowunsafeclusterupgrade.xml16
-rw-r--r--source3/smbd/server.c114
2 files changed, 130 insertions, 0 deletions
diff --git a/docs-xml/smbdotconf/misc/allowunsafeclusterupgrade.xml b/docs-xml/smbdotconf/misc/allowunsafeclusterupgrade.xml
new file mode 100644
index 00000000000..02398ff2c95
--- /dev/null
+++ b/docs-xml/smbdotconf/misc/allowunsafeclusterupgrade.xml
@@ -0,0 +1,16 @@
+<samba:parameter name="allow unsafe cluster upgrade"
+ context="G"
+ type="boolean"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>If set to no (the default), smbd checks at startup if
+ other smbd versions are running in the cluster and refuses to
+ start if so. This is done to protect data corruption in
+ internal data structures due to incompatible Samba versions
+ running concurrently in the same cluster. Setting this
+ parameter to <value type="example">yes</value> disables this
+ safety check.
+ </para>
+</description>
+<value type="default">no</value>
+</samba:parameter>
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 494e188308b..4883c4ec510 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -53,6 +53,7 @@
#include "smbd/smbd_cleanupd.h"
#include "lib/util/sys_rw.h"
#include "cleanupdb.h"
+#include "g_lock.h"
#ifdef CLUSTER_SUPPORT
#include "ctdb_protocol.h"
@@ -1442,6 +1443,110 @@ static void smbd_parent_sig_hup_handler(struct tevent_context *ev,
printing_subsystem_update(parent->ev_ctx, parent->msg_ctx, true);
}
+struct smbd_claim_version_state {
+ TALLOC_CTX *mem_ctx;
+ char *version;
+};
+
+static void smbd_claim_version_parser(const struct g_lock_rec *locks,
+ size_t num_locks,
+ const uint8_t *data,
+ size_t datalen,
+ void *private_data)
+{
+ struct smbd_claim_version_state *state = private_data;
+
+ if (datalen == 0) {
+ state->version = NULL;
+ return;
+ }
+ if (data[datalen-1] != '\0') {
+ DBG_WARNING("Invalid samba version\n");
+ dump_data(DBGLVL_WARNING, data, datalen);
+ state->version = NULL;
+ return;
+ }
+ state->version = talloc_strdup(state->mem_ctx, (const char *)data);
+}
+
+static NTSTATUS smbd_claim_version(struct messaging_context *msg,
+ const char *version)
+{
+ const char *name = "samba_version_string";
+ struct smbd_claim_version_state state;
+ struct g_lock_ctx *ctx;
+ NTSTATUS status;
+
+ ctx = g_lock_ctx_init(msg, msg);
+ if (ctx == NULL) {
+ DBG_WARNING("g_lock_ctx_init failed\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = g_lock_lock(ctx, name, G_LOCK_READ,
+ (struct timeval) { .tv_sec = 60 });
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("g_lock_lock(G_LOCK_READ) failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(ctx);
+ return status;
+ }
+
+ state = (struct smbd_claim_version_state) { .mem_ctx = ctx };
+
+ status = g_lock_dump(ctx, name, smbd_claim_version_parser, &state);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DBG_ERR("Could not read samba_version_string\n");
+ g_lock_unlock(ctx, name);
+ TALLOC_FREE(ctx);
+ return status;
+ }
+
+ if ((state.version != NULL) && (strcmp(version, state.version) == 0)) {
+ /*
+ * Leave the read lock for us around. Someone else already
+ * set the version correctly
+ */
+ TALLOC_FREE(ctx);
+ return NT_STATUS_OK;
+ }
+
+ status = g_lock_lock(ctx, name, G_LOCK_WRITE,
+ (struct timeval) { .tv_sec = 60 });
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("g_lock_lock(G_LOCK_WRITE) failed: %s\n",
+ nt_errstr(status));
+ DBG_ERR("smbd %s already running, refusing to start "
+ "version %s\n", state.version, version);
+ TALLOC_FREE(ctx);
+ return NT_STATUS_SXS_VERSION_CONFLICT;
+ }
+
+ status = g_lock_write_data(ctx, name, (const uint8_t *)version,
+ strlen(version)+1);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("g_lock_write_data failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(ctx);
+ return status;
+ }
+
+ status = g_lock_lock(ctx, name, G_LOCK_READ,
+ (struct timeval) { .tv_sec = 60 });
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("g_lock_lock(G_LOCK_READ) failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(ctx);
+ return status;
+ }
+
+ /*
+ * Leave "ctx" dangling so that g_lock.tdb keeps opened.
+ */
+ return NT_STATUS_OK;
+}
+
/****************************************************************************
main program.
****************************************************************************/
@@ -1905,6 +2010,15 @@ extern void build_options(bool screen);
exit_daemon("Samba cannot init global open", map_errno_from_nt_status(status));
}
+ if (lp_clustering() && !lp_allow_unsafe_cluster_upgrade()) {
+ status = smbd_claim_version(msg_ctx, samba_version_string());
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("Could not claim version: %s\n",
+ nt_errstr(status));
+ return -1;
+ }
+ }
+
/* This MUST be done before start_epmd() because otherwise
* start_epmd() forks and races against dcesrv_ep_setup() to
* call directory_create_or_exist() */