Updates/fixes to gssweb filter code.
authorMargaret Wasserman <margaret@margaret-moonshot3.painless-security.com>
Tue, 2 Sep 2014 12:02:12 +0000 (08:02 -0400)
committerMargaret Wasserman <margaret@margaret-moonshot3.painless-security.com>
Tue, 2 Sep 2014 12:02:12 +0000 (08:02 -0400)
gss.c
gss.h
mod_auth_gssweb.c

diff --git a/gss.c b/gss.c
index d176de6..4b0055f 100644 (file)
--- a/gss.c
+++ b/gss.c
@@ -72,6 +72,7 @@ gss_get_conn_ctx(request_rec *r)
            return NULL;
        ctx->context = GSS_C_NO_CONTEXT;
        ctx->state = GSS_CTX_EMPTY;
+       ctx->filter_stat = GSS_FILT_NEW;
        ctx->user = NULL;
        apr_pool_userdata_set(ctx, key, cleanup_conn_ctx, r->connection->pool);
     }
diff --git a/gss.h b/gss.h
index cb6a74a..3e80755 100644 (file)
--- a/gss.h
+++ b/gss.h
@@ -58,8 +58,13 @@ typedef struct gss_conn_ctx_t {
     GSS_CTX_IN_PROGRESS,
     GSS_CTX_FAILED,
     GSS_CTX_ESTABLISHED,
-    GSS_CTX_ERROR,
   } state;
+  enum {
+    GSS_FILT_NEW,
+    GSS_FILT_INPROGRESS,
+    GSS_FILT_ERROR,
+  } filter_stat;
+
   char *user;
   gss_buffer_desc output_token;
   unsigned int nonce;
index d10778f..74a43a6 100644 (file)
@@ -52,6 +52,7 @@
  * described in the protocol.txt file included with module source.
  */
 
+#include <stdio.h>
 #include "mod_auth_gssweb.h"
 
 module AP_MODULE_DECLARE_DATA auth_gssweb_module;
@@ -183,51 +184,103 @@ static apr_status_t gssweb_authenticate_filter (ap_filter_t *f,
   const char *data = NULL;
   apr_size_t len = 0;
   char *buf = NULL;
+  char *stoken = NULL;
   apr_size_t n = 0;
+  gss_conn_ctx conn_ctx = NULL;
+  const char *c_type = NULL;
+  const char *c_len = NULL;
+
   apr_status_t ret = 0;
 
   /* get the context from the request */
+  conn_ctx = gss_get_conn_ctx(r);
+  if ((NULL == conn_ctx) || 
+      (GSS_C_NO_CONTEXT == conn_ctx->context) ||
+      (GSS_CTX_EMPTY == conn_ctx->state) ||
+      (0 == conn_ctx->output_token.length)) {
+    conn_ctx->filter_stat = GSS_FILT_ERROR;
+    gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_filter: Failed to find valid context.");
+    apr_brigade_cleanup(brig_in);
+    return HTTP_INTERNAL_SERVER_ERROR;
+  }
+    
+  /* if this is the first call for a response, send opening JSON block */
 
-  /* if this is the first call for this response, send JSON block */
+  if (GSS_FILT_NEW == conn_ctx->filter_stat) {
+    if (NULL == (brig_out = apr_brigade_create(r->pool, c->bucket_alloc))) {
+      conn_ctx->filter_stat = GSS_FILT_ERROR;
+      gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_filter: Unable to allocate output brigade (opening)");
+      apr_brigade_cleanup(brig_in);
+      return HTTP_INTERNAL_SERVER_ERROR;
+    }
 
-  if (first call) {
-    if (NULL = (brig_out = apr_brigade_create(r->pool, c->bucket_alloc))) {
-      // indicate processing error
+    len = apr_base64_encode_len(conn_ctx->output_token.length);
+    if (NULL == (data = apr_bucket_alloc(len+1024, c->bucket_alloc)) ||
+       NULL == (stoken = apr_bucket_alloc(len+1, c->bucket_alloc))) {
+      gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_filter: Unable to allocate space for opening json block");
       apr_brigade_cleanup(brig_in);
-      return ??;
+      apr_brigade_cleanup(brig_out);
+      return HTTP_INTERNAL_SERVER_ERROR;
     }
-    
-    //create and send opening JSON block
-    
-    if (?? != (ret = ap_pass_brigade(f->next,pbbOut))) {
-      // indicate a processing error
+
+    apr_base64_encode_binary(stoken, conn_ctx->output_token.value, conn_ctx->output_token.length);
+    snprintf((char *)data, len+1024, 
+            "{gssweb: {\ntoken: \"%s\",\nnonce: \"%d\"},\n application: {\ndata: \"", 
+            stoken, conn_ctx->nonce);
+    bkt_out = apr_bucket_heap_create(data, strlen(data), apr_bucket_free,
+                                    c->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(brig_out, bkt_out);
+    if (0 != (ret = ap_pass_brigade(f->next, brig_out))) {
       apr_brigade_cleanup(brig_in);
       apr_brigade_cleanup(brig_out);
       return ret;
     }
+
+    conn_ctx->filter_stat = GSS_FILT_INPROGRESS;
   }
 
-  /* loop through the buckets, escaping and sending each one */
+  /* loop through the app data buckets, escaping and sending each one */
   for (bkt_in = APR_BRIGADE_FIRST(brig_in);
        bkt_in != APR_BRIGADE_SENTINEL(brig_in);
        bkt_in = APR_BUCKET_NEXT(bkt_in))
     {
-      brig_out = apr_brigade_create(r->pool, c->bucket_alloc);
+      if (NULL == (brig_out = apr_brigade_create(r->pool, c->bucket_alloc))) {
+           gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_filter: Unable to allocate brigade (loop)");
+           conn_ctx->filter_stat = GSS_FILT_ERROR;
+           apr_brigade_cleanup(brig_in);
+           return HTTP_INTERNAL_SERVER_ERROR;
+      }
 
       /* if this is an EOS, send the JSON closing block */
       if(APR_BUCKET_IS_EOS(bkt_in))
        {
-         // create and send the JSON closing block
+         /* create and add the JSON closing block */
+         
+         if (NULL == (data = apr_bucket_alloc(1024, c->bucket_alloc))) {
+             gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_filter: Unable to allocate space for closing json block");
+             apr_brigade_cleanup(brig_in);
+             apr_brigade_cleanup(brig_out);
+             return HTTP_INTERNAL_SERVER_ERROR;
+         }
 
-         // flag that the next filter call will be a new response 
+         c_type = apr_table_get(r->headers_in, "Content-Type");
+          c_len = apr_table_get(r->headers_in, "Content-Length");
+         snprintf((char *)data, 1024, "\"\ncontent-type: \"%s,\ncontent-length: \"%s\"\n}\n}", c_type, c_len);
+         bkt_out = apr_bucket_heap_create(data, strlen(data), apr_bucket_free,
+                                          c->bucket_alloc);
+         APR_BRIGADE_INSERT_TAIL(brig_out, bkt_out);
 
+         /* Indicate that the next filter call is a new response */
+         conn_ctx->filter_stat = GSS_FILT_NEW;
+         
          /* set EOS in the outbound brigade */
          bkt_eos = apr_bucket_eos_create(c->bucket_alloc);
          APR_BRIGADE_INSERT_TAIL (brig_out, bkt_eos);
-
+         
          /* pass the brigade */
-         if (?? != (ret = ap_pass_brigade(f->next, bkt_out))) {
-           // indicate a processing error
+         if (0 != (ret = ap_pass_brigade(f->next, brig_out))) {
+           gss_log(APLOG_MARK, APLOG_ERR, 0, r, "gssweb_authenticate_filter: Unable to pass output brigade (eos)");
+           conn_ctx->filter_stat = GSS_FILT_ERROR;
            apr_brigade_cleanup(brig_in);
            apr_brigade_cleanup(brig_out);
            return ret;
@@ -235,26 +288,39 @@ static apr_status_t gssweb_authenticate_filter (ap_filter_t *f,
          continue;
        }
 
-      /* read */
+      /* Read application data from each input bucket */
       apr_bucket_read(bkt_in, &data, &len, APR_BLOCK_READ);
 
-      /* write */
-      buf = apr_bucket_alloc(len, c->bucket_alloc);
-      bkt_out = apr_bucket_heap_create(buf, len, apr_bucket_free,
-                                      c->bucket_alloc);
-
+      /* Write data to an output brigade, escaping quotes */
       for(n=0 ; n < len ; ++n) {
-       // escape quotes
+       if ('"' == data[n]) {
+         /* Write n bytes of app data */
+         buf = apr_bucket_alloc(n, c->bucket_alloc);
+         bkt_out = apr_bucket_heap_create(buf, n, apr_bucket_free,
+                                          c->bucket_alloc);
+         APR_BRIGADE_INSERT_TAIL(brig_out, bkt_out);
+         
+         /* Write the escaped quote character */
+         buf = apr_bucket_alloc(3, c->bucket_alloc);
+         bkt_out = apr_bucket_heap_create("\\\"", 2, NULL, c->bucket_alloc);
+         APR_BRIGADE_INSERT_TAIL(brig_out, bkt_out);
+
+         /* Process remaining data */
+         buf = &buf[n];
+         len = len - n;
+         n = 0;
+       }
       }
 
-      APR_BRIGADE_INSERT_TAIL(brig_out, bkt_out);
-      if (?? == (ret = ap_pass_brigade(f->next, bkt_out))) {
+      /* Send the output brigade */
+      if (OK != (ret = ap_pass_brigade(f->next, brig_out))) {
        apr_brigade_cleanup(brig_in);
        apr_brigade_cleanup(brig_out);
        return ret;
       }
     }
 
+  /* Make sure we don't see the same data again */
   apr_brigade_cleanup(brig_in);
   return ret;
 }
@@ -294,7 +360,6 @@ gssweb_authenticate_user(request_rec *r)
     (gss_auth_config *) ap_get_module_config(r->per_dir_config,
                                                &auth_gssweb_module);
   const char *auth_line = NULL;
-  const char *type = NULL;
   char *auth_type = NULL;
   char *negotiate_ret_value = NULL;
   gss_conn_ctx conn_ctx = NULL;
@@ -311,11 +376,11 @@ gssweb_authenticate_user(request_rec *r)
   gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Entering GSSWeb authentication");
    
   /* Check if this is for our auth type */
-  type = ap_auth_type(r);
-  if (type == NULL || strcasecmp(type, "GSSWeb") != 0) {
+  auth_type = (char *)ap_auth_type(r);
+  if (auth_type == NULL || strcasecmp(auth_type, "GSSWeb") != 0) {
         gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
                "gssweb_authenticate_user: AuthType '%s' is not GSSWeb, bailing out",
-               (type) ? type : "(NULL)");
+               (auth_type) ? auth_type : "(NULL)");
 
         return DECLINED;
   }
@@ -342,17 +407,19 @@ gssweb_authenticate_user(request_rec *r)
     }
     conn_ctx->context = GSS_C_NO_CONTEXT;
     conn_ctx->state = GSS_CTX_EMPTY;
+    conn_ctx->filter_stat = GSS_FILT_NEW;
     conn_ctx->user = NULL;
     if (0 != conn_ctx->output_token.length) {
       gss_release_buffer(&minor_status, &(conn_ctx->output_token));
     }
+    conn_ctx->output_token.length = 0;
   }
  
   /* If the output filter reported an internal server error, return it */
-  if (GSS_CTX_ERROR == conn_ctx->state) {
+  if (GSS_FILT_ERROR == conn_ctx->filter_stat) {
     ret = HTTP_INTERNAL_SERVER_ERROR;
     gss_log(APLOG_MARK, APLOG_ERR, 0, r,
-           "gssweb_authenticate_user: Output filter returned internal server error, reporting.");
+           "gssweb_authenticate_user: Output filter returned error, reporting.");
     goto end;
   }