diff --git a/core/src/dird/authenticate_console.cc b/core/src/dird/authenticate_console.cc index c01e32cd611..13225e566fe 100644 --- a/core/src/dird/authenticate_console.cc +++ b/core/src/dird/authenticate_console.cc @@ -32,44 +32,165 @@ #include "lib/qualified_resource_name_type_converter.h" #include "lib/bstringlist.h" #include "lib/jcr.h" +#include "include/version_numbers.h" namespace directordaemon { -static bool NumberOfConsoleConnectionsExceeded() +enum class OptionResult { + Completed, + Skipped +}; + +class ConsoleAuthenticator { - JobControlRecord *jcr; - unsigned int cnt = 0; + public: + ConsoleAuthenticator(UaContext *ua, const std::string &console_name); + virtual ~ConsoleAuthenticator() = default; - foreach_jcr(jcr) - { - if (jcr->is_JobType(JT_CONSOLE)) { cnt++; } + ConsoleAuthenticator (const ConsoleAuthenticator &other) = delete; + virtual OptionResult AuthenticateDefaultConsole() = 0; + virtual void AuthenticateNamedConsole() = 0; + virtual OptionResult AuthenticatePamUser() = 0; + virtual bool SendInfoMessage() { return false; } + + bool auth_success_; + std::string console_name_; + + protected: + OptionResult DoDefaultAuthentication(); + void DoNamedAuthentication(); + + UaContext *ua_; + ConsoleResource *optional_console_resource_; +}; + +ConsoleAuthenticator::ConsoleAuthenticator(UaContext *ua, const std::string &console_name) + : auth_success_(false) + , console_name_(console_name) + , ua_(ua) +{ + optional_console_resource_ = (ConsoleResource *)my_config->GetResWithName(R_CONSOLE, console_name_.c_str()); +} + +OptionResult ConsoleAuthenticator::DoDefaultAuthentication() +{ + const std::string default_console_name { "*UserAgent*" }; + + if (console_name_ != default_console_name) { + return OptionResult::Skipped; } - endeach_jcr(jcr); - return (cnt >= me->MaxConsoleConnections) ? true : false; + auth_success_ = ua_->UA_sock->AuthenticateInboundConnection(NULL, "Console", + default_console_name.c_str(), + me->password_, + me); + ua_->cons = nullptr; + return OptionResult::Completed; } -static bool GetConsoleName(BareosSocket *ua_sock, std::string &name) +void ConsoleAuthenticator::DoNamedAuthentication() { - char buffer[MAX_NAME_LENGTH]; /* zero terminated C-string */ + if (!optional_console_resource_) { + Dmsg1(200, "Could not find resource for console %s\n", console_name_.c_str()); + auth_success_ = false; + return; + } - if (sscanf(ua_sock->msg, "Hello %127s calling\n", buffer) != 1) { - Emsg4(M_ERROR, 0, _("UA Hello from %s:%s:%d is invalid. Got: %s\n"), ua_sock->who(), ua_sock->host(), ua_sock->port(), - ua_sock->msg); - return false; + auth_success_ = ua_->UA_sock->AuthenticateInboundConnection(NULL, "Console", + console_name_.c_str(), + optional_console_resource_->password_, + optional_console_resource_); + if (auth_success_) { + ua_->cons = optional_console_resource_; + } else { + ua_->cons = nullptr; + Dmsg1(200, "Could not authenticate console %s\n", console_name_.c_str()); } - UnbashSpaces(buffer); - name = buffer; - return true; } -static void LogErrorMessage(std::string console_name, UaContext *ua) +class ConsoleAuthenticatorBefore_18_2 : public ConsoleAuthenticator { - Emsg4(M_ERROR, 0, _("Unable to authenticate console \"%s\" at %s:%s:%d.\n"), console_name.c_str(), - ua->UA_sock->who(), ua->UA_sock->host(), ua->UA_sock->port()); + public: + ConsoleAuthenticatorBefore_18_2(UaContext *ua, const std::string &console_name) + : ConsoleAuthenticator(ua, console_name) {} + OptionResult AuthenticateDefaultConsole() override; + void AuthenticateNamedConsole() override; + OptionResult AuthenticatePamUser() override; + + private: + bool SendOkMessageWithSpaceSeparators() { + std::string cipher; + ua_->UA_sock->GetCipherMessageString(cipher); + if (ua_ && ua_->UA_sock) { + return ua_->UA_sock->fsend(_("1000 OK: %s Version: %s (%s) " + "-- %s\n"), + my_name, VERSION, BDATE, + cipher.c_str()); + } + return false; + } + + bool SendNotAuthorizedMessageWithSpaceSeparators() { + if (ua_ && ua_->UA_sock) { + return ua_->UA_sock->fsend("%s", "1999 You are not authorized.\n"); + } + return false; + } +}; + +OptionResult ConsoleAuthenticatorBefore_18_2::AuthenticateDefaultConsole() +{ + if (DoDefaultAuthentication() == OptionResult::Skipped) { return OptionResult::Skipped; } + + if (auth_success_) { + auth_success_ = SendOkMessageWithSpaceSeparators(); + } else { + SendNotAuthorizedMessageWithSpaceSeparators(); + } + if (!auth_success_) { + Dmsg1(200, "Could not authenticate outdated console %s\n", console_name_.c_str()); + } + return OptionResult::Completed; } -static bool SendResponseMessage(UaContext *ua, uint32_t response_id, bool send_version_info) +void ConsoleAuthenticatorBefore_18_2::AuthenticateNamedConsole() +{ + DoNamedAuthentication(); + + if (auth_success_) { + auth_success_ = SendOkMessageWithSpaceSeparators(); + } else { + SendNotAuthorizedMessageWithSpaceSeparators(); + } +} + +OptionResult ConsoleAuthenticatorBefore_18_2::AuthenticatePamUser() +{ + if (!optional_console_resource_) { + return OptionResult::Skipped; + } + if (optional_console_resource_->use_pam_authentication_) { + Dmsg1(200, "PAM authentication using outdated Bareos console denied: %s\n", console_name_.c_str()); + auth_success_ = false; /* console before 18_2 cannot do pam authentication */ + return OptionResult::Completed; + } + return OptionResult::Skipped; +} + +class ConsoleAuthenticatorFrom_18_2 : public ConsoleAuthenticator +{ + public: + ConsoleAuthenticatorFrom_18_2(UaContext *ua, const std::string &console_name) + : ConsoleAuthenticator(ua, console_name) {} + OptionResult AuthenticateDefaultConsole() override; + void AuthenticateNamedConsole() override; + OptionResult AuthenticatePamUser() override; + bool SendInfoMessage() override; + private: + bool SendResponseMessage(uint32_t response_id, bool send_version_info); +}; + +bool ConsoleAuthenticatorFrom_18_2::SendResponseMessage(uint32_t response_id, bool send_version_info) { std::string message; if (send_version_info) { @@ -77,83 +198,89 @@ static bool SendResponseMessage(UaContext *ua, uint32_t response_id, bool send_v ::snprintf(version_info, 100, "OK: %s Version: %s (%s)", my_name, VERSION, BDATE); message = version_info; } - return ua->UA_sock->FormatAndSendResponseMessage(response_id, message); + return ua_->UA_sock->FormatAndSendResponseMessage(response_id, message); +} + +bool ConsoleAuthenticatorFrom_18_2::SendInfoMessage() +{ + std::string message {"You are "}; + if (ua_->cons) { + message += "logged in as: "; + message += ua_->cons->name(); + } else { + message += "connected using the default console"; + } + auth_success_ = ua_->UA_sock->FormatAndSendResponseMessage(kMessageIdInfoMessage, message); + return true; } -static bool OptionalAuthenticateRootConsole(std::string console_name, UaContext *ua, bool &auth_success) +OptionResult ConsoleAuthenticatorFrom_18_2::AuthenticateDefaultConsole() { - const std::string root_console_name { "*UserAgent*" }; - if (console_name != root_console_name) { - return false; /* no need to evaluate auth_success */ + if (DoDefaultAuthentication() == OptionResult::Skipped) { + return OptionResult::Skipped; + } + + if (auth_success_) { + if (!SendResponseMessage(kMessageIdOk, true)) { + auth_success_ = false; + } } - auth_success = ua->UA_sock->AuthenticateInboundConnection(NULL, "Console", root_console_name.c_str(), me->password_, me); - if (!SendResponseMessage(ua, kMessageIdOk, true)) { - auth_success = false; + if (!auth_success_) { + Dmsg1(200, "Could not authenticate console %s\n", console_name_.c_str()); } - return true; + return OptionResult::Completed; } -static void AuthenticateNamedConsole(std::string console_name, UaContext *ua, bool &auth_success) +void ConsoleAuthenticatorFrom_18_2::AuthenticateNamedConsole() { - ConsoleResource *cons; - cons = (ConsoleResource *)my_config->GetResWithName(R_CONSOLE, console_name.c_str()); - if (!cons) { /* if console resource cannot be obtained is treated as an error */ - auth_success = false; - return; + DoNamedAuthentication(); + + if (!auth_success_) { return; } + + uint32_t response_id = kMessageIdOk; + bool send_version = true; + + if (optional_console_resource_ && optional_console_resource_->use_pam_authentication_) { + response_id = kMessageIdPamRequired; + send_version = false; } - if (!ua->UA_sock->AuthenticateInboundConnection(NULL, "Console", console_name.c_str(), cons->password_, cons)) { - ua->cons = nullptr; - auth_success = false; - } else { - ua->cons = cons; - auth_success = true; - } - if (auth_success) { - uint32_t response_id = kMessageIdOk; - bool send_version = true; - if (cons->use_pam_authentication_) { - response_id = kMessageIdPamRequired; - send_version = false; - } - if (!SendResponseMessage(ua, response_id, send_version)) { - auth_success = false; - } + + if (!SendResponseMessage(response_id, send_version)) { + Dmsg1(200, "Send of response message failed %s\n", console_name_.c_str()); + auth_success_ = false; } } -static bool OptionalAuthenticatePamUser(std::string console_name, UaContext *ua, bool &auth_success) +OptionResult ConsoleAuthenticatorFrom_18_2::AuthenticatePamUser() { - ConsoleResource *cons = (ConsoleResource *)my_config->GetResWithName(R_CONSOLE, console_name.c_str()); - #if !defined(HAVE_PAM) { - if (cons && cons->use_pam_authentication_) { + if (optional_console_resource_ && optional_console_resource_->use_pam_authentication_) { Emsg0(M_ERROR, 0, _("PAM is not available on this director\n")); - auth_success = false; - return true; + auth_success_ = false; + return OptionResult::Completed; } else { - return false; /* auth_success can be ignored */ + return OptionResult::Skipped; } } #else /* HAVE_PAM */ { - if (!cons) { /* if console resource cannot be obtained is treated as an error */ - auth_success = false; - return true; + if (!optional_console_resource_) { + auth_success_ = false; + return OptionResult::Completed; } - /* no need to evaluate auth_success if no pam is required */ - if (!cons->use_pam_authentication_) { return false; } + if (!optional_console_resource_->use_pam_authentication_) { return OptionResult::Skipped; } uint32_t response_id; BStringList message_arguments; - if (!ua->UA_sock->ReceiveAndEvaluateResponseMessage(response_id, message_arguments)) { + if (!ua_->UA_sock->ReceiveAndEvaluateResponseMessage(response_id, message_arguments)) { Dmsg2(100, "Could not evaluate response_id: %d - %d", response_id, message_arguments.JoinReadable().c_str()); - auth_success = false; - return true; + auth_success_ = false; + return OptionResult::Completed; } std::string pam_username; @@ -163,8 +290,8 @@ static bool OptionalAuthenticatePamUser(std::string console_name, UaContext *ua, Dmsg0(200, "Console chooses PAM direct credentials\n"); if (message_arguments.size() < 3) { Dmsg0(200, "Console sent wrong number of credentials\n"); - auth_success = false; - return true; + auth_success_ = false; + return OptionResult::Completed; } else { pam_username = message_arguments.at(1); pam_password = message_arguments.at(2); @@ -173,74 +300,121 @@ static bool OptionalAuthenticatePamUser(std::string console_name, UaContext *ua, Dmsg0(200, "Console chooses PAM interactive\n"); } - Dmsg1(200, "Try to authenticate user:%s\n", pam_username.c_str()); + Dmsg1(200, "Try to authenticate user using PAM:%s\n", pam_username.c_str()); std::string authenticated_username; - if (!PamAuthenticateUser(ua->UA_sock, pam_username, pam_password, authenticated_username)) { - ua->cons = nullptr; - auth_success = false; + if (!PamAuthenticateUser(ua_->UA_sock, pam_username, pam_password, authenticated_username)) { + ua_->cons = nullptr; + auth_success_ = false; } else { ConsoleResource *user = (ConsoleResource *)my_config->GetResWithName(R_CONSOLE, authenticated_username.c_str()); if (!user) { Dmsg1(200, "No user config found for user %s\n", authenticated_username.c_str()); - ua->cons = nullptr; - auth_success = false; + ua_->cons = nullptr; + auth_success_ = false; } else { - ua->cons = user; - auth_success = true; + ua_->cons = user; + auth_success_ = true; } } - if (auth_success) { - if (!SendResponseMessage(ua, kMessageIdOk, true)) { - auth_success = false; + if (auth_success_) { + if (!SendResponseMessage(kMessageIdOk, true)) { + auth_success_ = false; } } - return true; + return OptionResult::Completed; } /* HAVE PAM */ #endif /* !HAVE_PAM */ } -bool AuthenticateConsole(UaContext *ua) +static void LogErrorMessage(std::string console_name, UaContext *ua) { - std::string console_name; - if (!GetConsoleName(ua->UA_sock, console_name)) { + Emsg4(M_ERROR, 0, _("Unable to authenticate console \"%s\" at %s:%s:%d.\n"), console_name.c_str(), + ua->UA_sock->who(), ua->UA_sock->host(), ua->UA_sock->port()); +} + +static bool NumberOfConsoleConnectionsExceeded() +{ + JobControlRecord *jcr; + unsigned int cnt = 0; + + foreach_jcr(jcr) + { + if (jcr->is_JobType(JT_CONSOLE)) { cnt++; } + } + endeach_jcr(jcr); + + return (cnt >= me->MaxConsoleConnections) ? true : false; +} + +static bool GetConsoleNameAndVersion(BareosSocket *ua_sock, std::string &name_out, BareosVersionNumber &version_out) +{ + std::string name; + std::string r_code_str_unused; + BareosVersionNumber version = BareosVersionNumber::kUndefined; + + if (GetNameAndResourceTypeAndVersionFromHello(ua_sock->msg, name, r_code_str_unused, version)) { + name_out = name; + version_out = version; + return true; + } else { + Emsg4(M_ERROR, 0, _("UA Hello from %s:%s:%d is invalid. Got: %s\n"), + ua_sock->who(), ua_sock->host(), ua_sock->port(), ua_sock->msg); return false; } +} +static ConsoleAuthenticator* CreateConsoleAuthenticator(UaContext *ua) +{ + std::string console_name; + BareosVersionNumber version = BareosVersionNumber::kUndefined; + if (!GetConsoleNameAndVersion(ua->UA_sock, console_name, version)) { + return nullptr; + } + + ua->UA_sock->connected_daemon_version_ = version; /* this is redundant if using cleartext handshake */ + + if (version < BareosVersionNumber::kRelease_18_2) { + return new ConsoleAuthenticatorBefore_18_2(ua, console_name); + } else /* == kRelease_18_2 */ { + return new ConsoleAuthenticatorFrom_18_2(ua, console_name); + } +} + +bool AuthenticateConsole(UaContext *ua) +{ if (NumberOfConsoleConnectionsExceeded()) { Emsg0(M_ERROR, 0, _("Number of console connections exceeded MaximumConsoleConnections\n")); return false; } - bool auth_success = false; + std::unique_ptr console_authenticator(CreateConsoleAuthenticator(ua)); + if (!console_authenticator) { return false; } - if (OptionalAuthenticateRootConsole(console_name, ua, auth_success)) { - if (!auth_success) { - LogErrorMessage(console_name, ua); + if (console_authenticator->AuthenticateDefaultConsole() == OptionResult::Completed) { + if (!console_authenticator->auth_success_) { + LogErrorMessage(console_authenticator->console_name_, ua); return false; } } else { - AuthenticateNamedConsole(console_name, ua, auth_success); - if (!auth_success) { - LogErrorMessage(console_name, ua); + console_authenticator->AuthenticateNamedConsole(); + if (!console_authenticator->auth_success_) { + LogErrorMessage(console_authenticator->console_name_, ua); return false; } - if (OptionalAuthenticatePamUser(console_name, ua, auth_success)) { - if (!auth_success) { - LogErrorMessage(console_name, ua); + if (console_authenticator->AuthenticatePamUser() == OptionResult::Completed) { + if (!console_authenticator->auth_success_) { + LogErrorMessage(console_authenticator->console_name_, ua); return false; } } } - std::string message{"You are "}; - if (ua->cons) { - message += "logged in as: "; - message += ua->cons->name(); - } else { - message += "connected using the default console"; + if (console_authenticator->SendInfoMessage()) { + if (!console_authenticator->auth_success_) { + return false; + } } - ua->UA_sock->FormatAndSendResponseMessage(kMessageIdInfoMessage, message); return true; } } /* namespace directordaemon */ diff --git a/core/src/include/version_numbers.h b/core/src/include/version_numbers.h new file mode 100644 index 00000000000..4895ddf6767 --- /dev/null +++ b/core/src/include/version_numbers.h @@ -0,0 +1,35 @@ +/* + BAREOSĀ® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2000-2010 Free Software Foundation Europe e.V. + Copyright (C) 2011-2012 Planets Communications B.V. + Copyright (C) 2013-2016 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef BAREOS_INCLUDE_RELEASE_NUMBERS_H_ +#define BAREOS_INCLUDE_RELEASE_NUMBERS_H_ + +#include "include/bc_types.h" + +enum class BareosVersionNumber : uint32_t +{ + kRelease_18_2 = static_cast(1802), + kUndefined = static_cast(1) +}; + +#endif /* BAREOS_INCLUDE_RELEASE_NUMBERS_H_ */ diff --git a/core/src/lib/bsock.cc b/core/src/lib/bsock.cc index 65491854ea1..d88765003f4 100644 --- a/core/src/lib/bsock.cc +++ b/core/src/lib/bsock.cc @@ -55,6 +55,7 @@ BareosSocket::BareosSocket() , sleep_time_after_authentication_error(5) , client_addr{0} , peer_addr{0} + , connected_daemon_version_(BareosVersionNumber::kUndefined) /* protected: */ , jcr_(nullptr) @@ -100,6 +101,7 @@ BareosSocket::BareosSocket(const BareosSocket &other) client_addr = other.client_addr; peer_addr = other.peer_addr; tls_conn = other.tls_conn; + connected_daemon_version_ = other.connected_daemon_version_; /* protected: */ jcr_ = other.jcr_; @@ -318,10 +320,6 @@ void BareosSocket::SetKillable(bool killable) if (jcr_) { jcr_->SetKillable(killable); } } -/** Commands sent to Director */ -static char hello[] = "Hello %s calling\n"; - - bool BareosSocket::ConsoleAuthenticateWithDirector(JobControlRecord *jcr, const char *identity, s_password &password, @@ -336,7 +334,7 @@ bool BareosSocket::ConsoleAuthenticateWithDirector(JobControlRecord *jcr, BashSpaces(bashed_name); dir->StartTimer(60 * 5); /* 5 minutes */ - dir->fsend(hello, bashed_name); + dir->fsend("Hello %s calling version %s\n", bashed_name, VERSION); if (!AuthenticateOutboundConnection(jcr, "Director", identity, password, tls_resource)) { Dmsg0(100, "Authenticate outbound connection failed\n"); @@ -588,8 +586,9 @@ bool BareosSocket::AuthenticateInboundConnection(JobControlRecord *jcr, } bool BareosSocket::EvaluateCleartextBareosHello(bool &cleartext_hello, - std::string &client_name, - std::string &r_code_str) const + std::string &client_name_out, + std::string &r_code_str_out, + BareosVersionNumber &version_out) const { char buffer[256] {0}; @@ -605,9 +604,12 @@ bool BareosSocket::EvaluateCleartextBareosHello(bool &cleartext_hello, if (cleartext_hello) { std::string name; std::string code; - if (GetNameAndResourceTypeFromHello(received, name, code)) { - client_name = name; - r_code_str = code; + BareosVersionNumber version = BareosVersionNumber::kUndefined; + if (GetNameAndResourceTypeAndVersionFromHello(received, name, code, version)) { + Dmsg3(200, "Identified from cleartext handshake: %s-%s recognized version: %d\n", name.c_str(), code.c_str(), version); + client_name_out = name; + r_code_str_out = code; + version_out = version; success = true; } } else { /* not cleartext hello */ diff --git a/core/src/lib/bsock.h b/core/src/lib/bsock.h index 308aa4a0369..8e6d1be45f2 100644 --- a/core/src/lib/bsock.h +++ b/core/src/lib/bsock.h @@ -43,6 +43,7 @@ #include #include "lib/tls.h" #include "lib/s_password.h" +#include "include/version_numbers.h" struct btimer_t; /* forward reference */ class BareosSocket; @@ -80,6 +81,7 @@ class BareosSocket : public SmartAlloc { bool TlsEstablished() const { return tls_established_; } std::shared_ptr tls_conn; /* Associated tls connection */ std::unique_ptr tls_conn_init; /* during initialization */ + BareosVersionNumber connected_daemon_version_; protected: JobControlRecord *jcr_; /* JobControlRecord or NULL for error msgs */ @@ -187,8 +189,9 @@ class BareosSocket : public SmartAlloc { void SetSourceAddress(dlist *src_addr_list); void ControlBwlimit(int bytes); /* in bsock.c */ bool EvaluateCleartextBareosHello(bool &cleartext, - std::string &client_name, - std::string &r_code_str) const; + std::string &client_name_out, + std::string &r_code_str_out, + BareosVersionNumber &version_out) const; void OutputCipherMessageString(std::function); void GetCipherMessageString(std::string &str) const; bool ReceiveAndEvaluateResponseMessage(uint32_t &id_out, BStringList &args_out); diff --git a/core/src/lib/try_tls_handshake_as_a_server.cc b/core/src/lib/try_tls_handshake_as_a_server.cc index 6429bee60b8..af130645c8a 100644 --- a/core/src/lib/try_tls_handshake_as_a_server.cc +++ b/core/src/lib/try_tls_handshake_as_a_server.cc @@ -36,18 +36,22 @@ static ConnectionHandshakeMode GetHandshakeMode(BareosSocket *bs, bool cleartext_hello; std::string client_name; std::string r_code_str; + BareosVersionNumber version = BareosVersionNumber::kUndefined; if (!bs->EvaluateCleartextBareosHello(cleartext_hello, client_name, - r_code_str)) { + r_code_str, + version)) { Dmsg0(100, "Error occured when trying to peek cleartext hello\n"); return ConnectionHandshakeMode::CloseConnection; } + bs->connected_daemon_version_ = static_cast(version); + if (cleartext_hello) { TlsPolicy tls_policy; if (!config->GetConfiguredTlsPolicyFromCleartextHello(r_code_str, client_name, tls_policy)) { - Dmsg0(100, "Could not read out cleartext configuration\n"); + Dmsg0(200, "Could not read out cleartext configuration\n"); return ConnectionHandshakeMode::CloseConnection; } if (r_code_str == std::string("R_CLIENT")) { @@ -56,10 +60,13 @@ static ConnectionHandshakeMode GetHandshakeMode(BareosSocket *bs, } else { /* kBnetTlsNone or kBnetTlsEnabled */ return ConnectionHandshakeMode::PerformCleartextHandshake; } - } else { /* not R_CLIENT */ + } else if (r_code_str == std::string("R_CONSOLE") && version < BareosVersionNumber::kRelease_18_2) { + return ConnectionHandshakeMode::PerformCleartextHandshake; + } else { if (tls_policy == kBnetTlsNone) { return ConnectionHandshakeMode::PerformCleartextHandshake; } else { + Dmsg1(200, "Connection to %s will be denied due to configuration mismatch\n", client_name.c_str()); return ConnectionHandshakeMode::CloseConnection; } } diff --git a/core/src/lib/util.cc b/core/src/lib/util.cc index f0fdf7918e4..d50077f6cf7 100644 --- a/core/src/lib/util.cc +++ b/core/src/lib/util.cc @@ -187,25 +187,27 @@ struct HelloInformation { std::string hello_string; std::string resource_type_string; uint32_t position_of_name; + int32_t position_of_version; }; +using SizeTypeOfHelloList = std::vector::size_type; + static std::list hello_list { /* this order is important */ - { "Hello Storage calling Start Job", "R_JOB", 5 }, - { "Hello Start Storage Job", "R_JOB", 4 }, - { "Hello Start Job", "R_JOB", 3 }, - { "Hello Director", "R_DIRECTOR", 2 }, - { "Hello Storage", "R_STORAGE", 2 }, - { "Hello Client", "R_CLIENT", 2 }, - { "Hello", "R_CONSOLE", 1 } + { "Hello Storage calling Start Job", "R_JOB", 5, -1 }, + { "Hello Start Storage Job", "R_JOB", 4, -1 }, + { "Hello Start Job", "R_JOB", 3, -1 }, + { "Hello Director", "R_DIRECTOR", 2, -1 }, + { "Hello Storage", "R_STORAGE", 2, -1 }, + { "Hello Client", "R_CLIENT", 2, -1 }, + { "Hello", "R_CONSOLE", 1, 4 } /* "Hello %s calling version %s" */ }; -bool GetNameAndResourceTypeFromHello(const std::string &input, +bool GetNameAndResourceTypeAndVersionFromHello(const std::string &input, std::string &name, - std::string &r_type_str) + std::string &r_type_str, + BareosVersionNumber &bareos_version) { - bool ok = false; - std::list::const_iterator hello = hello_list.cbegin(); bool found = false; @@ -226,14 +228,42 @@ bool GetNameAndResourceTypeFromHello(const std::string &input, return false; } - BStringList args(input, ' '); /* split at blanks */ + BStringList arguments_of_hello_string(input, ' '); /* split at blanks */ - if (args.size() >= hello->position_of_name) { - name = args[hello->position_of_name]; + bool ok = false; + if (arguments_of_hello_string.size() > hello->position_of_name) { + name = arguments_of_hello_string[hello->position_of_name]; std::replace(name.begin(),name.end(), (char)0x1, ' '); r_type_str = hello->resource_type_string; ok = true; + } else { + Dmsg0(100, "Failed to retrieve the name from hello message\n"); } + + if (ok) { + bareos_version = BareosVersionNumber::kUndefined; + if (hello->position_of_version >= 0) { + if (arguments_of_hello_string.size() > static_cast(hello->position_of_version)) { + std::string version_str = arguments_of_hello_string[hello->position_of_version]; + if (!version_str.empty()) { + ok = false; + BStringList splittet_version (version_str, '.'); + if (splittet_version.size() > 1) { + uint32_t v; + try { + v = std::stoul(splittet_version[0]) * 100; + v += std::stoul(splittet_version[1]); + bareos_version = static_cast(v); + ok = true; + } catch (const std::exception &e) { + Dmsg0(100, "Could not read out any version from hello message\n"); + } + } + } + } + } + } + return ok; } diff --git a/core/src/lib/util.h b/core/src/lib/util.h index ab14c6e1127..57bd4f5be65 100644 --- a/core/src/lib/util.h +++ b/core/src/lib/util.h @@ -32,9 +32,10 @@ void BashSpaces(char *str); void BashSpaces(PoolMem &pm); void UnbashSpaces(char *str); void UnbashSpaces(PoolMem &pm); -bool GetNameAndResourceTypeFromHello(const std::string &input, +bool GetNameAndResourceTypeAndVersionFromHello(const std::string &input, std::string &name, - std::string &r_type_str); + std::string &r_type_str, + BareosVersionNumber &version); const char* IndentMultilineString(PoolMem &resultbuffer, const char *multilinestring, const char *separator); char *encode_time(utime_t time, char *buf); bool ConvertTimeoutToTimespec(timespec &timeout, int timeout_in_seconds); diff --git a/core/src/tests/bsock_test_connection_setup.cc b/core/src/tests/bsock_test_connection_setup.cc index 9da9cb4dea0..9a24c0a5f9a 100644 --- a/core/src/tests/bsock_test_connection_setup.cc +++ b/core/src/tests/bsock_test_connection_setup.cc @@ -121,7 +121,7 @@ static PBareosSocket ConnectToDirector() JobControlRecord jcr; memset(&jcr, 0, sizeof(jcr)); BStringList args; - uint32_t response_id; + uint32_t response_id = kMessageIdUnknown; PBareosSocket UA_sock(console::ConnectToDirector(jcr, 0, args, response_id), [](BareosSocket *p) { delete p; }); diff --git a/core/src/tests/lib_tests.cc b/core/src/tests/lib_tests.cc index 0d0bf7bb641..75b11c64a4c 100644 --- a/core/src/tests/lib_tests.cc +++ b/core/src/tests/lib_tests.cc @@ -41,7 +41,7 @@ TEST(BStringList, ConstructorsTest) TEST(BStringList, AppendTest) { BStringList list1; - std::vector list {"T", "est", "123"}; + std::vector list{"T", "est", "123"}; list1.Append(list); EXPECT_EQ(0, list1.front().compare(std::string("T"))); list1.erase(list1.begin()); @@ -161,7 +161,8 @@ enum { static void do_get_name_from_hello_test(const char *client_string_fmt, const char *client_name, - std::string r_type_test) + const std::string &r_type_test, + const BareosVersionNumber &version_test) { char bashed_client_name[20]; sprintf(bashed_client_name, client_name); @@ -172,23 +173,44 @@ static void do_get_name_from_hello_test(const char *client_string_fmt, std::string name; std::string r_type_str; + BareosVersionNumber version = BareosVersionNumber::kUndefined; - bool ok = GetNameAndResourceTypeFromHello(output_text, name, r_type_str); + bool ok = GetNameAndResourceTypeAndVersionFromHello(output_text, name, r_type_str, version); EXPECT_TRUE(ok); EXPECT_STREQ(name.c_str(), client_name); EXPECT_STREQ(r_type_str.c_str(), r_type_test.c_str()); + EXPECT_EQ(version, version_test); } TEST(Util, get_name_from_hello_test) { - do_get_name_from_hello_test("Hello Client %s calling", "Test Client", "R_CLIENT"); - do_get_name_from_hello_test("Hello Storage calling Start Job %s", "Test Client", "R_JOB"); - do_get_name_from_hello_test("Hello %s", "Console Name", "R_CONSOLE"); - do_get_name_from_hello_test("Hello %s", "*UserAgent*", "R_CONSOLE"); - do_get_name_from_hello_test("Hello %s", "*UserAgent*", "R_CONSOLE"); - do_get_name_from_hello_test("Hello %s calling", "Console", "R_CONSOLE"); - do_get_name_from_hello_test("Hello Director %s calling\n", "bareos dir", "R_DIRECTOR"); - do_get_name_from_hello_test("Hello Start Storage Job %s", "Test Job", "R_JOB"); - do_get_name_from_hello_test("Hello Client %s FdProtocolVersion=123 calling\n", "Test Client again", "R_CLIENT"); + // clang-format off + do_get_name_from_hello_test("Hello Client %s calling", + "Test Client", "R_CLIENT", BareosVersionNumber::kUndefined); + do_get_name_from_hello_test("Hello Storage calling Start Job %s", + "Test Client", "R_JOB", BareosVersionNumber::kUndefined); + do_get_name_from_hello_test("Hello %s", + "Console Name", "R_CONSOLE", BareosVersionNumber::kUndefined); + do_get_name_from_hello_test("Hello %s", + "*UserAgent*", "R_CONSOLE", BareosVersionNumber::kUndefined); + do_get_name_from_hello_test("Hello %s", + "*UserAgent*", "R_CONSOLE", BareosVersionNumber::kUndefined); + do_get_name_from_hello_test("Hello %s calling version 18.2.4rc2", + "Console", "R_CONSOLE", BareosVersionNumber::kRelease_18_2); + do_get_name_from_hello_test("Hello Director %s calling\n", + "bareos dir", "R_DIRECTOR", BareosVersionNumber::kUndefined); + do_get_name_from_hello_test("Hello Start Storage Job %s", + "Test Job", "R_JOB", BareosVersionNumber::kUndefined); + do_get_name_from_hello_test("Hello Client %s FdProtocolVersion=123 calling\n", + "Test Client again", "R_CLIENT", BareosVersionNumber::kUndefined); + // clang-format on +} + +TEST(Util, version_number_test) +{ + EXPECT_EQ(BareosVersionNumber::kRelease_18_2, static_cast(1802)); + EXPECT_EQ(BareosVersionNumber::kUndefined, static_cast(1)); + EXPECT_NE(BareosVersionNumber::kRelease_18_2, static_cast(1702)); + EXPECT_GT(BareosVersionNumber::kRelease_18_2, BareosVersionNumber::kUndefined); }