Server's database password can be supplied by external tool
authorVictor Kirhenshtein <victor@netxms.org>
Tue, 25 Jul 2017 16:19:15 +0000 (19:19 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Tue, 25 Jul 2017 16:19:15 +0000 (19:19 +0300)
17 files changed:
ChangeLog
include/netxmsdb.h
include/nms_cscp.h
include/nms_util.h
sql/setup.in
src/java/client/netxms-base/src/main/java/org/netxms/base/NXCPCodes.java
src/libnetxms/tools.cpp
src/server/core/admin.cpp
src/server/core/config.cpp
src/server/core/console.cpp
src/server/core/main.cpp
src/server/include/nxsrvapi.h
src/server/libnxsrv/main.cpp
src/server/libnxsrv/messages.mc
src/server/tools/nxadm/nxadm.cpp
src/server/tools/nxdbmgr/nxdbmgr.cpp
src/server/tools/nxdbmgr/upgrade.cpp

index 41e9a31..837d9c3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,7 @@
 - XEN monitoring subagent
 - Fixed issues with agent tunnels
 - Fixed bug in STP-based topology discovery
+- Server's database password can be supplied by external tool
 - NXSL:
        - New class "InetAddress"
        - New attribute "ipAddressList" for class "Interface"
index 37f8685..259b07d 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   456
+#define DB_FORMAT_VERSION   457
 
 #endif
index a9feefc..de78551 100644 (file)
@@ -395,7 +395,7 @@ typedef struct
 #define CMD_RENAME_SCRIPT                 0x009C
 #define CMD_GET_SESSION_LIST              0x009D
 #define CMD_KILL_SESSION                  0x009E
-//unused: #define CMD_GET_TRAP_LOG                  0x009F
+#define CMD_SET_DB_PASSWORD               0x009F
 #define CMD_TRAP_LOG_RECORDS              0x00A0
 #define CMD_START_SNMP_WALK               0x00A1
 #define CMD_SNMP_WALK_DATA                0x00A2
index 43aacc5..19f24a5 100644 (file)
@@ -2379,6 +2379,8 @@ void LIBNETXMS_EXPORTABLE WriteToTerminalEx(const TCHAR *format, ...)
 #endif
 ;
 
+bool LIBNETXMS_EXPORTABLE ReadPassword(const TCHAR *prompt, TCHAR *buffer, size_t bufferSize);
+
 #ifdef _WIN32
 int LIBNETXMS_EXPORTABLE mkstemp(char *tmpl);
 int LIBNETXMS_EXPORTABLE wmkstemp(WCHAR *tmpl);
index 1cdca5a..d897775 100644 (file)
@@ -58,7 +58,6 @@ INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_rest
 INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_restart,data_type,description) VALUES ('DiscoveryFilter','none','none',1,0,'S','');
 INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_restart,data_type,description) VALUES ('DiscoveryFilterFlags','0','0',1,0,'I','');
 INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_restart,data_type,description) VALUES ('DiscoveryPollingInterval','900','900',1,1,'I','Interval in seconds between passive network discovery polls.');
-INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_restart,data_type,description) VALUES ('EnableAdminInterface','1','1',1,1,'B','');
 INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_restart,data_type,description) VALUES ('EnableAgentRegistration','1','1',1,0,'B','Enable/disable agent self-registration');
 INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_restart,data_type,description) VALUES ('EnableAuditLog','1','1',1,1,'B','Enable/disable audit log.');
 INSERT INTO config (var_name,var_value,default_value,is_visible,need_server_restart,data_type,description) VALUES ('EnableCheckPointSNMP','0','0',1,0,'B','');
index 8b6194e..d908950 100644 (file)
@@ -182,7 +182,7 @@ public class NXCPCodes
        public static final int CMD_RENAME_SCRIPT = 0x009C;
        public static final int CMD_GET_SESSION_LIST = 0x009D;
        public static final int CMD_KILL_SESSION = 0x009E;
-//unused:      public static final int CMD_GET_TRAP_LOG = 0x009F;
+       public static final int CMD_SET_DB_PASSWORD = 0x009F;
        public static final int CMD_TRAP_LOG_RECORDS = 0x00A0;
        public static final int CMD_START_SNMP_WALK = 0x00A1;
        public static final int CMD_SNMP_WALK_DATA = 0x00A2;
index 92d8df6..adcb3bc 100644 (file)
@@ -3028,3 +3028,39 @@ StringList LIBNETXMS_EXPORTABLE *ParseCommandLine(const TCHAR *cmdline)
    free(temp);
    return args;
 }
+
+/**
+ * Read password from terminal
+ */
+bool LIBNETXMS_EXPORTABLE ReadPassword(const TCHAR *prompt, TCHAR *buffer, size_t bufferSize)
+{
+   if (prompt != NULL)
+   {
+      _tprintf(_T("%s"), prompt);
+      fflush(stdout);
+   }
+
+   /* Turn echoing off and fail if we can’t. */
+   struct termios oldSettings;
+   if (tcgetattr(fileno(stdin), &oldSettings) != 0)
+      return false;
+
+   struct termios newSettings;
+   memcpy(&newSettings, &oldSettings, sizeof(struct termios));
+   newSettings.c_lflag &= ~ECHO;
+   if (tcsetattr(fileno(stdin), TCSAFLUSH, &newSettings) != 0)
+      return false;
+
+   /* Read the password. */
+   if (_fgetts(buffer, bufferSize, stdin) != NULL)
+   {
+      TCHAR *nl = _tcschr(buffer, _T('\n'));
+      if (nl != NULL)
+         *nl = 0;
+   }
+
+   /* Restore terminal. */
+   tcsetattr(fileno(stdin), TCSAFLUSH, &oldSettings);
+   _tprintf(_T("\n"));
+   return true;
+}
index bafe703..5635765 100644 (file)
@@ -1,6 +1,6 @@
 /* 
 ** NetXMS - Network Management System
-** Copyright (C) 2003-2013 Victor Kirhenshtein
+** Copyright (C) 2003-2017 Victor Kirhenshtein
 **
 ** 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
 /**
  * Max message size
  */
-#define MAX_MSG_SIZE       32768
+#define MAX_MSG_SIZE       65536
+
+/**
+ * DB password set condition
+ */
+extern Condition g_dbPasswordReady;
 
 /**
  * Request processing thread
  */
 static THREAD_RESULT THREAD_CALL ProcessingThread(void *pArg)
 {
+   static NXCPEncryptionContext *dummyCtx = NULL;
+
    SOCKET sock = CAST_FROM_POINTER(pArg, SOCKET);
-   int iError, nExitCode;
-   NXCP_MESSAGE *pRawMsg, *pRawMsgOut;
-   NXCP_BUFFER *pRecvBuffer;
-   NXCPMessage *pRequest, response;
-   TCHAR szCmd[256];
-   struct __console_ctx ctx;
-       static NXCPEncryptionContext *pDummyCtx = NULL;
 
-   pRawMsg = (NXCP_MESSAGE *)malloc(MAX_MSG_SIZE);
-   pRecvBuffer = (NXCP_BUFFER *)malloc(sizeof(NXCP_BUFFER));
-   NXCPInitBuffer(pRecvBuffer);
+   struct __console_ctx ctx;
    ctx.hSocket = sock;
        ctx.socketMutex = MutexCreate();
-   ctx.pMsg = &response;
+   ctx.pMsg = NULL;
        ctx.session = NULL;
    ctx.output = NULL;
 
-   while(1)
+   SocketMessageReceiver receiver(sock, 4096, MAX_MSG_SIZE);
+   while(true)
    {
-      iError = RecvNXCPMessage(sock, pRawMsg, pRecvBuffer, MAX_MSG_SIZE, &pDummyCtx, NULL, INFINITE);
-      if (iError <= 0)
-         break;   // Communication error or closed connection
+      MessageReceiverResult result;
+      NXCPMessage *request = receiver.readMessage(INFINITE, &result);
 
-      if (iError == 1)
-         continue;   // Too big message
+      // Receive error
+      if (request == NULL)
+      {
+         if (result == MSGRECV_CLOSED)
+            nxlog_debug(5, _T("LocalServerConsole: connection closed"));
+         else
+            nxlog_debug(5, _T("LocalServerConsole: message receiving error (%s)"), AbstractMessageReceiver::resultToText(result));
+         break;
+      }
 
-      pRequest = new NXCPMessage(pRawMsg);
-      pRequest->getFieldAsString(VID_COMMAND, szCmd, 256);
+      NXCPMessage response(CMD_ADM_MESSAGE, request->getId());
+      if (request->getCode() == CMD_ADM_REQUEST)
+      {
+         TCHAR command[256];
+         request->getFieldAsString(VID_COMMAND, command, 256);
 
-      response.setCode(CMD_ADM_MESSAGE);
-      response.setId(pRequest->getId());
-      nExitCode = ProcessConsoleCommand(szCmd, &ctx);
-      switch(nExitCode)
+         ctx.pMsg = &response;
+         int exitCode = ProcessConsoleCommand(command, &ctx);
+         switch(exitCode)
+         {
+            case CMD_EXIT_SHUTDOWN:
+               InitiateShutdown();
+               break;
+            case CMD_EXIT_CLOSE_SESSION:
+               delete request;
+               goto close_session;
+            default:
+               break;
+         }
+      }
+      else if (request->getCode() == CMD_SET_DB_PASSWORD)
       {
-         case CMD_EXIT_SHUTDOWN:
-            InitiateShutdown();
-            break;
-         case CMD_EXIT_CLOSE_SESSION:
-            delete pRequest;
-            goto close_session;
-         default:
-            break;
+         request->getFieldAsString(VID_PASSWORD, g_szDbPassword, MAX_PASSWORD);
+         DecryptPassword(g_szDbLogin, g_szDbPassword, g_szDbPassword, MAX_PASSWORD);
+         g_dbPasswordReady.set();
       }
 
       response.setCode(CMD_REQUEST_COMPLETED);
-      pRawMsgOut = response.createMessage();
-               SendEx(sock, pRawMsgOut, ntohl(pRawMsgOut->size), 0, ctx.socketMutex);
-      
-      free(pRawMsgOut);
-      delete pRequest;
+      NXCP_MESSAGE *rawMsgOut = response.createMessage();
+               SendEx(sock, rawMsgOut, ntohl(rawMsgOut->size), 0, ctx.socketMutex);
+      free(rawMsgOut);
+      delete request;
    }
 
 close_session:
    shutdown(sock, 2);
    closesocket(sock);
-   free(pRawMsg);
-   free(pRecvBuffer);
        MutexDestroy(ctx.socketMutex);
    return THREAD_OK;
 }
index 0637aa8..216d690 100644 (file)
@@ -36,7 +36,7 @@ extern TCHAR g_serverCertificateKeyPath[];
 extern char g_serverCertificatePassword[];
 
 /**
- * database connection parameters
+ * Database connection parameters
  */
 TCHAR g_szDbDriver[MAX_PATH] = _T("");
 TCHAR g_szDbDrvParams[MAX_PATH] = _T("");
@@ -76,6 +76,7 @@ static NX_CFG_TEMPLATE m_cfgTemplate[] =
    { _T("DBServer"), CT_STRING, 0, 0, MAX_PATH, 0, g_szDbServer, NULL },
    { _T("DebugLevel"), CT_LONG, 0, 0, 0, 0, &s_debugLevel, &s_debugLevel },
    { _T("DumpDirectory"), CT_STRING, 0, 0, MAX_PATH, 0, g_szDumpDir, NULL },
+   { _T("EnableServerConsole"), CT_BOOLEAN64, 0, 0, AF_ENABLE_LOCAL_CONSOLE, 0, &g_flags, NULL },
    { _T("FullCrashDumps"), CT_BOOLEAN64, 0, 0, AF_WRITE_FULL_DUMP, 0, &g_flags, NULL },
    { _T("LibraryDirectory"), CT_STRING, 0, 0, MAX_PATH, 0, g_netxmsdLibDir, NULL },
    { _T("ListenAddress"), CT_STRING, 0, 0, MAX_PATH, 0, g_szListenAddress, NULL },
index e7cf7b5..1bb9c07 100644 (file)
@@ -539,6 +539,7 @@ int ProcessConsoleCommand(const TCHAR *pszCmdLine, CONSOLE_CTX pCtx)
          ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_CASE_INSENSITIVE_LOGINS));
          ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_TRAP_SOURCES_IN_ALL_ZONES));
          ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SYSLOG_DISCOVERY));
+         ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_ENABLE_LOCAL_CONSOLE));
          ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SERVER_INITIALIZED));
          ConsolePrintf(pCtx, SHOW_FLAG_VALUE(AF_SHUTDOWN));
          ConsolePrintf(pCtx, _T("\n"));
index d6a1981..f5350de 100644 (file)
@@ -159,6 +159,7 @@ DB_DRIVER g_dbDriver = NULL;
 ThreadPool NXCORE_EXPORTABLE *g_mainThreadPool = NULL;
 INT16 g_defaultAgentCacheMode = AGENT_CACHE_OFF;
 InetAddressList g_peerNodeAddrList;
+Condition g_dbPasswordReady(true);
 
 /**
  * Static data
@@ -605,6 +606,19 @@ static void OracleSessionInitCallback(DB_HANDLE hdb)
 }
 
 /**
+ * Get database password
+ */
+static void GetDatabasePassword()
+{
+   if (_tcscmp(g_szDbPassword, _T("?")))
+      return;
+
+   nxlog_write(MSG_WAITING_FOR_DB_PASSWORD, NXLOG_INFO, NULL);
+   g_dbPasswordReady.wait(INFINITE);
+   nxlog_debug(1, _T("Database password received"));
+}
+
+/**
  * Server initialization
  */
 BOOL NXCORE_EXPORTABLE Initialize()
@@ -690,6 +704,13 @@ BOOL NXCORE_EXPORTABLE Initialize()
        if (g_dbDriver == NULL)
                return FALSE;
 
+   // Start local administrative interface listener if required
+   if (g_flags & AF_ENABLE_LOCAL_CONSOLE)
+      ThreadCreate(LocalAdminListener, 0, NULL);
+
+       // Wait for database password if needed
+       GetDatabasePassword();
+
        // Connect to database
        DB_HANDLE hdbBootstrap = NULL;
        TCHAR errorText[DBDRV_MAX_ERROR_TEXT];
@@ -937,10 +958,6 @@ retry_db_lock:
        // Start database _T("lazy") write thread
        StartDBWriter();
 
-       // Start local administartive interface listener if required
-       if (ConfigReadInt(_T("EnableAdminInterface"), 1))
-               ThreadCreate(LocalAdminListener, 0, NULL);
-
        // Start beacon host poller
        ThreadCreate(BeaconPoller, 0, NULL);
 
index 79b3d93..e9fd137 100644 (file)
 #define AF_CASE_INSENSITIVE_LOGINS             _ULL(0x0000000400000000)
 #define AF_TRAP_SOURCES_IN_ALL_ZONES           _ULL(0x0000000800000000)
 #define AF_SYSLOG_DISCOVERY                    _ULL(0x0000001000000000)
+#define AF_ENABLE_LOCAL_CONSOLE                _ULL(0x0000002000000000)
 #define AF_SERVER_INITIALIZED                  _ULL(0x4000000000000000)
 #define AF_SHUTDOWN                            _ULL(0x8000000000000000)
 
index c7d6160..68d7edf 100644 (file)
@@ -26,7 +26,7 @@
 /**
  * Global variables
  */
-UINT64 LIBNXSRV_EXPORTABLE g_flags = AF_USE_SYSLOG | AF_CATCH_EXCEPTIONS | AF_LOG_SQL_ERRORS;
+UINT64 LIBNXSRV_EXPORTABLE g_flags = AF_USE_SYSLOG | AF_CATCH_EXCEPTIONS | AF_LOG_SQL_ERRORS | AF_ENABLE_LOCAL_CONSOLE;
 
 /**
  * Agent error codes
index ae7ce16..09834c8 100644 (file)
@@ -1064,4 +1064,10 @@ Language=English
 Cannot load server certificate from %1 (%2)
 .
 
+MessageId=
+SymbolicName=MSG_WAITING_FOR_DB_PASSWORD
+Language=English
+Server is waiting for database password to be supplied
+.
+
 ;#endif
index 761fad1..3452019 100644 (file)
@@ -40,9 +40,40 @@ static void Help()
             _T("       nxadm -i\n")
             _T("       nxadm -h\n\n")
             _T("Options:\n")
-            _T("   -c  Execute given command at server debug console and disconnect\n")
-            _T("   -i  Connect to server debug console in interactive mode\n")
-            _T("   -h  Display help and exit\n\n"));
+            _T("   -c <command>   Execute given command at server debug console and disconnect\n")
+            _T("   -i             Connect to server debug console in interactive mode\n")
+            _T("   -h             Display help and exit\n")
+            _T("   -p <password>  Provide database password for server startup\n")
+            _T("   -P             Provide database password for server startup (password read from terminal)\n\n"));
+}
+
+/**
+ * Send database password
+ */
+static bool SendPassword(const TCHAR *password)
+{
+   NXCPMessage msg;
+   msg.setCode(CMD_SET_DB_PASSWORD);
+   msg.setId(g_dwRqId++);
+   msg.setField(VID_PASSWORD, password);
+   SendMsg(&msg);
+
+   bool success = false;
+   NXCPMessage *response = RecvMsg();
+   if (response != NULL)
+   {
+      UINT32 rcc = response->getFieldAsUInt32(VID_RCC);
+      if (rcc != 0)
+         _tprintf(_T("ERROR: server error %d\n"), rcc);
+      delete response;
+      success = (rcc == 0);
+   }
+   else
+   {
+      _tprintf(_T("ERROR: no response from server\n"));
+   }
+
+   return success;
 }
 
 /**
@@ -69,12 +100,13 @@ static bool ExecCommand(const TCHAR *command)
       }
 
       UINT16 code = response->getCode();
-      if(code == CMD_ADM_MESSAGE)
+      if (code == CMD_ADM_MESSAGE)
       {
          TCHAR *text = response->getFieldAsString(VID_MESSAGE);
          if (text != NULL)
          {
             WriteToTerminal(text);
+            free(text);
          }
       }
       delete response;
@@ -163,7 +195,8 @@ int main(int argc, char *argv[])
 {
    int iError, ch;
    BOOL bStart = TRUE, bCmdLineOK = FALSE;
-   TCHAR *command = NULL;
+   TCHAR *command = NULL, *password = NULL;
+   TCHAR passwordBuffer[MAX_PASSWORD];
 
    InitNetXMSProcess(true);
 
@@ -176,7 +209,7 @@ int main(int argc, char *argv[])
    {
       // Parse command line
       opterr = 1;
-      while((ch = getopt(argc, argv, "c:ih")) != -1)
+      while((ch = getopt(argc, argv, "c:ihp:P")) != -1)
       {
          switch(ch)
          {
@@ -197,6 +230,18 @@ int main(int argc, char *argv[])
                command = NULL;
                bCmdLineOK = TRUE;
                break;
+            case 'p':
+#ifdef UNICODE
+               password = WideStringFromMBStringSysLocale(optarg);
+#else
+               password = optarg;
+#endif
+               bCmdLineOK = TRUE;
+               break;
+            case 'P':
+               password = passwordBuffer;
+               bCmdLineOK = TRUE;
+               break;
             case '?':
                bStart = FALSE;
                iError = 1;
@@ -210,16 +255,30 @@ int main(int argc, char *argv[])
       {
          if (Connect())
          {
-            if (command == NULL)
+            if ((command == NULL) && (password == NULL))
             {
                Shell();
+               iError = 0;
             }
-            else
+            else if (command != NULL)
             {
                ExecCommand(command);
+               iError = 0;
+            }
+            else
+            {
+               if (password == passwordBuffer)
+               {
+                  if (!ReadPassword(_T("Database password: "), passwordBuffer, MAX_PASSWORD))
+                  {
+                     _tprintf(_T("Cannot read password from terminal\n"));
+                     iError = 4;
+                     goto stop;
+                  }
+               }
+               iError = SendPassword(password) ? 0 : 3;
             }
             Disconnect();
-            iError = 0;
          }
          else
          {
@@ -237,8 +296,12 @@ int main(int argc, char *argv[])
       Help();
       iError = 1;
    }
+
+stop:
 #ifdef UNICODE
    free(command);
+   if (password != passwordBuffer)
+      free(password);
 #endif
    return iError;
 }
index 228f3cc..50b55f6 100644 (file)
@@ -704,7 +704,15 @@ stop_search:
    }
        delete config;
 
-       // Decrypt password
+       // Read and decrypt password
+       if (!_tcscmp(s_dbPassword, _T("?")))
+   {
+          if (!ReadPassword(_T("Database password: "), s_dbPassword, MAX_PASSWORD))
+          {
+             _tprintf(_T("Cannot read password from terminal\n"));
+             return 3;
+          }
+   }
    DecryptPassword(s_dbLogin, s_dbPassword, s_dbPassword, MAX_PASSWORD);
 
 #ifndef _WIN32
index 5faaf3f..806c012 100644 (file)
@@ -585,6 +585,16 @@ static bool SetSchemaVersion(int version)
 }
 
 /**
+ * Upgrade from V456 to V457
+ */
+static BOOL H_UpgradeFromV456(int currVersion, int newVersion)
+{
+   CHK_EXEC(SQLQuery(_T("DELETE FROM config WHERE var_name='EnableAdminInterface'")));
+   CHK_EXEC(SetSchemaVersion(457));
+   return TRUE;
+}
+
+/**
  * Upgrade from V455 to V456
  */
 static BOOL H_UpgradeFromV455(int currVersion, int newVersion)
@@ -11863,6 +11873,7 @@ static struct
    { 453, 454, H_UpgradeFromV453 },
    { 454, 455, H_UpgradeFromV454 },
    { 455, 456, H_UpgradeFromV455 },
+   { 456, 457, H_UpgradeFromV456 },
    { 0, 0, NULL }
 };