put back dynamic SSL lib loading

This commit is contained in:
valenok 2010-08-26 20:50:59 +01:00
parent 4f716e2b9a
commit ee997ba7d4
2 changed files with 145 additions and 64 deletions

View File

@ -22,8 +22,7 @@ all:
CFLAGS= -W -Wall -std=c99 -pedantic -Os -fomit-frame-pointer $(COPT)
MAC_SHARED= -flat_namespace -bundle -undefined suppress
SSLFLAGS= -lssl -lcrypto
LINFLAGS= -ldl -pthread $(SSLFLAGS) $(CFLAGS)
LINFLAGS= -ldl -pthread $(CFLAGS)
LIB= _$(PROG).so
linux:
@ -32,17 +31,16 @@ linux:
bsd:
$(CC) $(CFLAGS) mongoose.c -shared -pthread -fpic -fPIC -o $(LIB)
$(CC) $(CFLAGS) mongoose.c main.c -pthread $(SSLFLAGS) -o $(PROG)
$(CC) $(CFLAGS) mongoose.c main.c -pthread -o $(PROG)
mac:
$(CC) $(CFLAGS) $(MAC_SHARED) mongoose.c -pthread $(SSLFLAGS) -o $(LIB)
$(CC) $(CFLAGS) mongoose.c main.c -pthread $(SSLFLAGS) -o $(PROG)
$(CC) $(CFLAGS) $(MAC_SHARED) mongoose.c -pthread -o $(LIB)
$(CC) $(CFLAGS) mongoose.c main.c -pthread -o $(PROG)
solaris:
gcc $(CFLAGS) mongoose.c -pthread -lnsl \
-lsocket $(SSLFLAGS) -fpic -fPIC -shared -o $(LIB)
gcc $(CFLAGS) mongoose.c main.c -pthread -lnsl -lsocket $(SSLFLAGS) \
-o $(PROG)
-lsocket -fpic -fPIC -shared -o $(LIB)
gcc $(CFLAGS) mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG)
##########################################################################

View File

@ -90,6 +90,8 @@ typedef long off_t;
#define ERRNO GetLastError()
#define NO_SOCKLEN_T
#define SSL_LIB "ssleay32.dll"
#define CRYPTO_LIB "libeay32.dll"
#define DIRSEP '\\'
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
#define O_NONBLOCK 0
@ -172,6 +174,13 @@ typedef struct DIR {
#include <dirent.h>
#include <dlfcn.h>
#include <pthread.h>
#if defined(__MACH__)
#define SSL_LIB "libssl.dylib"
#define CRYPTO_LIB "libcrypto.dylib"
#else
#define SSL_LIB "libssl.so"
#define CRYPTO_LIB "libcrypto.so"
#endif
#define DIRSEP '/'
#define IS_DIRSEP_CHAR(c) ((c) == '/')
#define O_BINARY 0
@ -230,27 +239,76 @@ typedef struct ssl_ctx_st SSL_CTX;
#define SSL_FILETYPE_PEM 1
#define CRYPTO_LOCK 1
extern void SSL_free(SSL *);
extern int SSL_accept(SSL *);
extern int SSL_connect(SSL *);
extern int SSL_read(SSL *, void *, int);
extern int SSL_write(SSL *, const void *, int);
extern int SSL_get_error(const SSL *, int);
extern int SSL_set_fd(SSL *, int);
extern SSL *SSL_new(SSL_CTX *);
extern SSL_CTX *SSL_CTX_new(SSL_METHOD *);
extern SSL_METHOD *SSLv23_server_method(void);
extern int SSL_library_init(void);
extern void SSL_load_error_strings(void);
extern int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);
extern int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int);
extern void SSL_CTX_set_default_passwd_cb(SSL_CTX *, mg_callback_t);
extern void SSL_CTX_free(SSL_CTX *);
extern unsigned long ERR_get_error(void);
extern char *ERR_error_string(unsigned long, char *);
extern int CRYPTO_num_locks(void);
extern void CRYPTO_set_locking_callback(void (*)(int, int, const char *, int));
extern void CRYPTO_set_id_callback(unsigned long (*)(void));
// Dynamically loaded SSL functionality
struct ssl_func {
const char *name; // SSL function name
void (*ptr)(void); // Function pointer
};
#define SSL_free(x) (* (void (*)(SSL *)) ssl_sw[0].ptr)(x)
#define SSL_accept(x) (* (int (*)(SSL *)) ssl_sw[1].ptr)(x)
#define SSL_connect(x) (* (int (*)(SSL *)) ssl_sw[2].ptr)(x)
#define SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) \
ssl_sw[3].ptr)((x),(y),(z))
#define SSL_write(x,y,z) (* (int (*)(SSL *, const void *,int)) \
ssl_sw[4].ptr)((x), (y), (z))
#define SSL_get_error(x,y)(* (int (*)(SSL *, int)) ssl_sw[5])((x), (y))
#define SSL_set_fd(x,y) (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)((x), (y))
#define SSL_new(x) (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)(x)
#define SSL_CTX_new(x) (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)(x)
#define SSLv23_server_method() (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)()
#define SSL_library_init() (* (int (*)(void)) ssl_sw[10].ptr)()
#define SSL_CTX_use_PrivateKey_file(x,y,z) (* (int (*)(SSL_CTX *, \
const char *, int)) ssl_sw[11].ptr)((x), (y), (z))
#define SSL_CTX_use_certificate_file(x,y,z) (* (int (*)(SSL_CTX *, \
const char *, int)) ssl_sw[12].ptr)((x), (y), (z))
#define SSL_CTX_set_default_passwd_cb(x,y) \
(* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr)((x),(y))
#define SSL_CTX_free(x) (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)(x)
#define ERR_get_error() (* (unsigned long (*)(void)) ssl_sw[15].ptr)()
#define ERR_error_string(x, y) (* (char * (*)(unsigned long, char *)) ssl_sw[16].ptr)((x), (y))
#define SSL_load_error_strings() (* (void (*)(void)) ssl_sw[17].ptr)()
#define CRYPTO_num_locks() (* (int (*)(void)) crypto_sw[0].ptr)()
#define CRYPTO_set_locking_callback(x) \
(* (void (*)(void (*)(int, int, const char *, int))) \
crypto_sw[1].ptr)(x)
#define CRYPTO_set_id_callback(x) \
(* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)(x)
// set_ssl_option() function updates this array.
// It loads SSL library dynamically and changes NULLs to the actual addresses
// of respective functions. The macros above (like SSL_connect()) are really
// just calling these functions indirectly via the pointer.
static struct ssl_func ssl_sw[] = {
{"SSL_free", NULL},
{"SSL_accept", NULL},
{"SSL_connect", NULL},
{"SSL_read", NULL},
{"SSL_write", NULL},
{"SSL_get_error", NULL},
{"SSL_set_fd", NULL},
{"SSL_new", NULL},
{"SSL_CTX_new", NULL},
{"SSLv23_server_method", NULL},
{"SSL_library_init", NULL},
{"SSL_CTX_use_PrivateKey_file", NULL},
{"SSL_CTX_use_certificate_file",NULL},
{"SSL_CTX_set_default_passwd_cb",NULL},
{"SSL_CTX_free", NULL},
{"ERR_get_error", NULL},
{"ERR_error_string", NULL},
{"SSL_load_error_strings", NULL},
{NULL, NULL}
};
// Similar array as ssl_sw. These functions could be located in different lib.
static struct ssl_func crypto_sw[] = {
{"CRYPTO_num_locks", NULL},
{"CRYPTO_set_locking_callback", NULL},
{"CRYPTO_set_id_callback", NULL},
{NULL, NULL}
};
static const char *month_names[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
@ -291,15 +349,15 @@ struct socket {
};
struct mg_context {
int stop_flag; // Should we stop event loop
SSL_CTX *ssl_ctx; // SSL context
int stop_flag; // Should we stop event loop
SSL_CTX *ssl_ctx; // SSL context
const struct mg_config *config; // Mongoose configuration
struct socket *listening_sockets;
int num_threads; // Number of threads
pthread_mutex_t mutex; // Protects (max|num)_threads
pthread_cond_t cond; // Condvar for tracking workers terminations
int num_threads; // Number of threads
pthread_mutex_t mutex; // Protects (max|num)_threads
pthread_cond_t cond; // Condvar for tracking workers terminations
struct socket queue[20]; // Accepted sockets
int sq_head; // Head of the socket queue
@ -373,6 +431,13 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
conn->request_info.log_message = NULL;
}
// Return OpenSSL error message
static const char *ssl_error(void) {
unsigned long err;
err = ERR_get_error();
return err == 0 ? "" : ERR_error_string(err, NULL);
}
// Return fake connection structure. Used for logging, if connection
// is not applicable at the moment of logging.
static struct mg_connection *fc(struct mg_context *ctx) {
@ -920,14 +985,12 @@ static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
return hThread == NULL ? -1 : 0;
}
#if 0
static HANDLE dlopen(const char *dll_name, int flags) {
wchar_t wbuf[PATH_MAX];
flags = 0; // Unused
to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
return LoadLibraryW(wbuf);
}
#endif
#if !defined(NO_CGI)
#define SIGKILL 0
@ -1125,12 +1188,9 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
/* How many bytes we send in this iteration */
k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
#if !defined(NO_SSL)
if (ssl != NULL) {
n = SSL_write(ssl, buf + sent, k);
} else
#endif // !NO_SSL
if (fp != NULL) {
} else if (fp != NULL) {
n = fwrite(buf + sent, 1, k, fp);
if (ferror(fp))
n = -1;
@ -1152,12 +1212,9 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
static int pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len) {
int nread;
#if !defined(NO_SSL)
if (ssl != NULL) {
nread = SSL_read(ssl, buf, len);
} else
#endif // !NO_SSL
if (fp != NULL) {
} else if (fp != NULL) {
nread = fread(buf, 1, (size_t) len, fp);
if (ferror(fp))
nread = -1;
@ -3251,33 +3308,68 @@ static unsigned long ssl_id_callback(void) {
return (unsigned long) pthread_self();
}
// Return OpenSSL error message
static const char *ssl_error(void) {
unsigned long err;
err = ERR_get_error();
return err == 0 ? "" : ERR_error_string(err, NULL);
static bool_t load_dll(struct mg_context *ctx, const char *dll_name,
struct ssl_func *sw) {
union {void *p; void (*fp)(void);} u;
void *dll_handle;
struct ssl_func *fp;
if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
return MG_FALSE;
}
for (fp = sw; fp->name != NULL; fp++) {
#ifdef _WIN32
// GetProcAddress() returns pointer to function
u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
#else
// dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
// function pointers. We need to use a union to make a cast.
u.p = dlsym(dll_handle, fp->name);
#endif /* _WIN32 */
if (u.fp == NULL) {
cry(fc(ctx), "%s: cannot find %s", __func__, fp->name);
return MG_FALSE;
} else {
fp->ptr = u.fp;
}
}
return MG_TRUE;
}
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
static enum mg_error_t set_ssl_option(struct mg_context *ctx) {
SSL_CTX *CTX;
int i, size;
const char *pem = ctx->config->ssl_certificate;
if (pem == NULL) {
return MG_SUCCESS;
}
if (load_dll(ctx, SSL_LIB, ssl_sw) == MG_FALSE ||
load_dll(ctx, CRYPTO_LIB, crypto_sw) == MG_FALSE) {
return MG_ERROR;
}
// Initialize SSL crap
SSL_library_init();
SSL_load_error_strings();
if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL) {
cry(fc(ctx), "SSL_CTX_new error: %s", ssl_error());
return MG_ERROR;
} else if (ctx->config->ssl_password_handler != NULL) {
SSL_CTX_set_default_passwd_cb(CTX, ctx->config->ssl_password_handler);
}
if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0) {
if (CTX != NULL && SSL_CTX_use_certificate_file(CTX, pem,
SSL_FILETYPE_PEM) == 0) {
cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
return MG_ERROR;
} else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0) {
} else if (CTX != NULL && SSL_CTX_use_PrivateKey_file(CTX, pem,
SSL_FILETYPE_PEM) == 0) {
cry(fc(ctx), "%s: cannot open %s: %s", NULL, pem, ssl_error());
return MG_ERROR;
}
@ -3365,12 +3457,10 @@ static void close_socket_gracefully(SOCKET sock) {
}
static void close_connection(struct mg_connection *conn) {
#if !defined(NO_SSL)
if (conn->ssl) {
SSL_free(conn->ssl);
conn->ssl = NULL;
}
#endif // !NO_SSL
if (conn->client.sock != INVALID_SOCKET) {
close_socket_gracefully(conn->client.sock);
@ -3486,7 +3576,6 @@ static void worker_thread(struct mg_context *ctx) {
conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip);
conn->request_info.is_ssl = conn->client.is_ssl;
#if !defined(NO_SSL)
if (conn->client.is_ssl && (conn->ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
cry(conn, "%s: SSL_new: %s", __func__, ssl_error());
} else if (conn->client.is_ssl &&
@ -3494,9 +3583,7 @@ static void worker_thread(struct mg_context *ctx) {
cry(conn, "%s: SSL_set_fd: %s", __func__, ssl_error());
} else if (conn->client.is_ssl && SSL_accept(conn->ssl) != 1) {
cry(conn, "%s: SSL handshake error: %s", __func__, ssl_error());
} else
#endif // !NO_SSL
{
} else {
process_new_connection(conn);
}
@ -3604,12 +3691,10 @@ static void master_thread(struct mg_context *ctx) {
}
(void) pthread_mutex_unlock(&ctx->mutex);
#if !defined(NO_SSL)
// Deallocate SSL context
if (ctx->ssl_ctx != NULL) {
SSL_CTX_free(ctx->ssl_ctx);
}
#endif // !NO_SSL
// All threads exited, no sync is needed. Destroy mutex and condvars
(void) pthread_mutex_destroy(&ctx->mutex);
@ -3667,11 +3752,9 @@ struct mg_context * mg_start(const struct mg_config *config) {
// NOTE(lsm): order is important here. SSL certificates must
// be initialized before listening ports. UID must be set last.
if (set_gpass_option(ctx) == MG_ERROR ||
#if !defined(NO_SSL)
(config->ssl_certificate != NULL && set_ssl_option(ctx) == MG_ERROR) ||
#endif // !NO_SSL
if (set_ssl_option(ctx) == MG_ERROR ||
set_ports_option(ctx) == MG_ERROR ||
set_gpass_option(ctx) == MG_ERROR ||
#if !defined(_WIN32)
set_uid_option(ctx) == MG_ERROR ||
#endif