Skip to Content.
Sympa Menu

shibboleth-dev - LightTPD SP module v0.2

Subject: Shibboleth Developers

List archive

LightTPD SP module v0.2


Chronological Thread 
  • 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




Archive powered by MHonArc 2.6.16.

Top of Page