Pārlūkot izejas kodu

HPCC-11764 Dafilesrv authentication and encryption on transport

Current dafilesrv uses unencrypted sockets which is not secure. This update
adds a new section to the environment.conf file to optionally specify that
dafilesrv and its clients should use SSL, and points to the cert and key
files.
If specified, dafilesrv and its clients
will enable secure sockets (openSSL) and communicate in a secure manner.

Signed-off-by: William Whitehead <william.whitehead@lexisnexis.com>
William Whitehead 11 gadi atpakaļ
vecāks
revīzija
1af66fcd32

+ 2 - 0
common/remote/CMakeLists.txt

@@ -55,6 +55,7 @@ include_directories (
          ./../../system/mp 
          ./../../system/mp 
          ./../../system/include 
          ./../../system/include 
          ./../../system/jlib 
          ./../../system/jlib 
+         ./../../system/security/securesocket
     )
     )
 
 
 ADD_DEFINITIONS( -D_USRDLL -DREMOTE_EXPORTS )
 ADD_DEFINITIONS( -D_USRDLL -DREMOTE_EXPORTS )
@@ -65,5 +66,6 @@ install ( TARGETS remote RUNTIME DESTINATION ${EXEC_DIR} LIBRARY DESTINATION ${L
 target_link_libraries ( remote 
 target_link_libraries ( remote 
     jlib 
     jlib 
     mp
     mp
+    securesocket
     ${URIPARSER_LIBRARIES}
     ${URIPARSER_LIBRARIES}
     )
     )

+ 15 - 2
common/remote/rmtfile.cpp

@@ -33,10 +33,23 @@
 
 
 //#define TEST_DAFILESRV_FOR_UNIX_PATHS     // probably not needed
 //#define TEST_DAFILESRV_FOR_UNIX_PATHS     // probably not needed
 
 
+static class CSecuritySettings
+{
+    bool useSSL;
+    unsigned short daliServixPort;
+public:
+    CSecuritySettings()
+    {
+        querySecuritySettings(&useSSL, &daliServixPort, NULL, NULL);
+    }
+
+    unsigned short queryDaliServixPort() { return daliServixPort; }
+} securitySettings;
+
 
 
 unsigned short getDaliServixPort()
 unsigned short getDaliServixPort()
 {
 {
-    return DAFILESRV_PORT;
+    return securitySettings.queryDaliServixPort();
 }
 }
 
 
 
 
@@ -209,7 +222,7 @@ public:
         SocketEndpoint ep = filename.queryEndpoint();
         SocketEndpoint ep = filename.queryEndpoint();
         bool noport = (ep.port==0);
         bool noport = (ep.port==0);
         setDafsEndpointPort(ep);
         setDafsEndpointPort(ep);
-        if (!filename.isLocal()||(ep.port!=DAFILESRV_PORT)) // assume standard port is running on local machine
+        if (!filename.isLocal()||(ep.port!=DAFILESRV_PORT && ep.port!=SECURE_DAFILESRV_PORT)) // assume standard port is running on local machine
         {
         {
 #ifdef __linux__
 #ifdef __linux__
 #ifndef USE_SAMBA
 #ifndef USE_SAMBA

+ 140 - 9
common/remote/sockfile.cpp

@@ -29,6 +29,7 @@
 #include "jmisc.hpp"
 #include "jmisc.hpp"
 #include "jthread.hpp"
 #include "jthread.hpp"
 
 
+#include "securesocket.hpp"
 #include "sockfile.hpp"
 #include "sockfile.hpp"
 #include "portlist.h"
 #include "portlist.h"
 #include "jsocket.hpp"
 #include "jsocket.hpp"
@@ -173,6 +174,37 @@ typedef int RemoteFileIOHandle;
 static unsigned maxConnectTime = 0;
 static unsigned maxConnectTime = 0;
 static unsigned maxReceiveTime = 0;
 static unsigned maxReceiveTime = 0;
 
 
+//Security and default port attributes
+#define PORT_UNKNOWN -1
+static unsigned short configepPort = PORT_UNKNOWN;
+static struct _ClientCertificate
+{
+    const char * certificate;
+    const char * privateKey;
+} clientCertificate;
+
+static CriticalSection              secureContextCrit;
+static Owned<ISecureSocketContext>  secureContext;
+
+static ISecureSocket *createSecureSocket(ISocket *sock,SecureSocketType type)
+{
+    {
+        CriticalBlock b(secureContextCrit);
+        if (!secureContext)
+        {
+            if (clientCertificate.certificate)
+                secureContext.setown(createSecureSocketContextEx(clientCertificate.certificate,clientCertificate.privateKey, NULL, type));
+            else
+                secureContext.setown(createSecureSocketContext(type));
+        }
+    }
+#ifdef _DEBUG
+    return secureContext->createSecureSocket(sock, SSLogMax);
+#else
+    return secureContext->createSecureSocket(sock);
+#endif
+}
+
 void clientSetRemoteFileTimeouts(unsigned maxconnecttime,unsigned maxreadtime)
 void clientSetRemoteFileTimeouts(unsigned maxconnecttime,unsigned maxreadtime)
 {
 {
     maxConnectTime = maxconnecttime;
     maxConnectTime = maxconnecttime;
@@ -400,7 +432,11 @@ void setDafsEndpointPort(SocketEndpoint &ep)
         }
         }
     }
     }
     if (ep.port==0)
     if (ep.port==0)
-        ep.port = DAFILESRV_PORT;
+    {
+        if (configepPort == (unsigned short)PORT_UNKNOWN)
+            querySecuritySettings(NULL, &configepPort, &clientCertificate.certificate, &clientCertificate.privateKey);
+        ep.port = configepPort;
+    }
 }
 }
 
 
 
 
@@ -948,9 +984,14 @@ class CRemoteBase: public CInterface
     Owned<ISocket>          socket;
     Owned<ISocket>          socket;
     static  SocketEndpoint  lastfailep;
     static  SocketEndpoint  lastfailep;
     static unsigned         lastfailtime;
     static unsigned         lastfailtime;
-
+    bool                    useSSL;
     void connectSocket(SocketEndpoint &ep)
     void connectSocket(SocketEndpoint &ep)
     {
     {
+#ifdef _DEBUG
+        StringBuffer sb;
+        ep.getUrlStr(sb);
+        DBGLOG("Client connecting %sto dafilesrv %s", useSSL?"SECURE ":"", sb.str());
+#endif
         sRFTM tm;
         sRFTM tm;
         // called in CConnectionTable::crit
         // called in CConnectionTable::crit
         unsigned retries = 3;
         unsigned retries = 3;
@@ -968,7 +1009,7 @@ class CRemoteBase: public CInterface
             StringBuffer eps;
             StringBuffer eps;
             if (TF_TRACE_CLIENT_CONN) {
             if (TF_TRACE_CLIENT_CONN) {
                 ep.getUrlStr(eps);
                 ep.getUrlStr(eps);
-                PROGLOG("Connecting to %s",eps.str());
+                PROGLOG("Connecting %sto %s",useSSL?"SECURE ":"",eps.str());
                 //PrintStackReport();
                 //PrintStackReport();
             }
             }
             bool ok = true;
             bool ok = true;
@@ -980,6 +1021,14 @@ class CRemoteBase: public CInterface
                 }
                 }
                 else
                 else
                     socket.setown(ISocket::connect(ep));
                     socket.setown(ISocket::connect(ep));
+                if (useSSL)
+                {
+                    Owned<ISecureSocket> ssock = createSecureSocket(socket.getClear(), ClientSocket);
+                    int status = ssock->secure_connect();
+                    if (status < 0)
+                        throw createDafsException(DAFSERR_connection_failed,"Failure to establish secure connection");
+                    socket.setown(ssock.getLink());
+                }
             } 
             } 
             catch (IJSOCK_Exception *e) {
             catch (IJSOCK_Exception *e) {
                 ok = false;
                 ok = false;
@@ -1026,7 +1075,7 @@ class CRemoteBase: public CInterface
                     sleeptime = remaining/2;
                     sleeptime = remaining/2;
             }
             }
             Sleep(sleeptime);       // prevent multiple retries beating
             Sleep(sleeptime);       // prevent multiple retries beating
-            PROGLOG("Retrying connect");
+            PROGLOG("Retrying %sconnect",useSSL?"SECURE ":"");
         }  
         }  
         if (ConnectionTable)
         if (ConnectionTable)
             ConnectionTable->addLink(ep,socket);
             ConnectionTable->addLink(ep,socket);
@@ -1114,7 +1163,19 @@ protected: friend class CRemoteFileIO;
                 }
                 }
             }
             }
             if (!socket) {
             if (!socket) {
-                connectSocket(tep);
+                if (useSSL) {
+                    try {
+                        connectSocket(tep);//first try secure connect
+                    }
+                    catch(...) {
+                        useSSL = false;
+                        tep.port = DAFILESRV_PORT;
+                        PROGLOG("Secure connect failed, retrying on legacy port");
+                        connectSocket(tep);
+                    }
+                }
+                else
+                    connectSocket(tep);
             }
             }
 
 
         }
         }
@@ -1256,6 +1317,8 @@ public:
         : filename(_filename)
         : filename(_filename)
     {
     {
         ep = _ep;
         ep = _ep;
+        //Determine whether or not client should use SSL
+        querySecuritySettings(&useSSL, &configepPort, &clientCertificate.certificate, &clientCertificate.privateKey);
     }
     }
 
 
 
 
@@ -2960,6 +3023,7 @@ class CRemoteFileServer : public CInterface, implements IRemoteFileServer, imple
     int                 lasthandle;
     int                 lasthandle;
     CriticalSection     sect;
     CriticalSection     sect;
     Owned<ISocket>      acceptsock;
     Owned<ISocket>      acceptsock;
+    Owned<ISocket>      rejectsock;
     Owned<ISocketSelectHandler> selecthandler;
     Owned<ISocketSelectHandler> selecthandler;
     Owned<IThreadPool>  threads;    // for commands
     Owned<IThreadPool>  threads;    // for commands
     bool stopping;
     bool stopping;
@@ -3020,7 +3084,7 @@ class CRemoteFileServer : public CInterface, implements IRemoteFileServer, imple
 
 
 
 
         IMPLEMENT_IINTERFACE;
         IMPLEMENT_IINTERFACE;
-        
+
         CRemoteClientHandler(CRemoteFileServer *_parent,ISocket *_socket,IAuthenticatedUser *_user,atomic_t &_globallasttick)
         CRemoteClientHandler(CRemoteFileServer *_parent,ISocket *_socket,IAuthenticatedUser *_user,atomic_t &_globallasttick)
             : socket(_socket), user(_user), globallasttick(_globallasttick)
             : socket(_socket), user(_user), globallasttick(_globallasttick)
         {
         {
@@ -4149,6 +4213,8 @@ public:
         stopping = true;
         stopping = true;
         if (acceptsock) 
         if (acceptsock) 
             acceptsock->cancel_accept();
             acceptsock->cancel_accept();
+        if (rejectsock)
+            rejectsock->cancel_accept();
         reply.append((unsigned)RFEnoerror);
         reply.append((unsigned)RFEnoerror);
         return false;
         return false;
     }
     }
@@ -4355,7 +4421,7 @@ public:
     }
     }
 
 
 
 
-    void run(SocketEndpoint &listenep)
+    void run(SocketEndpoint &listenep, bool useSSL)
     {
     {
         if (listenep.isNull())
         if (listenep.isNull())
             acceptsock.setown(ISocket::create(listenep.port));
             acceptsock.setown(ISocket::create(listenep.port));
@@ -4365,12 +4431,67 @@ public:
             listenep.getIpText(ips);
             listenep.getIpText(ips);
             acceptsock.setown(ISocket::create_ip(listenep.port,ips.str()));
             acceptsock.setown(ISocket::create_ip(listenep.port,ips.str()));
         }
         }
+        if (useSSL) {
+            querySecuritySettings(&useSSL, &configepPort, &clientCertificate.certificate, &clientCertificate.privateKey);
+            if (!clientCertificate.certificate)
+                throw createDafsException(DAFSERR_connection_failed,"SSL Certificate information not found in environment.conf");
+            if (listenep.port <= 0)
+                listenep.port = configepPort;
+
+            //Create unsecure socket to reject non-ssl client requests
+            StringBuffer ip;
+            IpAddress ipAddr;
+            acceptsock->getPeerAddress(ipAddr);
+            ipAddr.getIpText(ip);
+            rejectsock.setown(ISocket::create_ip(DAFILESRV_PORT, ip.str()));
+        }
+#ifdef _DEBUG
+        StringBuffer sb;
+        listenep.getUrlStr(sb);
+        DBGLOG("Server accepting %sfrom %s", useSSL?"SECURE ":"", sb.str());
+#endif
         selecthandler->start();
         selecthandler->start();
+
+        UnsignedArray readSocks;
+        if (useSSL) {
+            readSocks.append(acceptsock->OShandle());
+            readSocks.append(rejectsock->OShandle());
+        }
+
         loop {
         loop {
             Owned<ISocket> sock;
             Owned<ISocket> sock;
-            bool sockavail;
+            bool sockavail = false;
             try {
             try {
-                sockavail = acceptsock->wait_read(1000*60*1)!=0;
+                if (!useSSL)
+                    sockavail = acceptsock->wait_read(1000*60*1)!=0;
+                else
+                {
+                    UnsignedArray waitingSocks;
+                    //SSL Enabled. Listen for non SSL connection on DAFILESRV_PORT and reject them
+                    int numReady = wait_read_multiple(readSocks, 1000*60*1, waitingSocks);
+                    if (numReady)
+                    {
+                        for (int idx = 0; idx < numReady; idx++)
+                        {
+                            if (waitingSocks.item(idx) == rejectsock->OShandle())
+                            {
+                                //Unsecure connection attemped, reject !
+                                Owned<ISocket> s;
+                                s.setown(rejectsock->accept(true));
+                                IpAddress ip;
+                                StringBuffer sb;
+                                s->getPeerAddress(ip);
+                                ip.getIpText(sb);
+                                DBGLOG("Rejecting nonsecure connect from %s",sb.str());
+                                s->close();
+                            }
+                            else
+                            {
+                                sockavail = true;
+                            }
+                        }
+                    }
+                }
 #if 0
 #if 0
                 if (!sockavail) {
                 if (!sockavail) {
                     JSocketStatistics stats;
                     JSocketStatistics stats;
@@ -4392,6 +4513,14 @@ public:
             if (sockavail) {
             if (sockavail) {
                 try {
                 try {
                     sock.setown(acceptsock->accept(true));
                     sock.setown(acceptsock->accept(true));
+                    if (useSSL)
+                    {
+                        Owned<ISecureSocket> ssock = createSecureSocket(sock.getClear(), ServerSocket);
+                        int status = ssock->secure_accept();
+                        if (status < 0)
+                            throw createDafsException(DAFSERR_connection_failed,"Failure to establish secure connection");
+                        sock.setown(ssock.getLink());
+                    }
                     if (!sock||stopping)
                     if (!sock||stopping)
                         break;
                         break;
                 }
                 }
@@ -4524,6 +4653,8 @@ public:
             PROGLOG("CRemoteFileServer::stop");
             PROGLOG("CRemoteFileServer::stop");
         if (acceptsock) 
         if (acceptsock) 
             acceptsock->cancel_accept();
             acceptsock->cancel_accept();
+        if (rejectsock)
+            rejectsock->cancel_accept();
         threads->stopAll();
         threads->stopAll();
         threads->joinAll(true,60*1000);
         threads->joinAll(true,60*1000);
     }
     }

+ 1 - 1
common/remote/sockfile.hpp

@@ -32,7 +32,7 @@
 interface IRemoteFileServer : public IInterface
 interface IRemoteFileServer : public IInterface
 {
 {
 public:
 public:
-    virtual void run(SocketEndpoint &listenep) = 0;
+    virtual void run(SocketEndpoint &listenep, bool useSSL = false) = 0;
     virtual void stop() = 0;
     virtual void stop() = 0;
     virtual unsigned idleTime() = 0; // in ms
     virtual unsigned idleTime() = 0; // in ms
 };
 };

+ 1 - 1
dali/base/dafdesc.cpp

@@ -2900,7 +2900,7 @@ IFileDescriptor *createFileDescriptorFromRoxieXML(IPropertyTree *tree,const char
                 ForEachItemIn(d,locdirs) {
                 ForEachItemIn(d,locdirs) {
                     if (strcmp(rfn.getLocalPath(locpath.clear()).str(),locdirs.item(d))==0) {
                     if (strcmp(rfn.getLocalPath(locpath.clear()).str(),locdirs.item(d))==0) {
                         SocketEndpoint ep = rfn.queryEndpoint();
                         SocketEndpoint ep = rfn.queryEndpoint();
-                        if (ep.port==DAFILESRV_PORT)
+                        if (ep.port==DAFILESRV_PORT || ep.port==SECURE_DAFILESRV_PORT)
                             ep.port = 0;
                             ep.port = 0;
                         epa[d].append(ep);
                         epa[d].append(ep);
                         found = true;
                         found = true;

+ 34 - 9
dali/dafilesrv/dafilesrv.cpp

@@ -36,7 +36,7 @@
 void usage()
 void usage()
 {
 {
     printf("dafilesrv usage:\n");
     printf("dafilesrv usage:\n");
-    printf("    dafilesrv -T<n> <port> [<send-buff-size-kb> <recv-buff-size-kb>]\n");
+    printf("    dafilesrv -T<n> <port> <-NOSSL> [<send-buff-size-kb> <recv-buff-size-kb>]\n");
     printf("                                                  -- run test local\n");
     printf("                                                  -- run test local\n");
     printf("    dafilesrv -D [ -L <log-dir> ] [ -LOCAL ]      -- run as linux daemon\n");
     printf("    dafilesrv -D [ -L <log-dir> ] [ -LOCAL ]      -- run as linux daemon\n");
     printf("    dafilesrv -R                                  -- run remote (linux daemon, windows standalone)\n");
     printf("    dafilesrv -R                                  -- run remote (linux daemon, windows standalone)\n");
@@ -45,7 +45,9 @@ void usage()
     
     
     printf("add -A to enable authentication to the above \n\n");
     printf("add -A to enable authentication to the above \n\n");
     printf("add -I <instance name>  to specify an instance name\n\n");
     printf("add -I <instance name>  to specify an instance name\n\n");
+    printf("add -NOSSL to disable SSL sockets, even when specified in configuration\n\n");
     printf("Standard port is %d\n",DAFILESRV_PORT);
     printf("Standard port is %d\n",DAFILESRV_PORT);
+    printf("Standard SSL port is %d (certificate specs required in environment.conf)\n",SECURE_DAFILESRV_PORT);
     printf("Version:  %s\n\n",remoteServerVersionString());
     printf("Version:  %s\n\n",remoteServerVersionString());
 }
 }
 
 
@@ -345,6 +347,13 @@ int main(int argc,char **argv)
     bool requireauthenticate = false;
     bool requireauthenticate = false;
     StringBuffer logDir;
     StringBuffer logDir;
     StringBuffer instanceName;
     StringBuffer instanceName;
+
+    //Get SSL Settings
+    const char *    sslCertFile;
+    bool            useSSL;
+    unsigned short  dafsPort;//DAFILESRV_PORT or SECURE_DAFILESRV_PORT
+    querySecuritySettings(&useSSL, &dafsPort, &sslCertFile, NULL);
+
     while (argc>i) {
     while (argc>i) {
         if (stricmp(argv[i],"-D")==0) {
         if (stricmp(argv[i],"-D")==0) {
             i++;
             i++;
@@ -380,10 +389,25 @@ int main(int argc,char **argv)
             i++;
             i++;
             locallisten = true;
             locallisten = true;
         }
         }
+        else if (stricmp(argv[i],"-NOSSL")==0) {//overrides config setting
+            i++;
+            if (useSSL)
+            {
+                PROGLOG("DaFileSrv SSL specified in config but overridden by -NOSSL in command line");
+                useSSL = false;
+                dafsPort = DAFILESRV_PORT;
+            }
+        }
         else
         else
             break;
             break;
     }
     }
 
 
+    if (useSSL && !sslCertFile)
+    {
+        ERRLOG("DaFileSrv SSL specified but certificate file information missing from environment.conf");
+        exit(-1);
+    }
+
     if (0 == logDir.length())
     if (0 == logDir.length())
     {
     {
         getConfigurationDirectory(NULL,"log","dafilesrv",instanceName.str(),logDir);
         getConfigurationDirectory(NULL,"log","dafilesrv",instanceName.str(),logDir);
@@ -413,10 +437,10 @@ int main(int argc,char **argv)
     }
     }
 #endif
 #endif
     if (argc == i)
     if (argc == i)
-      listenep.port = DAFILESRV_PORT;
+        listenep.port = dafsPort;
     else {
     else {
         if (strchr(argv[i],'.')||!isdigit(argv[i][0]))
         if (strchr(argv[i],'.')||!isdigit(argv[i][0]))
-            listenep.set(argv[i],DAFILESRV_PORT);
+            listenep.set(argv[i], dafsPort);
         else
         else
             listenep.port = atoi(argv[i]);
             listenep.port = atoi(argv[i]);
         if (listenep.port==0) {
         if (listenep.port==0) {
@@ -433,6 +457,7 @@ int main(int argc,char **argv)
             bool stopped;
             bool stopped;
             bool started;
             bool started;
             SocketEndpoint listenep;
             SocketEndpoint listenep;
+            bool useSSL;
             bool requireauthenticate;
             bool requireauthenticate;
 
 
             
             
@@ -456,8 +481,8 @@ int main(int argc,char **argv)
 
 
         public:
         public:
 
 
-            cserv(SocketEndpoint _listenep) 
-                : listenep(_listenep),pollthread(this)
+            cserv(SocketEndpoint _listenep, bool _useSSL)
+                : listenep(_listenep),useSSL(_useSSL),pollthread(this)
             {
             {
                 stopped = false;
                 stopped = false;
                 started = false;
                 started = false;
@@ -518,7 +543,7 @@ int main(int argc,char **argv)
                 else
                 else
                     listenep.getUrlStr(eps);
                     listenep.getUrlStr(eps);
                 enableDafsAuthentication(requireauthenticate!=0);
                 enableDafsAuthentication(requireauthenticate!=0);
-                PROGLOG("Opening "DAFS_SERVICE_DISPLAY_NAME" on %s", eps.str());
+                PROGLOG("Opening "DAFS_SERVICE_DISPLAY_NAME" on %s%s", useSSL?"SECURE ":"",eps.str());
                 const char * verstring = remoteServerVersionString();
                 const char * verstring = remoteServerVersionString();
                 PROGLOG("Version: %s", verstring);
                 PROGLOG("Version: %s", verstring);
                 PROGLOG("Authentication:%s required",requireauthenticate?"":" not");
                 PROGLOG("Authentication:%s required",requireauthenticate?"":" not");
@@ -534,7 +559,7 @@ int main(int argc,char **argv)
                 PROGLOG(DAFS_SERVICE_DISPLAY_NAME " Stopped");
                 PROGLOG(DAFS_SERVICE_DISPLAY_NAME " Stopped");
                 stopped = true;
                 stopped = true;
             }
             }
-        } service(listenep);
+        } service(listenep, useSSL);
         service.start();
         service.start();
         return 0;
         return 0;
 #else
 #else
@@ -556,14 +581,14 @@ int main(int argc,char **argv)
     else
     else
         listenep.getUrlStr(eps);
         listenep.getUrlStr(eps);
     enableDafsAuthentication(requireauthenticate);
     enableDafsAuthentication(requireauthenticate);
-    PROGLOG("Opening Dali File Server on %s", eps.str());
+    PROGLOG("Opening Dali File Server on %s%s", useSSL?"SECURE ":"",eps.str());
     PROGLOG("Version: %s", verstring);
     PROGLOG("Version: %s", verstring);
     PROGLOG("Authentication:%s required",requireauthenticate?"":" not");
     PROGLOG("Authentication:%s required",requireauthenticate?"":" not");
     startPerformanceMonitor(10*60*1000, PerfMonStandard);
     startPerformanceMonitor(10*60*1000, PerfMonStandard);
     server.setown(createRemoteFileServer());
     server.setown(createRemoteFileServer());
     writeSentinelFile(sentinelFile);
     writeSentinelFile(sentinelFile);
     try {
     try {
-        server->run(listenep);
+        server->run(listenep, useSSL);
     }
     }
     catch (IException *e) {
     catch (IException *e) {
         EXCLOG(e,"DAFILESRV");
         EXCLOG(e,"DAFILESRV");

+ 18 - 4
dali/dfuplus/dfuplus.cpp

@@ -32,6 +32,20 @@
 #include "rmtfile.hpp"
 #include "rmtfile.hpp"
 #include "sockfile.hpp"
 #include "sockfile.hpp"
 
 
+
+static class CSecuritySettings
+{
+    bool useSSL;
+    unsigned short daliServixPort;
+public:
+    CSecuritySettings()
+    {
+        querySecuritySettings(&useSSL, &daliServixPort, NULL, NULL);
+    }
+
+    unsigned short queryDaliServixPort() { return daliServixPort; }
+} securitySettings;
+
 class CDafsThread: public Thread
 class CDafsThread: public Thread
 {
 {
     Owned<IRemoteFileServer> server;
     Owned<IRemoteFileServer> server;
@@ -43,7 +57,7 @@ public:
         : listenep(_listenep)
         : listenep(_listenep)
     {
     {
         if (listenep.port==0)
         if (listenep.port==0)
-            listenep.port = DAFILESRV_PORT;
+            listenep.port = securitySettings.queryDaliServixPort();
         StringBuffer eps;
         StringBuffer eps;
         if (listenep.isNull())
         if (listenep.isNull())
             eps.append(listenep.port);
             eps.append(listenep.port);
@@ -96,7 +110,7 @@ bool CDfuPlusHelper::runLocalDaFileSvr(SocketEndpoint &listenep,bool requireauth
     thr->start();
     thr->start();
     StringBuffer eps;
     StringBuffer eps;
     if (listenep.isNull())
     if (listenep.isNull())
-        progress("Started local Dali file server on port %d\n", listenep.port?listenep.port:DAFILESRV_PORT);
+        progress("Started local Dali file server on port %d\n", listenep.port?listenep.port:securitySettings.queryDaliServixPort());
     else
     else
         progress("Started local Dali file server on %s\n", listenep.getUrlStr(eps).str());
         progress("Started local Dali file server on %s\n", listenep.getUrlStr(eps).str());
     if (timeout==0) {
     if (timeout==0) {
@@ -118,9 +132,9 @@ bool CDfuPlusHelper::runLocalDaFileSvr(SocketEndpoint &listenep,bool requireauth
 bool CDfuPlusHelper::checkLocalDaFileSvr(const char *eps,SocketEndpoint &epout)
 bool CDfuPlusHelper::checkLocalDaFileSvr(const char *eps,SocketEndpoint &epout)
 {
 {
     if (!eps||!*eps)
     if (!eps||!*eps)
-        epout.setLocalHost(DAFILESRV_PORT);
+        epout.setLocalHost(securitySettings.queryDaliServixPort());
     else {
     else {
-        epout.set(eps,DAFILESRV_PORT);
+        epout.set(eps,securitySettings.queryDaliServixPort());
         if (!epout.isLocal())
         if (!epout.isLocal())
             return false;
             return false;
     }
     }

+ 5 - 0
initfiles/etc/DIR_NAME/environment.conf.in

@@ -21,3 +21,8 @@ interface=*
 use_epoll=true
 use_epoll=true
 # allow kernel pagecache flushing where enabled (true/false)
 # allow kernel pagecache flushing where enabled (true/false)
 allow_pgcache_flush=true
 allow_pgcache_flush=true
+
+#enable SSL for dafilesrv remote file access
+#dfsUseSSL=false
+#dfsSSLCertFile=/certfilepath/certfile
+#dfsSSLPrivateKeyFile=/keyfilepath/keyfile

+ 2 - 0
system/include/portlist.h

@@ -57,6 +57,8 @@
 #define MP_START_PORT                   7101 // Default range for MP ports
 #define MP_START_PORT                   7101 // Default range for MP ports
 #define MP_END_PORT                     7500
 #define MP_END_PORT                     7500
 
 
+#define SECURE_DAFILESRV_PORT           7600 // aka daliservix
+
 //ESP SERVICES
 //ESP SERVICES
 //INSECURE
 //INSECURE
 #define WS_ECL_DEFAULT_PORT             8002
 #define WS_ECL_DEFAULT_PORT             8002

+ 1 - 1
system/jlib/jfile.cpp

@@ -4411,7 +4411,7 @@ StringBuffer & RemoteFilename::getRemotePath(StringBuffer & out) const
 
 
 bool RemoteFilename::isLocal() const
 bool RemoteFilename::isLocal() const
 {
 {
-    if (ep.port&&(ep.port!=DAFILESRV_PORT))
+    if (ep.port&&(ep.port!=DAFILESRV_PORT && ep.port!=SECURE_DAFILESRV_PORT))
         return false;  // treat non-dafilesrv port as remote
         return false;  // treat non-dafilesrv port as remote
     return ep.isLocal() || ep.isNull();
     return ep.isLocal() || ep.isNull();
 }
 }

+ 94 - 1
system/jlib/jsocket.cpp

@@ -372,7 +372,7 @@ protected:
     SOCKETMODE      sockmode;
     SOCKETMODE      sockmode;
     IpAddress       targetip;
     IpAddress       targetip;
     SocketEndpoint  returnep;   // set by set_return_addr
     SocketEndpoint  returnep;   // set by set_return_addr
-    
+
     MCASTREQ    *   mcastreq;
     MCASTREQ    *   mcastreq;
     size32_t        nextblocksize;
     size32_t        nextblocksize;
     unsigned        blockflags;
     unsigned        blockflags;
@@ -6066,3 +6066,96 @@ ISocketConnectWait *nonBlockingConnect(SocketEndpoint &ep,unsigned connecttimeou
 {
 {
     return new CSocketConnectWait(ep,connecttimeoutms);
     return new CSocketConnectWait(ep,connecttimeoutms);
 }
 }
+
+
+
+int wait_multiple(bool isRead,               //IN   true if wait read, false it wait write
+                  UnsignedArray &socks,      //IN   sockets to be checked for readiness
+                  unsigned timeoutMS,        //IN   timeout
+                  UnsignedArray &readySocks) //OUT  sockets ready
+{
+    aindex_t numSocks = socks.length();
+    if (numSocks == 0)
+        THROWJSOCKEXCEPTION2(JSOCKERR_bad_address);
+
+    SOCKET maxSocket = 0;
+    T_FD_SET fds;
+    XFD_ZERO(&fds);
+
+    //Add each SOCKET in array to T_FD_SET
+#ifdef _DEBUG
+    StringBuffer dbgSB("wait_multiple() on sockets :");
+#endif
+    for (aindex_t idx = 0; idx < numSocks; idx++)
+    {
+#ifdef _DEBUG
+        dbgSB.appendf(" %d",socks.item(idx));
+#endif
+        maxSocket = socks.item(idx) > maxSocket ? socks.item(idx) : maxSocket;
+        FD_SET((unsigned)socks.item(idx), &fds);
+    }
+#ifdef _DEBUG
+    DBGLOG("%s",dbgSB.str());
+#endif
+
+    //Check socket states
+    int res;
+    if (timeoutMS == WAIT_FOREVER)
+        res = ::select( maxSocket + 1, isRead ? (fd_set *)&fds : NULL, isRead ? NULL : (fd_set *)&fds, NULL, NULL );
+    else
+    {
+        struct timeval tv;
+        tv.tv_sec = timeoutMS / 1000;
+        tv.tv_usec = (timeoutMS % 1000)*1000;
+        res = ::select( maxSocket + 1,  isRead ? (fd_set *)&fds : NULL, isRead ? NULL : (fd_set *)&fds, NULL, &tv );
+    }
+
+    if (res != SOCKET_ERROR)
+    {
+#ifdef _DEBUG
+        StringBuffer dbgSB("wait_multiple() ready socket(s) :");
+#endif
+        //Build up list of socks which are ready for accept read/write without blocking
+        for (aindex_t idx = 0; res && socks.length(); idx++)
+        {
+            if (FD_ISSET(socks.item(idx), &fds))
+            {
+#ifdef _DEBUG
+                dbgSB.appendf(" %d",socks.item(idx));
+#endif
+                readySocks.append(socks.item(idx));
+                if (readySocks.length() == res)
+                    break;
+            }
+        }
+#ifdef _DEBUG
+        if (res)
+            DBGLOG("%s",dbgSB.str());
+#endif
+    }
+    else
+    {
+        int err = ERRNO();
+        if (err != EINTRCALL)
+        {
+            throw MakeStringException(-1,"wait_multiple::select error %d", err);
+        }
+    }
+    return res;
+}
+
+//Given a list of sockets, wait until any one or more are ready to be read (wont block)
+//returns 0 if timeout, number of waiting sockets otherwise
+int wait_read_multiple(UnsignedArray &socks,        //IN   sockets to be checked for readiness
+                       unsigned timeoutMS,          //IN   timeout
+                       UnsignedArray &readySocks)   //OUT  sockets ready to be read
+{
+    return wait_multiple(true, socks, timeoutMS, readySocks);
+}
+
+int wait_write_multiple(UnsignedArray &socks,       //IN   sockets to be checked for readiness
+                       unsigned timeoutMS,          //IN   timeout
+                       UnsignedArray &readySocks)   //OUT  sockets ready to be written
+{
+    return wait_multiple(false, socks, timeoutMS, readySocks);
+}

+ 9 - 0
system/jlib/jsocket.hpp

@@ -603,5 +603,14 @@ extern jlib_decl StringBuffer lookupHostName(const IpAddress &ip,StringBuffer &r
 extern jlib_decl bool isInterfaceIp(const IpAddress &ip, const char *ifname);
 extern jlib_decl bool isInterfaceIp(const IpAddress &ip, const char *ifname);
 extern jlib_decl bool getInterfaceIp(IpAddress &ip, const char *ifname);
 extern jlib_decl bool getInterfaceIp(IpAddress &ip, const char *ifname);
 
 
+//Given a list of server sockets, wait until any one or more are ready to be read/written (wont block)
+//return array of ready sockets
+extern jlib_decl int wait_read_multiple(UnsignedArray  &socks,      //IN   sockets to be checked for read readiness
+                                        unsigned timeoutMS,         //IN   timeout
+                                        UnsignedArray  &readySocks);//OUT  sockets ready to be read
+extern jlib_decl int wait_write_multiple(UnsignedArray  &socks,     //IN   sockets to be checked for write readiness
+                                        unsigned timeoutMS,         //IN   timeout
+                                        UnsignedArray  &readySocks);//OUT  sockets ready to be written
+
 #endif
 #endif
 
 

+ 58 - 0
system/jlib/jutil.cpp

@@ -48,6 +48,8 @@
 #include "build-config.h"
 #include "build-config.h"
 #endif
 #endif
 
 
+#include "portlist.h"
+
 static SpinLock * cvtLock;
 static SpinLock * cvtLock;
 
 
 #ifdef _WIN32
 #ifdef _WIN32
@@ -2280,6 +2282,62 @@ IPropertyTree *getHPCCEnvironment(const char *configFileName)
     return NULL;
     return NULL;
 }
 }
 
 
+jlib_decl bool querySecuritySettings(bool *          _useSSL,
+                                     unsigned short *_port,
+                                     const char * *  _certificate,
+                                     const char * *  _privateKey)
+{
+    static CriticalSection securitySettingsCrit;
+    static bool useSSL = false;
+    static StringAttr certificate;
+    static StringAttr privateKey;
+    static bool retrieved = false;
+
+    if (!retrieved)
+    {
+        CriticalBlock b(securitySettingsCrit);
+        if (!retrieved)
+        {
+            try
+            {
+                StringBuffer configFileSpec;
+#ifndef _WIN32
+                configFileSpec.set(CONFIG_DIR).append(PATHSEPSTR).append("environment.conf");
+#endif
+                Owned<IProperties> conf = createProperties(configFileSpec.str(), true);
+                useSSL = conf->getPropBool("dfsUseSSL", false);
+                certificate.set(conf->queryProp("dfsSSLCertFile"));
+                privateKey.set(conf->queryProp("dfsSSLPrivateKeyFile"));
+                retrieved = true;
+            }
+            catch (IException *e)
+            {
+                EXCLOG(e, "Error processing environment.conf\n");
+                throwUnexpected();
+            }
+        }
+    }
+    if (retrieved)
+    {
+        if (_useSSL)
+            *_useSSL = useSSL;
+        if (_port)
+            *_port = useSSL ? SECURE_DAFILESRV_PORT : DAFILESRV_PORT;
+        if (_certificate)
+            *_certificate = certificate.get();
+        if (_privateKey)
+            *_privateKey = privateKey.get();
+    }
+    else
+    {
+        if (_useSSL)
+            *_useSSL = false;
+        if (_port)
+            *_port = DAFILESRV_PORT;
+    }
+    return retrieved;
+}
+
 static IPropertyTree *getOSSdirTree()
 static IPropertyTree *getOSSdirTree()
 {
 {
     Owned<IPropertyTree> envtree = getHPCCEnvironment();
     Owned<IPropertyTree> envtree = getHPCCEnvironment();

+ 5 - 0
system/jlib/jutil.hpp

@@ -312,6 +312,11 @@ extern jlib_decl bool getConfigurationDirectory(const IPropertyTree *dirtree, //
                                                 const char *instance, 
                                                 const char *instance, 
                                                 StringBuffer &dirout);
                                                 StringBuffer &dirout);
 
 
+extern jlib_decl bool querySecuritySettings(bool *          _useSSL,
+                                            unsigned short *_port,
+                                            const char * *  _certificate,
+                                            const char * *  _privateKey);
+
 extern jlib_decl const char * matchConfigurationDirectoryEntry(const char *path,const char *mask,StringBuffer &name, StringBuffer &component, StringBuffer &instance);
 extern jlib_decl const char * matchConfigurationDirectoryEntry(const char *path,const char *mask,StringBuffer &name, StringBuffer &component, StringBuffer &instance);
 extern jlib_decl bool replaceConfigurationDirectoryEntry(const char *path,const char *frommask,const char *tomask,StringBuffer &out);
 extern jlib_decl bool replaceConfigurationDirectoryEntry(const char *path,const char *frommask,const char *tomask,StringBuffer &out);
 
 

+ 11 - 5
system/security/securesocket/securesocket.cpp

@@ -163,7 +163,8 @@ public:
 
 
     virtual void   read(void* buf, size32_t size)
     virtual void   read(void* buf, size32_t size)
     {
     {
-        throw MakeStringException(-1, "not implemented");
+        size32_t size_read;
+        readTimeout(buf, size, size, size_read, 0, false);
     }
     }
 
 
     virtual size32_t get_max_send_size()
     virtual size32_t get_max_send_size()
@@ -312,7 +313,10 @@ public:
 
 
     virtual size32_t avail_read()            // called after wait_read to see how much data available
     virtual size32_t avail_read()            // called after wait_read to see how much data available
     {
     {
-        throw MakeStringException(-1, "not implemented");
+        int pending = SSL_pending(m_ssl);
+        if(pending > 0)
+            return pending;
+        return m_socket->avail_read();
     }
     }
 
 
     virtual size32_t write_multiple(unsigned num,const void **buf, size32_t *size)
     virtual size32_t write_multiple(unsigned num,const void **buf, size32_t *size)
@@ -556,10 +560,11 @@ int CSecureSocket::secure_accept()
     }
     }
     else if(err < 0)
     else if(err < 0)
     {
     {
+        int ret = SSL_get_error(m_ssl, err);
         char errbuf[512];
         char errbuf[512];
         ERR_error_string_n(ERR_get_error(), errbuf, 512);
         ERR_error_string_n(ERR_get_error(), errbuf, 512);
         errbuf[511] = '\0';
         errbuf[511] = '\0';
-        DBGLOG("SSL_accept returned %d, error - %s", err, errbuf);
+        DBGLOG("SSL_accept returned %d, SSL_get_error=%d, error - %s", err, ret, errbuf);
         if(strstr(errbuf, "error:1408F455:") != NULL)
         if(strstr(errbuf, "error:1408F455:") != NULL)
         {
         {
             DBGLOG("Unrecoverable SSL library error.");
             DBGLOG("Unrecoverable SSL library error.");
@@ -596,10 +601,11 @@ int CSecureSocket::secure_connect()
     int err = SSL_connect (m_ssl);                     
     int err = SSL_connect (m_ssl);                     
     if(err <= 0)
     if(err <= 0)
     {
     {
+        int ret = SSL_get_error(m_ssl, err);
         char errbuf[512];
         char errbuf[512];
         ERR_error_string_n(ERR_get_error(), errbuf, 512);
         ERR_error_string_n(ERR_get_error(), errbuf, 512);
-        DBGLOG("SSL_connect error - %s", errbuf);
-        throw MakeStringException(-1, "Certificate verification failed: %s", errbuf);
+        DBGLOG("SSL_connect error - %s, SSL_get_error=%d, error - %d", errbuf,ret, err);
+        throw MakeStringException(-1, "SSL_connect failed: %s", errbuf);
     }
     }