]> granicus.if.org Git - apache/blob - modules/session/mod_session_cookie.c
mod_session_cookie: Add a session implementation capable of storing
[apache] / modules / session / mod_session_cookie.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define CORE_PRIVATE
18
19 #include "mod_session.h"
20 #include "apr_lib.h"
21 #include "apr_strings.h"
22 #include "http_log.h"
23 #include "util_cookies.h"
24
25 #define LOG_PREFIX "mod_session_cookie: "
26 #define MOD_SESSION_COOKIE "mod_session_cookie"
27
28 module AP_MODULE_DECLARE_DATA session_cookie_module;
29
30 /**
31  * Structure to carry the per-dir session config.
32  */
33 typedef struct {
34     const char *name;
35     int name_set;
36     const char *name_attrs;
37     const char *name2;
38     int name2_set;
39     const char *name2_attrs;
40     int remove;
41     int remove_set;
42 } session_cookie_dir_conf;
43
44 /**
45  * Set the cookie and embed the session within it.
46  *
47  * This function adds an RFC2109 compliant Set-Cookie header for
48  * the cookie specified in SessionCookieName, and an RFC2965 compliant
49  * Set-Cookie2 header for the cookie specified in SessionCookieName2.
50  *
51  * If specified, the optional cookie attributes will be added to
52  * each cookie. If defaults are not specified, DEFAULT_ATTRS
53  * will be used.
54  *
55  * On success, this method will return APR_SUCCESS.
56  *
57  * @param r The request pointer.
58  * @param z A pointer to where the session will be written.
59  */
60 AP_DECLARE(int) ap_session_cookie_save(request_rec * r, session_rec * z)
61 {
62
63     session_cookie_dir_conf *conf = ap_get_module_config(r->per_dir_config,
64                                                     &session_cookie_module);
65
66     /* don't cache auth protected pages */
67     apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
68
69     /* create RFC2109 compliant cookie */
70     if (conf->name_set) {
71         if (z->encoded && z->encoded[0]) {
72             ap_cookie_write(r, conf->name, z->encoded, conf->name_attrs, z->maxage);
73         }
74         else {
75             ap_cookie_remove(r, conf->name);
76         }
77     }
78
79     /* create RFC2965 compliant cookie */
80     if (conf->name2_set) {
81         if (z->encoded && z->encoded[0]) {
82             ap_cookie_write2(r, conf->name2, z->encoded, conf->name2_attrs, z->maxage);
83         }
84         else {
85             ap_cookie_remove2(r, conf->name2);
86         }
87     }
88
89     if (conf->name_set || conf->name2_set) {
90         return OK;
91     }
92     return DECLINED;
93
94 }
95
96 /**
97  * Isolate the cookie with the name "name", and if present, extract
98  * the payload from the cookie.
99  *
100  * If the cookie is found, the cookie and any other cookies with the
101  * same name are removed from the cookies passed in the request, so
102  * that credentials are not leaked to a backend server or process.
103  *
104  * A missing or malformed cookie will cause this function to return
105  * APR_EGENERAL.
106  *
107  * On success, this returns APR_SUCCESS.
108  */
109 AP_DECLARE(int) ap_session_cookie_load(request_rec * r, session_rec ** z)
110 {
111
112     session_cookie_dir_conf *conf = ap_get_module_config(r->per_dir_config,
113                                                     &session_cookie_module);
114
115     session_rec *zz = NULL;
116     const char *val = NULL;
117     const char *note = NULL;
118     const char *name = NULL;
119     request_rec *m = r->main ? r->main : r;
120
121     /* is our session in a cookie? */
122     if (conf->name2_set) {
123         name = conf->name2;
124     }
125     else if (conf->name_set) {
126         name = conf->name;
127     }
128     else {
129         return DECLINED;
130     }
131
132     /* first look in the notes */
133     note = apr_pstrcat(r->pool, MOD_SESSION_COOKIE, name, NULL);
134     zz = (session_rec *)apr_table_get(m->notes, note);
135     if (zz) {
136         *z = zz;
137         return OK;
138     }
139
140     /* otherwise, try parse the cookie */
141     ap_cookie_read(r, name, &val, conf->remove);
142
143     /* create a new session and return it */
144     zz = (session_rec *) apr_pcalloc(r->pool, sizeof(session_rec));
145     zz->pool = r->pool;
146     zz->entries = apr_table_make(r->pool, 10);
147     zz->encoded = val;
148     zz->uuid = (apr_uuid_t *) apr_pcalloc(r->pool, sizeof(apr_uuid_t));
149     *z = zz;
150
151     /* put the session in the notes so we don't have to parse it again */
152     apr_table_setn(m->notes, note, (char *)zz);
153
154     return OK;
155
156 }
157
158
159
160 static void *create_session_cookie_dir_config(apr_pool_t * p, char *dummy)
161 {
162     session_cookie_dir_conf *new =
163     (session_cookie_dir_conf *) apr_pcalloc(p, sizeof(session_cookie_dir_conf));
164
165     return (void *) new;
166 }
167
168 static void *merge_session_cookie_dir_config(apr_pool_t * p, void *basev, void *addv)
169 {
170     session_cookie_dir_conf *new = (session_cookie_dir_conf *) apr_pcalloc(p, sizeof(session_cookie_dir_conf));
171     session_cookie_dir_conf *add = (session_cookie_dir_conf *) addv;
172     session_cookie_dir_conf *base = (session_cookie_dir_conf *) basev;
173
174     new->name = (add->name_set == 0) ? base->name : add->name;
175     new->name_attrs = (add->name_set == 0) ? base->name_attrs : add->name_attrs;
176     new->name_set = add->name_set || base->name_set;
177     new->name2 = (add->name2_set == 0) ? base->name2 : add->name2;
178     new->name2_attrs = (add->name2_set == 0) ? base->name2_attrs : add->name2_attrs;
179     new->name2_set = add->name2_set || base->name2_set;
180     new->remove = (add->remove_set == 0) ? base->remove : add->remove;
181     new->remove_set = add->remove_set || base->remove_set;
182
183     return new;
184 }
185
186 /**
187  * Sanity check a given string that it exists, is not empty,
188  * and does not contain special characters.
189  */
190 static const char *check_string(cmd_parms * cmd, const char *string)
191 {
192     if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&')) {
193         return apr_pstrcat(cmd->pool, cmd->directive->directive,
194                            " cannot be empty, or contain '=' or '&'.",
195                            NULL);
196     }
197     return NULL;
198 }
199
200 static const char *set_cookie_name(cmd_parms * cmd, void *config, const char *args)
201 {
202     char *last;
203     char *line = apr_pstrdup(cmd->pool, args);
204     session_cookie_dir_conf *conf = (session_cookie_dir_conf *) config;
205     char *cookie = apr_strtok(line, " \t", &last);
206     conf->name = cookie;
207     conf->name_set = 1;
208     while (apr_isspace(*last)) {
209         last++;
210     }
211     conf->name_attrs = last;
212     return check_string(cmd, cookie);
213 }
214
215 static const char *set_cookie_name2(cmd_parms * cmd, void *config, const char *args)
216 {
217     char *last;
218     char *line = apr_pstrdup(cmd->pool, args);
219     session_cookie_dir_conf *conf = (session_cookie_dir_conf *) config;
220     char *cookie = apr_strtok(line, " \t", &last);
221     conf->name2 = cookie;
222     conf->name2_set = 1;
223     while (apr_isspace(*last)) {
224         last++;
225     }
226     conf->name2_attrs = last;
227     return check_string(cmd, cookie);
228 }
229
230 static const char *
231      set_remove(cmd_parms * parms, void *dconf, int flag)
232 {
233     session_cookie_dir_conf *conf = dconf;
234
235     conf->remove = flag;
236     conf->remove_set = 1;
237
238     return NULL;
239 }
240
241 static const command_rec session_cookie_cmds[] =
242 {
243     AP_INIT_RAW_ARGS("SessionCookieName", set_cookie_name, NULL, OR_AUTHCFG,
244                      "The name of the RFC2109 cookie carrying the session"),
245     AP_INIT_RAW_ARGS("SessionCookieName2", set_cookie_name2, NULL, OR_AUTHCFG,
246                      "The name of the RFC2965 cookie carrying the session"),
247     AP_INIT_FLAG("SessionCookieRemove", set_remove, NULL, OR_AUTHCFG,
248                  "Set to 'On' to remove the session cookie from the headers "
249                  "and hide the cookie from a backend server or process"),
250     {NULL}
251 };
252
253 static void register_hooks(apr_pool_t * p)
254 {
255     ap_hook_session_load(ap_session_cookie_load, NULL, NULL, APR_HOOK_MIDDLE);
256     ap_hook_session_save(ap_session_cookie_save, NULL, NULL, APR_HOOK_MIDDLE);
257 }
258
259 module AP_MODULE_DECLARE_DATA session_cookie_module =
260 {
261     STANDARD20_MODULE_STUFF,
262     create_session_cookie_dir_config, /* dir config creater */
263     merge_session_cookie_dir_config,  /* dir merger --- default is to
264                                        * override */
265     NULL,                             /* server config */
266     NULL,                             /* merge server config */
267     session_cookie_cmds,              /* command apr_table_t */
268     register_hooks                    /* register hooks */
269 };