4141
4242#include "include/cami.h"
4343
44+ #ifdef HAVE_OPENSSL
45+ #include <openssl/opensslv.h>
46+ #include <openssl/bio.h>
47+ #include <openssl/ssl.h>
48+ #include <openssl/err.h>
49+ static char root_certs [84 ] = "/etc/ssl/certs/ca-certificates.crt" ; /* Path on Debian */
50+ #endif
51+
4452/*! \brief Default Asterisk Manager Interface port is 5038 */
45- #define AMI_PORT 5038
53+ #define AMI_PORT_UNENCRYPTED 5038
54+ #define AMI_PORT_ENCRYPTED 5039
4655/*! \brief This is the finale to any message exchange. */
4756#define AMI_EOM "\r\n\r\n"
4857
6069}
6170
6271#define ami_warning (ami , fmt , ...) ami_debug(ami, 1, "WARNING: " fmt, ## __VA_ARGS__)
63- #define ami_error (ami , fmt , ...) ami_debug(ami, 1, "WARNING : " fmt, ## __VA_ARGS__)
72+ #define ami_error (ami , fmt , ...) ami_debug(ami, 1, "ERROR : " fmt, ## __VA_ARGS__)
6473
6574#define strlen_zero (s ) ((!s || *s == '\0'))
6675#define ltrim (s ) while (isspace(*s)) s++;
@@ -81,6 +90,9 @@ struct ami_session {
8190 struct ami_response * current_response ;
8291 int ami_rfd ; /* Read file descriptor (typically socket fd) */
8392 int ami_wfd ; /* Write file descriptor (typically socket fd) */
93+ #ifdef HAVE_OPENSSL
94+ SSL * ssl ;
95+ #endif
8496 int debugfd ;
8597 int debug_level ;
8698 int ami_pipe [2 ]; /* Pipe for sending actions to Asterisk */
@@ -157,6 +169,13 @@ static void ami_cleanup(struct ami_session *ami)
157169 ami -> ami_wfd = -1 ;
158170 }
159171
172+ #ifdef HAVE_OPENSSL
173+ if (ami -> ssl ) {
174+ SSL_shutdown (ami -> ssl );
175+ SSL_free (ami -> ssl );
176+ }
177+ #endif
178+
160179 ami -> loggedin = 0 ;
161180 ami -> tx = ami -> rx = 0 ;
162181 if (ami -> current_response ) {
@@ -165,6 +184,91 @@ static void ami_cleanup(struct ami_session *ami)
165184 }
166185}
167186
187+ #ifdef HAVE_OPENSSL
188+ static int start_tls (struct ami_session * ami , const char * hostname )
189+ {
190+ SSL_CTX * ctx ;
191+ X509 * server_cert ;
192+ long verify_result ;
193+ char * str ;
194+
195+ OpenSSL_add_ssl_algorithms ();
196+ SSL_load_error_strings ();
197+
198+ ctx = SSL_CTX_new (TLS_client_method ());
199+ if (!ctx ) {
200+ ami_error (ami , "Failed to setup new SSL context\n" );
201+ return -1 ;
202+ }
203+
204+ ami -> ssl = SSL_new (ctx );
205+ if (!ami -> ssl ) {
206+ ami_error (ami , "Failed to create new SSL\n" );
207+ SSL_CTX_free (ctx );
208+ return -1 ;
209+ }
210+
211+ if (SSL_set_fd (ami -> ssl , ami -> ami_rfd ) != 1 ) {
212+ ami_error (ami , "Failed to connect SSL: %s\n" , ERR_error_string (ERR_get_error (), NULL ));
213+ goto sslcleanup ;
214+ }
215+
216+ SSL_CTX_set_verify (ctx , SSL_VERIFY_PEER , NULL );
217+ SSL_CTX_set_verify_depth (ctx , 4 );
218+ if (SSL_CTX_load_verify_locations (ctx , root_certs , NULL ) != 1 ) {
219+ ami_error (ami , "Failed to load root certs from %s: %s\n" , root_certs , ERR_error_string (ERR_get_error (), NULL ));
220+ goto sslcleanup ;
221+ }
222+
223+ /* SNI */
224+ if (SSL_set_tlsext_host_name (ami -> ssl , hostname ) != 1 ) {
225+ ami_warning (ami , "Failed to set SNI for TLS connection\n" );
226+ }
227+
228+ if (SSL_connect (ami -> ssl ) == -1 ) {
229+ ami_error (ami , "Failed to connect SSL: %s\n" , ERR_error_string (ERR_get_error (), NULL ));
230+ }
231+
232+ /* Verify cert */
233+ #if defined(OPENSSL_VERSION_MAJOR ) && OPENSSL_VERSION_MAJOR >= 3
234+ server_cert = SSL_get1_peer_certificate (ami -> ssl );
235+ #else
236+ server_cert = SSL_get_peer_certificate (ami -> ssl );
237+ #endif
238+ if (!server_cert ) {
239+ ami_error (ami , "Failed to get peer certificate\n" );
240+ goto sslcleanup ;
241+ }
242+ str = X509_NAME_oneline (X509_get_subject_name (server_cert ), 0 , 0 );
243+ if (!str ) {
244+ ami_error (ami , "Failed to get peer certificate\n" );
245+ goto sslcleanup ;
246+ }
247+ OPENSSL_free (str );
248+ str = X509_NAME_oneline (X509_get_issuer_name (server_cert ), 0 , 0 );
249+ if (!str ) {
250+ ami_error (ami , "Failed to get peer certificate\n" );
251+ goto sslcleanup ;
252+ }
253+ OPENSSL_free (str );
254+ X509_free (server_cert );
255+ verify_result = SSL_get_verify_result (ami -> ssl );
256+ if (verify_result != X509_V_OK ) {
257+ ami_warning (ami , "SSL verify failed: %ld (%s)\n" , verify_result , X509_verify_cert_error_string (verify_result ));
258+ goto sslcleanup ;
259+ }
260+
261+ SSL_CTX_free (ctx ); /* ctx will still have a ref count of 1 after this, to be cleaned up in ssl_close by SSL_free */
262+ return 0 ;
263+
264+ sslcleanup :
265+ SSL_CTX_free (ctx );
266+ SSL_free (ami -> ssl );
267+ ami -> ssl = NULL ;
268+ return -1 ;
269+ }
270+ #endif /* HAVE_OPENSSL */
271+
168272static void * ami_loop (void * varg )
169273{
170274 int res , got_id = 0 , response_pending = 0 , event_pending = 0 ;
@@ -200,6 +304,11 @@ static void *ami_loop(void *varg)
200304 }
201305 /* Data from AMI to deliver to consumer? */
202306 if (fds [0 ].revents ) {
307+ #ifdef HAVE_OPENSSL
308+ if (ami -> ssl ) {
309+ res = SSL_read (ami -> ssl , readinbuf , AMI_BUFFER_SIZE - 2 - (readinbuf - inbuf ));
310+ } else
311+ #endif
203312 res = read (ami -> ami_rfd , readinbuf , AMI_BUFFER_SIZE - 2 - (readinbuf - inbuf ));
204313 if (res < 1 ) {
205314 if (res < 0 ) {
@@ -356,6 +465,11 @@ static void *ami_loop(void *varg)
356465 ami_debug (ami , 1 , "read returned %d\n" , res );
357466 break ;
358467 }
468+ #ifdef HAVE_OPENSSL
469+ if (ami -> ssl ) {
470+ res = SSL_write (ami -> ssl , outbuf , res );
471+ } else
472+ #endif
359473 res = write (ami -> ami_wfd , outbuf , res );
360474 if (res < 1 ) {
361475 ami_warning (ami , "write(%d) returned %d: %s\n" , ami -> ami_wfd , res , strerror (errno ));
@@ -450,7 +564,7 @@ struct ami_session *ami_session_from_fd(int rfd, int wfd, void (*callback)(struc
450564 return ami ;
451565}
452566
453- struct ami_session * ami_connect (const char * hostname , int port , void (* callback )(struct ami_session * ami , struct ami_event * event ), void (* dis_callback )(struct ami_session * ami ))
567+ static struct ami_session * _ami_connect (const char * hostname , int port , int tls , void (* callback )(struct ami_session * ami , struct ami_event * event ), void (* dis_callback )(struct ami_session * ami ))
454568{
455569 int fd ;
456570 struct sockaddr_in saddr ;
@@ -463,10 +577,10 @@ struct ami_session *ami_connect(const char *hostname, int port, void (*callback)
463577
464578 memset (& saddr , 0 , sizeof (saddr ));
465579 if (!port ) {
466- port = AMI_PORT ;
580+ port = tls ? AMI_PORT_ENCRYPTED : AMI_PORT_UNENCRYPTED ;
467581 }
468582
469- ami_debug (ami , 1 , "Initiating AMI connection on port % d\n" , port );
583+ ami_debug (ami , 1 , "Initiating AMI connection to %s://%s:% d\n" , tls ? "tls" : "tcp" , hostname , port );
470584
471585 fd = socket (AF_INET , SOCK_STREAM , 0 );
472586 if (fd < 0 ) {
@@ -519,7 +633,6 @@ struct ami_session *ami_connect(const char *hostname, int port, void (*callback)
519633 ami_cleanup (ami );
520634 goto cleanup ;
521635 }
522-
523636 freeaddrinfo (res );
524637 } else {
525638 ami_error (ami , "host %s not valid\n" , hostname );
@@ -530,6 +643,16 @@ struct ami_session *ami_connect(const char *hostname, int port, void (*callback)
530643
531644 ami -> ami_callback = callback ;
532645 ami -> disconnected_callback = dis_callback ;
646+
647+ #ifdef HAVE_OPENSSL
648+ /* Need to start SSL before launching threads and such */
649+ if (tls && start_tls (ami , hostname )) {
650+ ami_error (ami , "Failed to set up TLS\n" );
651+ ami_cleanup (ami );
652+ goto cleanup ;
653+ }
654+ #endif
655+
533656 if (common_init (ami )) {
534657 ami_cleanup (ami );
535658 goto cleanup ;
@@ -542,6 +665,21 @@ struct ami_session *ami_connect(const char *hostname, int port, void (*callback)
542665 return NULL ;
543666}
544667
668+ struct ami_session * ami_connect (const char * hostname , int port , void (* callback )(struct ami_session * ami , struct ami_event * event ), void (* dis_callback )(struct ami_session * ami ))
669+ {
670+ return _ami_connect (hostname , port , 0 , callback , dis_callback );
671+ }
672+
673+ struct ami_session * ami_ssl_connect (const char * hostname , int port , void (* callback )(struct ami_session * ami , struct ami_event * event ), void (* dis_callback )(struct ami_session * ami ))
674+ {
675+ #ifdef HAVE_OPENSSL
676+ return _ami_connect (hostname , port , 1 , callback , dis_callback );
677+ #else
678+ errno = EPROTONOSUPPORT ;
679+ return NULL ;
680+ #endif
681+ }
682+
545683void ami_set_callback_data (struct ami_session * ami , void * data )
546684{
547685 ami -> cb_data = data ;
0 commit comments