changed agent tunnel binding; implemented tunnel unbind
authorVictor Kirhenshtein <victor@netxms.org>
Tue, 28 Mar 2017 12:45:21 +0000 (15:45 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Tue, 28 Mar 2017 12:45:21 +0000 (15:45 +0300)
include/netxmsdb.h
include/nms_cscp.h
sql/schema.in
src/agent/core/tunnel.cpp
src/server/core/cert.cpp
src/server/core/node.cpp
src/server/core/tunnel.cpp
src/server/include/nms_core.h
src/server/nxapisrv/java/pom.xml
src/server/tools/nxdbmgr/upgrade.cpp

index 67357a3..123991b 100644 (file)
@@ -23,6 +23,6 @@
 #ifndef _netxmsdb_h
 #define _netxmsdb_h
 
-#define DB_FORMAT_VERSION   444
+#define DB_FORMAT_VERSION   445
 
 #endif
index 9c112a5..bb7a5ea 100644 (file)
@@ -1176,6 +1176,7 @@ typedef struct
 #define VID_NUM_URLS                ((UINT32)575)
 #define VID_GRACE_LOGINS            ((UINT32)576)
 #define VID_TUNNEL_GUID             ((UINT32)577)
+#define VID_ORGANIZATION            ((UINT32)578)
 
 // Base variabe for single threshold in message
 #define VID_THRESHOLD_BASE          ((UINT32)0x00800000)
index 30b1d71..0e03773 100644 (file)
@@ -280,6 +280,7 @@ CREATE TABLE nodes
   id integer not null,
   primary_name varchar(255) null,
   primary_ip varchar(48) not null,
+  tunnel_id varchar(36) null,
   node_flags integer not null,
   runtime_flags integer not null,
   snmp_version integer not null,
index dd55e23..5f63037 100644 (file)
@@ -99,7 +99,7 @@ private:
    void processChannelCloseRequest(NXCPMessage *request);
    void createSession(NXCPMessage *request);
 
-   X509_REQ *createCertificateRequest(const char *cn, EVP_PKEY **pkey);
+   X509_REQ *createCertificateRequest(const char *country, const char *org, const char *cn, EVP_PKEY **pkey);
    bool saveCertificate(X509 *cert, EVP_PKEY *key);
    void loadCertificate();
 
@@ -592,7 +592,7 @@ void Tunnel::checkConnection()
 /**
  * Create certificate request
  */
-X509_REQ *Tunnel::createCertificateRequest(const char *cn, EVP_PKEY **pkey)
+X509_REQ *Tunnel::createCertificateRequest(const char *country, const char *org, const char *cn, EVP_PKEY **pkey)
 {
    RSA *key = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, NULL);
    if (key == NULL)
@@ -608,8 +608,10 @@ X509_REQ *Tunnel::createCertificateRequest(const char *cn, EVP_PKEY **pkey)
       X509_NAME *subject = X509_REQ_get_subject_name(req);
       if (subject != NULL)
       {
-         X509_NAME_add_entry_by_txt(subject,"O", MBSTRING_UTF8, (const BYTE *)"netxms.org", -1, -1, 0);
-         X509_NAME_add_entry_by_txt(subject,"CN", MBSTRING_UTF8, (const BYTE *)cn, -1, -1, 0);
+         if (country != NULL)
+            X509_NAME_add_entry_by_txt(subject, "C", MBSTRING_UTF8, (const BYTE *)country, -1, -1, 0);
+         X509_NAME_add_entry_by_txt(subject, "O", MBSTRING_UTF8, (const BYTE *)((org != NULL) ? org : "netxms.org"), -1, -1, 0);
+         X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_UTF8, (const BYTE *)cn, -1, -1, 0);
 
          EVP_PKEY *ekey = EVP_PKEY_new();
          if (ekey != NULL)
@@ -709,8 +711,11 @@ void Tunnel::processBindRequest(NXCPMessage *request)
    uuid guid = request->getFieldAsGUID(VID_GUID);
    char *cn = guid.toString().getUTF8String();
 
+   char *country = request->getFieldAsUtf8String(VID_COUNTRY);
+   char *org = request->getFieldAsUtf8String(VID_ORGANIZATION);
+
    EVP_PKEY *key = NULL;
-   X509_REQ *req = createCertificateRequest(cn, &key);
+   X509_REQ *req = createCertificateRequest(country, org, cn, &key);
    free(cn);
 
    if (req != NULL)
index c5685bd..987e1aa 100644 (file)
@@ -58,9 +58,10 @@ static Mutex s_certificateStoreLock;
 /**
  * Issue certificate signed with server's certificate
  */
-X509 *IssueCertificate(X509_REQ *request, const char *cn, int days)
+X509 *IssueCertificate(X509_REQ *request, const char *ou, const char *cn, int days)
 {
-   nxlog_debug(4, _T("IssueCertificate: new certificate request (CN override: %hs)"), (cn != NULL) ? cn : "<not set>");
+   nxlog_debug(4, _T("IssueCertificate: new certificate request (CN override: %hs, OU override: %hs)"),
+            (cn != NULL) ? cn : "<not set>", (ou != NULL) ? ou : "<not set>");
 
    X509_NAME *requestSubject = X509_REQ_get_subject_name(request);
    if (requestSubject == NULL)
@@ -95,27 +96,26 @@ X509 *IssueCertificate(X509_REQ *request, const char *cn, int days)
    }
 
    X509_NAME *subject;
-   if (cn != NULL)
+   if ((cn != NULL) || (ou != NULL))
    {
       subject = X509_NAME_dup(requestSubject);
       if (subject != NULL)
       {
-         int idx = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
-         if (idx != -1)
+         if (ou != NULL)
          {
-            X509_NAME_ENTRY *entry = X509_NAME_get_entry(subject, idx);
-            if (entry != NULL)
-            {
-               X509_NAME_ENTRY_set_data(entry, MBSTRING_UTF8, (const BYTE *)cn, -1);
-            }
-            else
-            {
-               nxlog_debug(4, _T("IssueCertificate: cannot get CN from certificate subject"));
-            }
+            int idx = X509_NAME_get_index_by_NID(subject, NID_organizationalUnitName, -1);
+            if (idx != -1)
+               X509_NAME_delete_entry(subject, idx);
+            if (!X509_NAME_add_entry_by_txt(subject, "OU", MBSTRING_UTF8, (const BYTE *)ou, -1, -1, 0))
+               nxlog_debug(4, _T("IssueCertificate: X509_NAME_add_entry_by_txt failed for OU=%hs"), ou);
          }
-         else
+         if (cn != NULL)
          {
-            nxlog_debug(4, _T("IssueCertificate: cannot find CN index in certificate subject"));
+            int idx = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
+            if (idx != -1)
+               X509_NAME_delete_entry(subject, idx);
+            if (!X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_UTF8, (const BYTE *)cn, -1, -1, 0))
+               nxlog_debug(4, _T("IssueCertificate: X509_NAME_add_entry_by_txt failed for CN=%hs"), cn);
          }
       }
       else
@@ -205,20 +205,22 @@ X509 *IssueCertificate(X509_REQ *request, const char *cn, int days)
       return NULL;
    }
 
-   nxlog_debug(4, _T("IssueCertificate: new certificate issued successfully"));
+   char subjectName[1024];
+   X509_NAME_oneline(X509_get_subject_name(cert), subjectName, 1024);
+   nxlog_debug(4, _T("IssueCertificate: new certificate with subject \"%hs\" issued successfully"), subjectName);
    return cert;
 }
 
 /**
  * Get CN from certificate
  */
-bool GetCertificateCN(X509 *cert, TCHAR *buffer, size_t size)
+bool GetCertificateSubjectField(X509 *cert, int nid, TCHAR *buffer, size_t size)
 {
    X509_NAME *subject = X509_get_subject_name(cert);
    if (subject == NULL)
       return false;
 
-   int idx = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
+   int idx = X509_NAME_get_index_by_NID(subject, nid, -1);
    if (idx == -1)
       return false;
 
@@ -230,19 +232,55 @@ bool GetCertificateCN(X509 *cert, TCHAR *buffer, size_t size)
    if (data == NULL)
       return false;
 
-   unsigned char *utf8CertCN;
-   ASN1_STRING_to_UTF8(&utf8CertCN, data);
+   unsigned char *text;
+   ASN1_STRING_to_UTF8(&text, data);
 #ifdef UNICODE
-   MultiByteToWideChar(CP_UTF8, 0, (char *)utf8CertCN, -1, buffer, (int)size);
+   MultiByteToWideChar(CP_UTF8, 0, (char *)text, -1, buffer, (int)size);
 #else
-   utf8_to_mb((char *)utf8CertCN, -1, buffer, (int)size);
+   utf8_to_mb((char *)text, -1, buffer, (int)size);
 #endif
    buffer[size - 1] = 0;
-   OPENSSL_free(utf8CertCN);
+   OPENSSL_free(text);
    return true;
 }
 
 /**
+ * Get CN from certificate
+ */
+bool GetCertificateCN(X509 *cert, TCHAR *buffer, size_t size)
+{
+   return GetCertificateSubjectField(cert, NID_commonName, buffer, size);
+}
+
+/**
+ * Get OU from certificate
+ */
+bool GetCertificateOU(X509 *cert, TCHAR *buffer, size_t size)
+{
+   return GetCertificateSubjectField(cert, NID_organizationalUnitName, buffer, size);
+}
+
+/**
+ * Get country name from server certificate
+ */
+bool GetServerCertificateCountry(TCHAR *buffer, size_t size)
+{
+   if (s_serverCertificate == NULL)
+      return false;
+   return GetCertificateSubjectField(s_serverCertificate, NID_countryName, buffer, size);
+}
+
+/**
+ * Get organization name from server certificate
+ */
+bool GetServerCertificateOrganization(TCHAR *buffer, size_t size)
+{
+   if (s_serverCertificate == NULL)
+      return false;
+   return GetCertificateSubjectField(s_serverCertificate, NID_organizationName, buffer, size);
+}
+
+/**
  * Create X509 certificate structure from login message
  */
 X509 *CertificateFromLoginMessage(NXCPMessage *pMsg)
index 8ba0406..79c735b 100644 (file)
@@ -303,7 +303,8 @@ bool Node::loadFromDatabase(DB_HANDLE hdb, UINT32 dwId)
       _T("rack_id,rack_image,rack_position,rack_height,")
       _T("last_agent_comm_time,syslog_msg_count,snmp_trap_count,")
       _T("node_type,node_subtype,ssh_login,ssh_password,ssh_proxy,")
-      _T("port_rows,port_numbering_scheme,agent_comp_mode FROM nodes WHERE id=?"));
+      _T("port_rows,port_numbering_scheme,agent_comp_mode,")
+      _T("tunnel_id FROM nodes WHERE id=?"));
    if (hStmt == NULL)
       return false;
 
@@ -396,6 +397,7 @@ bool Node::loadFromDatabase(DB_HANDLE hdb, UINT32 dwId)
    m_portRowCount = DBGetFieldULong(hResult, 0, 45);
    m_portNumberingScheme = DBGetFieldULong(hResult, 0, 46);
    m_agentCompressionMode = (INT16)DBGetFieldLong(hResult, 0, 47);
+   m_tunnelId = DBGetFieldGUID(hResult, 0, 48);
 
    DBFreeResult(hResult);
    DBFreeStatement(hStmt);
@@ -493,7 +495,7 @@ BOOL Node::saveToDatabase(DB_HANDLE hdb)
          _T("runtime_flags=?,down_since=?,driver_name=?,rack_image=?,rack_position=?,rack_height=?,rack_id=?,boot_time=?,")
          _T("agent_cache_mode=?,snmp_sys_contact=?,snmp_sys_location=?,last_agent_comm_time=?,")
          _T("syslog_msg_count=?,snmp_trap_count=?,node_type=?,node_subtype=?,ssh_login=?,ssh_password=?,")
-         _T("ssh_proxy=?,chassis_id=?,port_rows=?,port_numbering_scheme=?,agent_comp_mode=? WHERE id=?"));
+         _T("ssh_proxy=?,chassis_id=?,port_rows=?,port_numbering_scheme=?,agent_comp_mode=?,tunnel_id=? WHERE id=?"));
    }
    else
    {
@@ -503,8 +505,8 @@ BOOL Node::saveToDatabase(DB_HANDLE hdb)
         _T("proxy_node,snmp_proxy,icmp_proxy,required_polls,use_ifxtable,usm_auth_password,usm_priv_password,usm_methods,")
         _T("snmp_sys_name,bridge_base_addr,runtime_flags,down_since,driver_name,rack_image,rack_position,rack_height,rack_id,boot_time,")
         _T("agent_cache_mode,snmp_sys_contact,snmp_sys_location,last_agent_comm_time,syslog_msg_count,snmp_trap_count,")
-        _T("node_type,node_subtype,ssh_login,ssh_password,ssh_proxy,chassis_id,port_rows,port_numbering_scheme,agent_comp_mode,id) ")
-        _T("VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
+        _T("node_type,node_subtype,ssh_login,ssh_password,ssh_proxy,chassis_id,port_rows,port_numbering_scheme,agent_comp_mode,tunnel_id,id) ")
+        _T("VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
    }
    if (hStmt == NULL)
    {
@@ -572,7 +574,8 @@ BOOL Node::saveToDatabase(DB_HANDLE hdb)
    DBBind(hStmt, 47, DB_SQLTYPE_INTEGER, m_portRowCount);
    DBBind(hStmt, 48, DB_SQLTYPE_INTEGER, m_portNumberingScheme);
    DBBind(hStmt, 49, DB_SQLTYPE_VARCHAR, _itot(m_agentCompressionMode, compressionMode, 10), DB_BIND_STATIC, 1);
-   DBBind(hStmt, 50, DB_SQLTYPE_INTEGER, m_id);
+   DBBind(hStmt, 50, DB_SQLTYPE_VARCHAR, m_tunnelId);
+   DBBind(hStmt, 51, DB_SQLTYPE_INTEGER, m_id);
 
    BOOL bResult = DBExecute(hStmt);
    DBFreeStatement(hStmt);
index 6b35bbc..ac4e78f 100644 (file)
@@ -133,7 +133,7 @@ UINT32 BindAgentTunnel(UINT32 tunnelId, UINT32 nodeId)
  */
 UINT32 UnbindAgentTunnel(UINT32 nodeId)
 {
-   Node *node = FindObjectById(nodeId, OBJECT_NODE);
+   Node *node = (Node *)FindObjectById(nodeId, OBJECT_NODE);
    if (node == NULL)
       return RCC_INVALID_OBJECT_ID;
 
@@ -463,6 +463,12 @@ UINT32 AgentTunnel::bind(UINT32 nodeId)
    m_guid = uuid::generate();
    msg.setField(VID_TUNNEL_GUID, m_guid);
 
+   TCHAR buffer[256];
+   if (GetServerCertificateCountry(buffer, 256))
+      msg.setField(VID_COUNTRY, buffer);
+   if (GetServerCertificateOrganization(buffer, 256))
+      msg.setField(VID_ORGANIZATION, buffer);
+
    m_bindRequestId = msg.getId();
    m_bindGuid = node->getGuid();
    sendMessage(&msg);
@@ -503,11 +509,10 @@ void AgentTunnel::processCertificateRequest(NXCPMessage *request)
          X509_REQ *certRequest = d2i_X509_REQ(NULL, &certRequestData, (long)certRequestLen);
          if (certRequest != NULL)
          {
-            String cnBuilder = m_bindGuid.toString();
-            cnBuilder.append(_T('@'));
-            cnBuilder.append(m_guid.toString());
-            char *cn = cnBuilder.getUTF8String();
-            X509 *cert = IssueCertificate(certRequest, cn, 365);
+            char *ou = m_bindGuid.toString().getUTF8String();
+            char *cn = m_guid.toString().getUTF8String();
+            X509 *cert = IssueCertificate(certRequest, ou, cn, 365);
+            free(ou);
             free(cn);
             if (cert != NULL)
             {
@@ -520,7 +525,7 @@ void AgentTunnel::processCertificateRequest(NXCPMessage *request)
                   OPENSSL_free(buffer);
                   debugPrintf(4, _T("Certificate issued"));
 
-                  Node *node = FindObjectByGUID(m_bindGuid, OBJECT_NODE);
+                  Node *node = (Node *)FindObjectByGUID(m_bindGuid, OBJECT_NODE);
                   if (node != NULL)
                   {
                      node->setTunnelId(m_guid);
@@ -749,21 +754,6 @@ void AgentTunnelCommChannel::putData(const BYTE *data, size_t size)
 }
 
 /**
- * Parse tunnel certificate CN
- */
-static bool ParseTunnelCertificateCN(TCHAR *cn, uuid& nodeGuid, uuid& tunnelGuid)
-{
-   TCHAR *p = _tcschr(cn, _T('@'));
-   if (p == NULL)
-      return false;
-   *p = 0;
-   p++;
-   nodeGuid = uuid::parse(cn);
-   tunnelGuid = uuid::parse(p);
-   return !nodeGuid.isNull() && !tunnelGuid.isNull();
-}
-
-/**
  * Incoming connection data
  */
 struct ConnectionRequest
@@ -848,12 +838,13 @@ retry:
    {
       if (ValidateAgentCertificate(cert))
       {
-         TCHAR cn[256];
-         if (GetCertificateCN(cert, cn, 256))
+         TCHAR ou[256], cn[256];
+         if (GetCertificateOU(cert, ou, 256) && GetCertificateCN(cert, cn, 256))
          {
-            nxlog_debug(4, _T("SetupTunnel(%s): certificate CN: %s"), (const TCHAR *)request->addr.toString(), cn);
-            uuid nodeGuid, tunnelGuid;
-            if (ParseTunnelCertificateCN(cn, nodeGuid, tunnelGuid))
+            nxlog_debug(4, _T("SetupTunnel(%s): certificate OU=%s CN=%s"), (const TCHAR *)request->addr.toString(), ou, cn);
+            uuid nodeGuid = uuid::parse(ou);
+            uuid tunnelGuid = uuid::parse(cn);
+            if (!nodeGuid.isNull() && !tunnelGuid.isNull())
             {
                Node *node = (Node *)FindObjectByGUID(nodeGuid, OBJECT_NODE);
                if (node != NULL)
@@ -877,12 +868,12 @@ retry:
             }
             else
             {
-               nxlog_debug(4, _T("SetupTunnel(%s): Certificate CN is not a valid tunnel ID"), (const TCHAR *)request->addr.toString());
+               nxlog_debug(4, _T("SetupTunnel(%s): Certificate OU or CN is not a valid GUID"), (const TCHAR *)request->addr.toString());
             }
          }
          else
          {
-            nxlog_debug(4, _T("SetupTunnel(%s): Cannot get certificate CN"), (const TCHAR *)request->addr.toString());
+            nxlog_debug(4, _T("SetupTunnel(%s): Cannot get certificate OU and CN"), (const TCHAR *)request->addr.toString());
          }
       }
       else
index 3a85700..5a104e2 100644 (file)
@@ -1164,8 +1164,12 @@ BOOL ValidateUserCertificate(X509 *pCert, const TCHAR *pszLogin, BYTE *pChalleng
                                                                          const TCHAR *pszMappingData);
 bool ValidateAgentCertificate(X509 *cert);
 void ReloadCertificates();
+bool GetCertificateSubjectField(X509 *cert, int nid, TCHAR *buffer, size_t size);
 bool GetCertificateCN(X509 *cert, TCHAR *buffer, size_t size);
-X509 *IssueCertificate(X509_REQ *request, const char *cn, int days);
+bool GetCertificateOU(X509 *cert, TCHAR *buffer, size_t size);
+bool GetServerCertificateCountry(TCHAR *buffer, size_t size);
+bool GetServerCertificateOrganization(TCHAR *buffer, size_t size);
+X509 *IssueCertificate(X509_REQ *request, const char *ou, const char *cn, int days);
 #endif
 
 #ifndef _WIN32
index 46ade03..25f5e87 100644 (file)
@@ -4,7 +4,7 @@
    <groupId>org.netxms</groupId>
    <artifactId>netxms-websvc</artifactId>
    <packaging>war</packaging>
-   <version>2.1-M2</version>
+   <version>2.1-M3</version>
    <name>NetXMS REST API Server</name>
    <url>http://netxms.org</url>
    <properties>
index 73e52e9..ece53e9 100644 (file)
@@ -398,7 +398,7 @@ static bool SetNotNullConstraint(const TCHAR *table, const TCHAR *column)
 /**
  * Resize varchar column
  */
-static BOOL ResizeColumn(const TCHAR *table, const TCHAR *column, int newSize, bool nullable)
+static bool ResizeColumn(const TCHAR *table, const TCHAR *column, int newSize, bool nullable)
 {
        TCHAR query[1024];
 
@@ -422,7 +422,7 @@ static BOOL ResizeColumn(const TCHAR *table, const TCHAR *column, int newSize, b
                        break;
        }
 
-       return (query[0] != 0) ? SQLQuery(query) : TRUE;
+       return (query[0] != 0) ? SQLQuery(query) : true;
 }
 
 /**
@@ -747,6 +747,16 @@ static bool SetSchemaVersion(int version)
 }
 
 /**
+ * Upgrade from V444 to V445
+ */
+static BOOL H_UpgradeFromV444(int currVersion, int newVersion)
+{
+   CHK_EXEC(SQLQuery(_T("ALTER TABLE nodes ADD tunnel_id varchar(36) null")));
+   CHK_EXEC(SetSchemaVersion(445));
+   return TRUE;
+}
+
+/**
  * Upgrade from V443 to V444
  */
 static BOOL H_UpgradeFromV443(int currVersion, int newVersion)
@@ -11603,6 +11613,7 @@ static struct
    { 441, 442, H_UpgradeFromV441 },
    { 442, 443, H_UpgradeFromV442 },
    { 443, 444, H_UpgradeFromV443 },
+   { 444, 445, H_UpgradeFromV444 },
    { 0, 0, NULL }
 };