ses_client.c

00001 /*
00002  * Copyright (c) 2005-2012 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 
00009 #include "klone_conf.h"
00010 #include <sys/types.h>
00011 #include <sys/stat.h>
00012 #include <stdlib.h>
00013 #include <time.h>
00014 #include <unistd.h>
00015 #include <fcntl.h>
00016 #include <u/libu.h>
00017 #include <klone/session.h>
00018 #include <klone/request.h>
00019 #include <klone/response.h>
00020 #include <klone/vars.h>
00021 #include <klone/utils.h>
00022 #include <klone/emb.h>
00023 #include <klone/ses_prv.h>
00024 #include <klone/codecs.h>
00025 #ifdef SSL_ON
00026 #include <openssl/hmac.h>
00027 #include <openssl/rand.h>
00028 #endif
00029 #ifdef SSL_CYASSL
00030 #include <config.h>
00031 #include <types.h>
00032 #include <ctc_hmac.h>
00033 #include <ctc_aes.h>
00034 #define EVP_MAX_MD_SIZE 64
00035 #endif
00036 
00037 #define KL1_CLISES_DATA     "KL1_CLISES_DATA"
00038 #define KL1_CLISES_MTIME    "KL1_CLISES_MTIME"
00039 #define KL1_CLISES_HMAC     "KL1_CLISES_HMAC"
00040 #define KL1_CLISES_IV       "KL1_CLISES_IV"
00041 
00042 enum { HMAC_HEX_SIZE = 2*EVP_MAX_MD_SIZE + 1 };
00043 
00044 /* calc the hmac of data+sid+mtime */
00045 static int session_client_hmac(HMAC_CTX *ctx, char *hmac, size_t hmac_sz, 
00046     const char *data, const char *sid, const char *mtime, const char *hex_iv)
00047 {
00048     char mac[EVP_MAX_MD_SIZE];
00049     int mac_len;
00050 
00051     dbg_err_if (ctx == NULL);
00052     dbg_err_if (hmac == NULL);
00053     dbg_err_if (data == NULL);
00054     dbg_err_if (sid == NULL);
00055     dbg_err_if (mtime == NULL);
00056     /* hex_iv may be NULL */
00057     
00058 #if SSL_OPENSSL
00059     /* hmac must be at least 'EVP_MAX_MD_SIZE*2 + 1' (it will be hex encoded) */
00060     dbg_err_if(hmac_sz < EVP_MAX_MD_SIZE*2 + 1);
00061 
00062     /* calc HMAC hash of the data buf + mtime (reuse stored key and md) */
00063     HMAC_Init_ex(ctx, NULL, 0, NULL, NULL); 
00064     HMAC_Update(ctx, data, strlen(data));
00065     HMAC_Update(ctx, sid, strlen(sid));
00066     HMAC_Update(ctx, mtime, strlen(mtime));
00067     if(hex_iv)
00068         HMAC_Update(ctx, hex_iv, strlen(hex_iv));
00069     HMAC_Final(ctx, mac, &mac_len);
00070 #endif
00071 
00072 #ifdef SSL_CYASSL
00073     HmacUpdate(ctx, (const byte*)data, strlen(data));
00074     HmacUpdate(ctx, (const byte*)sid, strlen(sid));
00075     HmacUpdate(ctx, (const byte*)mtime, strlen(mtime));
00076     if(hex_iv)
00077         HmacUpdate(ctx, (const byte*)hex_iv, strlen(hex_iv));
00078     HmacFinal(ctx, (byte*)mac);
00079 
00080     if (ctx->macType == MD5)
00081         mac_len = MD5_DIGEST_SIZE;
00082     else if (ctx->macType == SHA)
00083         mac_len = SHA_DIGEST_SIZE;
00084     else
00085         crit_err("unknown hash");
00086 #endif
00087 
00088     /* encode the hash */
00089     dbg_err_if(u_hexncpy(hmac, mac, mac_len, HEXCPY_ENCODE) <= 0);
00090 
00091     return 0;
00092 err:
00093     return -1;
00094 }
00095 
00096 static int session_client_save(session_t *ss)
00097 {
00098     /* BUF_SIZE: cookie size + MAC size + gzip header + encryption padding  */
00099     enum { 
00100         MTIME_SIZE = 32, 
00101         BUF_SIZE = COOKIE_MAX_SIZE + EVP_MAX_BLOCK_LENGTH + 96 
00102     };
00103     session_opt_t *so = ss->so;
00104     char hmac[HMAC_HEX_SIZE], ebuf[BUF_SIZE], mtime[MTIME_SIZE];
00105     char *buf = NULL, cipher_iv_hex[CIPHER_IV_LEN * 2 + 1];
00106     size_t sz;
00107     time_t now;
00108 
00109     dbg_err_if (ss == NULL);
00110 
00111     #ifdef SSL_ON
00112     if(ss->so->encrypt)
00113     {
00114         /* generate a new IV for each session */
00115         #ifdef SSL_OPENSSL
00116         dbg_err_if(!RAND_pseudo_bytes(ss->so->cipher_iv, CIPHER_IV_LEN));
00117         #endif
00118 
00119         #ifdef SSL_CYASSL
00120         dbg_err_if(!RAND_bytes(ss->so->cipher_iv, CIPHER_IV_LEN));
00121         #endif
00122 
00123         /* hex encode the IV and save it in a cookie */
00124         dbg_err_if(u_hexncpy(cipher_iv_hex, ss->so->cipher_iv, CIPHER_IV_LEN,
00125             HEXCPY_ENCODE) <= 0);
00126         dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_IV, cipher_iv_hex, 
00127             0, NULL, NULL, 0));
00128     }
00129     #endif
00130 
00131     /* save the session data to freshly alloc'd buf of size sz */
00132     dbg_err_if(session_prv_save_to_buf(ss, &buf, &sz));
00133 
00134     warn_err_ifm(sz > COOKIE_MAX_SIZE, 
00135                 "session data too big for client-side sessions");
00136 
00137     /* hex-encode the buffer */
00138     dbg_err_if(u_hexncpy(ebuf, buf, sz, HEXCPY_ENCODE) <= 0);
00139 
00140     dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_DATA, ebuf, 0, NULL, 
00141         NULL, 0));
00142 
00143     /* set mtime cookie */
00144     dbg_err_sif ((now = time(NULL)) == (time_t) -1);
00145     dbg_err_if (u_snprintf(mtime, sizeof mtime, "%d", (ss->mtime = (int) now)));
00146     dbg_err_if (response_set_cookie(ss->rs, KL1_CLISES_MTIME, mtime, 0, NULL,
00147                 NULL, 0));
00148 
00149     /* calc the HMAC */
00150     dbg_err_if(session_client_hmac(&so->hmac_ctx, hmac, HMAC_HEX_SIZE, 
00151         ebuf, ss->id, mtime, ss->so->encrypt ? cipher_iv_hex : NULL));
00152 
00153     /* store the hash in a cookie */
00154     dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_HMAC, hmac, 0, NULL, 
00155         NULL, 0));
00156 
00157     u_free(buf);
00158 
00159     return 0;
00160 err:
00161     if(buf)
00162         u_free(buf);
00163     return ~0;
00164 }
00165 
00166 static int session_client_load(session_t *ss)
00167 {
00168     session_opt_t *so = ss->so;
00169     char hmac[HMAC_HEX_SIZE], buf[COOKIE_MAX_SIZE];
00170     const char *cli_ebuf, *cli_hmac, *cli_mtime, *cli_iv;
00171     ssize_t c;
00172 
00173     dbg_err_if (ss == NULL);
00174 
00175     /* extract session data, mtime and hmac from cookies */
00176     cli_ebuf = request_get_cookie(ss->rq, KL1_CLISES_DATA);
00177     cli_mtime = request_get_cookie(ss->rq, KL1_CLISES_MTIME);
00178     cli_hmac = request_get_cookie(ss->rq, KL1_CLISES_HMAC);
00179     cli_iv = request_get_cookie(ss->rq, KL1_CLISES_IV);
00180 
00181     //nop_err_if(cli_ebuf == NULL || cli_mtime == NULL || cli_hmac == NULL);
00182     dbg_err_if(cli_ebuf == NULL || cli_mtime == NULL || cli_hmac == NULL);
00183     /* cli_iv may be NULL */
00184 
00185     /* calc the HMAC */
00186     dbg_err_if(session_client_hmac(&so->hmac_ctx, hmac, HMAC_HEX_SIZE, 
00187         cli_ebuf, ss->id, cli_mtime, ss->so->encrypt ? cli_iv : NULL));
00188 
00189     /* compare HMACs */
00190     if(strcmp(hmac, cli_hmac))
00191     {
00192         session_remove(ss); /* remove all bad stale data */
00193         warn_err("HMAC don't match, rejecting session data");
00194     }
00195 
00196     /* hash ckeched. decode/uncompress/decrypt session data */
00197 
00198     /* hex decode and save current cipher IV */
00199     dbg_err_if((c = u_hexncpy(ss->so->cipher_iv, cli_iv, strlen(cli_iv), 
00200         HEXCPY_DECODE)) <= 0);
00201 
00202     /* set client provided mtime */
00203     ss->mtime = strtoul(cli_mtime, NULL, 0);
00204 
00205     dbg_err_if(strlen(cli_ebuf) > COOKIE_MAX_SIZE);
00206 
00207     /* hex decode session data */
00208     dbg_err_if((c = u_hexncpy(buf, cli_ebuf, strlen(cli_ebuf), 
00209         HEXCPY_DECODE)) <= 0);
00210 
00211     /* load session data from the buffer */
00212     dbg_err_if(session_prv_load_from_buf(ss, buf, c));
00213 
00214     return 0;
00215 err:
00216     return ~0;
00217 }
00218 
00219 static int session_client_term(session_t *ss)
00220 {
00221     u_unused_args(ss);
00222     return 0;
00223 }
00224 
00225 static int session_client_remove(session_t *ss)
00226 {
00227     dbg_err_if (ss == NULL);
00228     
00229     /* removes all clises-related cookies */
00230     dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_DATA, NULL, 0, NULL, 
00231         NULL, 0));
00232     dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_MTIME, NULL, 0, NULL, 
00233         NULL, 0));
00234     dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_HMAC, NULL, 0, NULL, 
00235         NULL, 0));
00236     dbg_err_if(response_set_cookie(ss->rs, KL1_CLISES_IV, NULL, 0, NULL, 
00237         NULL, 0));
00238 
00239     return 0;
00240 err:
00241     return ~0;
00242 }
00243 
00244 int session_client_create(session_opt_t *so, request_t *rq, response_t *rs, 
00245         session_t **pss)
00246 {
00247     session_t *ss = NULL;
00248 
00249     dbg_err_if (rq == NULL);
00250     dbg_err_if (rs == NULL);
00251     dbg_err_if (pss == NULL);
00252     dbg_err_if (so == NULL);
00253 
00254     ss = u_zalloc(sizeof(session_t));
00255     dbg_err_if(ss == NULL);
00256 
00257     ss->load = session_client_load;
00258     ss->save = session_client_save;
00259     ss->remove = session_client_remove;
00260     ss->term = session_client_term;
00261     ss->mtime = time(0);
00262     ss->so = so;
00263 
00264     dbg_err_if(session_prv_init(ss, rq, rs));
00265 
00266     *pss = ss;
00267 
00268     return 0;
00269 err:
00270     if(ss)
00271         session_free(ss);
00272     return ~0;
00273 }
00274 
00275 /* this function will be called once by the server during startup */
00276 int session_client_module_init(u_config_t *config, session_opt_t *so)
00277 {
00278     u_config_t *c;
00279     const char *v;
00280 
00281     /* config may be NULL */
00282     dbg_err_if (so == NULL);
00283     
00284     /* default */
00285     so->hash = EVP_sha1(); 
00286 
00287     /* always enable encryption for client-side sessions */
00288     if(!so->encrypt)
00289         warn("encryption is required for client side session");
00290     so->encrypt = 1;
00291 
00292     if(config && !u_config_get_subkey(config, "client", &c))
00293     {
00294         if((v = u_config_get_subkey_value(c, "hash_function")) != NULL)
00295         {
00296             if(!strcasecmp(v, "md5"))
00297                 so->hash = EVP_md5();
00298             else if(!strcasecmp(v, "sha1"))
00299                 so->hash = EVP_sha1();
00300 #ifdef SSL_OPENSSL
00301             else if(!strcasecmp(v, "ripemd160"))
00302                 so->hash = EVP_ripemd160();
00303 #endif
00304             else
00305                 warn_err("config error: bad hash_function");
00306         } 
00307     }
00308 
00309 #ifdef SSL_OPENSSL
00310     /* initialize OpenSSL HMAC stuff */
00311     HMAC_CTX_init(&so->hmac_ctx);
00312 
00313     /* gen HMAC key */
00314     dbg_err_if(!RAND_bytes(so->hmac_key, HMAC_KEY_LEN));
00315 
00316     /* init HMAC with our key and chose hash algorithm */
00317     HMAC_Init_ex(&so->hmac_ctx, so->hmac_key, HMAC_KEY_LEN, so->hash, NULL);
00318 #endif
00319 
00320 #ifdef SSL_CYASSL
00321     /* gen HMAC key */
00322     dbg_err_if(!RAND_bytes(so->hmac_key, HMAC_KEY_LEN));
00323 
00324     if(strcmp(so->hash, "MD5") == 0)
00325         HmacSetKey(&so->hmac_ctx, MD5, so->hmac_key, HMAC_KEY_LEN);
00326     else if(strcmp(so->hash, "SHA") == 0)
00327         HmacSetKey(&so->hmac_ctx, SHA, so->hmac_key, HMAC_KEY_LEN);
00328 #endif
00329 
00330     return 0;
00331 err:
00332     return ~0;
00333 }
00334 
00335 

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