Ejemplo n.º 1
0
/*
 * validate an access token against the validation endpoint of the Authorization server and gets a response back
 */
static int oidc_oauth_validate_access_token(request_rec *r, oidc_cfg *c,
		const char *token, const char **response) {

	/* get a handle to the directory config */
	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
			&auth_openidc_module);

	/* assemble parameters to call the token endpoint for validation */
	apr_table_t *params = apr_table_make(r->pool, 4);

	/* add any configured extra static parameters to the introspection endpoint */
	oidc_util_table_add_query_encoded_params(r->pool, params,
			c->oauth.introspection_endpoint_params);

	/* add the access_token itself */
	apr_table_addn(params, "token", token);

	/* see if we want to do basic auth or post-param-based auth */
	const char *basic_auth = NULL;
	if ((c->oauth.introspection_endpoint_auth != NULL)
			&& (apr_strnatcmp(c->oauth.introspection_endpoint_auth,
					"client_secret_post") == 0)) {
		apr_table_addn(params, "client_id", c->oauth.client_id);
		apr_table_addn(params, "client_secret", c->oauth.client_secret);
	} else {
		basic_auth = apr_psprintf(r->pool, "%s:%s", c->oauth.client_id,
				c->oauth.client_secret);
	}

	/* call the endpoint with the constructed parameter set and return the resulting response */
	return oidc_util_http_post_form(r, c->oauth.introspection_endpoint_url,
			params, basic_auth, NULL, c->oauth.ssl_validate_server, response,
			c->http_timeout_long, c->outgoing_proxy, dir_cfg->pass_cookies);
}
Ejemplo n.º 2
0
static void table_overlap(abts_case *tc, void *data)
{
    const char *val;
    apr_table_t *t1 = apr_table_make(p, 1);
    apr_table_t *t2 = apr_table_make(p, 1);

    apr_table_addn(t1, "a", "0");
    apr_table_addn(t1, "g", "7");
    apr_table_addn(t2, "a", "1");
    apr_table_addn(t2, "b", "2");
    apr_table_addn(t2, "c", "3");
    apr_table_addn(t2, "b", "2.0");
    apr_table_addn(t2, "d", "4");
    apr_table_addn(t2, "e", "5");
    apr_table_addn(t2, "b", "2.");
    apr_table_addn(t2, "f", "6");
    apr_table_overlap(t1, t2, APR_OVERLAP_TABLES_SET);

    ABTS_INT_EQUAL(tc, 7, apr_table_elts(t1)->nelts);
    val = apr_table_get(t1, "a");
    ABTS_STR_EQUAL(tc, "1", val);
    val = apr_table_get(t1, "b");
    ABTS_STR_EQUAL(tc, "2.", val);
    val = apr_table_get(t1, "c");
    ABTS_STR_EQUAL(tc, "3", val);
    val = apr_table_get(t1, "d");
    ABTS_STR_EQUAL(tc, "4", val);
    val = apr_table_get(t1, "e");
    ABTS_STR_EQUAL(tc, "5", val);
    val = apr_table_get(t1, "f");
    ABTS_STR_EQUAL(tc, "6", val);
    val = apr_table_get(t1, "g");
    ABTS_STR_EQUAL(tc, "7", val);
}
Ejemplo n.º 3
0
/* This function sets the value of our cookie.
 *
 * Parameters:
 *  request_rec *r       The request we should set the cookie in.
 *  const char *id       The value ve should store in the cookie.
 *
 * Returns:
 *  Nothing.
 */
void am_cookie_set(request_rec *r, const char *id)
{
    am_req_cfg_rec *req_cfg;
    const char *name;
    const char *cookie_params;
    char *cookie;

    if (id == NULL)
        return;

    name = am_cookie_name(r);
    cookie_params = am_cookie_params(r);

    cookie = apr_psprintf(r->pool, "%s=%s; %s", name, id, cookie_params);
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                 "cookie_set: %s", cookie);

    /* Setting the headers inn err_headers_out ensures that they will be
     * sent for all responses.
     */
    apr_table_addn(r->err_headers_out, "Set-Cookie", cookie);

    /* Add a note on the current request, to allow us to retrieve this
     * cookie in the current request.
     */
    req_cfg = am_get_req_cfg(r);
    req_cfg->cookie_value = apr_pstrdup(r->pool, id);
}
Ejemplo n.º 4
0
static void check_push(request_rec *r, const char *tag)
{
    const h2_config *conf = h2_config_rget(r);
    if (!r->expecting_100 
        && conf && conf->push_list && conf->push_list->nelts > 0) {
        int i, old_status;
        const char *old_line;
        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 
                      "%s, early announcing %d resources for push",
                      tag, conf->push_list->nelts);
        for (i = 0; i < conf->push_list->nelts; ++i) {
            h2_push_res *push = &APR_ARRAY_IDX(conf->push_list, i, h2_push_res);
            apr_table_addn(r->headers_out, "Link", 
                           apr_psprintf(r->pool, "<%s>; rel=preload%s", 
                                        push->uri_ref, push->critical? "; critical" : ""));
        }
        old_status = r->status;
        old_line = r->status_line;
        r->status = 103;
        r->status_line = "103 Early Hints";
        ap_send_interim_response(r, 1);
        r->status = old_status;
        r->status_line = old_line;
    }
}
Ejemplo n.º 5
0
/**
 * Write an RFC2965 compliant cookie.
 *
 * @param r The request
 * @param name2 The name of the cookie.
 * @param val The value to place in the cookie.
 * @param attrs2 The string containing additional cookie attributes. If NULL, the
 *               DEFAULT_ATTRS will be used.
 * @param maxage If non zero, a Max-Age header will be added to the cookie.
 */
AP_DECLARE(apr_status_t) ap_cookie_write2(request_rec * r, const char *name2, const char *val,
                                          const char *attrs2, long maxage, ...)
{

    const char *buffer;
    const char *rfc2965;
    apr_table_t *t;
    va_list vp;

    /* handle expiry */
    buffer = "";
    if (maxage) {
        buffer = apr_pstrcat(r->pool, "Max-Age=", apr_ltoa(r->pool, maxage), ";", NULL);
    }

    /* create RFC2965 compliant cookie */
    rfc2965 = apr_pstrcat(r->pool, name2, "=", val, ";", buffer,
                          attrs2 && *attrs2 ? attrs2 : DEFAULT_ATTRS, NULL);
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00008) LOG_PREFIX
                  "user '%s' set cookie2: '%s'", r->user, rfc2965);

    /* write the cookie to the header table(s) provided */
    va_start(vp, maxage);
    while ((t = va_arg(vp, apr_table_t *))) {
        apr_table_addn(t, SET_COOKIE2, rfc2965);
    }
    va_end(vp);

    return APR_SUCCESS;

}
Ejemplo n.º 6
0
void aos_curl_response_headers_parse(aos_pool_t *p, aos_table_t *headers, char *buffer, int len)
{
    char *pos;
    aos_string_t str;
    aos_string_t key;
    aos_string_t value;
    
    str.data = buffer;
    str.len = len;

    aos_trip_space_and_cntrl(&str);

    pos = aos_strlchr(str.data, str.data + str.len, ':');
    if (pos == NULL) {
        return;
    }
    key.data = str.data;
    key.len = pos - str.data;

    pos += 1;
    value.len = str.data + str.len - pos;
    value.data = pos;
    aos_strip_space(&value);

    apr_table_addn(headers, aos_pstrdup(p, &key), aos_pstrdup(p, &value));
}
Ejemplo n.º 7
0
/*
 * refreshes the access_token/id_token /refresh_token received from the OP using the refresh_token
 */
apr_byte_t oidc_proto_refresh_request(request_rec *r, oidc_cfg *cfg,
		oidc_provider_t *provider, const char *rtoken, char **id_token,
		char **access_token, char **token_type, int *expires_in,
		char **refresh_token) {

	oidc_debug(r, "enter");

	/* assemble the parameters for a call to the token endpoint */
	apr_table_t *params = apr_table_make(r->pool, 5);
	apr_table_addn(params, "grant_type", "refresh_token");
	apr_table_addn(params, "refresh_token", rtoken);
	apr_table_addn(params, "scope", provider->scope);

	return oidc_proto_token_endpoint_request(r, cfg, provider, params, id_token,
			access_token, token_type, expires_in, refresh_token);
}
Ejemplo n.º 8
0
/*
 * resolves the code received from the OP in to an id_token, access_token and refresh_token
 */
apr_byte_t oidc_proto_resolve_code(request_rec *r, oidc_cfg *cfg,
		oidc_provider_t *provider, const char *code, char **id_token,
		char **access_token, char **token_type, int *expires_in,
		char **refresh_token) {

	oidc_debug(r, "enter");

	/* assemble the parameters for a call to the token endpoint */
	apr_table_t *params = apr_table_make(r->pool, 5);
	apr_table_addn(params, "grant_type", "authorization_code");
	apr_table_addn(params, "code", code);
	apr_table_addn(params, "redirect_uri", cfg->redirect_uri);

	return oidc_proto_token_endpoint_request(r, cfg, provider, params, id_token,
			access_token, token_type, expires_in, refresh_token);
}
Ejemplo n.º 9
0
/* Iterate through the cookies, isolate our cookie and then remove it.
 *
 * If our cookie appears two or more times, but with different values,
 * remove it twice and set the duplicated flag to true. Remove any
 * $path or other attributes following our cookie if present. If we end
 * up with an empty cookie, remove the whole header.
 */
static int extract_cookie_line(ap_cookie_do * v, const char *key, const char *val)
{
    char *last1, *last2;
    char *cookie = apr_pstrdup(v->r->pool, val);
    const char *name = apr_pstrcat(v->r->pool, v->name ? v->name : "", "=", NULL);
    apr_size_t len = strlen(name);
    const char *new_cookie = "";
    const char *comma = ",";
    char *next1;
    const char *semi = ";";
    char *next2;
    const char *sep = "";
    int cookies = 0;

    /* find the cookie called name */
    int eat = 0;
    next1 = apr_strtok(cookie, comma, &last1);
    while (next1) {
        next2 = apr_strtok(next1, semi, &last2);
        while (next2) {
            char *trim = next2;
            while (apr_isspace(*trim)) {
                trim++;
            }
            if (!strncmp(trim, name, len)) {
                if (v->encoded) {
                    if (strcmp(v->encoded, trim + len)) {
                        v->duplicated = 1;
                    }
                }
                v->encoded = apr_pstrdup(v->r->pool, trim + len);
                eat = 1;
            }
            else {
                if (*trim != '$') {
                    cookies++;
                    eat = 0;
                }
                if (!eat) {
                    new_cookie = apr_pstrcat(v->r->pool, new_cookie, sep, next2, NULL);
                }
            }
            next2 = apr_strtok(NULL, semi, &last2);
            sep = semi;
        }

        next1 = apr_strtok(NULL, comma, &last1);
        sep = comma;
    }

    /* any cookies left over? */
    if (cookies) {
        apr_table_addn(v->new_cookies, key, new_cookie);
    }

    return 1;
}
Ejemplo n.º 10
0
static void make_cookie(request_rec *r)
{
    cookie_log_state *cls = ap_get_module_config(r->server->module_config,
                                                 &usertrack_module);
    char cookiebuf[2 * (sizeof(apr_uint64_t) + sizeof(int)) + 2];
    unsigned int random;
    apr_time_t now = r->request_time ? r->request_time : apr_time_now();
    char *new_cookie;
    cookie_dir_rec *dcfg;

    ap_random_insecure_bytes(&random, sizeof(random));
    apr_snprintf(cookiebuf, sizeof(cookiebuf), "%x.%" APR_UINT64_T_HEX_FMT,
                 random, (apr_uint64_t)now);
    dcfg = ap_get_module_config(r->per_dir_config, &usertrack_module);
    if (cls->expires) {

        /* Cookie with date; as strftime '%a, %d-%h-%y %H:%M:%S GMT' */
        new_cookie = apr_psprintf(r->pool, "%s=%s; path=/",
                                  dcfg->cookie_name, cookiebuf);

        if ((dcfg->style == CT_UNSET) || (dcfg->style == CT_NETSCAPE)) {
            apr_time_exp_t tms;
            apr_time_exp_gmt(&tms, r->request_time
                                 + apr_time_from_sec(cls->expires));
            new_cookie = apr_psprintf(r->pool,
                                       "%s; expires=%s, "
                                       "%.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
                                       new_cookie, apr_day_snames[tms.tm_wday],
                                       tms.tm_mday,
                                       apr_month_snames[tms.tm_mon],
                                       tms.tm_year % 100,
                                       tms.tm_hour, tms.tm_min, tms.tm_sec);
        }
        else {
            new_cookie = apr_psprintf(r->pool, "%s; max-age=%d",
                                      new_cookie, cls->expires);
        }
    }
    else {
        new_cookie = apr_psprintf(r->pool, "%s=%s; path=/",
                                  dcfg->cookie_name, cookiebuf);
    }
    if (dcfg->cookie_domain != NULL) {
        new_cookie = apr_pstrcat(r->pool, new_cookie, "; domain=",
                                 dcfg->cookie_domain,
                                 (dcfg->style == CT_COOKIE2
                                  ? "; version=1"
                                  : ""),
                                 NULL);
    }

    apr_table_addn(r->err_headers_out,
                   (dcfg->style == CT_COOKIE2 ? "Set-Cookie2" : "Set-Cookie"),
                   new_cookie);
    apr_table_setn(r->notes, "cookie", apr_pstrdup(r->pool, cookiebuf));   /* log first time */
    return;
}
Ejemplo n.º 11
0
void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg)
{
    if (msr->txcfg->debuglog_level >= 5) {
        msr_log(msr, 5, "Adding request argument (%s): name \"%s\", value \"%s\"",
                arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len),
                log_escape_ex(msr->mp, arg->value, arg->value_len));
    }

    apr_table_addn(arguments, log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *)arg);
}
Ejemplo n.º 12
0
/**
 * Callback function to extract multiple HTTP headers from the outgoing header 
 * and change them a little bit. (borrowed from server/util_script.c)
 */
static int replace_header_cb(void *v, const char *key, const char *val)
{
    header_replace_cb_t *data;
    data = (header_replace_cb_t *) v;
    // do replacement and finally add the new value to the table
    int len = strlen(val);
    int rc = 0;
    int re_vector[RE_VECTOR_SIZE];  // 3 elements per matched pattern
    request_rec *r;
    r = data->r;

    rc = pcre_exec(data->pattern, data->extra, val, 
                   len, 0, 0, re_vector, RE_VECTOR_SIZE);

    if (rc < 0 && rc != PCRE_ERROR_NOMATCH) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, 
                      "Matching Error %d", rc);
        return rc;
    }

    if (rc == 0) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, 
                      "PCRE output vector too small (%d)", 
                      RE_VECTOR_SIZE/3-1);
    }

    /* If the result count is greater than 0 then there are
     * matches in the data string. Thus we try to replace those
     * strings with the user provided string.
     */
    if (rc > 0) {
        char *replacement;
        char *prefix, *postfix;
        replacement = apr_pstrcat(r->pool, data->replacement, 
                                  NULL);
        prefix = apr_pcalloc(r->pool, re_vector[0] + 1);
        if (prefix == NULL) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                          "Unable to allocate memory for prefix");
            return -1;
        }
        memcpy(prefix, val, (size_t)re_vector[0]);
    
        postfix = apr_pcalloc(r->pool, len);
        if (postfix == NULL) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
                          "Unable to allocate memory for postfix");
            return -1;
        }
        memcpy(postfix, (val + re_vector[1]), len - re_vector[1]);
        val = apr_pstrcat(r->pool, prefix, replacement, postfix, NULL);
    }
    apr_table_addn(data->header_table, key, val);
    return 1;
}
Ejemplo n.º 13
0
static void table_overlap2(abts_case *tc, void *data)
{
    apr_pool_t *subp;
    apr_table_t *t1, *t2;

    apr_pool_create(&subp, p);

    t1 = apr_table_make(subp, 1);
    t2 = apr_table_make(p, 1);
    apr_table_addn(t1, "t1", "one");
    apr_table_addn(t2, "t2", "two");

    apr_table_overlap(t1, t2, APR_OVERLAP_TABLES_SET);

    ABTS_INT_EQUAL(tc, 2, apr_table_elts(t1)->nelts);

    ABTS_STR_EQUAL(tc, "one", apr_table_get(t1, "t1"));
    ABTS_STR_EQUAL(tc, "two", apr_table_get(t1, "t2"));

}
static int filter_header_do(void *v, const char *key, const char *val)
{
    if ((*key == 'W' || *key == 'w') && !strcasecmp(key, "Warning")
            && *val == '1') {
        /* any stored Warning headers with warn-code 1xx (see section
         * 14.46) MUST be deleted from the cache entry and the forwarded
         * response.
         */
    }
    else {
        apr_table_addn(v, key, val);
    }
    return 1;
}
Ejemplo n.º 15
0
static authn_status pam_authenticate_with_login_password(request_rec * r, const char * pam_service,
	const char * login, const char * password, int steps) {
	pam_handle_t * pamh = NULL;
	struct pam_conv pam_conversation = { &pam_authenticate_conv, (void *) password };
	const char * stage = "PAM transaction failed for service";
	const char * param = pam_service;
	int ret;
	ret = pam_start(pam_service, login, &pam_conversation, &pamh);
	if (ret == PAM_SUCCESS) {
		const char * remote_host_or_ip = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL);
		if (remote_host_or_ip) {
			stage = "PAM pam_set_item PAM_RHOST failed for service";
			ret = pam_set_item(pamh, PAM_RHOST, remote_host_or_ip);
		}
	}
	if (ret == PAM_SUCCESS) {
		if (steps & _PAM_STEP_AUTH) {
			param = login;
			stage = "PAM authentication failed for user";
			ret = pam_authenticate(pamh, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
		}
		if ((ret == PAM_SUCCESS) && (steps & _PAM_STEP_ACCOUNT)) {
			param = login;
			stage = "PAM account validation failed for user";
			ret = pam_acct_mgmt(pamh, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK);
			if (ret == PAM_NEW_AUTHTOK_REQD) {
				authnz_pam_config_rec * conf = ap_get_module_config(r->per_dir_config, &authnz_pam_module);
				if (conf && conf->expired_redirect_url) {
					ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
						"mod_authnz_pam: PAM_NEW_AUTHTOK_REQD: redirect to [%s]",
						conf->expired_redirect_url);
					apr_table_addn(r->headers_out, "Location", format_location(r, conf->expired_redirect_url, login));
					return HTTP_TEMPORARY_REDIRECT;
				}
			}
		}
	}
	if (ret != PAM_SUCCESS) {
		const char * strerr = pam_strerror(pamh, ret);
		ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, "mod_authnz_pam: %s %s: %s", stage, param, strerr);
		apr_table_setn(r->subprocess_env, _EXTERNAL_AUTH_ERROR_ENV_NAME, apr_pstrdup(r->pool, strerr));
		pam_end(pamh, ret);
		return AUTH_DENIED;
	}
	apr_table_setn(r->subprocess_env, _REMOTE_USER_ENV_NAME, login);
	r->user = apr_pstrdup(r->pool, login);
	ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server, "mod_authnz_pam: PAM authentication passed for user %s", login);
	pam_end(pamh, ret);
	return AUTH_GRANTED;
}
Ejemplo n.º 16
0
static int copy_headers_out(void *vbaton, const char *key, const char *value)
{
    s_baton_t *ctx = vbaton;
    int done = 0;

    /* XXXXX: Special Treatment required for MANY other headers. fixme.*/
    switch (key[0]) {
    case 'c':
    case 'C':
        if (strcasecmp("Content-Type", key) == 0) {
            ap_set_content_type(ctx->r, value);
            done = 1;
            break;
        }
        else if (strcasecmp("Connection", key) == 0) {
            done = 1;
            break;
        }
        else if (strcasecmp("Content-Encoding", key) == 0) {
            done = 1;
            break;
        }
        else if (strcasecmp("Content-Length", key) == 0) {
            done = 1;
            break;
        }
        break;
    case 't':
    case 'T':
        if (strcasecmp("Transfer-Encoding", key) == 0) {
            done = 1;
            break;
        }
        break;
    default:
            break;
    }

    if (!done) {
        apr_table_addn(ctx->r->headers_out, key, value);
    }

    return 0;
}
Ejemplo n.º 17
0
int json_add_argument(modsec_rec *msr, const char *value, unsigned length)
{
    msc_arg *arg = (msc_arg *) NULL;

    /**
     * If we do not have a prefix, we cannot create a variable name
     * to reference this argument; for now we simply ignore these
     */
    if (!msr->json->current_key) {
        msr_log(msr, 3, "Cannot add scalar value without an associated key");
        return 1;
    }

    arg = (msc_arg *) apr_pcalloc(msr->mp, sizeof(msc_arg));

    /**
     * Argument name is 'prefix + current_key'
     */
    if (msr->json->prefix) {
        arg->name = apr_psprintf(msr->mp, "%s.%s", msr->json->prefix,
            msr->json->current_key);
    }
    else {
        arg->name = apr_psprintf(msr->mp, "%s", msr->json->current_key);
    }
    arg->name_len = strlen(arg->name);

    /**
     * Argument value is copied from the provided string
     */
    arg->value = apr_pstrmemdup(msr->mp, value, length);
    arg->value_len = length;
    arg->origin = "JSON";

    if (msr->txcfg->debuglog_level >= 9) {
        msr_log(msr, 9, "Adding JSON argument '%s' with value '%s'",
            arg->name, arg->value);
    }

    apr_table_addn(msr->arguments,
        log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *) arg);

    return 1;
}
Ejemplo n.º 18
0
/* This function deletes the cookie.
 *
 * Parameters:
 *  request_rec *r       The request we should clear the cookie in. We will
 *                       allocate any neccesary memory from r->pool.
 *
 * Returns:
 *  Nothing.
 */
void am_cookie_delete(request_rec *r)
{
    const char *name;
    const char *cookie_params;
    char *cookie;

    name = am_cookie_name(r);
    cookie_params = am_cookie_params(r);


    /* Format a cookie. To delete a cookie we set the expires-timestamp
     * to the past.
     */
    cookie = apr_psprintf(r->pool, "%s=NULL;"
                          " expires=Thu, 01-Jan-1970 00:00:00 GMT;"
                          " %s",
                          name, cookie_params);

    apr_table_addn(r->err_headers_out, "Set-Cookie", cookie);
}
Ejemplo n.º 19
0
/**
 * Set the cookie and embed the session within it.
 *
 * This function adds an RFC2109 compliant Set-Cookie header for
 * the cookie specified in SessionCookieName, and an RFC2965 compliant
 * Set-Cookie2 header for the cookie specified in SessionCookieName2.
 *
 * If specified, the optional cookie attributes will be added to
 * each cookie. If defaults are not specified, DEFAULT_ATTRS
 * will be used.
 *
 * On success, this method will return APR_SUCCESS.
 *
 * @param r The request pointer.
 * @param z A pointer to where the session will be written.
 */
static apr_status_t session_cookie_save(request_rec * r, session_rec * z)
{

    session_cookie_dir_conf *conf = ap_get_module_config(r->per_dir_config,
                                                    &session_cookie_module);

    /* don't cache auth protected pages */
    apr_table_addn(r->headers_out, "Cache-Control", "no-cache");

    /* create RFC2109 compliant cookie */
    if (conf->name_set) {
        if (z->encoded && z->encoded[0]) {
            ap_cookie_write(r, conf->name, z->encoded, conf->name_attrs,
                            z->maxage, r->headers_out, r->err_headers_out,
                            NULL);
        }
        else {
            ap_cookie_remove(r, conf->name, conf->name_attrs, r->headers_out,
                             r->err_headers_out, NULL);
        }
    }

    /* create RFC2965 compliant cookie */
    if (conf->name2_set) {
        if (z->encoded && z->encoded[0]) {
            ap_cookie_write2(r, conf->name2, z->encoded, conf->name2_attrs,
                             z->maxage, r->headers_out, r->err_headers_out,
                             NULL);
        }
        else {
            ap_cookie_remove2(r, conf->name2, conf->name2_attrs,
                              r->headers_out, r->err_headers_out, NULL);
        }
    }

    if (conf->name_set || conf->name2_set) {
        return OK;
    }
    return DECLINED;

}
Ejemplo n.º 20
0
/**
 * Remove an RFC2965 compliant cookie.
 *
 * @param r The request
 * @param name2 The name of the cookie.
 */
AP_DECLARE(apr_status_t) ap_cookie_remove2(request_rec * r, const char *name2, const char *attrs2, ...)
{
    apr_table_t *t;
    va_list vp;

    /* create RFC2965 compliant cookie */
    const char *rfc2965 = apr_pstrcat(r->pool, name2, "=;Max-Age=0;",
                                attrs2 ? attrs2 : CLEAR_ATTRS, NULL);
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00010) LOG_PREFIX
                  "user '%s' removed cookie2: '%s'", r->user, rfc2965);

    /* write the cookie to the header table(s) provided */
    va_start(vp, attrs2);
    while ((t = va_arg(vp, apr_table_t *))) {
        apr_table_addn(t, SET_COOKIE2, rfc2965);
    }
    va_end(vp);

    return APR_SUCCESS;

}
/* user apr_table_do to set session information in header http */
static int Auth_memCookie_DoSetHeader(void *rec, const char *szKey, const char *szValue)
{
    // strAuth_memCookie_config_rec *conf = NULL;
    request_rec *r = (request_rec*)rec;
    const char *szHeaderName = szKey;

    /* if key does not start with X-, preprent X-MCAC_ */
    if (strncasecmp(szHeaderName, "x-", 2) != 0)
        szHeaderName = apr_pstrcat(r->pool, "X-", szHeaderName, NULL);

    ap_log_rerror(APLOG_MARK,APLOG_DEBUG, 0,r,ERRTAG  "adding header: %s ", szHeaderName);

	/* set string header */
    if (apr_table_get(r->headers_in, szHeaderName) == NULL) {
        apr_table_addn(r->headers_in, szHeaderName, szValue);
    }
    else {
        apr_table_set(r->headers_in, szHeaderName, szValue);
    }
    return 1;
}
static void addCookie(request_rec *r, uint8_t *secret, int secretLen) {
    authn_google_config_rec *conf = ap_get_module_config(r->per_dir_config,
                                                       &authn_google_module);
	if (conf->cookieLife) {

		unsigned long exp = (apr_time_now() / (1000000) ) + conf->cookieLife;
		char *h = hash_cookie(r->pool,secret,secretLen,exp);
    char * cookie = apr_psprintf(r->pool,"google_authn=%s:%lu:%s",r->user,exp,h);
    if (conf->domain) {
      cookie = apr_pstrcat(r->pool,cookie,";domain=",conf->domain, NULL);
    }
    if (conf->path) {
      cookie = apr_pstrcat(r->pool,cookie,";path=",conf->path, NULL);
    }

  if (conf->debugLevel)
		ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
			"Created cookie expires %lu hash is %s Cookie: %s",exp,h,cookie);

		apr_table_addn(r->headers_out,"Set-Cookie",cookie);
	}
}
Ejemplo n.º 23
0
/* Parse form data from a string. The input string is preserved. */
apr_table_t *mapcache_http_parse_param_string(mapcache_context *r, char *args_str)
{
  apr_table_t *params;
  char *args = apr_pstrdup(r->pool,args_str);
  char *key;
  char *value;
  const char *delim = "&";
  char *last;
  if (args == NULL) {
    return apr_table_make(r->pool,0);
  }
  params = apr_table_make(r->pool,20);
  /* Split the input on '&' */
  for (key = apr_strtok(args, delim, &last); key != NULL;
       key = apr_strtok(NULL, delim, &last)) {
    /* key is a pointer to the key=value string */
    /*loop through key=value string to replace '+' by ' ' */
    for (value = key; *value; ++value) {
      if (*value == '+') {
        *value = ' ';
      }
    }

    /* split into Key / Value and unescape it */
    value = strchr(key, '=');
    if (value) {
      *value++ = '\0'; /* replace '=' by \0, thus terminating the key string */
      _mapcache_unescape_url(key);
      _mapcache_unescape_url(value);
    } else {
      value = "";
      _mapcache_unescape_url(key);
    }
    /* Store key/value pair in our form hash. */
    apr_table_addn(params, key, value);
  }
  return params;
}
Ejemplo n.º 24
0
/*
 * send a code/refresh request to the token endpoint and return the parsed contents
 */
static apr_byte_t oidc_proto_token_endpoint_request(request_rec *r,
		oidc_cfg *cfg, oidc_provider_t *provider, apr_table_t *params,
		char **id_token, char **access_token, char **token_type,
		int *expires_in, char **refresh_token) {

	const char *response = NULL;

	/* see if we need to do basic auth or auth-through-post-params (both applied through the HTTP POST method though) */
	const char *basic_auth = NULL;
	if ((provider->token_endpoint_auth == NULL)
			|| (apr_strnatcmp(provider->token_endpoint_auth,
					"client_secret_basic") == 0)) {
		basic_auth = apr_psprintf(r->pool, "%s:%s", provider->client_id,
				provider->client_secret);
	} else {
		apr_table_addn(params, "client_id", provider->client_id);
		apr_table_addn(params, "client_secret", provider->client_secret);
	}

	/* add any configured extra static parameters to the token endpoint */
	oidc_util_table_add_query_encoded_params(r->pool, params,
			provider->token_endpoint_params);

	/* send the refresh request to the token endpoint */
	if (oidc_util_http_post_form(r, provider->token_endpoint_url, params,
			basic_auth, NULL, provider->ssl_validate_server, &response,
			cfg->http_timeout_long, cfg->outgoing_proxy) == FALSE) {
		oidc_warn(r, "error when calling the token endpoint (%s)",
				provider->token_endpoint_url);
		return FALSE;
	}

	/* check for errors, the response itself will have been logged already */
	json_t *result = NULL;
	if (oidc_util_decode_json_and_check_error(r, response, &result) == FALSE)
		return FALSE;

	/* get the id_token from the parsed response */
	oidc_json_object_get_string(r->pool, result, "id_token", id_token, NULL);

	/* get the access_token from the parsed response */
	oidc_json_object_get_string(r->pool, result, "access_token", access_token,
			NULL);

	/* get the token type from the parsed response */
	oidc_json_object_get_string(r->pool, result, "token_type", token_type,
			NULL);

	/* check the new token type */
	if (token_type != NULL) {
		if (oidc_proto_validate_token_type(r, provider, *token_type) == FALSE) {
			oidc_warn(r, "access token type did not validate, dropping it");
			*access_token = NULL;
		}
	}

	/* get the expires_in value */
	oidc_json_object_get_int(r->pool, result, "expires_in", expires_in, -1);

	/* get the refresh_token from the parsed response */
	oidc_json_object_get_string(r->pool, result, "refresh_token", refresh_token,
			NULL);

	json_decref(result);

	return TRUE;
}
Ejemplo n.º 25
0
static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob, unsigned int blob_size,
    int log_vars)
{
    apr_table_t *col = NULL;
    unsigned int blob_offset;

    col = apr_table_make(msr->mp, 32);
    if (col == NULL) return NULL;

    /* ENH verify the first 3 bytes (header) */

    blob_offset = 3;
    while (blob_offset + 1 < blob_size) {
        msc_string *var = apr_pcalloc(msr->mp, sizeof(msc_string));

        var->name_len = (blob[blob_offset] << 8) + blob[blob_offset + 1];
        if (var->name_len == 0) {
            /* Is the length a name length, or just the end of the blob? */
            if (blob_offset < blob_size - 2) {
                /* This should never happen as the name length
                 * includes the terminating NUL and should be 1 for ""
                 */
                if (msr->txcfg->debuglog_level >= 9) {
                    msr_log(msr, 9, "BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset));
                }
                msr_log(msr, 4, "Possibly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1);
            }
            break;
        }
        else if (var->name_len > 65536) {
            /* This should never happen as the length is restricted on store
             * to 65536.
             */
            if (msr->txcfg->debuglog_level >= 9) {
                msr_log(msr, 9, "BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset));
            }
            msr_log(msr, 4, "Possibly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1);
            break;
        }

        blob_offset += 2;
        if (blob_offset + var->name_len > blob_size) return NULL;
        var->name = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->name_len - 1);
        blob_offset += var->name_len;
        var->name_len--;

        var->value_len = (blob[blob_offset] << 8) + blob[blob_offset + 1];
        blob_offset += 2;

        if (blob_offset + var->value_len > blob_size) return NULL;
        var->value = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->value_len - 1);
        blob_offset += var->value_len;
        var->value_len--;

        if (log_vars && (msr->txcfg->debuglog_level >= 9)) {
            msr_log(msr, 9, "Read variable: name \"%s\", value \"%s\".",
                log_escape_ex(msr->mp, var->name, var->name_len),
                log_escape_ex(msr->mp, var->value, var->value_len));
        }

        apr_table_addn(col, var->name, (void *)var);
    }

    return col;
}
Ejemplo n.º 26
0
static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
{
    ap_filter_provider_t *provider;
    int match = 0;
    const char *err = NULL;
    request_rec *r = f->r;
    harness_ctx *ctx = f->ctx;
    provider_ctx *pctx;
#ifndef NO_PROTOCOL
    unsigned int proto_flags;
    mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
                                                &filter_module);
#endif

    /* Check registered providers in order */
    for (provider = filter->providers; provider; provider = provider->next) {
        if (provider->expr) {
            match = ap_expr_exec(r, provider->expr, &err);
            if (err) {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01379)
                              "Error evaluating filter dispatch condition: %s",
                              err);
                match = 0;
            }
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                          "Expression condition for '%s' %s",
                          provider->frec->name,
                          match ? "matched" : "did not match");
        }
        else if (r->content_type) {
            const char **type = provider->types;
            size_t len = strcspn(r->content_type, "; \t");
            AP_DEBUG_ASSERT(type != NULL);
            ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
                          "Content-Type '%s' ...", r->content_type);
            while (*type) {
                /* Handle 'content-type;charset=...' correctly */
                if (strncmp(*type, r->content_type, len) == 0
                    && (*type)[len] == '\0') {
                    ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
                                  "... matched '%s'", *type);
                    match = 1;
                    break;
                }
                else {
                    ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
                                  "... did not match '%s'", *type);
                }
                type++;
            }
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                          "Content-Type condition for '%s' %s",
                          provider->frec->name,
                          match ? "matched" : "did not match");
        }
        else {
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
                          "Content-Type condition for '%s' did not match: "
                          "no Content-Type", provider->frec->name);
        }

        if (match) {
            /* condition matches this provider */
#ifndef NO_PROTOCOL
            /* check protocol
             *
             * FIXME:
             * This is a quick hack and almost certainly buggy.
             * The idea is that by putting this in mod_filter, we relieve
             * filter implementations of the burden of fixing up HTTP headers
             * for cases that are routinely affected by filters.
             *
             * Default is ALWAYS to do nothing, so as not to tread on the
             * toes of filters which want to do it themselves.
             *
             */
            proto_flags = provider->frec->proto_flags;

            /* some specific things can't happen in a proxy */
            if (r->proxyreq) {
                if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
                    /* can't use this provider; try next */
                    continue;
                }

                if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
                    const char *str = apr_table_get(r->headers_out,
                                                    "Cache-Control");
                    if (str) {
                        if (ap_strcasestr(str, "no-transform")) {
                            /* can't use this provider; try next */
                            continue;
                        }
                    }
                    apr_table_addn(r->headers_out, "Warning",
                                   apr_psprintf(r->pool,
                                                "214 %s Transformation applied",
                                                r->hostname));
                }
            }

            /* things that are invalidated if the filter transforms content */
            if (proto_flags & AP_FILTER_PROTO_CHANGE) {
                apr_table_unset(r->headers_out, "Content-MD5");
                apr_table_unset(r->headers_out, "ETag");
                if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
                    apr_table_unset(r->headers_out, "Content-Length");
                }
            }

            /* no-cache is for a filter that has different effect per-hit */
            if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
                apr_table_unset(r->headers_out, "Last-Modified");
                apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
            }

            if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
                apr_table_setn(r->headers_out, "Accept-Ranges", "none");
            }
            else if (rctx && rctx->range) {
                /* restore range header we saved earlier */
                apr_table_setn(r->headers_in, "Range", rctx->range);
                rctx->range = NULL;
            }
#endif
            for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
                if (pctx->provider == provider) {
                    ctx->fctx = pctx->ctx ;
                }
            }
            ctx->func = provider->frec->filter_func.out_func;
            return 1;
        }
    }

    /* No provider matched */
    return 0;
}
Ejemplo n.º 27
0
static int set_cookie_doo_doo(void *v, const char *key, const char *val)
{
    apr_table_addn(v, key, val);
    return 1;
}
Ejemplo n.º 28
0
AP_DECLARE(void) ap_add_common_vars(request_rec *r)
{
    apr_table_t *e;
    server_rec *s = r->server;
    conn_rec *c = r->connection;
    const char *rem_logname;
    char *env_path;
#if defined(WIN32) || defined(OS2) || defined(BEOS)
    char *env_temp;
#endif
    const char *host;
    const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in);
    const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
    int i;
    apr_port_t rport;

    /* use a temporary apr_table_t which we'll overlap onto
     * r->subprocess_env later
     * (exception: if r->subprocess_env is empty at the start,
     * write directly into it)
     */
    if (apr_is_empty_table(r->subprocess_env)) {
        e = r->subprocess_env;
    }
    else {
        e = apr_table_make(r->pool, 25 + hdrs_arr->nelts);
    }

    /* First, add environment vars from headers... this is as per
     * CGI specs, though other sorts of scripting interfaces see
     * the same vars...
     */

    for (i = 0; i < hdrs_arr->nelts; ++i) {
        if (!hdrs[i].key) {
            continue;
        }

        /* A few headers are special cased --- Authorization to prevent
         * rogue scripts from capturing passwords; content-type and -length
         * for no particular reason.
         */

        if (!strcasecmp(hdrs[i].key, "Content-type")) {
            apr_table_addn(e, "CONTENT_TYPE", hdrs[i].val);
        }
        else if (!strcasecmp(hdrs[i].key, "Content-length")) {
            apr_table_addn(e, "CONTENT_LENGTH", hdrs[i].val);
        }
        /*
         * You really don't want to disable this check, since it leaves you
         * wide open to CGIs stealing passwords and people viewing them
         * in the environment with "ps -e".  But, if you must...
         */
#ifndef SECURITY_HOLE_PASS_AUTHORIZATION
        else if (!strcasecmp(hdrs[i].key, "Authorization")
                 || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
            continue;
        }
#endif
        else {
            apr_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
        }
    }

    if (!(env_path = getenv("PATH"))) {
        env_path = DEFAULT_PATH;
    }
    apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_path));

#ifdef WIN32
    if ((env_temp = getenv("SystemRoot")) != NULL) {
        apr_table_addn(e, "SystemRoot", env_temp);
    }
    if ((env_temp = getenv("COMSPEC")) != NULL) {
        apr_table_addn(e, "COMSPEC", env_temp);
    }
    if ((env_temp = getenv("PATHEXT")) != NULL) {
        apr_table_addn(e, "PATHEXT", env_temp);
    }
    if ((env_temp = getenv("WINDIR")) != NULL) {
        apr_table_addn(e, "WINDIR", env_temp);
    }
#endif

#ifdef OS2
    if ((env_temp = getenv("COMSPEC")) != NULL) {
        apr_table_addn(e, "COMSPEC", env_temp);
    }
    if ((env_temp = getenv("ETC")) != NULL) {
        apr_table_addn(e, "ETC", env_temp);
    }
    if ((env_temp = getenv("DPATH")) != NULL) {
        apr_table_addn(e, "DPATH", env_temp);
    }
    if ((env_temp = getenv("PERLLIB_PREFIX")) != NULL) {
        apr_table_addn(e, "PERLLIB_PREFIX", env_temp);
    }
#endif

#ifdef BEOS
    if ((env_temp = getenv("LIBRARY_PATH")) != NULL) {
        apr_table_addn(e, "LIBRARY_PATH", env_temp);
    }
#endif

    apr_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r));
    apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_banner());
    apr_table_addn(e, "SERVER_NAME",
                   ap_escape_html(r->pool, ap_get_server_name(r)));
    apr_table_addn(e, "SERVER_ADDR", r->connection->local_ip);  /* Apache */
    apr_table_addn(e, "SERVER_PORT",
                  apr_psprintf(r->pool, "%u", ap_get_server_port(r)));
    host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL);
    if (host) {
        apr_table_addn(e, "REMOTE_HOST", host);
    }
    apr_table_addn(e, "REMOTE_ADDR", c->remote_ip);
    apr_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r));    /* Apache */
    apr_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */
    apr_table_addn(e, "SCRIPT_FILENAME", r->filename);  /* Apache */

    rport = c->remote_addr->port;
    apr_table_addn(e, "REMOTE_PORT", apr_itoa(r->pool, rport));

    if (r->user) {
        apr_table_addn(e, "REMOTE_USER", r->user);
    }
    else if (r->prev) {
        request_rec *back = r->prev;

        while (back) {
            if (back->user) {
                apr_table_addn(e, "REDIRECT_REMOTE_USER", back->user);
                break;
            }
            back = back->prev;
        }
    }
    if (r->ap_auth_type) {
        apr_table_addn(e, "AUTH_TYPE", r->ap_auth_type);
    }
    rem_logname = ap_get_remote_logname(r);
    if (rem_logname) {
        apr_table_addn(e, "REMOTE_IDENT", apr_pstrdup(r->pool, rem_logname));
    }

    /* Apache custom error responses. If we have redirected set two new vars */

    if (r->prev) {
        if (r->prev->args) {
            apr_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args);
        }
        if (r->prev->uri) {
            apr_table_addn(e, "REDIRECT_URL", r->prev->uri);
        }
    }

    if (e != r->subprocess_env) {
      apr_table_overlap(r->subprocess_env, e, APR_OVERLAP_TABLES_SET);
    }
}
Ejemplo n.º 29
0
/*
 * based on an account name, perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata
 */
apr_byte_t oidc_proto_account_based_discovery(request_rec *r, oidc_cfg *cfg,
		const char *acct, char **issuer) {

	// TODO: maybe show intermediate/progress screen "discovering..."

	oidc_debug(r, "enter, acct=%s", acct);

	const char *resource = apr_psprintf(r->pool, "acct:%s", acct);
	const char *domain = strrchr(acct, '@');
	if (domain == NULL) {
		oidc_error(r, "invalid account name");
		return FALSE;
	}
	domain++;
	const char *url = apr_psprintf(r->pool, "https://%s/.well-known/webfinger",
			domain);

	apr_table_t *params = apr_table_make(r->pool, 1);
	apr_table_addn(params, "resource", resource);
	apr_table_addn(params, "rel", "http://openid.net/specs/connect/1.0/issuer");

	const char *response = NULL;
	if (oidc_util_http_get(r, url, params, NULL, NULL,
			cfg->provider.ssl_validate_server, &response,
			cfg->http_timeout_short, cfg->outgoing_proxy) == FALSE) {
		/* errors will have been logged by now */
		return FALSE;
	}

	/* decode and see if it is not an error response somehow */
	json_t *j_response = NULL;
	if (oidc_util_decode_json_and_check_error(r, response, &j_response) == FALSE)
		return FALSE;

	/* get the links parameter */
	json_t *j_links = json_object_get(j_response, "links");
	if ((j_links == NULL) || (!json_is_array(j_links))) {
		oidc_error(r, "response JSON object did not contain a \"links\" array");
		json_decref(j_response);
		return FALSE;
	}

	/* get the one-and-only object in the "links" array */
	json_t *j_object = json_array_get(j_links, 0);
	if ((j_object == NULL) || (!json_is_object(j_object))) {
		oidc_error(r,
				"response JSON object did not contain a JSON object as the first element in the \"links\" array");
		json_decref(j_response);
		return FALSE;
	}

	/* get the href from that object, which is the issuer value */
	json_t *j_href = json_object_get(j_object, "href");
	if ((j_href == NULL) || (!json_is_string(j_href))) {
		oidc_error(r,
				"response JSON object did not contain a \"href\" element in the first \"links\" array object");
		json_decref(j_response);
		return FALSE;
	}

	*issuer = apr_pstrdup(r->pool, json_string_value(j_href));

	oidc_debug(r,
			"returning issuer \"%s\" for account \"%s\" after doing successful webfinger-based discovery",
			*issuer, acct);

	json_decref(j_response);

	return TRUE;
}
// See here for the structure of request_rec:
// http://ci.apache.org/projects/httpd/trunk/doxygen/structrequest__rec.html
static int hook(request_rec *r)
{
    settings_rec *cfg = ap_get_module_config( r->per_dir_config,
                                              &querystring2cookie_module );

    /* Do not run in subrequests, don't run if not enabled */
    if( !(cfg->enabled || r->main) ) {
        return DECLINED;
    }

    /* No query string? nothing to do here */
    if( !(r->args) || strlen( r->args ) < 1 ) {
        return DECLINED;
    }

    /* skip if dnt headers are present? */
    if( !(cfg->enabled_if_dnt) && apr_table_get( r->headers_in, "DNT" ) ) {
        _DEBUG && fprintf( stderr, "DNT header sent: declined\n" );
        return DECLINED;
    }

    _DEBUG && fprintf( stderr, "Query string: '%s'\n", r->args );

    // ***********************************
    // Calculate expiry time
    // ***********************************

    // The expiry time. We can't use max-age because IE6 - IE8 do not
    // support it :(
    char *expires = "";

    if( cfg->cookie_expires > 0 ) {

        apr_time_exp_t tms;
        apr_time_exp_gmt( &tms, r->request_time
                              + apr_time_from_sec( cfg->cookie_expires ) );

        expires = apr_psprintf( r->pool,
                            "expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
                            apr_day_snames[tms.tm_wday],
                            tms.tm_mday,
                            apr_month_snames[tms.tm_mon],
                            tms.tm_year % 100,
                            tms.tm_hour, tms.tm_min, tms.tm_sec
                        );
    }

    // ***********************************
    // Find key/value pairs
    // ***********************************

    // keep track of how much data we've been writing - there's a limit to how
    // much a browser will store per domain (usually 4k) so we want to make sure
    // it's not getting flooded.
    int total_pair_size = 0;

    // This holds the final cookie we'll send back - make sure to initialize
    // or it can point at garbage!
    char *cookie = "";

    // string to use as the cookie name (together with the prefix) - make sure to
    // initialize or it can point at garbage!
    char *cookie_name = "";

    // Iterate over the key/value pairs
    char *last_pair;
    char *pair = apr_strtok( apr_pstrdup( r->pool, r->args ), "&", &last_pair );

    _DEBUG && fprintf( stderr, "about to parse query string for pairs\n" );

    _DEBUG && fprintf( stderr, "looking for cookie name in %s\n", cfg->cookie_name_from );

    while( pair != NULL ) {

        // length of the substr before the = sign (or index of the = sign)
        int contains_equals_at = strcspn( pair, "=" );

        // Does not contains a =, or starts with a =, meaning it's garbage
        if( !strstr(pair, "=") || contains_equals_at < 1 ) {
            _DEBUG && fprintf( stderr, "invalid pair: %s\n", pair );

            // And get the next pair -- has to be done at every break
            pair = apr_strtok( NULL, "&", &last_pair );
            continue;
        }

        _DEBUG && fprintf( stderr, "pair looks valid: %s - = sign at pos: %i\n",
                            pair, contains_equals_at );

        // So this IS a key value pair. Let's get the key and the value.
        // first, get the key - everything up to the first =
        char *key   = apr_pstrndup( r->pool, pair, contains_equals_at );

        // now get the value, everything AFTER the = sign. We do that by
        // moving the pointer past the = sign.
        char *value = apr_pstrdup( r->pool, pair );
        value += contains_equals_at + 1;

        _DEBUG && fprintf( stderr, "pair=%s, key=%s, value=%s\n", pair, key, value );

        // you want us to use a name from the query string?
        // This might be that name.
        if( cfg->cookie_name_from && !(strlen(cookie_name)) &&
            strcasecmp( key, cfg->cookie_name_from ) == 0
        ) {
            // get everything after the = sign -- that's our name.
            cookie_name = apr_pstrcat( r->pool, cfg->cookie_prefix, value, NULL );

            _DEBUG && fprintf( stderr, "using %s as the cookie name\n", cookie_name );

            // And get the next pair -- has to be done at every break
            pair = apr_strtok( NULL, "&", &last_pair );
            continue;

        // may be on the ignore list
        } else {

            // may have to continue the outer loop, use this as a marker
            int do_continue = 0;

            // you might have blacklisted this key; let's check
            // Following tutorial code here again:
            // http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-19.html
            int i;
            for( i = 0; i < cfg->qs_ignore->nelts; i++ ) {

                char *ignore = ((char **)cfg->qs_ignore->elts)[i];

                _DEBUG && fprintf( stderr, "processing ignore %s against pair %s\n",
                                        ignore, pair );

                // it's indeed on the ignore list; move on
                // do this by comparing the string length first - if the length of
                // the ignore key and the key are identical AND the first N characters
                // of the string are the same
                if( strcasecmp( key, ignore ) == 0 ) {
                    _DEBUG && fprintf( stderr, "pair %s is on the ignore list: %s\n",
                                        pair, ignore );

                    // signal to continue the outer loop; we found an ignore match
                    do_continue = 1;
                    break;
                }
            }

            // ignore match found, move on
            if( do_continue ) {
                // And get the next pair -- has to be done at every break
                pair = apr_strtok( NULL, "&", &last_pair );
                continue;
            }
        }

        // looks like a valid key=value declaration
        _DEBUG && fprintf( stderr, "valid key/value pair: %s\n", pair );

        // Now, the key may contain URL unsafe characters, which are also
        // not allowed in Cookies. See here:
        // http://tools.ietf.org/html/rfc2068, section 2.2 on 'tspecials'
        //
        // So instead, we url encode the key. The size of the key is max
        // 3 times old key size (any char gets encoded into %xx), so allow
        // for that space. See the documentation here:
        // http://httpd.apache.org/apreq/docs/libapreq2/apreq__util_8h.html#785be2ceae273b0a7b2ffda223b2ebae
        char *escaped_key   = apreq_escape( r->pool, key, strlen(key) );
        char *escaped_value = apreq_escape( r->pool, value, strlen(value) );

        _DEBUG && fprintf( stderr, "Original key: %s - Escaped key: %s\n", key, escaped_key );
        _DEBUG && fprintf( stderr, "Original value: %s - Escaped value: %s\n", value, escaped_value );

        // Now, let's do some transposing: The '=' sign needs to be replaced
        // with whatever the separator is. It can't be a '=' sign, as that's
        // illegal in cookies. The string may be larger than a single char,
        // so split the string and do the magix.

        // This makes key[delim]value - redefining pair here is safe, we're
        // just using it for printing now.
        char *key_value = apr_pstrcat( r->pool,
                                       escaped_key,
                                       cfg->cookie_key_value_delimiter,
                                       escaped_value,
                                       NULL
                                    );

        int this_pair_size = strlen( key_value );

        // Make sure the individual pair, as well as the whole thing doesn't
        // get too long

        _DEBUG && fprintf( stderr,
                "this pair size: %i, total pair size: %i, max size: %i\n",
                this_pair_size, total_pair_size, cfg->cookie_max_size  );

        if( (this_pair_size <= cfg->cookie_max_size) &&
            (total_pair_size + this_pair_size <= cfg->cookie_max_size)
        ) {

            cookie = apr_pstrcat( r->pool,
                                  cookie,       // the cookie so far
                                  // If we already have pairs in here, we need the
                                  // delimiter, otherwise we don't.
                                  (strlen(cookie) ? cfg->cookie_pair_delimiter : ""),
                                  key_value,    // the next pair.
                                  NULL
                            );

            // update the book keeping - this is the new size including delims
            total_pair_size = strlen(cookie);

            _DEBUG && fprintf( stderr, "this pair size: %i, total pair size: %i\n",
                                    this_pair_size, total_pair_size );

        } else {
            _DEBUG && fprintf( stderr,
                "Pair size too long to add: %s (this: %i total: %i max: %i)\n",
                key_value, this_pair_size, total_pair_size, cfg->cookie_max_size );
        }

        // and move the pointer
        pair = apr_strtok( NULL, "&", &last_pair );
    }

     // So you told us we should use a cookie name from the query string,
     // but we never found it in there. That's a problem.
     if( cfg->cookie_name_from && !strlen(cookie_name) ) {

         // r->err_headers_out also honors non-2xx responses and
         // internal redirects. See the patch here:
         // http://svn.apache.org/viewvc?view=revision&revision=1154620
         apr_table_addn( r->err_headers_out,
             "X-QS2Cookie",
             apr_pstrcat( r->pool,
                 "ERROR: Did not detect cookie name - missing QS argument: ",
                 cfg->cookie_name_from,
                 NULL
             )
         );

     // Let's return the output
     } else {

         // we got here without a cookie name? We can use the default.
         if( !strlen(cookie_name) ) {
             _DEBUG && fprintf( stderr, "explicitly setting cookie name to: %s\n",
                                         cfg->cookie_name );

             cookie_name = apr_pstrcat( r->pool,
                                        cfg->cookie_prefix, cfg->cookie_name,
                                        NULL );
         }

         _DEBUG && fprintf( stderr, "cookie name: %s\n", cookie_name );

        // XXX use a sprintf format for more flexibility?
        if( cfg->encode_in_key ) {
            _DEBUG && fprintf( stderr, "%s: encoding in the key\n", cookie_name );

            cookie = apr_pstrcat( r->pool,
                            // cookie data
                            cookie_name, cfg->cookie_pair_delimiter, cookie, "=",
                            // The format is different on 32 (%ld) vs 64bit (%lld), so
                            // use the constant for it instead. You can find this in apr.h
                            apr_psprintf( r->pool, "%" APR_OFF_T_FMT, apr_time_sec(apr_time_now()) ),
                            NULL
                         );
        } else {
            _DEBUG && fprintf( stderr, "%s: encoding in the value\n", cookie_name );
            cookie = apr_pstrcat( r->pool, cookie_name, "=", cookie, NULL );

        }

        // And now add the meta data to the cookie
        cookie = apr_pstrcat( r->pool, cookie, "; ",
                                "path=/; ", cfg->cookie_domain, expires,
                                NULL );

        _DEBUG && fprintf( stderr, "cookie: %s\n", cookie );

        // r->err_headers_out also honors non-2xx responses and
        // internal redirects. See the patch here:
        // http://svn.apache.org/viewvc?view=revision&revision=1154620
        apr_table_addn( r->err_headers_out, "Set-Cookie", cookie );
    }

    return OK;
}