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

tls.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: tls.c,v 1.10 2006/03/21 19:15:38 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <sys/types.h>
00013 #include <sys/time.h>
00014 #include <unistd.h>
00015 #include <strings.h>
00016 #ifdef HAVE_LIBOPENSSL
00017 #include <openssl/ssl.h>
00018 #include <openssl/rand.h>
00019 #include <openssl/err.h>
00020 #include <u/libu.h>
00021 #include <klone/tls.h>
00022 #include <klone/utils.h>
00023 #include <klone/tlsprv.h>
00024 
00025 static int tls_sid = 1;
00026 static int tls_inited = 0; 
00027 
00028 /* private methods */
00029 static int  tls_init (void);
00030 static int  tls_context (SSL_CTX **);
00031 static int  tls_load_creds (SSL_CTX *, tls_ctx_args_t *);
00032 static int  tls_gendh_params (SSL_CTX *, const char *);
00033 static int  tls_gen_eph_rsa (SSL_CTX *);
00034 static void tls_rand_seed (void);
00035 static int  tls_sid_context (SSL_CTX *, int *);
00036 static DH   *tls_load_dh_param (const char *);
00037 static int  tls_no_passphrase_cb (char *, int, int, void *);
00038 static int  tls_init_ctx_args (tls_ctx_args_t *);
00039 static int  tls_set_ctx_vdepth (u_config_t *, tls_ctx_args_t *);
00040 static int  tls_set_ctx_vmode (u_config_t *, tls_ctx_args_t *);
00041 static int  tls_check_ctx (tls_ctx_args_t *);
00042 static void tls_free_ctx_args (tls_ctx_args_t *cargs);
00043 
00044 
00045 /* load SSL_CTX args from configuration */
00046 int tls_load_ctx_args (u_config_t *cfg, tls_ctx_args_t **cargs)
00047 {
00048     dbg_return_if (!cfg || !cargs, ~0);
00049 
00050     dbg_err_if (!(*cargs = u_zalloc(sizeof(tls_ctx_args_t))));
00051 
00052     (void) tls_init_ctx_args(*cargs);
00053 
00054     (*cargs)->cert = u_config_get_subkey_value(cfg, "cert_file");
00055     (*cargs)->key = u_config_get_subkey_value(cfg, "key_file");
00056     (*cargs)->certchain = u_config_get_subkey_value(cfg, "certchain_file");
00057     (*cargs)->ca = u_config_get_subkey_value(cfg, "ca_file");
00058     (*cargs)->dh = u_config_get_subkey_value(cfg, "dh_file");
00059     dbg_err_if (tls_set_ctx_vdepth(cfg, *cargs));
00060     dbg_err_if (tls_set_ctx_vmode(cfg, *cargs));
00061 
00062     /* check cargs consistency against the supplied values */
00063     dbg_err_if (tls_check_ctx(*cargs));
00064 
00065     return 0;
00066 
00067 err:
00068     if (*cargs)
00069         tls_free_ctx_args(*cargs);
00070     return ~0;
00071 }
00072 
00073 
00074 /* initialize 'parent' SSL context */
00075 SSL_CTX *tls_init_ctx (tls_ctx_args_t *cargs)
00076 {
00077     SSL_CTX *c = NULL;
00078 
00079     dbg_return_if (!cargs, NULL);
00080 
00081     /* global init */
00082     dbg_err_if (tls_init());
00083 
00084     /* create ctx */
00085     dbg_err_if (tls_context(&c));
00086 
00087     /* don't ask for unlocking passphrases */
00088     SSL_CTX_set_default_passwd_cb(c, tls_no_passphrase_cb);
00089     
00090     /* set key and certs against the SSL context */
00091     dbg_err_if (tls_load_creds(c, cargs));
00092  
00093     /* (possibly) generate DH parameters and load into SSL_CTX */
00094     dbg_err_if (tls_gen_eph_rsa(c));
00095 
00096     /* generate RSA ephemeral parameters and load into SSL_CTX */
00097     dbg_err_if (tls_gendh_params(c, cargs->dh));
00098 
00099     /* set the session id context */
00100     dbg_err_if (tls_sid_context(c, &tls_sid));
00101     
00102     return c;
00103 
00104 err:
00105     if (c)
00106         SSL_CTX_free(c);
00107     return NULL;
00108 }
00109 
00110 static int tls_sid_context (SSL_CTX *c, int *sid)
00111 {
00112     if (!c || !sid)
00113         return ~0;
00114     
00115     /* every time tls_init_ctx() is called, move on the session id context */
00116     (*sid)++;
00117     dbg_err_if (!SSL_CTX_set_session_id_context(c, (void *) sid, sizeof(*sid)));
00118     
00119     return 0;
00120 
00121 err:
00122     dbg("%s", tls_get_error()); 
00123 
00124     return ~0;
00125 }
00126 
00127 static int tls_context (SSL_CTX **c)
00128 {
00129     if (!c)
00130         return ~0;
00131 
00132     dbg_err_if (!(*c = SSL_CTX_new(SSLv23_server_method())));
00133 
00134     return 0;
00135 
00136 err:
00137     dbg("%s", tls_get_error()); 
00138     *c = NULL;  /* reset pointer (useless) */
00139 
00140     return ~0;
00141 }
00142 
00143 /* XXX very primitive */
00144 char *tls_get_error (void)
00145 {
00146     unsigned long   e;
00147 
00148     e = ERR_get_error();
00149     return ERR_error_string(e, NULL);
00150 }
00151 
00152 void tls_dbg_openssl_err (void)
00153 {
00154     enum { BUFSZ = 256 };
00155     unsigned long e;
00156     char buf[BUFSZ];
00157 
00158     while ((e = ERR_get_error()))
00159         warn("%s", ERR_error_string(e, buf));
00160 
00161     return;
00162 }
00163 
00164 /* if skey is NULL, assume private key in scert, ca can be NULL */
00165 static int tls_load_creds (SSL_CTX *c, tls_ctx_args_t *cargs)
00166 {
00167     dbg_return_if (!c || !cargs || !cargs->cert, ~0);
00168 
00169     if (!cargs->key)
00170         cargs->key = cargs->cert;
00171     
00172     /* set ca if supplied */
00173     if (cargs->ca)
00174         dbg_err_if (tls_load_verify_locations(c, cargs->ca));
00175 
00176     /* explicitly set the list of CAs for which we accept certificates */
00177     if (cargs->ca && cargs->vmode != SSL_VERIFY_NONE)
00178         SSL_CTX_set_client_CA_list(c, tls_load_client_CA_file(cargs->ca));
00179 
00180     /* load server certificate */
00181     dbg_err_if (tls_use_certificate_file(c, cargs->cert, 
00182                                          SSL_FILETYPE_PEM) <= 0);
00183 
00184     /* load private key (perhaps from the cert file) */
00185     dbg_err_if (tls_use_PrivateKey_file(c, cargs->key, SSL_FILETYPE_PEM) <= 0);
00186 
00187     /* check skey consistency against scert */
00188     dbg_err_if (!SSL_CTX_check_private_key(c));
00189 
00190     /* load optional server certficate chain */
00191     if (cargs->certchain)
00192         dbg_err_if (tls_use_certificate_chain(c, cargs->certchain, 
00193                                               0, NULL) < 0);
00194 
00195     /* set SSL verify mode (no, optional, required) and depth */
00196     SSL_CTX_set_verify(c, cargs->vmode, NULL);
00197     if (cargs->depth > 0)
00198         SSL_CTX_set_verify_depth(c, cargs->depth);
00199 
00200     return 0;
00201 
00202 err:
00203     dbg("%s", tls_get_error()); 
00204     return ~0;
00205 }
00206 
00207 static int tls_init (void)
00208 {
00209     if (tls_inited)
00210         return 0;
00211 
00212     SSL_load_error_strings();
00213     dbg_err_if (!SSL_library_init());
00214     tls_rand_seed(); 
00215     tls_inited++;
00216 
00217     return 0;
00218 
00219 err:
00220     dbg("%s", tls_get_error()); 
00221     return ~0;
00222 }
00223 
00224 static void tls_rand_seed (void)
00225 {
00226     struct timeval  tv;
00227     tls_rand_seed_t seed;
00228 
00229     (void) gettimeofday(&tv, NULL);
00230     
00231     seed.pid = getpid();
00232     seed.t1 = tv.tv_sec; 
00233     seed.t2 = tv.tv_usec;
00234     seed.stack = (void *) &seed;
00235 
00236     RAND_seed((const void *) &seed, sizeof seed);
00237 }
00238 
00239 /* generate RSA ephemeral parameters and load'em into SSL_CTX */
00240 static int tls_gen_eph_rsa(SSL_CTX *c)
00241 {
00242     RSA *eph_rsa = NULL;
00243 
00244     dbg_return_if (!c, ~0);
00245 
00246     dbg_err_if (!(eph_rsa = RSA_generate_key(512, RSA_F4, 0, NULL)));
00247     dbg_err_if (!SSL_CTX_set_tmp_rsa(c, eph_rsa));
00248     RSA_free(eph_rsa); /* eph_rsa is dup'ed by SSL_CTX_set_tmp_rsa() */
00249 
00250     return 0;
00251 
00252 err:
00253     dbg("%s", tls_get_error());
00254     if (eph_rsa)
00255         RSA_free(eph_rsa);    
00256 
00257     return ~0;
00258 }
00259 
00260 /* generate DH ephemeral parameters and load'em into SSL_CTX */
00261 static int tls_gendh_params(SSL_CTX *c, const char *dhfile)
00262 {
00263     DH  *eph_dh = NULL;
00264 
00265     dbg_return_if (!c, ~0);
00266 
00267     eph_dh = dhfile ? tls_load_dh_param(dhfile) : get_dh1024(); 
00268     dbg_err_if (!(eph_dh));
00269 
00270     dbg_err_if (!SSL_CTX_set_tmp_dh(c, eph_dh));
00271 
00272 #if 0
00273     /* Avoid small subgroup attacks (if p and g are strong primes
00274      * this is not strictly necessary).  This is said to have a negligible (?)
00275      * impact during negotiation phase. TODO: test it ! */
00276     (void) SSL_CTX_set_options(c, SSL_OP_SINGLE_DH_USE); */
00277 #endif /* 0 */
00278 
00279     return 0;
00280 
00281 err:
00282     dbg("%s", tls_get_error());
00283     if (eph_dh)
00284         DH_free(eph_dh);
00285 
00286     return ~0;
00287 }
00288 
00289 static DH *tls_load_dh_param (const char *res_name)
00290 {
00291     DH  *dh = NULL;
00292     BIO *bio = NULL;
00293 
00294     dbg_return_if (!res_name, NULL);
00295 
00296     /* XXX say return_if here instead of err_if because bio_from_emb()
00297      * could have failed for a non-openssl error */
00298     dbg_return_if (!(bio = tls_get_file_bio(res_name)), NULL);
00299 
00300     dbg_err_if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)));
00301 
00302     BIO_free(bio);
00303     return dh;
00304 
00305 err:
00306     dbg("%s", tls_get_error());
00307     if (bio) 
00308         BIO_free(bio);
00309 
00310     return NULL;
00311 }
00312 
00313 static int tls_no_passphrase_cb (char *buf, int num, int w, void *arg)
00314 {
00315     /* avoid gcc complains */
00316     buf = NULL;
00317     arg = NULL;
00318     num = w = 0;
00319 
00320     return -1;
00321 }
00322 
00323 static int tls_init_ctx_args (tls_ctx_args_t *cargs)
00324 {
00325     dbg_return_if (!cargs, ~0);
00326 
00327     cargs->cert  = NULL;
00328     cargs->key   = NULL;
00329     cargs->ca    = NULL;
00330     cargs->dh    = NULL;
00331     cargs->depth = 1;
00332     cargs->vmode = SSL_VERIFY_NONE;
00333 
00334     return 0;
00335 }
00336 
00337 static int tls_set_ctx_vdepth (u_config_t *cfg, tls_ctx_args_t *cargs)
00338 {
00339     u_config_t    *k;
00340     
00341     dbg_return_if (!cfg || !cargs, ~0);
00342 
00343     if (!u_config_get_subkey(cfg, "verify_depth", &k))
00344     {
00345         cargs->depth = atoi(u_config_get_value(k));    
00346     } 
00347 
00348     /* XXX check consistent values for the verification chain's depth ? */
00349 
00350     return 0;
00351 }
00352 
00353 static int tls_set_ctx_vmode (u_config_t *cfg, tls_ctx_args_t *cargs)
00354 {
00355     const char  *v;
00356     
00357     dbg_return_if (!cfg || !cargs, ~0);
00358     
00359     if (!(v = u_config_get_subkey_value(cfg, "verify_mode")))
00360         return 0;    /* will use the default (none) */ 
00361 
00362     if (!strcasecmp(v, "no"))
00363         cargs->vmode = SSL_VERIFY_NONE;
00364     else if (!strcasecmp(v, "optional"))
00365         cargs->vmode = SSL_VERIFY_PEER;
00366     else if (!strcasecmp(v, "require"))
00367         cargs->vmode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
00368     else {
00369         dbg("unknown verification value:\'%s\'", v);
00370         return ~0;
00371     }
00372     
00373     return 0;
00374 }
00375 
00376 static int tls_check_ctx (tls_ctx_args_t *cargs)
00377 {
00378     dbg_return_if (!cargs, ~0);
00379 
00380     /* cert_file is a MUST */
00381     dbg_err_if (!cargs->cert || strlen(cargs->cert) == 0);
00382 
00383     /* if private key file is missing, assume the key is inside cert_file */
00384     dbg_if (!cargs->key);
00385 
00386     /* if verify_mode == "required" the CA file MUST be present */
00387     if (cargs->vmode & SSL_VERIFY_PEER)
00388         dbg_err_if (!cargs->ca);
00389 
00390     return 0;
00391 err:
00392     return ~0;
00393 }
00394 
00395 
00396 static void tls_free_ctx_args (tls_ctx_args_t *cargs)
00397 {
00398     KLONE_FREE(cargs);
00399     return;
00400 }
00401 #endif /* HAVE_LIBOPENSSL */

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