Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

ses_client.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: ses_client.c,v 1.29 2006/03/21 15:38:01 tat Exp $
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 /* calc the hmac of data+sid+mtime */
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     /* hex_iv may be NULL */
00051     
00052     /* hmac must be at least 'EVP_MAX_MD_SIZE*2 + 1' (it will be hex encoded) */
00053     dbg_err_if(hmac_sz < EVP_MAX_MD_SIZE*2 + 1);
00054 
00055     /* calc HMAC hash of the data buf + mtime (reuse stored key and md) */
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     /* encode the hash */
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     /* BUF_SIZE: cookie size + MAC size + gzip header + encryption padding  */
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         /* generate a new IV for each session */
00090         dbg_err_if(!RAND_pseudo_bytes(ss->so->cipher_iv, CIPHER_IV_SIZE));
00091 
00092         /* hex encode the IV and save it in a cookie */
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     /* save the session data to freshly alloc'd buf of size sz */
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     /* hex-encode the buffer */
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     /* set mtime cookie */
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     /* calc the HMAC */
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     /* store the hash in a cookie */
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     /* extract session data, mtime and hmac from cookies */
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     /* cli_iv may be NULL */
00148 
00149     /* calc the HMAC */
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     /* compare HMACs */
00154     if(strcmp(hmac, cli_hmac))
00155     {
00156         session_remove(ss); /* remove all bad stale data */
00157         warn_err("HMAC don't match, rejecting session data");
00158     }
00159 
00160     /* hash ckeched. decode/uncompress/decrypt session data */
00161 
00162     /* hex decode and save current cipher IV */
00163     dbg_err_if((c = u_hexncpy(ss->so->cipher_iv, cli_iv, strlen(cli_iv), 
00164         HEXCPY_DECODE)) <= 0);
00165 
00166     /* set client provided mtime */
00167     ss->mtime = strtoul(cli_mtime, NULL, 0);
00168 
00169     dbg_err_if(strlen(cli_ebuf) > COOKIE_MAX_SIZE);
00170 
00171     /* hex decode session data */
00172     dbg_err_if((c = u_hexncpy(buf, cli_ebuf, strlen(cli_ebuf), 
00173         HEXCPY_DECODE)) <= 0);
00174 
00175     c--; /* ignore last '\0' that hexncpy adds */
00176 
00177     /* load session data from the buffer */
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     /* removes all clises-related cookies */
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 /* this function will be called once by the server during startup */
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     /* config may be NULL */
00248     dbg_err_if (so == NULL);
00249     
00250     /* default */
00251     so->hash = EVP_sha1(); 
00252 
00253     /* always enable encryption for client-side sessions */
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     /* initialize OpenSSL HMAC stuff */
00274     HMAC_CTX_init(&so->hmac_ctx);
00275 
00276     /* gen HMAC key */
00277     dbg_err_if(!RAND_bytes(so->hmac_key, HMAC_KEY_SIZE));
00278 
00279     /* init HMAC with our key and chose hash algorithm */
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 

←Products
© 2005-2006 - KoanLogic S.r.l. - All rights reserved