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 25module AP_MODULE_DECLARE_DATA session_cookie_module; 26 27/** 28 * Structure to carry the per-dir session config. 29 */ 30typedef 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 */ 57static 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 */ 112static 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 *z = zz; 161 162 /* put the session in the notes so we don't have to parse it again */ 163 apr_table_setn(m->notes, note, (char *)zz); 164 165 return OK; 166 167} 168 169 170 171static void *create_session_cookie_dir_config(apr_pool_t * p, char *dummy) 172{ 173 session_cookie_dir_conf *new = 174 (session_cookie_dir_conf *) apr_pcalloc(p, sizeof(session_cookie_dir_conf)); 175 176 return (void *) new; 177} 178 179static void *merge_session_cookie_dir_config(apr_pool_t * p, void *basev, 180 void *addv) 181{ 182 session_cookie_dir_conf *new = (session_cookie_dir_conf *) 183 apr_pcalloc(p, sizeof(session_cookie_dir_conf)); 184 session_cookie_dir_conf *add = (session_cookie_dir_conf *) addv; 185 session_cookie_dir_conf *base = (session_cookie_dir_conf *) basev; 186 187 new->name = (add->name_set == 0) ? base->name : add->name; 188 new->name_attrs = (add->name_set == 0) ? base->name_attrs : add->name_attrs; 189 new->name_set = add->name_set || base->name_set; 190 new->name2 = (add->name2_set == 0) ? base->name2 : add->name2; 191 new->name2_attrs = (add->name2_set == 0) ? base->name2_attrs : add->name2_attrs; 192 new->name2_set = add->name2_set || base->name2_set; 193 new->remove = (add->remove_set == 0) ? base->remove : add->remove; 194 new->remove_set = add->remove_set || base->remove_set; 195 196 return new; 197} 198 199/** 200 * Sanity check a given string that it exists, is not empty, 201 * and does not contain special characters. 202 */ 203static const char *check_string(cmd_parms * cmd, const char *string) 204{ 205 if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&')) { 206 return apr_pstrcat(cmd->pool, cmd->directive->directive, 207 " cannot be empty, or contain '=' or '&'.", 208 NULL); 209 } 210 return NULL; 211} 212 213static const char *set_cookie_name(cmd_parms * cmd, void *config, 214 const char *args) 215{ 216 char *last; 217 char *line = apr_pstrdup(cmd->pool, args); 218 session_cookie_dir_conf *conf = (session_cookie_dir_conf *) config; 219 char *cookie = apr_strtok(line, " \t", &last); 220 conf->name = cookie; 221 conf->name_set = 1; 222 while (apr_isspace(*last)) { 223 last++; 224 } 225 conf->name_attrs = last; 226 return check_string(cmd, cookie); 227} 228 229static const char *set_cookie_name2(cmd_parms * cmd, void *config, 230 const char *args) 231{ 232 char *last; 233 char *line = apr_pstrdup(cmd->pool, args); 234 session_cookie_dir_conf *conf = (session_cookie_dir_conf *) config; 235 char *cookie = apr_strtok(line, " \t", &last); 236 conf->name2 = cookie; 237 conf->name2_set = 1; 238 while (apr_isspace(*last)) { 239 last++; 240 } 241 conf->name2_attrs = last; 242 return check_string(cmd, cookie); 243} 244 245static const char * 246 set_remove(cmd_parms * parms, void *dconf, int flag) 247{ 248 session_cookie_dir_conf *conf = dconf; 249 250 conf->remove = flag; 251 conf->remove_set = 1; 252 253 return NULL; 254} 255 256static const command_rec session_cookie_cmds[] = 257{ 258 AP_INIT_RAW_ARGS("SessionCookieName", set_cookie_name, NULL, RSRC_CONF|OR_AUTHCFG, 259 "The name of the RFC2109 cookie carrying the session"), 260 AP_INIT_RAW_ARGS("SessionCookieName2", set_cookie_name2, NULL, RSRC_CONF|OR_AUTHCFG, 261 "The name of the RFC2965 cookie carrying the session"), 262 AP_INIT_FLAG("SessionCookieRemove", set_remove, NULL, RSRC_CONF|OR_AUTHCFG, 263 "Set to 'On' to remove the session cookie from the headers " 264 "and hide the cookie from a backend server or process"), 265 {NULL} 266}; 267 268static void register_hooks(apr_pool_t * p) 269{ 270 ap_hook_session_load(session_cookie_load, NULL, NULL, APR_HOOK_MIDDLE); 271 ap_hook_session_save(session_cookie_save, NULL, NULL, APR_HOOK_MIDDLE); 272} 273 274AP_DECLARE_MODULE(session_cookie) = 275{ 276 STANDARD20_MODULE_STUFF, 277 create_session_cookie_dir_config, /* dir config creater */ 278 merge_session_cookie_dir_config, /* dir merger --- default is to 279 * override */ 280 NULL, /* server config */ 281 NULL, /* merge server config */ 282 session_cookie_cmds, /* command apr_table_t */ 283 register_hooks /* register hooks */ 284}; 285