Browse Source

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 10 years ago
parent
commit
1af66fcd32

+ 2 - 0
common/remote/CMakeLists.txt

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

+ 15 - 2
common/remote/rmtfile.cpp

@@ -33,10 +33,23 @@
 
 //#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()
 {
-    return DAFILESRV_PORT;
+    return securitySettings.queryDaliServixPort();
 }
 
 
@@ -209,7 +222,7 @@ public:
         SocketEndpoint ep = filename.queryEndpoint();
         bool noport = (ep.port==0);
         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__
 #ifndef USE_SAMBA

+ 140 - 9
common/remote/sockfile.cpp

@@ -29,6 +29,7 @@
 #include "jmisc.hpp"
 #include "jthread.hpp"
 
+#include "securesocket.hpp"
 #include "sockfile.hpp"
 #include "portlist.h"
 #include "jsocket.hpp"
@@ -173,6 +174,37 @@ typedef int RemoteFileIOHandle;
 static unsigned maxConnectTime = 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)
 {
     maxConnectTime = maxconnecttime;
@@ -400,7 +432,11 @@ void setDafsEndpointPort(SocketEndpoint &ep)
         }
     }
     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;
     static  SocketEndpoint  lastfailep;
     static unsigned         lastfailtime;
-
+    bool                    useSSL;
     void connectSocket(SocketEndpoint &ep)
     {
+#ifdef _DEBUG
+        StringBuffer sb;
+        ep.getUrlStr(sb);
+        DBGLOG("Client connecting %sto dafilesrv %s", useSSL?"SECURE ":"", sb.str());
+#endif
         sRFTM tm;
         // called in CConnectionTable::crit
         unsigned retries = 3;
@@ -968,7 +1009,7 @@ class CRemoteBase: public CInterface
             StringBuffer eps;
             if (TF_TRACE_CLIENT_CONN) {
                 ep.getUrlStr(eps);
-                PROGLOG("Connecting to %s",eps.str());
+                PROGLOG("Connecting %sto %s",useSSL?"SECURE ":"",eps.str());
                 //PrintStackReport();
             }
             bool ok = true;
@@ -980,6 +1021,14 @@ class CRemoteBase: public CInterface
                 }
                 else
                     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) {
                 ok = false;
@@ -1026,7 +1075,7 @@ class CRemoteBase: public CInterface
                     sleeptime = remaining/2;
             }
             Sleep(sleeptime);       // prevent multiple retries beating
-            PROGLOG("Retrying connect");
+            PROGLOG("Retrying %sconnect",useSSL?"SECURE ":"");
         }  
         if (ConnectionTable)
             ConnectionTable->addLink(ep,socket);
@@ -1114,7 +1163,19 @@ protected: friend class CRemoteFileIO;
                 }
             }
             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)
     {
         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;
     CriticalSection     sect;
     Owned<ISocket>      acceptsock;
+    Owned<ISocket>      rejectsock;
     Owned<ISocketSelectHandler> selecthandler;
     Owned<IThreadPool>  threads;    // for commands
     bool stopping;
@@ -3020,7 +3084,7 @@ class CRemoteFileServer : public CInterface, implements IRemoteFileServer, imple
 
 
         IMPLEMENT_IINTERFACE;
-        
+
         CRemoteClientHandler(CRemoteFileServer *_parent,ISocket *_socket,IAuthenticatedUser *_user,atomic_t &_globallasttick)
             : socket(_socket), user(_user), globallasttick(_globallasttick)
         {
@@ -4149,6 +4213,8 @@ public:
         stopping = true;
         if (acceptsock) 
             acceptsock->cancel_accept();
+        if (rejectsock)
+            rejectsock->cancel_accept();
         reply.append((unsigned)RFEnoerror);
         return false;
     }
@@ -4355,7 +4421,7 @@ public:
     }
 
 
-    void run(SocketEndpoint &listenep)
+    void run(SocketEndpoint &listenep, bool useSSL)
     {
         if (listenep.isNull())
             acceptsock.setown(ISocket::create(listenep.port));
@@ -4365,12 +4431,67 @@ public:
             listenep.getIpText(ips);
             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();
+
+        UnsignedArray readSocks;
+        if (useSSL) {
+            readSocks.append(acceptsock->OShandle());
+            readSocks.append(rejectsock->OShandle());
+        }
+
         loop {
             Owned<ISocket> sock;
-            bool sockavail;
+            bool sockavail = false;
             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 (!sockavail) {
                     JSocketStatistics stats;
@@ -4392,6 +4513,14 @@ public:
             if (sockavail) {
                 try {
                     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)
                         break;
                 }
@@ -4524,6 +4653,8 @@ public:
             PROGLOG("CRemoteFileServer::stop");
         if (acceptsock) 
             acceptsock->cancel_accept();
+        if (rejectsock)
+            rejectsock->cancel_accept();
         threads->stopAll();
         threads->joinAll(true,60*1000);
     }

+ 1 - 1
common/remote/sockfile.hpp

@@ -32,7 +32,7 @@
 interface IRemoteFileServer : public IInterface
 {
 public:
-    virtual void run(SocketEndpoint &listenep) = 0;
+    virtual void run(SocketEndpoint &listenep, bool useSSL = false) = 0;
     virtual void stop() = 0;
     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) {
                     if (strcmp(rfn.getLocalPath(locpath.clear()).str(),locdirs.item(d))==0) {
                         SocketEndpoint ep = rfn.queryEndpoint();
-                        if (ep.port==DAFILESRV_PORT)
+                        if (ep.port==DAFILESRV_PORT || ep.port==SECURE_DAFILESRV_PORT)
                             ep.port = 0;
                         epa[d].append(ep);
                         found = true;

+ 34 - 9
dali/dafilesrv/dafilesrv.cpp

@@ -36,7 +36,7 @@
 void usage()
 {
     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("    dafilesrv -D [ -L <log-dir> ] [ -LOCAL ]      -- run as linux daemon\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 -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 SSL port is %d (certificate specs required in environment.conf)\n",SECURE_DAFILESRV_PORT);
     printf("Version:  %s\n\n",remoteServerVersionString());
 }
 
@@ -345,6 +347,13 @@ int main(int argc,char **argv)
     bool requireauthenticate = false;
     StringBuffer logDir;
     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) {
         if (stricmp(argv[i],"-D")==0) {
             i++;
@@ -380,10 +389,25 @@ int main(int argc,char **argv)
             i++;
             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
             break;
     }
 
+    if (useSSL && !sslCertFile)
+    {
+        ERRLOG("DaFileSrv SSL specified but certificate file information missing from environment.conf");
+        exit(-1);
+    }
+
     if (0 == logDir.length())
     {
         getConfigurationDirectory(NULL,"log","dafilesrv",instanceName.str(),logDir);
@@ -413,10 +437,10 @@ int main(int argc,char **argv)
     }
 #endif
     if (argc == i)
-      listenep.port = DAFILESRV_PORT;
+        listenep.port = dafsPort;
     else {
         if (strchr(argv[i],'.')||!isdigit(argv[i][0]))
-            listenep.set(argv[i],DAFILESRV_PORT);
+            listenep.set(argv[i], dafsPort);
         else
             listenep.port = atoi(argv[i]);
         if (listenep.port==0) {
@@ -433,6 +457,7 @@ int main(int argc,char **argv)
             bool stopped;
             bool started;
             SocketEndpoint listenep;
+            bool useSSL;
             bool requireauthenticate;
 
             
@@ -456,8 +481,8 @@ int main(int argc,char **argv)
 
         public:
 
-            cserv(SocketEndpoint _listenep) 
-                : listenep(_listenep),pollthread(this)
+            cserv(SocketEndpoint _listenep, bool _useSSL)
+                : listenep(_listenep),useSSL(_useSSL),pollthread(this)
             {
                 stopped = false;
                 started = false;
@@ -518,7 +543,7 @@ int main(int argc,char **argv)
                 else
                     listenep.getUrlStr(eps);
                 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();
                 PROGLOG("Version: %s", verstring);
                 PROGLOG("Authentication:%s required",requireauthenticate?"":" not");
@@ -534,7 +559,7 @@ int main(int argc,char **argv)
                 PROGLOG(DAFS_SERVICE_DISPLAY_NAME " Stopped");
                 stopped = true;
             }
-        } service(listenep);
+        } service(listenep, useSSL);
         service.start();
         return 0;
 #else
@@ -556,14 +581,14 @@ int main(int argc,char **argv)
     else
         listenep.getUrlStr(eps);
     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("Authentication:%s required",requireauthenticate?"":" not");
     startPerformanceMonitor(10*60*1000, PerfMonStandard);
     server.setown(createRemoteFileServer());
     writeSentinelFile(sentinelFile);
     try {
-        server->run(listenep);
+        server->run(listenep, useSSL);
     }
     catch (IException *e) {
         EXCLOG(e,"DAFILESRV");

+ 18 - 4
dali/dfuplus/dfuplus.cpp

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

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

@@ -21,3 +21,8 @@ interface=*
 use_epoll=true
 # allow kernel pagecache flushing where enabled (true/false)
 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_END_PORT                     7500
 
+#define SECURE_DAFILESRV_PORT           7600 // aka daliservix
+
 //ESP SERVICES
 //INSECURE
 #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
 {
-    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 ep.isLocal() || ep.isNull();
 }

+ 94 - 1
system/jlib/jsocket.cpp

@@ -372,7 +372,7 @@ protected:
     SOCKETMODE      sockmode;
     IpAddress       targetip;
     SocketEndpoint  returnep;   // set by set_return_addr
-    
+
     MCASTREQ    *   mcastreq;
     size32_t        nextblocksize;
     unsigned        blockflags;
@@ -6066,3 +6066,96 @@ ISocketConnectWait *nonBlockingConnect(SocketEndpoint &ep,unsigned connecttimeou
 {
     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 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
 

+ 58 - 0
system/jlib/jutil.cpp

@@ -48,6 +48,8 @@
 #include "build-config.h"
 #endif
 
+#include "portlist.h"
+
 static SpinLock * cvtLock;
 
 #ifdef _WIN32
@@ -2280,6 +2282,62 @@ IPropertyTree *getHPCCEnvironment(const char *configFileName)
     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()
 {
     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, 
                                                 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 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)
     {
-        throw MakeStringException(-1, "not implemented");
+        size32_t size_read;
+        readTimeout(buf, size, size, size_read, 0, false);
     }
 
     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
     {
-        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)
@@ -556,10 +560,11 @@ int CSecureSocket::secure_accept()
     }
     else if(err < 0)
     {
+        int ret = SSL_get_error(m_ssl, err);
         char errbuf[512];
         ERR_error_string_n(ERR_get_error(), errbuf, 512);
         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)
         {
             DBGLOG("Unrecoverable SSL library error.");
@@ -596,10 +601,11 @@ int CSecureSocket::secure_connect()
     int err = SSL_connect (m_ssl);                     
     if(err <= 0)
     {
+        int ret = SSL_get_error(m_ssl, err);
         char 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);
     }