]> granicus.if.org Git - apache/blob - modules/session/mod_session_cookie.c
mod_session: Use apr_status_t as a return code across the mod_session API,
[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 #include "mod_session.h"
18 #include "apr_lib.h"
19 #include "apr_strings.h"
20 #include "http_log.h"
21 #include "util_cookies.h"
22
23 #define MOD_SESSION_COOKIE "mod_session_cookie"
24
25 module AP_MODULE_DECLARE_DATA session_cookie_module;
26
27 /**
28  * Structure to carry the per-dir session config.
29  */
30 typedef struct {
31     const char *name;
32     int name_set;
33     const char *name_attrs;
34     const char *name2;
35     int name2_set;
36     const char *name2_attrs;
37     int remove;
38     int remove_set;
39 } session_cookie_dir_conf;
40
41 /**
42  * Set the cookie and embed the session within it.
43  *
44  * This function adds an RFC2109 compliant Set-Cookie header for
45  * the cookie specified in SessionCookieName, and an RFC2965 compliant
46  * Set-Cookie2 header for the cookie specified in SessionCookieName2.
47  *
48  * If specified, the optional cookie attributes will be added to
49  * each cookie. If defaults are not specified, DEFAULT_ATTRS
50  * will be used.
51  *
52  * On success, this method will return APR_SUCCESS.
53  *
54  * @param r The request pointer.
55  * @param z A pointer to where the session will be written.
56  */
57 static apr_status_t session_cookie_save(request_rec * r, session_rec * z)
58 {
59
60     session_cookie_dir_conf *conf = ap_get_module_config(r->per_dir_config,
61                                                     &session_cookie_module);
62
63     /* don't cache auth protected pages */
64     apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
65
66     /* create RFC2109 compliant cookie */
67     if (conf->name_set) {
68         if (z->encoded && z->encoded[0]) {
69             ap_cookie_write(r, conf->name, z->encoded, conf->name_attrs,
70                             z->maxage, r->headers_out, r->err_headers_out,
71                             NULL);
72         }
73         else {
74             ap_cookie_remove(r, conf->name, conf->name_attrs, r->headers_out,
75                              r->err_headers_out, NULL);
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,
83                              z->maxage, r->headers_out, r->err_headers_out,
84                              NULL);
85         }
86         else {
87             ap_cookie_remove2(r, conf->name2, conf->name2_attrs,
88                               r->headers_out, r->err_headers_out, NULL);
89         }
90     }
91
92     if (conf->name_set || conf->name2_set) {
93         return OK;
94     }
95     return DECLINED;
96
97 }
98
99 /**
100  * Isolate the cookie with the name "name", and if present, extract
101  * the payload from the cookie.
102  *
103  * If the cookie is found, the cookie and any other cookies with the
104  * same name are removed from the cookies passed in the request, so
105  * that credentials are not leaked to a backend server or process.
106  *
107  * A missing or malformed cookie will cause this function to return
108  * APR_EGENERAL.
109  *
110  * On success, this returns APR_SUCCESS.
111  */
112 static apr_status_t session_cookie_load(request_rec * r, session_rec ** z)
113 {
114
115     session_cookie_dir_conf *conf = ap_get_module_config(r->per_dir_config,
116                                                     &session_cookie_module);
117
118     session_rec *zz = NULL;
119     const char *val = NULL;
120     const char *note = NULL;
121     const char *name = NULL;
122     request_rec *m = r;
123
124     /* find the first redirect */
125     while (m->prev) {
126         m = m->prev;
127     }
128     /* find the main request */
129     while (m->main) {
130         m = m->main;
131     }
132
133     /* is our session in a cookie? */
134     if (conf->name2_set) {
135         name = conf->name2;
136     }
137     else if (conf->name_set) {
138         name = conf->name;
139     }
140     else {
141         return DECLINED;
142     }
143
144     /* first look in the notes */
145     note = apr_pstrcat(m->pool, MOD_SESSION_COOKIE, name, NULL);
146     zz = (session_rec *)apr_table_get(m->notes, note);
147     if (zz) {
148         *z = zz;
149         return OK;
150     }
151
152     /* otherwise, try parse the cookie */
153     ap_cookie_read(r, name, &val, conf->remove);
154
155     /* create a new session and return it */
156     zz = (session_rec *) apr_pcalloc(m->pool, sizeof(session_rec));
157     zz->pool = m->pool;
158     zz->entries = apr_table_make(m->pool, 10);
159     zz->encoded = val;
160     zz->uuid = (apr_uuid_t *) apr_pcalloc(m->pool, sizeof(apr_uuid_t));
161     *z = zz;
162
163     /* put the session in the notes so we don't have to parse it again */
164     apr_table_setn(m->notes, note, (char *)zz);
165
166     return OK;
167
168 }
169
170
171
172 static void *create_session_cookie_dir_config(apr_pool_t * p, char *dummy)
173 {
174     session_cookie_dir_conf *new =
175     (session_cookie_dir_conf *) apr_pcalloc(p, sizeof(session_cookie_dir_conf));
176
177     return (void *) new;
178 }
179
180 static void *merge_session_cookie_dir_config(apr_pool_t * p, void *basev,
181                                              void *addv)
182 {
183     session_cookie_dir_conf *new = (session_cookie_dir_conf *)
184                                 apr_pcalloc(p, sizeof(session_cookie_dir_conf));
185     session_cookie_dir_conf *add = (session_cookie_dir_conf *) addv;
186     session_cookie_dir_conf *base = (session_cookie_dir_conf *) basev;
187
188     new->name = (add->name_set == 0) ? base->name : add->name;
189     new->name_attrs = (add->name_set == 0) ? base->name_attrs : add->name_attrs;
190     new->name_set = add->name_set || base->name_set;
191     new->name2 = (add->name2_set == 0) ? base->name2 : add->name2;
192     new->name2_attrs = (add->name2_set == 0) ? base->name2_attrs : add->name2_attrs;
193     new->name2_set = add->name2_set || base->name2_set;
194     new->remove = (add->remove_set == 0) ? base->remove : add->remove;
195     new->remove_set = add->remove_set || base->remove_set;
196
197     return new;
198 }
199
200 /**
201  * Sanity check a given string that it exists, is not empty,
202  * and does not contain special characters.
203  */
204 static const char *check_string(cmd_parms * cmd, const char *string)
205 {
206     if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&')) {
207         return apr_pstrcat(cmd->pool, cmd->directive->directive,
208                            " cannot be empty, or contain '=' or '&'.",
209                            NULL);
210     }
211     return NULL;
212 }
213
214 static const char *set_cookie_name(cmd_parms * cmd, void *config,
215                                    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->name = cookie;
222     conf->name_set = 1;
223     while (apr_isspace(*last)) {
224         last++;
225     }
226     conf->name_attrs = last;
227     return check_string(cmd, cookie);
228 }
229
230 static const char *set_cookie_name2(cmd_parms * cmd, void *config,
231                                     const char *args)
232 {
233     char *last;
234     char *line = apr_pstrdup(cmd->pool, args);
235     session_cookie_dir_conf *conf = (session_cookie_dir_conf *) config;
236     char *cookie = apr_strtok(line, " \t", &last);
237     conf->name2 = cookie;
238     conf->name2_set = 1;
239     while (apr_isspace(*last)) {
240         last++;
241     }
242     conf->name2_attrs = last;
243     return check_string(cmd, cookie);
244 }
245
246 static const char *
247      set_remove(cmd_parms * parms, void *dconf, int flag)
248 {
249     session_cookie_dir_conf *conf = dconf;
250
251     conf->remove = flag;
252     conf->remove_set = 1;
253
254     return NULL;
255 }
256
257 static const command_rec session_cookie_cmds[] =
258 {
259     AP_INIT_RAW_ARGS("SessionCookieName", set_cookie_name, NULL, RSRC_CONF|OR_AUTHCFG,
260                      "The name of the RFC2109 cookie carrying the session"),
261     AP_INIT_RAW_ARGS("SessionCookieName2", set_cookie_name2, NULL, RSRC_CONF|OR_AUTHCFG,
262                      "The name of the RFC2965 cookie carrying the session"),
263     AP_INIT_FLAG("SessionCookieRemove", set_remove, NULL, RSRC_CONF|OR_AUTHCFG,
264                  "Set to 'On' to remove the session cookie from the headers "
265                  "and hide the cookie from a backend server or process"),
266     {NULL}
267 };
268
269 static void register_hooks(apr_pool_t * p)
270 {
271     ap_hook_session_load(session_cookie_load, NULL, NULL, APR_HOOK_MIDDLE);
272     ap_hook_session_save(session_cookie_save, NULL, NULL, APR_HOOK_MIDDLE);
273 }
274
275 AP_DECLARE_MODULE(session_cookie) =
276 {
277     STANDARD20_MODULE_STUFF,
278     create_session_cookie_dir_config, /* dir config creater */
279     merge_session_cookie_dir_config,  /* dir merger --- default is to
280                                        * override */
281     NULL,                             /* server config */
282     NULL,                             /* merge server config */
283     session_cookie_cmds,              /* command apr_table_t */
284     register_hooks                    /* register hooks */
285 };