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 <stdlib.h>
00015 #include <time.h>
00016 #include <unistd.h>
00017 #include <fcntl.h>
00018 #include <openssl/hmac.h>
00019 #include <openssl/evp.h>
00020 #include <openssl/rand.h>
00021 #include <u/libu.h>
00022 #include <klone/session.h>
00023 #include <klone/request.h>
00024 #include <klone/response.h>
00025 #include <klone/vars.h>
00026 #include <klone/utils.h>
00027 #include <klone/emb.h>
00028 #include <klone/ses_prv.h>
00029 #include <klone/codecs.h>
00030
00031 #define KL1_CLISES_DATA "KL1_CLISES_DATA"
00032 #define KL1_CLISES_MTIME "KL1_CLISES_MTIME"
00033 #define KL1_CLISES_HMAC "KL1_CLISES_HMAC"
00034 #define KL1_CLISES_IV "KL1_CLISES_IV"
00035
00036 enum { HMAC_HEX_SIZE = 2*EVP_MAX_MD_SIZE + 1 };
00037
00038
00039 static int session_client_hmac(HMAC_CTX *ctx, char *hmac, size_t hmac_sz,
00040 const char *data, const char *sid, const char *mtime, const char *hex_iv)
00041 {
00042 char mac[EVP_MAX_MD_SIZE];
00043 int mac_len;
00044
00045 dbg_err_if (ctx == NULL);
00046 dbg_err_if (hmac == NULL);
00047 dbg_err_if (data == NULL);
00048 dbg_err_if (sid == NULL);
00049 dbg_err_if (mtime == NULL);
00050
00051
00052
00053 dbg_err_if(hmac_sz < EVP_MAX_MD_SIZE*2 + 1);
00054
00055
00056 HMAC_Init_ex(ctx, NULL, 0, NULL, NULL);
00057 HMAC_Update(ctx, data, strlen(data));
00058 HMAC_Update(ctx, sid, strlen(sid));
00059 HMAC_Update(ctx, mtime, strlen(mtime));
00060 if(hex_iv)
00061 HMAC_Update(ctx, hex_iv, strlen(hex_iv));
00062 HMAC_Final(ctx, mac, &mac_len);
00063
00064
00065 dbg_err_if(u_hexncpy(hmac, mac, mac_len, HEXCPY_ENCODE) <= 0);
00066
00067 return 0;
00068 err:
00069 return -1;
00070 }
00071
00072 static int session_client_save(session_t *ss)
00073 {
00074
00075 enum {
00076 MTIME_SIZE = 32,
00077 BUF_SIZE = COOKIE_MAX_SIZE + EVP_MAX_BLOCK_LENGTH + 96
00078 };
00079 session_opt_t *so = ss->so;
00080 char hmac[HMAC_HEX_SIZE], ebuf[BUF_SIZE], mtime[MTIME_SIZE];
00081 char *buf = NULL, cipher_iv_hex[CIPHER_IV_SIZE * 2 + 1];
00082 size_t sz;
00083
00084 dbg_err_if (ss == NULL);
00085
00086 #ifdef HAVE_LIBOPENSSL
00087 if(ss->so->encrypt)
00088 {
00089
00090 dbg_err_if(!RAND_pseudo_bytes(ss->so->cipher_iv, CIPHER_IV_SIZE));
00091
00092
00093 dbg_err_if(u_hexncpy(cipher_iv_hex, ss->so->cipher_iv, CIPHER_IV_SIZE,
00094 HEXCPY_ENCODE) <= 0);
00095 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_IV, cipher_iv_hex,
00096 0, NULL, NULL, 0));
00097 }
00098 #endif
00099
00100
00101 dbg_err_if(session_prv_save_to_buf(ss, &buf, &sz));
00102
00103 warn_err_ifm(sz > COOKIE_MAX_SIZE,
00104 "session data too big for client-side sessions");
00105
00106
00107 dbg_err_if(u_hexncpy(ebuf, buf, sz, HEXCPY_ENCODE) <= 0);
00108
00109 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_DATA, ebuf, 0, NULL,
00110 NULL, 0));
00111
00112
00113 ss->mtime = time(0);
00114 dbg_err_if(u_snprintf(mtime, MTIME_SIZE, "%lu", ss->mtime));
00115 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_MTIME, mtime, 0, NULL,
00116 NULL, 0));
00117
00118
00119 dbg_err_if(session_client_hmac(&so->hmac_ctx, hmac, HMAC_HEX_SIZE,
00120 ebuf, ss->id, mtime, ss->so->encrypt ? cipher_iv_hex : NULL));
00121
00122
00123 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_HMAC, hmac, 0, NULL,
00124 NULL, 0));
00125
00126 return 0;
00127 err:
00128 return ~0;
00129 }
00130
00131 static int session_client_load(session_t *ss)
00132 {
00133 session_opt_t *so = ss->so;
00134 char hmac[HMAC_HEX_SIZE], buf[COOKIE_MAX_SIZE];
00135 const char *cli_ebuf, *cli_hmac, *cli_mtime, *cli_iv;
00136 ssize_t c;
00137
00138 dbg_err_if (ss == NULL);
00139
00140
00141 cli_ebuf = request_get_cookie(ss->rq, KL1_CLISES_DATA);
00142 cli_mtime = request_get_cookie(ss->rq, KL1_CLISES_MTIME);
00143 cli_hmac = request_get_cookie(ss->rq, KL1_CLISES_HMAC);
00144 cli_iv = request_get_cookie(ss->rq, KL1_CLISES_IV);
00145
00146 nop_err_if(cli_ebuf == NULL || cli_mtime == NULL || cli_hmac == NULL);
00147
00148
00149
00150 dbg_err_if(session_client_hmac(&so->hmac_ctx, hmac, HMAC_HEX_SIZE,
00151 cli_ebuf, ss->id, cli_mtime, ss->so->encrypt ? cli_iv : NULL));
00152
00153
00154 if(strcmp(hmac, cli_hmac))
00155 {
00156 session_remove(ss);
00157 warn_err("HMAC don't match, rejecting session data");
00158 }
00159
00160
00161
00162
00163 dbg_err_if((c = u_hexncpy(ss->so->cipher_iv, cli_iv, strlen(cli_iv),
00164 HEXCPY_DECODE)) <= 0);
00165
00166
00167 ss->mtime = strtoul(cli_mtime, NULL, 0);
00168
00169 dbg_err_if(strlen(cli_ebuf) > COOKIE_MAX_SIZE);
00170
00171
00172 dbg_err_if((c = u_hexncpy(buf, cli_ebuf, strlen(cli_ebuf),
00173 HEXCPY_DECODE)) <= 0);
00174
00175 c--;
00176
00177
00178 dbg_err_if(session_prv_load_from_buf(ss, buf, c));
00179
00180 return 0;
00181 err:
00182 return ~0;
00183 }
00184
00185 static int session_client_term(session_t *ss)
00186 {
00187 u_unused_args(ss);
00188 return 0;
00189 }
00190
00191 static int session_client_remove(session_t *ss)
00192 {
00193 dbg_err_if (ss == NULL);
00194
00195
00196 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_DATA, NULL, 0, NULL,
00197 NULL, 0));
00198 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_MTIME, NULL, 0, NULL,
00199 NULL, 0));
00200 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_HMAC, NULL, 0, NULL,
00201 NULL, 0));
00202 dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_IV, NULL, 0, NULL,
00203 NULL, 0));
00204
00205 return 0;
00206 err:
00207 return ~0;
00208 }
00209
00210 int session_client_create(session_opt_t *so, request_t *rq, response_t *rs,
00211 session_t **pss)
00212 {
00213 session_t *ss = NULL;
00214
00215 dbg_err_if (rq == NULL);
00216 dbg_err_if (rs == NULL);
00217 dbg_err_if (pss == NULL);
00218 dbg_err_if (so == NULL);
00219
00220 ss = u_zalloc(sizeof(session_t));
00221 dbg_err_if(ss == NULL);
00222
00223 ss->load = session_client_load;
00224 ss->save = session_client_save;
00225 ss->remove = session_client_remove;
00226 ss->term = session_client_term;
00227 ss->mtime = time(0);
00228 ss->so = so;
00229
00230 dbg_err_if(session_prv_init(ss, rq, rs));
00231
00232 *pss = ss;
00233
00234 return 0;
00235 err:
00236 if(ss)
00237 session_free(ss);
00238 return ~0;
00239 }
00240
00241
00242 int session_client_module_init(u_config_t *config, session_opt_t *so)
00243 {
00244 u_config_t *c;
00245 const char *v;
00246
00247
00248 dbg_err_if (so == NULL);
00249
00250
00251 so->hash = EVP_sha1();
00252
00253
00254 if(!so->encrypt)
00255 warn("encryption is required for client side session");
00256 so->encrypt = 1;
00257
00258 if(config && !u_config_get_subkey(config, "client", &c))
00259 {
00260 if((v = u_config_get_subkey_value(c, "hash_function")) != NULL)
00261 {
00262 if(!strcasecmp(v, "md5"))
00263 so->hash = EVP_md5();
00264 else if(!strcasecmp(v, "sha1"))
00265 so->hash = EVP_sha1();
00266 else if(!strcasecmp(v, "ripemd160"))
00267 so->hash = EVP_ripemd160();
00268 else
00269 warn_err("config error: bad hash_function");
00270 }
00271 }
00272
00273
00274 HMAC_CTX_init(&so->hmac_ctx);
00275
00276
00277 dbg_err_if(!RAND_bytes(so->hmac_key, HMAC_KEY_SIZE));
00278
00279
00280 HMAC_Init_ex(&so->hmac_ctx, so->hmac_key, HMAC_KEY_SIZE, so->hash, NULL);
00281
00282 return 0;
00283 err:
00284 return ~0;
00285 }
00286
00287