diff options
Diffstat (limited to 'lib/ssl/c_src/esock_openssl.c')
-rw-r--r-- | lib/ssl/c_src/esock_openssl.c | 1213 |
1 files changed, 1213 insertions, 0 deletions
diff --git a/lib/ssl/c_src/esock_openssl.c b/lib/ssl/c_src/esock_openssl.c new file mode 100644 index 0000000000..2621c9934e --- /dev/null +++ b/lib/ssl/c_src/esock_openssl.c @@ -0,0 +1,1213 @@ +/*<copyright> + * <year>1999-2008</year> + * <holder>Ericsson AB, All Rights Reserved</holder> + *</copyright> + *<legalnotice> + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson AB. + *</legalnotice> + */ +/* + * Purpose: Adaptions for the OpenSSL package. + * + * This file implements the functions defined in esock_ssl.h for + * the OpenSSL package. + * + * The following holds true for non-blockling I/O: + * + * Function Return values + * -------- ------------- + * SSL_accept() success: 1, failure: =<0 + * SSL_connect() success: 1, failure: =<0 + * SSL_read() success: >0, eof: 0, failure: <0 + * SSL_write() success: > 0, failure: =<0 + * SSL_shutdown() success: 1, not finished: 0 + * + * If the return value of any of the above functions is `ret' and the + * ssl connection is `ssl', the call + * + * ssl_error = SSL_get_error(ssl, ret); + * + * returns one of the following eight values: + * + * SSL_ERROR_NONE ret > 0 + * SSL_ERROR_ZERO_RETURN ret = 0 + * SSL_ERROR_WANT_READ ret < 0 and ssl wants to read + * SSL_ERROR_WANT_WRITE ret < 0 and ssl wants to write + * SSL_ERROR_SYSCALL ret < 0 or ret = 0 + * SSL_ERROR_SSL if there was an ssl internal error + * SSL_ERROR_WANT_X509_LOOKUP ret < 0 and ssl wants x509 lookup + * SSL_ERROR_WANT_CONNECT ret < 0 and ssl wants connect + * + * It is the case that SSL_read() sometimes returns -1, even when the + * underlying file descriptor is ready for reading. + * + * Also, sometimes we may have SSL_ERROR_SSL in SSL_accept() and SSL_connect() + * when a retry should be done. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifndef __WIN32__ +# include <fcntl.h> +# include <unistd.h> +#endif + +#include "esock.h" +#include "esock_ssl.h" +#include "debuglog.h" +#include "esock_utils.h" +#include "esock_posix_str.h" + +#include <openssl/crypto.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +int ephemeral_rsa = 0; +int ephemeral_dh = 0; /* XXX Not used yet */ +int protocol_version = 0; + +char *esock_ssl_errstr = ""; + +#define FLAGSBUFSIZE 512 +#define X509BUFSIZE 256 +#define DEFAULT_VERIFY_DEPTH 1 + +#define SET_WANT(cp, ssl_error) \ + switch((ssl_error)) { \ + case SSL_ERROR_WANT_READ: \ + (cp)->ssl_want = ESOCK_SSL_WANT_READ; \ + break; \ + case SSL_ERROR_WANT_WRITE: \ + (cp)->ssl_want = ESOCK_SSL_WANT_WRITE; \ + break; \ + default: \ + (cp)->ssl_want = 0; \ + break; \ + } + +#define RESET_ERRSTR() \ + esock_ssl_errstr = ""; + +#define MAYBE_SET_ERRSTR(s) \ + if (!esock_ssl_errstr[0]) \ + esock_ssl_errstr = (s); + +typedef struct { + int code; + char *text; +} err_entry; + +typedef struct { + SSL_CTX *ctx; + char *passwd; + int verify_depth; +} callback_data; + +static char *ssl_error_str(int error); +static void end_ssl_call(int ret, Connection *cp, int ssl_error); +static void check_shutdown(Connection *cp); +static int set_ssl_parameters(Connection *cp, SSL_CTX *ctx); +static int verify_callback(int ok, X509_STORE_CTX *ctx); +static int passwd_callback(char *buf, int num, int rwflag, void *userdata); +static void info_callback(const SSL *ssl, int where, int ret); +static void callback_data_free(void *parent, void *ptr, + CRYPTO_EX_DATA *ad, + int idx, long arg1, void *argp); +static RSA *tmp_rsa_callback(SSL *ssl, int is_export, int keylen); +static void restrict_protocols(SSL_CTX *ctx); + +static err_entry errs[] = { + {SSL_ERROR_NONE, "SSL_ERROR_NONE"}, + {SSL_ERROR_ZERO_RETURN, "SSL_ERROR_ZERO_RETURN"}, + {SSL_ERROR_WANT_READ, "SSL_ERROR_WANT_READ"}, + {SSL_ERROR_WANT_WRITE, "SSL_ERROR_WANT_WRITE"}, + {SSL_ERROR_SYSCALL, "SSL_ERROR_SYSCALL"}, + {SSL_ERROR_SSL, "SSL_ERROR_SSL"}, + {SSL_ERROR_WANT_X509_LOOKUP, "SSL_ERROR_WANT_X509_LOOKUP"}, + {SSL_ERROR_WANT_CONNECT, "SSL_ERROR_WANT_CONNECT"} +}; + +static SSL_METHOD *method; /* for listen and connect init */ +static char x509_buf[X509BUFSIZE]; /* for verify_callback */ +static int callback_data_index = -1; /* for ctx ex_data */ +static unsigned char randvec[1024]; /* XXX */ + +#if defined(__WIN32__) || OPEN_MAX > 256 +# define FOPEN_WORKAROUND(var, expr) var = (expr) +# define VOID_FOPEN_WORKAROUND(expr) expr +#else +/* + * This is an ugly workaround. On Solaris, fopen() will return NULL if + * it gets a file descriptor > 255. To avoid that, we'll make sure that + * there is always one low-numbered file descriptor available when + * fopen() is called. + */ +static int reserved_fd; /* Reserve a low-numbered file descriptor */ +# define USE_FOPEN_WORKAROUND 1 + +# define FOPEN_WORKAROUND(var, expr) \ +do { \ + close(reserved_fd); \ + var = (expr); \ + reserved_fd = open("/dev/null", O_RDONLY); \ +} while (0) + +# define VOID_FOPEN_WORKAROUND(expr) \ +do { \ + close(reserved_fd); \ + expr; \ + reserved_fd = open("/dev/null", O_RDONLY); \ +} while (0) +#endif + +esock_version *esock_ssl_version(void) +{ + static esock_version vsn; + + vsn.compile_version = OPENSSL_VERSION_TEXT; + vsn.lib_version = SSLeay_version(SSLEAY_VERSION); + return &vsn; +} + +char *esock_ssl_ciphers(void) +{ + SSL_CTX *ctx; + SSL *ssl; + char *ciphers; + const char *cp; + int i = 0, used = 0, len, incr = 1024; + + if (!(ctx = SSL_CTX_new(method))) + return NULL; + restrict_protocols(ctx); + if (!(ssl = SSL_new(ctx))) { + SSL_CTX_free(ctx); + return NULL; + } + + ciphers = esock_malloc(incr); + len = incr; + *ciphers = '\0'; + + while (1) { + if (!(cp = SSL_get_cipher_list(ssl, i))) + break; + if (i > 0) { + if (used == len) { + len += incr; + ciphers = esock_realloc(ciphers, len); + } + strcat(ciphers, ":"); + used++; + } + if (strlen(cp) + used >= len) { + len += incr; + ciphers = esock_realloc(ciphers, len); + } + strcat(ciphers, cp); + used += strlen(cp); + i++; + } + SSL_free(ssl); + SSL_CTX_free(ctx); + return ciphers; +} + +void esock_ssl_seed(void *buf, int len) +{ + RAND_seed(buf, len); + + /* XXX Maybe we should call RAND_status() and check if we have got + * enough randomness. + */ +} + +int esock_ssl_init(void) +{ + method = SSLv23_method(); /* SSLv2, SSLv3 and TLSv1, may be restricted + in listen and connect */ + SSL_load_error_strings(); + SSL_library_init(); + esock_ssl_seed(randvec, sizeof(randvec)); + callback_data_index = SSL_CTX_get_ex_new_index(0, "callback_data", + NULL, NULL, + callback_data_free); +#ifdef USE_FOPEN_WORKAROUND + reserved_fd = open("/dev/null", O_RDONLY); + DEBUGF(("init: reserved_fd=%d\r\n", reserved_fd)); +#endif + return 0; +} + + +void esock_ssl_finish(void) +{ + /* Nothing */ +} + + +void esock_ssl_free(Connection *cp) +{ + SSL *ssl = cp->opaque; + SSL_CTX *ctx; + + if (ssl) { + ctx = SSL_get_SSL_CTX(ssl); + SSL_free(ssl); + if (cp->origin != ORIG_ACCEPT) + SSL_CTX_free(ctx); + cp->opaque = NULL; + } +} + + +/* + * Print SSL specific errors. + */ +void esock_ssl_print_errors_fp(FILE *fp) +{ + ERR_print_errors_fp(fp); +} + + +int esock_ssl_accept_init(Connection *cp, void *listenssl) +{ + SSL_CTX *listenctx; + SSL *ssl; + + RESET_ERRSTR(); + MAYBE_SET_ERRSTR("esslacceptinit"); + + if(!listenssl) { + DEBUGF(("esock_ssl_accept_init: listenssl null\n")); + return -1; + } + if (!(listenctx = SSL_get_SSL_CTX(listenssl))) { + DEBUGF(("esock_ssl_accept_init: SSL_get_SSL_CTX\n")); + return -1; + } + if (!(ssl = cp->opaque = SSL_new(listenctx))) { + DEBUGF(("esock_ssl_accept_init: SSL_new(listenctx)\n")); + return -1; + } + SSL_set_fd(ssl, cp->fd); + return 0; + +} + + +int esock_ssl_connect_init(Connection *cp) +{ + SSL_CTX *ctx; + SSL *ssl; + + RESET_ERRSTR(); + MAYBE_SET_ERRSTR("esslconnectinit"); + + if (!(ctx = SSL_CTX_new(method))) + return -1; + if (set_ssl_parameters(cp, ctx) < 0) { + SSL_CTX_free(ctx); + return -1; + } + restrict_protocols(ctx); + if (!(ssl = cp->opaque = SSL_new(ctx))) { + SSL_CTX_free(ctx); + return -1; + } + SSL_set_fd(ssl, cp->fd); + return 0; +} + + +int esock_ssl_listen_init(Connection *cp) +{ + SSL_CTX *ctx; + SSL *ssl; + + RESET_ERRSTR(); + MAYBE_SET_ERRSTR("essllisteninit"); + + if (!(ctx = SSL_CTX_new(method))) + return -1; + if (set_ssl_parameters(cp, ctx) < 0) { + SSL_CTX_free(ctx); + return -1; + } + restrict_protocols(ctx); + + /* The allocation of ctx is for setting ssl parameters, so that + * accepts can inherit them. We allocate ssl to be able to + * refer to it via cp->opaque, but will not be used otherwise. + */ + if (!(ssl = cp->opaque = SSL_new(ctx))) { + SSL_CTX_free(ctx); + return -1; + } + /* Set callback for temporary ephemeral RSA key generation. + * Note: for servers only. */ + SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_callback); + return 0; +} + +/* + * esock_ssl_accept(Connection *cp) + * + */ +int esock_ssl_accept(Connection *cp) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + + DEBUGF(("esock_ssl_accept: calling SSL_accept fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_accept(ssl); + DEBUGF((" sock_errno %d errno %d \n", sock_errno(), errno)); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_accept = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + DEBUGF((" ret %d os error %s\n", ret, strerror(errno))); + if (ret > 0) + return ret; + else if (ret == 0) { + const char* f; int l; unsigned int e; + while ((e = ERR_get_error_line(&f, &l))) { + DEBUGF((" error %s:%d %s\n", f, l, ssl_error_str(e))); + } + /* permanent accept error */ + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("esslaccept"); + return -1; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + +/* + * esock_ssl_connect(Connection *cp) + * + */ +int esock_ssl_connect(Connection *cp) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + + DEBUGF(("esock_ssl_connect: calling SSL_connect fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_connect(ssl); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_connect() = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + if (ret > 0) + return ret; + else if (ret == 0) { + /* permanent connect error */ + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("esslconnect"); + return -1; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + + +int esock_ssl_session_reused(Connection *cp) +{ + SSL *ssl = cp->opaque; + + return SSL_session_reused(ssl); +} + + +/* esock_ssl_read(Connection *cp, char *buf, int len) + * + * Read at most `len' chars into `buf'. Returns number of chars + * read ( > 0), or 0 at EOF, or -1 on error. Sets cp->eof, cp->bp if + * appropriate. + */ + +int esock_ssl_read(Connection *cp, char *buf, int len) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + DEBUGF(("esock_ssl_read: calling SSL_read fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + + ret = SSL_read(ssl, buf, len); + ssl_error = SSL_get_error(ssl, ret); + + DEBUGF((" SSL_read = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + + if (ssl_error == SSL_ERROR_NONE) { + DEBUGMSGF(("message (hex) : [%3.*a]\n", ret, buf)); + DEBUGMSGF(("message (char): [%3.*b]\n", ret, buf)); + } + if (ret > 0) + return ret; + if (ret == 0) { + check_shutdown(cp); + return ret; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + +/* + * esock_ssl_write(Connection *cp, char *buf, int len) + * + * Writes at most `len' chars from `buf'. Returns number of chars + * written, or -1 on error. + */ +int esock_ssl_write(Connection *cp, char *buf, int len) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + DEBUGF(("esock_ssl_write: calling SSL_write fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_write(ssl, buf, len); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_write = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + if (ssl_error == SSL_ERROR_NONE) { + DEBUGMSGF(("message (hex) : [%3.*a]\n", ret, buf)); + DEBUGMSGF(("message (char): [%3.*b]\n", ret, buf)); + } + if (ret > 0) + return ret; + if (ret == 0) { + check_shutdown(cp); + return ret; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + + +int esock_ssl_shutdown(Connection *cp) +{ + int ret, ssl_error; + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + DEBUGF(("esock_ssl_shutdown: calling SSL_shutdown fd = %d\n" + " state before: %s\n", cp->fd, SSL_state_string(ssl))); + ret = SSL_shutdown(ssl); + ssl_error = SSL_get_error(ssl, ret); + DEBUGF((" SSL_shutdown = %d\n" + " ssl_error: %s\n" + " state after: %s\n", + ret, ssl_error_str(ssl_error), SSL_state_string(ssl))); + if (ret >= 0) { + check_shutdown(cp); + return ret; + } + end_ssl_call(ret, cp, ssl_error); + return ret; +} + + +/* Returns total number of bytes in DER encoded cert pointed to by + * *buf, which is allocated by this function, unless return < 0. + * XXX X509_free ?? + */ +int esock_ssl_getpeercert(Connection *cp, unsigned char **buf) +{ + int len; + SSL *ssl = cp->opaque; + X509 *x509; + unsigned char *tmp; + + RESET_ERRSTR(); + if((x509 = SSL_get_peer_certificate(ssl)) == NULL) { + MAYBE_SET_ERRSTR("enopeercert"); /* XXX doc */ + return -1; + } + + if ((len = i2d_X509(x509, NULL)) <= 0) { + MAYBE_SET_ERRSTR("epeercert"); + return -1; + } + + tmp = *buf = esock_malloc(len); + + /* We must use a temporary value here, since i2d_X509(X509 *x, + * unsigned char **out) increments *out. + */ + if (i2d_X509(x509, &tmp) < 0) { + esock_free(tmp); + MAYBE_SET_ERRSTR("epeercert"); + return -1; + } + return len; +} + +/* Returns total number of bytes in chain of certs. Each cert begins + * with a 4-bytes length. The last cert is ended with 4-bytes of + * zeros. The result is returned in *buf, which is allocated unless + * the return value is < 0. + * XXX X509_free ? sk_X509_free ? + * XXX X509_free is reference counting. + */ +int esock_ssl_getpeercertchain(Connection *cp, unsigned char **buf) +{ + SSL *ssl = cp->opaque; + STACK_OF(X509) *x509_stack; + X509 *x509; + int num, i, totlen, pos, *der_len; + unsigned char *vbuf; + + RESET_ERRSTR(); + if((x509_stack = SSL_get_peer_cert_chain(ssl)) == NULL) { + MAYBE_SET_ERRSTR("enopeercertchain"); /* XXX doc */ + return -1; + } + + num = sk_X509_num(x509_stack); + der_len = esock_malloc(num * sizeof(int)); + totlen = 0; + + for (i = 0; i < num; i++) { + x509 = sk_X509_value(x509_stack, i); + totlen += 4; + if ((der_len[i] = i2d_X509(x509, NULL)) < 0) { + MAYBE_SET_ERRSTR("epeercertchain"); + esock_free(der_len); + return -1; + } + totlen += der_len[i]; + } + totlen += 4; + + vbuf = *buf = esock_malloc(totlen); + pos = 0; + + for (i = 0; i < num; i++) { + x509 = sk_X509_value(x509_stack, i); + PUT_INT32(der_len[i], vbuf); + vbuf += 4; + /* Note: i2d_X509 increments vbuf */ + if (i2d_X509(x509, &vbuf) < 0) { + MAYBE_SET_ERRSTR("epeercertchain"); + esock_free(*buf); + esock_free(der_len); + return -1; + } + } + esock_free(der_len); + return totlen; +} + + +int esock_ssl_getprotocol_version(Connection *cp, char **buf) +{ + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + if (!ssl) { + MAYBE_SET_ERRSTR("enoent"); + return -1; + } + *buf = (char *) SSL_get_version(ssl); + + return 0; +} + + +int esock_ssl_getcipher(Connection *cp, char **buf) +{ + SSL *ssl = cp->opaque; + + RESET_ERRSTR(); + if (!ssl) { + MAYBE_SET_ERRSTR("enoent"); + return -1; + } + *buf = (char *) SSL_get_cipher(ssl); + + return 0; +} + +/* Local functions */ + +static char *ssl_error_str(int ssl_error) +{ + int i; + static char buf[128]; + + for (i = 0; i < sizeof(errs)/sizeof(err_entry); i ++) { + if (ssl_error == errs[i].code) + return errs[i].text; + } + sprintf(buf, "esock_openssl: SSL_error unknown: %d", ssl_error); + return buf; +} + +void end_ssl_call(int ret, Connection *cp, int ssl_error) +{ + SET_WANT(cp, ssl_error); + switch (ssl_error) { + case SSL_ERROR_SYSCALL: + /* Typically sock_errno() is equal to ERRNO_BLOCK */ + MAYBE_SET_ERRSTR(esock_posix_str(sock_errno())); + break; + case SSL_ERROR_SSL: + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("esslerrssl"); + break; + case SSL_ERROR_WANT_X509_LOOKUP: + SSLDEBUGF(); + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("ex509lookup"); + break; + case SSL_ERROR_WANT_CONNECT: + SSLDEBUGF(); + sock_set_errno(ERRNO_NONE); + MAYBE_SET_ERRSTR("ewantconnect"); + break; + default: + break; + } +} + +void check_shutdown(Connection *cp) +{ + int sd_mode; + SSL *ssl = cp->opaque; + + sd_mode = SSL_get_shutdown(ssl); + if (sd_mode & SSL_RECEIVED_SHUTDOWN) + cp->eof = 1; + if (sd_mode & SSL_SENT_SHUTDOWN) { + DEBUGF(("check_shutdown SSL_SENT_SHUTDOWN\n")); + cp->bp = 1; + } +} + +/* + * set_ssl_parameters + * + * Set ssl parameters from connection structure. Only called for + * listen and connect. + * + * Note: The -cacertdir option is not documented. + */ +static int set_ssl_parameters(Connection *cp, SSL_CTX *ctx) +{ + char *cacertfile = NULL, *cacertdir = NULL, *certfile = NULL; + char *keyfile = NULL, *ciphers = NULL, *password = NULL; + int verify = 0, verify_depth = DEFAULT_VERIFY_DEPTH, verify_mode; + int i, argc; + char **argv; + callback_data *cb_data; + + RESET_ERRSTR(); + + argc = esock_build_argv(cp->flags, &argv); + + DEBUGF(("Argv:\n")); + for (i = 0; i < argc; i++) { + DEBUGF(("%d: %s\n", i, argv[i])); + } + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "-verify") == 0) { + verify = atoi(argv[++i]); + } else if (strcmp(argv[i], "-depth") == 0) { + verify_depth = atoi(argv[++i]); + } else if (strcmp(argv[i], "-log") == 0) { + /* XXX ignored: logging per connection not supported */ + i++; + } else if (strcmp(argv[i], "-certfile") == 0) { + certfile = argv[++i]; + } else if (strcmp(argv[i], "-keyfile") == 0) { + keyfile = argv[++i]; + } else if (strcmp(argv[i], "-password") == 0) { + password = argv[++i]; + } else if (strcmp(argv[i], "-cacertfile") == 0) { + cacertfile = argv[++i]; + } else if (strcmp(argv[i], "-cacertdir") == 0) { + cacertdir = argv[++i]; + } else if (strcmp(argv[i], "-d") == 0) { + /* XXX ignored: debug per connection not supported */ + i++; + } else if (strcmp(argv[i], "-ciphers") == 0) { + ciphers = argv[++i]; + } else { + /* XXX Error: now ignored */ + } + } + DEBUGF(("set_ssl_parameters: all arguments read\n")); + + if (cp->origin == ORIG_LISTEN && !certfile) { + DEBUGF(("ERROR: Server must have certificate\n")); + MAYBE_SET_ERRSTR("enoservercert"); + goto err_end; + } + + /* Define callback data */ + /* XXX Check for NULL */ + cb_data = esock_malloc(sizeof(callback_data)); + cb_data->ctx = ctx; + if (password) { + cb_data->passwd = esock_malloc(strlen(password) + 1); + strcpy(cb_data->passwd, password); + } else + cb_data->passwd = NULL; + cb_data->verify_depth = verify_depth; + SSL_CTX_set_ex_data(ctx, callback_data_index, cb_data); + + /* password callback */ + SSL_CTX_set_default_passwd_cb(ctx, passwd_callback); + SSL_CTX_set_default_passwd_cb_userdata(ctx, cb_data); + + /* Set location for "trusted" certificates */ + if (cacertfile || cacertdir) { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_load_verify_locations\n")); + FOPEN_WORKAROUND(res, SSL_CTX_load_verify_locations(ctx, cacertfile, + cacertdir)); + if (!res) { + DEBUGF(("ERROR: Cannot load verify locations\n")); + MAYBE_SET_ERRSTR("ecacertfile"); + goto err_end; + } + } else { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_set_default_verify_paths\n")); + FOPEN_WORKAROUND(res, SSL_CTX_set_default_verify_paths(ctx)); + if (!res) { + DEBUGF(("ERROR: Cannot set default verify paths\n")); + MAYBE_SET_ERRSTR("ecacertfile"); + goto err_end; + } + } + + /* For a server the following sets the list of CA distinguished + * names that it sends to its client when it requests the + * certificate from the client. + * XXX The names of certs in cacertdir ignored. + */ + if (cp->origin == ORIG_LISTEN && cacertfile) { + DEBUGF(("set_ssl_parameters: SSL_CTX_set_client_CA_list\n")); + VOID_FOPEN_WORKAROUND(SSL_CTX_set_client_CA_list(ctx, + SSL_load_client_CA_file(cacertfile))); + if (!SSL_CTX_get_client_CA_list(ctx)) { + DEBUGF(("ERROR: Cannot set client CA list\n")); + MAYBE_SET_ERRSTR("ecacertfile"); + goto err_end; + } + } + + /* Use certificate file if key file has not been set. */ + if (!keyfile) + keyfile = certfile; + + if (certfile) { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_use_certificate_file\n")); + FOPEN_WORKAROUND(res, SSL_CTX_use_certificate_file(ctx, certfile, + SSL_FILETYPE_PEM)); + if (res <= 0) { + DEBUGF(("ERROR: Cannot set certificate file\n")); + MAYBE_SET_ERRSTR("ecertfile"); + goto err_end; + } + } + if (keyfile) { + int res; + DEBUGF(("set_ssl_parameters: SSL_CTX_use_PrivateKey_file\n")); + FOPEN_WORKAROUND(res, SSL_CTX_use_PrivateKey_file(ctx, keyfile, + SSL_FILETYPE_PEM)); + if (res <= 0) { + DEBUGF(("ERROR: Cannot set private key file\n")); + MAYBE_SET_ERRSTR("ekeyfile"); + goto err_end; + } + } + if(certfile && keyfile) { + DEBUGF(("set_ssl_parameters: SSL_CTX_check_private_key\n")); + if (!SSL_CTX_check_private_key(ctx)) { + DEBUGF(("ERROR: Private key does not match the certificate\n")); + MAYBE_SET_ERRSTR("ekeymismatch"); + goto err_end; + } + } + + /* Ciphers */ + if (ciphers) { + DEBUGF(("set_ssl_parameters: SSL_CTX_set_cipher_list\n")); + if (!SSL_CTX_set_cipher_list(ctx, ciphers)) { + DEBUGF(("ERROR: Cannot set cipher list\n")); + MAYBE_SET_ERRSTR("ecipher"); + goto err_end; + } + } + + /* Verify depth */ + DEBUGF(("set_ssl_parameters: SSL_CTX_set_verify_depth (depth = %d)\n", + verify_depth)); + SSL_CTX_set_verify_depth(ctx, verify_depth); + + /* Verify mode and callback */ + /* XXX Why precisely these modes? */ + switch (verify) { + case 0: + verify_mode = SSL_VERIFY_NONE; + break; + case 1: + verify_mode = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE; + break; + case 2: + verify_mode = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE| + SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + break; + default: + verify_mode = SSL_VERIFY_NONE; + } + DEBUGF(("set_ssl_parameters: SSL_CTX_set_verify (verify = %d)\n", + verify)); + SSL_CTX_set_verify(ctx, verify_mode, verify_callback); + + /* Session id context. Should be an option really. */ + if (cp->origin == ORIG_LISTEN) { + unsigned char *sid = "Erlang/OTP/ssl"; + SSL_CTX_set_session_id_context(ctx, sid, strlen(sid)); + } + + /* info callback */ + if (debug) + SSL_CTX_set_info_callback(ctx, info_callback); + + DEBUGF(("set_ssl_parameters: done\n")); + /* Free arg list */ + for (i = 0; argv[i]; i++) + esock_free(argv[i]); + esock_free(argv); + return 0; + + err_end: + DEBUGF(("set_ssl_parameters: error\n")); + /* Free arg list */ + for (i = 0; argv[i]; i++) + esock_free(argv[i]); + esock_free(argv); + return -1; +} + +/* Call back functions */ + +static int verify_callback(int ok, X509_STORE_CTX *x509_ctx) +{ + X509 *cert; + int cert_err, depth; + SSL *ssl; + SSL_CTX *ctx; + callback_data *cb_data; + + cert = X509_STORE_CTX_get_current_cert(x509_ctx); + cert_err = X509_STORE_CTX_get_error(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + ctx = SSL_get_SSL_CTX(ssl); + cb_data = SSL_CTX_get_ex_data(ctx, callback_data_index); + + X509_NAME_oneline(X509_get_subject_name(cert), x509_buf, sizeof(x509_buf)); + DEBUGF((" +vfy: depth = %d\n", depth)); + DEBUGF((" subject = %s\n", x509_buf)); + X509_NAME_oneline(X509_get_issuer_name(cert), x509_buf, sizeof(x509_buf)); + DEBUGF((" issuer = %s\n", x509_buf)); + + if (!ok) { + DEBUGF((" +vfy: error = %d [%s]\n", cert_err, + X509_verify_cert_error_string(cert_err))); + if (depth >= cb_data->verify_depth) + ok = 1; + } + + switch (cert_err) { + case X509_V_OK: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + ok = 1; + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + MAYBE_SET_ERRSTR("enoissuercert"); + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + MAYBE_SET_ERRSTR("epeercertexpired"); + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + MAYBE_SET_ERRSTR("epeercertinvalid"); + break; + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + MAYBE_SET_ERRSTR("eselfsignedcert"); + break; + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + MAYBE_SET_ERRSTR("echaintoolong"); + break; + default: + MAYBE_SET_ERRSTR("epeercert"); + break; + } + DEBUGF((" +vfy: return = %d\n",ok)); + return ok; +} + +static int passwd_callback(char *buf, int num, int rwflag, void *userdata) +{ + callback_data *cb_data = userdata; + int len; + + if (cb_data && cb_data->passwd) { + DEBUGF((" +passwd: %s\n", cb_data->passwd)); + strncpy(buf, cb_data->passwd, num); + len = strlen(cb_data->passwd); + return len; + } + DEBUGF((" +passwd: ERROR: No password set.\n")); + return 0; +} + +static void info_callback(const SSL *ssl, int where, int ret) +{ + char *str; + + if (where & SSL_CB_LOOP) { + DEBUGF((" info: %s\n",SSL_state_string_long(ssl))); + } else if (where & SSL_CB_ALERT) { + str = (where & SSL_CB_READ) ? "read" : "write"; + DEBUGF((" info: SSL3 alert %s:%s:%s\n", str, + SSL_alert_type_string_long(ret), + SSL_alert_desc_string_long(ret))); + } else if (where & SSL_CB_EXIT) { + if (ret == 0) { + DEBUGF((" info: failed in %s\n", SSL_state_string_long(ssl))); + } else if (ret < 0) { + DEBUGF((" info: error in %s\n", SSL_state_string_long(ssl))); + } + } +} + +/* This function is called whenever a SSL_CTX *ctx structure is + * freed. +*/ +static void callback_data_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int idx, long arg1, void *argp) +{ + callback_data *cb_data = ptr; + + if (cb_data) { + if (cb_data->passwd) + esock_free(cb_data->passwd); + esock_free(cb_data); + } +} + +static RSA *tmp_rsa_callback(SSL *ssl, int is_export, int keylen) +{ + static RSA *rsa512 = NULL; + static RSA *rsa1024 = NULL; + + switch (keylen) { + case 512: + if (!rsa512) + rsa512 = RSA_generate_key(keylen, RSA_F4, NULL, NULL); + return rsa512; + break; + case 1024: + if (!rsa1024) + rsa1024 = RSA_generate_key(keylen, RSA_F4, NULL, NULL); + return rsa1024; + break; + default: + if (rsa1024) + return rsa1024; + if (rsa512) + return rsa512; + rsa512 = RSA_generate_key(keylen, RSA_F4, NULL, NULL); + return rsa512; + } +} + +/* Restrict protocols (SSLv2, SSLv3, TLSv1) */ +static void restrict_protocols(SSL_CTX *ctx) +{ + long options = 0; + + if (protocol_version) { + if ((protocol_version & ESOCK_SSLv2) == 0) + options |= SSL_OP_NO_SSLv2; + if ((protocol_version & ESOCK_SSLv3) == 0) + options |= SSL_OP_NO_SSLv3; + if ((protocol_version & ESOCK_TLSv1) == 0) + options |= SSL_OP_NO_TLSv1; + SSL_CTX_set_options(ctx, options); + } +} + + +static unsigned char randvec [] = { + 181, 177, 237, 240, 107, 24, 43, 148, + 105, 4, 248, 13, 199, 255, 23, 58, + 71, 181, 57, 151, 156, 25, 165, 7, + 73, 80, 80, 231, 70, 110, 96, 162, + 24, 205, 178, 178, 67, 122, 210, 180, + 92, 6, 156, 182, 84, 159, 85, 6, + 175, 66, 165, 167, 137, 34, 179, 237, + 77, 90, 87, 185, 21, 106, 92, 115, + 137, 65, 233, 42, 164, 153, 208, 133, + 160, 172, 129, 202, 46, 220, 98, 66, + 115, 66, 46, 28, 226, 200, 140, 145, + 207, 194, 58, 71, 56, 203, 113, 34, + 221, 116, 63, 114, 188, 210, 45, 238, + 200, 123, 35, 150, 2, 78, 160, 22, + 226, 167, 162, 10, 182, 75, 109, 97, + 86, 252, 93, 125, 117, 214, 220, 37, + 105, 160, 56, 158, 97, 57, 22, 14, + 73, 169, 111, 190, 222, 176, 14, 82, + 111, 42, 87, 90, 136, 236, 22, 209, + 156, 207, 40, 251, 88, 141, 51, 211, + 31, 158, 153, 91, 119, 83, 255, 60, + 55, 94, 5, 115, 119, 210, 224, 185, + 163, 163, 5, 3, 197, 106, 110, 206, + 109, 132, 50, 190, 177, 133, 175, 129, + 225, 161, 156, 244, 77, 150, 99, 38, + 17, 111, 46, 230, 152, 64, 50, 164, + 19, 78, 3, 164, 169, 175, 104, 97, + 103, 158, 91, 168, 186, 191, 73, 88, + 118, 112, 41, 188, 219, 0, 198, 209, + 206, 7, 5, 169, 127, 180, 80, 74, + 124, 4, 4, 108, 197, 67, 204, 29, + 101, 95, 174, 147, 64, 163, 89, 160, + 10, 5, 56, 134, 209, 69, 209, 55, + 214, 136, 45, 212, 113, 85, 159, 133, + 141, 249, 75, 40, 175, 91, 142, 13, + 179, 179, 51, 0, 136, 63, 148, 175, + 103, 162, 8, 214, 4, 24, 59, 71, + 9, 185, 48, 127, 159, 165, 8, 8, + 135, 151, 92, 214, 132, 151, 204, 169, + 24, 112, 229, 59, 236, 81, 238, 64, + 150, 196, 97, 213, 140, 159, 20, 24, + 79, 210, 191, 53, 130, 33, 157, 87, + 16, 180, 175, 217, 56, 123, 115, 196, + 130, 6, 155, 37, 220, 80, 232, 129, + 240, 57, 199, 249, 196, 152, 28, 111, + 124, 192, 59, 46, 29, 21, 178, 51, + 156, 17, 248, 61, 254, 80, 201, 131, + 203, 59, 227, 191, 71, 121, 134, 181, + 55, 79, 130, 225, 246, 36, 179, 224, + 189, 243, 200, 75, 73, 41, 251, 41, + 71, 251, 78, 146, 99, 101, 104, 69, + 18, 122, 65, 24, 232, 84, 246, 242, + 209, 18, 241, 114, 3, 65, 177, 99, + 49, 99, 215, 59, 9, 175, 195, 11, + 25, 46, 43, 120, 109, 179, 159, 250, + 239, 246, 135, 78, 2, 238, 214, 237, + 64, 170, 50, 44, 68, 67, 111, 232, + 225, 230, 224, 124, 76, 32, 52, 158, + 151, 54, 184, 135, 122, 66, 211, 215, + 121, 90, 124, 158, 55, 73, 116, 137, + 240, 15, 38, 31, 183, 86, 93, 49, + 148, 184, 125, 250, 155, 216, 84, 246, + 27, 172, 141, 54, 80, 158, 227, 254, + 189, 164, 238, 229, 68, 26, 231, 11, + 198, 222, 15, 141, 98, 8, 124, 219, + 60, 125, 170, 213, 114, 24, 189, 65, + 80, 186, 71, 126, 223, 153, 20, 141, + 110, 73, 173, 218, 214, 63, 205, 177, + 132, 115, 184, 28, 122, 232, 210, 72, + 237, 41, 93, 17, 152, 95, 242, 138, + 79, 98, 47, 197, 36, 17, 137, 230, + 15, 73, 193, 1, 181, 123, 0, 186, + 185, 135, 142, 200, 139, 78, 57, 145, + 191, 32, 98, 250, 113, 188, 71, 32, + 205, 81, 219, 99, 60, 87, 42, 95, + 249, 252, 121, 125, 246, 230, 74, 162, + 73, 59, 179, 142, 178, 47, 163, 161, + 236, 14, 123, 219, 18, 6, 102, 140, + 215, 210, 76, 9, 119, 147, 252, 63, + 13, 51, 161, 172, 180, 116, 212, 129, + 116, 237, 38, 64, 213, 222, 35, 14, + 183, 237, 78, 204, 250, 250, 5, 41, + 142, 5, 207, 154, 65, 183, 108, 82, + 1, 43, 149, 233, 89, 195, 25, 233, + 4, 34, 19, 122, 16, 58, 121, 5, + 118, 168, 22, 213, 49, 226, 163, 169, + 21, 78, 179, 232, 125, 216, 198, 147, + 245, 196, 199, 138, 185, 167, 179, 82, + 175, 53, 6, 162, 5, 141, 180, 212, + 95, 201, 234, 169, 111, 175, 138, 197, + 177, 246, 154, 41, 185, 201, 134, 187, + 88, 99, 231, 23, 190, 36, 72, 174, + 244, 185, 205, 50, 230, 226, 210, 119, + 175, 107, 109, 244, 12, 122, 84, 51, + 146, 95, 68, 74, 76, 212, 221, 103, + 244, 71, 63, 133, 149, 233, 48, 3, + 176, 168, 6, 98, 88, 226, 120, 190, + 205, 249, 38, 157, 205, 148, 250, 203, + 147, 62, 195, 229, 219, 109, 177, 119, + 120, 43, 165, 99, 253, 210, 180, 32, + 227, 180, 174, 64, 156, 139, 251, 53, + 205, 132, 210, 208, 3, 199, 115, 64, + 59, 27, 249, 164, 224, 191, 124, 241, + 142, 10, 19, 120, 227, 46, 174, 231, + 48, 65, 41, 56, 51, 38, 185, 95, + 250, 182, 100, 40, 196, 124, 173, 119, + 162, 148, 170, 34, 51, 68, 175, 60, + 242, 201, 225, 34, 146, 157, 159, 0, + 144, 148, 82, 72, 149, 53, 201, 10, + 248, 206, 154, 126, 33, 153, 56, 48, + 5, 90, 194, 22, 251, 173, 211, 202, + 203, 253, 112, 147, 188, 200, 142, 206, + 206, 175, 233, 76, 93, 104, 125, 41, + 64, 145, 202, 53, 130, 251, 23, 90, + 28, 199, 13, 128, 185, 154, 53, 194, + 195, 55, 80, 56, 151, 216, 195, 138, + 7, 170, 143, 236, 74, 141, 229, 174, + 32, 165, 131, 68, 174, 104, 35, 143, + 183, 41, 80, 191, 120, 79, 166, 240, + 123, 55, 60, 2, 128, 56, 4, 199, + 122, 85, 90, 76, 246, 29, 13, 6, + 126, 229, 14, 203, 244, 73, 121, 42, + 169, 35, 44, 202, 18, 69, 153, 120, + 141, 77, 124, 191, 215, 18, 115, 187, + 108, 246, 135, 151, 225, 192, 50, 89, + 128, 45, 39, 253, 149, 234, 203, 84, + 51, 174, 15, 237, 17, 57, 76, 81, + 39, 107, 40, 36, 22, 52, 92, 39}; |