Merge changes from team/group/sip-tcptls
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: http://svn.digium.com/svn/asterisk/trunk@99085 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
cc1fcc7539
commit
d6e19bdc91
5
CHANGES
5
CHANGES
|
@ -128,6 +128,11 @@ SIP changes
|
||||||
SIP uri.
|
SIP uri.
|
||||||
* Added a new global and per-peer option, qualifyfreq, which allows you to configure
|
* Added a new global and per-peer option, qualifyfreq, which allows you to configure
|
||||||
the qualify frequency.
|
the qualify frequency.
|
||||||
|
* Added SIP Session Timers support (RFC 4028). This prevents stuck SIP sessions that
|
||||||
|
were not properly torn down due to network or endpoint failures during an established
|
||||||
|
SIP session.
|
||||||
|
* Added TCP and TLS support for SIP. See doc/siptls.txt and configs/sip.conf.sample for
|
||||||
|
more information on how it is used.
|
||||||
|
|
||||||
IAX2 changes
|
IAX2 changes
|
||||||
------------
|
------------
|
||||||
|
|
7
CREDITS
7
CREDITS
|
@ -56,7 +56,7 @@ Anthony Minessale II - Countless big and small fixes, and relentless forward
|
||||||
and sip configs.
|
and sip configs.
|
||||||
anthmct(AT)yahoo.com http://www.asterlink.com
|
anthmct(AT)yahoo.com http://www.asterlink.com
|
||||||
|
|
||||||
James Golovich - Innumerable contributions
|
James Golovich - Innumerable contributions, including SIP TCP and TLS support.
|
||||||
You can find him and asterisk-perl at http://asterisk.gnuinter.net
|
You can find him and asterisk-perl at http://asterisk.gnuinter.net
|
||||||
|
|
||||||
Andre Bierwirth - Extension hints and status
|
Andre Bierwirth - Extension hints and status
|
||||||
|
@ -176,6 +176,11 @@ Marta Carbone - console_video and the astobj2 framework
|
||||||
Luigi Rizzo - astobj2, console_video, windows build, chan_oss cleanup,
|
Luigi Rizzo - astobj2, console_video, windows build, chan_oss cleanup,
|
||||||
and a bunch of infrastructure work (loader, new_cli, ...)
|
and a bunch of infrastructure work (loader, new_cli, ...)
|
||||||
|
|
||||||
|
Brett Bryant - digit option for musiconhold selection, ENUMQUERY and ENUMRESULT functions,
|
||||||
|
feature group configuration for features.conf, per-file CLI debug and verbose settings,
|
||||||
|
TCP and TLS support for SIP, and various bug fixes.
|
||||||
|
brettbryant(AT)gmail.com
|
||||||
|
|
||||||
=== OTHER CONTRIBUTIONS ===
|
=== OTHER CONTRIBUTIONS ===
|
||||||
John Todd - Monkey sounds and associated teletorture prompt
|
John Todd - Monkey sounds and associated teletorture prompt
|
||||||
Michael Jerris - bug marshaling
|
Michael Jerris - bug marshaling
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -70,6 +70,16 @@ allowoverlap=no ; Disable overlap dialing support. (Default is yes)
|
||||||
bindport=5060 ; UDP Port to bind to (SIP standard port is 5060)
|
bindport=5060 ; UDP Port to bind to (SIP standard port is 5060)
|
||||||
; bindport is the local UDP port that Asterisk will listen on
|
; bindport is the local UDP port that Asterisk will listen on
|
||||||
bindaddr=0.0.0.0 ; IP address to bind to (0.0.0.0 binds to all)
|
bindaddr=0.0.0.0 ; IP address to bind to (0.0.0.0 binds to all)
|
||||||
|
|
||||||
|
tcpenable=yes ; Enable server for incoming TCP connections (default is yes)
|
||||||
|
tcpbindaddr=0.0.0.0 ; IP address for TCP server to bind to (0.0.0.0 binds to all interfaces)
|
||||||
|
; Optionally add a port number, 192.168.1.1:5062 (default is port 5060)
|
||||||
|
|
||||||
|
;tlsenable=no ; Enable server for incoming TLS (secure) connections (default is no)
|
||||||
|
;tlsbindaddr=0.0.0.0 ; IP address for TLS server to bind to (0.0.0.0) binds to all interfaces)
|
||||||
|
; Optionally add a port number, 192.168.1.1:5063 (default is port 5061)
|
||||||
|
;tlscertfile=asterisk.pem ; Certificate file (*.pem only) to use for TLS connections
|
||||||
|
; default is to look for "asterisk.pem" in current directory
|
||||||
srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
||||||
; Note: Asterisk only uses the first host
|
; Note: Asterisk only uses the first host
|
||||||
; in SRV records
|
; in SRV records
|
||||||
|
@ -320,7 +330,9 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
||||||
;----------------------------------------- OUTBOUND SIP REGISTRATIONS ------------------------
|
;----------------------------------------- OUTBOUND SIP REGISTRATIONS ------------------------
|
||||||
; Asterisk can register as a SIP user agent to a SIP proxy (provider)
|
; Asterisk can register as a SIP user agent to a SIP proxy (provider)
|
||||||
; Format for the register statement is:
|
; Format for the register statement is:
|
||||||
; register => user[:secret[:authuser]]@host[:port][/extension]
|
; register => [transport://]user[:secret[:authuser]]@host[:port][/extension]
|
||||||
|
;
|
||||||
|
;
|
||||||
;
|
;
|
||||||
; If no extension is given, the 's' extension is used. The extension needs to
|
; If no extension is given, the 's' extension is used. The extension needs to
|
||||||
; be defined in extensions.conf to be able to accept calls from this SIP proxy
|
; be defined in extensions.conf to be able to accept calls from this SIP proxy
|
||||||
|
@ -607,7 +619,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
||||||
; User config options: Peer configuration:
|
; User config options: Peer configuration:
|
||||||
; -------------------- -------------------
|
; -------------------- -------------------
|
||||||
; context context
|
; context context
|
||||||
; callingpres callingpres
|
; callingpres callingpres
|
||||||
; permit permit
|
; permit permit
|
||||||
; deny deny
|
; deny deny
|
||||||
; secret secret
|
; secret secret
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#define _ASTERISK_HTTP_H
|
#define _ASTERISK_HTTP_H
|
||||||
|
|
||||||
#include "asterisk/config.h"
|
#include "asterisk/config.h"
|
||||||
|
#include "asterisk/tcptls.h"
|
||||||
|
#include "asterisk/linkedlists.h"
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \file http.h
|
* \file http.h
|
||||||
|
@ -50,90 +52,6 @@
|
||||||
* be run earlier in the startup process so modules have it available.
|
* be run earlier in the startup process so modules have it available.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#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"
|
|
||||||
|
|
||||||
struct tls_config {
|
|
||||||
int enabled;
|
|
||||||
char *certfile;
|
|
||||||
char *cipher;
|
|
||||||
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 */
|
|
||||||
struct sockaddr_in requestor;
|
|
||||||
struct server_args *parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* arguments for the accepting thread
|
|
||||||
*/
|
|
||||||
struct server_args {
|
|
||||||
struct sockaddr_in sin;
|
|
||||||
struct sockaddr_in oldsin;
|
|
||||||
struct 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
void *server_root(void *);
|
|
||||||
void server_start(struct server_args *desc);
|
|
||||||
int ssl_setup(struct tls_config *cfg);
|
|
||||||
|
|
||||||
/*! \brief HTTP Callbacks take the socket
|
/*! \brief HTTP Callbacks take the socket
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
|
@ -19,7 +19,7 @@ include $(ASTTOPDIR)/Makefile.moddir_rules
|
||||||
|
|
||||||
RESAMPLE_OBJS:=libresample/src/resample.o libresample/src/resamplesubs.o libresample/src/filterkit.o
|
RESAMPLE_OBJS:=libresample/src/resample.o libresample/src/resamplesubs.o libresample/src/filterkit.o
|
||||||
|
|
||||||
OBJS= io.o sched.o logger.o frame.o loader.o config.o channel.o \
|
OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
|
||||||
translate.o file.o pbx.o cli.o md5.o term.o \
|
translate.o file.o pbx.o cli.o md5.o term.o \
|
||||||
ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
|
ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
|
||||||
cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \
|
cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \
|
||||||
|
|
200
main/http.c
200
main/http.c
|
@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||||
#include "minimime/mm.h"
|
#include "minimime/mm.h"
|
||||||
|
|
||||||
#include "asterisk/cli.h"
|
#include "asterisk/cli.h"
|
||||||
|
#include "asterisk/tcptls.h"
|
||||||
#include "asterisk/http.h"
|
#include "asterisk/http.h"
|
||||||
#include "asterisk/utils.h"
|
#include "asterisk/utils.h"
|
||||||
#include "asterisk/strings.h"
|
#include "asterisk/strings.h"
|
||||||
|
@ -59,7 +60,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||||
#define DO_SSL /* comment in/out if you want to support ssl */
|
#define DO_SSL /* comment in/out if you want to support ssl */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct tls_config http_tls_cfg;
|
static struct ast_tls_config http_tls_cfg;
|
||||||
|
|
||||||
static void *httpd_helper_thread(void *arg);
|
static void *httpd_helper_thread(void *arg);
|
||||||
|
|
||||||
|
@ -647,7 +648,7 @@ cleanup:
|
||||||
* We use wrappers rather than SSL_read/SSL_write directly so
|
* We use wrappers rather than SSL_read/SSL_write directly so
|
||||||
* we can put in some debugging.
|
* we can put in some debugging.
|
||||||
*/
|
*/
|
||||||
static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
|
/*static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
|
||||||
{
|
{
|
||||||
int i = SSL_read(cookie, buf, len-1);
|
int i = SSL_read(cookie, buf, len-1);
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -675,55 +676,9 @@ static int ssl_close(void *cookie)
|
||||||
SSL_shutdown(cookie);
|
SSL_shutdown(cookie);
|
||||||
SSL_free(cookie);
|
SSL_free(cookie);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}*/
|
||||||
#endif /* DO_SSL */
|
#endif /* DO_SSL */
|
||||||
|
|
||||||
/*!
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
static void *make_file_from_fd(void *data)
|
|
||||||
{
|
|
||||||
struct server_instance *ser = data;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 (SSL_accept(ser->ssl) == 0)
|
|
||||||
ast_verbose(" error setting up ssl connection");
|
|
||||||
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 */
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return ser->parent->worker_fn(ser);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *httpd_helper_thread(void *data)
|
static void *httpd_helper_thread(void *data)
|
||||||
{
|
{
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
@ -876,153 +831,6 @@ done:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
if (ast_pthread_create_detached_background(&launched, NULL, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ssl_setup(struct tls_config *cfg)
|
|
||||||
{
|
|
||||||
#ifndef DO_SSL
|
|
||||||
cfg->enabled = 0;
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
if (!cfg->enabled)
|
|
||||||
return 0;
|
|
||||||
SSL_load_error_strings();
|
|
||||||
SSLeay_add_ssl_algorithms();
|
|
||||||
cfg->ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
|
|
||||||
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 ) {
|
|
||||||
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 ) {
|
|
||||||
ast_verbose("ssl cipher error <%s>", cfg->cipher);
|
|
||||||
sleep(2);
|
|
||||||
cfg->enabled = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast_verbose("ssl cert ok");
|
|
||||||
return 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* 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))) {
|
|
||||||
ast_debug(1, "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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Add a new URI redirect
|
* \brief Add a new URI redirect
|
||||||
* The entries in the redirect list are sorted by length, just like the list
|
* The entries in the redirect list are sorted by length, just like the list
|
||||||
|
|
|
@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||||
#include "asterisk/md5.h"
|
#include "asterisk/md5.h"
|
||||||
#include "asterisk/acl.h"
|
#include "asterisk/acl.h"
|
||||||
#include "asterisk/utils.h"
|
#include "asterisk/utils.h"
|
||||||
|
#include "asterisk/tcptls.h"
|
||||||
#include "asterisk/http.h"
|
#include "asterisk/http.h"
|
||||||
#include "asterisk/version.h"
|
#include "asterisk/version.h"
|
||||||
#include "asterisk/threadstorage.h"
|
#include "asterisk/threadstorage.h"
|
||||||
|
@ -3425,7 +3426,7 @@ static void purge_old_stuff(void *data)
|
||||||
purge_events();
|
purge_events();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tls_config ami_tls_cfg;
|
struct ast_tls_config ami_tls_cfg;
|
||||||
static struct server_args ami_desc = {
|
static struct server_args ami_desc = {
|
||||||
.accept_fd = -1,
|
.accept_fd = -1,
|
||||||
.master = AST_PTHREADT_NULL,
|
.master = AST_PTHREADT_NULL,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Reference in New Issue