https://issues.shibboleth.net/jira/browse/SSPCPP-326
authorScott Cantor <cantor.2@osu.edu>
Mon, 17 Oct 2011 17:35:31 +0000 (17:35 +0000)
committerScott Cantor <cantor.2@osu.edu>
Mon, 17 Oct 2011 17:35:31 +0000 (17:35 +0000)
18 files changed:
config_win32.h
configure.ac
shibsp/Makefile.am
shibsp/config_pub.h.in
shibsp/handler/impl/AssertionLookup.cpp
shibsp/handler/impl/MetadataGenerator.cpp
shibsp/handler/impl/SessionHandler.cpp
shibsp/handler/impl/StatusHandler.cpp
shibsp/remoting/impl/SocketListener.cpp
shibsp/remoting/impl/SocketListener.h
shibsp/remoting/impl/TCPListener.cpp
shibsp/remoting/impl/UnixListener.cpp
shibsp/shibsp-lite.vcxproj
shibsp/shibsp-lite.vcxproj.filters
shibsp/shibsp.vcxproj
shibsp/shibsp.vcxproj.filters
shibsp/util/IPRange.cpp [new file with mode: 0644]
shibsp/util/IPRange.h [new file with mode: 0644]

index 5718055..8889cde 100644 (file)
 /* Define to 1 if you have the <unistd.h> header file. */
 /* #undef HAVE_UNISTD_H */
 
+/* Define to 1 if the system has the type `struct sockaddr_storage'. */
+#define HAVE_STRUCT_SOCKADDR_STORAGE 1
+
 /* Name of package */
 #define PACKAGE "shibboleth"
 
index 3301ae0..29326dd 100644 (file)
@@ -73,7 +73,9 @@ AC_STRUCT_TM
 AC_FUNC_STRFTIME
 AC_FUNC_STRERROR_R
 AC_CHECK_HEADERS([sys/utsname.h grp.h pwd.h])
+AC_CHECK_HEADERS([sys/socket.h], [AC_DEFINE([SHIBSP_HAVE_SYS_SOCKET_H],[1],[Define to 1 if you have the <sys/socket> header file.])], [])
 AC_CHECK_FUNCS([strchr strdup strstr timegm gmtime_r strtok_r strcasecmp getpwnam getgrnam])
+AC_CHECK_TYPES([struct sockaddr_storage], [], [], [[#include <sys/socket.h>]])
 
 # checks for pthreads
 ACX_PTHREAD([enable_threads="pthread"],[enable_threads="no"])
index 4b12364..e87a7e8 100644 (file)
@@ -98,6 +98,7 @@ secinclude_HEADERS = \
 utilinclude_HEADERS = \
        util/CGIParser.h \
        util/DOMPropertySet.h \
+       util/IPRange.h \
        util/PropertySet.h \
        util/SPConstants.h \
        util/TemplateParameters.h
@@ -159,6 +160,7 @@ common_sources = \
        remoting/impl/UnixListener.cpp \
        util/CGIParser.cpp \
        util/DOMPropertySet.cpp \
+       util/IPRange.cpp \
        util/SPConstants.cpp \
        util/TemplateParameters.cpp
 
index 8d8a058..9f39699 100644 (file)
@@ -24,3 +24,6 @@
 
 /* Define to 1 if XML-Security-C supports white/blacklisting algorithms. */
 #undef SHIBSP_XMLSEC_WHITELISTING
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef SHIBSP_HAVE_SYS_SOCKET_H
index 94b491f..65312e8 100644 (file)
@@ -32,6 +32,7 @@
 #include "SPRequest.h"
 #include "handler/AbstractHandler.h"
 #include "handler/RemotedHandler.h"
+#include "util/IPRange.h"
 #include "util/SPConstants.h"
 
 #ifndef SHIBSP_LITE
@@ -84,7 +85,7 @@ namespace shibsp {
     private:
         pair<bool,long> processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
 
-        set<string> m_acl;
+        vector<IPRange> m_acl;
     };
 
 #if defined (_MSC_VER)
@@ -101,31 +102,51 @@ namespace shibsp {
 AssertionLookup::AssertionLookup(const DOMElement* e, const char* appId)
     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".AssertionLookup"), &g_Blocker)
 {
-    setAddress("run::AssertionLookup");
     if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
         pair<bool,const char*> acl = getString("exportACL");
-        if (!acl.first) {
-            m_acl.insert("127.0.0.1");
-            return;
-        }
-        string aclbuf=acl.second;
-        int j = 0;
-        for (unsigned int i=0;  i < aclbuf.length();  i++) {
-            if (aclbuf.at(i)==' ') {
-                m_acl.insert(aclbuf.substr(j, i-j));
-                j = i+1;
+        if (acl.first) {
+            string aclbuf=acl.second;
+            int j = 0;
+            for (unsigned int i=0;  i < aclbuf.length();  i++) {
+                if (aclbuf.at(i)==' ') {
+                    try {
+                        m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, i-j).c_str()));
+                    }
+                    catch (exception& ex) {
+                        m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, i-j).c_str(), ex.what());
+                    }
+                    j = i+1;
+                }
+            }
+            try {
+                m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, aclbuf.length()-j).c_str()));
+            }
+            catch (exception& ex) {
+                m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, aclbuf.length()-j).c_str(), ex.what());
+            }
+
+            if (m_acl.empty()) {
+                m_log.warn("invalid CIDR range(s) in acl property, allowing 127.0.0.1 as a fall back");
+                m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
             }
         }
-        m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
+        else {
+            m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
+        }
     }
+
+    setAddress("run::AssertionLookup");
 }
 
 pair<bool,long> AssertionLookup::run(SPRequest& request, bool isHandler) const
 {
-    string relayState;
     SPConfig& conf = SPConfig::getConfig();
-    if (conf.isEnabled(SPConfig::InProcess)) {
-        if (m_acl.count(request.getRemoteAddr()) == 0) {
+    if (conf.isEnabled(SPConfig::InProcess) && !m_acl.empty()) {
+        bool found = false;
+        for (vector<IPRange>::const_iterator acl = m_acl.begin(); !found && acl != m_acl.end(); ++acl) {
+            found = acl->contains(request.getRemoteAddr().c_str());
+        }
+        if (!found) {
             m_log.error("request for assertion lookup blocked from invalid address (%s)", request.getRemoteAddr().c_str());
             istringstream msg("Assertion Lookup Blocked");
             return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
index f60cea7..fe541b2 100644 (file)
@@ -31,6 +31,7 @@
 #include "SPRequest.h"
 #include "handler/AbstractHandler.h"
 #include "handler/RemotedHandler.h"
+#include "util/IPRange.h"
 
 #ifndef SHIBSP_LITE
 # include "attribute/resolver/AttributeExtractor.h"
@@ -110,7 +111,7 @@ namespace shibsp {
             HTTPResponse& httpResponse
             ) const;
 
-        set<string> m_acl;
+        vector<IPRange> m_acl;
 #ifndef SHIBSP_LITE
         string m_salt;
         short m_http,m_https;
@@ -145,18 +146,34 @@ MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId)
     string address(appId);
     address += getString("Location").second;
     setAddress(address.c_str());
+
     if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
         pair<bool,const char*> acl = getString("acl");
         if (acl.first) {
             string aclbuf=acl.second;
             int j = 0;
-            for (unsigned int i=0;  i < aclbuf.length();  i++) {
+            for (unsigned int i=0;  i < aclbuf.length();  ++i) {
                 if (aclbuf.at(i)==' ') {
-                    m_acl.insert(aclbuf.substr(j, i-j));
-                    j = i+1;
+                    try {
+                        m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, i-j).c_str()));
+                    }
+                    catch (exception& ex) {
+                        m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, i-j).c_str(), ex.what());
+                    }
+                    j = i + 1;
                 }
             }
-            m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
+            try {
+                m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, aclbuf.length()-j).c_str()));
+            }
+            catch (exception& ex) {
+                m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, aclbuf.length()-j).c_str(), ex.what());
+            }
+
+            if (m_acl.empty()) {
+                m_log.warn("invalid CIDR range(s) in Metadata Generator acl property, allowing 127.0.0.1 as a fall back");
+                m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
+            }
         }
     }
 
@@ -255,8 +272,12 @@ MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId)
 pair<bool,long> MetadataGenerator::run(SPRequest& request, bool isHandler) const
 {
     SPConfig& conf = SPConfig::getConfig();
-    if (conf.isEnabled(SPConfig::InProcess)) {
-        if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) {
+    if (conf.isEnabled(SPConfig::InProcess) && !m_acl.empty()) {
+        bool found = false;
+        for (vector<IPRange>::const_iterator acl = m_acl.begin(); !found && acl != m_acl.end(); ++acl) {
+            found = acl->contains(request.getRemoteAddr().c_str());
+        }
+        if (!found) {
             m_log.error("request for metadata blocked from invalid address (%s)", request.getRemoteAddr().c_str());
             istringstream msg("Metadata Request Blocked");
             return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
index 57d0fd7..ec0f57b 100644 (file)
@@ -32,6 +32,7 @@
 #include "SPRequest.h"
 #include "attribute/Attribute.h"
 #include "handler/AbstractHandler.h"
+#include "util/IPRange.h"
 
 #include <ctime>
 
@@ -71,7 +72,7 @@ namespace shibsp {
 
     private:
         bool m_values;
-        set<string> m_acl;
+        vector<IPRange> m_acl;
     };
 
 #if defined (_MSC_VER)
@@ -92,13 +93,28 @@ SessionHandler::SessionHandler(const DOMElement* e, const char* appId)
     if (acl.first) {
         string aclbuf=acl.second;
         int j = 0;
-        for (unsigned int i=0;  i < aclbuf.length();  i++) {
+        for (unsigned int i=0;  i < aclbuf.length();  ++i) {
             if (aclbuf.at(i)==' ') {
-                m_acl.insert(aclbuf.substr(j, i-j));
-                j = i+1;
+                try {
+                    m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, i-j).c_str()));
+                }
+                catch (exception& ex) {
+                    m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, i-j).c_str(), ex.what());
+                }
+                j = i + 1;
             }
         }
-        m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
+        try {
+            m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, aclbuf.length()-j).c_str()));
+        }
+        catch (exception& ex) {
+            m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, aclbuf.length()-j).c_str(), ex.what());
+        }
+
+        if (m_acl.empty()) {
+            m_log.warn("invalid CIDR range(s) in Session handler acl property, allowing 127.0.0.1 as a fall back");
+            m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
+        }
     }
 
     pair<bool,bool> flag = getBool("showAttributeValues");
@@ -108,10 +124,16 @@ SessionHandler::SessionHandler(const DOMElement* e, const char* appId)
 
 pair<bool,long> SessionHandler::run(SPRequest& request, bool isHandler) const
 {
-    if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) {
-        m_log.error("session handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str());
-        istringstream msg("Session Handler Blocked");
-        return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
+    if (!m_acl.empty()) {
+        bool found = false;
+        for (vector<IPRange>::const_iterator acl = m_acl.begin(); !found && acl != m_acl.end(); ++acl) {
+            found = acl->contains(request.getRemoteAddr().c_str());
+        }
+        if (!found) {
+            m_log.error("session handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str());
+            istringstream msg("Session Handler Blocked");
+            return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
+        }
     }
 
     stringstream s;
index 3bef77c..9a57870 100644 (file)
@@ -31,6 +31,7 @@
 #include "SPRequest.h"
 #include "handler/AbstractHandler.h"
 #include "handler/RemotedHandler.h"
+#include "util/IPRange.h"
 #include "util/CGIParser.h"
 
 #include <xmltooling/version.h>
@@ -90,7 +91,7 @@ namespace shibsp {
         pair<bool,long> processMessage(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
         ostream& systemInfo(ostream& os) const;
 
-        set<string> m_acl;
+        vector<IPRange> m_acl;
     };
 
 #if defined (_MSC_VER)
@@ -267,9 +268,6 @@ namespace shibsp {
 StatusHandler::StatusHandler(const DOMElement* e, const char* appId)
     : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".StatusHandler"), &g_Blocker)
 {
-    string address(appId);
-    address += getString("Location").second;
-    setAddress(address.c_str());
     if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
         pair<bool,const char*> acl = getString("acl");
         if (acl.first) {
@@ -277,20 +275,43 @@ StatusHandler::StatusHandler(const DOMElement* e, const char* appId)
             int j = 0;
             for (unsigned int i=0;  i < aclbuf.length();  i++) {
                 if (aclbuf.at(i)==' ') {
-                    m_acl.insert(aclbuf.substr(j, i-j));
-                    j = i+1;
+                    try {
+                        m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, i-j).c_str()));
+                    }
+                    catch (exception& ex) {
+                        m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, i-j).c_str(), ex.what());
+                    }
+                    j = i + 1;
                 }
             }
-            m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
+            try {
+                m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, aclbuf.length()-j).c_str()));
+            }
+            catch (exception& ex) {
+                m_log.error("invalid CIDR block (%s): %s", aclbuf.substr(j, aclbuf.length()-j).c_str(), ex.what());
+            }
+
+            if (m_acl.empty()) {
+                m_log.warn("invalid CIDR range(s) in Status handler acl property, allowing 127.0.0.1 as a fall back");
+                m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
+            }
         }
     }
+
+    string address(appId);
+    address += getString("Location").second;
+    setAddress(address.c_str());
 }
 
 pair<bool,long> StatusHandler::run(SPRequest& request, bool isHandler) const
 {
     SPConfig& conf = SPConfig::getConfig();
-    if (conf.isEnabled(SPConfig::InProcess)) {
-        if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) {
+    if (conf.isEnabled(SPConfig::InProcess) && !m_acl.empty()) {
+        bool found = false;
+        for (vector<IPRange>::const_iterator acl = m_acl.begin(); !found && acl != m_acl.end(); ++acl) {
+            found = acl->contains(request.getRemoteAddr().c_str());
+        }
+        if (!found) {
             m_log.error("status handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str());
             istringstream msg("Status Handler Blocked");
             return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
index cd53cd2..5553dbf 100644 (file)
@@ -390,8 +390,10 @@ DDF SocketListener::send(const DDF& in)
     return out;
 }
 
-bool SocketListener::log_error() const
+bool SocketListener::log_error(const char* fn) const
 {
+    if (!fn)
+        fn = "unknown";
 #ifdef WIN32
     int rc=WSAGetLastError();
 #else
@@ -401,10 +403,10 @@ bool SocketListener::log_error() const
     char buf[256];
     memset(buf,0,sizeof(buf));
     strerror_r(rc,buf,sizeof(buf));
-    log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
+    log->error("socket call (%s) resulted in error (%d): %s",fn, rc, isprint(*buf) ? buf : "no message");
 #else
     const char* buf=strerror(rc);
-    log->error("socket call resulted in error (%d): %s",rc,isprint(*buf) ? buf : "no message");
+    log->error("socket call (%s) resulted in error (%d): %s", fn, rc, isprint(*buf) ? buf : "no message");
 #endif
     return false;
 }
index df4dc23..bbcde89 100644 (file)
@@ -78,7 +78,7 @@ namespace shibsp {
 
         bool m_catchAll;
     protected:
-        bool log_error() const; // for OS-level errors
+        bool log_error(const char* fn=nullptr) const; // for OS-level errors
         xmltooling::logging::Category* log;
         /// @endcond
 
index 2daf68a..ff49126 100644 (file)
  */
 
 #include "internal.h"
+#include "exceptions.h"
 #include "remoting/impl/SocketListener.h"
+#include "util/IPRange.h"
 
 #include <xercesc/util/XMLUniDefs.hpp>
 #include <xmltooling/unicode.h>
 #include <xmltooling/util/XMLHelper.h>
 
+#ifdef WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#endif
+
 #ifdef HAVE_UNISTD_H
 # include <sys/socket.h>
 # include <sys/un.h>
+# include <netdb.h>
 # include <unistd.h>
 # include <arpa/inet.h>
 # include <netinet/in.h>
@@ -72,11 +80,16 @@ namespace shibsp {
         }
 
     private:
-        void setup_tcp_sockaddr(struct sockaddr_in* addr) const;
+        bool setup_tcp_sockaddr();
 
         string m_address;
         unsigned short m_port;
-        set<string> m_acl;
+        vector<IPRange> m_acl;
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+        struct sockaddr_storage m_sockaddr;
+#else
+        struct sockaddr_in m_sockaddr;
+#endif
     };
 
     ListenerService* SHIBSP_DLLLOCAL TCPListenerServiceFactory(const DOMElement* const & e)
@@ -106,73 +119,125 @@ TCPListener::TCPListener(const DOMElement* e)
     }
 
     int j = 0;
-    string sockacl = XMLHelper::getAttrString(e, "127.0.0.1", acl);
-    for (unsigned int i = 0;  i < sockacl.length();  i++) {
-        if (sockacl.at(i) == ' ') {
-            m_acl.insert(sockacl.substr(j, i-j));
-            j = i+1;
+    string aclbuf = XMLHelper::getAttrString(e, "127.0.0.1", acl);
+    for (unsigned int i = 0;  i < aclbuf.length();  ++i) {
+        if (aclbuf.at(i) == ' ') {
+            try {
+                m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, i-j).c_str()));
+            }
+            catch (exception& ex) {
+                log->error("invalid CIDR block (%s): %s", aclbuf.substr(j, i-j).c_str(), ex.what());
+            }
+            j = i + 1;
         }
     }
-    m_acl.insert(sockacl.substr(j, sockacl.length()-j));
+    try {
+        m_acl.push_back(IPRange::parseCIDRBlock(aclbuf.substr(j, aclbuf.length()-j).c_str()));
+    }
+    catch (exception& ex) {
+        log->error("invalid CIDR block (%s): %s", aclbuf.substr(j, aclbuf.length()-j).c_str(), ex.what());
+    }
+
+    if (m_acl.empty()) {
+        log->warn("invalid CIDR range(s) in acl property, allowing 127.0.0.1 as a fall back");
+        m_acl.push_back(IPRange::parseCIDRBlock("127.0.0.1"));
+    }
+
+    if (!setup_tcp_sockaddr()) {
+        throw ConfigurationException("Unable to use configured socket address property.");
+    }
 }
 
-void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const
+bool TCPListener::setup_tcp_sockaddr()
 {
-    // Split on host:port boundary. Default to port only.
-    memset(addr,0,sizeof(struct sockaddr_in));
-    addr->sin_family=AF_INET;
-    addr->sin_port=htons(m_port);
-    addr->sin_addr.s_addr=inet_addr(m_address.c_str());
+    struct addrinfo* ret = nullptr;
+    struct addrinfo hints;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_flags = AI_NUMERICHOST;
+    hints.ai_family = AF_UNSPEC;
+
+    if (getaddrinfo(m_address.c_str(), nullptr, &hints, &ret) != 0) {
+        log->error("unable to parse server address (%s)", m_address.c_str());
+        return false;
+    }
+
+    if (ret->ai_family == AF_INET) {
+        memcpy(&m_sockaddr, ret->ai_addr, ret->ai_addrlen);
+        freeaddrinfo(ret);
+        ((struct sockaddr_in*)&m_sockaddr)->sin_port=htons(m_port);
+        return true;
+    }
+#if defined(AF_INET6) && defined(HAVE_STRUCT_SOCKADDR_STORAGE)
+    else if (ret->ai_family == AF_INET6) {
+        memcpy(&m_sockaddr, ret->ai_addr, ret->ai_addrlen);
+        freeaddrinfo(ret);
+        ((struct sockaddr_in6*)&m_sockaddr)->sin6_port=htons(m_port);
+        return true;
+    }
+#endif
+
+    log->error("unknown address type (%d)", ret->ai_family);
+    freeaddrinfo(ret);
+    return false;
 }
 
 bool TCPListener::create(ShibSocket& s) const
 {
-    s=socket(AF_INET,SOCK_STREAM,0);
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+    s = socket(m_sockaddr.ss_family, SOCK_STREAM, 0);
+#else
+    s = socket(m_sockaddr.sin_family, SOCK_STREAM, 0);
+#endif
 #ifdef WIN32
-    if(s==INVALID_SOCKET)
+    if(s == INVALID_SOCKET)
 #else
     if (s < 0)
 #endif
-        return log_error();
+        return log_error("socket");
     return true;
 }
 
 bool TCPListener::bind(ShibSocket& s, bool force) const
 {
-    struct sockaddr_in addr;
-    setup_tcp_sockaddr(&addr);
-
     // XXX: Do we care about the return value from setsockopt?
     int opt = 1;
     ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
 
 #ifdef WIN32
-    if (SOCKET_ERROR==::bind(s,(struct sockaddr *)&addr,sizeof(addr)) || SOCKET_ERROR==::listen(s,3)) {
-        log_error();
+    if (SOCKET_ERROR==::bind(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.ss_len) || SOCKET_ERROR==::listen(s, 3)) {
+        log_error("bind");
         close(s);
         return false;
     }
 #else
-    if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
-        log_error();
+# ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+    if (::bind(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.ss_len) < 0)
+# else
+    if (::bind(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.sin_len) < 0)
+# endif
+    {
+        log_error("bind");
         close(s);
         return false;
     }
-    ::listen(s,3);
+    ::listen(s, 3);
 #endif
     return true;
 }
 
 bool TCPListener::connect(ShibSocket& s) const
 {
-    struct sockaddr_in addr;
-    setup_tcp_sockaddr(&addr);
 #ifdef WIN32
-    if(SOCKET_ERROR==::connect(s,(struct sockaddr *)&addr,sizeof(addr)))
-        return log_error();
+    if(SOCKET_ERROR==::connect(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.ss_len))
+        return log_error("connect");
 #else
-    if (::connect(s, (struct sockaddr*)&addr, sizeof (addr)) < 0)
-        return log_error();
+# ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+    if (::connect(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.ss_len) < 0)
+# else
+    if (::connect(s, (const struct sockaddr*)&m_sockaddr, m_sockaddr.sin_len) < 0)
+# endif
+        return log_error("connect");
 #endif
     return true;
 }
@@ -189,23 +254,31 @@ bool TCPListener::close(ShibSocket& s) const
 
 bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const
 {
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+    struct sockaddr_storage addr;
+#else
     struct sockaddr_in addr;
+#endif
+    memset(&addr, 0, sizeof(addr));
 
 #ifdef WIN32
     int size=sizeof(addr);
-    s=::accept(listener,(struct sockaddr*)&addr,&size);
+    s=::accept(listener, (struct sockaddr*)&addr, &size);
     if(s==INVALID_SOCKET)
 #else
     socklen_t size=sizeof(addr);
-    s=::accept(listener,(struct sockaddr*)&addr,&size);
+    s=::accept(listener, (struct sockaddr*)&addr, &size);
     if (s < 0)
 #endif
-        return log_error();
-    char* client=inet_ntoa(addr.sin_addr);
-    if (m_acl.count(client) == 0) {
+        return log_error("accept");
+    bool found = false;
+    for (vector<IPRange>::const_iterator acl = m_acl.begin(); !found && acl != m_acl.end(); ++acl) {
+        found = acl->contains((const struct sockaddr*)&addr);
+    }
+    if (!found) {
         close(s);
-        s=-1;
-        log->error("accept() rejected client at %s", client);
+        s = -1;
+        log->error("accept() rejected client with invalid address");
         return false;
     }
     return true;
index 554da2e..75c5ff7 100644 (file)
@@ -102,7 +102,7 @@ bool UnixListener::create(ShibSocket& sock) const
 {
     sock = socket(PF_UNIX, SOCK_STREAM, 0);
     if (sock < 0)
-        return log_error();
+        return log_error("socket");
     return true;
 }
 
@@ -117,7 +117,7 @@ bool UnixListener::bind(ShibSocket& s, bool force) const
         unlink(m_address.c_str());
 
     if (::bind(s, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
-        log_error();
+        log_error("bind");
         close(s);
         return false;
     }
@@ -125,7 +125,7 @@ bool UnixListener::bind(ShibSocket& s, bool force) const
     // Make sure that only the creator can read -- we don't want just
     // anyone connecting, do we?
     if (chmod(m_address.c_str(),0777) < 0) {
-        log_error();
+        log_error("chmod");
         close(s);
         unlink(m_address.c_str());
         return false;
@@ -143,7 +143,7 @@ bool UnixListener::connect(ShibSocket& s) const
     strncpy(addr.sun_path, m_address.c_str(), UNIX_PATH_MAX);
 
     if (::connect(s, (struct sockaddr *)&addr, sizeof (addr)) < 0)
-        return log_error();
+        return log_error("connect");
     return true;
 }
 
@@ -157,6 +157,6 @@ bool UnixListener::accept(ShibSocket& listener, ShibSocket& s) const
 {
     s=::accept(listener,nullptr,nullptr);
     if (s < 0)
-        return log_error();
+        return log_error("accept");
     return true;
 }
index 08e359f..7802ee7 100644 (file)
     <ClCompile Include="SPConfig.cpp" />\r
     <ClCompile Include="util\CGIParser.cpp" />\r
     <ClCompile Include="util\DOMPropertySet.cpp" />\r
+    <ClCompile Include="util\IPRange.cpp" />\r
     <ClCompile Include="util\SPConstants.cpp" />\r
     <ClCompile Include="util\TemplateParameters.cpp" />\r
     <ClCompile Include="remoting\impl\ddf.cpp" />\r
     <ClInclude Include="version.h" />\r
     <ClInclude Include="util\CGIParser.h" />\r
     <ClInclude Include="util\DOMPropertySet.h" />\r
+    <ClInclude Include="util\IPRange.h" />\r
     <ClInclude Include="util\PropertySet.h" />\r
     <ClInclude Include="util\SPConstants.h" />\r
     <ClInclude Include="util\TemplateParameters.h" />\r
index a01dba9..8d81e3d 100644 (file)
@@ -81,6 +81,9 @@
     <ClCompile Include="util\DOMPropertySet.cpp">\r
       <Filter>Source Files\util</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="util\IPRange.cpp">\r
+      <Filter>Source Files\util</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="util\SPConstants.cpp">\r
       <Filter>Source Files\util</Filter>\r
     </ClCompile>\r
     <ClInclude Include="util\DOMPropertySet.h">\r
       <Filter>Header Files\util</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="util\IPRange.h">\r
+      <Filter>Header Files\util</Filter>\r
+    </ClInclude>\r
     <ClInclude Include="util\PropertySet.h">\r
       <Filter>Header Files\util</Filter>\r
     </ClInclude>\r
index a6cca62..93eb4bf 100644 (file)
     <ClCompile Include="SPConfig.cpp" />\r
     <ClCompile Include="util\CGIParser.cpp" />\r
     <ClCompile Include="util\DOMPropertySet.cpp" />\r
+    <ClCompile Include="util\IPRange.cpp" />\r
     <ClCompile Include="util\SPConstants.cpp" />\r
     <ClCompile Include="util\TemplateParameters.cpp" />\r
     <ClCompile Include="security\PKIXTrustEngine.cpp" />\r
     <ClInclude Include="version.h" />\r
     <ClInclude Include="util\CGIParser.h" />\r
     <ClInclude Include="util\DOMPropertySet.h" />\r
+    <ClInclude Include="util\IPRange.h" />\r
     <ClInclude Include="util\PropertySet.h" />\r
     <ClInclude Include="util\SPConstants.h" />\r
     <ClInclude Include="util\TemplateParameters.h" />\r
index 5278e45..0b2f624 100644 (file)
     <ClCompile Include="util\DOMPropertySet.cpp">\r
       <Filter>Source Files\util</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="util\IPRange.cpp">\r
+      <Filter>Source Files\util</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="util\SPConstants.cpp">\r
       <Filter>Source Files\util</Filter>\r
     </ClCompile>\r
     <ClInclude Include="util\DOMPropertySet.h">\r
       <Filter>Header Files\util</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="util\IPRange.h">\r
+      <Filter>Header Files\util</Filter>\r
+    </ClInclude>\r
     <ClInclude Include="util\PropertySet.h">\r
       <Filter>Header Files\util</Filter>\r
     </ClInclude>\r
diff --git a/shibsp/util/IPRange.cpp b/shibsp/util/IPRange.cpp
new file mode 100644 (file)
index 0000000..72dd8fc
--- /dev/null
@@ -0,0 +1,192 @@
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+/**
+ * @file IPRange.cpp
+ * 
+ * Represents a range of IP addresses.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "util/IPRange.h"
+
+#include <xmltooling/logging.h>
+
+#ifdef WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <netdb.h>
+# include <netinet/in.h>
+#endif
+
+using namespace shibsp;
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using namespace std;
+
+namespace {
+    // Gets the byte-level representation of a numeric IP address.
+    struct addrinfo* parseIPAddress(const char* s)
+    {
+        struct addrinfo* ret = nullptr;
+        struct addrinfo hints;
+
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_flags = AI_NUMERICHOST;
+        hints.ai_family = AF_UNSPEC;
+
+        if (getaddrinfo(s, nullptr, &hints, &ret) != 0)
+            return nullptr;
+        if (ret) {
+            if (ret->ai_family != AF_INET
+#ifdef AF_INET6
+                && ret->ai_family != AF_INET6
+#endif
+                ) {
+                freeaddrinfo(ret);
+                return nullptr;
+            }
+        }
+        return ret;
+    }
+};
+
+IPRange::IPRange(const bitset<32>& address, int maskSize) : m_addressLength(32)
+{
+    if (maskSize < 0 || maskSize > m_addressLength)
+        throw ConfigurationException("CIDR prefix length out of range.");
+
+    for (int i = m_addressLength - maskSize; i < m_addressLength; ++i)
+        m_mask4.set(i, true);
+
+    m_network4 = address;
+    m_network4 &= m_mask4;
+}
+
+IPRange::IPRange(const bitset<128>& address, int maskSize) : m_addressLength(128)
+{
+    if (maskSize < 0 || maskSize > m_addressLength)
+        throw ConfigurationException("CIDR prefix length out of range.");
+
+    for (int i = m_addressLength - maskSize; i < m_addressLength; ++i)
+        m_mask6.set(i, true);
+
+    m_network6 = address;
+    m_network6 &= m_mask6;
+}
+
+bool IPRange::contains(const char* address) const
+{
+
+    struct addrinfo* parsed = parseIPAddress(address);
+    if (!parsed)
+        return false;
+    bool ret  = contains(parsed->ai_addr);
+    freeaddrinfo(parsed);
+    return ret;
+}
+
+bool IPRange::contains(const struct sockaddr* address) const
+{
+
+    Category& log = Category::getInstance(SHIBSP_LOGCAT".IPRange");
+
+    if (address->sa_family == AF_INET) {
+        if (m_addressLength != 32)
+            return false;
+        unsigned long raw = 0;
+        memcpy(&raw, &((struct sockaddr_in*)address)->sin_addr, 4);
+        bitset<32> rawbits(ntohl(raw));    // the bitset loads from a host-order variable
+        if (log.isDebugEnabled()) {
+            log.debug(
+                "comparing address (%s) to network (%s) with mask (%s)",
+                rawbits.to_string().c_str(),
+                m_network4.to_string().c_str(),
+                m_mask4.to_string().c_str()
+                );
+        }
+        rawbits &= m_mask4;
+        return (rawbits == m_network4);
+    }
+#ifdef AF_INET6
+    else if (address->sa_family == AF_INET6) {
+        if (m_addressLength != 128)
+            return false;
+        unsigned char raw[16];
+        memcpy(raw, &((struct sockaddr_in6*)address)->sin6_addr, 16);
+        bitset<128> rawbits(raw[0]);
+        for (int i = 1; i < 16; ++i) {
+            rawbits <<= 8;
+            rawbits |= bitset<128>(raw[i]);
+        }
+        if (log.isDebugEnabled()) {
+            log.debug(
+                "comparing address (%s) to network (%s) with mask (%s)",
+                rawbits.to_string().c_str(),
+                m_network6.to_string().c_str(),
+                m_mask6.to_string().c_str()
+                );
+        }
+        rawbits &= m_mask6;
+        return (rawbits == m_network6);
+    }
+#endif
+    return false;
+}
+
+IPRange IPRange::parseCIDRBlock(const char* cidrBlock)
+{
+    string block = cidrBlock;
+    string::size_type sep = block.find("/");
+    if (sep == string::npos) {
+        if (block.find(":") == string::npos)
+            block += "/32";
+        else
+            block += "/128";
+        sep = block.find("/");
+    }
+    struct addrinfo* address = parseIPAddress(block.substr(0, sep).c_str());
+    if (!address)
+        throw ConfigurationException("Unable to parse address in CIDR block.");
+    int maskSize = atoi(block.substr(++sep).c_str());
+    if (address->ai_family == AF_INET) {
+         unsigned long raw = 0;
+         memcpy(&raw, &((struct sockaddr_in*)address->ai_addr)->sin_addr, 4);
+         freeaddrinfo(address);
+         bitset<32> rawbits(ntohl(raw));    // the bitset loads from a host-order variable
+         return IPRange(rawbits, maskSize);
+    }
+#ifdef AF_INET6
+    else if (address->ai_family == AF_INET6) {
+        unsigned char raw[16];
+        memcpy(raw, &((struct sockaddr_in6*)address->ai_addr)->sin6_addr, 16);
+        freeaddrinfo(address);
+        bitset<128> rawbits(raw[0]);
+        for (int i = 1; i < 16; ++i) {
+            rawbits <<= 8;
+            rawbits |= bitset<128>(raw[i]);
+        }
+        return IPRange(rawbits, maskSize);
+    }
+#endif
+    throw ConfigurationException("Unrecognized address type in CIDR block.");
+}
diff --git a/shibsp/util/IPRange.h b/shibsp/util/IPRange.h
new file mode 100644 (file)
index 0000000..d4198bc
--- /dev/null
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+/**
+ * @file shibsp/util/IPRange.h
+ * 
+ * Represents a range of IP addresses.
+ */
+
+#ifndef __shibsp_iprange_h__
+#define __shibsp_iprange_h__
+
+#include <shibsp/base.h>
+
+#include <bitset>
+
+#ifdef WIN32
+# include <winsock.h>
+#elif defined(SHIBSP_HAVE_SYS_SOCKET_H)
+# include <sys/socket.h>
+#endif
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4251 )
+#endif
+
+    /**
+     * Represents a range of IP addresses.
+     */
+    class SHIBSP_API IPRange
+    {
+    public:
+        /**
+         * Constructor.
+         *
+         * @param address address to base the range on; may be the network address or the
+         *                address of a host within the network
+         * @param maskSize the number of bits in the netmask
+         */
+        IPRange(const std::bitset<32>& address, int maskSize);
+
+        /**
+         * Constructor.
+         *
+         * @param address address to base the range on; may be the network address or the
+         *                address of a host within the network
+         * @param maskSize the number of bits in the netmask
+         */
+        IPRange(const std::bitset<128>& address, int maskSize);
+
+        /**
+         * Determines whether the given address is contained in the IP range.
+         *
+         * @param address the address to check
+         *
+         * @return true iff the address is in the range
+         */
+        bool contains(const char* address) const;
+
+        /**
+         * Determines whether the given address is contained in the IP range.
+         *
+         * @param address the address to check
+         *
+         * @return true iff the address is in the range
+         */
+        bool contains(const struct sockaddr* address) const;
+
+        /**
+         * Parses a CIDR block definition in to an IP range.
+         *
+         * @param cidrBlock the CIDR block definition
+         *
+         * @return the resultant IP range
+         */
+        static IPRange parseCIDRBlock(const char* cidrBlock);
+
+    private:
+        /** Number of bits within the address.  32 bits for IPv4 address, 128 bits for IPv6 addresses. */
+        int m_addressLength;
+
+        /** The IP network address for the range. */
+        std::bitset<32> m_network4;
+
+        /** The netmask for the range. */
+        std::bitset<32> m_mask4;
+
+        /** The IP network address for the range. */
+        std::bitset<128> m_network6;
+
+        /** The netmask for the range. */
+        std::bitset<128> m_mask6;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+};
+
+#endif /* __shibsp_iprange_h__ */