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
00058 dbg_err_if (config == NULL);
00059 dbg_err_if (pso == NULL);
00060
00061 so = u_zalloc(sizeof(session_opt_t));
00062 dbg_err_if(so == NULL);
00063
00064
00065 so->type = SESSION_TYPE_FILE;
00066 so->max_age = DEFAULT_SESSION_EXPIRATION;
00067 so->compress = 0;
00068 so->encrypt = 0;
00069 (void) u_strlcpy(so->name, SID_NAME, sizeof so->name);
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 so->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
00103 if ((v = u_config_get_subkey_value(c, "sid_name")) != NULL)
00104 dbg_err_if (u_strlcpy(so->name, v, sizeof so->name));
00105
00106 #ifndef HAVE_LIBZ
00107 if(so->compress)
00108 warn_err("config error: compression is enabled but libz is not "
00109 "linked");
00110 #endif
00111
00112 #ifndef HAVE_LIBOPENSSL
00113 if(so->encrypt)
00114 warn_err("config error: encryption is enabled but OpenSSL is not "
00115 "linked");
00116 #else
00117
00118 so->cipher = EVP_aes_256_cbc();
00119
00120 EVP_add_cipher(so->cipher);
00121
00122
00123 dbg_err_if(!RAND_bytes(so->cipher_key, CIPHER_KEY_SIZE));
00124 dbg_err_if(!RAND_pseudo_bytes(so->cipher_iv, CIPHER_IV_SIZE));
00125
00126
00127 dbg_err_if(!RAND_bytes(so->session_key, CIPHER_KEY_SIZE));
00128 dbg_err_if(!RAND_pseudo_bytes(so->session_iv, CIPHER_IV_SIZE));
00129
00130 #endif
00131 }
00132
00133
00134 if(so->type == SESSION_TYPE_MEMORY)
00135 warn_err_ifm(session_mem_module_init(c, so),
00136 "in-memory session engine init error");
00137 else if(so->type == SESSION_TYPE_FILE)
00138 warn_err_ifm(session_file_module_init(c, so),
00139 "file session engine init error");
00140 #ifdef HAVE_LIBOPENSSL
00141 else if(so->type == SESSION_TYPE_CLIENT)
00142 warn_err_ifm(session_client_module_init(c, so),
00143 "client-side session engine init error");
00144 #endif
00145
00146 *pso = so;
00147
00148 return 0;
00149 err:
00150 U_FREE(so);
00151 return ~0;
00152 }
00153
00154 int session_prv_calc_maxsize(var_t *v, void *p)
00155 {
00156 const char *value = NULL;
00157 size_t *psz = (size_t*)p;
00158
00159 dbg_err_if (v == NULL);
00160 dbg_err_if (var_get_name(v) == NULL);
00161 dbg_err_if (psz == NULL);
00162
00163 #ifdef HAVE_LIBOPENSSL
00164 if(*psz == 0)
00165 {
00166 *psz = CODEC_CIPHER_BLOCK_SIZE;
00167 }
00168 #endif
00169
00170
00171 *psz += 3 * strlen(var_get_name(v)) + 3;
00172
00173
00174 if((value = var_get_value(v)) != NULL)
00175 *psz += 3 * strlen(value) + 1;
00176
00177 return 0;
00178 err:
00179 return ~0;
00180 }
00181
00182 int session_prv_load_from_buf(session_t *ss, char *buf, size_t size)
00183 {
00184 io_t *io = NULL;
00185
00186 dbg_err_if (ss == NULL);
00187 dbg_err_if (buf == NULL);
00188
00189
00190 dbg_err_if(io_mem_create(buf, size, 0, &io));
00191
00192
00193 dbg_err_if(session_prv_load_from_io(ss, io));
00194
00195 io_free(io);
00196
00197 return 0;
00198 err:
00199 if(io)
00200 io_free(io);
00201 return ~0;
00202 }
00203
00204 int session_prv_save_to_buf(session_t *ss, char **pbuf, size_t *psz)
00205 {
00206 io_t *io = NULL;
00207 char *buf = NULL;
00208 size_t sz = 0;
00209
00210 dbg_err_if (ss == NULL);
00211 dbg_err_if (pbuf == NULL);
00212 dbg_err_if (psz == NULL);
00213
00214
00215
00216 vars_foreach(ss->vars, session_prv_calc_maxsize, (void*)&sz);
00217
00218
00219 buf = u_malloc(sz);
00220 dbg_err_if(buf == NULL);
00221
00222
00223 dbg_err_if(io_mem_create(buf, sz, 0, &io));
00224
00225
00226 dbg_err_if(session_prv_save_to_io(ss, io));
00227
00228
00229
00230
00231 dbg_err_if(io_codecs_remove(io));
00232
00233
00234 sz = io_tell(io);
00235
00236 io_free(io);
00237 io = NULL;
00238
00239 *pbuf = buf;
00240 *psz = sz;
00241
00242 return 0;
00243 err:
00244 if(io)
00245 io_free(io);
00246 U_FREE(buf);
00247 return ~0;
00248 }
00249
00250 static int session_is_good_id(const char *id)
00251 {
00252 const char *p;
00253 size_t len;
00254
00255 dbg_return_if (id == NULL, 0);
00256
00257 dbg_ifb((len = strlen(id)) != SESSION_ID_LENGTH)
00258 return 0;
00259
00260 for(p = id; len; --len, ++p)
00261 {
00262
00263 if(! ((*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f') ||
00264 (*p >= '0' && *p <= '9')) )
00265 return 0;
00266 }
00267
00268 return 1;
00269 }
00270
00271 static int session_set_filename(session_t *ss)
00272 {
00273 kaddr_t *addr = NULL;
00274
00275 dbg_err_if(strlen(ss->id) == 0);
00276
00277 dbg_err_if((addr = request_get_addr(ss->rq)) == NULL);
00278 switch(addr->type)
00279 {
00280 case ADDR_IPV4:
00281 dbg_err_if(u_path_snprintf(ss->filename, U_FILENAME_MAX,
00282 U_PATH_SEPARATOR, "%s/klone_sess_%s_%lu", ss->so->path, ss->id,
00283 addr->sa.sin.sin_addr));
00284 break;
00285 case ADDR_IPV6:
00286
00287 dbg_err_if(u_path_snprintf(ss->filename, U_FILENAME_MAX,
00288 U_PATH_SEPARATOR, "%s/klone_sess_%s", ss->so->path, ss->id));
00289 break;
00290 #ifdef OS_UNIX
00291 case ADDR_UNIX:
00292
00293 dbg_err_if(u_path_snprintf(ss->filename, U_FILENAME_MAX,
00294 U_PATH_SEPARATOR, "%s/klone_sess_%s", ss->so->path, ss->id));
00295 break;
00296 #endif
00297 }
00298
00299 return 0;
00300 err:
00301 return 0;
00302 }
00303
00304 static int session_gen_id(session_t *ss)
00305 {
00306 enum { BUFSZ = 256 };
00307 char buf[BUFSZ];
00308 struct timeval tv;
00309
00310 dbg_err_if (ss == NULL);
00311
00312
00313 gettimeofday(&tv, NULL);
00314
00315 dbg_err_if(u_snprintf(buf, BUFSZ, "%lu%d%lu%d", tv.tv_sec, getpid(),
00316 tv.tv_usec, rand()));
00317
00318
00319 dbg_err_if(u_md5(buf, strlen(buf), ss->id));
00320
00321
00322 dbg_err_if(response_set_cookie(ss->rs, ss->so->name, NULL, 0, NULL,
00323 NULL, 0));
00324
00325
00326 dbg_err_if(response_set_cookie(ss->rs, ss->so->name, ss->id, 0, NULL,
00327 NULL, 0));
00328
00329 return 0;
00330 err:
00331 return ~0;
00332 }
00333
00334 int session_priv_set_id(session_t *ss, const char *sid)
00335 {
00336
00337 if(sid && session_is_good_id(sid))
00338 {
00339 dbg_err_if(u_snprintf(ss->id, SESSION_ID_BUFSZ, "%s", sid));
00340 ss->id[SESSION_ID_BUFSZ-1] = 0;
00341 } else
00342 dbg_err_if(session_gen_id(ss));
00343
00344
00345 dbg_err_if(session_set_filename(ss));
00346
00347 return 0;
00348 err:
00349 return ~0;
00350 }
00351
00352 int session_load(session_t *ss)
00353 {
00354 dbg_return_if (ss == NULL, ~0);
00355 dbg_return_if (ss->load == NULL, ~0);
00356
00357 return ss->load(ss);
00358 }
00359
00360 int session_save(session_t *ss)
00361 {
00362 size_t vcount;
00363 dbg_err_if (ss == NULL);
00364 dbg_err_if (ss->save == NULL);
00365
00366 vcount = vars_count(ss->vars);
00367
00368 if(ss->id[0] == 0 && vcount == 0)
00369 return 0;
00370
00371 if(ss->id[0] != 0 && vcount == 0)
00372 return session_remove(ss);
00373
00374 if(ss->id[0] == 0)
00375 {
00376
00377
00378
00379 dbg_err_if(session_priv_set_id(ss, NULL));
00380 }
00381
00382 return ss->save(ss);
00383 err:
00384 return ~0;
00385 }
00386
00387 int session_remove(session_t *ss)
00388 {
00389 dbg_return_if (ss == NULL, ~0);
00390 dbg_return_if (ss->remove == NULL, ~0);
00391
00392
00393 response_set_cookie(ss->rs, ss->so->name, NULL, 0, NULL, NULL, 0);
00394
00395 ss->removed = 1;
00396
00397 return ss->remove(ss);
00398 }
00399
00400 int session_prv_init(session_t *ss, request_t *rq, response_t *rs)
00401 {
00402 const char *sid;
00403
00404 dbg_err_if (ss == NULL);
00405 dbg_err_if (rq == NULL);
00406 dbg_err_if (rs == NULL);
00407
00408 dbg_err_if(vars_create(&ss->vars));
00409
00410 ss->rq = rq;
00411 ss->rs = rs;
00412
00413
00414 sid = request_get_cookie(ss->rq, ss->so->name);
00415 if(sid)
00416 dbg_err_if(session_priv_set_id(ss, sid));
00417
00418 return 0;
00419 err:
00420 return ~0;
00421 }
00422
00423 int session_prv_load_from_io(session_t *ss, io_t *io)
00424 {
00425 u_string_t *line = NULL;
00426 var_t *v = NULL;
00427 codec_t *unzip = NULL, *decrypt = NULL;
00428 unsigned char key[CODEC_CIPHER_KEY_SIZE];
00429 size_t ksz;
00430
00431 dbg_return_if (ss == NULL, ~0);
00432 dbg_return_if (io == NULL, ~0);
00433
00434 #ifdef HAVE_LIBOPENSSL
00435 if(ss->so->encrypt)
00436 {
00437 dbg_err_if(codec_cipher_create(CIPHER_DECRYPT, ss->so->cipher,
00438 ss->so->cipher_key, ss->so->cipher_iv, &decrypt));
00439 dbg_err_if(io_codec_add_tail(io, decrypt));
00440 decrypt = NULL;
00441 }
00442 #else
00443 u_unused_args(key, ksz);
00444 #endif
00445
00446 #ifdef HAVE_LIBZ
00447 if(ss->so->compress)
00448 {
00449 dbg_err_if(codec_gzip_create(GZIP_UNCOMPRESS, &unzip));
00450 dbg_err_if(io_codec_add_tail(io, unzip));
00451 unzip = NULL;
00452 }
00453 #endif
00454
00455 dbg_err_if(u_string_create(NULL, 0, &line));
00456
00457 while(u_getline(io, line) == 0)
00458 {
00459 if(u_string_len(line))
00460 {
00461 dbg_err_if(vars_add_urlvar(ss->vars, u_string_c(line), &v));
00462
00463 #ifdef HAVE_LIBOPENSSL
00464 if(!strcmp(var_get_name(v), "KLONE_CIPHER_KEY"))
00465 {
00466
00467 memset(key, 0, sizeof(key));
00468 dbg_ifb(u_cipher_decrypt(EVP_aes_256_cbc(), ss->so->session_key,
00469 ss->so->session_iv, key, &ksz,
00470 var_get_value(v), var_get_value_size(v)))
00471 {
00472 v = vars_get(ss->vars, "KLONE_CIPHER_KEY");
00473 vars_del(ss->vars, v);
00474 } else {
00475
00476 dbg_err_if(var_set_bin_value(v, key, ksz));
00477 }
00478 }
00479 #endif
00480 }
00481 }
00482
00483
00484 io_codecs_remove(io);
00485
00486 u_string_free(line);
00487
00488 return 0;
00489 err:
00490 if(io)
00491 io_codecs_remove(io);
00492 if(decrypt)
00493 codec_free(decrypt);
00494 if(unzip)
00495 codec_free(unzip);
00496 if(line)
00497 u_string_free(line);
00498 return ~0;
00499 }
00500
00501 int session_free(session_t *ss)
00502 {
00503 if (ss)
00504 {
00505 if(!ss->removed)
00506 dbg_if(session_save(ss));
00507
00508
00509 dbg_if(ss->term(ss));
00510
00511 if(ss->vars)
00512 vars_free(ss->vars);
00513
00514 U_FREE(ss);
00515 }
00516
00517 return 0;
00518 }
00519
00530 vars_t *session_get_vars(session_t *ss)
00531 {
00532 dbg_return_if (ss == NULL, NULL);
00533
00534 return ss->vars;
00535 }
00536
00549 const char *session_get(session_t *ss, const char *name)
00550 {
00551 var_t *v;
00552
00553 dbg_return_if (ss == NULL, NULL);
00554 dbg_return_if (name == NULL, NULL);
00555
00556 v = vars_get(ss->vars, name);
00557 return v ? var_get_value(v): NULL;
00558 }
00559
00570 const char *session_get_id (session_t *ss)
00571 {
00572 dbg_return_if (ss == NULL, NULL);
00573
00574 return ss->id;
00575 }
00576
00589 int session_set(session_t *ss, const char *name, const char *value)
00590 {
00591 var_t *v = NULL;
00592
00593 dbg_err_if (ss == NULL);
00594 dbg_err_if (name == NULL);
00595 dbg_err_if (value == NULL);
00596 dbg_err_if (strlen(name) == 0);
00597
00598 if((v = vars_get(ss->vars, name)) == NULL)
00599 {
00600
00601 dbg_err_if(var_create(name, value, &v));
00602
00603 dbg_err_if(vars_add(ss->vars, v));
00604 } else {
00605
00606 dbg_ifb(var_set_value(v, value))
00607 return ~0;
00608 }
00609
00610 return 0;
00611 err:
00612 if(v)
00613 var_free(v);
00614 return ~0;
00615 }
00616
00629 int session_age(session_t *ss)
00630 {
00631 time_t now;
00632
00633 dbg_return_if (ss == NULL, -1);
00634
00635 now = time(0);
00636
00637
00638 return (int)(now - ss->mtime);
00639 }
00640
00651 int session_clean(session_t *ss)
00652 {
00653 var_t *v = NULL;
00654
00655 dbg_err_if (ss == NULL);
00656
00657 while((v = vars_getn(ss->vars, 0)) != NULL)
00658 {
00659 dbg_err_if(vars_del(ss->vars, v));
00660 var_free(v);
00661 }
00662
00663 return 0;
00664 err:
00665 return ~0;
00666 }
00667
00681 int session_del(session_t *ss, const char *name)
00682 {
00683 var_t *v = NULL;
00684
00685 dbg_err_if (ss == NULL);
00686 dbg_err_if (name == NULL);
00687
00688 dbg_err_if((v = vars_get(ss->vars, name)) == NULL);
00689 dbg_err_if(vars_del(ss->vars, v));
00690 var_free(v);
00691
00692 return 0;
00693 err:
00694 return ~0;
00695 }
00696
00697 int session_prv_save_to_io(session_t *ss, io_t *out)
00698 {
00699 save_cb_params_t prm;
00700 codec_t *zip = NULL, *cencrypt = NULL;
00701
00702 dbg_err_if (ss == NULL);
00703 dbg_err_if (out == NULL);
00704
00705 #ifdef HAVE_LIBZ
00706 if(ss->so->compress)
00707 {
00708 dbg_err_if(codec_gzip_create(GZIP_COMPRESS, &zip));
00709 dbg_err_if(io_codec_add_tail(out, zip));
00710 zip = NULL;
00711 }
00712 #endif
00713
00714 #ifdef HAVE_LIBOPENSSL
00715 if(ss->so->encrypt)
00716 {
00717 dbg_err_if(codec_cipher_create(CIPHER_ENCRYPT, ss->so->cipher,
00718 ss->so->cipher_key, ss->so->cipher_iv, &cencrypt));
00719 dbg_err_if(io_codec_add_tail(out, cencrypt));
00720 cencrypt = NULL;
00721 }
00722 #endif
00723
00724
00725 prm.io = out;
00726 prm.ss = ss;
00727
00728 vars_foreach(ss->vars, session_prv_save_var, (void*)&prm);
00729
00730
00731 io_codecs_remove(out);
00732
00733 return 0;
00734 err:
00735 if(out)
00736 io_codecs_remove(out);
00737 if(zip)
00738 codec_free(zip);
00739 if(cencrypt)
00740 codec_free(cencrypt);
00741 return ~0;
00742 }
00743
00744
00745 int session_prv_save_var(var_t *v, void *vp)
00746 {
00747 enum { NAMESZ = 256, VALSZ = 4096 };
00748 char sname[NAMESZ], svalue[VALSZ];
00749 char *uname = sname, *uvalue = svalue;
00750 save_cb_params_t *pprm = (save_cb_params_t*)vp;
00751
00752 unsigned char ekey[CODEC_CIPHER_KEY_SIZE + CODEC_CIPHER_BLOCK_SIZE + 1];
00753 size_t eksz, nsz, vsz;
00754 int rc = ~0;
00755
00756 dbg_err_if (v == NULL);
00757
00758
00759
00760 nsz = 1 + 3 * strlen(var_get_name(v));
00761 vsz = 1 + 3 * var_get_value_size(v);
00762
00763 #ifdef HAVE_LIBOPENSSL
00764 vsz += CODEC_CIPHER_BLOCK_SIZE;
00765
00766 #else
00767 u_unused_args(ekey, eksz);
00768 #endif
00769
00770
00771 if(NAMESZ <= nsz)
00772 dbg_err_if((uname = u_zalloc(nsz)) == NULL);
00773
00774
00775 dbg_err_if(u_urlncpy(uname, var_get_name(v), strlen(var_get_name(v)),
00776 URLCPY_ENCODE) <= 0);
00777
00778 if(var_get_value(v))
00779 {
00780
00781 if(VALSZ <= vsz)
00782 dbg_err_if((uvalue = u_zalloc(vsz)) == NULL);
00783
00784 #ifdef HAVE_LIBOPENSSL
00785 if(!strcmp(var_get_name(v), "KLONE_CIPHER_KEY"))
00786 {
00787
00788 dbg_err_if(u_cipher_encrypt(EVP_aes_256_cbc(),
00789 pprm->ss->so->session_key, pprm->ss->so->session_iv,
00790 ekey, &eksz, var_get_value(v), var_get_value_size(v)));
00791
00792
00793 dbg_err_if(var_set_bin_value(v, ekey, eksz));
00794 }
00795 #endif
00796
00797 dbg_err_if(u_urlncpy(uvalue, var_get_value(v), var_get_value_size(v),
00798 URLCPY_ENCODE) <= 0);
00799
00800 dbg_err_if(io_printf(pprm->io, "%s=%s\n", uname, uvalue) < 0);
00801 } else
00802 dbg_err_if(io_printf(pprm->io, "%s=\n", uname) < 0);
00803
00804 rc = 0;
00805 err:
00806
00807 if(uname && uname != sname)
00808 U_FREE(uname);
00809
00810 if(uvalue && uvalue != svalue)
00811 U_FREE(uvalue);
00812
00813 return rc;
00814 }
00815
00816 int session_create(session_opt_t *so, request_t *rq, response_t *rs,
00817 session_t **pss)
00818 {
00819 session_t *ss = NULL;
00820
00821 dbg_err_if (so == NULL);
00822 dbg_err_if (rq == NULL);
00823 dbg_err_if (rs == NULL);
00824 dbg_err_if (pss == NULL);
00825
00826 switch(so->type)
00827 {
00828 case SESSION_TYPE_FILE:
00829 dbg_err_if(session_file_create(so, rq, rs, &ss));
00830 break;
00831 case SESSION_TYPE_MEMORY:
00832 dbg_err_if(session_mem_create(so, rq, rs, &ss));
00833 break;
00834 #ifdef HAVE_LIBOPENSSL
00835 case SESSION_TYPE_CLIENT:
00836 dbg_err_if(session_client_create(so, rq, rs, &ss));
00837 break;
00838 #endif
00839 default:
00840 warn_err("bad session type");
00841 }
00842
00843
00844 if(ss->id[0] != 0)
00845 {
00846 session_load(ss);
00847
00848 dbg_ifb(session_age(ss) > so->max_age)
00849 {
00850 session_clean(ss);
00851 session_remove(ss);
00852 }
00853 }
00854
00855 *pss = ss;
00856
00857 return 0;
00858 err:
00859 if(ss)
00860 session_free(ss);
00861 return ~0;
00862 }