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 + -
显示快捷键?