FastCGI support.
authorScott Cantor <cantor.2@osu.edu>
Fri, 14 Sep 2007 19:03:08 +0000 (19:03 +0000)
committerScott Cantor <cantor.2@osu.edu>
Fri, 14 Sep 2007 19:03:08 +0000 (19:03 +0000)
Shibboleth.dsw
fastcgi/.gitignore [new file with mode: 0644]
fastcgi/shibauthorizer.cpp [new file with mode: 0644]
fastcgi/shibauthorizer.dsp [new file with mode: 0644]
fastcgi/shibresponder.cpp [new file with mode: 0644]
fastcgi/shibresponder.dsp [new file with mode: 0644]

index 24656f0..ff84911 100644 (file)
@@ -3,7 +3,7 @@ Microsoft Developer Studio Workspace File, Format Version 6.00
 
 ###############################################################################
 
-Project: "adfs"=.\adfs\adfs.dsp - Package Owner=<4>
+Project: "adfs"=".\adfs\adfs.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -21,7 +21,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "isapi_shib"=.\isapi_shib\isapi_shib.dsp - Package Owner=<4>
+Project: "isapi_shib"=".\isapi_shib\isapi_shib.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -39,7 +39,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "mod_shib13"=.\apache\mod_shib13.dsp - Package Owner=<4>
+Project: "mod_shib13"=".\apache\mod_shib13.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -57,7 +57,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "mod_shib20"=.\apache\mod_shib20.dsp - Package Owner=<4>
+Project: "mod_shib20"=".\apache\mod_shib20.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -75,7 +75,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "mod_shib22"=.\apache\mod_shib22.dsp - Package Owner=<4>
+Project: "mod_shib22"=".\apache\mod_shib22.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -93,7 +93,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "nsapi_shib"=.\nsapi_shib\nsapi_shib.dsp - Package Owner=<4>
+Project: "nsapi_shib"=".\nsapi_shib\nsapi_shib.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -111,7 +111,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "oncrpc"=.\oncrpc\oncrpc.dsp - Package Owner=<4>
+Project: "oncrpc"=".\oncrpc\oncrpc.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -123,7 +123,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "posttest"=.\posttest\posttest.dsp - Package Owner=<4>
+Project: "posttest"=".\posttest\posttest.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -138,7 +138,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "shar"=.\shar\shar.dsp - Package Owner=<4>
+Project: "shar"=".\shar\shar.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -159,7 +159,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "shib"=.\shib\shib.dsp - Package Owner=<4>
+Project: "shib"=".\shib\shib.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -189,6 +189,36 @@ Package=<4>
 
 ###############################################################################
 
+Project: "shibauthorizer"=".\fastcgi\shibauthorizer.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name shibtarget
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "shibresponder"=".\fastcgi\shibresponder.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name shibtarget
+    End Project Dependency
+}}}
+
+###############################################################################
+
 Project: "shibtarget"=".\shib-target\shibtarget.dsp" - Package Owner=<4>
 
 Package=<5>
@@ -207,7 +237,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "shibtest"=.\shibtest\shibtest.dsp - Package Owner=<4>
+Project: "shibtest"=".\shibtest\shibtest.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -225,7 +255,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "siterefresh"=.\siterefresh\siterefresh.dsp - Package Owner=<4>
+Project: "siterefresh"=".\siterefresh\siterefresh.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -243,7 +273,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "testclient"=.\shar\testclient.dsp - Package Owner=<4>
+Project: "testclient"=".\shar\testclient.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -264,7 +294,7 @@ Package=<4>
 
 ###############################################################################
 
-Project: "xmlproviders"=.\xmlproviders\xmlproviders.dsp - Package Owner=<4>
+Project: "xmlproviders"=".\xmlproviders\xmlproviders.dsp" - Package Owner=<4>
 
 Package=<5>
 {{{
@@ -286,10 +316,6 @@ Global:
 
 Package=<5>
 {{{
-    begin source code control
-    "$/NSAPI", WNAAAAAA
-    ..\..\..\..\documents and settings\cantor.2\my documents\projects\nsapi
-    end source code control
 }}}
 
 Package=<3>
diff --git a/fastcgi/.gitignore b/fastcgi/.gitignore
new file mode 100644 (file)
index 0000000..3f85816
--- /dev/null
@@ -0,0 +1,5 @@
+/shibresponder___Win32_Debug
+/shibauthorizer___Win32_Release
+/shibauthorizer___Win32_Debug
+/shibresponder___Win32_Release
+/*.plg
diff --git a/fastcgi/shibauthorizer.cpp b/fastcgi/shibauthorizer.cpp
new file mode 100644 (file)
index 0000000..db16d85
--- /dev/null
@@ -0,0 +1,350 @@
+/*\r
+ *  Copyright 2001-2007 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* shibauthorizer.cpp - Shibboleth FastCGI Authorizer\r
+\r
+   Andre Cruz\r
+*/\r
+\r
+// SAML Runtime\r
+#include <saml/saml.h>\r
+#include <shib-target/shib-target.h>\r
+\r
+#include <stdlib.h>\r
+#ifdef HAVE_UNISTD_H\r
+# include <unistd.h>\r
+# include <sys/mman.h>\r
+#endif\r
+#include <fcgio.h>\r
+\r
+using namespace shibtarget;\r
+using namespace saml;\r
+using namespace std;\r
+\r
+typedef enum {\r
+    SHIB_RETURN_OK,\r
+    SHIB_RETURN_KO,\r
+    SHIB_RETURN_DONE\r
+} shib_return_t;\r
+\r
+class ShibTargetFCGIAuth : public ShibTarget\r
+{\r
+    FCGX_Request* m_req;\r
+    string m_cookie;\r
+public:\r
+    map<string,string> m_headers;\r
+\r
+    ShibTargetFCGIAuth(FCGX_Request* req) : m_req(req) {\r
+        char* server_name_str = FCGX_GetParam("SHIBSP_SERVER_NAME", req->envp);\r
+        if (!server_name_str || !*server_name_str)\r
+            server_name_str = FCGX_GetParam("SERVER_NAME", req->envp);\r
+\r
+        char* server_port_str = FCGX_GetParam("SHIBSP_SERVER_PORT", req->envp);\r
+        if (!server_port_str || !*server_port_str)\r
+            server_port_str = FCGX_GetParam("SERVER_PORT", req->envp);\r
+        int server_port = strtol(server_port_str, &server_port_str, 10);\r
+        if (*server_port_str) {\r
+            cerr << "can't parse SERVER_PORT (" << FCGX_GetParam("SERVER_PORT", req->envp) << ")" << endl;\r
+            throw SAMLException("Unable to determine server port.");\r
+        }\r
+\r
+        char* server_scheme_str = FCGX_GetParam("SHIBSP_SERVER_SCHEME", req->envp);\r
+        if (!server_scheme_str || !*server_scheme_str)\r
+            server_scheme_str = (server_port == 443 || server_port == 8443) ? "https" : "http";\r
+\r
+        char* request_uri_str = FCGX_GetParam("REQUEST_URI", req->envp);\r
+        char* content_type_str = FCGX_GetParam("CONTENT_TYPE", req->envp);\r
+        char* remote_addr_str = FCGX_GetParam("REMOTE_ADDR", req->envp);\r
+        char* request_method_str = FCGX_GetParam("REQUEST_METHOD", req->envp);\r
+\r
+        init(server_scheme_str,\r
+             server_name_str,\r
+             server_port,\r
+             request_uri_str,\r
+             content_type_str ? content_type_str : "",\r
+             remote_addr_str,\r
+             request_method_str\r
+             );\r
+    }\r
+\r
+    ~ShibTargetFCGIAuth() { }\r
+\r
+    virtual void log(ShibLogLevel level, const string& msg) {\r
+        ShibTarget::log(level,msg);\r
+        if (level == LogLevelError)\r
+            cerr << "shib: " << msg;\r
+    }\r
+  \r
+    virtual string getCookies(void) const {\r
+        char* cookie = FCGX_GetParam("HTTP_COOKIE", m_req->envp);\r
+        return cookie ? cookie : "";\r
+    }\r
+  \r
+    virtual void setCookie(const string &name, const string &value) {\r
+        m_cookie += "Set-Cookie: " + name + "=" + value + "\r\n";\r
+    }\r
+\r
+      virtual string getArgs(void) {\r
+        char* args = FCGX_GetParam("QUERY_STRING", m_req->envp);\r
+        return args ? args : "";\r
+    }\r
+\r
+    virtual string getPostData(void) {\r
+        throw SAMLException("getPostData not implemented by FastCGI authorizer.");\r
+    }\r
+\r
+    virtual void clearHeader(const string& name) {\r
+        // no need, since request headers turn into actual environment variables\r
+    }\r
+  \r
+    virtual void setHeader(const string& name, const string &value) {\r
+        m_headers[name] = value;\r
+    }\r
+\r
+    virtual string getHeader(const string& name) {\r
+        if (m_headers.find(name) != m_headers.end())\r
+            return m_headers[name];\r
+        else\r
+            return "";\r
+    }\r
+\r
+    virtual void setRemoteUser(const string& user) {\r
+        m_headers["REMOTE_USER"] = user;\r
+    }\r
+\r
+    virtual string getRemoteUser(void) {\r
+        if (m_headers.find("REMOTE_USER") != m_headers.end())\r
+            return m_headers["REMOTE_USER"];\r
+        else {\r
+            char* remote_user = FCGX_GetParam("REMOTE_USER", m_req->envp);\r
+            if (remote_user)\r
+                return remote_user;\r
+        }\r
+        return "";\r
+    }\r
+\r
+    virtual void* sendPage(\r
+        const string& msg,\r
+        int code=200,\r
+        const string& content_type="text/html",\r
+        const Iterator<header_t>& headers=EMPTY(header_t)) {\r
+\r
+        string hdr = m_cookie + "Connection: close\r\nContent-type: " + content_type + "\r\n";\r
+        while (headers.hasNext()) {\r
+            const header_t& h=headers.next();\r
+            hdr += h.first + ": " + h.second + "\r\n";\r
+        }\r
+\r
+        // We can't return 200 OK here or else the filter is bypassed\r
+        // so custom Shib errors will get turned into a generic page.\r
+        const char* codestr="Status: 500 Server Error";\r
+        switch (code) {\r
+            case 403:   codestr="Status: 403 Forbidden"; break;\r
+            case 404:   codestr="Status: 404 Not Found"; break;\r
+        }\r
+\r
+        cout << codestr << "\r\n" << hdr << "\r\n" << msg;\r
+        return (void*)SHIB_RETURN_DONE;\r
+    }\r
+\r
+    virtual void* sendRedirect(const string& url) {\r
+        cout << "Status: 302 Please Wait" << "\r\n"\r
+             << "Location: " << url << "\r\n"\r
+             <<  m_cookie << "\r\n"\r
+             << "<HTML><BODY>Redirecting...</BODY></HTML>";\r
+        return (void*)SHIB_RETURN_DONE;\r
+    }\r
+\r
+    virtual void* returnDecline(void) { \r
+        return (void*)SHIB_RETURN_KO;\r
+    }\r
+\r
+    virtual void* returnOK(void) {\r
+        return (void*)SHIB_RETURN_OK;\r
+    }\r
+};\r
+\r
+static void print_ok(const map<string,string>& headers)\r
+{\r
+    cout << "Status: 200 OK" << "\r\n";\r
+    for (map<string,string>::const_iterator iter = headers.begin(); iter != headers.end(); iter++) {\r
+        cout << "Variable-" << iter->first << ": " << iter->second << "\r\n";\r
+    }\r
+    cout << "\r\n";\r
+}\r
+\r
+static void print_error(const char* msg)\r
+{\r
+    cout << "Status: 500 Server Error" << "\r\n\r\n" << msg;\r
+}\r
+\r
+int main(void)\r
+{\r
+    char* shib_config = getenv("SHIB_CONFIG");\r
+    char* shib_schema = getenv("SHIB_SCHEMA");\r
+    if ((shib_config == NULL) || (shib_schema == NULL)) {\r
+      cerr << "SHIB_CONFIG or SHIB_SCHEMA not initialized!" << endl;\r
+      exit(1);\r
+    }\r
+    cerr << "SHIB_CONFIG = " << shib_config << endl\r
+         << "SHIB_SCHEMA = " << shib_schema << endl;\r
+\r
+    ShibTargetConfig* g_Config;\r
+\r
+    try {\r
+        g_Config = &ShibTargetConfig::getConfig();\r
+        g_Config->setFeatures(\r
+            ShibTargetConfig::Listener |\r
+            ShibTargetConfig::Metadata |\r
+            ShibTargetConfig::AAP |\r
+            ShibTargetConfig::RequestMapper |\r
+            ShibTargetConfig::LocalExtensions |\r
+            ShibTargetConfig::Logging\r
+            );\r
+        if (!g_Config->init(shib_schema)) {\r
+            cerr << "failed to initialize Shibboleth libraries" << endl;\r
+            exit(1);\r
+        }\r
+        \r
+        if (!g_Config->load(shib_config)) {\r
+            cerr << "failed to load Shibboleth configuration" << endl;\r
+            exit(1);\r
+        }\r
+    }\r
+    catch (...) {\r
+        cerr << "exception while initializing Shibboleth configuration" << endl;\r
+        exit(1);\r
+    }\r
+\r
+    streambuf* cout_streambuf = cout.rdbuf();\r
+    streambuf* cerr_streambuf = cerr.rdbuf();\r
+\r
+    FCGX_Request request;\r
+\r
+    FCGX_Init();\r
+    FCGX_InitRequest(&request, 0, 0);\r
+    \r
+    cout << "Shibboleth initialization complete. Starting request loop." << endl;\r
+    while (FCGX_Accept_r(&request) == 0)\r
+    {\r
+        // Note that the default bufsize (0) will cause the use of iostream\r
+        // methods that require positioning (such as peek(), seek(),\r
+        // unget() and putback()) to fail (in favour of more efficient IO).\r
+        fcgi_streambuf cout_fcgi_streambuf(request.out);\r
+        fcgi_streambuf cerr_fcgi_streambuf(request.err);\r
+\r
+        cout.rdbuf(&cout_fcgi_streambuf);\r
+        cerr.rdbuf(&cerr_fcgi_streambuf);\r
+\r
+        try {\r
+            saml::NDC ndc("FastCGI shibauthorizer");\r
+            ShibTargetFCGIAuth sta(&request);\r
+          \r
+            pair<bool,void*> res = sta.doCheckAuthN();\r
+            if (res.first) {\r
+#ifdef _DEBUG\r
+                cerr << "shib: doCheckAuthN handled the request" << endl;\r
+#endif\r
+                switch((int)res.second) {\r
+                    case SHIB_RETURN_OK:\r
+                        print_ok(sta.m_headers);\r
+                        continue;\r
+              \r
+                    case SHIB_RETURN_KO:\r
+                        print_ok(sta.m_headers);\r
+                        continue;\r
+\r
+                    case SHIB_RETURN_DONE:\r
+                        continue;\r
+              \r
+                    default:\r
+                        cerr << "shib: doCheckAuthN returned an unexpected result: " << (int)res.second << endl;\r
+                        print_error("<html><body>FastCGI Shibboleth authorizer returned an unexpected result.</body></html>");\r
+                        continue;\r
+                }\r
+            }\r
+          \r
+            res = sta.doExportAssertions();\r
+            if (res.first) {\r
+#ifdef _DEBUG\r
+                cerr << "shib: doExportAssertions handled request" << endl;\r
+#endif\r
+                switch((int)res.second) {\r
+                    case SHIB_RETURN_OK:\r
+                        print_ok(sta.m_headers);\r
+                        continue;\r
+              \r
+                    case SHIB_RETURN_KO:\r
+                        print_ok(sta.m_headers);\r
+                        continue;\r
+\r
+                    case SHIB_RETURN_DONE:\r
+                        continue;\r
+              \r
+                    default:\r
+                        cerr << "shib: doExportAssertions returned an unexpected result: " << (int)res.second << endl;\r
+                        print_error("<html><body>FastCGI Shibboleth authorizer returned an unexpected result.</body></html>");\r
+                        continue;\r
+                }\r
+            }\r
+\r
+            res = sta.doCheckAuthZ();\r
+            if (res.first) {\r
+#ifdef _DEBUG\r
+                cerr << "shib: doCheckAuthZ handled request" << endl;\r
+#endif\r
+                switch((int)res.second) {\r
+                    case SHIB_RETURN_OK:\r
+                        print_ok(sta.m_headers);\r
+                        continue;\r
+              \r
+                    case SHIB_RETURN_KO:\r
+                        print_ok(sta.m_headers);\r
+                        continue;\r
+\r
+                    case SHIB_RETURN_DONE:\r
+                        continue;\r
+              \r
+                    default:\r
+                        cerr << "shib: doCheckAuthZ returned an unexpected result: " << (int)res.second << endl;\r
+                        print_error("<html><body>FastCGI Shibboleth authorizer returned an unexpected result.</body></html>");\r
+                        continue;\r
+                }\r
+            }\r
+\r
+            print_ok(sta.m_headers);\r
+          \r
+        }\r
+        catch (SAMLException& e) {\r
+            cerr << "shib: FastCGI authorizer caught an exception: " << e.what() << endl;\r
+            print_error("<html><body>FastCGI Shibboleth authorizer caught an exception, check log for details.</body></html>");\r
+        }\r
+\r
+        // If the output streambufs had non-zero bufsizes and\r
+        // were constructed outside of the accept loop (i.e.\r
+        // their destructor won't be called here), they would\r
+        // have to be flushed here.\r
+    }\r
+    cout << "Request loop ended." << endl;\r
+\r
+    cout.rdbuf(cout_streambuf);\r
+    cerr.rdbuf(cerr_streambuf);\r
+\r
+    if (g_Config)\r
+        g_Config->shutdown();\r
\r
+    return 0;\r
+}\r
diff --git a/fastcgi/shibauthorizer.dsp b/fastcgi/shibauthorizer.dsp
new file mode 100644 (file)
index 0000000..0d030a2
--- /dev/null
@@ -0,0 +1,90 @@
+# Microsoft Developer Studio Project File - Name="shibauthorizer" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=shibauthorizer - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "shibauthorizer.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "shibauthorizer.mak" CFG="shibauthorizer - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "shibauthorizer - Win32 Release" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "shibauthorizer - Win32 Debug" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "shibauthorizer - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "shibauthorizer___Win32_Release"\r
+# PROP Intermediate_Dir "shibauthorizer___Win32_Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "." /I ".." /I "..\..\cpp-opensaml1" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD BASE RSC /l 0x409 /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 xerces-c_2.lib saml_5.lib libfcgi.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\cpp-opensaml1\saml\Release" /libpath:"\fcgi-2.4.0\libfcgi\Release"\r
+\r
+!ELSEIF  "$(CFG)" == "shibauthorizer - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "shibauthorizer___Win32_Debug"\r
+# PROP BASE Intermediate_Dir "shibauthorizer___Win32_Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "shibauthorizer___Win32_Debug"\r
+# PROP Intermediate_Dir "shibauthorizer___Win32_Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I "." /I ".." /I "..\..\cpp-opensaml1" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c\r
+# ADD BASE RSC /l 0x409 /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 xerces-c_2D.lib saml_5D.lib libfcgi.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\cpp-opensaml1\saml\Debug" /libpath:"\fcgi-2.4.0\libfcgi\Debug"\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "shibauthorizer - Win32 Release"\r
+# Name "shibauthorizer - Win32 Debug"\r
+# Begin Source File\r
+\r
+SOURCE=.\shibauthorizer.cpp\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/fastcgi/shibresponder.cpp b/fastcgi/shibresponder.cpp
new file mode 100644 (file)
index 0000000..682dcd5
--- /dev/null
@@ -0,0 +1,350 @@
+/*\r
+ *  Copyright 2001-2007 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/* shibresponder.cpp - Shibboleth FastCGI Responder/Handler\r
+\r
+   Andre Cruz\r
+*/\r
+\r
+// SAML Runtime\r
+#include <saml/saml.h>\r
+#include <shib-target/shib-target.h>\r
+\r
+#include <stdlib.h>\r
+#ifdef HAVE_UNISTD_H\r
+# include <unistd.h>\r
+# include <sys/mman.h>\r
+#endif\r
+#include <fcgio.h>\r
+\r
+using namespace shibtarget;\r
+using namespace saml;\r
+using namespace std;\r
+\r
+typedef enum {\r
+    SHIB_RETURN_OK,\r
+    SHIB_RETURN_KO,\r
+    SHIB_RETURN_DONE\r
+} shib_return_t;\r
+\r
+class ShibTargetFCGI : public ShibTarget\r
+{\r
+    FCGX_Request* m_req;\r
+    char* m_body;\r
+    string m_cookie;\r
+    map<string, string> m_headers;\r
+\r
+public:\r
+    ShibTargetFCGI(FCGX_Request* req, char* post_data) : m_req(req), m_body(post_data) {\r
+\r
+        char* server_name_str = FCGX_GetParam("SHIBSP_SERVER_NAME", req->envp);\r
+        if (!server_name_str || !*server_name_str)\r
+            server_name_str = FCGX_GetParam("SERVER_NAME", req->envp);\r
+\r
+        char* server_port_str = FCGX_GetParam("SHIBSP_SERVER_PORT", req->envp);\r
+        if (!server_port_str || !*server_port_str)\r
+            server_port_str = FCGX_GetParam("SERVER_PORT", req->envp);\r
+        int server_port = strtol(server_port_str, &server_port_str, 10);\r
+        if (*server_port_str) {\r
+            cerr << "can't parse SERVER_PORT (" << FCGX_GetParam("SERVER_PORT", req->envp) << ")" << endl;\r
+            throw SAMLException("Unable to determine server port.");\r
+        }\r
+\r
+        char* server_scheme_str = FCGX_GetParam("SHIBSP_SERVER_SCHEME", req->envp);\r
+        if (!server_scheme_str || !*server_scheme_str)\r
+            server_scheme_str = (server_port == 443 || server_port == 8443) ? "https" : "http";\r
+\r
+        char* request_uri_str = FCGX_GetParam("REQUEST_URI", req->envp);\r
+        char* content_type_str = FCGX_GetParam("CONTENT_TYPE", req->envp);\r
+        char* remote_addr_str = FCGX_GetParam("REMOTE_ADDR", req->envp);\r
+        char* request_method_str = FCGX_GetParam("REQUEST_METHOD", req->envp);\r
+\r
+#ifdef _DEBUG\r
+        cerr << "server_name = " << server_name_str << endl\r
+             << "server_port = " << server_port << endl\r
+             << "request_uri_str = " << request_uri_str << endl\r
+             << "content_type = " << content_type_str << endl\r
+             << "remote_address = " << remote_addr_str << endl\r
+             << "request_method = " << request_method_str << endl;\r
+#endif\r
+\r
+        init(server_scheme_str,\r
+             server_name_str,\r
+             server_port,\r
+             request_uri_str,\r
+             content_type_str ? content_type_str : "",\r
+             remote_addr_str,\r
+             request_method_str\r
+             );\r
+    }\r
+\r
+    ~ShibTargetFCGI() { }\r
+\r
+    virtual void log(ShibLogLevel level, const string& msg) {\r
+        ShibTarget::log(level,msg);\r
+    \r
+        if (level == LogLevelError)\r
+            cerr << "shib: " << msg;\r
+    }\r
+  \r
+    virtual string getCookies(void) const {\r
+        char * cookie = FCGX_GetParam("HTTP_COOKIE", m_req->envp);\r
+        return cookie ? cookie : "";\r
+    }\r
+  \r
+    virtual void setCookie(const string& name, const string& value) {\r
+        m_cookie += "Set-Cookie: " + name + "=" + value + "\r\n";\r
+    }\r
+\r
+    virtual string getArgs(void) {\r
+        char * args = FCGX_GetParam("QUERY_STRING", m_req->envp);\r
+        return args ? args : "";\r
+    }\r
+\r
+    virtual string getPostData(void) {\r
+        return m_body ? m_body : "";\r
+    }\r
+\r
+    virtual void clearHeader(const string &name) {\r
+        throw SAMLException("clearHeader not implemented by FastCGI responder.");\r
+    }\r
+  \r
+    virtual void setHeader(const string &name, const string &value) {\r
+        throw SAMLException("setHeader not implemented by FastCGI responder.");\r
+    }\r
+\r
+    virtual string getHeader(const string &name) {\r
+        throw SAMLException("getHeader not implemented by FastCGI responder.");\r
+    }\r
+\r
+    virtual void setRemoteUser(const string &user) {\r
+        throw SAMLException("setRemoteUser not implemented by FastCGI responder.");\r
+    }\r
+\r
+    virtual string getRemoteUser(void) {\r
+        throw SAMLException("getRemoteUser not implemented by FastCGI responder.");\r
+    }\r
+\r
+    virtual void* sendPage(\r
+        const string& msg,\r
+        int code=200,\r
+        const string& content_type="text/html",\r
+        const Iterator<header_t>& headers=EMPTY(header_t)) {\r
+\r
+        string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n" + m_cookie;\r
+        while (headers.hasNext()) {\r
+            const header_t& h=headers.next();\r
+            hdr += h.first + ": " + h.second + "\r\n";\r
+        }\r
+\r
+        const char* codestr="Status: 200 OK";\r
+        switch (code) {\r
+            case 500:   codestr="Status: 500 Server Error"; break;\r
+            case 403:   codestr="Status: 403 Forbidden"; break;\r
+            case 404:   codestr="Status: 404 Not Found"; break;\r
+        }\r
+\r
+        cout << codestr << "\r\n" << hdr << m_cookie << "\r\n" << msg;\r
+        return (void*)SHIB_RETURN_DONE;\r
+    }\r
+\r
+    virtual void* sendRedirect(const string& url) {\r
+        cout << "Status: 302 Please Wait" << "\r\n" << "Location: " << url << "\r\n" << m_cookie << "\r\n"\r
+            << "<HTML><BODY>Redirecting...</BODY></HTML>";\r
+        return (void*)SHIB_RETURN_DONE;\r
+    }\r
+\r
+    virtual void* returnDecline(void) { \r
+        return (void*)SHIB_RETURN_KO;\r
+    }\r
+\r
+    virtual void* returnOK(void) {\r
+        return (void*)SHIB_RETURN_OK;\r
+    }\r
+};\r
+\r
+// Maximum number of bytes allowed to be read from stdin\r
+static const unsigned long STDIN_MAX = 1000000;\r
+\r
+static long gstdin(FCGX_Request* request, char** content)\r
+{\r
+    char* clenstr = FCGX_GetParam("CONTENT_LENGTH", request->envp);\r
+    unsigned long clen = STDIN_MAX;\r
+\r
+    if (clenstr) {\r
+        clen = strtol(clenstr, &clenstr, 10);\r
+        if (*clenstr) {\r
+            cerr << "can't parse CONTENT_LENGTH (" << FCGX_GetParam("CONTENT_LENGTH", request->envp) << ")" << endl;\r
+            clen = STDIN_MAX;\r
+        }\r
+\r
+        // *always* put a cap on the amount of data that will be read\r
+        if (clen > STDIN_MAX)\r
+            clen = STDIN_MAX;\r
+\r
+        *content = new char[clen];\r
+\r
+        cin.read(*content, clen);\r
+        clen = cin.gcount();\r
+    }\r
+    else {\r
+        // *never* read stdin when CONTENT_LENGTH is missing or unparsable\r
+        *content = 0;\r
+        clen = 0;\r
+    }\r
+\r
+    // Chew up any remaining stdin - this shouldn't be necessary\r
+    // but is because mod_fastcgi doesn't handle it correctly.\r
+\r
+    // ignore() doesn't set the eof bit in some versions of glibc++\r
+    // so use gcount() instead of eof()...\r
+    do cin.ignore(1024); while (cin.gcount() == 1024);\r
+\r
+    return clen;\r
+}\r
+\r
+static void print_ok() {\r
+    cout << "Status: 200 OK" << "\r\n\r\n";\r
+}\r
+\r
+static void print_error(const char* msg) {\r
+    cout << "Status: 500 Server Error" << "\r\n\r\n" << msg;\r
+}\r
+\r
+int main(void)\r
+{\r
+    char* shib_config = getenv("SHIB_CONFIG");\r
+    char* shib_schema = getenv("SHIB_SCHEMA");\r
+    if ((shib_config == NULL) || (shib_schema == NULL)) {\r
+        cerr << "SHIB_CONFIG or SHIB_SCHEMA not set." << endl;\r
+        exit(1);\r
+    }\r
+    cerr << "SHIB_CONFIG = " << shib_config << endl\r
+         << "SHIB_SCHEMA = " << shib_schema << endl;\r
+\r
+    ShibTargetConfig* g_Config;\r
+\r
+    try {\r
+        g_Config = &ShibTargetConfig::getConfig();\r
+        g_Config->setFeatures(\r
+            ShibTargetConfig::Listener |\r
+            ShibTargetConfig::Metadata |\r
+            ShibTargetConfig::RequestMapper |\r
+            ShibTargetConfig::LocalExtensions |\r
+            ShibTargetConfig::Logging\r
+            );\r
+        if (!g_Config->init(shib_schema)) {\r
+            cerr << "failed to initialize Shibboleth libraries" << endl;\r
+            exit(1);\r
+        }\r
+        \r
+        if (!g_Config->load(shib_config)) {\r
+            cerr << "failed to load Shibboleth configuration" << endl;\r
+            exit(1);\r
+        }\r
+    }\r
+    catch (...) {\r
+        cerr << "exception while initializing Shibboleth configuration" << endl;\r
+        exit(1);\r
+    }\r
+\r
+    streambuf* cin_streambuf  = cin.rdbuf();\r
+    streambuf* cout_streambuf = cout.rdbuf();\r
+    streambuf* cerr_streambuf = cerr.rdbuf();\r
+\r
+    FCGX_Request request;\r
+\r
+    FCGX_Init();\r
+    FCGX_InitRequest(&request, 0, 0);\r
+    \r
+    cout << "Shibboleth initialization complete. Starting request loop." << endl;\r
+    while (FCGX_Accept_r(&request) == 0) {\r
+        // Note that the default bufsize (0) will cause the use of iostream\r
+        // methods that require positioning (such as peek(), seek(),\r
+        // unget() and putback()) to fail (in favour of more efficient IO).\r
+        fcgi_streambuf cin_fcgi_streambuf(request.in);\r
+        fcgi_streambuf cout_fcgi_streambuf(request.out);\r
+        fcgi_streambuf cerr_fcgi_streambuf(request.err);\r
+\r
+        cin.rdbuf(&cin_fcgi_streambuf);\r
+        cout.rdbuf(&cout_fcgi_streambuf);\r
+        cerr.rdbuf(&cerr_fcgi_streambuf);\r
+\r
+        // Although FastCGI supports writing before reading,\r
+        // many http clients (browsers) don't support it (so\r
+        // the connection deadlocks until a timeout expires!).\r
+        char* content;\r
+        gstdin(&request, &content);\r
+\r
+        try {\r
+            saml::NDC ndc("FastCGI shibresponder");\r
+            ShibTargetFCGI stf(&request, content);\r
+          \r
+            pair<bool,void*> res = stf.doHandler();\r
+            if (res.first) {\r
+#ifdef _DEBUG\r
+                cerr << "shib: doHandler handled the request" << endl;\r
+#endif\r
+                switch((int)res.second) {\r
+                    case SHIB_RETURN_OK:\r
+                        print_ok();\r
+                        break;\r
+              \r
+                    case SHIB_RETURN_KO:\r
+                        cerr << "shib: doHandler failed to handle the request" << endl;\r
+                        print_error("<html><body>FastCGI Shibboleth responder should only be used for Shibboleth protocol requests.</body></html>");\r
+                        break;\r
+\r
+                    case SHIB_RETURN_DONE:\r
+                        // response already handled\r
+                        break;\r
+              \r
+                    default:\r
+                        cerr << "shib: doHandler returned an unexpected result: " << (int)res.second << endl;\r
+                        print_error("<html><body>FastCGI Shibboleth responder returned an unexpected result.</body></html>");\r
+                        break;\r
+                }\r
+            }\r
+            else {\r
+                cerr << "shib: doHandler failed to handle request." << endl;\r
+                print_error("<html><body>FastCGI Shibboleth responder failed to process request.</body></html>");\r
+            }          \r
+          \r
+        }\r
+        catch (SAMLException& e) {\r
+            cerr << "shib: FastCGI responder caught an exception: " << e.what() << endl;\r
+            print_error("<html><body>FastCGI Shibboleth responder caught an exception, check log for details.</body></html>");\r
+        }\r
+\r
+        delete[] content;\r
+\r
+        // If the output streambufs had non-zero bufsizes and\r
+        // were constructed outside of the accept loop (i.e.\r
+        // their destructor won't be called here), they would\r
+        // have to be flushed here.\r
+    }\r
+\r
+    cout << "Request loop ended." << endl;\r
+\r
+    cin.rdbuf(cin_streambuf);\r
+    cout.rdbuf(cout_streambuf);\r
+    cerr.rdbuf(cerr_streambuf);\r
+\r
+    if (g_Config)\r
+        g_Config->shutdown();\r
\r
+    return 0;\r
+}\r
diff --git a/fastcgi/shibresponder.dsp b/fastcgi/shibresponder.dsp
new file mode 100644 (file)
index 0000000..dca121e
--- /dev/null
@@ -0,0 +1,90 @@
+# Microsoft Developer Studio Project File - Name="shibresponder" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=shibresponder - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "shibresponder.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "shibresponder.mak" CFG="shibresponder - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "shibresponder - Win32 Release" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "shibresponder - Win32 Debug" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "shibresponder - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "shibresponder___Win32_Release"\r
+# PROP Intermediate_Dir "shibresponder___Win32_Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "." /I ".." /I "..\..\cpp-opensaml1" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD BASE RSC /l 0x409 /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 xerces-c_2.lib saml_5.lib libfcgi.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\cpp-opensaml1\saml\Release" /libpath:"\fcgi-2.4.0\libfcgi\Release"\r
+\r
+!ELSEIF  "$(CFG)" == "shibresponder - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "shibresponder___Win32_Debug"\r
+# PROP BASE Intermediate_Dir "shibresponder___Win32_Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "shibresponder___Win32_Debug"\r
+# PROP Intermediate_Dir "shibresponder___Win32_Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ  /c\r
+# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I "." /I ".." /I "..\..\cpp-opensaml1" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ  /c\r
+# ADD BASE RSC /l 0x409 /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib  kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 xerces-c_2D.lib saml_5D.lib libfcgi.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\cpp-opensaml1\saml\Debug" /libpath:"\fcgi-2.4.0\libfcgi\Debug"\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "shibresponder - Win32 Release"\r
+# Name "shibresponder - Win32 Debug"\r
+# Begin Source File\r
+\r
+SOURCE=.\shibresponder.cpp\r
+# End Source File\r
+# End Target\r
+# End Project\r