mirror of https://github.com/asterisk/asterisk
This set of changes introduces TCP and TLS support for chan_sip. There are various new options in configs/sip.conf.sample that are used to enable these features. Also, there is a document, doc/siptls.txt that describes some things in more detail. This code was implemented by Brett Bryant and James Golovich. It was reviewed by Joshua Colp and myself. A number of other people participated in the testing of this code, but since it was done outside of the bug tracker, I do not have their names. If you were one of them, thanks a lot for the help! (closes issue #4903, but with completely different code that what exists there.) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@99085 65c4cc65-6c06-0410-ace0-fbb531ad65f31.6.0
parent
1807acb9b0
commit
b995c78c31
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,94 @@
|
||||
Asterisk SIP/TLS Transport
|
||||
==========================
|
||||
|
||||
When using TLS the client will typically check the validity of the
|
||||
certificate chain. So that means you either need a certificate that is
|
||||
signed by one of the larger CAs, or if you use a self signed certificate
|
||||
you must install a copy of your CA on the client.
|
||||
|
||||
So far this code has been test with:
|
||||
Asterisk as client and server (TLS and TCP)
|
||||
Polycom Soundpoint IP Phones (TLS and TCP)
|
||||
Polycom phones require that the host (ip or hostname) that is
|
||||
configured match the 'common name' in the certificate
|
||||
Minisip Softphone (TLS and TCP)
|
||||
Cisco IOS Gateways (TCP only)
|
||||
SNOM 360 (TLS only)
|
||||
Zoiper Biz Softphone (TLS and TCP)
|
||||
|
||||
|
||||
sip.conf options
|
||||
----------------
|
||||
tlsenable=[yes|no]
|
||||
Enable TLS server, default is no
|
||||
|
||||
tlsbindaddr=<ip address>
|
||||
Specify IP address to bind TLS server to, default is 0.0.0.0
|
||||
|
||||
tlscertfile=</path/to/certificate>
|
||||
The server's certificate file. Should include the key and
|
||||
certificate. This is mandatory if your going to run a TLS server.
|
||||
|
||||
tlscafile=</path/to/certificate>
|
||||
If the server your connecting to uses a self signed certificate
|
||||
you should have their certificate installed here so the code can
|
||||
verify the authenticity of their certificate.
|
||||
|
||||
tlscadir=</path/to/ca/dir>
|
||||
A directory full of CA certificates. The files must be named with
|
||||
the CA subject name hash value.
|
||||
(see man SSL_CTX_load_verify_locations for more info)
|
||||
|
||||
tlsdontverifyserver=[yes|no]
|
||||
If set to yes, don't verify the servers certificate when acting as
|
||||
a client. If you don't have the server's CA certificate you can
|
||||
set this and it will connect without requiring tlscafile to be set.
|
||||
Default is no.
|
||||
|
||||
tlscipher=<SSL cipher string>
|
||||
A string specifying which SSL ciphers to use or not use
|
||||
|
||||
|
||||
Sample config
|
||||
-------------
|
||||
|
||||
Here are the relevant bits of config for setting up TLS between 2
|
||||
asterisk servers. With server_a registering to server_b
|
||||
|
||||
On server_a:
|
||||
[general]
|
||||
tlsenable=yes
|
||||
tlscertfgile=/etc/asterisk/asterisk.pem
|
||||
tlscafile=/etc/ssl/ca.pem ; This is the CA file used to generate both certificates
|
||||
register => tls://100:test@192.168.0.100:5061
|
||||
|
||||
[101]
|
||||
type=friend
|
||||
context=internal
|
||||
host=192.168.0.100 ; The host should be either IP or hostname and should
|
||||
; match the 'common name' field in the servers certificate
|
||||
secret=test
|
||||
dtmfmode=rfc2833
|
||||
disallow=all
|
||||
allow=ulaw
|
||||
transport=tls
|
||||
port=5061
|
||||
|
||||
On server_b:
|
||||
[general]
|
||||
tlsenable=yes
|
||||
tlscertfgile=/etc/asterisk/asterisk.pem
|
||||
|
||||
[100]
|
||||
type=friend
|
||||
context=internal
|
||||
host=dynamic
|
||||
secret=test
|
||||
dtmfmode=rfc2833
|
||||
disallow=all
|
||||
allow=ulaw
|
||||
;You can specify transport= and port=5061 for TLS, but its not necessary in
|
||||
;the server configuration, any type of SIP transport will work
|
||||
;transport=tls
|
||||
;port=5061
|
||||
|
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2006, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file server.h
|
||||
*
|
||||
* \brief Generic support for tcp/tls servers in Asterisk.
|
||||
* \note In order to have TLS/SSL support, we need the openssl libraries.
|
||||
* Still we can decide whether or not to use them by commenting
|
||||
* in or out the DO_SSL macro.
|
||||
* TLS/SSL support is basically implemented by reading from a config file
|
||||
* (currently http.conf) the names of the certificate and cipher to use,
|
||||
* and then run ssl_setup() to create an appropriate SSL_CTX (ssl_ctx)
|
||||
* If we support multiple domains, presumably we need to read multiple
|
||||
* certificates.
|
||||
* When we are requested to open a TLS socket, we run make_file_from_fd()
|
||||
* on the socket, to do the necessary setup. At the moment the context's name
|
||||
* is hardwired in the function, but we can certainly make it into an extra
|
||||
* parameter to the function.
|
||||
* We declare most of ssl support variables unconditionally,
|
||||
* because their number is small and this simplifies the code.
|
||||
*
|
||||
* \note: the ssl-support variables (ssl_ctx, do_ssl, certfile, cipher)
|
||||
* and their setup should be moved to a more central place, e.g. asterisk.conf
|
||||
* and the source files that processes it. Similarly, ssl_setup() should
|
||||
* be run earlier in the startup process so modules have it available.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ASTERISK_SERVER_H
|
||||
#define _ASTERISK_SERVER_H
|
||||
|
||||
#include "asterisk/utils.h"
|
||||
|
||||
#if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
|
||||
#define DO_SSL /* comment in/out if you want to support ssl */
|
||||
#endif
|
||||
|
||||
#ifdef DO_SSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#else
|
||||
/* declare dummy types so we can define a pointer to them */
|
||||
typedef struct {} SSL;
|
||||
typedef struct {} SSL_CTX;
|
||||
#endif /* DO_SSL */
|
||||
|
||||
/*! SSL support */
|
||||
#define AST_CERTFILE "asterisk.pem"
|
||||
|
||||
enum ast_ssl_flags {
|
||||
/*! Verify certificate when acting as server */
|
||||
AST_SSL_VERIFY_CLIENT = (1 << 0),
|
||||
/*! Don't verify certificate when connecting to a server */
|
||||
AST_SSL_DONT_VERIFY_SERVER = (1 << 1),
|
||||
/*! Don't compare "Common Name" against IP or hostname */
|
||||
AST_SSL_IGNORE_COMMON_NAME = (1 << 2)
|
||||
};
|
||||
|
||||
struct ast_tls_config {
|
||||
int enabled;
|
||||
char *certfile;
|
||||
char *cipher;
|
||||
char *cafile;
|
||||
char *capath;
|
||||
struct ast_flags flags;
|
||||
SSL_CTX *ssl_ctx;
|
||||
};
|
||||
|
||||
/*!
|
||||
* The following code implements a generic mechanism for starting
|
||||
* services on a TCP or TLS socket.
|
||||
* The service is configured in the struct server_args, and
|
||||
* then started by calling server_start(desc) on the descriptor.
|
||||
* server_start() first verifies if an instance of the service is active,
|
||||
* and in case shuts it down. Then, if the service must be started, creates
|
||||
* a socket and a thread in charge of doing the accept().
|
||||
*
|
||||
* The body of the thread is desc->accept_fn(desc), which the user can define
|
||||
* freely. We supply a sample implementation, server_root(), structured as an
|
||||
* infinite loop. At the beginning of each iteration it runs periodic_fn()
|
||||
* if defined (e.g. to perform some cleanup etc.) then issues a poll()
|
||||
* or equivalent with a timeout of 'poll_timeout' milliseconds, and if the
|
||||
* following accept() is successful it creates a thread in charge of
|
||||
* running the session, whose body is desc->worker_fn(). The argument of
|
||||
* worker_fn() is a struct server_instance, which contains the address
|
||||
* of the other party, a pointer to desc, the file descriptors (fd) on which
|
||||
* we can do a select/poll (but NOT IO/, and a FILE *on which we can do I/O.
|
||||
* We have both because we want to support plain and SSL sockets, and
|
||||
* going through a FILE *lets us provide the encryption/decryption
|
||||
* on the stream without using an auxiliary thread.
|
||||
*
|
||||
* NOTE: in order to let other parts of asterisk use these services,
|
||||
* we need to do the following:
|
||||
* + move struct server_instance and struct server_args to
|
||||
* a common header file, together with prototypes for
|
||||
* server_start() and server_root().
|
||||
*/
|
||||
|
||||
/*!
|
||||
* describes a server instance
|
||||
*/
|
||||
struct server_instance {
|
||||
FILE *f; /* fopen/funopen result */
|
||||
int fd; /* the socket returned by accept() */
|
||||
SSL *ssl; /* ssl state */
|
||||
// iint (*ssl_setup)(SSL *);
|
||||
int client;
|
||||
struct sockaddr_in requestor;
|
||||
struct server_args *parent;
|
||||
};
|
||||
|
||||
/*!
|
||||
* arguments for the accepting thread
|
||||
*/
|
||||
struct server_args {
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in oldsin;
|
||||
char hostname[MAXHOSTNAMELEN]; /* only necessary for SSL clients so we can compare to common name */
|
||||
struct ast_tls_config *tls_cfg; /* points to the SSL configuration if any */
|
||||
int accept_fd;
|
||||
int poll_timeout;
|
||||
pthread_t master;
|
||||
void *(*accept_fn)(void *); /* the function in charge of doing the accept */
|
||||
void (*periodic_fn)(void *);/* something we may want to run before after select on the accept socket */
|
||||
void *(*worker_fn)(void *); /* the function in charge of doing the actual work */
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#if defined(HAVE_FUNOPEN)
|
||||
#define HOOK_T int
|
||||
#define LEN_T int
|
||||
#else
|
||||
#define HOOK_T ssize_t
|
||||
#define LEN_T size_t
|
||||
#endif
|
||||
|
||||
struct server_instance *client_start(struct server_args *desc);
|
||||
|
||||
void *server_root(void *);
|
||||
void server_start(struct server_args *desc);
|
||||
void server_stop(struct server_args *desc);
|
||||
int ssl_setup(struct ast_tls_config *cfg);
|
||||
|
||||
void *ast_make_file_from_fd(void *data);
|
||||
|
||||
HOOK_T server_read(struct server_instance *ser, void *buf, size_t count);
|
||||
HOOK_T server_write(struct server_instance *ser, void *buf, size_t count);
|
||||
|
||||
#endif /* _ASTERISK_SERVER_H */
|
@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2007 - 2008, Digium, Inc.
|
||||
*
|
||||
* Luigi Rizzo (TCP and TLS server code)
|
||||
* Brett Bryant <brettbryant@gmail.com> (updated for client support)
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief Code to support TCP and TLS server/client
|
||||
*
|
||||
* \author Luigi Rizzo
|
||||
* \author Brett Bryant <brettbryant@gmail.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <sys/signal.h>
|
||||
|
||||
#include "asterisk/compat.h"
|
||||
#include "asterisk/tcptls.h"
|
||||
#include "asterisk/http.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/strings.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/manager.h"
|
||||
|
||||
/*!
|
||||
* replacement read/write functions for SSL support.
|
||||
* We use wrappers rather than SSL_read/SSL_write directly so
|
||||
* we can put in some debugging.
|
||||
*/
|
||||
|
||||
#ifdef DO_SSL
|
||||
static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
|
||||
{
|
||||
int i = SSL_read(cookie, buf, len-1);
|
||||
#if 0
|
||||
if (i >= 0)
|
||||
buf[i] = '\0';
|
||||
ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
|
||||
#endif
|
||||
return i;
|
||||
}
|
||||
|
||||
static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
|
||||
{
|
||||
#if 0
|
||||
char *s = alloca(len+1);
|
||||
strncpy(s, buf, len);
|
||||
s[len] = '\0';
|
||||
ast_verbose("ssl write size %d <%s>\n", (int)len, s);
|
||||
#endif
|
||||
return SSL_write(cookie, buf, len);
|
||||
}
|
||||
|
||||
static int ssl_close(void *cookie)
|
||||
{
|
||||
close(SSL_get_fd(cookie));
|
||||
SSL_shutdown(cookie);
|
||||
SSL_free(cookie);
|
||||
return 0;
|
||||
}
|
||||
#endif /* DO_SSL */
|
||||
|
||||
HOOK_T server_read(struct server_instance *ser, void *buf, size_t count)
|
||||
{
|
||||
if (!ser->ssl)
|
||||
return read(ser->fd, buf, count);
|
||||
#ifdef DO_SSL
|
||||
else
|
||||
return ssl_read(ser->ssl, buf, count);
|
||||
#endif
|
||||
}
|
||||
|
||||
HOOK_T server_write(struct server_instance *ser, void *buf, size_t count)
|
||||
{
|
||||
if (!ser->ssl)
|
||||
return write(ser->fd, buf, count);
|
||||
#ifdef DO_SSL
|
||||
else
|
||||
return ssl_write(ser->ssl, buf, count);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *server_root(void *data)
|
||||
{
|
||||
struct server_args *desc = data;
|
||||
int fd;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t sinlen;
|
||||
struct server_instance *ser;
|
||||
pthread_t launched;
|
||||
|
||||
for (;;) {
|
||||
int i, flags;
|
||||
|
||||
if (desc->periodic_fn)
|
||||
desc->periodic_fn(desc);
|
||||
i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
|
||||
if (i <= 0)
|
||||
continue;
|
||||
sinlen = sizeof(sin);
|
||||
fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
|
||||
if (fd < 0) {
|
||||
if ((errno != EAGAIN) && (errno != EINTR))
|
||||
ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
ser = ast_calloc(1, sizeof(*ser));
|
||||
if (!ser) {
|
||||
ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
|
||||
ser->fd = fd;
|
||||
ser->parent = desc;
|
||||
memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
|
||||
|
||||
ser->client = 0;
|
||||
|
||||
if (ast_pthread_create_detached_background(&launched, NULL, ast_make_file_from_fd, ser)) {
|
||||
ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
|
||||
close(ser->fd);
|
||||
ast_free(ser);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __ssl_setup(struct ast_tls_config *cfg, int client)
|
||||
{
|
||||
#ifndef DO_SSL
|
||||
cfg->enabled = 0;
|
||||
return 0;
|
||||
#else
|
||||
if (!cfg->enabled)
|
||||
return 0;
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSLeay_add_ssl_algorithms();
|
||||
|
||||
if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) {
|
||||
ast_log(LOG_DEBUG, "Sorry, SSL_CTX_new call returned null...\n");
|
||||
cfg->enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
if (!ast_strlen_zero(cfg->certfile)) {
|
||||
if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
|
||||
SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
|
||||
SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) {
|
||||
if (!client) {
|
||||
/* Clients don't need a certificate, but if its setup we can use it */
|
||||
ast_verbose("ssl cert error <%s>", cfg->certfile);
|
||||
sleep(2);
|
||||
cfg->enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(cfg->cipher)) {
|
||||
if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
|
||||
if (!client) {
|
||||
ast_verbose("ssl cipher error <%s>", cfg->cipher);
|
||||
sleep(2);
|
||||
cfg->enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) {
|
||||
if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0)
|
||||
ast_verbose("ssl CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath);
|
||||
}
|
||||
|
||||
ast_verbose("ssl cert ok\n");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ssl_setup(struct ast_tls_config *cfg)
|
||||
{
|
||||
return __ssl_setup(cfg, 0);
|
||||
}
|
||||
|
||||
/*! A generic client routine for a TCP client
|
||||
* and starts a thread for handling accept()
|
||||
*/
|
||||
struct server_instance *client_start(struct server_args *desc)
|
||||
{
|
||||
int flags;
|
||||
struct server_instance *ser = NULL;
|
||||
|
||||
/* Do nothing if nothing has changed */
|
||||
if(!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
desc->oldsin = desc->sin;
|
||||
|
||||
if (desc->accept_fd != -1)
|
||||
close(desc->accept_fd);
|
||||
|
||||
desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (desc->accept_fd < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
|
||||
desc->name, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (connect(desc->accept_fd, (const struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
|
||||
ast_log(LOG_NOTICE, "Unable to connect %s to %s:%d: %s\n",
|
||||
desc->name,
|
||||
ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(ser = ast_calloc(1, sizeof(*ser))))
|
||||
goto error;
|
||||
|
||||
flags = fcntl(desc->accept_fd, F_GETFL);
|
||||
fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
|
||||
|
||||
ser->fd = desc->accept_fd;
|
||||
ser->parent = desc;
|
||||
ser->parent->worker_fn = NULL;
|
||||
memcpy(&ser->requestor, &desc->sin, sizeof(ser->requestor));
|
||||
|
||||
ser->client = 1;
|
||||
|
||||
if (desc->tls_cfg) {
|
||||
desc->tls_cfg->enabled = 1;
|
||||
__ssl_setup(desc->tls_cfg, 1);
|
||||
}
|
||||
|
||||
if (!ast_make_file_from_fd(ser))
|
||||
goto error;
|
||||
|
||||
return ser;
|
||||
|
||||
error:
|
||||
close(desc->accept_fd);
|
||||
desc->accept_fd = -1;
|
||||
if (ser)
|
||||
ast_free(ser);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This is a generic (re)start routine for a TCP server,
|
||||
* which does the socket/bind/listen and starts a thread for handling
|
||||
* accept().
|
||||
*/
|
||||
|
||||
void server_start(struct server_args *desc)
|
||||
{
|
||||
int flags;
|
||||
int x = 1;
|
||||
|
||||
/* Do nothing if nothing has changed */
|
||||
if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
|
||||
if (option_debug)
|
||||
ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
|
||||
return;
|
||||
}
|
||||
|
||||
desc->oldsin = desc->sin;
|
||||
|
||||
/* Shutdown a running server if there is one */
|
||||
if (desc->master != AST_PTHREADT_NULL) {
|
||||
pthread_cancel(desc->master);
|
||||
pthread_kill(desc->master, SIGURG);
|
||||
pthread_join(desc->master, NULL);
|
||||
}
|
||||
|
||||
if (desc->accept_fd != -1)
|
||||
close(desc->accept_fd);
|
||||
|
||||
/* If there's no new server, stop here */
|
||||
if (desc->sin.sin_family == 0)
|
||||
return;
|
||||
|
||||
desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (desc->accept_fd < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
|
||||
desc->name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
|
||||
if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
|
||||
ast_log(LOG_NOTICE, "Unable to bind %s to %s:%d: %s\n",
|
||||
desc->name,
|
||||
ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
if (listen(desc->accept_fd, 10)) {
|
||||
ast_log(LOG_NOTICE, "Unable to listen for %s!\n", desc->name);
|
||||
goto error;
|
||||
}
|
||||
flags = fcntl(desc->accept_fd, F_GETFL);
|
||||
fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
|
||||
ast_log(LOG_NOTICE, "Unable to launch %s on %s:%d: %s\n",
|
||||
desc->name,
|
||||
ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
|
||||
strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
close(desc->accept_fd);
|
||||
desc->accept_fd = -1;
|
||||
}
|
||||
|
||||
void server_stop(struct server_args *desc)
|
||||
{
|
||||
/* Shutdown a running server if there is one */
|
||||
if (desc->master != AST_PTHREADT_NULL) {
|
||||
pthread_cancel(desc->master);
|
||||
pthread_kill(desc->master, SIGURG);
|
||||
pthread_join(desc->master, NULL);
|
||||
}
|
||||
if (desc->accept_fd != -1)
|
||||
close(desc->accept_fd);
|
||||
desc->accept_fd = -1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* creates a FILE * from the fd passed by the accept thread.
|
||||
* This operation is potentially expensive (certificate verification),
|
||||
* so we do it in the child thread context.
|
||||
*/
|
||||
void *ast_make_file_from_fd(void *data)
|
||||
{
|
||||
struct server_instance *ser = data;
|
||||
int (*ssl_setup)(SSL *) = (ser->client) ? SSL_connect : SSL_accept;
|
||||
int ret;
|
||||
char err[256];
|
||||
|
||||
/*
|
||||
* open a FILE * as appropriate.
|
||||
*/
|
||||
if (!ser->parent->tls_cfg)
|
||||
ser->f = fdopen(ser->fd, "w+");
|
||||
#ifdef DO_SSL
|
||||
else if ( (ser->ssl = SSL_new(ser->parent->tls_cfg->ssl_ctx)) ) {
|
||||
SSL_set_fd(ser->ssl, ser->fd);
|
||||
if ((ret = ssl_setup(ser->ssl)) <= 0) {
|
||||
if(option_verbose > 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err));
|
||||
} else {
|
||||
#if defined(HAVE_FUNOPEN) /* the BSD interface */
|
||||
ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
|
||||
|
||||
#elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
|
||||
static const cookie_io_functions_t cookie_funcs = {
|
||||
ssl_read, ssl_write, NULL, ssl_close
|
||||
};
|
||||
ser->f = fopencookie(ser->ssl, "w+", cookie_funcs);
|
||||
#else
|
||||
/* could add other methods here */
|
||||
ast_log(LOG_WARNING, "no ser->f methods attempted!");
|
||||
#endif
|
||||
if ((ser->client && !ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER))
|
||||
|| (!ser->client && ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) {
|
||||
X509 *peer;
|
||||
long res;
|
||||
peer = SSL_get_peer_certificate(ser->ssl);
|
||||
if (!peer)
|
||||
ast_log(LOG_WARNING, "No peer certificate\n");
|
||||
res = SSL_get_verify_result(ser->ssl);
|
||||
if (res != X509_V_OK)
|
||||
ast_log(LOG_WARNING, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res));
|
||||
if (!ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
|
||||
ASN1_STRING *str;
|
||||
unsigned char *str2;
|
||||
X509_NAME *name = X509_get_subject_name(peer);
|
||||
int pos = -1;
|
||||
int found = 0;
|
||||
|
||||
for (;;) {
|
||||
/* Walk the certificate to check all available "Common Name" */
|
||||
/* XXX Probably should do a gethostbyname on the hostname and compare that as well */
|
||||
pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos);
|
||||
if (pos < 0)
|
||||
break;
|
||||
str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
|
||||
ASN1_STRING_to_UTF8(&str2, str);
|
||||
if (str2) {
|
||||
if (!strcasecmp(ser->parent->hostname, (char *) str2))
|
||||
found = 1;
|
||||
ast_log(LOG_DEBUG, "SSL Common Name compare s1='%s' s2='%s'\n", ser->parent->hostname, str2);
|
||||
OPENSSL_free(str2);
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
ast_log(LOG_WARNING, "Certificate common name did not match (%s)\n", ser->parent->hostname);
|
||||
if (peer)
|
||||
X509_free(peer);
|
||||
fclose(ser->f);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (peer)
|
||||
X509_free(peer);
|
||||
}
|
||||
}
|
||||
if (!ser->f) /* no success opening descriptor stacking */
|
||||
SSL_free(ser->ssl);
|
||||
}
|
||||
#endif /* DO_SSL */
|
||||
|
||||
if (!ser->f) {
|
||||
close(ser->fd);
|
||||
ast_log(LOG_WARNING, "FILE * open failed!\n");
|
||||
ast_free(ser);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ser && ser->parent->worker_fn)
|
||||
return ser->parent->worker_fn(ser);
|
||||
else
|
||||
return ser;
|
||||
}
|
Loading…
Reference in new issue