shibboleth-dev - LightTPD SP module v0.2
Subject: Shibboleth Developers
List archive
- From: André Cruz <>
- To:
- Subject: LightTPD SP module v0.2
- Date: Tue, 09 Jan 2007 12:42:49 +0000
Hello.
This is the "final" version for now. It works and is documented in the
source file.
Regarding the contributor's agreement... I didn't find an electronic
address I can send it after I fill it. Does anyone know where to send it?
Thanks,
André
#include <ctype.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <errno.h> #include <fcntl.h> extern "C" { #include "base.h" #include "log.h" #include "buffer.h" #include "plugin.h" #include "inet_ntop_cache.h" #include "response.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif } // SAML Runtime #include <saml/saml.h> #include <shib/shib.h> #include <shib/shib-threads.h> #include <shib-target/shib-target.h> #include <xercesc/util/regx/RegularExpression.hpp> using namespace std; using namespace saml; using namespace shibboleth; using namespace shibtarget; /* Shibboleth 1.3 plug-in for Lighty v0.2 - server.name has to be set! alias for error page logo and css should be set for default pages alias.url += ( "/shibboleth-sp/logo.jpg" => "/usr/local/shib-sp/doc/shibboleth/logo.jpg", "/shibboleth-sp/main.css" => "/usr/local/shib-sp/doc/shibboleth/main.css" ) shib.disable - disable all shibboleth processing. Can be used with conditionals. If the URL of the shib handler is disabled then nothing will work! shib.config - shibboleth config file. Mandatory. shib.schema.dir - schema dir. Mandatory. Lighttpd >= 1.4.13 is needed Patch for lighttpd bug #289 is needed to compile the module. Compile: /bin/bash ../libtool --mode=compile --tag=CXX g++ -DHAVE_CONFIG_H -DLIBRARY_DIR="\"/usr/local/lib\"" -I. -I. -I.. -I/usr/local/shib-sp/include -D_REENTRANT -D__EXTENSIONS__ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES -g -O2 -Wall -W -Wshadow -MT mod_shib.lo -MD -MP -MF ".deps/mod_shib.Tpo" -c -o mod_shib.lo mod_shib.c Link: /bin/bash ../libtool --tag=CXX --mode=link g++ -g -O2 -Wall -W -Wshadow -o mod_shib.la -rpath /usr/local/lib -module -export-dynamic -avoid-version -no-undefined -L/usr/local/shib-sp/lib mod_shib.lo ../../shibboleth-1.3/shib/libshib.la ../../shibboleth-1.3/shib-target/libshib-target.la -lsaml -lxml-security-c -lxerces-c -lssl -lcrypto -ldl */ typedef enum { SHIB_RETURN_OK, SHIB_RETURN_KO, SHIB_RETURN_DONE } shib_return_t; typedef struct { unsigned short shib_disable; buffer *shib_config; buffer *shib_schema_dir; } plugin_config; typedef struct { PLUGIN_DATA; ShibTargetConfig* g_Config; buffer *g_unsetHeaderValue; plugin_config **config_storage; plugin_config conf; } plugin_data; class ShibTargetLighty : public ShibTarget { public: server *_srv; connection *_con; plugin_data *_p_d; ShibTargetLighty(server *srv, connection *con, plugin_data *p_d) : _srv(srv), _con(con), _p_d(p_d) { init(_con->uri.scheme->ptr, _con->server_name->ptr, _srv->srvconf.port, _con->uri.path->used ? _con->uri.path->ptr : _con->uri.path_raw->ptr, _con->request.http_content_type, inet_ntop_cache_get_ip(_srv, &(_con->dst_addr)), (_con->request.http_method == HTTP_METHOD_GET) ? "GET" : (_con->request.http_method == HTTP_METHOD_POST) ? "POST" : (_con->request.http_method == HTTP_METHOD_HEAD) ? "HEAD" : "" ); } ~ShibTargetLighty() { } virtual void log(ShibLogLevel level, const string &msg) { ShibTarget::log(level,msg); if (level == LogLevelError) log_error_write(_srv, __FILE__, __LINE__, "ss", "shib: ", msg.c_str()); } virtual string getCookies(void) const { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(_con->request.headers, "Cookie"))) { return string(ds->value->ptr ? ds->value->ptr : ""); } else { return ""; } } virtual void setCookie(const string &name, const string &value) { data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(_con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string(ds->key, "Set-Cookie"); buffer_copy_string(ds->value, name.c_str()); buffer_append_string(ds->value, "="); buffer_append_string(ds->value, value.c_str()); array_insert_unique(_con->response.headers, (data_unset *)ds); } virtual string getArgs(void) { return string(_con->uri.query->ptr ? _con->uri.query->ptr : ""); } virtual string getPostData(void) { string post_data; if (_con->request.content_length) { chunkqueue *req_cq = _con->request_content_queue; buffer *b = buffer_init(); chunk *req_c; off_t offset; for (offset = 0, req_c = req_cq->first; offset != req_cq->bytes_in; req_c = req_c->next) { off_t weWant = req_cq->bytes_in - offset; off_t weHave = 0; switch (req_c->type) { case chunk::FILE_CHUNK: weHave = req_c->file.length - req_c->offset; if (weHave > weWant) weHave = weWant; if (req_c->file.mmap.start == MAP_FAILED) { if (-1 == req_c->file.fd && /* open the file if not already open */ -1 == (req_c->file.fd = open(req_c->file.name->ptr, O_RDONLY))) { log_error_write(_srv, __FILE__, __LINE__, "sss", "shib: ", "open failed: ", strerror(errno)); return post_data; } req_c->file.mmap.length = req_c->file.length; if (MAP_FAILED == (req_c->file.mmap.start = (char *)mmap(0, req_c->file.mmap.length, PROT_READ, MAP_SHARED, req_c->file.fd, 0))) { log_error_write(_srv, __FILE__, __LINE__, "sssbd", "shib: ", "mmap failed: ", strerror(errno), req_c->file.name, req_c->file.fd); close(req_c->file.fd); req_c->file.fd = -1; return post_data; } close(req_c->file.fd); req_c->file.fd = -1; /* chunk_reset() or chunk_free() will cleanup for us */ } buffer_append_memory(b, req_c->file.mmap.start + req_c->offset, req_c->file.length - req_c->offset); req_c->offset += req_c->file.length - req_c->offset; req_cq->bytes_out += req_c->file.length - req_c->offset; break; case chunk::MEM_CHUNK: /* append to the buffer */ weHave = req_c->mem->used - 1 - req_c->offset; if (weHave > weWant) weHave = weWant; buffer_append_memory(b, req_c->mem->ptr + req_c->offset, weHave); b->used++; /* add virtual \0 */ req_c->offset += weHave; req_cq->bytes_out += weHave; break; default: break; } offset += weHave; } post_data = b->ptr; buffer_free(b); } return post_data; } virtual void clearHeader(const string &name) { if (_p_d->g_unsetHeaderValue->used) { setHeader(name, _p_d->g_unsetHeaderValue->ptr); } else { data_string *ds_dst; if (NULL != (ds_dst = (data_string *)array_get_element(_con->request.headers, name.c_str()))) { buffer_reset(ds_dst->value); } } } virtual void setHeader(const string &name, const string &value) { data_string *ds_dst; if (NULL != (ds_dst = (data_string *)array_get_element(_con->request.headers, name.c_str()))) { buffer_copy_string_len(ds_dst->value, value.c_str(), value.length()); return; } if (NULL == (ds_dst = (data_string *)array_get_unused_element(_con->request.headers, TYPE_STRING))) { ds_dst = data_string_init(); } buffer_copy_string(ds_dst->key, name.c_str()); buffer_copy_string(ds_dst->value, value.c_str()); array_insert_unique(_con->request.headers, (data_unset *)ds_dst); } virtual string getHeader(const string &name) { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(_con->request.headers, name.c_str()))) { return string(ds->value->ptr ? ds->value->ptr : ""); } else { return ""; } } virtual void setRemoteUser(const string &user) { buffer_copy_string(_con->authed_user, user.c_str()); } virtual string getRemoteUser(void) { if (_con->authed_user) return string(_con->authed_user->ptr ? _con->authed_user->ptr : ""); else return ""; } virtual void* sendPage(const string& msg, int code=200, const string& content_type="text/html", const Iterator<header_t>& headers=EMPTY(header_t)) { while (headers.hasNext()) { const header_t& h=headers.next(); response_header_insert(_srv, _con, h.first.c_str(), h.first.length(), h.second.c_str(), h.second.length()); } response_header_overwrite(_srv, _con, CONST_STR_LEN("Content-Type"), content_type.c_str(), content_type.length()); chunkqueue_append_mem(_con->write_queue, msg.c_str(), msg.length()); _con->http_status = code; _con->file_finished = 1; return (void*)SHIB_RETURN_DONE; } virtual void* sendRedirect(const string& url) { response_header_insert(_srv, _con, CONST_STR_LEN("Location"), url.c_str(), url.length()); _con->http_status = 302; _con->file_finished = 1; return (void*)SHIB_RETURN_DONE; } virtual void* returnDecline(void) { return (void*)SHIB_RETURN_KO; } virtual void* returnOK(void) { return (void*)SHIB_RETURN_OK; } }; /* init the plugin data */ INIT_FUNC(mod_shib_init) { plugin_data *p; p = (plugin_data *)calloc(1, sizeof(*p)); p->g_Config = NULL; p->g_unsetHeaderValue = buffer_init(); return p; } /* detroy the plugin data */ FREE_FUNC(mod_shib_free) { plugin_data *p = (plugin_data *)p_d; UNUSED(srv); if (!p) return HANDLER_GO_ON; if (p->config_storage) { size_t i; for (i = 0; i < srv->config_context->used; i++) { plugin_config *s = p->config_storage[i]; if (!s) continue; buffer_free(s->shib_config); buffer_free(s->shib_schema_dir); free(s); } free(p->config_storage); } if (p->g_Config) { p->g_Config->shutdown(); p->g_Config = NULL; } buffer_free(p->g_unsetHeaderValue); free(p); return HANDLER_GO_ON; } /* handle plugin config and check values */ SETDEFAULTS_FUNC(mod_shib_set_defaults) { plugin_data *p = (plugin_data *)p_d; size_t i = 0; plugin_config *s; config_values_t cv[] = { { "shib.disable", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, { "shib.config", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "shib.schema.dir", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; if (!p) return HANDLER_ERROR; p->config_storage = (plugin_config**)calloc(1, srv->config_context->used * sizeof(specific_config *)); for (i = 0; i < srv->config_context->used; i++) { s = (plugin_config *)calloc(1, sizeof(plugin_config)); s->shib_config = buffer_init(); s->shib_schema_dir = buffer_init(); cv[0].destination = &(s->shib_disable); cv[1].destination = s->shib_config; cv[2].destination = s->shib_schema_dir; p->config_storage[i] = s; if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { return HANDLER_ERROR; } } s = p->config_storage[0]; if (s->shib_schema_dir->used && s->shib_config->used) { log_error_write(srv, __FILE__, __LINE__, "s", "shib_init starting"); if (p->g_Config) { log_error_write(srv, __FILE__, __LINE__, "s", "shib_init() already initialized!"); return HANDLER_ERROR; } try { p->g_Config = &ShibTargetConfig::getConfig(); p->g_Config->setFeatures(ShibTargetConfig::Listener | ShibTargetConfig::Metadata | ShibTargetConfig::AAP | ShibTargetConfig::RequestMapper | ShibTargetConfig::LocalExtensions | ShibTargetConfig::Logging ); if (!p->g_Config->init(s->shib_schema_dir->ptr)) { log_error_write(srv, __FILE__, __LINE__, "s", "shib_init() failed to initialize libraries"); return HANDLER_ERROR; } if (!p->g_Config->load(s->shib_config->ptr)) { log_error_write(srv, __FILE__, __LINE__, "s", "shib_init() failed to load configuration"); return HANDLER_ERROR; } IConfig* conf=p->g_Config->getINI(); Locker locker(conf); const IPropertySet* props=conf->getPropertySet("Local"); if (props) { pair<bool,const char*> unsetValue=props->getString("unsetHeaderValue"); if (unsetValue.first) buffer_copy_string(p->g_unsetHeaderValue, unsetValue.second); } } catch (...) { log_error_write(srv, __FILE__, __LINE__, "s", "shib_init() failed to initialize system"); return HANDLER_ERROR; } } else { log_error_write(srv, __FILE__, __LINE__, "s", "shib module config and/or schema dir not initialized."); return HANDLER_ERROR; } return HANDLER_GO_ON; } #define PATCH(x) \ p->conf.x = s->x; static int mod_shib_patch_connection(server *srv, connection *con, plugin_data *p) { size_t i, j; plugin_config *s = p->config_storage[0]; PATCH(shib_disable); /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; if (buffer_is_equal_string(du->key, CONST_STR_LEN("shib.disable"))) { PATCH(shib_disable); } } } return 0; } #undef PATCH URIHANDLER_FUNC(mod_shib_raw_handler) { plugin_data *p = (plugin_data *)p_d; UNUSED(srv); if (con->uri.path_raw->used == 0) return HANDLER_GO_ON; mod_shib_patch_connection(srv, con, p); if (p->conf.shib_disable == 0) { log_error_write(srv, __FILE__, __LINE__, "ssb", "shib: ", "RAW URL : ", con->uri.path_raw); try { saml::NDC ndc("mod_shib_raw_handler"); ShibTargetLighty sta(srv, con, p); pair<bool,void*> res = sta.doHandler(); if (res.first) { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doHandler() processed request."); switch((int)res.second) { case SHIB_RETURN_OK: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doHandler() returned OK."); return HANDLER_GO_ON; case SHIB_RETURN_KO: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doHandler() returned KO."); return HANDLER_GO_ON; case SHIB_RETURN_DONE: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doHandler() returned FINISHED."); return HANDLER_FINISHED; default: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doHandler() returned something strange."); return HANDLER_ERROR; } } } catch (SAMLException& e) { log_error_write(srv, __FILE__, __LINE__, "sss", "shib: ", "mod_shib_raw_handler threw an exception: ", e.what()); return HANDLER_ERROR; } catch (...) { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "mod_shib_raw_handler threw an uncaught exception!"); return HANDLER_ERROR; } log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doHandler() did not do anything."); return HANDLER_ERROR; } return HANDLER_GO_ON; } URIHANDLER_FUNC(mod_shib_uri_handler) { plugin_data *p = (plugin_data *)p_d; UNUSED(srv); if (con->uri.path->used == 0) return HANDLER_GO_ON; mod_shib_patch_connection(srv, con, p); if (p->conf.shib_disable == 0) { log_error_write(srv, __FILE__, __LINE__, "ssb", "shib: ", "CLEAN URL : ", con->uri.path); try { saml::NDC ndc("mod_shib_uri_handler"); ShibTargetLighty sta(srv, con, p); // Check user authentication and export information, then set the handler bypass pair<bool,void*> res = sta.doCheckAuthN(); if (res.first) { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthN caught request"); switch((int)res.second) { case SHIB_RETURN_OK: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthN returned OK."); return HANDLER_GO_ON; case SHIB_RETURN_KO: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthN returned KO."); return HANDLER_GO_ON; case SHIB_RETURN_DONE: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthN returned FINISHED."); return HANDLER_FINISHED; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "shib: ", "doCheckAuthN returned something strange: ", (int)res.second); return HANDLER_ERROR; } } else { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthN passed request."); } // User auth was okay -- export the assertions now res = sta.doExportAssertions(); if (res.first) { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doExportAssertions caught request"); switch((int)res.second) { case SHIB_RETURN_OK: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doExportAssertions returned OK."); return HANDLER_GO_ON; case SHIB_RETURN_KO: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doExportAssertions returned KO."); return HANDLER_GO_ON; case SHIB_RETURN_DONE: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doExportAssertions returned FINISHED."); return HANDLER_FINISHED; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "shib: ", "doExportAssertions returned something strange: ", (int)res.second); return HANDLER_ERROR; } } else { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doExportAssertions passed request."); } // Check authorization res = sta.doCheckAuthZ(); if (res.first) { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthZ caught request"); switch((int)res.second) { case SHIB_RETURN_OK: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthZ returned OK."); return HANDLER_GO_ON; case SHIB_RETURN_KO: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthZ returned KO."); return HANDLER_GO_ON; case SHIB_RETURN_DONE: log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthZ returned FINISHED."); return HANDLER_FINISHED; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "shib: ", "doCheckAuthZ returned something strange: ", (int)res.second); return HANDLER_ERROR; } } else { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "doCheckAuthZ passed request."); } } catch (SAMLException& e) { log_error_write(srv, __FILE__, __LINE__, "sss", "shib: ", "mod_shib_uri_handler threw an exception: ", e.what()); return HANDLER_ERROR; } catch (...) { log_error_write(srv, __FILE__, __LINE__, "ss", "shib: ", "mod_shib_uri_handler threw an uncaught exception!"); return HANDLER_ERROR; } } return HANDLER_GO_ON; } /* this function is called at dlopen() time and inits the callbacks */ extern "C" int mod_shib_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("shib"); p->init = mod_shib_init; p->handle_uri_clean = mod_shib_uri_handler; p->handle_uri_raw = mod_shib_raw_handler; p->set_defaults = mod_shib_set_defaults; p->cleanup = mod_shib_free; p->data = NULL; return 0; }
Attachment:
signature.asc
Description: OpenPGP digital signature
- LightTPD SP module v0.2, André Cruz, 01/09/2007
- Re: LightTPD SP module v0.2, Steve Olshansky, 01/09/2007
Archive powered by MHonArc 2.6.16.