00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "klone_conf.h"
00012 #include <sys/types.h>
00013 #include <sys/stat.h>
00014 #include <sys/time.h>
00015 #include <stdlib.h>
00016 #include <time.h>
00017 #include <unistd.h>
00018 #include <fcntl.h>
00019 #ifdef HAVE_LIBOPENSSL
00020 #include <openssl/hmac.h>
00021 #include <openssl/evp.h>
00022 #include <openssl/rand.h>
00023 #include <klone/ccipher.h>
00024 #endif
00025 #include <u/libu.h>
00026 #include <klone/session.h>
00027 #include <klone/request.h>
00028 #include <klone/response.h>
00029 #include <klone/vars.h>
00030 #include <klone/utils.h>
00031 #include <klone/ses_prv.h>
00032 #include <klone/codecs.h>
00033
00034 enum { DEFAULT_SESSION_EXPIRATION = 60*20 };
00035 static const char SID_NAME[] = "klone_sid";
00036
00037 struct save_cb_params_s
00038 {
00039 session_t *ss;
00040 io_t *io;
00041 };
00042
00043 typedef struct save_cb_params_s save_cb_params_t;
00044
00045 int session_module_term(session_opt_t *so)
00046 {
00047 U_FREE(so);
00048
00049 return 0;
00050 }
00051
00052 int session_module_init(u_config_t *config, session_opt_t **pso)
00053 {
00054 session_opt_t *so = NULL;
00055 u_config_t *c = NULL;
00056 const char *v;
00057 int max_age;
00058
00059 dbg_err_if (config == NULL);
00060 dbg_err_if (pso == NULL);
00061
00062 so = u_zalloc(sizeof(session_opt_t));
00063 dbg_err_if(so == NULL);
00064
00065
00066 so->type = SESSION_TYPE_FILE;
00067 so->max_age = DEFAULT_SESSION_EXPIRATION;
00068 so->compress = 0;
00069 so->encrypt = 0;
00070
00071 if(!u_config_get_subkey(config, "session", &c))
00072 {
00073
00074
00075
00076 if((v = u_config_get_subkey_value(c, "type")) != NULL)
00077 {
00078 if(!strcasecmp(v, "memory")) {
00079 so->type = SESSION_TYPE_MEMORY;
00080 } else if(!strcasecmp(v, "file")) {
00081 so->type = SESSION_TYPE_FILE;
00082 #ifdef HAVE_LIBOPENSSL
00083 } else if(!strcasecmp(v, "client")) {
00084 so->type = SESSION_TYPE_CLIENT;
00085 #endif
00086 } else
00087 warn_err("config error: bad session type (typo or missing "
00088 "library)");
00089 }
00090
00091
00092 if((v = u_config_get_subkey_value(c, "max_age")) != NULL)
00093 max_age = MAX(atoi(v) * 60, 60);
00094
00095
00096 dbg_err_if(u_config_get_subkey_value_b(c, "compress", 0,
00097 &so->compress));
00098
00099
00100 dbg_err_if(u_config_get_subkey_value_b(c, "encrypt", 0, &so->encrypt));
00101
00102 #ifndef HAVE_LIBZ
00103 if(so->compress)
00104 warn_err("config error: compression is enabled but libz is not "
00105 "linked");
00106 #endif
00107
00108 #ifndef HAVE_LIBOPENSSL
00109 if(so->encrypt)
00110 warn_err("config error: encryption is enabled but OpenSSL is not "
00111 "linked");
00112 #else
00113
00114 so->cipher = EVP_aes_256_cbc();
00115
00116 EVP_add_cipher(so->cipher);
00117
00118
00119 dbg_err_if(!RAND_bytes(so->cipher_key, CIPHER_KEY_SIZE));
00120 dbg_err_if(!RAND_pseudo_bytes(so->cipher_iv, CIPHER_IV_SIZE));
00121
00122
00123 dbg_err_if(!RAND_bytes(so->session_key, CIPHER_KEY_SIZE));
00124 dbg_err_if(!RAND_pseudo_bytes(so->session_iv, CIPHER_IV_SIZE));
00125
00126 #endif
00127 }
00128
00129
00130 if(so->type == SESSION_TYPE_MEMORY)
00131 warn_err_ifm(session_mem_module_init(c, so),
00132 "in-memory session engine init error");
00133 else if(so->type == SESSION_TYPE_FILE)
00134 warn_err_ifm(session_file_module_init(c, so),
00135 "file session engine init error");
00136 #ifdef HAVE_LIBOPENSSL
00137 else if(so->type == SESSION_TYPE_CLIENT)
00138 warn_err_ifm(session_client_module_init(c, so),
00139 "client-side session engine init error");
00140 #endif
00141
00142 *pso = so;
00143
00144 return 0;
00145 err:
00146 U_FREE(so);
00147 return ~0;
00148 }
00149
00150 int session_prv_calc_maxsize(var_t *v, void *p)
00151 {
00152 const char *value = NULL;
00153 size_t *psz = (size_t*)p;
00154
00155 dbg_err_if (v == NULL);
00156 dbg_err_if (var_get_name(v) == NULL);
00157 dbg_err_if (psz == NULL);
00158
00159 #ifdef HAVE_LIBOPENSSL
00160 if(*psz == 0)
00161 {
00162 *psz = CODEC_CIPHER_BLOCK_SIZE;
00163 }
00164 #endif
00165
00166
00167 *psz += 3 * strlen(var_get_name(v)) + 3;
00168
00169
00170 if((value = var_get_value(v)) != NULL)
00171 *psz += 3 * strlen(value) + 1;
00172
00173 return 0;
00174 err:
00175 return ~0;
00176 }
00177
00178 int session_prv_load_from_buf(session_t *ss, char *buf, size_t size)
00179 {
00180 io_t *io = NULL;
00181
00182 dbg_err_if (ss == NULL);
00183 dbg_err_if (buf == NULL);
00184
00185
00186 dbg_err_if(io_mem_create(buf, size, 0, &io));
00187
00188
00189 dbg_err_if(session_prv_load_from_io(ss, io));
00190
00191 io_free(io);
00192
00193 return 0;
00194 err:
00195 if(io)
00196 io_free(io);
00197 return ~0;
00198 }
00199
00200 int session_prv_save_to_buf(session_t *ss, char **pbuf, size_t *psz)
00201 {
00202 io_t *io = NULL;
00203 char *buf = NULL;
00204 size_t sz = 0;
00205
00206 dbg_err_if (ss == NULL);
00207 dbg_err_if (pbuf == NULL);
00208 dbg_err_if (psz == NULL);
00209
00210
00211
00212 vars_foreach(ss->vars, session_prv_calc_maxsize, (void*)&sz);
00213
00214
00215 buf = u_malloc(sz);
00216 dbg_err_if(buf == NULL);
00217
00218
00219 dbg_err_if(io_mem_create(buf, sz, 0, &io));
00220
00221
00222 dbg_err_if(session_prv_save_to_io(ss, io));
00223
00224
00225
00226
00227 dbg_err_if(io_codecs_remove(io));
00228
00229
00230 sz = io_tell(io);
00231
00232 io_free(io);
00233 io = NULL;
00234
00235 *pbuf = buf;
00236 *psz = sz;
00237
00238 return 0;
00239 err:
00240 if(io)
00241 io_free(io);
00242 U_FREE(buf);
00243 return ~0;
00244 }
00245
00246 static int session_is_good_id(const char *id)
00247 {
00248 const char *p;
00249 size_t len;
00250
00251 dbg_return_if (id == NULL, 0);
00252
00253 dbg_ifb((len = strlen(id)) != SESSION_ID_LENGTH)
00254 return 0;
00255
00256 for(p = id; len; --len, ++p)
00257 {
00258
00259 if(! ((*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f') ||
00260 (*p >= '0' && *p <= '9')) )
00261 return 0;
00262 }
00263
00264 return 1;
00265 }
00266
00267 static int session_set_filename(session_t *ss)
00268 {
00269 addr_t *addr = NULL;
00270
00271 dbg_err_if(strlen(ss->id) == 0);
00272
00273 dbg_err_if((addr = request_get_addr(ss->rq)) == NULL);
00274 switch(addr->type)
00275 {
00276 case ADDR_IPV4:
00277 dbg_err_if(u_path_snprintf(ss->filename, U_FILENAME_MAX,
00278 U_PATH_SEPARATOR, "%s/klone_sess_%s_%lu", ss->so->path, ss->id,
00279 addr->sa.sin.sin_addr));
00280 break;
00281 case ADDR_IPV6:
00282
00283 dbg_err_if(u_path_snprintf(ss->filename, U_FILENAME_MAX,
00284 U_PATH_SEPARATOR, "%s/klone_sess_%s", ss->so->path, ss->id));
00285 break;
00286 #ifdef OS_UNIX
00287 case ADDR_UNIX:
00288
00289 dbg_err_if(u_path_snprintf(ss->filename, U_FILENAME_MAX,
00290 U_PATH_SEPARATOR, "%s/klone_sess_%s", ss->so->path, ss->id));
00291 break;
00292 #endif
00293 }
00294
00295 return 0;
00296 err:
00297 return 0;
00298 }
00299
00300 static int session_gen_id(session_t *ss)
00301 {
00302 enum { BUFSZ = 256 };
00303 char buf[BUFSZ];
00304 struct timeval tv;
00305
00306 dbg_err_if (ss == NULL);
00307
00308
00309 gettimeofday(&tv, NULL);
00310
00311 dbg_err_if(u_snprintf(buf, BUFSZ, "%lu%d%lu%d", tv.tv_sec, getpid(),
00312 tv.tv_usec, rand()));
00313
00314
00315 dbg_err_if(u_md5(buf, strlen(buf), ss->id));
00316
00317
00318 dbg_err_if(response_set_cookie(ss->rs, SID_NAME, NULL, 0, NULL, NULL, 0));
00319
00320
00321 dbg_err_if(response_set_cookie(ss->rs, SID_NAME, ss->id, 0, NULL,
00322 NULL, 0));
00323
00324 return 0;
00325 err:
00326 return ~0;
00327 }
00328
00329 int session_priv_set_id(session_t *ss, const char *sid)
00330 {
00331
00332 if(sid && session_is_good_id(sid))
00333 {
00334 dbg_err_if(u_snprintf(ss->id, SESSION_ID_BUFSZ, "%s", sid));
00335 ss->id[SESSION_ID_BUFSZ-1] = 0;
00336 } else
00337 dbg_err_if(session_gen_id(ss));
00338
00339
00340 dbg_err_if(session_set_filename(ss));
00341
00342 return 0;
00343 err:
00344 return ~0;
00345 }
00346
00347 int session_load(session_t *ss)
00348 {
00349 dbg_return_if (ss == NULL, ~0);
00350 dbg_return_if (ss->load == NULL, ~0);
00351
00352 return ss->load(ss);
00353 }
00354
00355 int session_save(session_t *ss)
00356 {
00357 dbg_err_if (ss == NULL);
00358 dbg_err_if (ss->save == NULL);
00359
00360 if(vars_count(ss->vars) == 0)
00361 return 0;
00362
00363 if(!strlen(ss->id))
00364 {
00365
00366 dbg_err_if(session_priv_set_id(ss, NULL));
00367 }
00368
00369 return ss->save(ss);
00370 err:
00371 return ~0;
00372 }
00373
00374 int session_remove(session_t *ss)
00375 {
00376 dbg_return_if (ss == NULL, ~0);
00377 dbg_return_if (ss->remove == NULL, ~0);
00378
00379 ss->removed = 1;
00380
00381 return ss->remove(ss);
00382 }
00383
00384 int session_prv_init(session_t *ss, request_t *rq, response_t *rs)
00385 {
00386 const char *sid;
00387
00388 dbg_err_if (ss == NULL);
00389 dbg_err_if (rq == NULL);
00390 dbg_err_if (rs == NULL);
00391
00392 dbg_err_if(vars_create(&ss->vars));
00393
00394 ss->rq = rq;
00395 ss->rs = rs;
00396
00397
00398 sid = request_get_cookie(ss->rq, SID_NAME);
00399 if(sid)
00400 dbg_err_if(session_priv_set_id(ss, sid));
00401
00402 return 0;
00403 err:
00404 return ~0;
00405 }
00406
00407 int session_prv_load_from_io(session_t *ss, io_t *io)
00408 {
00409 u_string_t *line = NULL;
00410 var_t *v = NULL;
00411 codec_t *unzip = NULL, *decrypt = NULL;
00412 unsigned char key[CODEC_CIPHER_KEY_SIZE];
00413 size_t ksz;
00414
00415 dbg_return_if (ss == NULL, ~0);
00416 dbg_return_if (io == NULL, ~0);
00417
00418 #ifdef HAVE_LIBOPENSSL
00419 if(ss->so->encrypt)
00420 {
00421 dbg_err_if(codec_cipher_create(CIPHER_DECRYPT, ss->so->cipher,
00422 ss->so->cipher_key, ss->so->cipher_iv, &decrypt));
00423 dbg_err_if(io_codec_add_tail(io, decrypt));
00424 decrypt = NULL;
00425 }
00426 #else
00427 u_unused_args(key, ksz);
00428 #endif
00429
00430 #ifdef HAVE_LIBZ
00431 if(ss->so->compress)
00432 {
00433 dbg_err_if(codec_gzip_create(GZIP_UNCOMPRESS, &unzip));
00434 dbg_err_if(io_codec_add_tail(io, unzip));
00435 unzip = NULL;
00436 }
00437 #endif
00438
00439 dbg_err_if(u_string_create(NULL, 0, &line));
00440
00441 while(u_getline(io, line) == 0)
00442 {
00443 if(u_string_len(line))
00444 {
00445 dbg_err_if(vars_add_urlvar(ss->vars, u_string_c(line), &v));
00446
00447 #ifdef HAVE_LIBOPENSSL
00448 if(!strcmp(var_get_name(v), "KLONE_CIPHER_KEY"))
00449 {
00450
00451 memset(key, 0, sizeof(key));
00452 dbg_ifb(u_cipher_decrypt(EVP_aes_256_cbc(), ss->so->session_key,
00453 ss->so->session_iv, key, &ksz,
00454 var_get_value(v), var_get_value_size(v)))
00455 {
00456 v = vars_get(ss->vars, "KLONE_CIPHER_KEY");
00457 vars_del(ss->vars, v);
00458 } else {
00459
00460 dbg_err_if(var_set_bin_value(v, key, ksz));
00461 }
00462 }
00463 #endif
00464 }
00465 }
00466
00467
00468 io_codecs_remove(io);
00469
00470 u_string_free(line);
00471
00472 return 0;
00473 err:
00474 if(io)
00475 io_codecs_remove(io);
00476 if(decrypt)
00477 codec_free(decrypt);
00478 if(unzip)
00479 codec_free(unzip);
00480 if(line)
00481 u_string_free(line);
00482 return ~0;
00483 }
00484
00485 int session_free(session_t *ss)
00486 {
00487 if (ss)
00488 {
00489 if(!ss->removed)
00490 dbg_if(session_save(ss));
00491
00492
00493 dbg_if(ss->term(ss));
00494
00495 if(ss->vars)
00496 vars_free(ss->vars);
00497
00498 U_FREE(ss);
00499 }
00500
00501 return 0;
00502 }
00503
00514 vars_t *session_get_vars(session_t *ss)
00515 {
00516 dbg_return_if (ss == NULL, NULL);
00517
00518 return ss->vars;
00519 }
00520
00533 const char *session_get(session_t *ss, const char *name)
00534 {
00535 var_t *v;
00536
00537 dbg_return_if (ss == NULL, NULL);
00538 dbg_return_if (name == NULL, NULL);
00539
00540 v = vars_get(ss->vars, name);
00541 return v ? var_get_value(v): NULL;
00542 }
00543
00556 int session_set(session_t *ss, const char *name, const char *value)
00557 {
00558 var_t *v = NULL;
00559
00560 dbg_err_if (ss == NULL);
00561 dbg_err_if (name == NULL);
00562 dbg_err_if (value == NULL);
00563 dbg_err_if (strlen(name) == 0);
00564
00565 if((v = vars_get(ss->vars, name)) == NULL)
00566 {
00567
00568 dbg_err_if(var_create(name, value, &v));
00569
00570 dbg_err_if(vars_add(ss->vars, v));
00571 } else {
00572
00573 dbg_ifb(var_set_value(v, value))
00574 return ~0;
00575 }
00576
00577 return 0;
00578 err:
00579 if(v)
00580 var_free(v);
00581 return ~0;
00582 }
00583
00596 int session_age(session_t *ss)
00597 {
00598 time_t now;
00599
00600 dbg_return_if (ss == NULL, -1);
00601
00602 now = time(0);
00603
00604
00605 return (int)(now - ss->mtime);
00606 }
00607
00618 int session_clean(session_t *ss)
00619 {
00620 var_t *v = NULL;
00621
00622 dbg_err_if (ss == NULL);
00623
00624 while((v = vars_getn(ss->vars, 0)) != NULL)
00625 {
00626 dbg_err_if(vars_del(ss->vars, v));
00627 var_free(v);
00628 }
00629
00630 return 0;
00631 err:
00632 return ~0;
00633 }
00634
00648 int session_del(session_t *ss, const char *name)
00649 {
00650 var_t *v = NULL;
00651
00652 dbg_err_if (ss == NULL);
00653 dbg_err_if (name == NULL);
00654
00655 dbg_err_if((v = vars_get(ss->vars, name)) == NULL);
00656 dbg_err_if(vars_del(ss->vars, v));
00657 var_free(v);
00658
00659 return 0;
00660 err:
00661 return ~0;
00662 }
00663
00664 int session_prv_save_to_io(session_t *ss, io_t *out)
00665 {
00666 save_cb_params_t prm;
00667 codec_t *zip = NULL, *cencrypt = NULL;
00668
00669 dbg_err_if (ss == NULL);
00670 dbg_err_if (out == NULL);
00671
00672 #ifdef HAVE_LIBZ
00673 if(ss->so->compress)
00674 {
00675 dbg_err_if(codec_gzip_create(GZIP_COMPRESS, &zip));
00676 dbg_err_if(io_codec_add_tail(out, zip));
00677 zip = NULL;
00678 }
00679 #endif
00680
00681 #ifdef HAVE_LIBOPENSSL
00682 if(ss->so->encrypt)
00683 {
00684 dbg_err_if(codec_cipher_create(CIPHER_ENCRYPT, ss->so->cipher,
00685 ss->so->cipher_key, ss->so->cipher_iv, &cencrypt));
00686 dbg_err_if(io_codec_add_tail(out, cencrypt));
00687 cencrypt = NULL;
00688 }
00689 #endif
00690
00691
00692 prm.io = out;
00693 prm.ss = ss;
00694
00695 vars_foreach(ss->vars, session_prv_save_var, (void*)&prm);
00696
00697
00698 io_codecs_remove(out);
00699
00700 return 0;
00701 err:
00702 if(out)
00703 io_codecs_remove(out);
00704 if(zip)
00705 codec_free(zip);
00706 if(cencrypt)
00707 codec_free(cencrypt);
00708 return ~0;
00709 }
00710
00711
00712 int session_prv_save_var(var_t *v, void *vp)
00713 {
00714 enum { NAMESZ = 256, VALSZ = 4096 };
00715 char sname[NAMESZ], svalue[VALSZ];
00716 char *uname = sname, *uvalue = svalue;
00717 save_cb_params_t *pprm = (save_cb_params_t*)vp;
00718
00719 unsigned char ekey[CODEC_CIPHER_KEY_SIZE + CODEC_CIPHER_BLOCK_SIZE + 1];
00720 size_t eksz, nsz, vsz;
00721 int rc = ~0;
00722
00723 dbg_err_if (v == NULL);
00724
00725
00726
00727 nsz = 1 + 3 * strlen(var_get_name(v));
00728 vsz = 1 + 3 * var_get_value_size(v);
00729
00730 #ifdef HAVE_LIBOPENSSL
00731 vsz += CODEC_CIPHER_BLOCK_SIZE;
00732
00733 #else
00734 u_unused_args(ekey, eksz);
00735 #endif
00736
00737
00738 if(NAMESZ <= nsz)
00739 dbg_err_if((uname = u_zalloc(nsz)) == NULL);
00740
00741
00742 dbg_err_if(u_urlncpy(uname, var_get_name(v), strlen(var_get_name(v)),
00743 URLCPY_ENCODE) <= 0);
00744
00745 if(var_get_value(v))
00746 {
00747
00748 if(VALSZ <= vsz)
00749 dbg_err_if((uvalue = u_zalloc(vsz)) == NULL);
00750
00751 #ifdef HAVE_LIBOPENSSL
00752 if(!strcmp(var_get_name(v), "KLONE_CIPHER_KEY"))
00753 {
00754
00755 dbg_err_if(u_cipher_encrypt(EVP_aes_256_cbc(),
00756 pprm->ss->so->session_key, pprm->ss->so->session_iv,
00757 ekey, &eksz, var_get_value(v), var_get_value_size(v)));
00758
00759
00760 dbg_err_if(var_set_bin_value(v, ekey, eksz));
00761 }
00762 #endif
00763
00764 dbg_err_if(u_urlncpy(uvalue, var_get_value(v), var_get_value_size(v),
00765 URLCPY_ENCODE) <= 0);
00766
00767 dbg_err_if(io_printf(pprm->io, "%s=%s\n", uname, uvalue) < 0);
00768 } else
00769 dbg_err_if(io_printf(pprm->io, "%s=\n", uname) < 0);
00770
00771 rc = 0;
00772 err:
00773
00774 if(uname && uname != sname)
00775 U_FREE(uname);
00776
00777 if(uvalue && uvalue != svalue)
00778 U_FREE(uvalue);
00779
00780 return rc;
00781 }
00782
00783 int session_create(session_opt_t *so, request_t *rq, response_t *rs,
00784 session_t **pss)
00785 {
00786 session_t *ss = NULL;
00787
00788 dbg_err_if (so == NULL);
00789 dbg_err_if (rq == NULL);
00790 dbg_err_if (rs == NULL);
00791 dbg_err_if (pss == NULL);
00792
00793 switch(so->type)
00794 {
00795 case SESSION_TYPE_FILE:
00796 dbg_err_if(session_file_create(so, rq, rs, &ss));
00797 break;
00798 case SESSION_TYPE_MEMORY:
00799 dbg_err_if(session_mem_create(so, rq, rs, &ss));
00800 break;
00801 #ifdef HAVE_LIBOPENSSL
00802 case SESSION_TYPE_CLIENT:
00803 dbg_err_if(session_client_create(so, rq, rs, &ss));
00804 break;
00805 #endif
00806 default:
00807 warn_err("bad session type");
00808 }
00809
00810
00811 session_load(ss);
00812
00813 dbg_ifb(session_age(ss) > so->max_age)
00814 {
00815 session_clean(ss);
00816 session_remove(ss);
00817 }
00818
00819 *pss = ss;
00820
00821 return 0;
00822 err:
00823 if(ss)
00824 session_free(ss);
00825 return ~0;
00826 }