Changes with Apache 2.3.0
[ When backported to 2.2.x, remove entry from this file ]
+ *) mod_session: Add a generic session interface to unify the different
+ attempts at saving persistent sessions across requests.
+ [Graham Leggett]
+
*) core, authn/z: Avoid calling access control hooks for internal requests
with configurations which match those of initial request. Revert to
original behaviour (call access control hooks for internal requests
APR_ADDTO(INCLUDES, [-I\$(top_builddir)/include])
fi
-APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/os/\$(OS_DIR) -I\$(top_srcdir)/server/mpm/\$(MPM_SUBDIR_NAME) -I\$(top_srcdir)/modules/http -I\$(top_srcdir)/modules/filters -I\$(top_srcdir)/modules/proxy -I\$(top_srcdir)/include -I\$(top_srcdir)/modules/generators -I\$(top_srcdir)/modules/mappers -I\$(top_srcdir)/modules/database])
+APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/os/\$(OS_DIR) -I\$(top_srcdir)/server/mpm/\$(MPM_SUBDIR_NAME) -I\$(top_srcdir)/modules/http -I\$(top_srcdir)/modules/filters -I\$(top_srcdir)/modules/proxy -I\$(top_srcdir)/modules/session -I\$(top_srcdir)/include -I\$(top_srcdir)/modules/generators -I\$(top_srcdir)/modules/mappers -I\$(top_srcdir)/modules/database])
# apr/apr-util --includes may pick up system paths for dependent
# libraries, so ensure these are later in INCLUDES than local source
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd">
+<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>
+<!-- $LastChangedRevision: 634760 $ -->
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<modulesynopsis metafile="mod_session.xml.meta">
+
+<name>mod_session</name>
+<description>Session support</description>
+<status>Extension</status>
+<sourcefile>mod_session.c</sourcefile>
+<identifier>session_module</identifier>
+
+<summary>
+ <note type="warning"><title>Warning</title>
+ <p>The session modules make use of HTTP cookies, and as such can fall
+ victim to Cross Site Scripting attacks, or expose potentially private
+ information to clients. Please ensure that the relevant risks have
+ been taken into account before enabling the session functionality on
+ your server.</p>
+ </note>
+
+ <p>This module provides support for a server wide per user session
+ interface. Sessions can be used for keeping track of whether a user
+ has been logged in, or for other per user information that should
+ be kept available across requests.</p>
+
+ <p>Sessions may be stored on the server, or may be stored on the
+ browser. Sessions may also be optionally encrypted for added security.
+ These features are divided into several modules in addition to
+ <module>mod_session</module>: <module>mod_session_crypto</module>,
+ <module>mod_session_cookie</module> and <module>mod_session_dbd</module>.
+ Depending on the server requirements, load the appropriate modules
+ into the server (either statically at compile time or dynamically
+ via the <directive module="mod_so">LoalModule</directive> directive).</p>
+
+ <p>Sessions may be manipulated from other modules that depend on the
+ session, or the session may be read from and written to using
+ environment variables and HTTP headers, as appropriate.</p>
+
+</summary>
+<seealso><module>mod_session_cookie</module></seealso>
+<seealso><module>mod_session_crypto</module></seealso>
+<seealso><module>mod_session_dbd</module></seealso>
+
+ <section id="whatisasession"><title>What is a session?</title>
+ <p>At the core of the session interface is a table of key and value pairs
+ that are made accessible across browser requests.</p>
+
+ <p>These pairs can be set any valid set of strings, as needed by the
+ application making use of the session.</p>
+
+ </section>
+ <section id="whocanuseasession"><title>Who can use a session?</title>
+ <p>The session interface is primarily developed for the use by other
+ server modules, such as <module>mod_auth_form</module>, however CGI
+ based applications can optionally be granted access to the contents
+ of the session via the HTTP_SESSION environment variable. Sessions
+ have the option to be modified and/or updated by inserting an HTTP
+ response header containing the new session parameters.</p>
+
+ </section>
+ <section id="serversession"><title>Keeping sessions on the server</title>
+ <p>Apache can be configured to keep track of per user sessions stored
+ on a particular server or group of servers. This functionality is
+ similar to the sessions available in typical application servers.</p>
+
+ <p>If configured, sessions are tracked through the use of a session ID that
+ is stored inside a cookie, or extracted from the parameters embedded
+ within the URL query string, as found in a typical GET request.</p>
+
+ <p>As the contents of the session are stored exclusively on the server,
+ there is an expectation of privacy of the contents of the session. This
+ does have performance and resource implications should a large number
+ of sessions be present, or where a large number of webservers have to
+ share sessions with one another.</p>
+
+ <p>The <module>mod_session_dbd</module> module allows the storage of user
+ sessions within a SQL database via <module>mod_dbd</module>.</p>
+
+ </section> <!-- /serversession -->
+
+ <section id="browsersession"><title>Keeping sessions on the browser</title>
+ <p>Where keeping track of a session on a server is too resource
+ intensive or inconvenient, the option exists to store the contents
+ of the session within a cookie on the client browser instead.</p>
+
+ <p>This has the advantage that minimal resources are required on the
+ server to keep track of sessions, and multiple servers within a server
+ farm have no need to share session information.</p>
+
+ <p>The contents of the session however are exposed to the client, with a
+ corresponding risk of a loss of privacy. The
+ <module>mod_session_crypto</module> module can be configured to encrypt the
+ contents of the session before writing the session to the client.</p>
+
+ <p>The <module>mod_session_cookie</module> allows the storage of user
+ sessions on the browser within an HTTP cookie.</p>
+
+ </section> <!-- /browsersession -->
+
+ <section id="basicexamples"><title>Basic Examples</title>
+
+ <p>Creating a session is as simple as turning the session on, and deciding
+ where the session will be stored. In this example, the session will be
+ stored on the browser, in a cookie called <code>session</code>.</p>
+
+ <example><title>Browser based session</title>
+ Session On<br />
+ SessionCookieName session path=/<br />
+ </example>
+
+ <p>The session is not useful unless it can be written to or read from. The
+ following example shows how values can be injected into the session through
+ the use of a predetermined HTTP response header called
+ <code>X-Replace-Session</code>.</p>
+
+ <example><title>Writing to a session</title>
+ Session On<br />
+ SessionCookieName session path=/<br />
+ SessionHeader X-Replace-Session<br />
+ </example>
+
+ <p>The header should contain name value pairs expressed in the same format
+ as a query string in a URL, as in the example below. Setting a key to the
+ empty string has the effect of removing that key from the session.</p>
+
+ <example><title>CGI to write to a session</title>
+ #!/bin/bash<br />
+ echo "Content-Type: text/plain"<br />
+ echo "X-Replace-Session: key1=foo&key2=&key3=bar"<br />
+ echo<br />
+ env<br />
+ </example>
+
+ <p>If configured, the session can be read back from the HTTP_SESSION
+ environment variable. By default, the session is kept private, so this
+ has to be explicitly turned on with the
+ <directive module="mod_session">SessionEnv</directive> directive.</p>
+
+ <example><title>Read from a session</title>
+ Session On<br />
+ SessionEnv On<br />
+ SessionCookieName session path=/<br />
+ SessionHeader X-Replace-Session<br />
+ </example>
+
+ <p>Once read, the CGI variable <code>HTTP_SESSION</code> should contain
+ the value <code>key1=foo&key3=bar</code>.</p>
+
+ </section>
+ <section id="sessionprivacy"><title>Session Privacy</title>
+
+ <p>Using the "show cookies" feature of your browser, you would have seen
+ a clear text representation of the session. This could potentially be a
+ problem should the end user need to be kept unaware of the contents of
+ the session, or where a third party could gain unauthorised access to the
+ data within the session.</p>
+
+ <p>The contents of the session can be optionally encrypted before being
+ placed on the browser using the <module>mod_session_crypto</module>
+ module.</p>
+
+ <example><title>Browser based encrypted session</title>
+ Session On<br />
+ SessionCryptoPassphrase secret<br />
+ SessionCookieName session path=/<br />
+ </example>
+
+ <p>The session will be automatically decrypted on load, and encrypted on
+ save by Apache, the underlying application using the session need have
+ no knowledge that encryption is taking place.</p>
+
+ <p>Sessions stored on the server rather than on the browser can also be
+ encrypted as needed, offering privacy where potentially sensitive
+ information is being shared between webservers in a server farm using
+ the <module>mod_session_dbd</module> module.</p>
+
+ </section>
+ <section id="cookieprivacy"><title>Cookie Privacy</title>
+
+ <p>The HTTP cookie mechanism also offers privacy features, such as the
+ ability to restrict cookie transport to SSL protected pages only, or
+ to prevent browser based javascript from gaining access to the contents
+ of the cookie.</p>
+
+ <note type="warning"><title>Warning</title>
+ <p>Some of the HTTP cookie privacy features are either non standard, or
+ are not implemented consistently across browsers. The session modules
+ allow you to set cookie parameters, but it makes no guarantee that privacy
+ will be respected by the browser. If security is a concern, use the
+ <module>mod_session_crypto</module> to encrypt the contents of the session,
+ or store the session on the server using the <module>mod_session_dbd</module>
+ module.</p>
+ </note>
+
+ <p>Standard cookie parameters can be specified after the name of the cookie,
+ as in the example below.</p>
+
+ <example><title>Setting cookie parameters</title>
+ Session On<br />
+ SessionCryptoPassphrase secret<br />
+ SessionCookieName session path=/private;domain=example.com;httponly;secure;<br />
+ </example>
+
+ <p>In cases where the Apache server forms the frontend for backend origin servers,
+ it is possible to have the session cookies removed from the incoming HTTP headers using
+ the <directive module="mod_session_cookie">SessionCookieRemove</directive> directive.
+ This keeps the contents of the session cookies from becoming accessible from the
+ backend server.
+ </p>
+
+ </section>
+ <section id="authentication"><title>Session Support for Authentication</title>
+
+ <p>As is possible within many application servers, authentication modules can use
+ a session for storing the username and password after login. The
+ <module>mod_auth_form</module> saves the user's login name and password within
+ the session.</p>
+
+ <example><title>Form based authentication</title>
+ Session On<br />
+ SessionCryptoPassphrase secret<br />
+ SessionCookieName session path=/<br />
+ AuthFormProvider file<br />
+ AuthUserFile conf/passwd<br />
+ AuthType form<br />
+ AuthName realm<br />
+ ...<br />
+ </example>
+
+ <p>See the <module>mod_auth_form</module> module for documentation and complete
+ examples.</p>
+
+ </section>
+
+<directivesynopsis>
+<name>Session</name>
+<description>Enables a session for the current directory or location</description>
+<syntax>Session On|Off</syntax>
+<default>Session Off</default>
+<contextlist><context>directory</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.0 and later</compatibility>
+
+<usage>
+ <p>The <directive>Session</directive> directive enables a session for the
+ directory or location container. Further directives control where the
+ session will be stored and how privacy is maintained.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>SessionMaxAge</name>
+<description>Define a maximum age in seconds for a session</description>
+<syntax>SessionMaxAge <var>maxage</var></syntax>
+<default>SessionMaxAge 0</default>
+<contextlist><context>directory</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.0 and later</compatibility>
+
+<usage>
+ <p>The <directive>SessionMaxAge</directive> directive defines a time limit
+ for which a session will remain valid. When a session is saved, this time
+ limit is reset and an existing session can be continued. If a session
+ becomes older than this limit without a request to the server to refresh
+ the session, the session will time out and be removed. Where a session is
+ used to stored user login details, this has the effect of logging the user
+ out automatically after the given time.</p>
+
+ <p>Setting the maxage to zero disables session expiry.</p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>SessionEnv</name>
+<description>Control whether the contents of the session are written to the
+<var>HTTP_SESSION</var> environment variable</description>
+<syntax>SessionEnv On|Off</syntax>
+<default>SessionEnv Off</default>
+<contextlist><context>directory</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.0 and later</compatibility>
+
+<usage>
+ <p>If set to <var>On</var>, the <directive>SessionEnv</directive> directive
+ causes the contents of the session to be written to a CGI environment
+ variable called <var>HTTP_SESSION</var>.</p>
+
+ <p>The string is written in the URL query format, for example:</p>
+
+ <example>
+ <code>key1=foo&key3=bar</code>
+ </example>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>SessionHeader</name>
+<description>Import session updates from a given HTTP response header</description>
+<syntax>SessionHeader <var>header</var></syntax>
+<default>none</default>
+<contextlist><context>directory</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.0 and later</compatibility>
+
+<usage>
+ <p>The <directive>SessionHeader</directive> directive defines the name of an
+ HTTP response header which, if present, will be parsed and written to the
+ current session.</p>
+
+ <p>The header value is expected to be in the URL query format, for example:</p>
+
+ <example>
+ <code>key1=foo&key2=&key3=bar</code>
+ </example>
+
+ <p>Where a key is set to the empty string, that key will be removed from the
+ session.</p>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>SessionInclude</name>
+<description>Define URL prefixes for which a session is valid</description>
+<syntax>SessionInclude <var>path</var></syntax>
+<default>all URLs</default>
+<contextlist><context>directory</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.0 and later</compatibility>
+
+<usage>
+ <p>The <directive>SessionInclude</directive> directive allows sessions to
+ be made valid for specific URL prefixes only. This can be used to make a
+ website more efficient, by targeting a more precise URL space for which
+ a session should be maintained. By default, all URLs within the directory
+ or location are included in the session.</p>
+
+ <note type="warning"><title>Warning</title>
+ <p>This directive has a similar purpose to the <var>path</var> attribute
+ in HTTP cookies, but should not be confused with this attribute. This
+ directive does not set the <var>path</var> attribute, which must be
+ configured separately.</p></note>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>SessionExclude</name>
+<description>Define URL prefixes for which a session is ignored</description>
+<syntax>SessionExclude <var>path</var></syntax>
+<default>none</default>
+<contextlist><context>directory</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.0 and later</compatibility>
+
+<usage>
+ <p>The <directive>SessionExclude</directive> directive allows sessions to
+ be disabled specific URL prefixes only. This can be used to make a
+ website more efficient, by targeting a more precise URL space for which
+ a session should be maintained. By default, all URLs within the directory
+ or location are included in the session. The
+ <directive module="mod_session">SessionExclude</directive> directive takes
+ precedence over the
+ <directive module="mod_session">SessionInclude</directive> directive.</p>
+
+ <note type="warning"><title>Warning</title>
+ <p>This directive has a similar purpose to the <var>path</var> attribute
+ in HTTP cookies, but should not be confused with this attribute. This
+ directive does not set the <var>path</var> attribute, which must be
+ configured separately.</p></note>
+</usage>
+</directivesynopsis>
+
+</modulesynopsis>
*/
AP_DECLARE(int) ap_is_url(const char *u);
+/**
+ * Unescape a string
+ * @param url The string to unescape
+ * @return 0 on success, non-zero otherwise
+ */
+AP_DECLARE(int) ap_unescape_all(char *url);
+
/**
* Unescape a URL
* @param url The url to unescape
*/
AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *s);
+/**
+ * Escape a path segment, as defined in RFC 1808, to a preallocated buffer.
+ * @param c The preallocated buffer to write to
+ * @param s The path to convert
+ * @return The converted URL (c)
+ */
+AP_DECLARE(char *) ap_escape_path_segment_b(char *c, const char *s);
+
/**
* convert an OS path to a URL in an OS dependant way.
* @param p The pool to allocate from
--- /dev/null
+# a modules Makefile has no explicit targets -- they will be defined by
+# whatever modules are enabled. just grab special.mk to deal with this.
+include $(top_srcdir)/build/special.mk
+
--- /dev/null
+dnl modules enabled in this directory by default
+
+dnl Session
+
+dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
+
+APACHE_MODPATH_INIT(session)
+
+dnl Session modules; modules that are capable of storing key value pairs in
+dnl various places, such as databases, LDAP, or cookies.
+dnl
+APACHE_MODULE(session, session module, , , most)
+dnl APACHE_MODULE(session_cookie, session cookie module, , , most)
+dnl APACHE_MODULE(session_crypto, session crypto module, , , most)
+dnl APACHE_MODULE(session_dbd, session dbd module, , , most)
+dnl APACHE_MODULE(session_ldap, session ldap module, , , most)
+
+APACHE_MODPATH_FINISH
+
--- /dev/null
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define CORE_PRIVATE
+
+#include "mod_session.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "util_filter.h"
+#include "http_log.h"
+#include "http_request.h"
+#include "http_protocol.h"
+
+#define SESSION_PREFIX "mod_session: "
+#define SESSION_EXPIRY "expiry"
+#define HTTP_SESSION "HTTP_SESSION"
+
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(session_load)
+ APR_HOOK_LINK(session_save)
+ APR_HOOK_LINK(session_encode)
+ APR_HOOK_LINK(session_decode)
+)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, session_load,
+ (request_rec * r, session_rec ** z), (r, z), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, session_save,
+ (request_rec * r, session_rec * z), (r, z), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int, session_encode,
+ (request_rec * r, session_rec * z), (r, z), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int, session_decode,
+ (request_rec * r, session_rec * z), (r, z), OK, DECLINED)
+/**
+ * Should the session be included within this URL.
+ *
+ * This function tests whether a session is valid for this URL. It uses the
+ * include and exclude arrays to determine whether they should be included.
+ */
+ static int session_included(request_rec * r, session_dir_conf * conf)
+{
+
+ const char **includes = (const char **) conf->includes->elts;
+ const char **excludes = (const char **) conf->excludes->elts;
+ int included = 1; /* defaults to included */
+ int i;
+
+ if (conf->includes->nelts) {
+ included = 0;
+ for (i = 0; !included && i < conf->includes->nelts; i++) {
+ const char *include = includes[i];
+ if (strncmp(r->parsed_uri.path, include, strlen(include))) {
+ included = 1;
+ }
+ }
+ }
+
+ if (conf->excludes->nelts) {
+ for (i = 0; included && i < conf->includes->nelts; i++) {
+ const char *exclude = excludes[i];
+ if (strncmp(r->parsed_uri.path, exclude, strlen(exclude))) {
+ included = 0;
+ }
+ }
+ }
+
+ return included;
+}
+
+/**
+ * Get a particular value from the session.
+ * @param r The current request.
+ * @param z The current session. If this value is NULL, the session will be
+ * looked up in the request, created if necessary, and saved to the request
+ * notes.
+ * @param key The key to get.
+ * @param value The buffer to write the value to.
+ */
+AP_DECLARE(void) ap_session_get(request_rec * r, session_rec * z, const char *key, const char **value)
+{
+ if (!z) {
+ ap_session_load(r, &z);
+ }
+ *value = apr_table_get(z->entries, key);
+}
+
+/**
+ * Set a particular value to the session.
+ *
+ * Using this method ensures that the dirty flag is set correctly, so that
+ * the session can be saved efficiently.
+ * @param r The current request.
+ * @param z The current session. If this value is NULL, the session will be
+ * looked up in the request, created if necessary, and saved to the request
+ * notes.
+ * @param key The key to set. The existing key value will be replaced.
+ * @param value The value to set.
+ */
+AP_DECLARE(void) ap_session_set(request_rec * r, session_rec * z,
+ const char *key, const char *value)
+{
+ if (!z) {
+ ap_session_load(r, &z);
+ }
+ if (value) {
+ apr_table_set(z->entries, key, value);
+ }
+ else {
+ apr_table_unset(z->entries, key);
+ }
+ z->dirty = 1;
+}
+
+/**
+ * Load the session.
+ *
+ * If the session doesn't exist, a blank one will be created.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE(int) ap_session_load(request_rec * r, session_rec ** z)
+{
+
+ session_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
+ &session_module);
+ apr_time_t now;
+ session_rec *zz = NULL;
+
+ /* is the session enabled? */
+ if (!dconf->enabled) {
+ return APR_SUCCESS;
+ }
+
+ /* should the session be loaded at all? */
+ if (!session_included(r, dconf)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, SESSION_PREFIX
+ "excluded by configuration for: %s", r->uri);
+ return APR_SUCCESS;
+ }
+
+ /* load the session from the session hook */
+ int rv = ap_run_session_load(r, &zz);
+ if (DECLINED == rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, SESSION_PREFIX
+ "session is enabled but no session modules have been configured, "
+ "session not loaded: %s", r->uri);
+ return APR_EGENERAL;
+ }
+ else if (OK != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, SESSION_PREFIX
+ "error while loading the session, "
+ "session not loaded: %s", r->uri);
+ return rv;
+ }
+
+ /* found a session that hasn't expired? */
+ now = apr_time_now();
+ if (!zz || (zz->expiry && zz->expiry < now)) {
+
+ /* no luck, create a blank session */
+ zz = (session_rec *) apr_pcalloc(r->pool, sizeof(session_rec));
+ zz->pool = r->pool;
+ zz->entries = apr_table_make(zz->pool, 10);
+ zz->uuid = (apr_uuid_t *) apr_pcalloc(zz->pool, sizeof(apr_uuid_t));
+ apr_uuid_get(zz->uuid);
+
+ }
+ else {
+ rv = ap_run_session_decode(r, zz);
+ if (OK != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, SESSION_PREFIX
+ "error while decoding the session, "
+ "session not loaded: %s", r->uri);
+ return rv;
+ }
+ }
+
+ /* make sure the expiry is set, if present */
+ if (!zz->expiry && dconf->maxage) {
+ zz->expiry = now + dconf->maxage * APR_USEC_PER_SEC;
+ zz->maxage = dconf->maxage;
+ }
+
+ *z = zz;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * Save the session.
+ *
+ * In most implementations the session is only saved if the dirty flag is
+ * true. This prevents the session being saved unnecessarily.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE(int) ap_session_save(request_rec * r, session_rec * z)
+{
+ if (z) {
+ apr_time_t now = apr_time_now();
+ int rv = 0;
+
+ /* sanity checks, should we try save at all? */
+ if (z->written) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, SESSION_PREFIX
+ "attempt made to save the session twice, "
+ "session not saved: %s", r->uri);
+ return APR_EGENERAL;
+ }
+ if (z->expiry && z->expiry < now) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, SESSION_PREFIX
+ "attempt made to save a session when the session had already expired, "
+ "session not saved: %s", r->uri);
+ return APR_EGENERAL;
+ }
+
+ /* encode the session */
+ rv = ap_run_session_encode(r, z);
+ if (OK != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, SESSION_PREFIX
+ "error while encoding the session, "
+ "session not saved: %s", r->uri);
+ return rv;
+ }
+
+ /* try the save */
+ rv = ap_run_session_save(r, z);
+ if (DECLINED == rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, SESSION_PREFIX
+ "session is enabled but no session modules have been configured, "
+ "session not saved: %s", r->uri);
+ return APR_EGENERAL;
+ }
+ else if (OK != rv) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, SESSION_PREFIX
+ "error while saving the session, "
+ "session not saved: %s", r->uri);
+ return rv;
+ }
+ else {
+ z->written = 1;
+ }
+ }
+
+ return APR_SUCCESS;
+
+}
+
+static int identity_count(int *count, const char *key, const char *val)
+{
+ *count += strlen(key) * 3 + strlen(val) * 3 + 1;
+ return 1;
+}
+
+static int identity_concat(char *buffer, const char *key, const char *val)
+{
+ char *slider = buffer;
+ int length = strlen(slider);
+ slider += length;
+ if (length) {
+ *slider = '&';
+ slider++;
+ }
+ ap_escape_path_segment_b(slider, key);
+ slider += strlen(slider);
+ *slider = '=';
+ slider++;
+ ap_escape_path_segment_b(slider, val);
+ return 1;
+}
+
+/**
+ * Default identity encoding for the session.
+ *
+ * By default, the name value pairs in the session are URLEncoded, separated
+ * by equals, and then in turn separated by ampersand, in the format of an
+ * html form.
+ *
+ * This was chosen to make it easy for external code to unpack a session,
+ * should there be a need to do so.
+ *
+ * @param r The request pointer.
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE(int) ap_session_identity_encode(request_rec * r, session_rec * z)
+{
+
+ char *buffer = NULL;
+ int length = 0;
+ if (z->expiry) {
+ char *expiry = apr_psprintf(r->pool, "%" APR_INT64_T_FMT, z->expiry);
+ apr_table_set(z->entries, SESSION_EXPIRY, expiry);
+ }
+ apr_table_do((int (*) (void *, const char *, const char *))
+ identity_count, &length, z->entries, NULL);;
+ buffer = apr_pcalloc(r->pool, length + 1);
+ apr_table_do((int (*) (void *, const char *, const char *))
+ identity_concat, buffer, z->entries, NULL);
+ z->encoded = buffer;
+ return OK;
+
+}
+
+/**
+ * Default identity decoding for the session.
+ *
+ * By default, the name value pairs in the session are URLEncoded, separated
+ * by equals, and then in turn separated by ampersand, in the format of an
+ * html form.
+ *
+ * This was chosen to make it easy for external code to unpack a session,
+ * should there be a need to do so.
+ *
+ * This function reverses that process, and populates the session table.
+ *
+ * Name / value pairs that are not encoded properly are ignored.
+ *
+ * @param r The request pointer.
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE(int) ap_session_identity_decode(request_rec * r, session_rec * z)
+{
+
+ char *last = NULL;
+ char *encoded, *pair;
+ const char *sep = "&";
+
+ /* sanity check - anything to decode? */
+ if (!z->encoded) {
+ return OK;
+ }
+
+ /* decode what we have */
+ encoded = apr_pstrcat(r->pool, z->encoded, NULL);
+ pair = apr_strtok(encoded, sep, &last);
+ while (pair && pair[0]) {
+ char *plast = NULL;
+ const char *psep = "=";
+ char *key = apr_strtok(pair, psep, &plast);
+ char *val = apr_strtok(NULL, psep, &plast);
+ if (key && *key) {
+ if (!val || !*val) {
+ apr_table_unset(z->entries, key);
+ }
+ if (!ap_unescape_all(key) && !ap_unescape_all(val)) {
+ if (!strcmp(SESSION_EXPIRY, key)) {
+ z->expiry = (apr_time_t) apr_atoi64(val);
+ }
+ else {
+ apr_table_set(z->entries, key, val);
+ }
+ }
+ }
+ pair = apr_strtok(NULL, sep, &last);
+ }
+ z->encoded = NULL;
+ return OK;
+
+}
+
+/**
+ * Ensure any changes to the session are committed.
+ *
+ * This is done in an output filter so that our options for where to
+ * store the session can include storing the session within a cookie:
+ * As an HTTP header, the cookie must be set before the output is
+ * written, but after the handler is run.
+ *
+ * NOTE: It is possible for internal redirects to cause more than one
+ * request to be present, and each request might have a session
+ * defined. We need to go through each session in turn, and save each
+ * one.
+ *
+ * The same session might appear in more than one request. The first
+ * attempt to save the session will be called
+ */
+static apr_status_t ap_session_output_filter(ap_filter_t * f,
+ apr_bucket_brigade * in)
+{
+
+ /* save all the sessions in all the requests */
+ request_rec *r = f->r->main;
+ if (!r) {
+ r = f->r;
+ }
+ while (r) {
+ session_rec *z = NULL;
+ session_dir_conf *conf = ap_get_module_config(r->per_dir_config,
+ &session_module);
+
+ /* load the session, or create one if necessary */
+ ap_session_load(r, &z);
+ if (!z || z->written) {
+ r = r->next;
+ continue;
+ }
+
+ /* if a header was specified, insert the new values from the header */
+ if (conf->header_set) {
+ const char *override = apr_table_get(r->err_headers_out, conf->header);
+ if (!override) {
+ override = apr_table_get(r->headers_out, conf->header);
+ }
+ if (override) {
+ z->encoded = override;
+ ap_session_identity_decode(r, z);
+ }
+ }
+
+ /* save away the session, and we're done */
+ ap_session_save(r, z);
+
+ r = r->next;
+ }
+
+ /* remove ourselves from the filter chain */
+ ap_remove_output_filter(f);
+
+ /* send the data up the stack */
+ return ap_pass_brigade(f->next, in);
+
+}
+
+/**
+ * Insert the output filter.
+ */
+static void ap_session_insert_output_filter(request_rec * r)
+{
+ ap_add_output_filter("MOD_SESSION_OUT", NULL, r, r->connection);
+}
+
+/**
+ * Fixups hook.
+ *
+ * Load the session within a fixup - this ensures that the session is
+ * properly loaded prior to the handler being called.
+ *
+ * The fixup is also responsible for injecting the session into the CGI
+ * environment, should the admin have configured it so.
+ *
+ * @param r The request
+ */
+AP_DECLARE(int) ap_session_fixups(request_rec * r)
+{
+ session_dir_conf *conf = ap_get_module_config(r->per_dir_config,
+ &session_module);
+
+ session_rec *z = NULL;
+ ap_session_load(r, &z);
+
+ if (conf->env) {
+ ap_session_identity_encode(r, z);
+ if (z->encoded) {
+ apr_table_set(r->subprocess_env, HTTP_SESSION, z->encoded);
+ z->encoded = NULL;
+ }
+ }
+
+ return OK;
+
+}
+
+
+static void *create_session_dir_config(apr_pool_t * p, char *dummy)
+{
+ session_dir_conf *new =
+ (session_dir_conf *) apr_pcalloc(p, sizeof(session_dir_conf));
+
+ new->includes = apr_array_make(p, 10, sizeof(const char **));
+ new->excludes = apr_array_make(p, 10, sizeof(const char **));
+
+ return (void *) new;
+}
+
+static void *merge_session_dir_config(apr_pool_t * p, void *basev, void *addv)
+{
+ session_dir_conf *new = (session_dir_conf *) apr_pcalloc(p, sizeof(session_dir_conf));
+ session_dir_conf *add = (session_dir_conf *) addv;
+ session_dir_conf *base = (session_dir_conf *) basev;
+
+ new->enabled = (add->enabled_set == 0) ? base->enabled : add->enabled;
+ new->enabled_set = add->enabled_set || base->enabled_set;
+ new->maxage = (add->maxage_set == 0) ? base->maxage : add->maxage;
+ new->maxage_set = add->maxage_set || base->maxage_set;
+ new->header = (add->header_set == 0) ? base->header : add->header;
+ new->header_set = add->header_set || base->header_set;
+ new->env = (add->env_set == 0) ? base->env : add->env;
+ new->env_set = add->env_set || base->env_set;
+ new->includes = apr_array_append(p, base->includes, add->includes);
+ new->excludes = apr_array_append(p, base->excludes, add->excludes);
+
+ return new;
+}
+
+
+static const char *
+ set_session_enable(cmd_parms * parms, void *dconf, int flag)
+{
+ session_dir_conf *conf = dconf;
+
+ conf->enabled = flag;
+ conf->enabled_set = 1;
+
+ return NULL;
+}
+
+static const char *
+ set_session_maxage(cmd_parms * parms, void *dconf, const char *arg)
+{
+ session_dir_conf *conf = dconf;
+
+ conf->maxage = atol(arg);
+ conf->maxage_set = 1;
+
+ return NULL;
+}
+
+static const char *
+ set_session_header(cmd_parms * parms, void *dconf, const char *arg)
+{
+ session_dir_conf *conf = dconf;
+
+ conf->header = arg;
+ conf->header_set = 1;
+
+ return NULL;
+}
+
+static const char *
+ set_session_env(cmd_parms * parms, void *dconf, int flag)
+{
+ session_dir_conf *conf = dconf;
+
+ conf->env = flag;
+ conf->env_set = 1;
+
+ return NULL;
+}
+
+static const char *add_session_include(cmd_parms * cmd, void *dconf, const char *f)
+{
+ session_dir_conf *conf = dconf;
+
+ const char **new = apr_array_push(conf->includes);
+ *new = f;
+
+ return NULL;
+}
+
+static const char *add_session_exclude(cmd_parms * cmd, void *dconf, const char *f)
+{
+ session_dir_conf *conf = dconf;
+
+ const char **new = apr_array_push(conf->excludes);
+ *new = f;
+
+ return NULL;
+}
+
+
+static const command_rec session_cmds[] =
+{
+ AP_INIT_FLAG("Session", set_session_enable, NULL, OR_AUTHCFG,
+ "on if a session should be maintained for these URLs"),
+ AP_INIT_TAKE1("SessionMaxAge", set_session_maxage, NULL, OR_AUTHCFG,
+ "length of time for which a session should be valid. Zero to disable"),
+ AP_INIT_TAKE1("SessionHeader", set_session_header, NULL, OR_AUTHCFG,
+ "output header, if present, whose contents will be injected into the session."),
+ AP_INIT_FLAG("SessionEnv", set_session_env, NULL, OR_AUTHCFG,
+ "on if a session should be written to the CGI environment. Defaults to off"),
+ AP_INIT_TAKE1("SessionInclude", add_session_include, NULL, OR_AUTHCFG,
+ "URL prefixes to include in the session. Defaults to all URLs"),
+ AP_INIT_TAKE1("SessionExclude", add_session_exclude, NULL, OR_AUTHCFG,
+ "URL prefixes to exclude from the session. Defaults to no URLs"),
+ {NULL}
+};
+
+static void register_hooks(apr_pool_t * p)
+{
+ ap_register_output_filter("MOD_SESSION_OUT", ap_session_output_filter,
+ NULL, AP_FTYPE_CONTENT_SET);
+ ap_hook_insert_filter(ap_session_insert_output_filter, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_insert_error_filter(ap_session_insert_output_filter,
+ NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_fixups(ap_session_fixups, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_session_encode(ap_session_identity_encode, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ ap_hook_session_decode(ap_session_identity_decode, NULL, NULL, APR_HOOK_REALLY_LAST);
+ APR_REGISTER_OPTIONAL_FN(ap_session_get);
+ APR_REGISTER_OPTIONAL_FN(ap_session_set);
+ APR_REGISTER_OPTIONAL_FN(ap_session_load);
+ APR_REGISTER_OPTIONAL_FN(ap_session_save);
+}
+
+module AP_MODULE_DECLARE_DATA session_module =
+{
+ STANDARD20_MODULE_STUFF,
+ create_session_dir_config, /* dir config creater */
+ merge_session_dir_config, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ session_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
--- /dev/null
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOD_SESSION_H
+#define MOD_SESSION_H
+
+/**
+ * @file mod_session.h
+ * @brief Session Module for Apache
+ *
+ * @defgroup MOD_SESSION mod_session
+ * @ingroup APACHE_MODS
+ * @{
+ */
+
+#define CORE_PRIVATE
+
+#include "apr_hooks.h"
+#include "apr_optional.h"
+#include "apr_tables.h"
+#include "apr_uuid.h"
+#include "apr_pools.h"
+#include "apr_time.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "ap_config.h"
+
+#define MOD_SESSION_NOTES_KEY "mod_session_key"
+
+/**
+ * Define the name of a username stored in the session, so that modules interested
+ * in the username can find it in a standard place.
+ */
+#define MOD_SESSION_USER "user"
+
+/**
+ * Define the name of a password stored in the session, so that modules interested
+ * in the password can find it in a standard place.
+ */
+#define MOD_SESSION_PW "pw"
+
+/**
+ * A session structure.
+ *
+ * At the core of the session is a set of name value pairs making up the
+ * session.
+ *
+ * The session might be uniquely identified by an anonymous uuid, or
+ * a remote_user value, or both.
+ */
+typedef struct {
+ apr_pool_t *pool; /* pool to be used for this session */
+ apr_uuid_t *uuid; /* anonymous uuid of this particular session */
+ const char *remote_user; /* user who owns this particular session */
+ apr_table_t *entries; /* key value pairs */
+ const char *encoded; /* the encoded version of the key value pairs */
+ apr_time_t expiry; /* if > 0, the time of expiry of this session */
+ long maxage; /* if > 0, the maxage of the session, from
+ * which expiry is calculated */
+ int dirty; /* dirty flag */
+ int cached; /* true if this session was loaded from a
+ * cache of some kind */
+ int written; /* true if this session has already been
+ * written */
+} session_rec;
+
+/**
+ * Structure to carry the per-dir session config.
+ */
+typedef struct {
+ int enabled; /* whether the session has been enabled for
+ * this directory */
+ int enabled_set;
+ long maxage; /* seconds until session expiry */
+ int maxage_set;
+ const char *header; /* header to inject session */
+ int header_set;
+ int env; /* whether the session has been enabled for
+ * this directory */
+ int env_set;
+ apr_array_header_t *includes; /* URL prefixes to be included. All
+ * URLs included if empty */
+ apr_array_header_t *excludes; /* URL prefixes to be excluded. No
+ * URLs excluded if empty */
+} session_dir_conf;
+
+/**
+ * Get a particular value from the session.
+ * @param r The current request.
+ * @param z The current session. If this value is NULL, the session will be
+ * looked up in the request, created if necessary, and saved to the request
+ * notes.
+ * @param key The key to get.
+ * @param value The buffer to write the value to.
+ */
+AP_DECLARE(void) ap_session_get(request_rec * r, session_rec * z, const char *key, const char **value);
+
+/**
+ * Set a particular value to the session.
+ *
+ * Using this method ensures that the dirty flag is set correctly, so that
+ * the session can be saved efficiently.
+ * @param r The current request.
+ * @param z The current session. If this value is NULL, the session will be
+ * looked up in the request, created if necessary, and saved to the request
+ * notes.
+ * @param key The key to set. The existing key value will be replaced.
+ * @param value The value to set.
+ */
+AP_DECLARE(void) ap_session_set(request_rec * r, session_rec * z,
+ const char *key, const char *value);
+
+/**
+ * Load the session.
+ *
+ * If the session doesn't exist, a blank one will be created.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE(int) ap_session_load(request_rec * r, session_rec ** z);
+
+/**
+ * Hook to load the session.
+ *
+ * If the session doesn't exist, a blank one will be created.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE_HOOK(int, session_load, (request_rec * r, session_rec ** z))
+
+/**
+ * Save the session.
+ *
+ * In most implementations the session is only saved if the dirty flag is
+ * true. This prevents the session being saved unnecessarily.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE(int) ap_session_save(request_rec * r, session_rec * z);
+
+/**
+ * Hook to save the session.
+ *
+ * In most implementations the session is only saved if the dirty flag is
+ * true. This prevents the session being saved unnecessarily.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE_HOOK(int, session_save, (request_rec * r, session_rec * z))
+
+/**
+ * Hook to encode the session.
+ *
+ * In the default implementation, the key value pairs are encoded using
+ * key value pairs separated by equals, in turn separated by ampersand,
+ * like a web form.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE_HOOK(int, session_encode, (request_rec * r, session_rec * z))
+
+/**
+ * Hook to decode the session.
+ *
+ * In the default implementation, the key value pairs are encoded using
+ * key value pairs separated by equals, in turn separated by ampersand,
+ * like a web form.
+ *
+ * @param r The request
+ * @param z A pointer to where the session will be written.
+ */
+AP_DECLARE_HOOK(int, session_decode, (request_rec * r, session_rec * z))
+
+APR_DECLARE_OPTIONAL_FN(void, ap_session_get, (request_rec * r, session_rec * z,
+ const char *key, const char **value));
+APR_DECLARE_OPTIONAL_FN(void, ap_session_set, (request_rec * r, session_rec * z,
+ const char *key, const char *value));
+APR_DECLARE_OPTIONAL_FN(int, ap_session_load, (request_rec *, session_rec **));
+APR_DECLARE_OPTIONAL_FN(int, ap_session_save, (request_rec *, session_rec *));
+
+/**
+ * The name of the module.
+ */
+extern module AP_MODULE_DECLARE_DATA session_module;
+
+#endif /* MOD_SESSION_H */
+/** @} */
}
}
+AP_DECLARE(int) ap_unescape_all(char *url)
+{
+ return unescape_url(url, NULL, NULL);
+}
+
/* c2x takes an unsigned, and expects the caller has guaranteed that
* 0 <= what < 256... which usually means that you have to cast to
* unsigned char first, because (unsigned)(char)(x) first goes through
* something with a '/' in it (and thus does not prefix "./").
*/
-AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+AP_DECLARE(char *) ap_escape_path_segment_b(char *copy, const char *segment)
{
- char *copy = apr_palloc(p, 3 * strlen(segment) + 1);
const unsigned char *s = (const unsigned char *)segment;
unsigned char *d = (unsigned char *)copy;
unsigned c;
return copy;
}
+AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+{
+ return ap_escape_path_segment_b(apr_palloc(p, 3 * strlen(segment) + 1), segment);
+}
+
AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
{
char *copy = apr_palloc(p, 3 * strlen(path) + 3);