Add support for escaping JSON strings.
authorAlejandro Perez <alex@um.es>
Thu, 9 Feb 2017 13:10:14 +0000 (14:10 +0100)
committerAlejandro Perez <alex@um.es>
Fri, 24 Mar 2017 07:34:31 +0000 (07:34 +0000)
JSON strings need to be scaped or attributes which " (such as SAML assertions) will not work as expected.

gss.c
mod_auth_gssapi.c

diff --git a/gss.c b/gss.c
index 9ebba62..75a40bd 100644 (file)
--- a/gss.c
+++ b/gss.c
@@ -352,6 +352,16 @@ static char mag_get_name_attr(request_rec *req,
     return 1;
 }
 
+#define GSS_NAME_ATTR_USERDATA "GSS Name Attributes Userdata"
+
+static apr_status_t mag_gss_name_attrs_cleanup(void *data)
+{
+    gss_conn_ctx_t *gss_ctx = (struct gss_conn_ctx_t *)data;
+    free(gss_ctx->name_attributes);
+    gss_ctx->name_attributes = NULL;
+    return 0;
+}
+
 static void mc_add_name_attribute(gss_conn_ctx_t *gss_ctx,
                                   const char *name, const char *value)
 {
@@ -361,6 +371,8 @@ static void mc_add_name_attribute(gss_conn_ctx_t *gss_ctx,
         size = sizeof(mag_attr) * (gss_ctx->na_count + 16);
         gss_ctx->name_attributes = realloc(gss_ctx->name_attributes, size);
         if (!gss_ctx->name_attributes) apr_pool_abort_get(gss_ctx->pool)(ENOMEM);
+        apr_pool_userdata_setn(gss_ctx, GSS_NAME_ATTR_USERDATA,
+                               mag_gss_name_attrs_cleanup, gss_ctx->pool);
     }
 
     gss_ctx->name_attributes[gss_ctx->na_count].name = apr_pstrdup(gss_ctx->pool, name);
@@ -404,6 +416,37 @@ static void mag_set_env_name_attr(request_rec *req, gss_conn_ctx_t *gss_ctx,
     }
 }
 
+static char* mag_escape_display_value(request_rec *req, gss_buffer_desc disp_value)
+{
+    /* This function returns a copy (in the pool) of the given gss_buffer_t where every
+     * occurrence of " has been replaced by \". This string is NULL terminated */
+    int i = 0, j = 0, n_quotes = 0;
+    char *escaped_value = NULL;
+    char *value = (char*) disp_value.value;
+
+    // count number of quotes in the input string
+    for (i = 0, j = 0; i < disp_value.length; i++)
+        if (value[i] == '"')
+            n_quotes++;
+
+    // if there are no quotes, just return a copy of the string
+    if (n_quotes == 0)
+        return apr_pstrndup(req->pool, value, disp_value.length);
+
+    // gss_buffer_t are not \0 terminated, but our result will be
+    escaped_value = apr_palloc(req->pool, disp_value.length + n_quotes + 1);
+    for (i = 0,j = 0; i < disp_value.length; i++, j++) {
+        if (value[i] == '"') {
+            escaped_value[j] = '\\';
+            j++;
+        }
+        escaped_value[j] = value[i];
+    }
+    // make the string NULL terminated
+    escaped_value[j] = '\0';
+    return escaped_value;
+}
+
 static void mag_add_json_name_attr(request_rec *req, char first,
                                    name_attr *attr, char **json)
 {
@@ -423,8 +466,8 @@ static void mag_add_json_name_attr(request_rec *req, char first,
                                    attr->value.length);
     }
     if (attr->display_value.length != 0) {
-        len = attr->display_value.length;
-        value = (const char *)attr->display_value.value;
+        value = mag_escape_display_value(req, attr->display_value);
+        len = strlen(value);
     }
     if (attr->number == 1) {
         *json = apr_psprintf(req->pool,
@@ -469,10 +512,6 @@ gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
 void mag_get_name_attributes(request_rec *req, gss_auth_config *cfg,
                              gss_name_t name, gss_conn_ctx_t *gss_ctx)
 {
-    if (!cfg->name_attributes) {
-        return;
-    }
-
     uint32_t maj, min;
     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
     name_attr attr;
@@ -526,7 +565,6 @@ void mag_get_name_attributes(request_rec *req, gss_auth_config *cfg,
         attr.number = 0;
         attr.more = -1;
         do {
-            char code = 0;
             attr.number++;
             attr.value = empty_buffer;
             attr.display_value = empty_buffer;
@@ -569,8 +607,6 @@ void mag_set_req_data(request_rec *req,
     }
 }
 
-#define GSS_NAME_ATTR_USERDATA "GSS Name Attributes Userdata"
-
 static apr_status_t mag_name_attrs_cleanup(void *data)
 {
     gss_auth_config *cfg = (gss_auth_config *)data;
@@ -630,4 +666,4 @@ const char *mag_name_attrs(cmd_parms *parms, void *mconfig,
     cfg->name_attributes->map_count += 1;
 
     return NULL;
-}
\ No newline at end of file
+}
index 02ea261..d533594 100644 (file)
@@ -212,11 +212,13 @@ gss_authenticate(request_rec *r, gss_auth_config *conf, gss_conn_ctx ctx,
   }
 
   major_status = gss_display_name(&minor_status, client_name, &output_token, NULL);
-  ctx->user = apr_pstrdup(r->pool, output_token.value);
+  ctx->user = apr_pstrndup(r->pool, output_token.value, output_token.length);
+
+  if (conf->name_attributes) {
+    mag_get_name_attributes(r, conf, client_name, ctx);
+    mag_set_req_data(r, conf, ctx);
+  }
 
-  /* TODO: Make it a single call! */
-  mag_get_name_attributes(r, conf, client_name, ctx);
-  mag_set_req_data(r, conf, ctx);
 
   gss_release_name(&minor_status, &client_name);
   if (GSS_ERROR(major_status)) {
@@ -248,6 +250,13 @@ end:
   return ret;
 }
 
+static int mag_post_config(apr_pool_t *cfgpool, apr_pool_t *log,
+                           apr_pool_t *temp, server_rec *s)
+{
+    ap_add_version_component(cfgpool, "Moonshot mod_auth_gssapi");
+    return OK;
+}
+
 static int
 gss_authenticate_user(request_rec *r)
 {
@@ -335,6 +344,7 @@ static void
 gss_register_hooks(apr_pool_t *p)
 {
     ap_hook_check_user_id(gss_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_post_config(mag_post_config, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
 module AP_MODULE_DECLARE_DATA auth_gssapi_module = {