esock_openssl.c

来自「OTP是开放电信平台的简称」· C语言 代码 · 共 1,212 行 · 第 1/3 页

C
1,212
字号
/* ``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 via the world wide web 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 Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' *  *     $Id$ *//* * 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)#endifesock_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)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?