agent tunnel setup code; server certificates
authorVictor Kirhenshtein <victor@netxms.org>
Thu, 2 Mar 2017 22:07:31 +0000 (00:07 +0200)
committerVictor Kirhenshtein <victor@netxms.org>
Thu, 2 Mar 2017 22:07:31 +0000 (00:07 +0200)
15 files changed:
include/nms_agent.h
include/nms_cscp.h
include/nxcpapi.h
src/agent/core/nxagentd.cpp
src/agent/core/nxagentd.h
src/agent/core/tunnel.cpp
src/libnetxms/crypto.cpp
src/libnetxms/msgrecv.cpp
src/server/core/cert.cpp
src/server/core/config.cpp
src/server/core/main.cpp
src/server/core/tunnel.cpp
src/server/include/agent_tunnel.h [new file with mode: 0644]
src/server/include/nms_core.h
src/server/libnxsrv/messages.mc

index 104026b..762c975 100644 (file)
 #define ERR_NO_SESSION_AGENT        ((UINT32)919)
 #define ERR_SERVER_ID_UNSET         ((UINT32)920)
 #define ERR_NO_SUCH_INSTANCE        ((UINT32)921)
+#define ERR_OUT_OF_STATE_REQUEST    ((UINT32)922)
 
 /**
  * Bulk data reconciliation DCI processing status codes
index 8890665..67fd32f 100644 (file)
@@ -1167,6 +1167,7 @@ typedef struct
 #define VID_ENABLE_COMPRESSION      ((UINT32)570)
 #define VID_AGENT_COMPRESSION_MODE  ((UINT32)571)
 #define VID_TRAP_TYPE               ((UINT32)572)
+#define VID_IS_ACTIVE               ((UINT32)573)
 
 // Base variabe for single threshold in message
 #define VID_THRESHOLD_BASE          ((UINT32)0x00800000)
index 6ba15c9..fd3b3b4 100644 (file)
@@ -358,12 +358,13 @@ class LIBNETXMS_EXPORTABLE TlsMessageReceiver : public AbstractMessageReceiver
 private:
    SOCKET m_socket;
    SSL *m_ssl;
+   MUTEX m_mutex;
 
 protected:
    virtual int readBytes(BYTE *buffer, size_t size, UINT32 timeout);
 
 public:
-   TlsMessageReceiver(SOCKET socket, SSL *ssl, size_t initialSize, size_t maxSize);
+   TlsMessageReceiver(SOCKET socket, SSL *ssl, MUTEX mutex, size_t initialSize, size_t maxSize);
    virtual ~TlsMessageReceiver();
 };
 
@@ -562,6 +563,8 @@ UINT32 LIBNETXMS_EXPORTABLE SetupEncryptionContext(NXCPMessage *pMsg,
                                                   RSA *pPrivateKey, int nNXCPVersion);
 void LIBNETXMS_EXPORTABLE PrepareKeyRequestMsg(NXCPMessage *pMsg, RSA *pServerKey, bool useX509Format);
 RSA LIBNETXMS_EXPORTABLE *LoadRSAKeys(const TCHAR *pszKeyFile);
+RSA LIBNETXMS_EXPORTABLE *RSAKeyFromData(const BYTE *data, size_t size, bool withPrivate);
+void LIBNETXMS_EXPORTABLE RSAFree(RSA *key);
 
 #ifdef _WIN32
 BOOL LIBNETXMS_EXPORTABLE SignMessageWithCAPI(BYTE *pMsg, UINT32 dwMsgLen, const CERT_CONTEXT *pCert,
index 9d2ca68..0dc3c1a 100644 (file)
@@ -163,8 +163,8 @@ TCHAR g_szLogParserDirectory[MAX_PATH] = _T("");
 TCHAR g_masterAgent[MAX_PATH] = _T("not_set");
 TCHAR g_szSNMPTrapListenAddress[MAX_PATH] = _T("*");
 UINT16 g_wListenPort = AGENT_LISTEN_PORT;
+TCHAR g_systemName[MAX_OBJECT_NAME] = _T("");
 ObjectArray<ServerInfo> g_serverList(8, 8, true);
-UINT32 g_dwServerCount = 0;
 UINT32 g_execTimeout = 2000;     // External process execution timeout in milliseconds
 UINT32 g_snmpTimeout = 1000;
 UINT16 g_snmpTrapPort = 162;
@@ -300,6 +300,7 @@ static NX_CFG_TEMPLATE m_cfgTemplate[] =
    { _T("StartupDelay"), CT_LONG, 0, 0, 0, 0, &g_dwStartupDelay, NULL },
    { _T("SubAgent"), CT_STRING_LIST, '\n', 0, 0, 0, &m_pszSubagentList, NULL },
    { _T("SyslogListenPort"), CT_WORD, 0, 0, 0, 0, &g_syslogListenPort, NULL },
+   { _T("SystemName"), CT_STRING, 0, 0, MAX_SECRET_LENGTH, 0, g_systemName, NULL },
    { _T("TimeOut"), CT_IGNORE, 0, 0, 0, 0, NULL, NULL },
    { _T("WaitForProcess"), CT_STRING, 0, 0, MAX_PATH, 0, s_processToWaitFor, NULL },
    { _T("ZoneId"), CT_LONG, 0, 0, 0, 0, &g_zoneId, NULL },
@@ -791,6 +792,20 @@ BOOL Initialize()
       nxlog_write(MSG_LOCAL_DB_OPEN_FAILED, NXLOG_ERROR, NULL);
    }
 
+   // Set system name to host name if not set in config
+   if (g_systemName[0] == 0)
+   {
+#ifdef UNICODE
+      char buffer[MAX_OBJECT_NAME];
+      gethostname(buffer, MAX_OBJECT_NAME);
+      MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buffer, -1, g_systemName, MAX_OBJECT_NAME);
+#else
+      gethostname(g_systemName, MAX_OBJECT_NAME);
+#endif
+      g_systemName[MAX_OBJECT_NAME - 1] = 0;
+   }
+   nxlog_debug(2, _T("Using system name \"%s\""), g_systemName);
+
        if (!(g_dwFlags & AF_SUBAGENT_LOADER))
        {
           if (g_dwFlags & AF_ENABLE_SNMP_PROXY)
index c3eb012..56b24d1 100644 (file)
@@ -660,8 +660,8 @@ extern TCHAR g_szDataDirectory[];
 extern TCHAR g_masterAgent[];
 extern TCHAR g_szSNMPTrapListenAddress[];
 extern UINT16 g_wListenPort;
+extern TCHAR g_systemName[];
 extern ObjectArray<ServerInfo> g_serverList;
-extern UINT32 g_dwServerCount;
 extern time_t g_tmAgentStartTime;
 extern TCHAR g_szPlatformSuffix[];
 extern UINT32 g_dwStartupDelay;
index e74505b..f61ebff 100644 (file)
@@ -34,6 +34,7 @@ private:
    SOCKET m_socket;
    SSL_CTX *m_context;
    SSL *m_ssl;
+   MUTEX m_sslLock;
    bool m_connected;
    UINT32 m_requestId;
    THREAD m_recvThread;
@@ -70,6 +71,7 @@ Tunnel::Tunnel(const InetAddress& addr, UINT16 port, const TCHAR *login) : m_add
    m_socket = INVALID_SOCKET;
    m_context = NULL;
    m_ssl = NULL;
+   m_sslLock = MutexCreate();
    m_connected = false;
    m_requestId = 0;
    m_recvThread = INVALID_THREAD_HANDLE;
@@ -88,6 +90,7 @@ Tunnel::~Tunnel()
       SSL_free(m_ssl);
    if (m_context != NULL)
       SSL_CTX_free(m_context);
+   MutexDestroy(m_sslLock);
 }
 
 /**
@@ -116,13 +119,18 @@ THREAD_RESULT THREAD_CALL Tunnel::recvThreadStarter(void *arg)
  */
 void Tunnel::recvThread()
 {
-   TlsMessageReceiver receiver(m_socket, m_ssl, 8192, MAX_AGENT_MSG_SIZE);
+   TlsMessageReceiver receiver(m_socket, m_ssl, m_sslLock, 8192, MAX_AGENT_MSG_SIZE);
    while(m_connected)
    {
       MessageReceiverResult result;
       NXCPMessage *msg = receiver.readMessage(1000, &result);
       if (msg != NULL)
       {
+         if (nxlog_get_debug_level() >= 6)
+         {
+            TCHAR buffer[64];
+            nxlog_debug(6, _T("[TUN-%s] Received message %s"), (const TCHAR *)m_address.toString(), NXCPMessageCodeName(msg->getCode(), buffer));
+         }
          m_queue->put(msg);
       }
       else if (result != MSGRECV_TIMEOUT)
@@ -143,7 +151,9 @@ bool Tunnel::sendMessage(const NXCPMessage *msg)
       return false;
 
    NXCP_MESSAGE *data = msg->createMessage(true);
+   MutexLock(m_sslLock);
    bool success = (SSL_write(m_ssl, data, ntohl(data->size)) == ntohl(data->size));
+   MutexUnlock(m_sslLock);
    free(data);
    return success;
 }
@@ -153,7 +163,7 @@ bool Tunnel::sendMessage(const NXCPMessage *msg)
  */
 bool Tunnel::connectToServer()
 {
-   // Cleanup from previous connection attemp
+   // Cleanup from previous connection attempt
    if (m_socket != INVALID_SOCKET)
       closesocket(m_socket);
    if (m_ssl != NULL)
@@ -190,7 +200,7 @@ bool Tunnel::connectToServer()
    }
 
    m_context = SSL_CTX_new(method);
-   if (method == NULL)
+   if (m_context == NULL)
    {
       nxlog_debug(4, _T("Cannot create context for tunnel %s@%s"), m_login, (const TCHAR *)m_address.toString());
       return false;
@@ -205,15 +215,32 @@ bool Tunnel::connectToServer()
    }
 
    SSL_set_connect_state(m_ssl);
-   SSL_set_bio(m_ssl, BIO_new_socket(m_socket, 0), BIO_new_socket(m_socket, 0));
+   SSL_set_fd(m_ssl, m_socket);
 
-   int rc = SSL_do_handshake(m_ssl);
-   if (rc != 1)
+   while(true)
    {
-      char buffer[128];
-      nxlog_debug(4, _T("TLS handshake failed for tunnel %s@%s (%hs)"),
-               m_login, (const TCHAR *)m_address.toString(), ERR_error_string(SSL_get_error(m_ssl, rc), buffer));
-      return false;
+      int rc = SSL_do_handshake(m_ssl);
+      if (rc != 1)
+      {
+         int sslErr = SSL_get_error(m_ssl, rc);
+         if (sslErr == SSL_ERROR_WANT_READ)
+         {
+            SocketPoller poller;
+            poller.add(m_socket);
+            if (poller.poll(5000) > 0)
+               continue;
+            nxlog_debug(4, _T("TLS handshake failed for tunnel %s@%s (timeout)"), m_login, (const TCHAR *)m_address.toString());
+            return false;
+         }
+         else
+         {
+            char buffer[128];
+            nxlog_debug(4, _T("TLS handshake failed for tunnel %s@%s (%hs)"),
+                     m_login, (const TCHAR *)m_address.toString(), ERR_error_string(sslErr, buffer));
+            return false;
+         }
+      }
+      break;
    }
 
    // Check server vertificate
@@ -236,6 +263,7 @@ bool Tunnel::connectToServer()
    // Setup receiver
    delete m_queue;
    m_queue = new MsgWaitQueue();
+   m_connected = true;
    m_recvThread = ThreadCreateEx(Tunnel::recvThreadStarter, 0, this);
 
    m_requestId = 1;
@@ -244,8 +272,16 @@ bool Tunnel::connectToServer()
    NXCPMessage msg;
    msg.setCode(CMD_SETUP_AGENT_TUNNEL);
    msg.setId(m_requestId++);
-   msg.setField(VID_LOGIN_NAME, m_login);
-   msg.setField(VID_SHARED_SECRET, g_szSharedSecret);
+   msg.setField(VID_AGENT_VERSION, NETXMS_BUILD_TAG);
+   msg.setField(VID_SYS_NAME, g_systemName);
+
+   VirtualSession session(0);
+   TCHAR buffer[MAX_RESULT_LENGTH];
+   if (GetParameterValue(_T("System.PlatformName"), buffer, &session) == ERR_SUCCESS)
+      msg.setField(VID_PLATFORM_NAME, buffer);
+   if (GetParameterValue(_T("System.UName"), buffer, &session) == ERR_SUCCESS)
+      msg.setField(VID_SYS_DESCRIPTION, buffer);
+
    sendMessage(&msg);
 
    NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId());
@@ -265,7 +301,6 @@ bool Tunnel::connectToServer()
       return false;
    }
 
-   m_connected = true;
    return true;
 }
 
@@ -318,6 +353,7 @@ Tunnel *Tunnel::createFromConfig(TCHAR *config)
    if (a == NULL)
       return NULL;
 
+   *a = 0;
    a++;
    int port = AGENT_TUNNEL_PORT;
    TCHAR *p = _tcschr(a, _T(':'));
index 5f40e05..595e6a5 100644 (file)
@@ -129,7 +129,7 @@ static unsigned long CryptoIdCallback()
 /**
  * Create RSA key from binary representation
  */
-RSA *RSAKeyFromData(const BYTE *data, size_t size, bool withPrivate)
+RSA LIBNETXMS_EXPORTABLE *RSAKeyFromData(const BYTE *data, size_t size, bool withPrivate)
 {
    const BYTE *bp = data;
    RSA *key = d2i_RSAPublicKey(NULL, (OPENSSL_CONST BYTE **)&bp, (int)size);
@@ -147,7 +147,7 @@ RSA *RSAKeyFromData(const BYTE *data, size_t size, bool withPrivate)
 /**
  * Destroy RSA key
  */
-void RSAFree(RSA *key)
+void LIBNETXMS_EXPORTABLE RSAFree(RSA *key)
 {
    RSA_free(key);
 }
@@ -159,7 +159,7 @@ void RSAFree(RSA *key)
 /**
  * Create RSA key from binary representation
  */
-RSA *RSAKeyFromData(const BYTE *data, size_t size, bool withPrivate)
+RSA LIBNETXMS_EXPORTABLE *RSAKeyFromData(const BYTE *data, size_t size, bool withPrivate)
 {
    SecKeyCreateWithData(keyData, keyAttr);
        return NULL;
@@ -168,7 +168,7 @@ RSA *RSAKeyFromData(const BYTE *data, size_t size, bool withPrivate)
 /**
  * Destroy RSA key
  */
-void RSAFree(RSA *key)
+void LIBNETXMS_EXPORTABLE RSAFree(RSA *key)
 {
    free(key);
 }
index 7114867..4cdafee 100644 (file)
@@ -202,10 +202,11 @@ int SocketMessageReceiver::readBytes(BYTE *buffer, size_t size, UINT32 timeout)
 /**
  * TLS message receiver constructor
  */
-TlsMessageReceiver::TlsMessageReceiver(SOCKET socket, SSL *ssl, size_t initialSize, size_t maxSize) : AbstractMessageReceiver(initialSize, maxSize)
+TlsMessageReceiver::TlsMessageReceiver(SOCKET socket, SSL *ssl, MUTEX mutex, size_t initialSize, size_t maxSize) : AbstractMessageReceiver(initialSize, maxSize)
 {
    m_socket = socket;
    m_ssl = ssl;
+   m_mutex = mutex;
 }
 
 /**
@@ -220,15 +221,20 @@ TlsMessageReceiver::~TlsMessageReceiver()
  */
 int TlsMessageReceiver::readBytes(BYTE *buffer, size_t size, UINT32 timeout)
 {
+   MutexLock(m_mutex);
    if (SSL_pending(m_ssl) == 0)
    {
+      MutexUnlock(m_mutex);
       SocketPoller sp;
       sp.add(m_socket);
       int rc = sp.poll(timeout);
       if (rc <= 0)
          return rc;
+      MutexLock(m_mutex);
    }
-   return SSL_read(m_ssl, buffer, size);
+   int bytes = SSL_read(m_ssl, buffer, size);
+   MutexUnlock(m_mutex);
+   return bytes;
 }
 
 #endif /* _WITH_ENCRYPTION */
index 04985ff..bf32c5c 100644 (file)
@@ -25,7 +25,7 @@
 
 #ifdef _WITH_ENCRYPTION
 
-// WARNING! this hack works only for d2i_X509(); be carefull when adding new code
+// WARNING! this hack works only for d2i_X509(); be careful when adding new code
 #ifdef OPENSSL_CONST
 # undef OPENSSL_CONST
 #endif
 #endif
 
 /**
- * Static data
+ * Server certificate file information
  */
-static X509_STORE *m_pTrustedCertStore = NULL;
-static MUTEX m_mutexStoreAccess = INVALID_MUTEX_HANDLE;
+TCHAR g_serverCertificatePath[MAX_PATH] = _T("");
+char g_serverCertificatePassword[MAX_PASSWORD] = "";
+
+/**
+ * Server certificate
+ */
+static X509 *s_serverCertificate = NULL;
+static EVP_PKEY *s_serverCertificateKey = NULL;
+
+/**
+ * Trusted CA certificate store
+ */
+static X509_STORE *s_trustedCertificateStore = NULL;
+static Mutex s_certificateStoreLock;
+
+/**
+ * Get CN from certificate
+ */
+bool GetCertificateCN(X509 *cert, 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);
+   if (idx == -1)
+      return false;
+
+   X509_NAME_ENTRY *entry = X509_NAME_get_entry(subject, idx);
+   if (entry == NULL)
+      return false;
+
+   ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry);
+   if (data == NULL)
+      return false;
+
+   unsigned char *utf8CertCN;
+   ASN1_STRING_to_UTF8(&utf8CertCN, data);
+#ifdef UNICODE
+   MultiByteToWideChar(CP_UTF8, 0, (char *)utf8CertCN, -1, buffer, (int)size);
+#else
+   utf8_to_mb((char *)utf8CertCN, -1, buffer, (int)size);
+#endif
+   buffer[size - 1] = 0;
+   OPENSSL_free(utf8CertCN);
+   return true;
+}
 
 /**
  * Create X509 certificate structure from login message
@@ -94,35 +139,12 @@ static BOOL CheckPublicKey(EVP_PKEY *key, const TCHAR *mappingData)
  */
 static BOOL CheckCommonName(X509 *cert, const TCHAR *cn)
 {
-   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);
-   if (idx == -1)
+   TCHAR certCn[256];
+   if (!GetCertificateCN(cert, certCn, 256))
       return FALSE;
 
-   X509_NAME_ENTRY *entry = X509_NAME_get_entry(subject, idx);
-   if (entry == NULL)
-      return FALSE;
-
-   ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry);
-   if (data == NULL)
-      return FALSE;
-
-   unsigned char *utf8CertCN;
-   ASN1_STRING_to_UTF8(&utf8CertCN, data);
-#ifdef UNICODE
-   DbgPrintf(3, _T("Certificate CN=\"%hs\", user CN=\"%s\""), utf8CertCN, cn);
-   char *utf8UserCN = UTF8StringFromWideString(cn);
-   BOOL success = !stricmp((char *)utf8CertCN, utf8UserCN);
-   free(utf8UserCN);
-#else
-   DbgPrintf(3, _T("Certificate CN=\"%s\", user CN=\"%s\""), utf8CertCN, cn);
-   BOOL success = !stricmp((char *)utf8CertCN, cn);
-#endif
-   OPENSSL_free(utf8CertCN);
-   return success;
+   nxlog_debug(3, _T("Certificate CN=\"%s\", user CN=\"%s\""), certCn, cn);
+   return !_tcsicmp(certCn, cn);
 }
 
 /**
@@ -142,12 +164,12 @@ BOOL ValidateUserCertificate(X509 *pCert, const TCHAR *pszLogin, BYTE *pChalleng
 #endif
 
        DbgPrintf(3, _T("Validating certificate \"%s\" for user %s"), certSubject, pszLogin);
-       MutexLock(m_mutexStoreAccess);
+       s_certificateStoreLock.lock();
 
-       if (m_pTrustedCertStore == NULL)
+       if (s_trustedCertificateStore == NULL)
        {
                DbgPrintf(3, _T("Cannot validate user certificate because certificate store is not initialized"));
-               MutexUnlock(m_mutexStoreAccess);
+               s_certificateStoreLock.unlock();
 #ifdef UNICODE
                free(certSubject);
 #endif
@@ -173,12 +195,10 @@ BOOL ValidateUserCertificate(X509 *pCert, const TCHAR *pszLogin, BYTE *pChalleng
        // Validate certificate
        if (bValid)
        {
-               X509_STORE_CTX *pStore;
-
-               pStore = X509_STORE_CTX_new();
+               X509_STORE_CTX *pStore = X509_STORE_CTX_new();
                if (pStore != NULL)
                {
-                       X509_STORE_CTX_init(pStore, m_pTrustedCertStore, pCert, NULL);
+                       X509_STORE_CTX_init(pStore, s_trustedCertificateStore, pCert, NULL);
                        bValid = X509_verify_cert(pStore);
                        X509_STORE_CTX_free(pStore);
                        DbgPrintf(3, _T("Certificate \"%s\" for user %s - validation %s"),
@@ -214,7 +234,7 @@ BOOL ValidateUserCertificate(X509 *pCert, const TCHAR *pszLogin, BYTE *pChalleng
                }
        }
 
-       MutexUnlock(m_mutexStoreAccess);
+       s_certificateStoreLock.unlock();
 
 #ifdef UNICODE
        free(certSubject);
@@ -224,11 +244,40 @@ BOOL ValidateUserCertificate(X509 *pCert, const TCHAR *pszLogin, BYTE *pChalleng
 #undef certSubject
 }
 
+/**
+ * Validate agent certificate
+ */
+bool ValidateAgentCertificate(X509 *cert)
+{
+   X509_STORE *store = X509_STORE_new();
+   if (store == NULL)
+   {
+      nxlog_debug(3, _T("ValidateAgentCertificate: cannot create certificate store"));
+   }
+
+   X509_STORE_add_cert(store, s_serverCertificate);
+   bool valid = false;
+
+   X509_STORE_CTX *ctx = X509_STORE_CTX_new();
+   if (ctx != NULL)
+   {
+      X509_STORE_CTX_init(ctx, store, cert, NULL);
+      valid = (X509_verify_cert(ctx) == 1);
+      X509_STORE_CTX_free(ctx);
+   }
+   else
+   {
+      TCHAR buffer[256];
+      nxlog_debug(3, _T("ValidateAgentCertificate: X509_STORE_CTX_new() failed: %s"), _ERR_error_tstring(ERR_get_error(), buffer));
+   }
+
+   X509_STORE_free(store);
+   return valid;
+}
 
-//
-// Reload certificates from database
-//
-
+/**
+ * Reload certificates from database
+ */
 void ReloadCertificates()
 {
        BYTE *pBinCert;
@@ -239,14 +288,18 @@ void ReloadCertificates()
        X509 *pCert;
        TCHAR szBuffer[256], szSubject[256], *pszCertData;
 
-       MutexLock(m_mutexStoreAccess);
+       s_certificateStoreLock.lock();
 
-       if (m_pTrustedCertStore != NULL)
-               X509_STORE_free(m_pTrustedCertStore);
+       if (s_trustedCertificateStore != NULL)
+               X509_STORE_free(s_trustedCertificateStore);
 
-       m_pTrustedCertStore = X509_STORE_new();
-       if (m_pTrustedCertStore != NULL)
+       s_trustedCertificateStore = X509_STORE_new();
+       if (s_trustedCertificateStore != NULL)
        {
+          // Add server's certificate as trusted
+          if (s_serverCertificate != NULL)
+             X509_STORE_add_cert(s_trustedCertificateStore, s_serverCertificate);
+
                _sntprintf(szBuffer, 256, _T("SELECT cert_data,subject FROM certificates WHERE cert_type=%d"), CERT_TYPE_TRUSTED_CA);
                DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
                hResult = DBSelect(hdb, szBuffer);
@@ -267,7 +320,7 @@ void ReloadCertificates()
                                        free(pBinCert);
                                        if (pCert != NULL)
                                        {
-                                               if (X509_STORE_add_cert(m_pTrustedCertStore, pCert))
+                                               if (X509_STORE_add_cert(s_trustedCertificateStore, pCert))
                                                {
                                                        nLoaded++;
                                                }
@@ -277,6 +330,7 @@ void ReloadCertificates()
                                                                                "ss", DBGetField(hResult, i, 1, szSubject, 256),
                                                                                _ERR_error_tstring(ERR_get_error(), szBuffer));
                                                }
+                                               X509_free(pCert); // X509_STORE_add_cert increments reference count
                                        }
                                        else
                                        {
@@ -298,7 +352,7 @@ void ReloadCertificates()
                nxlog_write(MSG_CANNOT_INIT_CERT_STORE, EVENTLOG_ERROR_TYPE, "s", _ERR_error_tstring(ERR_get_error(), szBuffer));
        }
 
-       MutexUnlock(m_mutexStoreAccess);
+       s_certificateStoreLock.unlock();
 }
 
 /**
@@ -306,8 +360,70 @@ void ReloadCertificates()
  */
 void InitCertificates()
 {
-       m_mutexStoreAccess = MutexCreate();
-       ReloadCertificates();
+   ReloadCertificates();
+}
+
+/**
+ * Load server certificate
+ */
+bool LoadServerCertificate(RSA **serverKey)
+{
+   if (g_serverCertificatePath[0] == 0)
+   {
+      nxlog_write(MSG_SERVER_CERT_NOT_SET, NXLOG_INFO, NULL);
+      return false;
+   }
+
+   FILE *f = _tfopen(g_serverCertificatePath, _T("r"));
+   if (f == NULL)
+   {
+      nxlog_write(MSG_CANNOT_LOAD_SERVER_CERT, NXLOG_ERROR, "ss", g_serverCertificatePath, _tcserror(errno));
+      return false;
+   }
+
+   DecryptPasswordA("system", g_serverCertificatePassword, g_serverCertificatePassword, MAX_PASSWORD);
+   s_serverCertificate = PEM_read_X509(f, NULL, NULL, g_serverCertificatePassword);
+   s_serverCertificateKey = PEM_read_PrivateKey(f, NULL, NULL, g_serverCertificatePassword);
+   fclose(f);
+
+   if ((s_serverCertificate == NULL) || (s_serverCertificateKey == NULL))
+   {
+      TCHAR buffer[1024];
+      nxlog_write(MSG_CANNOT_LOAD_SERVER_CERT, NXLOG_ERROR, "ss", g_serverCertificatePath, _ERR_error_tstring(ERR_get_error(), buffer));
+      return false;
+   }
+
+   RSA *privKey = EVP_PKEY_get1_RSA(s_serverCertificateKey);
+   RSA *pubKey = EVP_PKEY_get1_RSA(X509_get_pubkey(s_serverCertificate));
+   if ((privKey != NULL) && (pubKey != NULL))
+   {
+      // Combine into one key
+      int len = i2d_RSAPublicKey(pubKey, NULL);
+      len += i2d_RSAPrivateKey(privKey, NULL);
+      BYTE *buffer = (BYTE *)malloc(len);
+
+      BYTE *pos = buffer;
+      i2d_RSAPublicKey(pubKey, &pos);
+      i2d_RSAPrivateKey(privKey, &pos);
+
+      *serverKey = RSAKeyFromData(buffer, len, true);
+      free(buffer);
+   }
+
+   return true;
+}
+
+/**
+ * Setup server-side TLS context
+ */
+bool SetupServerTlsContext(SSL_CTX *context)
+{
+   if ((s_serverCertificate == NULL) || (s_serverCertificateKey == NULL))
+      return false;
+
+   SSL_CTX_use_certificate(context, s_serverCertificate);
+   SSL_CTX_use_PrivateKey(context, s_serverCertificateKey);
+   return true;
 }
 
 #else          /* _WITH_ENCRYPTION */
index 049a230..3b2ca17 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ** NetXMS - Network Management System
-** Copyright (C) 2003-2016 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
@@ -30,6 +30,8 @@ extern char g_szCodePage[];
 extern TCHAR *g_moduleLoadList;
 extern TCHAR *g_pdsLoadList;
 extern InetAddressList g_peerNodeAddrList;
+extern TCHAR g_serverCertificatePath[];
+extern char g_serverCertificatePassword[];
 
 /**
  * database connection parameters
@@ -84,6 +86,8 @@ static NX_CFG_TEMPLATE m_cfgTemplate[] =
    { _T("PeerNode"), CT_STRING, 0, 0, MAX_DB_STRING, 0, s_peerNode, NULL },
    { _T("PerfDataStorageDriver"), CT_STRING_LIST, '\n', 0, 0, 0, &g_pdsLoadList, NULL },
    { _T("ProcessAffinityMask"), CT_LONG, 0, 0, 0, 0, &g_processAffinityMask, NULL },
+   { _T("ServerCertificate"), CT_STRING, 0, 0, MAX_PATH, 0, g_serverCertificatePath, NULL },
+   { _T("ServerCertificatePassword"), CT_MB_STRING, 0, 0, MAX_PASSWORD, 0, g_serverCertificatePassword, NULL },
    { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL, NULL }
 };
 
index 5b4fd1c..c31b1d8 100644 (file)
@@ -67,6 +67,7 @@ extern Queue g_dciCacheLoaderQueue;
 void InitClientListeners();
 void InitMobileDeviceListeners();
 void InitCertificates();
+bool LoadServerCertificate(RSA **serverKey);
 void InitUsers();
 void CleanupUsers();
 void LoadPerfDataStorageDrivers();
@@ -377,10 +378,10 @@ static void LoadGlobalConfig()
 /**
  * Initialize cryptografic functions
  */
-static BOOL InitCryptografy()
+static bool InitCryptografy()
 {
 #ifdef _WITH_ENCRYPTION
-       BOOL bResult = FALSE;
+       bool success = false;
 
    if (!InitCryptoLib(ConfigReadULong(_T("AllowedCiphers"), 0x7F)))
                return FALSE;
@@ -389,58 +390,70 @@ static BOOL InitCryptografy()
    SSL_library_init();
    SSL_load_error_strings();
 
-   TCHAR szKeyFile[MAX_PATH];
-       _tcscpy(szKeyFile, g_netxmsdDataDir);
-       _tcscat(szKeyFile, DFILE_KEYS);
-       g_pServerKey = LoadRSAKeys(szKeyFile);
-       if (g_pServerKey == NULL)
-       {
-          nxlog_debug(1, _T("Generating RSA key pair..."));
-               g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
-               if (g_pServerKey != NULL)
-               {
-                       int fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
-                       if (fd != -1)
-                       {
-                               UINT32 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
-                               dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
-                               BYTE *pKeyBuffer = (BYTE *)malloc(dwLen);
-
-                               BYTE *pBufPos = pKeyBuffer;
-                               i2d_RSAPublicKey(g_pServerKey, &pBufPos);
-                               i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
-                               _write(fd, &dwLen, sizeof(UINT32));
-                               _write(fd, pKeyBuffer, dwLen);
-
-                          BYTE hash[SHA1_DIGEST_SIZE];
-                               CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
-                               _write(fd, hash, SHA1_DIGEST_SIZE);
-
-                               _close(fd);
-                               free(pKeyBuffer);
-                               bResult = TRUE;
-                       }
+   if (LoadServerCertificate(&g_pServerKey))
+   {
+      nxlog_debug(1, _T("Server certificate loaded"));
+   }
+   if (g_pServerKey != NULL)
+   {
+      nxlog_debug(1, _T("Using server certificate key"));
+      success = true;
+   }
+   else
+   {
+      TCHAR szKeyFile[MAX_PATH];
+      _tcscpy(szKeyFile, g_netxmsdDataDir);
+      _tcscat(szKeyFile, DFILE_KEYS);
+      g_pServerKey = LoadRSAKeys(szKeyFile);
+      if (g_pServerKey == NULL)
+      {
+         nxlog_debug(1, _T("Generating RSA key pair..."));
+         g_pServerKey = RSA_generate_key(NETXMS_RSA_KEYLEN, 17, NULL, 0);
+         if (g_pServerKey != NULL)
+         {
+            int fd = _topen(szKeyFile, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0600);
+            if (fd != -1)
+            {
+               UINT32 dwLen = i2d_RSAPublicKey(g_pServerKey, NULL);
+               dwLen += i2d_RSAPrivateKey(g_pServerKey, NULL);
+               BYTE *pKeyBuffer = (BYTE *)malloc(dwLen);
+
+               BYTE *pBufPos = pKeyBuffer;
+               i2d_RSAPublicKey(g_pServerKey, &pBufPos);
+               i2d_RSAPrivateKey(g_pServerKey, &pBufPos);
+               _write(fd, &dwLen, sizeof(UINT32));
+               _write(fd, pKeyBuffer, dwLen);
+
+               BYTE hash[SHA1_DIGEST_SIZE];
+               CalculateSHA1Hash(pKeyBuffer, dwLen, hash);
+               _write(fd, hash, SHA1_DIGEST_SIZE);
+
+               _close(fd);
+               free(pKeyBuffer);
+               success = true;
+            }
+            else
+            {
+               nxlog_debug(0, _T("Failed to open %s for writing"), szKeyFile);
+            }
+         }
          else
          {
-            nxlog_debug(0, _T("Failed to open %s for writing"), szKeyFile);
+            nxlog_debug(0, _T("Failed to generate RSA key"));
          }
-               }
+      }
       else
       {
-         nxlog_debug(0, _T("Failed to generate RSA key"));
+         success = true;
       }
-       }
-       else
-       {
-               bResult = TRUE;
-       }
+   }
 
        int iPolicy = ConfigReadInt(_T("DefaultEncryptionPolicy"), 1);
        if ((iPolicy < 0) || (iPolicy > 3))
                iPolicy = 1;
        SetAgentDEP(iPolicy);
 
-       return bResult;
+       return success;
 #else
        return TRUE;
 #endif
index e56aef8..09de68e 100644 (file)
 **/
 
 #include "nxcore.h"
+#include <agent_tunnel.h>
+
+#define MAX_MSG_SIZE    268435456
+
+/**
+ * Next free tunnel ID
+ */
+static VolatileCounter s_nextTunnelId = 0;
+
+/**
+ * Agent tunnel constructor
+ */
+AgentTunnel::AgentTunnel(SSL_CTX *context, SSL *ssl, SOCKET sock, const InetAddress& addr, UINT32 nodeId) : RefCountObject()
+{
+   m_id = InterlockedIncrement(&s_nextTunnelId);
+   m_address = addr;
+   m_socket = sock;
+   m_context = context;
+   m_ssl = ssl;
+   m_sslLock = MutexCreate();
+   m_recvThread = INVALID_THREAD_HANDLE;
+   m_nodeId = nodeId;
+   m_state = AGENT_TUNNEL_INIT;
+   m_systemName = NULL;
+   m_platformName = NULL;
+   m_systemInfo = NULL;
+   m_agentVersion = NULL;
+}
+
+/**
+ * Agent tunnel destructor
+ */
+AgentTunnel::~AgentTunnel()
+{
+   ThreadJoin(m_recvThread);
+   SSL_CTX_free(m_context);
+   SSL_free(m_ssl);
+   MutexDestroy(m_sslLock);
+   closesocket(m_socket);
+   free(m_systemName);
+   free(m_platformName);
+   free(m_systemInfo);
+   free(m_agentVersion);
+   debugPrintf(4, _T("Tunnel destroyed"));
+}
+
+/**
+ * Debug output
+ */
+void AgentTunnel::debugPrintf(int level, const TCHAR *format, ...)
+{
+   va_list args;
+   va_start(args, format);
+   TCHAR buffer[8192];
+   _vsntprintf(buffer, 8192, format, args);
+   va_end(args);
+   nxlog_debug(level, _T("[TUN-%d] %s"), m_id, buffer);
+}
+
+/**
+ * Tunnel receiver thread
+ */
+void AgentTunnel::recvThread()
+{
+   TlsMessageReceiver receiver(m_socket, m_ssl, m_sslLock, 4096, MAX_MSG_SIZE);
+   while(true)
+   {
+      MessageReceiverResult result;
+      NXCPMessage *msg = receiver.readMessage(60000, &result);
+      if (result != MSGRECV_SUCCESS)
+      {
+         if (result == MSGRECV_CLOSED)
+            debugPrintf(4, _T("Tunnel closed by peer"));
+         else
+            debugPrintf(4, _T("Communication error (%s)"), AbstractMessageReceiver::resultToText(result));
+         break;
+      }
+
+      if (nxlog_get_debug_level() >= 6)
+      {
+         TCHAR buffer[64];
+         debugPrintf(6, _T("Received message %s"), NXCPMessageCodeName(msg->getCode(), buffer));
+      }
+
+      switch(msg->getCode())
+      {
+         case CMD_KEEPALIVE:
+            break;
+         case CMD_SETUP_AGENT_TUNNEL:
+            setup(msg);
+            break;
+      }
+   }
+   debugPrintf(5, _T("Receiver thread stopped"));
+}
+
+/**
+ * Tunnel receiver thread starter
+ */
+THREAD_RESULT THREAD_CALL AgentTunnel::recvThreadStarter(void *arg)
+{
+   ((AgentTunnel *)arg)->recvThread();
+   return THREAD_OK;
+}
+
+/**
+ * Send message on tunnel
+ */
+bool AgentTunnel::sendMessage(NXCPMessage *msg)
+{
+   NXCP_MESSAGE *data = msg->createMessage(true);
+   MutexLock(m_sslLock);
+   bool success = (SSL_write(m_ssl, data, ntohl(data->size)) == ntohl(data->size));
+   MutexUnlock(m_sslLock);
+   free(data);
+   return success;
+}
+
+/**
+ * Start tunel
+ */
+void AgentTunnel::start()
+{
+   incRefCount();
+   debugPrintf(4, _T("Tunnel started"));
+   m_recvThread = ThreadCreateEx(AgentTunnel::recvThreadStarter, 0, this);
+}
+
+/**
+ * Process setup request
+ */
+void AgentTunnel::setup(const NXCPMessage *request)
+{
+   NXCPMessage response;
+   response.setCode(CMD_REQUEST_COMPLETED);
+   response.setId(request->getId());
+
+   if (m_state == AGENT_TUNNEL_INIT)
+   {
+      m_systemName = request->getFieldAsString(VID_SYS_NAME);
+      m_systemInfo = request->getFieldAsString(VID_SYS_DESCRIPTION);
+      m_platformName = request->getFieldAsString(VID_PLATFORM_NAME);
+      m_agentVersion = request->getFieldAsString(VID_AGENT_VERSION);
+
+      m_state = (m_nodeId != 0) ? AGENT_TUNNEL_BOUND : AGENT_TUNNEL_UNBOUND;
+      response.setField(VID_RCC, ERR_SUCCESS);
+      response.setField(VID_IS_ACTIVE, m_state == AGENT_TUNNEL_BOUND);
+   }
+   else
+   {
+      response.setField(VID_RCC, ERR_OUT_OF_STATE_REQUEST);
+   }
+
+   sendMessage(&response);
+}
+
+/**
+ * Incoming connection data
+ */
+struct ConnectionRequest
+{
+   SOCKET sock;
+   InetAddress addr;
+};
+
+/**
+ * Setup tunnel
+ */
+static void SetupTunnel(void *arg)
+{
+   ConnectionRequest *request = (ConnectionRequest *)arg;
+
+   SSL_CTX *context = NULL;
+   SSL *ssl = NULL;
+   AgentTunnel *tunnel = NULL;
+   int rc;
+   UINT32 nodeId = 0;
+   X509 *cert = NULL;
+
+   // Setup secure connection
+   const SSL_METHOD *method = SSLv23_method();
+   if (method == NULL)
+   {
+      nxlog_debug(4, _T("SetupTunnel(%s): cannot obtain TLS method"), (const TCHAR *)request->addr.toString());
+      goto failure;
+   }
+
+   context = SSL_CTX_new(method);
+   if (context == NULL)
+   {
+      nxlog_debug(4, _T("SetupTunnel(%s): cannot create TLS context"), (const TCHAR *)request->addr.toString());
+      goto failure;
+   }
+   SSL_CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
+   if (!SetupServerTlsContext(context))
+   {
+      nxlog_debug(4, _T("SetupTunnel(%s): cannot configure TLS context"), (const TCHAR *)request->addr.toString());
+      goto failure;
+   }
+
+   ssl = SSL_new(context);
+   if (ssl == NULL)
+   {
+      nxlog_debug(4, _T("SetupTunnel(%s): cannot create SSL object"), (const TCHAR *)request->addr.toString());
+      goto failure;
+   }
+
+   SSL_set_accept_state(ssl);
+   SSL_set_fd(ssl, request->sock);
+
+retry:
+   rc = SSL_do_handshake(ssl);
+   if (rc != 1)
+   {
+      int sslErr = SSL_get_error(ssl, rc);
+      if (sslErr == SSL_ERROR_WANT_READ)
+      {
+         SocketPoller poller;
+         poller.add(request->sock);
+         if (poller.poll(5000) > 0)
+            goto retry;
+         nxlog_debug(4, _T("SetupTunnel(%s): TLS handshake failed (timeout)"), (const TCHAR *)request->addr.toString());
+      }
+      else
+      {
+         char buffer[128];
+         nxlog_debug(4, _T("SetupTunnel(%s): TLS handshake failed (%hs)"),
+                     (const TCHAR *)request->addr.toString(), ERR_error_string(sslErr, buffer));
+      }
+      goto failure;
+   }
+
+   cert = SSL_get_peer_certificate(ssl);
+   if (cert != NULL)
+   {
+      if (ValidateAgentCertificate(cert))
+      {
+         TCHAR cn[256];
+         if (GetCertificateCN(cert, cn, 256))
+         {
+            uuid guid = uuid::parse(cn);
+            if (!guid.isNull())
+            {
+               Node *node = (Node *)FindObjectByGUID(guid, OBJECT_NODE);
+               if (node != NULL)
+               {
+                  nxlog_debug(4, _T("SetupTunnel(%s): Tunnel attached to node %s [%d]"), (const TCHAR *)request->addr.toString(), node->getName(), node->getId());
+                  nodeId = node->getId();
+               }
+               else
+               {
+                  nxlog_debug(4, _T("SetupTunnel(%s): Node with GUID %s not found"), (const TCHAR *)request->addr.toString(), (const TCHAR *)guid.toString());
+               }
+            }
+            else
+            {
+               nxlog_debug(4, _T("SetupTunnel(%s): Certificate 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());
+         }
+      }
+      else
+      {
+         nxlog_debug(4, _T("SetupTunnel(%s): Agent certificate validation failed"), (const TCHAR *)request->addr.toString());
+      }
+      X509_free(cert);
+   }
+   else
+   {
+      nxlog_debug(4, _T("SetupTunnel(%s): Agent certificate not provided"), (const TCHAR *)request->addr.toString());
+   }
+
+   tunnel = new AgentTunnel(context, ssl, request->sock, request->addr, nodeId);
+   tunnel->start();
+   tunnel->decRefCount();
+
+   delete request;
+   return;
+
+failure:
+   if (ssl != NULL)
+      SSL_free(ssl);
+   if (context != NULL)
+      SSL_CTX_free(context);
+   shutdown(request->sock, SHUT_RDWR);
+   closesocket(request->sock);
+   delete request;
+}
 
 /**
  * Tunnel listener
@@ -205,7 +496,10 @@ THREAD_RESULT THREAD_CALL TunnelListener(void *arg)
          InetAddress addr = InetAddress::createFromSockaddr((struct sockaddr *)clientAddr);
          nxlog_debug(5, _T("TunnelListener: incoming connection from %s"), addr.toString(buffer));
 
-         shutdown(hClientSocket, SHUT_RDWR);
+         ConnectionRequest *request = new ConnectionRequest();
+         request->sock = hClientSocket;
+         request->addr = addr;
+         ThreadPoolExecute(g_mainThreadPool, SetupTunnel, request);
       }
       else if (nRet == -1)
       {
diff --git a/src/server/include/agent_tunnel.h b/src/server/include/agent_tunnel.h
new file mode 100644 (file)
index 0000000..d68a4e4
--- /dev/null
@@ -0,0 +1,80 @@
+/* 
+** NetXMS - Network Management System
+** Server Core
+** 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
+** the Free Software Foundation; either version 2 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, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: nxcore.h
+**
+**/
+
+#ifndef _agent_tunnel_h_
+#define _agent_tunnel_h_
+
+#include <nms_util.h>
+
+/**
+ * Tunnel state
+ */
+enum AgentTunnelState
+{
+   AGENT_TUNNEL_INIT = 0,
+   AGENT_TUNNEL_UNBOUND = 1,
+   AGENT_TUNNEL_BOUND = 2
+};
+
+/**
+ * Agent tunnel
+ */
+class AgentTunnel : public RefCountObject
+{
+protected:
+   INT32 m_id;
+   InetAddress m_address;
+   SOCKET m_socket;
+   SSL_CTX *m_context;
+   SSL *m_ssl;
+   MUTEX m_sslLock;
+   THREAD m_recvThread;
+   UINT32 m_nodeId;
+   AgentTunnelState m_state;
+   TCHAR *m_systemName;
+   TCHAR *m_platformName;
+   TCHAR *m_systemInfo;
+   TCHAR *m_agentVersion;
+   
+   virtual ~AgentTunnel();
+   
+   void debugPrintf(int level, const TCHAR *format, ...);
+   
+   void recvThread();
+   static THREAD_RESULT THREAD_CALL recvThreadStarter(void *arg);
+   
+   void setup(const NXCPMessage *request);
+
+public:
+   AgentTunnel(SSL_CTX *context, SSL *ssl, SOCKET sock, const InetAddress& addr, UINT32 nodeId);
+   
+   void start();
+   bool sendMessage(NXCPMessage *msg);
+};
+
+/**
+ * Setup server side TLS context
+ */
+bool SetupServerTlsContext(SSL_CTX *context);
+
+#endif
index 2817b89..66bc100 100644 (file)
@@ -215,14 +215,18 @@ typedef void * HSNMPSESSION;
 #define INFO_CAT_SYSLOG_MSG      5
 #define INFO_CAT_SNMP_TRAP       6
 #define INFO_CAT_AUDIT_RECORD    7
-//#define INFO_CAT_SITUATION       8
-#define INFO_CAT_LIBRARY_IMAGE   9
+#define INFO_CAT_LIBRARY_IMAGE   8
 
 /**
  * Certificate types
  */
-#define CERT_TYPE_TRUSTED_CA           0
-#define CERT_TYPE_USER                         1
+enum CertificateType
+{
+   CERT_TYPE_TRUSTED_CA = 0,
+   CERT_TYPE_USER = 1,
+   CERT_TYPE_AGENT = 2,
+   CERT_TYPE_SERVER = 3
+};
 
 /**
  * Audit subsystems
@@ -1156,7 +1160,9 @@ X509 *CertificateFromLoginMessage(NXCPMessage *pMsg);
 BOOL ValidateUserCertificate(X509 *pCert, const TCHAR *pszLogin, BYTE *pChallenge,
                                                                          BYTE *pSignature, UINT32 dwSigLen, int nMappingMethod,
                                                                          const TCHAR *pszMappingData);
+bool ValidateAgentCertificate(X509 *cert);
 void ReloadCertificates();
+bool GetCertificateCN(X509 *cert, TCHAR *buffer, size_t size);
 #endif
 
 #ifndef _WIN32
index cee8f42..ae7ce16 100644 (file)
@@ -1052,4 +1052,16 @@ Language=English
 Listening for agent connections on TCP socket %1:%2
 .
 
+MessageId=
+SymbolicName=MSG_SERVER_CERT_NOT_SET
+Language=English
+Server certificate not set
+.
+
+MessageId=
+SymbolicName=MSG_CANNOT_LOAD_SERVER_CERT
+Language=English
+Cannot load server certificate from %1 (%2)
+.
+
 ;#endif