From: Scott Cantor Date: Mon, 17 Oct 2011 17:35:31 +0000 (+0000) Subject: https://issues.shibboleth.net/jira/browse/SSPCPP-326 X-Git-Tag: 2.5.0~223 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-sp.git;a=commitdiff_plain;h=2345473e07500a6ccaffef7fb4c02bee5c19effc https://issues.shibboleth.net/jira/browse/SSPCPP-326 --- diff --git a/config_win32.h b/config_win32.h index 5718055..8889cde 100644 --- a/config_win32.h +++ b/config_win32.h @@ -122,6 +122,9 @@ /* Define to 1 if you have the 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" diff --git a/configure.ac b/configure.ac index 3301ae0..29326dd 100644 --- a/configure.ac +++ b/configure.ac @@ -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 header file.])], []) AC_CHECK_FUNCS([strchr strdup strstr timegm gmtime_r strtok_r strcasecmp getpwnam getgrnam]) +AC_CHECK_TYPES([struct sockaddr_storage], [], [], [[#include ]]) # checks for pthreads ACX_PTHREAD([enable_threads="pthread"],[enable_threads="no"]) diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 4b12364..e87a7e8 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -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 diff --git a/shibsp/config_pub.h.in b/shibsp/config_pub.h.in index 8d8a058..9f39699 100644 --- a/shibsp/config_pub.h.in +++ b/shibsp/config_pub.h.in @@ -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 header file. */ +#undef SHIBSP_HAVE_SYS_SOCKET_H diff --git a/shibsp/handler/impl/AssertionLookup.cpp b/shibsp/handler/impl/AssertionLookup.cpp index 94b491f..65312e8 100644 --- a/shibsp/handler/impl/AssertionLookup.cpp +++ b/shibsp/handler/impl/AssertionLookup.cpp @@ -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 processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const; - set m_acl; + vector 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 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 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::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)); diff --git a/shibsp/handler/impl/MetadataGenerator.cpp b/shibsp/handler/impl/MetadataGenerator.cpp index f60cea7..fe541b2 100644 --- a/shibsp/handler/impl/MetadataGenerator.cpp +++ b/shibsp/handler/impl/MetadataGenerator.cpp @@ -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 m_acl; + vector 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 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 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::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)); diff --git a/shibsp/handler/impl/SessionHandler.cpp b/shibsp/handler/impl/SessionHandler.cpp index 57d0fd7..ec0f57b 100644 --- a/shibsp/handler/impl/SessionHandler.cpp +++ b/shibsp/handler/impl/SessionHandler.cpp @@ -32,6 +32,7 @@ #include "SPRequest.h" #include "attribute/Attribute.h" #include "handler/AbstractHandler.h" +#include "util/IPRange.h" #include @@ -71,7 +72,7 @@ namespace shibsp { private: bool m_values; - set m_acl; + vector 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 flag = getBool("showAttributeValues"); @@ -108,10 +124,16 @@ SessionHandler::SessionHandler(const DOMElement* e, const char* appId) pair 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::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; diff --git a/shibsp/handler/impl/StatusHandler.cpp b/shibsp/handler/impl/StatusHandler.cpp index 3bef77c..9a57870 100644 --- a/shibsp/handler/impl/StatusHandler.cpp +++ b/shibsp/handler/impl/StatusHandler.cpp @@ -31,6 +31,7 @@ #include "SPRequest.h" #include "handler/AbstractHandler.h" #include "handler/RemotedHandler.h" +#include "util/IPRange.h" #include "util/CGIParser.h" #include @@ -90,7 +91,7 @@ namespace shibsp { pair processMessage(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const; ostream& systemInfo(ostream& os) const; - set m_acl; + vector 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 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 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::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)); diff --git a/shibsp/remoting/impl/SocketListener.cpp b/shibsp/remoting/impl/SocketListener.cpp index cd53cd2..5553dbf 100644 --- a/shibsp/remoting/impl/SocketListener.cpp +++ b/shibsp/remoting/impl/SocketListener.cpp @@ -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; } diff --git a/shibsp/remoting/impl/SocketListener.h b/shibsp/remoting/impl/SocketListener.h index df4dc23..bbcde89 100644 --- a/shibsp/remoting/impl/SocketListener.h +++ b/shibsp/remoting/impl/SocketListener.h @@ -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 diff --git a/shibsp/remoting/impl/TCPListener.cpp b/shibsp/remoting/impl/TCPListener.cpp index 2daf68a..ff49126 100644 --- a/shibsp/remoting/impl/TCPListener.cpp +++ b/shibsp/remoting/impl/TCPListener.cpp @@ -25,15 +25,23 @@ */ #include "internal.h" +#include "exceptions.h" #include "remoting/impl/SocketListener.h" +#include "util/IPRange.h" #include #include #include +#ifdef WIN32 +# include +# include +#endif + #ifdef HAVE_UNISTD_H # include # include +# include # include # include # include @@ -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 m_acl; + vector 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::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; diff --git a/shibsp/remoting/impl/UnixListener.cpp b/shibsp/remoting/impl/UnixListener.cpp index 554da2e..75c5ff7 100644 --- a/shibsp/remoting/impl/UnixListener.cpp +++ b/shibsp/remoting/impl/UnixListener.cpp @@ -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; } diff --git a/shibsp/shibsp-lite.vcxproj b/shibsp/shibsp-lite.vcxproj index 08e359f..7802ee7 100644 --- a/shibsp/shibsp-lite.vcxproj +++ b/shibsp/shibsp-lite.vcxproj @@ -191,6 +191,7 @@ + @@ -260,6 +261,7 @@ + diff --git a/shibsp/shibsp-lite.vcxproj.filters b/shibsp/shibsp-lite.vcxproj.filters index a01dba9..8d81e3d 100644 --- a/shibsp/shibsp-lite.vcxproj.filters +++ b/shibsp/shibsp-lite.vcxproj.filters @@ -81,6 +81,9 @@ Source Files\util + + Source Files\util + Source Files\util @@ -281,6 +284,9 @@ Header Files\util + + Header Files\util + Header Files\util diff --git a/shibsp/shibsp.vcxproj b/shibsp/shibsp.vcxproj index a6cca62..93eb4bf 100644 --- a/shibsp/shibsp.vcxproj +++ b/shibsp/shibsp.vcxproj @@ -188,6 +188,7 @@ + @@ -302,6 +303,7 @@ + diff --git a/shibsp/shibsp.vcxproj.filters b/shibsp/shibsp.vcxproj.filters index 5278e45..0b2f624 100644 --- a/shibsp/shibsp.vcxproj.filters +++ b/shibsp/shibsp.vcxproj.filters @@ -105,6 +105,9 @@ Source Files\util + + Source Files\util + Source Files\util @@ -452,6 +455,9 @@ Header Files\util + + Header Files\util + Header Files\util diff --git a/shibsp/util/IPRange.cpp b/shibsp/util/IPRange.cpp new file mode 100644 index 0000000..72dd8fc --- /dev/null +++ b/shibsp/util/IPRange.cpp @@ -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 + +#ifdef WIN32 +# include +# include +#else +# include +# include +#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 index 0000000..d4198bc --- /dev/null +++ b/shibsp/util/IPRange.h @@ -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 + +#include + +#ifdef WIN32 +# include +#elif defined(SHIBSP_HAVE_SYS_SOCKET_H) +# include +#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__ */