Asterisk Summary - TLS support for HTTP, AMI and more

Note: Luigi Rizzo posted this on the Asterisk Dev list.  Some of our readers should get some value out of this.

A few days ago i asked about certificate negotiation in asterisk.

On a related topic, i have been thinking for a while on how to provide TLS support for HTTP, AMI and more services within asterisk, keeping in mind the current state of affairs, the feedback i received on the above request, and with the goal of minimizing changes to the current code base.  I think I am at a stage where i have no more ideas, so attached you find:

- a description of the current situation with respect to the implementation of TLS support for HTTP and AMI;
- a workplan on how to add TLS support to AMI,
- general considerations on how to provide TLS TCP sockets.

Please let me know if you have better ideas on the above, but be careful that "the devil is in the details" (e.g. we still needs a poll()-able fd for the TLS sockets), and that we are a bit constrained by backward compatibility issues so e.g. changing internal interfaces such as the one for CLI commands is not really feasible without widespread changes to the code.

Note that the relevant code (currently in main/http.c, possibly to be relocated elsewhere) is designed to be as independent as possible from the specific service. So its functionalities can be reused
(hopefully without modifications) wherever we need TLS TCP sockets.

Feedback welcome.

        cheers
        luigi

--- CURRENT STATE - AMI ---

As it is now, the AMI code does I/O on the level-2 file descriptor returned by accept() (or created in generic_http_callback() in the case of AMI-over-HTTP).  On the input side, the code loops around the function get_input() interpreting a returns of 1 as "full line available" and 0 as "possibly an interrupt arrived".

On the output side, with only one exception, all I/O is performed in the function send_string(), called by either astman_append(), or by (internally) by process_events(). The routine tries to write
a buffer to the file descriptor with a bounded timeout.  (in fact, the way it is written, ast_carefulwrite() does not give any guarantee). In order for the above to work, the socket must be in non-blocking
mode (block-sockets=false in manager.conf).

The exception, on the output side, is the function action_command(), which runs a CLI command over AMI. In this case the file descriptor is passed directly to ast_cli_command() so there is no control on the way I/O is performed.
  
The input part loops around get_input() to read one line at a time from the socketinput() to read one line at a time from the socket. get_input() is also expected to return 0 (and an empty buffer) when a signal is sent to the thread, typically because there is a new event to be processed.

--- CURRENT STATE - HTTP ---

The code in main/http.c has full https support. This is relatively straightforward because all I/O is done using a FILE * descriptor. The latter is obtained by just using fdopen() on the socket returned by accept() in case of plain http requests, or by using funopen() or fopencookie() (depending on
which one is available) to install handlers to encrypt/decrypt the data.

The routines to set up the tls session are generalized in a way that they can be made globally visible and used by other modules.

--- ADDING TLS SUPPORT TO AMI ---
In my opinion the way to go for adding TLS support to the manager interface is the same used for http, i.e.

  1. change the code to use a FILE * instead of a level-2 file descriptor

  2. on setup, call the function ssl_setup() (from main/http.c) to establish the encrypted connection.

#2 is trivial - it basically requires to change from static to globally visible the TLS/SSL support functions/variables currently in http.c

#1 is mostly trivial too, and requires the following steps.

a) after the accept, make sure that make_file_from_fd() (from http.c) is called on the socket to create a proper FILE * for plain or encrypted sessions;

b) change the function get_input() to use fread() instead of read() to collect the data. One can still do the ast_wait_for_input() on the original descriptor returned by accept().

c) change the function send_string() to work on the FILE *.
   This is also relatively straightforward, especially given that a rewrite is necessary anyways because ast_carefulwrite() does not give the guarantees we want.

d) modify the function action_command() so that it creates a temporary file descriptor to be passed to ast_cli_command(), and then read back the data from the temp file and write it to the output with send_string(). The code is similar to what is done in generic_http_callback() to support AMI-over-HTTP.

--- GENERIC TLS TCP SOCKETS ---

Other applications needing (server) TLS sockets should likely be able to reuse most of the functions in main/http.c, namely server_start() and server_root(), which take charge of killing any old instance of the service on the same port, creating a new thread in charge of doing the accept(), and then
creating new threads in charge of the certificate negotiation and I/O for the new session.

For client TLS sockets, there is not much support, but all should be needed after the conventional socket()/[bind()/]connect() is just a call to make_file_from_fd() to set up a proper FILE *.

We can provide a wrapper for the above if there is a need (i don't know if any other modules currently use client TLS sockets).

--- SERVER CERTIFICATE ---

At the moment, the code that reads the server's certificate is within function ssl_setup(), and the code above is written assuming only one certificate for the whole server, accessible from the
variable ssl_ctx. It is not difficult to let each service use a different certificate. As pointed out by Klaus Darilion, let the server pick the certificate depending on the request (useful for the equivalent of 'virtual domains') requires some additional features ("server name" TLS extension) which i
don't know how to implement.

--- THE END ---

« ChanSkype 1.2.6 Released | Main | Intelliverse Improves VoIP Fax Transmissions »

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

Powered by: Dal