shibboleth-dev - Re: LightTPD SP module v0.2
Subject: Shibboleth Developers
List archive
- From: Steve Olshansky <>
- To:
- Subject: Re: LightTPD SP module v0.2
- Date: Tue, 09 Jan 2007 08:04:43 -0700
Thanks very much for this Andre. I will follow up with you about this off
list.
Steve
André Cruz wrote:
> 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;
> }
--
Steve Olshansky
Internet2 Middleware & Security Flywheel
http://middleware.internet2.edu/
http://security.internet2.edu/
- 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.