From 7c1326aa14ad651ff916b8ef35fa08d2dc90a3a7 Mon Sep 17 00:00:00 2001 From: S-P Chan Date: Tue, 14 Jun 2022 14:47:32 +0800 Subject: [PATCH] tls_wolfssl: new module TLS stack based on wolfSSL Initial support. Use OpenSSL-compatiblity layer to achieve compilation. --- src/modules/tls_wolfssl/Makefile | 81 + src/modules/tls_wolfssl/README | 1713 +++++++++++++++++++ src/modules/tls_wolfssl/TODO.md | 7 + src/modules/tls_wolfssl/doc/Makefile | 4 + src/modules/tls_wolfssl/doc/certs_howto.xml | 154 ++ src/modules/tls_wolfssl/doc/functions.xml | 63 + src/modules/tls_wolfssl/doc/history.xml | 38 + src/modules/tls_wolfssl/doc/hsm_howto.xml | 64 + src/modules/tls_wolfssl/doc/params.xml | 1410 +++++++++++++++ src/modules/tls_wolfssl/doc/rpc.xml | 69 + src/modules/tls_wolfssl/doc/tls.xml | 367 ++++ src/modules/tls_wolfssl/sbufq.h | 283 +++ src/modules/tls_wolfssl/tls.cfg | 106 ++ src/modules/tls_wolfssl/tls_bio.c | 314 ++++ src/modules/tls_wolfssl/tls_bio.h | 68 + src/modules/tls_wolfssl/tls_cert.sh | 201 +++ src/modules/tls_wolfssl/tls_cfg.c | 289 ++++ src/modules/tls_wolfssl/tls_cfg.h | 111 ++ src/modules/tls_wolfssl/tls_config.c | 536 ++++++ src/modules/tls_wolfssl/tls_config.h | 47 + src/modules/tls_wolfssl/tls_ct_q.h | 133 ++ src/modules/tls_wolfssl/tls_ct_wrq.c | 203 +++ src/modules/tls_wolfssl/tls_ct_wrq.h | 98 ++ src/modules/tls_wolfssl/tls_domain.c | 1433 ++++++++++++++++ src/modules/tls_wolfssl/tls_domain.h | 238 +++ src/modules/tls_wolfssl/tls_dump_vf.c | 150 ++ src/modules/tls_wolfssl/tls_dump_vf.h | 41 + src/modules/tls_wolfssl/tls_init.c | 583 +++++++ src/modules/tls_wolfssl/tls_init.h | 85 + src/modules/tls_wolfssl/tls_map.c | 195 +++ src/modules/tls_wolfssl/tls_map.h | 77 + src/modules/tls_wolfssl/tls_mod.c | 602 +++++++ src/modules/tls_wolfssl/tls_mod.h | 45 + src/modules/tls_wolfssl/tls_rand.c | 187 ++ src/modules/tls_wolfssl/tls_rand.h | 30 + src/modules/tls_wolfssl/tls_rpc.c | 263 +++ src/modules/tls_wolfssl/tls_rpc.h | 33 + src/modules/tls_wolfssl/tls_select.c | 1707 ++++++++++++++++++ src/modules/tls_wolfssl/tls_select.h | 52 + src/modules/tls_wolfssl/tls_server.c | 1539 +++++++++++++++++ src/modules/tls_wolfssl/tls_server.h | 101 ++ src/modules/tls_wolfssl/tls_util.c | 99 ++ src/modules/tls_wolfssl/tls_util.h | 87 + src/modules/tls_wolfssl/tls_verify.c | 135 ++ src/modules/tls_wolfssl/tls_verify.h | 42 + src/modules/tls_wolfssl/todo.txt | 4 + 46 files changed, 14087 insertions(+) create mode 100644 src/modules/tls_wolfssl/Makefile create mode 100644 src/modules/tls_wolfssl/README create mode 100644 src/modules/tls_wolfssl/TODO.md create mode 100644 src/modules/tls_wolfssl/doc/Makefile create mode 100644 src/modules/tls_wolfssl/doc/certs_howto.xml create mode 100644 src/modules/tls_wolfssl/doc/functions.xml create mode 100644 src/modules/tls_wolfssl/doc/history.xml create mode 100644 src/modules/tls_wolfssl/doc/hsm_howto.xml create mode 100644 src/modules/tls_wolfssl/doc/params.xml create mode 100644 src/modules/tls_wolfssl/doc/rpc.xml create mode 100644 src/modules/tls_wolfssl/doc/tls.xml create mode 100644 src/modules/tls_wolfssl/sbufq.h create mode 100644 src/modules/tls_wolfssl/tls.cfg create mode 100644 src/modules/tls_wolfssl/tls_bio.c create mode 100644 src/modules/tls_wolfssl/tls_bio.h create mode 100755 src/modules/tls_wolfssl/tls_cert.sh create mode 100644 src/modules/tls_wolfssl/tls_cfg.c create mode 100644 src/modules/tls_wolfssl/tls_cfg.h create mode 100644 src/modules/tls_wolfssl/tls_config.c create mode 100644 src/modules/tls_wolfssl/tls_config.h create mode 100644 src/modules/tls_wolfssl/tls_ct_q.h create mode 100644 src/modules/tls_wolfssl/tls_ct_wrq.c create mode 100644 src/modules/tls_wolfssl/tls_ct_wrq.h create mode 100644 src/modules/tls_wolfssl/tls_domain.c create mode 100644 src/modules/tls_wolfssl/tls_domain.h create mode 100644 src/modules/tls_wolfssl/tls_dump_vf.c create mode 100644 src/modules/tls_wolfssl/tls_dump_vf.h create mode 100644 src/modules/tls_wolfssl/tls_init.c create mode 100644 src/modules/tls_wolfssl/tls_init.h create mode 100644 src/modules/tls_wolfssl/tls_map.c create mode 100644 src/modules/tls_wolfssl/tls_map.h create mode 100644 src/modules/tls_wolfssl/tls_mod.c create mode 100644 src/modules/tls_wolfssl/tls_mod.h create mode 100644 src/modules/tls_wolfssl/tls_rand.c create mode 100644 src/modules/tls_wolfssl/tls_rand.h create mode 100644 src/modules/tls_wolfssl/tls_rpc.c create mode 100644 src/modules/tls_wolfssl/tls_rpc.h create mode 100644 src/modules/tls_wolfssl/tls_select.c create mode 100644 src/modules/tls_wolfssl/tls_select.h create mode 100644 src/modules/tls_wolfssl/tls_server.c create mode 100644 src/modules/tls_wolfssl/tls_server.h create mode 100644 src/modules/tls_wolfssl/tls_util.c create mode 100644 src/modules/tls_wolfssl/tls_util.h create mode 100644 src/modules/tls_wolfssl/tls_verify.c create mode 100644 src/modules/tls_wolfssl/tls_verify.h create mode 100644 src/modules/tls_wolfssl/todo.txt diff --git a/src/modules/tls_wolfssl/Makefile b/src/modules/tls_wolfssl/Makefile new file mode 100644 index 00000000000..1238319597d --- /dev/null +++ b/src/modules/tls_wolfssl/Makefile @@ -0,0 +1,81 @@ +# +# TLS module makefile +# +# +# WARNING: do not run this directly, it should be run by the main Makefile + +include ../../Makefile.defs +auto_gen= +NAME=tls_wolfssl.so + +# set to yes when wanting to link with static libraries +LIBSSL_STATIC ?= no +# set to yes when wanting to link with static libraries compiled from source +LIBSSL_STATIC_SRCLIB ?= no +# set to the path of the folder with static libraries compiled from source +LIBSSL_STATIC_SRCPATH ?= /usr/local/src/openssl + +ifeq ($(CROSS_COMPILE),) +WOLFSSL_BUILDER=$(shell \ + if pkg-config --exists wolfssl; then \ + echo 'pkg-config wolfssl'; \ + fi) +endif + +ifneq ($(WOLFSSL_BUILDER),) + +ifneq ($(WOLFSSL_STATIC),yes) + DEFS += $(shell $(WOLFSSL_BUILDER) --cflags) + LIBS += $(shell $(WOLFSSL_BUILDER) --libs) + +else # $(WOLFSSL_STATIC),yes) + + DEFS += -DKSR_WOLFSSL_STATIC + +ifneq ($(WOLFSSL_STATIC_SRCLIB),yes) + ## when static libs (*.a) from packages are compiled with -fPIC + DEFS += $(shell $(WOLFSSL_BUILDER) --cflags) + LIBS += $(shell $(WOLFSSL_BUILDER) --libs-only-L) + # TODO: explore use of LIBS += -Wl,-Bstatic $(shell $(SSL_BUILDER) --libs-only-l) + LIBS += -l:libwolfssl.a -l:libz.a -l:libdl.a +else + ## when linking against static libs compiled from sources + DEFS += -I$(WOLFSSL_STATIC_SRCPATH)/include + LIBS += $(WOLFSSL_STATIC_SRCPATH)/libwolfssl.a +endif # ifneq ($(WOLFSSL_STATIC_SRCLIB),yes) + +endif # ifneq ($(WOLFSSL_STATIC),yes) + +else # ifneq ($(SSL_BUILDER),) + + DEFS += -I$(LOCALBASE)/wolfssl/include + LIBS += -L$(LOCALBASE)/lib \ + -L$(LOCALBASE)/lib64 \ + -lwolfssl +endif # ifneq ($(SSL_BUILDER),) + +LIBS+= $(TLS_EXTRA_LIBS) + +# dcm: tls.cfg installed via local 'install-cfg' to update paths +#MOD_INSTALL_CFGS=tls.cfg + +include ../../Makefile.modules + +install-tls-cert: $(cfg_prefix)/$(cfg_dir) + MAIN_NAME=$(MAIN_NAME) ./tls_cert.sh -d $(cfg_prefix)/$(cfg_dir) + +install-cfg: + @if ! [ -d $(cfg_prefix)/$(cfg_dir) ]; then \ + mkdir -p "$(cfg_prefix)/$(cfg_dir)" ; \ + fi + @$(call try_err, $(INSTALL_TOUCH) \ + "$(cfg_prefix)/$(cfg_dir)tls.cfg.sample" ) + @sed -e "s#\/usr/local/etc/kamailio/#$(cfg_target)#g" \ + -e "s#kamailio-selfsigned#$(MAIN_NAME)-selfsigned#g" \ + < ./tls.cfg > "$(cfg_prefix)/$(cfg_dir)tls.cfg.sample" + @if [ -z "${skip_cfg_install}" -a \ + ! -f "$(cfg_prefix)/$(cfg_dir)tls.cfg" ]; then \ + mv -f "$(cfg_prefix)/$(cfg_dir)tls.cfg.sample" \ + "$(cfg_prefix)/$(cfg_dir)tls.cfg" ; \ + fi + diff --git a/src/modules/tls_wolfssl/README b/src/modules/tls_wolfssl/README new file mode 100644 index 00000000000..94c8cfc069e --- /dev/null +++ b/src/modules/tls_wolfssl/README @@ -0,0 +1,1713 @@ +TLS Module + +Andrei Pelinescu-Onciul + + iptelorg GmbH + +Carsten Bock + + ng-voice GmbH + +Olle E. Johansson + + Edvina AB + + Copyright © 2007 iptelorg GmbH + + Copyright © 2014 ng-voice GmbH + __________________________________________________________________ + + Table of Contents + + 1. Admin Guide + + 1. Overview + 2. Quick Start + 3. Important Notes + 4. Compiling the TLS Module + 5. TLS and Low Memory + 6. TLS Debugging + 7. Known Limitations + 8. Quick Certificate Howto + 9. HSM Howto + 10. Parameters + + 10.1. tls_method (string) + 10.2. certificate (string) + 10.3. private_key (string) + 10.4. ca_list (string) + 10.5. ca_path (str) + 10.6. crl (string) + 10.7. verify_certificate (boolean) + 10.8. verify_depth (integer) + 10.9. require_certificate (boolean) + 10.10. cipher_list (string) + 10.11. server_name (string) + 10.12. connection_timeout (int) + 10.13. tls_disable_compression (boolean) + 10.14. ssl_release_buffers (integer) + 10.15. ssl_freelist_max_len (integer) + 10.16. ssl_max_send_fragment (integer) + 10.17. ssl_read_ahead (boolean) + 10.18. send_close_notify (boolean) + 10.19. con_ct_wq_max (integer) + 10.20. ct_wq_max (integer) + 10.21. ct_wq_blk_size (integer) + 10.22. tls_log (int) + 10.23. tls_debug (int) + 10.24. low_mem_threshold1 (integer) + 10.25. low_mem_threshold2 (integer) + 10.26. tls_force_run (boolean) + 10.27. session_cache (boolean) + 10.28. session_id (str) + 10.29. renegotiation (boolean) + 10.30. config (string) + 10.31. xavp_cfg (string) + 10.32. event_callback (str) + 10.33. rand_engine (str) + 10.34. engine (string) + 10.35. engine_config (string) + 10.36. engine_algorithms (string) + 10.37. verify_client (string) + + 11. Functions + + 11.1. is_peer_verified() + 11.2. tls_set_connect_server_id(srvid) + + 12. RPC Commands + + 12.1. tls.info + 12.2. tls.list + 12.3. tls.options + 12.4. tls.reload + + 13. Status + + 13.1. License + 13.2. History + + 14. Event Routes + + 14.1. event_route[tls:connection-out] + + 15. TLS With Database Backend + + List of Examples + + 1.1. Quick Start Basic Config + 1.2. Compiling TLS with Debug Messages + 1.3. Set tls_method parameter + 1.4. Set certificate parameter + 1.5. Set private_key parameter + 1.6. Set ca_list parameter + 1.7. Set ca_path parameter + 1.8. Set crl parameter + 1.9. Set verify_certificate parameter + 1.10. Set verify_depth parameter + 1.11. Set require_certificate parameter + 1.12. Set cipher_list parameter + 1.13. Set server_name parameter + 1.14. Set connection_timeout parameter + 1.15. Set tls.connection_timeout at runtime + 1.16. Set tls_disable_compression parameter + 1.17. Set ssl_release_buffers parameter + 1.18. Set ssl_freelist_max_len parameter + 1.19. Set ssl_max_send_fragment parameter + 1.20. Set ssl_read_ahead parameter + 1.21. Set send_close_notify parameter + 1.22. Set tls.send_close_notify at runtime + 1.23. Set con_ct_wq_max parameter + 1.24. Set tls.con_ct_wq_max at runtime + 1.25. Set ct_wq_max parameter + 1.26. Set tls.ct_wq_max at runtime + 1.27. Set ct_wq_blk_size parameter + 1.28. Set tls.ct_wq_max at runtime + 1.29. Set tls_log parameter + 1.30. Set tls.log at runtime + 1.31. Set tls_debug parameter + 1.32. Set tls.debug at runtime + 1.33. Set low_mem_threshold1 parameter + 1.34. Set tls.low_mem_threshold1 at runtime + 1.35. Set tls.low_mem_threshold2 parameter + 1.36. Set tls.low_mem_threshold2 at runtime + 1.37. Set tls_force_run parameter + 1.38. Set session_cache parameter + 1.39. Set session_id parameter + 1.40. Set renegotiation parameter + 1.41. Sample TLS Config File + 1.42. Set config parameter + 1.43. Change and reload the TLS configuration at runtime + 1.44. Set xavp_cfg parameter + 1.45. Set event_callback parameter + 1.46. Set rand_engine parameter + 1.47. Set verify_client modparam parameter + 1.48. Set verify_client tls.cfg parameter + 1.49. is_peer_verified usage + 1.50. tls_set_connect_server_id usage + 1.51. Use of event_route[tls:connection-out] + +Chapter 1. Admin Guide + + Table of Contents + + 1. Overview + 2. Quick Start + 3. Important Notes + 4. Compiling the TLS Module + 5. TLS and Low Memory + 6. TLS Debugging + 7. Known Limitations + 8. Quick Certificate Howto + 9. HSM Howto + 10. Parameters + + 10.1. tls_method (string) + 10.2. certificate (string) + 10.3. private_key (string) + 10.4. ca_list (string) + 10.5. ca_path (str) + 10.6. crl (string) + 10.7. verify_certificate (boolean) + 10.8. verify_depth (integer) + 10.9. require_certificate (boolean) + 10.10. cipher_list (string) + 10.11. server_name (string) + 10.12. connection_timeout (int) + 10.13. tls_disable_compression (boolean) + 10.14. ssl_release_buffers (integer) + 10.15. ssl_freelist_max_len (integer) + 10.16. ssl_max_send_fragment (integer) + 10.17. ssl_read_ahead (boolean) + 10.18. send_close_notify (boolean) + 10.19. con_ct_wq_max (integer) + 10.20. ct_wq_max (integer) + 10.21. ct_wq_blk_size (integer) + 10.22. tls_log (int) + 10.23. tls_debug (int) + 10.24. low_mem_threshold1 (integer) + 10.25. low_mem_threshold2 (integer) + 10.26. tls_force_run (boolean) + 10.27. session_cache (boolean) + 10.28. session_id (str) + 10.29. renegotiation (boolean) + 10.30. config (string) + 10.31. xavp_cfg (string) + 10.32. event_callback (str) + 10.33. rand_engine (str) + 10.34. engine (string) + 10.35. engine_config (string) + 10.36. engine_algorithms (string) + 10.37. verify_client (string) + + 11. Functions + + 11.1. is_peer_verified() + 11.2. tls_set_connect_server_id(srvid) + + 12. RPC Commands + + 12.1. tls.info + 12.2. tls.list + 12.3. tls.options + 12.4. tls.reload + + 13. Status + + 13.1. License + 13.2. History + + 14. Event Routes + + 14.1. event_route[tls:connection-out] + + 15. TLS With Database Backend + +1. Overview + + This module implements the TLS transport for Kamailio using the OpenSSL + library (http://www.openssl.org). To enable the Kamailio TLS support + this module must be loaded and enable_tls=yes core setting must be + added to the Kamailio config file. + + IMPORTANT: the tls module must be loaded before any other Kamailio + module that uses libssl (OpenSSL library). A safe option is to have the + tls module loaded first (be in the first "loadmodule" in Kamailio.cfg). + + IMPORTANT: using this module compiled with newer versions of libssl + (e.g., v1.1+) may require Kamailio to be started with --atexit=no + command line parameters to avoid calling C atexit callbacks inside the + process ending during daemonize procedure as well as during shut down, + which can lead to crashes because it destroys and then accesses shared + memory. For example, such case has been reported for Ubuntu 20.04 or + RedHat 8. + +2. Quick Start + + The default kamailio.cfg file has basic tls support included, it has to + be enabled with "#!define WITH_TLS" directive. + + The most important parameters to set the path to the public certificate + and private key files. You can either have them in different file or in + the same file in PEM format. The parameters for them are certificate + and private_key. They can be given as modparam or or provided in the + profiles of tls.cfg file. + + When installing tls module of kamailio, a sample 'tls.cfg' file is + deployed in the same folder with 'kamailio.cfg', along with freshly + generated self signed certificates. + + HINT: be sure you have enable_tls=yes to your kamailio.cfg. + + Example 1.1. Quick Start Basic Config +#... +loadmodule "sl.so" +loadmodule "tls.so" + +modparam("tls", "private_key", "./server-test.pem") +modparam("tls", "certificate", "./server-test.pem") +modparam("tls", "ca_list", "./calist.pem") + +enable_tls=yes + +request_route { + if(proto != TLS) { + sl_send_reply("403", "Accepting TLS Only"); + exit; + } + ... +} + +3. Important Notes + + The TLS module needs some special options enabled when compiling + Kamailio. These options are enabled by default, however in case you're + using a modified Kamailio version or Makefile, make sure that you + enable -DUSE_TLS and -DTLS_HOOKS (or compile with make TLS_HOOKS=1 + which will take care of both options). + + To quickly check if your Kamailio version was compiled with these + options, run kamailio -V and look for USE_TLS and TLS_HOOKS among the + flags. + + For OpenSSL (libssl) v1.1.x, it is required to preload + 'openssl_mutex_shared' library shipped by Kamailio. For more details + see 'src/modules/tls/openssl_mutex_shared/README.md'. + + This module includes several workarounds for various Openssl bugs (like + compression and Kerberos using the wrong memory allocations functions, + low memory problems a.s.o). On startup it will try to enable the needed + workarounds based on the OpenSSL library version. Each time a known + problem is detected and a workaround is enabled, a message will be + logged. In general it is recommended to compile this module on the same + machine or a similar machine to where kamailio will be run or to link + it statically with libssl. For example if on the compile machine + OpenSSL does not have the Kerberos support enabled, but on the target + machine a Kerberos enabled OpenSSL library is installed, Kamailio + cannot apply the needed workarounds and will refuse to start. The same + thing will happen if the OpenSSL versions are too different (to force + Kamailio startup anyway, see the tls_force_run module parameter). + + Compression is fully supported if you have a new enough OpenSSL version + (starting with 0.9.8). Although there are some problems with zlib + compression in currently deployed OpenSSL versions (up to and including + 0.9.8d, see openssl bug #1468), the TLS module will automatically + switch to its own fixed version. Note however that starting with + Kamailio 3.1 compression is not enabled by default, due to the huge + extra memory consumption that it causes (about 10x more memory). To + enable it use modparam("tls", "tls_disable_compression", 0) (see + tls_disable_compression). + + The TLS module includes workarounds for the following known openssl + bugs: + * openssl #1204 (disable SS_OP_TLS_BLOCK_PADDING_BUG if compression + is enabled, for versions between 0.9.8 and 0.9.8c), + * openssl #1468 (fix zlib compression memory allocation), + * openssl #1467 (kerberos support will be disabled if the openssl + version is less than 0.9.8e-beta1) + * openssl #1491 (stop using tls in low memory situations due to the + very high risk of openssl crashing or leaking memory). + + The bug reports can be viewed at http://rt.openssl.org/. + +4. Compiling the TLS Module + + In most case compiling the TLS module is as simple as: +make -C modules/tls + + or +make modules modules=modules/tls + + or (compiling whole Kamailio and the tls module) +make all include_modules=tls + + . + + However in some cases the OpenSSL library requires linking with other + libraries. For example compiling the OpenSSL library with Kerberos and + zlib-shared support will require linking the TLS module with libkrb5 + and libz. In this case just add TLS_EXTRA_LIBS="library list" to make's + command line. E.g.: +make TLS_EXTRA_LIBS="-lkrb5 -lz" all include_modules=tls + + In general, if Kamailio fails to start with a symbol not found error + when trying to load the TLS module (check the log), it means some + needed library was not linked and it must be added to TLS_EXTRA_LIBS + + Elliptic Curve Diffie-Hellman (EDCH)-Ciphers are only supported in + OpenSSL 1.0.0e and later. + +5. TLS and Low Memory + + The Openssl library doesn't handle low memory situations very well. If + memory allocations start to fail (due to memory shortage), Openssl can + crash or cause memory leaks (making the memory shortage even worse). As + of this writing all Openssl versions were affected (including 0.9.8e), + see Openssl bug #1491. The TLS module has some workarounds for + preventing this problem (see low_mem_treshold1 and low_mem_threshold2), + however starting Kamailio with enough shared memory is higly + recommended. When this is not possible a quick way to significantly + reduce Openssl memory usage it to disable compression (see + tls_disable_compression). + +6. TLS Debugging + + Debugging messages can be selectively enabled by recompiling the TLS + module with a combination of the following defines: + * TLS_WR_DEBUG - debug messages for the write/send part. + * TLS_RD_DEBUG - debug messages for the read/receive part. + * TLS_BIO_DEBUG - debug messages for the custom BIO. + + Example 1.2. Compiling TLS with Debug Messages +make -C modules/tls extra_defs="-DTLS_WR_DEBUG -DTLS_RD_DEBUG" + + To change the level at which the debug messages are logged, change the + tls_debug module parameter. + +7. Known Limitations + + The private key must not be encrypted (Kamailio cannot ask you for a + password on startup). + + The TLS certificate verifications ignores the certificate name, Subject + Altname and IP extensions, it just checks if the certificate is signed + by a recognized CA. One can use the select framework to try to overcome + this limitation (check in the script for the contents of various + certificate fields), but this is not only slow, but also not exactly + standard conforming (the verification should happen during TLS + connection establishment and not after). + + TLS specific config reloading is not safe, so for now better don't use + it, especially under heavy traffic. + + This documentation is incomplete. The provided selects are not + documented in this file. A list with all the ones implemented by the + TLS module can be found in the Cookbook https://www.kamailio.org/wiki/ + in the section Selects for the respective version of Kamailio. + +8. Quick Certificate Howto + + There are various ways to create, sign certificates and manage small + CAs (Certificate Authorities). If you are in a hurry and everything you + have are the installed OpenSSL libraries and utilities, read on. + + Assumptions: we run our own CA. + + Warning: in this example no key is encrypted. The client and server + private keys must not be encrypted (Kamailio doesn't support encrypted + keys), so make sure the corresponding files are readable only by + trusted people. You should use a password to protect your CA private + key. + +Assumptions +------------ + +The default openssl configuration (usually /etc/ssl/openssl.cnf) +default_ca section is the one distributed with openssl and uses the default +directories: + +... + +default_ca = CA_default # The default ca section + +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several certificates with same subject +. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current CRL number +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +... + +If this is not the case create a new OpenSSL config file that uses the above +paths for the default CA and add to all the openssl commands: + -config filename. E.g.: + openssl ca -config my_openssl.cnf -in kamailio1_cert_req.pem -out kamail +io1_cert.pem + + +Creating the CA certificate +--------------------------- +1. Create the CA directory + mkdir ca + cd ca + +2. Create the CA directory structure and files (see ca(1)) + mkdir demoCA #default CA name, edit /etc/ssl/openssl.cnf + mkdir demoCA/private + mkdir demoCA/newcerts + touch demoCA/index.txt + echo 01 >demoCA/serial + echo 01 >demoCA/crlnumber + +2. Create CA private key + openssl genrsa -out demoCA/private/cakey.pem 2048 + chmod 600 demoCA/private/cakey.pem + +3. Create CA self-signed certificate + openssl req -out demoCA/cacert.pem -x509 -new -key demoCA/private/cake +y.pem + + +Creating a server/client TLS certificate +---------------------------------------- +1. Create a certificate request (and its private key in privkey.pem) + + openssl req -out kamailio1_cert_req.pem -new -nodes + + WARNING: the organization name should be the same as in the CA certifica +te. + +2. Sign it with the CA certificate + openssl ca -in kamailio1_cert_req.pem -out kamailio1_cert.pem + +3. Copy kamailio1_cert.pem to your Kamailio configuration dir + + +Setting Kamailio to use the TLS certificate +--------------------------------------------- +1. Create the CA list file: + for each of your CA certificates that you intend to use do: + cat cacert.pem >>calist.pem + +2. Copy your Kamailio certificate, private key and ca list file to your + intended machine (preferably in your Kamailio configuration directory, + this is the default place Kamailio searches for). + +3. Set up Kamailio.cfg to use the certificate + if your Kamailio certificate name is different from cert.pem or it is no +t + placed in Kamailio cfg. directory, add to your kamailio.cfg: + modparam("tls", "certificate", "/path/cert_file_name") + +4. Set up Kamailio to use the private key + if your private key is not contained in the same file as the certificate + (or the certificate name is not the default cert.pem), add to your + Kamailio.cfg: + modparam("tls", "private_key", "/path/private_key_file") + +5. Set up Kamailio to use the CA list (optional) + The CA list is not used for your server certificate - it's used to approve ot +her servers + and clients connecting to your server with a client certificate or for approv +ing + a certificate used by a server your server connects to. + add to your Kamailio.cfg: + modparam("tls", "ca_list", "/path/ca_list_file") + +6. Set up TLS authentication options: + modparam("tls", "verify_certificate", 1) + modparam("tls", "require_certificate", 1) + (for more information see the module parameters documentation) + + +Revoking a certificate and using a CRL +-------------------------------------- +1. Revoking a certificate: + openssl ca -revoke bad_cert.pem + +2. Generate/update the certificate revocation list: + openssl ca -gencrl -out my_crl.pem + +3. Copy my_crl.pem to your Kamailio config. dir + +4. Set up Kamailio to use the CRL: + modparam("tls", "crl", "path/my_crl.pem") + +9. HSM Howto + + This documents OpenSSL engine support for private keys in HSM. + + Assumptions: an OpenSSL engine configured with private key. We still + require the certificate file and list of CA certificates per a regular + TLS configuration. + +Thales Luna Example +-------------------- + +... +# Example for Thales Luna +modparam("tls", "engine", "gem") +modparam("tls", "engine_config", "/usr/local/etc/kamailio/thales.cnf") +modparam("tls", "engine_algorithms", "EC") +... + +/usr/local/etc/kamailio/thales.cnf is a OpenSSL config format file used to +bootstrap the engine, e.g., pass the PIN. + +... +# the key kamailio is mandatory +kamailio = openssl_init + +[ openssl_init ] +engines = engine_section + +[ engine_section ] +# gem is the name of the Thales Luna OpenSSL engine +gem = gem_section + +[ gem_section ] +# from Thales documentation +dynamic_path = /usr/lib64/engines-1.1/gem.so +ENGINE_INIT = 0:20:21:password=1234-ABCD-5678-EFGH +... + + +Thales nShield Connect +---------------------- + +Place holder + +10. Parameters + + 10.1. tls_method (string) + 10.2. certificate (string) + 10.3. private_key (string) + 10.4. ca_list (string) + 10.5. ca_path (str) + 10.6. crl (string) + 10.7. verify_certificate (boolean) + 10.8. verify_depth (integer) + 10.9. require_certificate (boolean) + 10.10. cipher_list (string) + 10.11. server_name (string) + 10.12. connection_timeout (int) + 10.13. tls_disable_compression (boolean) + 10.14. ssl_release_buffers (integer) + 10.15. ssl_freelist_max_len (integer) + 10.16. ssl_max_send_fragment (integer) + 10.17. ssl_read_ahead (boolean) + 10.18. send_close_notify (boolean) + 10.19. con_ct_wq_max (integer) + 10.20. ct_wq_max (integer) + 10.21. ct_wq_blk_size (integer) + 10.22. tls_log (int) + 10.23. tls_debug (int) + 10.24. low_mem_threshold1 (integer) + 10.25. low_mem_threshold2 (integer) + 10.26. tls_force_run (boolean) + 10.27. session_cache (boolean) + 10.28. session_id (str) + 10.29. renegotiation (boolean) + 10.30. config (string) + 10.31. xavp_cfg (string) + 10.32. event_callback (str) + 10.33. rand_engine (str) + 10.34. engine (string) + 10.35. engine_config (string) + 10.36. engine_algorithms (string) + 10.37. verify_client (string) + +10.1. tls_method (string) + + Sets the TLS protocol method. Possible values are: + * TLSv1.2+ - TLSv1.2 or newer (TLSv1.3, ...) connections are accepted + (available starting with openssl/libssl v1.1.1) + * TLSv1.2 - only TLSv1.2 connections are accepted (available starting + with openssl/libssl v1.0.1e) + * TLSv1.1+ - TLSv1.1 or newer (TLSv1.2, ...) connections are accepted + (available starting with openssl/libssl v1.0.1) + * TLSv1.1 - only TLSv1.1 connections are accepted (available starting + with openssl/libssl v1.0.1) + * TLSv1+ - TLSv1.0 or newer (TLSv1.1, TLSv1.2, ...) connections are + accepted. + * TLSv1 - only TLSv1 (TLSv1.0) connections are accepted. This is the + default value. + * SSLv3 - only SSLv3 connections are accepted. Note: you shouldn't + use SSLv3 for anything which should be secure. + * SSLv2 - only SSLv2 connections, for old clients. Note: you + shouldn't use SSLv2 for anything which should be secure. Newer + versions of OpenSSL libraries don't include support for it anymore. + * SSLv23 - any of the SSLv2, SSLv3 and TLSv1 or newer methods will be + accepted. + From the OpenSSL manual: "A TLS/SSL connection established with + these methods may understand the SSLv3, TLSv1, TLSv1.1 and TLSv1.2 + protocols. If extensions are required (for example server name) a + client will send out TLSv1 client hello messages including + extensions and will indicate that it also understands TLSv1.1, + TLSv1.2 and permits a fallback to SSLv3. A server will support + SSLv3, TLSv1, TLSv1.1 and TLSv1.2 protocols. This is the best + choice when compatibility is a concern." + Note: For older OpenSSL library versions, this option allows SSLv2, + with hello messages done over SSLv2. You shouldn't use SSLv2 or + SSLv3 for anything which should be secure. + + If RFC 3261 conformance is desired, at least TLSv1 must be used. For + compatibility with older clients SSLv23 is the option, but again, be + aware of security concerns, SSLv2/3 being considered very insecure by + 2014. For current information about what's considered secure, please + consult, IETF BCP 195, currently RFC 7525 - "Recommendations for Secure + Use of Transport Layer Security (TLS) and Datagram Transport Layer + Security (DTLS)" + + Example 1.3. Set tls_method parameter +... +modparam("tls", "tls_method", "TLSv1") +... + +10.2. certificate (string) + + Sets the certificate file name. The certificate file can also contain + the private key in PEM format. + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts with a '/' it will be an + absolute path and if it starts with anything else the path will be + relative to the main config file directory (e.g.: for kamailio -f + /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + The default value is /usr/local/etc/kamailio/cert.pem + + Example 1.4. Set certificate parameter +... +modparam("tls", "certificate", "/usr/local/etc/kamailio/my_certificate.pem") +... + +10.3. private_key (string) + + Sets the private key file name. The private key can be in the same file + as the certificate or in a separate file, specified by this + configuration parameter. + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts with a '/' it will be an + absolute path and if it starts with anything else the path will be + relative to the main config file directory (e.g.: for kamailio -f + /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + Note: the private key can be contained in the same file as the + certificate (just append it to the certificate file, e.g.: cat pkey.pem + >> cert.pem) + + The default value is /usr/local/etc/kamailio/cert.pem + + Example 1.5. Set private_key parameter +... +modparam("tls", "private_key", "/usr/local/etc/kamailio/my_pkey.pem") +... + +10.4. ca_list (string) + + Sets the CA list file name. This file contains a list of all the + trusted CAs certificates used when connecting to other SIP + implementations. If a signature in a certificate chain belongs to one + of the listed CAs, the verification of that certificate will succeed. + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts with a '/' it will be an + absolute path and if it starts with anything else the path will be + relative to the main config file directory (e.g.: for kamailio -f + /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + By default the CA file is not set. + + An easy way to create the CA list is to append each trusted trusted CA + certificate in the PEM format to one file, e.g.: +for f in trusted_cas/*.pem ; do cat "$f" >> ca_list.pem ; done + + See also verify_certificate, verify_depth, require_certificate and crl. + + Example 1.6. Set ca_list parameter +... +modparam("tls", "ca_list", "/usr/local/etc/kamailio/ca_list.pem") +... + +10.5. ca_path (str) + + Sets the path with the trusted CA files, to be given as parameter + SSL_CTX_load_verify_locations(). The certificates in ca_path are only + looked up when required, e.g. when building the certificate chain or + when actually performing the verification of a peer certificate. They + are not given to the client (not loaded to be provided to + SSL_CTX_set_client_CA_list()), only the ones in ca_list files are sent + to the client. It requires to use c_rehash to generate the hash map for + certificate search, for more see the manual of libssl for + SSL_CTX_load_verify_locations() function. + + By default it is not set. + + Example 1.7. Set ca_path parameter +... +modparam("tls", "ca_path", "/usr/local/etc/kamailio/ca") +... + +10.6. crl (string) + + Sets the certificate revocation list (CRL) file name. This file + contains a list of revoked certificates. Any attempt to verify a + revoked certificate will fail. + + If not set, no CRL list will be used. + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts with a '/' it will be an + absolute path and if it starts with anything else the path will be + relative to the main config file directory (e.g.: for kamailio -f + /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + +Note + + If set, require_certificate should also be set or it will not have any + effect. + + By default the CRL file name is not set. + + To update the CRL in a running Kamailio, make sure you configure TLS + via a separate TLS config file (the config modparam) and issue a + tls.reload RPC call, e.g.: + $ kamcmd tls.reload + + A quick way to create the CRL in PEM format, using OpenSSL is: + $ openssl ca -gencrl -keyfile cacert.key -cert cacert.pem -out my_crl.pem + + my_crl.pem will contain the signed list of the revoked certificates. + + To revoke a TLS certificate use something like: + $ openssl ca -revoke bad_cert.pem -keyfile cacert.key -cert cacert.pem + + and then refresh the crl file using the command above. + + To display the CRL contents use: + $ openssl crl -in crl.pem -noout -text + + See also ca_list, verify_certificate, verify_depth and + require_certificate. + + Example 1.8. Set crl parameter +... +modparam("tls", "crl", "/usr/local/etc/kamailio/crl.pem") +... + +10.7. verify_certificate (boolean) + + If enabled it will force certificate verification when connecting to + other SIP servers.. For more information see the verify(1) OpenSSL man + page. + + Note: the certificate verification will always fail if the ca_list is + empty. + + See also: ca_list, require_certificate, verify_depth. + + By default the certificate verification is off. + + Example 1.9. Set verify_certificate parameter +... +modparam("tls", "verify_certificate", 1) +... + +10.8. verify_depth (integer) + + Sets how far up the certificate chain will the certificate verification + go in the search for a trusted CA. + + See also: ca_list, require_certificate, verify_certificate, + + The default value is 9. + + Example 1.10. Set verify_depth parameter +... +modparam("tls", "verify_depth", 9) +... + +10.9. require_certificate (boolean) + + When enabled Kamailio will require a certificate from a client + connecting to the TLS port. If the client does not offer a certificate + and verify_certificate is on, certificate verification will fail. + + The default value is off. + + Example 1.11. Set require_certificate parameter +... +modparam("tls", "require_certificate", 1) +... + +10.10. cipher_list (string) + + Sets the list of accepted ciphers. The list consists of cipher strings + separated by colons. For more information on the cipher list format see + the cipher(1) OpenSSL man page. + + The default value is not set (all the OpenSSL supported ciphers are + enabled). + + Example 1.12. Set cipher_list parameter +... +modparam("tls", "cipher_list", "HIGH") +... + +10.11. server_name (string) + + Sets the Server Name Indication (SNI) value. + + This is a TLS extension enabling one TLS server to serve multiple host + names with unique certificates. + + The default value is empty (not set). + + Example 1.13. Set server_name parameter +... +modparam("tls", "server_name", "kamailio.org") +... + +10.12. connection_timeout (int) + + Sets the amount of time after which an idle TLS connection will be + closed, if no I/O ever occurred after the initial open. If an I/O event + occurs, the timeout will be extended with tcp_connection_lifetime. The + value is expressed in seconds. + + The default value is 10 min. + + If the value set is -1, the connection will never be close on idle. + + This setting can be changed also at runtime, via the RPC interface and + config framework. The config variable name is tls.connection_timeout. + + Example 1.14. Set connection_timeout parameter +... +modparam("tls", "connection_timeout", 60) +... + + Example 1.15. Set tls.connection_timeout at runtime + $ kamcmd cfg.set_now_int tls connection_timeout 180 + +10.13. tls_disable_compression (boolean) + + If set compression over TLS will be disabled. Note that compression + uses a lot of memory (about 10x more then with the compression + disabled), so if you want to minimize memory usage is a good idea to + disable it. TLS compression also expose you for the CRIME security + vulnerability. + + By default TLS compression is disabled. + + Example 1.16. Set tls_disable_compression parameter +... +modparam("tls", "tls_disable_compression", 0) # enable +... + +10.14. ssl_release_buffers (integer) + + Release internal OpenSSL read or write buffers as soon as they are no + longer needed. Combined with ssl_freelist_max_len has the potential of + saving a lot of memory ( ~ 32k per connection in the default + configuration, or 16k + ssl_max_send_fragment). For Kamailio versions > + 3.0 it makes little sense to disable it (0) since the tls module + already has its own internal buffering. + + A value of -1 would not change this option from its openssl default. + Use 0 or 1 for enable/disable. + + By default the value is 1 (enabled). + +Note + + This option is supported only for OpenSSL versions >= 1.0.0. On all the + other versions attempting to change the default will trigger an error. + + Example 1.17. Set ssl_release_buffers parameter +modparam("tls", "ssl_release_buffers", 1) + +10.15. ssl_freelist_max_len (integer) + + Sets the maximum number of free memory chunks, that OpenSSL will keep + per connection. Setting it to 0 would cause any unused memory chunk to + be immediately freed, reducing the memory footprint. A too large value + would result in extra memory consumption. + + Should be combined with ssl_release_buffers. + + A value of -1 has a special meaning: the OpenSSL default will be used + (no attempt on changing the value will be made). For OpenSSL 1.0 the + internal default is 32. + + By default the value is 0 (no freelist). + +Note + + This option is supported only for OpenSSL versions >= 1.0.0. On all the + other versions attempting to change the default will trigger an error. + + Example 1.18. Set ssl_freelist_max_len parameter +modparam("tls", "ssl_freelist_max_len", 0) + +10.16. ssl_max_send_fragment (integer) + + Sets the maximum number of bytes (from the clear text) sent into one + TLS record. Valid values are between 512 and 16384. Note however that + even valid low values might not be big enough to allow a successful + handshake (try minimum 1024). + + Lower values would lead to less memory usage, but values lower then the + typical Kamailio write size would incur a slight performance penalty. + Good values are bigger then the size of the biggest SIP packet one + normally expects to forward. For example in most setups 2048 would be a + good value. + +Note + + Values on the lower side, even if valid (> 512), might not allow for a + successful initial handshake. This happens if the certificate does not + fit inside one send fragment. Values lower then 1024 should not be + used. Even with higher values, if the handshake fails, try increasing + the value. + + A value of -1 has a special meaning: the OpenSSL default will be used + (no attempt on changing the value will be made). + + By default the value is -1 (the OpenSSL default, which at least in + OpenSSL 1.0.0 is ~ 16k). + +Note + + This option is supported only for OpenSSL versions >= 0.9.9. On all the + other versions attempting to change the default will trigger an error. + + Example 1.19. Set ssl_max_send_fragment parameter +modparam("tls", "ssl_max_send_fragment", 4096) + +10.17. ssl_read_ahead (boolean) + + Enables read ahead, reducing the number of internal OpenSSL BIO read() + calls. This option has only debugging value, in normal circumstances it + should not be changed from the default. + + When disabled OpenSSL will make at least 2 BIO read() calls per + received record: one to get the record header and one to get the rest + of the record. + + The TLS module buffers internally all read()s and defines its own fast + BIO so enabling this option would only cause more memory consumption + and a minor slow-down (extra memcpy). + + A value of -1 has a special meaning: the OpenSSL default will be used + (no attempt on changing the value will be made). + + By default the value is 0 (disabled). + + Example 1.20. Set ssl_read_ahead parameter +modparam("tls", "ssl_read_ahead", 1) + +10.18. send_close_notify (boolean) + + Enables/disables sending close notify alerts prior to closing the + corresponding TCP connection. Sending the close notify prior to TCP + shutdown is "nicer" from a TLS point of view, but it has a measurable + performance impact. Default: off. Can be set at runtime + (tls.send_close_notify). + + The default value is 0 (off). + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.send_close_notify. + + Example 1.21. Set send_close_notify parameter +... +modparam("tls", "send_close_notify", 1) +... + + Example 1.22. Set tls.send_close_notify at runtime + $ kamcmd cfg.set_now_int tls send_close_notify 1 + +10.19. con_ct_wq_max (integer) + + Sets the maximum allowed per connection clear-text send queue size in + bytes. This queue is used when data cannot be encrypted and sent + immediately because of an ongoing TLS level renegotiation. + + The default value is 65536 (64 Kb). + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.con_ct_wq_max. + + Example 1.23. Set con_ct_wq_max parameter +... +modparam("tls", "con_ct_wq_max", 1048576) +... + + Example 1.24. Set tls.con_ct_wq_max at runtime + $ kamcmd cfg.set_now_int tls con_ct_wq_max 1048576 + +10.20. ct_wq_max (integer) + + Sets the maximum total number of bytes queued in all the clear-text + send queues. These queues are used when data cannot be encrypted and + sent immediately because of an ongoing TLS level renegotiation. + + The default value is 10485760 (10 Mb). + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.ct_wq_max. + + Example 1.25. Set ct_wq_max parameter +... +modparam("tls", "ct_wq_max", 4194304) +... + + Example 1.26. Set tls.ct_wq_max at runtime + $ kamcmd cfg.set_now_int tls ct_wq_max 4194304 + +10.21. ct_wq_blk_size (integer) + + Minimum block size for the internal clear-text send queues (debugging / + advanced tuning). Good values are multiple of typical datagram sizes. + + The default value is 4096. + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.ct_wq_blk_size. + + Example 1.27. Set ct_wq_blk_size parameter +... +modparam("tls", "ct_wq_blk_size", 2048) +... + + Example 1.28. Set tls.ct_wq_max at runtime + $ kamcmd cfg.set_now_int tls ct_wq_blk_size 2048 + +10.22. tls_log (int) + + Sets the log level at which TLS related messages will be logged. + + The default value is 3 (L_DBG). + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.log. + + Example 1.29. Set tls_log parameter +... +# ignore TLS messages if Kamailio is started with debug less than 10 +modparam("tls", "tls_log", 10) +... + + Example 1.30. Set tls.log at runtime + $ kamcmd cfg.set_now_int tls log 10 + +10.23. tls_debug (int) + + Sets the log level at which TLS debug messages will be logged. Note + that TLS debug messages are enabled only if the TLS module is compiled + with debugging enabled (e.g. -DTLS_WR_DEBUG, -DTLS_RD_DEBUG or + -DTLS_BIO_DEBUG). + + The default value is 3 (L_DBG). + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.debug. + + Example 1.31. Set tls_debug parameter +... +# ignore TLS debug messages if Kamailio is started with debug less than 10 +modparam("tls", "tls_debug", 10) +... + + Example 1.32. Set tls.debug at runtime + $ kamcmd cfg.set_now_int tls debug 10 + +10.24. low_mem_threshold1 (integer) + + Sets the minimal free memory from which attempts to open or accept new + TLS connections will start to fail. The value is expressed in KB. + + The default value depends on whether the OpenSSL library used handles + low memory situations in a good way (openssl bug #1491). As of this + writing this is not true for any OpenSSL version (including 0.9.8e). + + If an ill-behaved OpenSSL version is detected, a very conservative + value is chosen, which depends on the maximum possible number of + simultaneously created TLS connections (and hence on the process + number). + + The following values have a special meaning: + * -1 - use the default value + * 0 - disable (TLS connections will not fail preemptively) + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.low_mem_threshold1. + + See also tls.low_mem_threshold2. + + Example 1.33. Set low_mem_threshold1 parameter +... +modparam("tls", "low_mem_threshold1", -1) +... + + Example 1.34. Set tls.low_mem_threshold1 at runtime + $ kamcmd cfg.set_now_int tls low_mem_threshold1 2048 + +10.25. low_mem_threshold2 (integer) + + Sets the minimal free memory from which TLS operations on already + established TLS connections will start to fail preemptively. The value + is expressed in KB. + + The default value depends on whether the OpenSSL library used handles + low memory situations (openssl bug #1491). As of this writing this is + not true for any OpenSSL version (including 0.9.8e). + + If an ill-behaved OpenSSL version is detected, a very conservative + value is chosen, which depends on the maximum possible number of + simultaneously created TLS connections (and hence on the process + number). + + The following values have a special meaning: + * -1 - use the default value + * 0 - disable (TLS operations will not fail preemptively) + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.low_mem_threshold2. + + See also tls.low_mem_threshold1. + + Example 1.35. Set tls.low_mem_threshold2 parameter +... +modparam("tls", "low_mem_threshold2", -1) +... + + Example 1.36. Set tls.low_mem_threshold2 at runtime + $ kamcmd cfg.set_now_int tls low_mem_threshold2 1024 + +10.26. tls_force_run (boolean) + + If enabled Kamailio will start even if some of the OpenSSL sanity + checks fail (turn it on at your own risk). + + If any of the following sanity checks fail, Kamailio will not start: + * the version of the library the TLS module was compiled with is "too + different" from the library used at runtime. The versions should + have the same major, minor and fix level (e.g.: 0.9.8a and 0.9.8c + are ok, but 0.9.8 and 0.9.9 are not) + * the OpenSSL library used at compile time and the one used at + runtime have different Kerberos options + + By default tls_force_run is disabled. + + Example 1.37. Set tls_force_run parameter +... +modparam("tls", "tls_force_run", 11) +... + +10.27. session_cache (boolean) + + If enabled Kamailio will do caching of the TLS sessions data, + generation a session_id and sending it back to client. + + By default TLS session caching is disabled (0). + + Example 1.38. Set session_cache parameter +... +modparam("tls", "session_cache", 1) +... + +10.28. session_id (str) + + The value for session ID context, making sense when session caching is + enabled. + + By default TLS session_id is "kamailio-tls-5.x.y". + + Example 1.39. Set session_id parameter +... +modparam("tls", "session_id", "my-session-id-context") +... + +10.29. renegotiation (boolean) + + If enabled Kamailio will allow renegotiations of TLS connection + initiated by the client. This may expose to a security risk if the + client is not a trusted peer and keeps renegotiating, consuming CPU and + bandwidth resources. + + By default TLS renegotiation is disabled (0). + + Example 1.40. Set renegotiation parameter +... +modparam("tls", "renegotiation", 1) +... + +10.30. config (string) + + Sets the name of the TLS specific configuration file or configuration + directory. + + If set the TLS module will load a special configuration file or + configuration files from configuration directory, in which different + TLS parameters can be specified on a per role (server or client) and + domain basis (for now only IPs). The corresponding module parameters + will be ignored if a separate configuration file is used. + + If the file or directory name starts with a '.' the path will be + relative to the working directory (at runtime). If it starts with a '/' + it will be an absolute path and if it starts with anything else the + path will be relative to the main config file directory (e.g.: for + kamailio -f /etc/kamailio/kamailio.cfg it will be relative to + /etc/kamailio/). + + By default no TLS configuration file is specified. + + The following parameters can be set in the config file, for each + domain: + * tls_method - (str) - TLS methods + * verify_certificate - (bool) - see modparam + * require_certificate - (bool) - see modparam + * verify_client - (str) - see modparam + * private_key - (str) - see modparam + * certificate - (str) - see modparam + * verify_depth - (int) - see modparam + * ca_list - (str) - see modparam + * crl - (str) - see modparam + * cipher_list - (str) - see modparam + * server_name - (str) - SNI (server name identification) + * server_name_mode - (int) - how to match server_name + * server_id - (str) - server id + + The value for server_name_mode specifies how to match the server_name + (SNI). If set to 1, match the domain and all its subdomains. If set to + 2, match only the subdomains. If set to 0 (or anything else), match + only the domain given in server_name. + + The value for server_id can be any string, being used to match TLS + client config profile, overriding the match on ip:port and server_name. + This is the recommended way for selecting a specific TLS client config + profile, because the local or remote port is hard to predict for a + stream connection - see parameter xavp_cfg to learn how to enable it. + + All the parameters that take filenames as values will be resolved using + the same rules as for the tls config filename itself: starting with a + '.' means relative to the working directory, a '/' means an absolute + path and anything else a path relative to the directory of the current + Kamailio main config file. + + Kamailio acts as a server when it accepts a connection and as a client + when it initiates a new connection by itself (it connects to + something). + + The tls.cfg consists on a set of server and client TLS domain profiles. + A server TLS domain profile starts with [server:domain]. A client TLS + domain profile starts with [client:domain]. The tokens 'server' and + 'client' are static values. The 'domain' part can be: 'ip:port' - the + IP address and port to match with the TLS connection; 'default' - + (static string) for client and server profiles to be used when no other + profile is matched; 'any' - (static string) for client and server + profiles to be matched based on 'server_name', no matter of IP and port + of the TLS connection. + + There can be only one of each [server:default] and [client:default] + profile definitions. Other profiles can be defined many times with the + same domain ('ip:port' or 'any'), but in that case they must have + 'server_name' set for matching SNI. + + It is highly recommended to have [server:default] and [client:default] + profile definitions. They are needed when SNI is not yet available. If + SNI is provided, then the profile definition is searched again to match + on 'server_name'. + + Example 1.41. Sample TLS Config File +... +[server:default] +method = TLSv1 +verify_certificate = yes +require_certificate = yes +private_key = default_key.pem +certificate = default_cert.pem +ca_list = default_ca.pem +crl = default_crl.pem + +[client:default] +verify_certificate = yes +require_certificate = yes + +# more relaxed for connection on the loopback interface +[server:127.0.0.1:5061] +method = TLSv1 +verify_certificate = yes +require_certificate = no +private_key = local_kamailio_org_key.pem +certificate = local_kamailio_org_cert.pem +verify_depth = 3 +ca_list = local_ca.pem +server_name = kamailio.org + +[client:127.0.0.1:5061] +method = TLSv1 +verify_certificate = yes +require_certificate = yes +private_key = default_key.pem +certificate = default_cert.pem +ca_list = default_ca.pem +crl = default_crl.pem +server_name = kamailio.org +server_id = kamailio.org + +# server profile on any address +[server:any] +method = TLSv1 +verify_certificate = yes +require_certificate = no +private_key = kamailio_net_key.pem +certificate = kamailio_net_cert.pem +verify_depth = 3 +ca_list = local_ca.pem +server_name = kamailio.net +server_name_mode = 1 +... + + For a more complete example check the tls.cfg distributed with the + Kamailio source (kamailio/modules/tls/tls.cfg). + + Example 1.42. Set config parameter +... +modparam("tls", "config", "/usr/local/etc/kamailio/tls.cfg") +... + + The file can be changed at runtime. The new config will not be loaded + immediately, but after the first tls.reload RPC call. + + Example 1.43. Change and reload the TLS configuration at runtime + $ kamcmd cfg.set_now_string tls config "/usr/local/etc/kamailio/new_tls.cfg" + $ kamcmd tls.reload + +10.31. xavp_cfg (string) + + Sets the name of XAVP that stores attributes for TLS connections. + + The following (inner) attributes can be set: + * server_name - SNI to be used for outbound connections + * server_id - string value to be used to match TLS config profile for + client (outbound) connections. If it is set, matching the TLS + config profile is done first on server_id and then on ip:port and + server_name. This is the recommended way for selecting a specific + TLS client config profile as the local or remote port is hard to + predict for a stream connection. + + The default value is empty (not set). + + Example 1.44. Set xavp_cfg parameter +... + modparam("tls", "xavp_cfg", "tls") + ... + $xavp(tls=>server_name) = "kamailio.org"; + $xavp(tls[0]=>server_id) = "kamailio.org"; + $du = "sip:kamailio.org:5061;transport=tls"; + route(RELAY); +... + +10.32. event_callback (str) + + The name of the function in the kemi configuration file (embedded + scripting language such as Lua, Python, ...) to be executed instead of + event_route[...] blocks specific for tls module. + + The function has one string parameter, the value is the name of the + event_route block, respectively "tls:connection-out". + + Default value is 'empty' (no function is executed for events). + + Example 1.45. Set event_callback parameter +... +modparam("tls", "event_callback", "ksr_tls_event") +... +-- event callback function implemented in Lua +function ksr_tls_event(evname) + KSR.info("===== tls module triggered event: " .. evname .. "\n"); + return 1; +end +... + +10.33. rand_engine (str) + + Set the random number generator engine for libssl. + + Note: the default random number generator (PRNG) engine of libssl + v1.1.x is not designed for multi-process applications and can result in + a crash. Therefore set the PRNG engine to one of the options listed in + this section. If libssl 1.1.x (or newer) is detected at compile time, + then the PRNG engine is set to "cryptorand". + + The following options are avaialble: + * krand - use internal kam_rand() function + * fastrand - use internal fastrand (ISAAC) function + * cryptorand - use internal cryptorand (Fortuna) function + * kxlibssl - default libssl rand engine wrapped by a Kamailio mutex + + Note: the krand and fastrand engines are not recommended for use on + systems requiring strong security, as they may not generate numbers + with enough randomness and are not cryptographically secure. + + The default value is empty (not set) for libssl v1.0.x or older, and + "cryptorand" for libssl v1.1.x or newer. + + Example 1.46. Set rand_engine parameter +... +modparam("tls", "rand_engine", "fastrand") +... + +10.34. engine (string) + + If OpenSSL is compiled with engine support this will allow algorithms + to be offloaded and private keys from HSM to be used. Currently only a + single global engine is supported. However, private keys can be + specified per_domain. + + To use private keys from the HSM, the name is the HSM key label + prefixed by /engine:. +... +## example for the Gem engine +modparam("tls", "engine", "gem") +# can also be set per-domain in tls.cfg +modparam("tls", "private_key", "/engine:my_HSM_key_label") + +## example for engine_pkcs11 +modparam("tls", "engine", "pkcs11") +modparam("tls", "private_key", "/engine:pkcs11:token=MYTOKEN;object=MYKEYLABEL") + +modparam("tls", "engine_config", "/usr/local/etc/kamailio/openssl.cnf") +modparam("tls", "engine_algorithms", "ALL") +... + + By default OpenSSL engine support is disabled (NONE). This global param + is not supported in the tls config file. + +10.35. engine_config (string) + + A OpenSSL configuration file to initialize the engine. Typically used + to send PIN to HSMs to unlock private keys. See the HSM howto for an + example. This global param is not supported in the tls config file. + +10.36. engine_algorithms (string) + + A list of cryptographic methods to be set as default in the engine. + This is a comma-separated list of values from ALL RSA DSA DH EC RAND + CIPHERS DIGESTS PKEY PKEY_CRYPTO PKEY_ASN1. Not all methods are + supported by every engine. + + The default is not to set any methods as default. This global param is + not supported in the tls config file. + +10.37. verify_client (string) + + Provides an alternative to verify_certificate and require_certificate + modparam and tls.cfg parameters, and creates an additional + opportunistic connection establishment option for connections with with + unverifiable certificates (optional_no_ca). + + This is useful for allowing connections from SIP phones with + self-signed certificates, signed by unrecognized root CAs, expired + certificates, etc. + + The following values have respective behaviors: + * off - no client certificate request performed. + * on - require a verified certificate from the client. + * optional - ask client for certificate. If one is provided, it must + be verified. Allowing missing certificate. + * optional_no_ca - ask client for certificate. Opportunistically try + to verify certificate. Allow connection regardless of whether there + is no certificate or whether certificate is present (verified or + not). Note that verification status can be retrieved via + $tls_peer_verified. + + Default value is 'off' (no client certificate request performed). + + Recommendation: when using this parameter, do not use + verify_certificate or require_certificate parameters. Conversion table + is as follows: + * verify_certificate=0, require_certificate=0 => verify_client="off" + * verify_certificate=1, require_certificate=0 => + verify_client="optional" + * verify_certificate=1, require_certificate=1 => verify_client="on" + + Example 1.47. Set verify_client modparam parameter +... +modparam("tls", "verify_client", "on") +... + + Example 1.48. Set verify_client tls.cfg parameter +... +[server:1.2.3.4:5061] +method = TLSv1 +verify_client = on +... + +[server:5.6.7.8:5061] +method = TLSv1.2 +verify_client = optional_no_ca +... + +11. Functions + + 11.1. is_peer_verified() + 11.2. tls_set_connect_server_id(srvid) + +11.1. is_peer_verified() + + Returns true if the connection on which the message was received is + TLS, the peer presented an X509 certificate and the certificate chain + verified ok. + + It can be used only in a request route. + + Example 1.49. is_peer_verified usage +... + if (proto==TLS && !is_peer_verified()) { + sl_send_reply("400", "No certificate or verification failed"); + exit; + } +... + +11.2. tls_set_connect_server_id(srvid) + + Set the server id of the tls profile to be used by next client connect, + being reset after use. It is an alternative to the use of xavp to + specify server id of a client profile for the cases when xavps are no + longer available (e.g., after event_route[tm:local-request]). + + If the parameter is an empty string, then the value is reset. + + It can be used only in ANY_ROUTE. + + Example 1.50. tls_set_connect_server_id usage +... + tls_set_connect_server_id("clientone"); +... + +12. RPC Commands + + 12.1. tls.info + 12.2. tls.list + 12.3. tls.options + 12.4. tls.reload + +12.1. tls.info + + List internal information related to the TLS module in a short list - + max connections, open connections and the write queue size. + + Parameters: + * None. + +12.2. tls.list + + List details about all active TLS connections. + + Parameters: + * None. + +12.3. tls.options + + List the current TLS configuration. + + Parameters: + * None. + +12.4. tls.reload + + Reload the external TLS configuration file (aka tls.cfg). It does not + reload modparam() parameters. Note that existing active TLS connections + are not terminated and they continue to use the old certificates. The + new configuration will be used for new connections. + + Parameters: + * None. + +13. Status + + 13.1. License + 13.2. History + +13.1. License + + Most of the code for this module has been released under BSD by + iptelorg. The GPL parts are released with an exception to link with + OpenSSL toolkit software components. + +13.2. History + + For version 3.1 most of the TLS specific code was completely re-written + to add support for asynchronous TLS and fix several long standing bugs. + + The code is currently maintained by Andrei Pelinescu-Onciul + . + + Install does not generate self-signed certificates by default anymore. + In order to generate them now you should do "make install-tls-cert" + +14. Event Routes + + 14.1. event_route[tls:connection-out] + +14.1. event_route[tls:connection-out] + + Event route to be executed when a TLS connection is opened by Kamailio. + If drop() is executed in the event route, then the data is no longer + sent over the connection. + + Example 1.51. Use of event_route[tls:connection-out] +... +event_route[tls:connection-out] { + if($sndto(ip)=="1.2.3.4") { + drop; + } +} +... + +15. TLS With Database Backend + + The module does not connect to database to fetch the values for the TLS + profile attributes. However the 'kamcli' tool can generate the tls.cfg + from a database table. Once generated, the 'tls.cfg' can be reloaded + with an RPC command. + + The kamcli tool can be found at https://github.com/kamailio/kamcli. + + The schema to create the database table can be seen with the command: + "kamcli tls sqlprint". The default name for database table is 'tlscfg'. + + The most of the column names matches the corresponding attribute names + from a TLS profile. + + The profile id in 'tls.cfg' is generated from + '[profile_type:profile_name]'. The 'profile_type' has to be 'server' or + 'client'. The 'profile_name' can be 'default', 'any' or the pair of IP + address and port like 'ipaddr:port'. + + The 'file_type' is specifying if the values for 'certificate', + 'private_key', 'ca_list' and 'crl' are path to files on disc (when is + set to 0) or the content of the files (when set to 1). If 'file_type' + is 1, then 'kamcli' will create new files on disc and store the values + from the database in them. The target folder for 'tls.cfg' and the + certificates related files can be set via command options for 'kamcli + tls', for more details see the output of 'kamcli tls --help' and + 'kamcli tls cfgprint --help'. diff --git a/src/modules/tls_wolfssl/TODO.md b/src/modules/tls_wolfssl/TODO.md new file mode 100644 index 00000000000..b707446ff21 --- /dev/null +++ b/src/modules/tls_wolfssl/TODO.md @@ -0,0 +1,7 @@ +# TODO: tls_wolfssl module + +* update `docs/` +* use native wolfSSL API instead of OpenSSL compatibility layer +* remove OpenSSL multi-process hacks: + * clean up per proc `SSL_CTX` + * clean up pthread hacks diff --git a/src/modules/tls_wolfssl/doc/Makefile b/src/modules/tls_wolfssl/doc/Makefile new file mode 100644 index 00000000000..d353e8d03fe --- /dev/null +++ b/src/modules/tls_wolfssl/doc/Makefile @@ -0,0 +1,4 @@ +docs = tls.xml + +docbook_dir = ../../../../doc/docbook +include $(docbook_dir)/Makefile.module diff --git a/src/modules/tls_wolfssl/doc/certs_howto.xml b/src/modules/tls_wolfssl/doc/certs_howto.xml new file mode 100644 index 00000000000..437274cc8ad --- /dev/null +++ b/src/modules/tls_wolfssl/doc/certs_howto.xml @@ -0,0 +1,154 @@ + + + +%docentities; + +]> + +
+ + + + Quick Certificate Howto + + There are various ways to create, sign certificates and manage small CAs (Certificate Authorities). + If you are in a hurry and everything you have are the installed OpenSSL libraries and utilities, read on. + + + Assumptions: we run our own CA. + + + Warning: in this example no key is encrypted. The client and server private keys must not be encrypted + (&kamailio; doesn't support encrypted keys), so make sure the corresponding files are readable only by + trusted people. You should use a password to protect your CA private key. + + + +Assumptions +------------ + +The default openssl configuration (usually /etc/ssl/openssl.cnf) +default_ca section is the one distributed with openssl and uses the default +directories: + +... + +default_ca = CA_default # The default ca section + +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several certificates with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current CRL number +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +... + +If this is not the case create a new OpenSSL config file that uses the above +paths for the default CA and add to all the openssl commands: + -config filename. E.g.: + openssl ca -config my_openssl.cnf -in kamailio1_cert_req.pem -out kamailio1_cert.pem + + +Creating the CA certificate +--------------------------- +1. Create the CA directory + mkdir ca + cd ca + +2. Create the CA directory structure and files (see ca(1)) + mkdir demoCA #default CA name, edit /etc/ssl/openssl.cnf + mkdir demoCA/private + mkdir demoCA/newcerts + touch demoCA/index.txt + echo 01 >demoCA/serial + echo 01 >demoCA/crlnumber + +2. Create CA private key + openssl genrsa -out demoCA/private/cakey.pem 2048 + chmod 600 demoCA/private/cakey.pem + +3. Create CA self-signed certificate + openssl req -out demoCA/cacert.pem -x509 -new -key demoCA/private/cakey.pem + + +Creating a server/client TLS certificate +---------------------------------------- +1. Create a certificate request (and its private key in privkey.pem) + + openssl req -out kamailio1_cert_req.pem -new -nodes + + WARNING: the organization name should be the same as in the CA certificate. + +2. Sign it with the CA certificate + openssl ca -in kamailio1_cert_req.pem -out kamailio1_cert.pem + +3. Copy kamailio1_cert.pem to your &kamailio; configuration dir + + +Setting &kamailio; to use the TLS certificate +--------------------------------------------- +1. Create the CA list file: + for each of your CA certificates that you intend to use do: + cat cacert.pem >>calist.pem + +2. Copy your &kamailio; certificate, private key and ca list file to your + intended machine (preferably in your &kamailio; configuration directory, + this is the default place &kamailio; searches for). + +3. Set up &kamailio;.cfg to use the certificate + if your &kamailio; certificate name is different from cert.pem or it is not + placed in &kamailio; cfg. directory, add to your kamailio.cfg: + modparam("tls", "certificate", "/path/cert_file_name") + +4. Set up &kamailio; to use the private key + if your private key is not contained in the same file as the certificate + (or the certificate name is not the default cert.pem), add to your + &kamailio;.cfg: + modparam("tls", "private_key", "/path/private_key_file") + +5. Set up &kamailio; to use the CA list (optional) + The CA list is not used for your server certificate - it's used to approve other servers + and clients connecting to your server with a client certificate or for approving + a certificate used by a server your server connects to. + add to your &kamailio;.cfg: + modparam("tls", "ca_list", "/path/ca_list_file") + +6. Set up TLS authentication options: + modparam("tls", "verify_certificate", 1) + modparam("tls", "require_certificate", 1) + (for more information see the module parameters documentation) + + +Revoking a certificate and using a CRL +-------------------------------------- +1. Revoking a certificate: + openssl ca -revoke bad_cert.pem + +2. Generate/update the certificate revocation list: + openssl ca -gencrl -out my_crl.pem + +3. Copy my_crl.pem to your &kamailio; config. dir + +4. Set up &kamailio; to use the CRL: + modparam("tls", "crl", "path/my_crl.pem") + + + + + + +
diff --git a/src/modules/tls_wolfssl/doc/functions.xml b/src/modules/tls_wolfssl/doc/functions.xml new file mode 100644 index 00000000000..2a5d4f4f2d7 --- /dev/null +++ b/src/modules/tls_wolfssl/doc/functions.xml @@ -0,0 +1,63 @@ + + + +%docentities; + +]> + +
+ + + + Functions + +
+ <function>is_peer_verified()</function> + + Returns true if the connection on which the message was received + is TLS, the peer presented an X509 certificate and the + certificate chain verified ok. + + + It can be used only in a request route. + + + <function>is_peer_verified</function> usage + +... + if (proto==TLS && !is_peer_verified()) { + sl_send_reply("400", "No certificate or verification failed"); + exit; + } +... + + +
+
+ <function>tls_set_connect_server_id(srvid)</function> + + Set the server id of the tls profile to be used by next client + connect, being reset after use. It is an alternative to the use + of xavp to specify server id of a client profile for the cases + when xavps are no longer available (e.g., after + event_route[tm:local-request]). + + + If the parameter is an empty string, then the value is reset. + + + It can be used only in ANY_ROUTE. + + + <function>tls_set_connect_server_id</function> usage + +... + tls_set_connect_server_id("clientone"); +... + + +
+
diff --git a/src/modules/tls_wolfssl/doc/history.xml b/src/modules/tls_wolfssl/doc/history.xml new file mode 100644 index 00000000000..7a9ec83c153 --- /dev/null +++ b/src/modules/tls_wolfssl/doc/history.xml @@ -0,0 +1,38 @@ + + + +%docentities; + +]> + +
+ Status +
+ License + + Most of the code for this module has been released under BSD by + iptelorg. The GPL parts are released with an exception to link + with OpenSSL toolkit software components. + +
+
+ History + + For version 3.1 most of the TLS specific code was completely + re-written to add support for asynchronous TLS and fix several + long standing bugs. + + + The code is currently maintained by Andrei Pelinescu-Onciul + andrei@iptel.org. + + + Install does not generate self-signed certificates by default + anymore. In order to generate them now you should do + "make install-tls-cert" + +
+
diff --git a/src/modules/tls_wolfssl/doc/hsm_howto.xml b/src/modules/tls_wolfssl/doc/hsm_howto.xml new file mode 100644 index 00000000000..3c3547aa084 --- /dev/null +++ b/src/modules/tls_wolfssl/doc/hsm_howto.xml @@ -0,0 +1,64 @@ + + + +%docentities; + +]> + +
+ + + + HSM Howto + + This documents OpenSSL engine support for private keys in HSM. + + + Assumptions: an OpenSSL engine configured with private key. We still require the certificate file + and list of CA certificates per a regular TLS configuration. + + + +Thales Luna Example +-------------------- + +... +# Example for Thales Luna +modparam("tls", "engine", "gem") +modparam("tls", "engine_config", "/usr/local/etc/kamailio/thales.cnf") +modparam("tls", "engine_algorithms", "EC") +... + +/usr/local/etc/kamailio/thales.cnf is a OpenSSL config format file used to +bootstrap the engine, e.g., pass the PIN. + +... +# the key kamailio is mandatory +kamailio = openssl_init + +[ openssl_init ] +engines = engine_section + +[ engine_section ] +# gem is the name of the Thales Luna OpenSSL engine +gem = gem_section + +[ gem_section ] +# from Thales documentation +dynamic_path = /usr/lib64/engines-1.1/gem.so +ENGINE_INIT = 0:20:21:password=1234-ABCD-5678-EFGH +... + + +Thales nShield Connect +---------------------- + +Place holder + + + + +
diff --git a/src/modules/tls_wolfssl/doc/params.xml b/src/modules/tls_wolfssl/doc/params.xml new file mode 100644 index 00000000000..7c1203eac3e --- /dev/null +++ b/src/modules/tls_wolfssl/doc/params.xml @@ -0,0 +1,1410 @@ + + + + %docentities; + ] +> + +
+ + + + Parameters + +
+ <varname>tls_method</varname> (string) + + Sets the TLS protocol method. Possible values are: + + + + + TLSv1.2+ - TLSv1.2 or newer (TLSv1.3, ...) + connections are accepted (available starting with openssl/libssl v1.1.1) + + + + + TLSv1.2 - only TLSv1.2 connections are accepted + (available starting with openssl/libssl v1.0.1e) + + + + + TLSv1.1+ - TLSv1.1 or newer (TLSv1.2, ...) + connections are accepted (available starting with openssl/libssl v1.0.1) + + + + + TLSv1.1 - only TLSv1.1 connections are accepted + (available starting with openssl/libssl v1.0.1) + + + + + TLSv1+ - TLSv1.0 or newer (TLSv1.1, TLSv1.2, ...) + connections are accepted. + + + + + TLSv1 - only TLSv1 (TLSv1.0) connections are + accepted. This is the default value. + + + + + SSLv3 - only SSLv3 connections are accepted. + Note: you shouldn't use SSLv3 for anything which should be secure. + + + + + SSLv2 - only SSLv2 connections, for old clients. + Note: you shouldn't use SSLv2 for anything which should be secure. + Newer versions of OpenSSL libraries don't include support for it anymore. + + + + + SSLv23 - any of the SSLv2, SSLv3 and TLSv1 or + newer methods will be accepted. + + + From the OpenSSL manual: "A TLS/SSL connection established with these + methods may understand the SSLv3, TLSv1, TLSv1.1 and TLSv1.2 protocols. + If extensions are required (for example server name) a client will + send out TLSv1 client hello messages including extensions and will + indicate that it also understands TLSv1.1, TLSv1.2 and permits a + fallback to SSLv3. A server will support SSLv3, TLSv1, TLSv1.1 + and TLSv1.2 protocols. This is the best choice when compatibility + is a concern." + + + Note: For older OpenSSL library versions, this option allows SSLv2, with hello + messages done over SSLv2. You shouldn't use SSLv2 or SSLv3 for anything + which should be secure. + + + + + If RFC 3261 conformance is desired, at least TLSv1 must be used. For + compatibility with older clients SSLv23 is the option, but again, be aware + of security concerns, SSLv2/3 being considered very insecure by 2014. + For current information about what's considered secure, please consult, + IETF BCP 195, currently RFC 7525 - "Recommendations for Secure Use of + Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)" + + + Set <varname>tls_method</varname> parameter + +... +modparam("tls", "tls_method", "TLSv1") +... + + +
+ +
+ <varname>certificate</varname> (string) + + Sets the certificate file name. The certificate file can also contain + the private key in PEM format. + + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts + with a '/' it will be an absolute path and if it starts with anything + else the path will be relative to the main config file directory + (e.g.: for kamailio -f /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + + The default value is &kamailioconfdir;/cert.pem + + + Set <varname>certificate</varname> parameter + +... +modparam("tls", "certificate", "/usr/local/etc/kamailio/my_certificate.pem") +... + + +
+ +
+ <varname>private_key</varname> (string) + + Sets the private key file name. The private key can be in the same + file as the certificate or in a separate file, specified by this + configuration parameter. + + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts + with a '/' it will be an absolute path and if it starts with anything + else the path will be relative to the main config file directory + (e.g.: for kamailio -f /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + + Note: the private key can be contained in the same file as the + certificate (just append it to the certificate file, e.g.: + cat pkey.pem >> cert.pem) + + + The default value is &kamailioconfdir;/cert.pem + + + Set <varname>private_key</varname> parameter + +... +modparam("tls", "private_key", "/usr/local/etc/kamailio/my_pkey.pem") +... + + +
+ +
+ <varname>ca_list</varname> (string) + + Sets the CA list file name. This file contains a list of all the + trusted CAs certificates used when connecting to other SIP implementations. + If a signature in a certificate chain belongs + to one of the listed CAs, the verification of that certificate will succeed. + + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts + with a '/' it will be an absolute path and if it starts with anything + else the path will be relative to the main config file directory + (e.g.: for kamailio -f /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + + By default the CA file is not set. + + + An easy way to create the CA list is to append each trusted trusted CA + certificate in the PEM format to one file, e.g.: + +for f in trusted_cas/*.pem ; do cat "$f" >> ca_list.pem ; done + + + + See also + verify_certificate, + verify_depth, + require_certificate and + crl. + + + Set <varname>ca_list</varname> parameter + +... +modparam("tls", "ca_list", "/usr/local/etc/kamailio/ca_list.pem") +... + + +
+ +
+ <varname>ca_path</varname> (str) + + Sets the path with the trusted CA files, to be given as parameter + SSL_CTX_load_verify_locations(). The certificates in ca_path are only + looked up when required, e.g. when building the certificate chain + or when actually performing the verification of a peer certificate. They + are not given to the client (not loaded to be provided to + SSL_CTX_set_client_CA_list()), only the ones in ca_list files are sent + to the client. It requires to use c_rehash to generate the hash map + for certificate search, for more see the manual of libssl for + SSL_CTX_load_verify_locations() function. + + + By default it is not set. + + + Set <varname>ca_path</varname> parameter + +... +modparam("tls", "ca_path", "/usr/local/etc/kamailio/ca") +... + + +
+ +
+ <varname>crl</varname> (string) + + Sets the certificate revocation list (CRL) file name. This file contains a + list of revoked certificates. Any attempt to verify a revoked + certificate will fail. + + + If not set, no CRL list will be used. + + + If the file name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts + with a '/' it will be an absolute path and if it starts with anything + else the path will be relative to the main config file directory + (e.g.: for kamailio -f /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + + If set, require_certificate should also be set + or it will not have any effect. + + + By default the CRL file name is not set. + + + To update the CRL in a running &kamailio;, make sure you configure TLS + via a separate TLS config file + (the config modparam) and issue a tls.reload + RPC call, e.g.: + + $ &sercmd; tls.reload + + + + A quick way to create the CRL in PEM format, using OpenSSL is: + + $ openssl ca -gencrl -keyfile cacert.key -cert cacert.pem -out my_crl.pem + + my_crl.pem will contain the signed list of the revoked certificates. + + + To revoke a TLS certificate use something like: + + $ openssl ca -revoke bad_cert.pem -keyfile cacert.key -cert cacert.pem + + and then refresh the crl file using the command above. + + + To display the CRL contents use: + + $ openssl crl -in crl.pem -noout -text + + + + See also + ca_list, + verify_certificate, + verify_depth and + require_certificate. + + + Set <varname>crl</varname> parameter + +... +modparam("tls", "crl", "/usr/local/etc/kamailio/crl.pem") +... + + +
+ +
+ <varname>verify_certificate</varname> (boolean) + + If enabled it will force certificate verification when connecting to + other SIP servers.. + For more information see the + verify(1) + OpenSSL man page. + + + Note: the certificate verification will always fail if the ca_list is empty. + + + See also: ca_list, require_certificate, verify_depth. + + + By default the certificate verification is off. + + + Set <varname>verify_certificate</varname> parameter + +... +modparam("tls", "verify_certificate", 1) +... + + +
+ +
+ <varname>verify_depth</varname> (integer) + + Sets how far up the certificate chain will the certificate verification go in the search for a trusted CA. + + + See also: ca_list, require_certificate, verify_certificate, + + + The default value is 9. + + + Set <varname>verify_depth</varname> parameter + +... +modparam("tls", "verify_depth", 9) +... + + +
+ +
+ <varname>require_certificate</varname> (boolean) + + When enabled &kamailio; will require a certificate from a client + connecting to the TLS port. If the client does not offer a certificate + and verify_certificate is on, certificate verification will fail. + + + The default value is off. + + + Set <varname>require_certificate</varname> parameter + +... +modparam("tls", "require_certificate", 1) +... + + +
+ +
+ <varname>cipher_list</varname> (string) + + Sets the list of accepted ciphers. The list consists of cipher strings separated by colons. + For more information on the cipher list format see the + + cipher(1) OpenSSL man page. + + + The default value is not set (all the OpenSSL supported ciphers are enabled). + + + Set <varname>cipher_list</varname> parameter + +... +modparam("tls", "cipher_list", "HIGH") +... + + +
+ +
+ <varname>server_name</varname> (string) + + Sets the Server Name Indication (SNI) value. + + + This is a TLS extension enabling one TLS server to serve multiple host + names with unique certificates. + + + The default value is empty (not set). + + + Set <varname>server_name</varname> parameter + +... +modparam("tls", "server_name", "kamailio.org") +... + + +
+ +
+ <varname>connection_timeout</varname> (int) + + Sets the amount of time after which an idle TLS connection will be + closed, if no I/O ever occurred after the initial open. If an I/O event + occurs, the timeout will be extended with tcp_connection_lifetime. + The value is expressed in seconds. + + + The default value is 10 min. + + + If the value set is -1, the connection will never be close on idle. + + + This setting can be changed also at runtime, via the RPC interface and config + framework. The config variable name is + tls.connection_timeout. + + + Set <varname>connection_timeout</varname> parameter + +... +modparam("tls", "connection_timeout", 60) +... + + + + Set <varname>tls.connection_timeout</varname> at runtime + + $ &sercmd; cfg.set_now_int tls connection_timeout 180 + + +
+ +
+ <varname>tls_disable_compression</varname> (boolean) + + If set compression over TLS will be disabled. + Note that compression uses a lot of memory (about 10x more then with + the compression disabled), so if you want to minimize + memory usage is a good idea to disable it. TLS compression also + expose you for the + + CRIME security vulnerability. + + + By default TLS compression is disabled. + + + Set <varname>tls_disable_compression</varname> parameter + +... +modparam("tls", "tls_disable_compression", 0) # enable +... + + +
+ + +
+ <varname>ssl_release_buffers</varname> (integer) + + Release internal OpenSSL read or write buffers as soon as they are + no longer needed. Combined with + ssl_freelist_max_len has the potential of saving + a lot of memory ( ~ 32k per connection in the default configuration, + or 16k + ssl_max_send_fragment). + For &kamailio; versions > 3.0 it makes little sense to disable it (0) + since the tls module already has its own internal buffering. + + + A value of -1 would not change this option from its openssl default. + Use 0 or 1 for enable/disable. + + + By default the value is 1 (enabled). + + + + This option is supported only for + OpenSSL versions >= 1.0.0. + On all the other versions attempting + to change the default will trigger an error. + + + + Set <varname>ssl_release_buffers</varname> parameter + +modparam("tls", "ssl_release_buffers", 1) + + +
+ + +
+ <varname>ssl_freelist_max_len</varname> (integer) + + Sets the maximum number of free memory chunks, that OpenSSL will keep + per connection. Setting it to 0 would cause any unused memory chunk + to be immediately freed, reducing the memory footprint. A too large + value would result in extra memory consumption. + + + Should be combined with ssl_release_buffers. + + + A value of -1 has a special meaning: the OpenSSL default will be used + (no attempt on changing the value will be made). For OpenSSL 1.0 + the internal default is 32. + + + By default the value is 0 (no freelist). + + + + This option is supported only for + OpenSSL versions >= 1.0.0. + On all the other versions attempting + to change the default will trigger an error. + + + + Set <varname>ssl_freelist_max_len</varname> parameter + +modparam("tls", "ssl_freelist_max_len", 0) + + +
+ + +
+ <varname>ssl_max_send_fragment</varname> (integer) + + Sets the maximum number of bytes (from the clear text) sent into + one TLS record. Valid values are between 512 and 16384. + Note however that even valid low values might not be big enough to + allow a successful handshake (try minimum 1024). + + + Lower values would lead to less memory usage, but values lower then + the typical &kamailio; write size would incur a slight performance + penalty. Good values are bigger then the size of the biggest + SIP packet one normally expects to forward. For example in most + setups 2048 would be a good value. + + + + Values on the lower side, even if valid (> 512), might not allow + for a successful initial handshake. This happens if the + certificate does not fit inside one send fragment. + Values lower then 1024 should not be used. + Even with higher values, if the handshake fails, + try increasing the value. + + + + A value of -1 has a special meaning: the OpenSSL default will be used + (no attempt on changing the value will be made). + + + By default the value is -1 (the OpenSSL default, which at least in + OpenSSL 1.0.0 is ~ 16k). + + + + This option is supported only for + OpenSSL versions >= 0.9.9. + On all the other versions attempting + to change the default will trigger an error. + + + + Set <varname>ssl_max_send_fragment</varname> parameter + +modparam("tls", "ssl_max_send_fragment", 4096) + + +
+ + +
+ <varname>ssl_read_ahead</varname> (boolean) + + Enables read ahead, reducing the number of internal OpenSSL BIO read() + calls. This option has only debugging value, in normal circumstances + it should not be changed from the default. + + + When disabled OpenSSL will make at least 2 BIO read() calls per + received record: one to get the record header and one to get the + rest of the record. + + + The TLS module buffers internally all read()s and defines its own fast + BIO so enabling this option would only cause more memory consumption + and a minor slow-down (extra memcpy). + + + A value of -1 has a special meaning: the OpenSSL default will be used + (no attempt on changing the value will be made). + + + By default the value is 0 (disabled). + + + Set <varname>ssl_read_ahead</varname> parameter + +modparam("tls", "ssl_read_ahead", 1) + + +
+ + +
+ <varname>send_close_notify</varname> (boolean) + + Enables/disables sending close notify alerts prior to closing the + corresponding TCP connection. Sending the close notify prior to TCP + shutdown is "nicer" from a TLS point of view, but it has a measurable + performance impact. Default: off. Can be set at runtime + (tls.send_close_notify). + + + The default value is 0 (off). + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.send_close_notify. + + + Set <varname>send_close_notify</varname> parameter + +... +modparam("tls", "send_close_notify", 1) +... + + + + Set <varname>tls.send_close_notify</varname> at runtime + + $ &sercmd; cfg.set_now_int tls send_close_notify 1 + + +
+ + +
+ <varname>con_ct_wq_max</varname> (integer) + + Sets the maximum allowed per connection clear-text send queue size in + bytes. This queue is used when data cannot be encrypted and sent + immediately because of an ongoing TLS level renegotiation. + + + The default value is 65536 (64 Kb). + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.con_ct_wq_max. + + + Set <varname>con_ct_wq_max</varname> parameter + +... +modparam("tls", "con_ct_wq_max", 1048576) +... + + + + Set <varname>tls.con_ct_wq_max</varname> at runtime + + $ &sercmd; cfg.set_now_int tls con_ct_wq_max 1048576 + + +
+ + +
+ <varname>ct_wq_max</varname> (integer) + + Sets the maximum total number of bytes queued in all the clear-text + send queues. These queues are used when data cannot be encrypted and + sent immediately because of an ongoing TLS level renegotiation. + + + The default value is 10485760 (10 Mb). + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.ct_wq_max. + + + Set <varname>ct_wq_max</varname> parameter + +... +modparam("tls", "ct_wq_max", 4194304) +... + + + + Set <varname>tls.ct_wq_max</varname> at runtime + + $ &sercmd; cfg.set_now_int tls ct_wq_max 4194304 + + +
+ + +
+ <varname>ct_wq_blk_size</varname> (integer) + + Minimum block size for the internal clear-text send queues + (debugging / advanced tuning). + Good values are multiple of typical datagram sizes. + + + The default value is 4096. + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.ct_wq_blk_size. + + + Set <varname>ct_wq_blk_size</varname> parameter + +... +modparam("tls", "ct_wq_blk_size", 2048) +... + + + + Set <varname>tls.ct_wq_max</varname> at runtime + + $ &sercmd; cfg.set_now_int tls ct_wq_blk_size 2048 + + +
+ + +
+ <varname>tls_log</varname> (int) + + Sets the log level at which TLS related messages will be logged. + + + The default value is 3 (L_DBG). + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.log. + + + Set <varname>tls_log</varname> parameter + +... +# ignore TLS messages if Kamailio is started with debug less than 10 +modparam("tls", "tls_log", 10) +... + + + + Set <varname>tls.log</varname> at runtime + + $ &sercmd; cfg.set_now_int tls log 10 + + +
+ + +
+ <varname>tls_debug</varname> (int) + + Sets the log level at which TLS debug messages will be logged. + Note that TLS debug messages are enabled only if the TLS module + is compiled with debugging enabled (e.g. -DTLS_WR_DEBUG, + -DTLS_RD_DEBUG or -DTLS_BIO_DEBUG). + + + The default value is 3 (L_DBG). + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.debug. + + + Set <varname>tls_debug</varname> parameter + +... +# ignore TLS debug messages if Kamailio is started with debug less than 10 +modparam("tls", "tls_debug", 10) +... + + + + Set <varname>tls.debug</varname> at runtime + + $ &sercmd; cfg.set_now_int tls debug 10 + + +
+ + +
+ <varname>low_mem_threshold1</varname> (integer) + + Sets the minimal free memory from which attempts to open or accept + new TLS connections will start to fail. The value is expressed in KB. + + + The default value depends on whether the OpenSSL library used handles + low memory situations in a good way (openssl bug #1491). + As of this writing this is not true for any OpenSSL version (including 0.9.8e). + + + If an ill-behaved OpenSSL version is detected, a very conservative value is chosen, + which depends on the maximum possible number of simultaneously created TLS connections + (and hence on the process number). + + + The following values have a special meaning: + + + + + -1 - use the default value + + + + + 0 - disable (TLS connections will not fail preemptively) + + + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.low_mem_threshold1. + + + See also tls.low_mem_threshold2. + + + Set <varname>low_mem_threshold1</varname> parameter + +... +modparam("tls", "low_mem_threshold1", -1) +... + + + + Set <varname>tls.low_mem_threshold1</varname> at runtime + + $ &sercmd; cfg.set_now_int tls low_mem_threshold1 2048 + + +
+ +
+ <varname>low_mem_threshold2</varname> (integer) + + Sets the minimal free memory from which TLS operations on already established + TLS connections will start to fail preemptively. The value is expressed in KB. + + + The default value depends on whether the OpenSSL library used handles low memory + situations (openssl bug #1491). + As of this writing this is not true for any OpenSSL version (including 0.9.8e). + + + If an ill-behaved OpenSSL version is detected, a very conservative value is chosen, + which depends on the maximum possible number of simultaneously created + TLS connections (and hence on the process number). + + + The following values have a special meaning: + + + + + -1 - use the default value + + + + + 0 - disable (TLS operations will not fail preemptively) + + + + + It can be changed also at runtime, via the RPC interface and config + framework. The config variable name is tls.low_mem_threshold2. + + + See also tls.low_mem_threshold1. + + + Set <varname>tls.low_mem_threshold2</varname> parameter + +... +modparam("tls", "low_mem_threshold2", -1) +... + + + + Set <varname>tls.low_mem_threshold2</varname> at runtime + + $ &sercmd; cfg.set_now_int tls low_mem_threshold2 1024 + + +
+ +
+ <varname>tls_force_run</varname> (boolean) + + If enabled &kamailio; will start even if some of the OpenSSL sanity checks fail (turn it on at your own risk). + + + If any of the following sanity checks fail, Kamailio will not start: + + + + + the version of the library the TLS module was compiled with is + "too different" from the library used at runtime. The versions should have the same major, + minor and fix level (e.g.: 0.9.8a and 0.9.8c are ok, but 0.9.8 and 0.9.9 are not) + + + + + the OpenSSL library used at compile time and the one used at + runtime have different Kerberos options + + + + + By default tls_force_run is disabled. + + + Set <varname>tls_force_run</varname> parameter + +... +modparam("tls", "tls_force_run", 11) +... + + +
+ +
+ <varname>session_cache</varname> (boolean) + + If enabled &kamailio; will do caching of the TLS sessions data, + generation a session_id and sending it back to client. + + + By default TLS session caching is disabled (0). + + + Set <varname>session_cache</varname> parameter + +... +modparam("tls", "session_cache", 1) +... + + +
+ +
+ <varname>session_id</varname> (str) + + The value for session ID context, making sense when session caching is enabled. + + + By default TLS session_id is "kamailio-tls-5.x.y". + + + Set <varname>session_id</varname> parameter + +... +modparam("tls", "session_id", "my-session-id-context") +... + + +
+ +
+ <varname>renegotiation</varname> (boolean) + + If enabled &kamailio; will allow renegotiations of TLS connection initiated by the client. This may + expose to a security risk if the client is not a trusted peer and keeps renegotiating, consuming CPU + and bandwidth resources. + + + By default TLS renegotiation is disabled (0). + + + Set <varname>renegotiation</varname> parameter + +... +modparam("tls", "renegotiation", 1) +... + + +
+ +
+ <varname>config</varname> (string) + + Sets the name of the TLS specific configuration file or configuration directory. + + + If set the TLS module will load a special configuration file or configuration files + from configuration directory, in which different TLS parameters can be specified on + a per role (server or client) and domain basis (for now only IPs). The corresponding + module parameters will be ignored if a separate configuration file is used. + + + If the file or directory name starts with a '.' the path will be relative to the + working directory (at runtime). If it starts + with a '/' it will be an absolute path and if it starts with anything + else the path will be relative to the main config file directory + (e.g.: for kamailio -f /etc/kamailio/kamailio.cfg it will be relative to /etc/kamailio/). + + + By default no TLS configuration file is specified. + + + The following parameters can be set in the config file, for each + domain: + + + tls_method - (str) - TLS methods + verify_certificate - (bool) - see modparam + require_certificate - (bool) - see modparam + verify_client - (str) - see modparam + private_key - (str) - see modparam + certificate - (str) - see modparam + verify_depth - (int) - see modparam + ca_list - (str) - see modparam + crl - (str) - see modparam + cipher_list - (str) - see modparam + server_name - (str) - SNI (server name identification) + server_name_mode - (int) - how to match server_name + server_id - (str) - server id + + + The value for server_name_mode specifies how to match the server_name + (SNI). If set to 1, match the domain and all its subdomains. If set to + 2, match only the subdomains. If set to 0 (or anything else), match only + the domain given in server_name. + + + The value for server_id can be any string, being used to match TLS + client config profile, overriding the match on ip:port and + server_name. This is the recommended way for selecting a specific + TLS client config profile, because the local or remote port is hard + to predict for a stream connection - see parameter + xavp_cfg to learn how to enable it. + + + All the parameters that take filenames as values will be resolved + using the same rules as for the tls config filename itself: starting + with a '.' means relative to the working directory, a '/' means an + absolute path and anything else a path relative to the directory of + the current &kamailio; main config file. + + + Kamailio acts as a server when it accepts a connection and as a + client when it initiates a new connection by itself (it connects to + something). + + + The tls.cfg consists on a set of server and client TLS domain profiles. + A server TLS domain profile starts with [server:domain]. A client TLS + domain profile starts with [client:domain]. The tokens 'server' and + 'client' are static values. The 'domain' part can be: 'ip:port' - the + IP address and port to match with the TLS connection; 'default' - + (static string) for client and server profiles to be used when + no other profile is matched; 'any' - (static string) for client and + server profiles to be matched based on 'server_name', no matter of + IP and port of the TLS connection. + + + There can be only one of each [server:default] and [client:default] + profile definitions. Other profiles can be defined many times with the + same domain ('ip:port' or 'any'), but in that case they must have + 'server_name' set for matching SNI. + + + It is highly recommended to have [server:default] and [client:default] + profile definitions. They are needed when SNI is not yet available. If + SNI is provided, then the profile definition is searched again to match + on 'server_name'. + + + + Sample TLS Config File + +... +[server:default] +method = TLSv1 +verify_certificate = yes +require_certificate = yes +private_key = default_key.pem +certificate = default_cert.pem +ca_list = default_ca.pem +crl = default_crl.pem + +[client:default] +verify_certificate = yes +require_certificate = yes + +# more relaxed for connection on the loopback interface +[server:127.0.0.1:5061] +method = TLSv1 +verify_certificate = yes +require_certificate = no +private_key = local_kamailio_org_key.pem +certificate = local_kamailio_org_cert.pem +verify_depth = 3 +ca_list = local_ca.pem +server_name = kamailio.org + +[client:127.0.0.1:5061] +method = TLSv1 +verify_certificate = yes +require_certificate = yes +private_key = default_key.pem +certificate = default_cert.pem +ca_list = default_ca.pem +crl = default_crl.pem +server_name = kamailio.org +server_id = kamailio.org + +# server profile on any address +[server:any] +method = TLSv1 +verify_certificate = yes +require_certificate = no +private_key = kamailio_net_key.pem +certificate = kamailio_net_cert.pem +verify_depth = 3 +ca_list = local_ca.pem +server_name = kamailio.net +server_name_mode = 1 +... + + + + For a more complete example check the tls.cfg distributed + with the &kamailio; source (kamailio/modules/tls/tls.cfg). + + + Set <varname>config</varname> parameter + +... +modparam("tls", "config", "/usr/local/etc/kamailio/tls.cfg") +... + + + + The file can be changed at runtime. The new config will not be loaded + immediately, but after the first tls.reload RPC call. + + Change and reload the TLS configuration at runtime + + $ &sercmd; cfg.set_now_string tls config "/usr/local/etc/kamailio/new_tls.cfg" + $ &sercmd; tls.reload + + + +
+ +
+ <varname>xavp_cfg</varname> (string) + + Sets the name of XAVP that stores attributes for TLS connections. + + + The following (inner) attributes can be set: + + + server_name - SNI to be used for outbound connections + server_id - string value to be used to match TLS config profile + for client (outbound) connections. If it is set, matching the TLS config + profile is done first on server_id and then on ip:port and server_name. + This is the recommended way for selecting a specific TLS client config + profile as the local or remote port is hard to predict for a stream + connection. + + + The default value is empty (not set). + + + Set <varname>xavp_cfg</varname> parameter + +... + modparam("tls", "xavp_cfg", "tls") + ... + $xavp(tls=>server_name) = "kamailio.org"; + $xavp(tls[0]=>server_id) = "kamailio.org"; + $du = "sip:kamailio.org:5061;transport=tls"; + route(RELAY); +... + + +
+
+ <varname>event_callback</varname> (str) + + The name of the function in the kemi configuration file (embedded + scripting language such as Lua, Python, ...) to be executed instead + of event_route[...] blocks specific for tls module. + + + The function has one string parameter, the value is the name of + the event_route block, respectively "tls:connection-out". + + + + Default value is 'empty' (no function is executed for events). + + + + Set <varname>event_callback</varname> parameter + +... +modparam("tls", "event_callback", "ksr_tls_event") +... +-- event callback function implemented in Lua +function ksr_tls_event(evname) + KSR.info("===== tls module triggered event: " .. evname .. "\n"); + return 1; +end +... + + +
+ +
+ <varname>rand_engine</varname> (str) + + Set the random number generator engine for libssl. + + + Note: the default random number generator (PRNG) engine of libssl v1.1.x + is not designed for multi-process applications and can result in a crash. + Therefore set the PRNG engine to one of the options listed in this + section. If libssl 1.1.x (or newer) is detected at compile time, then + the PRNG engine is set to "cryptorand". + + + The following options are avaialble: + + + krand - use internal kam_rand() function + fastrand - use internal fastrand (ISAAC) function + cryptorand - use internal cryptorand (Fortuna) function + kxlibssl - default libssl rand engine wrapped by a &kamailio; mutex + + + Note: the krand and fastrand engines are not recommended for use on + systems requiring strong security, as they may not generate numbers + with enough randomness and are not cryptographically secure. + + + The default value is empty (not set) for libssl v1.0.x or older, and + "cryptorand" for libssl v1.1.x or newer. + + + Set <varname>rand_engine</varname> parameter + +... +modparam("tls", "rand_engine", "fastrand") +... + + +
+ +
+ <varname>engine</varname> (string) + + If OpenSSL is compiled with engine support this will allow algorithms to be offloaded and + private keys from HSM to be used. Currently only a single global engine is supported. + However, private keys can be specified per_domain. + + + + To use private keys from the HSM, the name is the HSM key label prefixed by /engine:. + + +... +## example for the Gem engine +modparam("tls", "engine", "gem") +# can also be set per-domain in tls.cfg +modparam("tls", "private_key", "/engine:my_HSM_key_label") + +## example for engine_pkcs11 +modparam("tls", "engine", "pkcs11") +modparam("tls", "private_key", "/engine:pkcs11:token=MYTOKEN;object=MYKEYLABEL") + +modparam("tls", "engine_config", "/usr/local/etc/kamailio/openssl.cnf") +modparam("tls", "engine_algorithms", "ALL") +... + + + By default OpenSSL engine support is disabled (NONE). This global param is not supported in the tls config file. + +
+ +
+ <varname>engine_config</varname> (string) + + A OpenSSL configuration file to initialize the engine. Typically used to send PIN to HSMs to unlock + private keys. See the HSM howto for an example. This global param is not supported in the tls config file. + +
+ + +
+ <varname>engine_algorithms</varname> (string) + + A list of cryptographic methods to be set as default in the engine. + This is a comma-separated list of values + from ALL RSA DSA DH EC RAND CIPHERS DIGESTS PKEY PKEY_CRYPTO PKEY_ASN1. + Not all methods are supported by every engine. + + + The default is not to set any methods as default. This global param is not supported in the tls config file. + +
+ +
+ <varname>verify_client</varname> (string) + + Provides an alternative to verify_certificate and require_certificate modparam and tls.cfg + parameters, and creates an additional opportunistic connection establishment option for connections with + with unverifiable certificates (optional_no_ca). + + + This is useful for allowing connections from SIP phones with self-signed + certificates, signed by unrecognized root CAs, expired certificates, etc. + + + The following values have respective behaviors: + + + off - no client certificate request performed. + on - require a verified certificate from the client. + optional - ask client for certificate. If one is provided, it must + be verified. Allowing missing certificate. + optional_no_ca - ask client for certificate. Opportunistically try to + verify certificate. Allow connection regardless of whether there is + no certificate or whether certificate is present (verified or not). + Note that verification status can be retrieved via $tls_peer_verified. + + + Default value is 'off' (no client certificate request performed). + + + Recommendation: when using this parameter, do not use verify_certificate or + require_certificate parameters. Conversion table is as follows: + + + verify_certificate=0, require_certificate=0 => verify_client="off" + verify_certificate=1, require_certificate=0 => verify_client="optional" + verify_certificate=1, require_certificate=1 => verify_client="on" + + + Set <varname>verify_client</varname> modparam parameter + +... +modparam("tls", "verify_client", "on") +... + + + + Set <varname>verify_client</varname> tls.cfg parameter + +... +[server:1.2.3.4:5061] +method = TLSv1 +verify_client = on +... + +[server:5.6.7.8:5061] +method = TLSv1.2 +verify_client = optional_no_ca +... + + +
+
diff --git a/src/modules/tls_wolfssl/doc/rpc.xml b/src/modules/tls_wolfssl/doc/rpc.xml new file mode 100644 index 00000000000..8f3070648bb --- /dev/null +++ b/src/modules/tls_wolfssl/doc/rpc.xml @@ -0,0 +1,69 @@ + + + +%docentities; + +]> + +
+ + + RPC Commands +
+ <function>tls.info</function> + + List internal information related to the TLS module in + a short list - max connections, open connections and the + write queue size. + + Parameters: + + + None. + + +
+
+ <function>tls.list</function> + + List details about all active TLS connections. + + Parameters: + + + None. + + +
+
+ <function>tls.options</function> + + List the current TLS configuration. + + Parameters: + + + None. + + +
+
+ <function>tls.reload</function> + + Reload the external TLS configuration file (aka tls.cfg). It does not reload + modparam() parameters. Note that existing active TLS connections are not + terminated and they continue to use the old certificates. The new configuration + will be used for new connections. + + Parameters: + + + None. + + +
+ +
diff --git a/src/modules/tls_wolfssl/doc/tls.xml b/src/modules/tls_wolfssl/doc/tls.xml new file mode 100644 index 00000000000..e356d5bff7c --- /dev/null +++ b/src/modules/tls_wolfssl/doc/tls.xml @@ -0,0 +1,367 @@ + + + +%docentities; + +]> + + + + TLS Module + + + Andrei + Pelinescu-Onciul + iptelorg GmbH +
+ andrei@iptel.org +
+
+ + Carsten + Bock + ng-voice GmbH +
+ carsten@ng-voice.com +
+
+ + Olle E. + Johansson + Edvina AB +
+ oej@edvina.net +
+
+
+ + 2007 + iptelorg GmbH + + + 2014 + ng-voice GmbH + +
+ + + + Admin Guide +
+ Overview + + This module implements the TLS transport for &kamailio; + using the OpenSSL library + (http://www.openssl.org). To enable the &kamailio; TLS support this + module must be loaded and enable_tls=yes core setting + must be added to the Kamailio config file. + + + IMPORTANT: the tls module must be loaded before any other &kamailio; module + that uses libssl (OpenSSL library). A safe option is to have the tls module + loaded first (be in the first "loadmodule" in &kamailio;.cfg). + + + IMPORTANT: using this module compiled with newer versions of libssl + (e.g., v1.1+) may require &kamailio; to be started with + --atexit=no command line parameters to avoid + calling C atexit callbacks inside the process ending during + daemonize procedure as well as during shut down, which can lead + to crashes because it destroys and then accesses shared memory. For + example, such case has been reported for Ubuntu 20.04 or RedHat 8. + +
+
+ Quick Start + + The default kamailio.cfg file has basic tls support included, it has to + be enabled with "#!define WITH_TLS" directive. + + + The most important parameters to set the path to the public certificate and private key + files. You can either have them in different file or in the same file in PEM format. + The parameters for them are certificate and private_key. + They can be given as modparam or or provided in the profiles of tls.cfg file. + + + When installing tls module of kamailio, a sample 'tls.cfg' file is deployed in the same + folder with 'kamailio.cfg', along with freshly generated self signed certificates. + + + HINT: be sure you have enable_tls=yes to your kamailio.cfg. + + + Quick Start Basic Config + +#... +loadmodule "sl.so" +loadmodule "tls.so" + +modparam("tls", "private_key", "./server-test.pem") +modparam("tls", "certificate", "./server-test.pem") +modparam("tls", "ca_list", "./calist.pem") + +enable_tls=yes + +request_route { + if(proto != TLS) { + sl_send_reply("403", "Accepting TLS Only"); + exit; + } + ... +} + + +
+ +
+ Important Notes + + The TLS module needs some special options enabled when compiling + Kamailio. These options are enabled by default, however in case + you're using a modified Kamailio version or Makefile, make sure + that you enable -DUSE_TLS and -DTLS_HOOKS (or compile with make + TLS_HOOKS=1 which will take care of both options). + + + To quickly check if your Kamailio version was compiled with these + options, run kamailio -V and look for USE_TLS and TLS_HOOKS among the flags. + + + For OpenSSL (libssl) v1.1.x, it is required to preload + 'openssl_mutex_shared' library shipped by &kamailio;. For more details + see 'src/modules/tls/openssl_mutex_shared/README.md'. + + + This module includes several workarounds for various Openssl bugs + (like compression and Kerberos using the wrong memory allocations + functions, low memory problems a.s.o). On startup it will try to enable + the needed workarounds based on the OpenSSL library version. Each time + a known problem is detected and a workaround is enabled, a message will + be logged. In general it is recommended to compile this module on the + same machine or a similar machine to where kamailio will be run or to + link it statically with libssl. For example if on + the compile machine OpenSSL does not have the Kerberos support enabled, + but on the target machine a Kerberos enabled OpenSSL library is installed, + Kamailio cannot apply the needed workarounds and will refuse to start. + The same thing will happen if the OpenSSL versions are too different + (to force Kamailio startup anyway, see the tls_force_run + module parameter). + + + Compression is fully supported if you have a new enough OpenSSL version + (starting with 0.9.8). Although there are some problems with zlib compression + in currently deployed OpenSSL versions (up to and including 0.9.8d, see + openssl bug #1468), the TLS module will automatically switch to its own fixed version. + Note however that starting with &kamailio; 3.1 compression is not enabled by + default, due to the huge extra memory consumption that it causes (about 10x + more memory). To enable it use + modparam("tls", "tls_disable_compression", 0) + (see tls_disable_compression). + + + The TLS module includes workarounds for the following known openssl bugs: + + + openssl #1204 (disable SS_OP_TLS_BLOCK_PADDING_BUG if compression is enabled, + for versions between 0.9.8 and 0.9.8c), + + + openssl #1468 (fix zlib compression memory allocation), + + + openssl #1467 (kerberos support will be disabled if the openssl version is less than 0.9.8e-beta1) + + + openssl #1491 (stop using tls in low memory situations due to the very high risk of openssl crashing or leaking memory). + + + The bug reports can be viewed at http://rt.openssl.org/. + +
+ + +
+ Compiling the TLS Module + + In most case compiling the TLS module is as simple as: + +make -C modules/tls + + or + +make modules modules=modules/tls + + or (compiling whole Kamailio and the tls module) + +make all include_modules=tls + + . + + + However in some cases the OpenSSL library requires linking with other libraries. + For example compiling the OpenSSL library with Kerberos and zlib-shared support + will require linking the TLS module with libkrb5 and libz. In this case just add + TLS_EXTRA_LIBS="library list" to make's command line. E.g.: + +make TLS_EXTRA_LIBS="-lkrb5 -lz" all include_modules=tls + + + + In general, if Kamailio fails to start with a symbol not found error when trying + to load the TLS module (check the log), it means some needed library was not + linked and it must be added to TLS_EXTRA_LIBS + + + + Elliptic Curve Diffie-Hellman (EDCH)-Ciphers are only supported in OpenSSL 1.0.0e and later. + +
+ +
+ TLS and Low Memory + + The Openssl library doesn't handle low memory situations very well. If memory + allocations start to fail (due to memory shortage), Openssl can crash or cause memory + leaks (making the memory shortage even worse). As of this writing all Openssl versions + were affected (including 0.9.8e), see Openssl bug #1491. The TLS module has some + workarounds for preventing this problem (see low_mem_treshold1 + and low_mem_threshold2), however starting Kamailio with enough shared + memory is higly recommended. When this is not possible a quick way to significantly + reduce Openssl memory usage it to disable compression (see tls_disable_compression). + +
+ +
+ TLS Debugging + + Debugging messages can be selectively enabled by recompiling + the TLS module with a combination of the following defines: + + + + TLS_WR_DEBUG - debug messages for the write/send part. + + + + + TLS_RD_DEBUG - debug messages for the read/receive + part. + + + + + TLS_BIO_DEBUG - debug messages for the custom BIO. + + + + + + Compiling TLS with Debug Messages + +make -C modules/tls extra_defs="-DTLS_WR_DEBUG -DTLS_RD_DEBUG" + + + + To change the level at which the debug messages are logged, + change the tls_debug module parameter. + +
+ +
+ Known Limitations + + The private key must not be encrypted (Kamailio cannot ask you for a password on startup). + + + The TLS certificate verifications ignores the certificate name, Subject Altname + and IP extensions, it just checks if the certificate is signed by a recognized CA. + One can use the select framework to try to overcome this limitation (check in the + script for the contents of various certificate fields), but this is not only slow, + but also not exactly standard conforming (the verification should happen during TLS + connection establishment and not after). + + + TLS specific config reloading is not safe, so for now better don't use it, + especially under heavy traffic. + + + This documentation is incomplete. + The provided selects are not documented in this file. A list with all the + ones implemented by the TLS module can be found in the Cookbook + &kamwiki; in the section + Selects for the respective version of &kamailio;. + +
+ + + + + + + + +
+ Event Routes +
+ event_route[tls:connection-out] + + Event route to be executed when a TLS connection is opened by + &kamailio;. If drop() is executed in the event route, then the + data is no longer sent over the connection. + + + Use of <varname>event_route[tls:connection-out]</varname> + +... +event_route[tls:connection-out] { + if($sndto(ip)=="1.2.3.4") { + drop; + } +} +... + + + +
+
+
+ TLS With Database Backend + + The module does not connect to database to fetch the values for the + TLS profile attributes. However the 'kamcli' tool can generate the tls.cfg + from a database table. Once generated, the 'tls.cfg' can be reloaded with + an RPC command. + + + The kamcli tool can be found at https://github.com/kamailio/kamcli. + + + The schema to create the database table can be seen with the command: "kamcli tls sqlprint". + The default name for database table is 'tlscfg'. + + + The most of the column names matches the corresponding attribute names + from a TLS profile. + + + The profile id in 'tls.cfg' is generated from '[profile_type:profile_name]'. + The 'profile_type' has to be 'server' or 'client'. The 'profile_name' + can be 'default', 'any' or the pair of IP address and port like 'ipaddr:port'. + + + The 'file_type' is specifying if the values for 'certificate', 'private_key', + 'ca_list' and 'crl' are path to files on disc (when is set to 0) or the + content of the files (when set to 1). If 'file_type' is 1, then 'kamcli' + will create new files on disc and store the values from the database in + them. The target folder for 'tls.cfg' and the certificates related files + can be set via command options for 'kamcli tls', for more details see + the output of 'kamcli tls --help' and 'kamcli tls cfgprint --help'. + +
+
+
+ diff --git a/src/modules/tls_wolfssl/sbufq.h b/src/modules/tls_wolfssl/sbufq.h new file mode 100644 index 00000000000..4f6bafa430e --- /dev/null +++ b/src/modules/tls_wolfssl/sbufq.h @@ -0,0 +1,283 @@ +/* + * Kamailio TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/** minimal overhead buffer queue in shm memory. + * @file modules/tls/sbufq.h + * @ingroup: tls + * Module: @ref tls + */ + +#ifndef __sbufq_h +#define __sbufq_h + +#include "../../core/compiler_opt.h" +#include "../../core/ut.h" +#include "../../core/mem/shm_mem.h" +#include "../../core/timer_ticks.h" +#include "../../core/timer.h" +#include "../../core/dprint.h" +#include + + +struct sbuf_elem { + struct sbuf_elem* next; + unsigned int b_size; /**< buf size */ + char buf[1]; /**< variable size buffer */ +}; + +struct sbuffer_queue { + struct sbuf_elem* first; + struct sbuf_elem* last; + ticks_t last_chg; /**< last change (creation time or partial flush)*/ + unsigned int queued; /**< total size */ + unsigned int offset; /**< offset in the first buffer where unflushed data + starts */ + unsigned int last_used; /**< how much of the last buffer is used */ +}; + + +/* sbufq_flush() output flags */ +#define F_BUFQ_EMPTY 1 +#define F_BUFQ_ERROR_FLUSH 2 + + +#define sbufq_empty(bq) ((bq)->first==0) +#define sbufq_non_empty(bq) ((bq)->first!=0) + + + +/** adds/appends data to a buffer queue. + * WARNING: it does no attempt to synchronize access/lock. If needed it should + * be called under lock. + * @param q - buffer queue + * @param data + * @param size + * @param min_buf_size - min size to allocate for new buffer elements + * @return 0 on success, -1 on error (mem. allocation) + */ +inline static int sbufq_add(struct sbuffer_queue* q, const void* data, + unsigned int size, unsigned int min_buf_size) +{ + struct sbuf_elem* b; + unsigned int last_free; + unsigned int b_size; + unsigned int crt_size; + + if (likely(q->last==0)) { + b_size=MAX_unsigned(min_buf_size, size); + b=shm_malloc(sizeof(*b)+b_size-sizeof(b->buf)); + if (unlikely(b==0)) + goto error; + b->b_size=b_size; + b->next=0; + q->last=b; + q->first=b; + q->last_used=0; + q->offset=0; + q->last_chg=get_ticks_raw(); + last_free=b_size; + crt_size=size; + goto data_cpy; + }else{ + b=q->last; + } + + while(size){ + last_free=b->b_size-q->last_used; + if (last_free==0){ + b_size=MAX_unsigned(min_buf_size, size); + b=shm_malloc(sizeof(*b)+b_size-sizeof(b->buf)); + if (unlikely(b==0)) + goto error; + b->b_size=b_size; + b->next=0; + q->last->next=b; + q->last=b; + q->last_used=0; + last_free=b->b_size; + } + crt_size=MIN_unsigned(last_free, size); +data_cpy: + memcpy(b->buf+q->last_used, data, crt_size); + q->last_used+=crt_size; + size-=crt_size; + data+=crt_size; + q->queued+=crt_size; + } + return 0; +error: + return -1; +} + + + +/** inserts data (at the beginning) in a buffer queue. + * Note: should never be called after sbufq_run(). + * WARNING: it does no attempt to synchronize access/lock. If needed it should + * be called under lock. + * @param q - buffer queue + * @param data + * @param size + * @param min_buf_size - min size to allocate for new buffer elements + * @return 0 on success, -1 on error (mem. allocation) + */ +inline static int sbufq_insert(struct sbuffer_queue* q, const void* data, + unsigned int size, unsigned int min_buf_size) +{ + struct sbuf_elem* b; + + if (likely(q->first==0)) /* if empty, use sbufq_add */ + return sbufq_add(q, data, size, min_buf_size); + + if (unlikely(q->offset)){ + LOG(L_CRIT, "BUG: non-null offset %d (bad call, should" + "never be called after sbufq_run())\n", q->offset); + goto error; + } + if ((q->first==q->last) && ((q->last->b_size-q->last_used)>=size)){ + /* one block with enough space in it for size bytes */ + memmove(q->first->buf+size, q->first->buf, size); + memcpy(q->first->buf, data, size); + q->last_used+=size; + }else{ + /* create a size bytes block directly */ + b=shm_malloc(sizeof(*b)+size-sizeof(b->buf)); + if (unlikely(b==0)) + goto error; + b->b_size=size; + /* insert it */ + b->next=q->first; + q->first=b; + memcpy(b->buf, data, size); + } + + q->queued+=size; + return 0; +error: + return -1; +} + + +/** destroy a buffer queue. + * Only the content is destroyed (shm_free()'d), the queue head is + * re-initialized. + * WARNING: it does no attempt to synchronize access/lock. If needed it should + * be called under lock. + * @param q - buffer queue + * @return - number of bytes that used to be queued (>=0). + */ +inline static unsigned int sbufq_destroy(struct sbuffer_queue* q) +{ + struct sbuf_elem* b; + struct sbuf_elem* next_b; + int unqueued; + + unqueued=0; + if (likely(q->first)){ + b=q->first; + do{ + next_b=b->next; + unqueued+=(b==q->last)?q->last_used:b->b_size; + if (b==q->first) + unqueued-=q->offset; + shm_free(b); + b=next_b; + }while(b); + } + memset(q, 0, sizeof(*q)); + return unqueued; +} + + + +/** tries to flush the queue. + * Tries to flush as much as possible from the given queue, using the + * given callback. + * WARNING: it does no attempt to synchronize access/lock. If needed it should + * be called under lock. + * @param q - buffer queue + * @param *flags - set to: + * F_BUFQ_EMPTY if the queued is completely flushed + * F_BUFQ_ERROR_FLUSH if the flush_f callback returned error. + * @param flush_f - flush function (callback). modeled after write(): + * flush_f(param1, param2, const void* buf, unsigned size). + * It should return the number of bytes "flushed" on + * success, or <0 on error. If the number of bytes + * "flushed" is smaller then the requested size, it + * would be assumed that no more bytes can be flushed + * and sbufq_flush will exit. + * @param flush_p1 - parameter for the flush function callback. + * @param flush_p2 - parameter for the flush function callback. + * @return -1 on internal error, or the number of bytes flushed on + * success (>=0). Note that the flags param is + * always set and it should be used to check for errors, since + * a flush_f() failure will not result in a negative return. + */ +inline static int sbufq_flush(struct sbuffer_queue* q, int* flags, + int (*flush_f)(void* p1, void* p2, + const void* buf, + unsigned size), + void* flush_p1, void* flush_p2) +{ + struct sbuf_elem *b; + int n; + int ret; + int block_size; + char* buf; + + *flags=0; + ret=0; + while(q->first){ + block_size=((q->first==q->last)?q->last_used:q->first->b_size)- + q->offset; + buf=q->first->buf+q->offset; + n=flush_f(flush_p1, flush_p2, buf, block_size); + if (likely(n>0)){ + ret+=n; + if (likely(n==block_size)){ + b=q->first; + q->first=q->first->next; + shm_free(b); + q->offset=0; + q->queued-=block_size; + }else{ + q->offset+=n; + q->queued-=n; + /* no break: if we are here n < block_size => partial write + => the write should be retried */ + } + }else{ + if (unlikely(n<0)) + *flags|=F_BUFQ_ERROR_FLUSH; + break; + } + } + if (likely(q->first==0)){ + q->last=0; + q->last_used=0; + q->offset=0; + *flags|=F_BUFQ_EMPTY; + } + return ret; +} + + + + +#endif /*__sbufq_h*/ + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls.cfg b/src/modules/tls_wolfssl/tls.cfg new file mode 100644 index 00000000000..1bfdfc69199 --- /dev/null +++ b/src/modules/tls_wolfssl/tls.cfg @@ -0,0 +1,106 @@ +# +# Example Kamailio TLS Configuration File +# + +# --- +# This is the default server domain profile. +# Settings in this domain will be used for all incoming +# connections that do not match any other server +# domain in this configuration file. +# +# We do not enable anything else than TLSv1.2+ +# over the public internet. Clients do not have +# to present client certificates by default. +# +[server:default] +method = TLSv1.2+ +verify_certificate = no +require_certificate = no +private_key = /usr/local/etc/kamailio/kamailio-selfsigned.key +certificate = /usr/local/etc/kamailio/kamailio-selfsigned.pem +#ca_list = /usr/local/etc/kamailio/tls/cacert.pem +#crl = /usr/local/etc/kamailio/tls/crl.pem + +# --- +# This is the default client domain profile. +# Settings in this domain will be used for all outgoing +# TLS connections that do not match any other +# client domain in this configuration file. +# We require that servers present valid certificate. +# +[client:default] +#method = TLSv1.2+ +verify_certificate = yes +require_certificate = yes + +# --- +# This is an example server domain for TLS connections +# received from the loopback interface. We allow +# the use of TLSv1.2+ protocols here, we do +# not require that clients present client certificates +# but if they present it it must be valid. We also use +# a special certificate and CA list for loopback +# interface. +# +#[server:5.6.7.8:5061] +#method = TLSv1.2+ +#verify_certificate = yes +#require_certificate = no +#private_key = /usr/local/etc/kamailio/tls/local_key.pem +#certificate = /usr/local/etc/kamailio/tls/local_cert.pem +#verify_depth = 3 +#ca_list = /usr/local/etc/kamailio/tls/local_ca.pem +#crl = /usr/local/etc/kamailio/tls/local_crl.pem +#server_name = kamailio.org +#server_id = kamailio.org + +# --- +# Special settings for connecting to the example.sip (1.2.3.4) +# public SIP server. We do not verify the certificate of the +# server because it can be expired. The server +# implements authentication using SSL client +# certificates so configure the client certificate +# that was given to use by iptel.org staff here. +# +#[client:1.2.3.4:5061] +#verify_certificate = no +#private_key = /usr/local/etc/kamailio/tls/example_key.pem +#certificate = /usr/local/etc/kamailio/tls/example_cert.pem +#ca_list = /usr/local/etc/kamailio/tls/example_ca.pem +#crl = /usr/local/etc/kamailio/tls/example_crl.pem +#server_name = example.sip +#server_id = example.sip + +# --- +# Example server profile for listening on any ip/port +# - it requires to have 'server_name' to match on SNI (domain and subdomains) +# +#[server:any] +#method = TLSv1.2+ +#verify_certificate = yes +#require_certificate = no +#private_key = /usr/local/etc/kamailio/tls/mysipserver_org_key.pem +#certificate = /usr/local/etc/kamailio/tls/mysipserver_org_cert.pem +#verify_depth = 3 +#ca_list = /usr/local/etc/kamailio/tls/mysipserver_org_ca.pem +#crl = /usr/local/etc/kamailio/tls/mysipserver_org_crl.pem +#server_name = mysipserver.org +#server_name_mode = 1 +#server_id = mysipserver.org + +# --- +# Example server profile for listening on any ip/port +# - it requires to have 'server_name' to match on SNI (only subdomains) +# +#[server:any] +#method = TLSv1.2+ +#verify_certificate = yes +#require_certificate = no +#private_key = /usr/local/etc/kamailio/tls/mysipserver_net_key.pem +#certificate = /usr/local/etc/kamailio/tls/mysipserver_net_cert.pem +#verify_depth = 3 +#ca_list = /usr/local/etc/kamailio/tls/mysipserver_net_ca.pem +#crl = /usr/local/etc/kamailio/tls/mysipserver_net_crl.pem +#server_name = mysipserver.net +#server_name_mode = 2 +#server_id = mysipserver.net diff --git a/src/modules/tls_wolfssl/tls_bio.c b/src/modules/tls_wolfssl/tls_bio.c new file mode 100644 index 00000000000..1cb0ff1f8de --- /dev/null +++ b/src/modules/tls_wolfssl/tls_bio.c @@ -0,0 +1,314 @@ +/* + * Kamailio TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * openssl BIOs for reading/writing via a fixed memory buffer. + * @file + * @ingroup tls + */ + +#include "tls_bio.h" +#include "../../core/compiler_opt.h" +#include "../../core/dprint.h" +#include "../../core/ut.h" +#include "tls_cfg.h" + +/* 0xf2 should be unused (as of openssl 1.0.0 max. + internal defined BIO is 23) */ +#define BIO_TYPE_TLS_MBUF 0x4f2 + +/* debugging */ +#ifdef NO_TLS_BIO_DEBUG +#undef TLS_BIO_DEBUG +#endif +#ifdef TLS_BIO_DEBUG + #ifdef __SUNPRO_C + #define TLS_BIO_DBG(...) \ + LOG_FP(DEFAULT_FACILITY, cfg_get(tls, tls_cfg, debug),\ + "tls_BIO: " LOC_INFO, __VA_ARGS__) + #else + #define TLS_BIO_DBG(args...) \ + LOG_FP(DEFAULT_FACILITY, cfg_get(tls, tls_cfg, debug),\ + "tls_BIO: " LOC_INFO, ## args) + #endif /* __SUNPRO_c */ +#else /* TLS_BIO_DEBUG */ + #ifdef __SUNPRO_C + #define TLS_BIO_DBG(...) + #else + #define TLS_BIO_DBG(fmt, args...) + #endif /* __SUNPRO_c */ +#endif /* TLS_BIO_DEBUG */ + + +static int tls_bio_mbuf_new(WOLFSSL_BIO* b); +static int tls_bio_mbuf_free(WOLFSSL_BIO* b); +static int tls_bio_mbuf_write(WOLFSSL_BIO* b, const char* buf, int num); +static int tls_bio_mbuf_read(WOLFSSL_BIO* b, char* buf, int num); +static int tls_bio_mbuf_puts(WOLFSSL_BIO* b, const char* s); +static long tls_bio_mbuf_ctrl(WOLFSSL_BIO* b, int cmd, long arg1, void* arg2); + + +static WOLFSSL_BIO_METHOD *tls_mbuf_method = NULL; + + +/** returns a custom tls_mbuf BIO. */ +WOLFSSL_BIO_METHOD* tls_BIO_mbuf(void) +{ + if(tls_mbuf_method != NULL) { + return tls_mbuf_method; + } + tls_mbuf_method = wolfSSL_BIO_meth_new(BIO_TYPE_TLS_MBUF, "sr_tls_mbuf"); + if(tls_mbuf_method==NULL) { + LM_ERR("cannot get a new bio method structure\n"); + return NULL; + } + wolfSSL_BIO_meth_set_write(tls_mbuf_method, tls_bio_mbuf_write); + wolfSSL_BIO_meth_set_read(tls_mbuf_method, tls_bio_mbuf_read); + wolfSSL_BIO_meth_set_puts(tls_mbuf_method, tls_bio_mbuf_puts); + wolfSSL_BIO_meth_set_gets(tls_mbuf_method, NULL); + wolfSSL_BIO_meth_set_ctrl(tls_mbuf_method, tls_bio_mbuf_ctrl); + wolfSSL_BIO_meth_set_create(tls_mbuf_method, tls_bio_mbuf_new); + wolfSSL_BIO_meth_set_destroy(tls_mbuf_method, tls_bio_mbuf_free); + return tls_mbuf_method; +} + + + +/** create an initialize a new tls_BIO_mbuf. + * @return new BIO on success (!=0), 0 on error. + */ +WOLFSSL_BIO* tls_BIO_new_mbuf(struct tls_mbuf* rd, struct tls_mbuf* wr) +{ + WOLFSSL_BIO* ret; + + TLS_BIO_DBG("tls_BIO_new_mbuf called (%p, %p)\n", rd, wr); + ret = wolfSSL_BIO_new(tls_BIO_mbuf()); + if (unlikely(ret == 0)) + return 0; + if (unlikely(tls_BIO_mbuf_set(ret, rd, wr) == 0)) { + wolfSSL_BIO_free(ret); + return 0; + } + return ret; +} + + + +/** sets the read and write mbuf for an mbuf BIO. + * @return 1 on success, 0 on error (openssl BIO convention). + */ +int tls_BIO_mbuf_set(BIO* b, struct tls_mbuf* rd, struct tls_mbuf* wr) +{ + struct tls_bio_mbuf_data* d; + + TLS_BIO_DBG("tls_BIO_mbuf_set called (%p => %p, %p)\n", b, rd, wr); + d = wolfSSL_BIO_get_data(b); + if (unlikely(d == 0)){ + BUG("null BIO ptr data\n"); + return 0; + } + d->rd = rd; + d->wr = wr; + wolfSSL_BIO_set_init(b, 1); + return 1; +} + + + +/** create a new BIO. + * (internal openssl use via the tls_mbuf method) + * @return 1 on success, 0 on error. + */ +static int tls_bio_mbuf_new(BIO* b) +{ + struct tls_bio_mbuf_data* d; + + TLS_BIO_DBG("tls_bio_mbuf_new called (%p)\n", b); + wolfSSL_BIO_set_init(b, 0); + wolfSSL_BIO_set_data(b, NULL); + d = wolfSSL_Malloc(sizeof(*d)); + memset(d, 0, sizeof(*d)); + if (unlikely(d == 0)) + return 0; + wolfSSL_BIO_set_data(b, d); + return 1; +} + + + +/** destroy a tls mbuf BIO. + * (internal openssl use via the tls_mbuf method) + * @return 1 on success, 0 on error. + */ +static int tls_bio_mbuf_free(BIO* b) +{ + TLS_BIO_DBG("tls_bio_mbuf_free called (%p)\n", b); + if (unlikely( b == 0)) + return 0; + do { + struct tls_bio_mbuf_data* d; + d = wolfSSL_BIO_get_data(b); + if (likely(d)) { + OPENSSL_free(d); + wolfSSL_BIO_set_data(b, NULL); + wolfSSL_BIO_set_init(b, 0); + } + } while(0); + return 1; +} + + + +/** read from a mbuf. + * (internal openssl use via the tls_mbuf method) + * @return bytes read on success (0< ret <=dst_len), -1 on empty buffer & sets + * should_retry_read, -1 on some other errors (w/o should_retry_read set). + */ +static int tls_bio_mbuf_read(BIO* b, char* dst, int dst_len) +{ + struct tls_bio_mbuf_data* d; + struct tls_mbuf* rd; + int ret; + + ret = 0; + if (likely(dst)) { + d = wolfSSL_BIO_get_data(b); + wolfSSL_BIO_clear_retry_flags(b); + if (unlikely(d == 0 || d->rd->buf == 0)) { + if (d == 0) + BUG("tls_BIO_mbuf %p: read called with null b->ptr\n", b); + else { + /* this form of calling read with a null buffer is used + as a shortcut when no data is available => + simulate EAGIAN/WANT_READ */ + TLS_BIO_DBG("read (%p, %p, %d) called with null read buffer" + "(%p->%p) => simulating EAGAIN/WANT_READ\n", + b, dst, dst_len, d, d->rd); + BIO_set_retry_read(b); + } + return -1; + } + rd = d->rd; + if (unlikely(rd->used == rd->pos && dst_len)) { + /* mimic non-blocking read behaviour */ + TLS_BIO_DBG("read (%p, %p, %d) called with full rd (%d)" + " => simulating EAGAIN/WANT_READ\n", + b, dst, dst_len, rd->used); + BIO_set_retry_read(b); + return -1; + } + ret = MIN_int(rd->used - rd->pos, dst_len); + /* copy data from rd.buf into dst */ + memcpy(dst, rd->buf+rd->pos, ret); + TLS_BIO_DBG("read(%p, %p, %d) called with rd=%p pos=%d => %d bytes\n", + b, dst, dst_len, rd->buf, rd->pos, ret); + rd->pos += ret; +/* if (unlikely(rd->pos < rd->used)) + wolfSSL_BIO_set_retry_read(b); +*/ + } + return ret; +} + + + +/** write to a mbuf. + * (internal openssl use via the tls_mbuf method) + * @return bytes written on success (0<= ret <=src_len), -1 on error or buffer + * full (in this case sets should_retry_write). + */ +static int tls_bio_mbuf_write(BIO* b, const char* src, int src_len) +{ + struct tls_bio_mbuf_data* d; + struct tls_mbuf* wr; + int ret; + + ret = 0; + d = wolfSSL_BIO_get_data(b); + wolfSSL_BIO_clear_retry_flags(b); + if (unlikely(d == 0 || d->wr->buf == 0)) { + if (d == 0) + BUG("tls_BIO_mbuf %p: write called with null b->ptr\n", b); + else { + /* this form of calling write with a null buffer is used + as a shortcut when no data is available => + simulate EAGAIN/WANT_WRITE */ + TLS_BIO_DBG("write (%p, %p, %d) called with null buffer" + " => simulating WANT_WRITE\n", b, src, src_len); + BIO_set_retry_write(b); + } + return -1; + } + wr = d->wr; + if (unlikely(wr->size == wr->used && src_len)) { + /* mimic non-blocking socket behaviour */ + TLS_BIO_DBG("write (%p, %p, %d) called with full wr buffer (%d)" + " => simulating WANT_WRITE\n", b, src, src_len, wr->used); + BIO_set_retry_write(b); + return -1; + } + ret = MIN_int(wr->size - wr->used, src_len); + memcpy(wr->buf + wr->used, src, ret); + wr->used += ret; +/* if (unlikely(ret < src_len)) + wolfSSL_BIO_set_retry_write(); +*/ + TLS_BIO_DBG("write called (%p, %p, %d) => %d\n", b, src, src_len, ret); + return ret; +} + + + +static long tls_bio_mbuf_ctrl(WOLFSSL_BIO* b, int cmd, long arg1, void* arg2) +{ + long ret; + ret=0; + switch(cmd) { + case BIO_CTRL_GET_CLOSE: + case BIO_CTRL_SET_CLOSE: + ret = 0; + break; + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: + ret = 1; + break; + case BIO_CTRL_RESET: + case BIO_CTRL_INFO: + case BIO_CTRL_PENDING: + case BIO_CTRL_WPENDING: + default: + ret = 0; + break; + } + TLS_BIO_DBG("ctrl called (%p, %d, %ld, %p) => %ld\n", + b, cmd, arg1, arg2, ret); + return ret; +} + + + +static int tls_bio_mbuf_puts(BIO* b, const char* s) +{ + int len; + + TLS_BIO_DBG("puts called (%p, %s)\n", b, s); + len=strlen(s); + return tls_bio_mbuf_write(b, s, len); +} + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_bio.h b/src/modules/tls_wolfssl/tls_bio.h new file mode 100644 index 00000000000..946babbba0d --- /dev/null +++ b/src/modules/tls_wolfssl/tls_bio.h @@ -0,0 +1,68 @@ +/* + * Kamailio TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** openssl BIOs for reading/writing via a fixed memory buffer. + * @file modules/tls/tls_bio.h + * @ingroup tls + */ + +#ifndef __tls_bio_h +#define __tls_bio_h + +#include +#include + +/* memory buffer used for tls I/O */ +struct tls_mbuf { + unsigned char* buf; + int pos; /**< current position in the buffer while reading or writing*/ + int used; /**< how much it's used (read or write)*/ + int size; /**< total buffer size (fixed) */ +}; + +struct tls_bio_mbuf_data { + struct tls_mbuf* rd; + struct tls_mbuf* wr; +}; + + +WOLFSSL_BIO_METHOD* tls_BIO_mbuf(void); +WOLFSSL_BIO* tls_BIO_new_mbuf(struct tls_mbuf* rd, struct tls_mbuf* wr); +int tls_BIO_mbuf_set(BIO* b, struct tls_mbuf* rd, struct tls_mbuf* wr); + + + +/** initialize an mbuf structure. + * @param mb - struct tls_mbuf pointer that will be initialized. + * @param b - buffer (unsigned char*). + * @param sz - suze of the buffer (int). + * WARNING: the buffer will not be copied, but referenced. + */ +#define tls_mbuf_init(mb, b, sz) \ + do { \ + (mb)->buf = (b); \ + (mb)->size = (sz); \ + (mb)->pos = 0; \ + (mb)->used = 0; \ + } while(0) + + + +#endif /*__tls_bio_h*/ + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_cert.sh b/src/modules/tls_wolfssl/tls_cert.sh new file mode 100755 index 00000000000..dc9ea243809 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_cert.sh @@ -0,0 +1,201 @@ +#!/bin/sh +# +# $Id$ +# +# This script generates a self-signed TLS/SSL certificate that can be +# immediately used with the TLS module of SIP Router. The file was inspired +# by a script from Debian's uw-imapd package. +# + +############################################################################# +# Configuration variables +############################################################################# +NAME=$MAIN_NAME +if [ -z "$NAME" ] ; then NAME="kamailio"; fi; +DEFAULT_DIR="/usr/local/etc/$NAME" +DEFAULT_DAYS=365 +DEFAULT_INFO="Self-signed certificate for $NAME" +DEFAULT_CERT_FILENAME="$NAME-selfsigned.pem" +DEFAULT_KEY_FILENAME="$NAME-selfsigned.key" + +DEFAULT_OPENSSL='openssl' + +HOSTNAME=`hostname -s` +if hostname -f >/dev/null 2>/dev/null ; then + FQDN=`hostname -f` +else + FQDN=`hostname` +fi +MAILNAME=`cat /etc/mailname 2> /dev/null || echo $FQDN` + +# test if we have the normal or enhanced getopt +getopt -T >/dev/null +if [ $? = 4 ]; then + LONGOPTS_SUPPORTED=1 +fi + +longopts() { + if [ -z "${LONGOPTS_SUPPORTED}" ]; then + exit; + fi + case "$1" in + -h) echo ', --help';; + -d) echo ', --dir' ;; + -c) echo ', --certificate';; + -k) echo ', --key';; + -e) echo ', --expires';; + -i) echo ', --info';; + -o) echo ', --overwrite' ;; + esac +} + +usage() { +cat < + +REPORTING BUGS + Report bugs to +EOF +} #usage + + +COMMAND=`basename $0` +if [ -z "$DIR" ] ; then DIR=$DEFAULT_DIR; fi; +if [ -z "$DAYS" ] ; then DAYS=$DEFAULT_DAYS; fi; +if [ -z "$INFO" ] ; then INFO=$DEFAULT_INFO; fi; +if [ -z "$CERT_FILENAME" ] ; then CERT_FILENAME=$DEFAULT_CERT_FILENAME; fi; +if [ -z "$KEY_FILENAME" ] ; then KEY_FILENAME=$DEFAULT_KEY_FILENAME; fi; +if [ -z "$OPENSSL" ] ; then OPENSSL=$DEFAULT_OPENSSL; fi; + +if [ -n "${LONGOPTS_SUPPORTED}" ]; then + # enhanced version + TEMP=`getopt -o hd:c:k:e:i:o --long help,dir:,certificate:,key:,expires:,info:,overwrite -n $COMMAND -- "$@"` +else + # basic version + TEMP=`getopt hd:c:k:e:i:o "$@"` +fi +if [ $? != 0 ] ; then exit 1; fi +eval set -- "$TEMP" + +while true ; do + case "$1" in + -h|--help) usage; exit 0 ;; + -d|--dir) DIR=$2; shift 2 ;; + -c|--certificate) CERT_FILENAME=$2; shift 2 ;; + -k|--key) KEY_FILENAME=$2; shift 2 ;; + -e|--expires) DAYS=$2; shift 2 ;; + -i|--info) INFO=$2; shift 2 ;; + -o|--overwrite) OVERWRITE=1; shift ;; + --) shift; break ;; + *) echo "Internal error"; exit 1 ;; + esac +done + +TEMP=`which $OPENSSL` +if [ $? != 0 ] ; then + echo "Could not find openssl command" + echo "Set OPENSSL environment variable properly (see -h for more info)" + exit 1 +fi + +if [ ! -d "$DIR" ] ; then + echo "Directory '$DIR' does not exist." + exit 1 +fi + +if [ -z "$OVERWRITE" -a \( -f "$DIR/$CERT_FILENAME" \) ] ; then + echo "File '$DIR/$CERT_FILENAME' already exists, doing nothing." + echo "(Use -o to override)" + exit 0; +fi + + +if [ -z "$OVERWRITE" -a \( -f "$DIR/$KEY_FILENAME" \) ] ; then + echo "File '$DIR/$KEY_FILENAME' already exists, doing nothing." + echo "(Use -o to override)." + exit 0; +fi + +touch "$DIR/$CERT_FILENAME" > /dev/null 2>&1 +if [ $? != 0 ] ; then + echo "Could not create file '$DIR/$CERT_FILENAME'" + exit 1 +fi + +touch "$DIR/$KEY_FILENAME" > /dev/null 2>&1 +if [ $? != 0 ] ; then + echo "Could not create file '$DIR/$KEY_FILENAME'" + rm -f "$DIR/$CERT_FILE" + exit 1 +fi + +echo "Creating a new $NAME self-signed certificate for '$FQDN'" \ + "valid for $DAYS days." +openssl req -new -x509 -days "$DAYS" -nodes -out "$DIR/$CERT_FILENAME" \ + -keyout "$DIR/$KEY_FILENAME" > /dev/null 2>&1 <<+ +. +. +. +$INFO +$HOSTNAME +$FQDN +root@$MAILNAME ++ + +if [ $? != 0 ] ; then + echo "Error while executing openssl command." + rm -f "$DIR/$CERT_FILE" "$DIR/$KEY_FILE" + exit 1; +else + echo "Private key stored in '$DIR/$KEY_FILENAME'." + echo "Certificate stored in '$DIR/$CERT_FILENAME'." + exit 0; +fi diff --git a/src/modules/tls_wolfssl/tls_cfg.c b/src/modules/tls_wolfssl/tls_cfg.c new file mode 100644 index 00000000000..1677665668d --- /dev/null +++ b/src/modules/tls_wolfssl/tls_cfg.c @@ -0,0 +1,289 @@ +/* + * Kamailio TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * Copyright (C) 2013 Motorola Solutions, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * Kamailio TLS support :: tls runtime configuration + * @file tls_cfg.c + * @ingroup tls + * Module: @ref tls + */ + +#include "tls_cfg.h" +#include "../../core/config.h" +#include "../../core/str.h" +#include "../../core/ut.h" +#include "../../core/pt.h" +#include "../../core/mem/shm_mem.h" +#include "../../core/dprint.h" + +struct cfg_group_tls default_tls_cfg = { + 0, /* tls_force_run */ + STR_STATIC_INIT("TLSv1"), /* method */ + STR_NULL, /* server name (sni) */ + 0, /* server name (sni) mode */ + STR_NULL, /* server id */ + 0, /* verify_certificate */ + 9, /* verify_depth */ + 0, /* require_certificate */ + STR_STATIC_INIT("off"), /* verify_client */ + STR_NULL, /* private_key (default value set in fix_tls_cfg) */ + STR_NULL, /* ca_list (default value set in fix_tls_cfg) */ + STR_NULL, /* ca_path (default value set in fix_tls_cfg) */ + STR_NULL, /* crl (default value set in fix_tls_cfg) */ + STR_NULL, /* certificate (default value set in fix_tls_cfg) */ + STR_NULL, /* cipher_list (default value set in fix_tls_cfg) */ + 0, /* session_cache */ + STR_STATIC_INIT("kamailio-tls-5.x.y"), /* session_id */ + STR_NULL, /* config_file */ + 3, /* log (L_DBG)*/ + 3, /* debug (L_DBG) */ + 600, /* con_lifetime (s)*/ + 1, /* disable_compression */ + 1, /* ssl_release_buffers (on, avoid extra buffering) */ + 0, /* ssl_freelist_max (immediately free) */ + -1, /* ssl_max_send_fragment (use the default: 16k), requires openssl + > 0.9.9 */ + 0, /* ssl_read_ahead (off, not needed, we have our own buffering BIO)*/ + -1, /* low_mem_threshold1 */ + -1, /* low_mem_threshold2 */ + 10*1024*1024, /* ct_wq_max: 10 Mb by default */ + 64*1024, /* con_ct_wq_max: 64Kb by default */ + 4096, /* ct_wq_blk_size */ + 0 /* send_close_notify (off by default)*/ +}; + +volatile void* tls_cfg = &default_tls_cfg; + + +/* if *to<0 to=default_val, else if to>max_val to=max_val */ +static void fix_timeout(char* name, int* to, int default_val, unsigned max_val) +{ + if (*to < 0) *to=default_val; + else if ((unsigned)*to > max_val){ + WARN("%s: timeout too big (%u), the maximum value is %u\n", + name, *to, max_val); + *to=max_val; + } +} + + +static int fix_con_lt(void* cfg_h, str* gname, str* name, void** val) +{ + int v; + v=S_TO_TICKS((int)(long)*val); + fix_timeout("tls.connection_timeout", &v, + MAX_TLS_CON_LIFETIME, MAX_TLS_CON_LIFETIME); + *val=(void*)(long)v; + return 0; +} + + + +/** cfg framework callback for fixing pathnames. */ +static int fix_rel_pathname(void* cfg_h, str* gname, str* name, void** val) +{ + str* f; + str new_f; + /* the cfg framework will immediately "clone" the value so + we can pass a pointer to a temporary static buffer to it + (using a dyn. alloc'd buffer would introduce the additional + problem of having to free it somehow) */ + static char path_buf[MAX_PATH_SIZE]; + + f = *val; + /* use absolute paths, except when the path starts with + '.' (in this case leave it as it is) */ + if (f && f->s && f->len && *f->s != '.' && *f->s != '/') { + new_f.s = get_abs_pathname(0, f); + if (new_f.s == 0) + return -1; + new_f.len = strlen(new_f.s); + if (new_f.len >= MAX_PATH_SIZE) { + ERR("%.*s.%.*s path too long (%d bytes): \"%.*s\"\n", + gname->len, gname->s, name->len, name->s, + new_f.len, new_f.len, new_f.s); + pkg_free(new_f.s); + return -1; + } + memcpy(path_buf, new_f.s, new_f.len); + pkg_free(new_f.s); + new_f.s = path_buf; + *f = new_f; + } + return 0; +} + + + +cfg_def_t tls_cfg_def[] = { + {"force_run", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0, + "force loading the tls module even when initial sanity checks fail"}, + {"method", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "TLS method used (TLSv1.2, TLSv1.1, TLSv1, SSLv3, SSLv2, SSLv23)"}, + {"server_name", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "Server name (SNI)"}, + {"server_name_mode", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0, + "Server name (SNI) mode" }, + {"server_id", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "Server id (match tls profile for outgoing connections)"}, + {"verify_certificate", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0, + "if enabled the certificates will be verified" }, + {"verify_depth", CFG_VAR_INT | CFG_READONLY, 0, 100, 0, 0, + "sets how far up the certificate chain will the certificate" + " verification go in the search for a trusted CA" }, + {"require_certificate", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0, + "if enabled a certificate will be required from clients" }, + {"verify_client", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "set to off (default), on, optional, or optional_no_ca" }, + {"private_key", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "name of the file containing the private key (pem format), if not" + " contained in the certificate file" }, + {"ca_list", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "name of the file containing the trusted CA list (pem format)" }, + {"ca_path", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "name of the directory containing the trusted CA files (pem format)" }, + {"crl", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "name of the file containing the CRL (certificare revocation list" + " in pem format)" }, + {"certificate", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "name of the file containing the certificate (pem format)" }, + {"cipher_list", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "list of the accepted ciphers (strings separated by colons)" }, + {"session_cache", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0, + "enables or disables the session cache" }, + {"session_id", CFG_VAR_STR | CFG_READONLY, 0, 0, 0, 0, + "string used for the session id" }, + {"config", CFG_VAR_STR, 0, 0, fix_rel_pathname, 0, + "tls config file name (used for the per domain options)" }, + {"log", CFG_VAR_INT | CFG_ATOMIC, 0, 1000, 0, 0, + "tls info messages log level" }, + {"debug", CFG_VAR_INT | CFG_ATOMIC, 0, 1000, 0, 0, + "tls debug messages log level" }, + {"connection_timeout", CFG_VAR_INT | CFG_ATOMIC, + -1, MAX_TLS_CON_LIFETIME, fix_con_lt, 0, + "initial connection lifetime (in s) (obsolete)" }, + {"disable_compression", CFG_VAR_INT | CFG_READONLY, 0, 1, 0, 0, + "if set disable the built-in OpenSSL compression" }, + {"ssl_release_buffers", CFG_VAR_INT | CFG_READONLY, -1, 1, 0, 0, + "quickly release internal OpenSSL read or write buffers." + " Works only for OpenSSL >= 1.0."}, + {"ssl_free_list_max", CFG_VAR_INT | CFG_READONLY, -1, 1<<30, 0, 0, + "maximum number of free/cached memory chunks that OpenSSL" + " will keep per connection. Works only for OpenSSL >= 1.0."}, + {"ssl_max_send_fragment", CFG_VAR_INT | CFG_READONLY, -1, 65536, 0, 0, + "sets the maximum number of bytes (clear text) send into one TLS" + " record. Valid values are between 512 and 16384." + " Works only for OpenSSL >= 0.9.9"}, + {"ssl_read_ahead", CFG_VAR_INT | CFG_READONLY, -1, 1, 0, 0, + "Enables read ahead, reducing the number of BIO read calls done" + " internally by the OpenSSL library. Note that in newer tls" + " module versions it is better to have read ahead disabled, since" + " everything it is buffered in memory anyway"}, + {"low_mem_threshold1", CFG_VAR_INT | CFG_ATOMIC, -1, 1<<30, 0, 0, + "sets the minimum amount of free memory for accepting new TLS" + " connections (KB)"}, + {"low_mem_threshold2", CFG_VAR_INT | CFG_ATOMIC, -1, 1<<30, 0, 0, + "sets the minimum amount of free memory after which no more TLS" + " operations will be attempted (even on existing connections)" }, + {"ct_wq_max", CFG_VAR_INT | CFG_ATOMIC, 0, 1<<30, 0, 0, + "maximum bytes queued globally for write when write has to wait due" + " to TLS-level renegotiation (SSL_ERROR_WANT_READ) or initial TLS" + " connection establishment (it is different from tcp.wq_max," + " which works at the TCP connection level)"}, + {"con_ct_wq_max", CFG_VAR_INT | CFG_ATOMIC, 0, 4*1024*1024, 0, 0, + "maximum bytes queued for write per connection when write has to wait" + " due to TLS-level renegotiation (SSL_ERROR_WANT_READ) or initial TLS" + " connection establishment (it is different from tcp.conn_wq_max," + " which works at the TCP connection level)"}, + {"ct_wq_blk_size", CFG_VAR_INT | CFG_ATOMIC, 1, 65536, 0, 0, + "internal TLS pre-write (clear-text) queue minimum block size" + " (advanced tunning or debugging for now)"}, + {"send_close_notify", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0, + "enable/disable sending a close notify TLS shutdown alert" + " before closing the corresponding TCP connection." + "Note that having it enabled has a performance impact."}, + {0, 0, 0, 0, 0, 0} +}; + + + +/** fix pathnames. + * to be used on start-up, with pkg_alloc'ed path names (path->s). + * @param path - path to be fixed. If it starts with '.' or '/' is left alone + * (forced "relative" or "absolute" path). Otherwise the path + * is considered to be relative to the main config file directory + * (e.g. for /etc/ser/ser.cfg => /etc/ser/\). + * @param def - default value used if path->s is empty (path->s == 0). + * @return 0 on success, -1 on error. + */ +static int fix_initial_pathname(str* path, char* def) +{ + str new_path; + if (path->s && path->len && *path->s != '.' && *path->s != '/') { + new_path.s = get_abs_pathname(0, path); + if (new_path.s == 0) return -1; + new_path.len = strlen(new_path.s); + pkg_free(path->s); + *path = new_path; + } else if (path->s == 0 && def) { + /* use defaults */ + new_path.len = strlen(def); + new_path.s = def; + new_path.s = get_abs_pathname(0, &new_path); + if (new_path.s == 0) return -1; + new_path.len = strlen(new_path.s); + *path = new_path; + } + return 0; +} + + + +/** fix the tls cfg, prior to registering. + * @return < 0 on error, 0 on success + */ +int fix_tls_cfg(struct cfg_group_tls* cfg) +{ + cfg->con_lifetime = S_TO_TICKS(cfg->con_lifetime); + fix_timeout("tls_connection_timeout", &cfg->con_lifetime, + MAX_TLS_CON_LIFETIME, MAX_TLS_CON_LIFETIME); + /* Update relative paths of files configured through modparams, relative + * pathnames will be converted to absolute and the directory of the main + * SER configuration file will be used as reference. + */ + if (fix_initial_pathname(&cfg->config_file, 0) < 0) + return -1; + if (fix_initial_pathname(&cfg->private_key, TLS_PKEY_FILE) < 0) + return -1; + if (fix_initial_pathname(&cfg->ca_list, TLS_CA_FILE) < 0 ) + return -1; + if (fix_initial_pathname(&cfg->ca_path, TLS_CA_PATH) < 0 ) + return -1; + if (fix_initial_pathname(&cfg->crl, TLS_CRL_FILE) < 0 ) + return -1; + if (fix_initial_pathname(&cfg->certificate, TLS_CERT_FILE) < 0) + return -1; + + return 0; +} + + + + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_cfg.h b/src/modules/tls_wolfssl/tls_cfg.h new file mode 100644 index 00000000000..b2f6919b25a --- /dev/null +++ b/src/modules/tls_wolfssl/tls_cfg.h @@ -0,0 +1,111 @@ +/* + * TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * Copyright (C) 2013 Motorola Solutions, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * TLS runtime configuration. + * @file tls_cfg.h + * @ingroup: tls + * Module: @ref tls + */ + +#ifndef __tls_cfg_h +#define __tls_cfg_h + +#include "../../core/str.h" +#include "../../core/cfg/cfg.h" + + +/* maximum accepted lifetime (maximum possible is ~ MAXINT/2) + * (it should be kept in sync w/ MAX_TCP_CON_LIFETIME from tcp_main.c: + * MAX_TLS_CON_LIFETIME <= MAX_TCP_CON_LIFETIME )*/ +#define MAX_TLS_CON_LIFETIME ((1U<<(sizeof(ticks_t)*8-1))-1) + + +struct cfg_group_tls { + int force_run; + str method; + str server_name; + int server_name_mode; + str server_id; + int verify_cert; + int verify_depth; + int require_cert; + str verify_client; + str private_key; + str ca_list; + str ca_path; + str crl; + str certificate; + str cipher_list; + int session_cache; + str session_id; + str config_file; + int log; + int debug; + int con_lifetime; + int disable_compression; + /* release internal openssl read or write buffer when they are no longer + * used (complete read or write that does not have to buffer anything). + * Should be used together with tls_free_list_max_len. Might have some + * performance impact (and extra *malloc pressure), but has also the + * potential of saving a lot of memory (at least 32k/idle connection in the + * default config, or ~ 16k+tls_max_send_fragment)) */ + int ssl_release_buffers; + /* maximum length of free/unused memory buffers/chunks per connection. + * Setting it to 0 would cause any unused buffers to be immediately freed + * and hence a lower memory footprint (at the cost of a possible + * performance decrease and more *malloc pressure). + * Too large value would result in extra memory consumption. + * The default is 32 in openssl. + * For lowest memory usage set it to 0 and tls_mode_release_buffers to 1 + */ + int ssl_freelist_max; + /* maximum number of bytes (clear text) sent into one record. + * The default and maximum value are ~16k. Lower values would lead to a + * lower memory footprint. + * Values lower then the typical app. write size might decrease + * performance (extra write() syscalls), so it should be kept ~2k for ser. + */ + int ssl_max_send_fragment; + /* enable read ahead. Should increase performance (1 less syscall when + * enabled, else openssl makes 1 read() for each record header and another + * for the content), but might interact with SSL_pending() (not used right + * now) + */ + int ssl_read_ahead; + int low_mem_threshold1; + int low_mem_threshold2; + int ct_wq_max; /* maximum overall tls write clear text queued bytes */ + int con_ct_wq_max; /* maximum clear text write queued bytes per con */ + int ct_wq_blk_size; /* minimum block size for the clear text write queue */ + int send_close_notify; /* if set try to be nice and send a shutdown alert + before closing the tcp connection */ +}; + + +extern struct cfg_group_tls default_tls_cfg; +extern volatile void* tls_cfg; +extern cfg_def_t tls_cfg_def[]; + + +extern int fix_tls_cfg(struct cfg_group_tls* cfg); + +#endif /*__tls_cfg_h*/ + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_config.c b/src/modules/tls_wolfssl/tls_config.c new file mode 100644 index 00000000000..6c605f527d8 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_config.c @@ -0,0 +1,536 @@ +/* + * TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/*! + * \file + * \brief Kamailio TLS support :: Configuration file parser + * \ingroup tls + * Module: \ref tls + */ + + +#include "tls_config.h" +#include "tls_domain.h" +#include "tls_mod.h" +#include "tls_util.h" + +#include "../../core/cfg_parser.h" + +#include "../../core/resolve.h" +#include "../../core/mem/mem.h" +#include "../../core/dprint.h" +#include "../../core/trim.h" +#include "../../core/ut.h" +#include "../../core/cfg/cfg.h" + +#include +#include +#include +#include + +static tls_domains_cfg_t* _ksr_tls_cfg = NULL; +static tls_domain_t* _ksr_tls_domain = NULL; + +static int parse_ipv6(struct ip_addr* ip, cfg_token_t* token, + cfg_parser_t* st) +{ + int ret; + cfg_token_t t; + struct ip_addr* ipv6; + str ip6_str; + char ip6_buff[IP_ADDR_MAX_STR_SIZE+3]; + + ip6_buff[0] = '\0'; + while(1) { + ret = cfg_get_token(&t, st, 0); + if (ret != 0) goto err; + if (t.type == ']') break; + if (t.type != CFG_TOKEN_ALPHA && t.type != ':') goto err; + strncat(ip6_buff, t.val.s, t.val.len); + } + ip6_str.s = ip6_buff; + ip6_str.len = strlen(ip6_buff); + LM_DBG("found IPv6 address [%.*s]\n", ip6_str.len, ip6_str.s); + ipv6 = str2ip6(&ip6_str); + if (ipv6 == 0) goto err; + *ip = *ipv6; + return 0; + +err: + LM_ERR("%s:%d:%d: Invalid IPv6 address\n", + st->file, token->start.line, token->start.col); + return -1; +} + + +static int parse_ipv4(struct ip_addr* ip, cfg_token_t* token, + cfg_parser_t* st) +{ + int ret, i; + cfg_token_t t; + unsigned int v; + + ip->af = AF_INET; + ip->len = 4; + + if (str2int(&token->val, &v) < 0) goto err; + if (v > 255) goto err; + + ip->u.addr[0] = v; + + for(i = 1; i < 4; i++) { + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0 || t.type != '.') goto err; + + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0 || t.type != CFG_TOKEN_ALPHA) goto err; + if (str2int(&t.val, &v) < 0) goto err; + if (v > 255) goto err; + ip->u.addr[i] = v; + } + + return 0; +err: + LM_ERR("%s:%d:%d: Invalid IPv4 address\n", + st->file, token->start.line, token->start.col); + return -1; +} + + +static cfg_option_t methods[] = { + {"SSLv2", .val = TLS_USE_SSLv2}, + {"SSLv3", .val = TLS_USE_SSLv3}, + {"SSLv23", .val = TLS_USE_SSLv23}, + {"TLSv1", .val = TLS_USE_TLSv1}, + {"TLSv1.0", .val = TLS_USE_TLSv1}, + {"TLSv1+", .val = TLS_USE_TLSv1_PLUS}, + {"TLSv1.0+", .val = TLS_USE_TLSv1_PLUS}, + {"TLSv1.1", .val = TLS_USE_TLSv1_1}, + {"TLSv1.1+", .val = TLS_USE_TLSv1_1_PLUS}, + {"TLSv1.2", .val = TLS_USE_TLSv1_2}, + {"TLSv1.2+", .val = TLS_USE_TLSv1_2_PLUS}, + {0} +}; + + +static cfg_option_t domain_types[] = { + {"server", .val = TLS_DOMAIN_SRV}, + {"srv", .val = TLS_DOMAIN_SRV}, + {"s", .val = TLS_DOMAIN_SRV}, + {"client", .val = TLS_DOMAIN_CLI}, + {"cli", .val = TLS_DOMAIN_CLI}, + {"c", .val = TLS_DOMAIN_CLI}, + {0} +}; + + +static cfg_option_t token_default[] = { + {"default"}, + {"def"}, + {"*"}, + {0} +}; + +static cfg_option_t ksr_tls_token_any[] = { + {"any"}, + {"all"}, + {0} +}; + + +static cfg_option_t verify_client_params[] = { + {"off", .val = TLS_VERIFY_CLIENT_OFF}, + {"on", .val = TLS_VERIFY_CLIENT_ON}, + {"optional", .val = TLS_VERIFY_CLIENT_OPTIONAL}, + {"optional_no_ca", .val = TLS_VERIFY_CLIENT_OPTIONAL_NO_CA}, + {0} +}; + + +static cfg_option_t options[] = { + {"method", .param = methods, .f = cfg_parse_enum_opt}, + {"tls_method", .param = methods, .f = cfg_parse_enum_opt}, + {"verify_certificate", .f = cfg_parse_bool_opt}, + {"verify_cert", .f = cfg_parse_bool_opt}, + {"verify_depth", .f = cfg_parse_int_opt}, + {"require_certificate", .f = cfg_parse_bool_opt}, + {"require_cert", .f = cfg_parse_bool_opt}, + {"private_key", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"pkey_file", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"calist_file", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"certificate", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"cert_file", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"cipher_list", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"ca_list", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"crl", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"server_name", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"server_name_mode", .f = cfg_parse_int_opt}, + {"server_id", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {"verify_client", .param = verify_client_params, .f = cfg_parse_enum_opt}, + {"ca_path", .f = cfg_parse_str_opt, .flags = CFG_STR_SHMMEM}, + {0} +}; + + +static void update_opt_variables(void) +{ + int i; + for(i = 0; methods[i].name; i++) { + methods[i].param = &_ksr_tls_domain->method; + } + options[2].param = &_ksr_tls_domain->verify_cert; + options[3].param = &_ksr_tls_domain->verify_cert; + options[4].param = &_ksr_tls_domain->verify_depth; + options[5].param = &_ksr_tls_domain->require_cert; + options[6].param = &_ksr_tls_domain->require_cert; + options[7].param = &_ksr_tls_domain->pkey_file; + options[8].param = &_ksr_tls_domain->pkey_file; + options[9].param = &_ksr_tls_domain->ca_file; + options[10].param = &_ksr_tls_domain->cert_file; + options[11].param = &_ksr_tls_domain->cert_file; + options[12].param = &_ksr_tls_domain->cipher_list; + options[13].param = &_ksr_tls_domain->ca_file; + options[14].param = &_ksr_tls_domain->crl_file; + options[15].param = &_ksr_tls_domain->server_name; + options[16].param = &_ksr_tls_domain->server_name_mode; + options[17].param = &_ksr_tls_domain->server_id; + for(i = 0; verify_client_params[i].name; i++) { + verify_client_params[i].param = &_ksr_tls_domain->verify_client; + } + options[19].param = &_ksr_tls_domain->ca_path; +} + + +static int ksr_tls_parse_hostport(int* type, struct ip_addr* ip, unsigned int* port, + cfg_token_t* token, cfg_parser_t* st) +{ + int ret; + cfg_token_t t; + cfg_option_t* opt; + + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0) { + LM_ERR("%s:%d:%d: Missing IP address\n", st->file, + token->start.line, token->start.col); + return -1; + } + + if (t.type == '[') { + if (parse_ipv6(ip, &t, st) < 0) return -1; + } else if (t.type == CFG_TOKEN_ALPHA) { + opt = cfg_lookup_token(token_default, &t.val); + if (opt) { + *type = TLS_DOMAIN_DEF; + /* Default domain */ + return 0; + } else { + opt = cfg_lookup_token(ksr_tls_token_any, &t.val); + if (opt) { + *type = TLS_DOMAIN_ANY; + /* Default domain */ + return 0; + } else { + if (parse_ipv4(ip, &t, st) < 0) return -1; + } + } + } else { + LM_ERR("%s:%d:%d: Syntax error, IP address expected\n", + st->file, t.start.line, t.start.col); + return -1; + } + *type = 0; + + /* Parse port */ + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0) { + LM_ERR("%s:%d:%d: Syntax error, ':' expected\n", st->file, st->line, + st->col); + return -1; + } + + if (t.type != ':') { + LM_ERR("%s:%d:%d: Syntax error, ':' expected\n", + st->file, t.start.line, t.start.col); + return -1; + } + + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0) { + LM_ERR("%s:%d:%d: Premature end of file, port number missing\n", + st->file, t.start.line, t.start.col); + return -1; + } + + if (t.type != CFG_TOKEN_ALPHA || (str2int(&t.val, port) < 0)) { + LM_ERR("%s:%d:%d: Invalid port number '%.*s'\n", + st->file, t.start.line, t.start.col, STR_FMT(&t.val)); + return -1; + } + return 0; +} + + +static int ksr_tls_parse_domain(void* param, cfg_parser_t* st, unsigned int flags) +{ + cfg_token_t t; + int ret; + cfg_option_t* opt; + + int type; + struct ip_addr ip; + unsigned int port; + + if(_ksr_tls_cfg!=NULL && _ksr_tls_domain!=NULL) { + /* Make sure the previous domain is not a duplicate */ + if (ksr_tls_domain_duplicated(_ksr_tls_cfg, _ksr_tls_domain)) { + return -1; + } + } + + memset(&ip, 0, sizeof(struct ip_addr)); + + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0) { + LM_ERR("%s:%d:%d: TLS domain type missing\n", + st->file, st->line, st->col); + return -1; + } + + if (t.type != CFG_TOKEN_ALPHA || + ((opt = cfg_lookup_token(domain_types, &t.val)) == NULL)) { + LM_ERR("%s:%d:%d: Invalid TLS domain type %d:'%.*s'\n", + st->file, t.start.line, t.start.col, t.type, STR_FMT(&t.val)); + return -1; + } + + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0) { + LM_ERR("%s:%d:%d: TLS domain IP address missing\n", + st->file, st->line, st->col); + return -1; + } + if (t.type != ':') { + LM_ERR("%s:%d:%d: Syntax error, ':' expected\n", + st->file, t.start.line, t.start.col); + return -1; + } + + port = 0; + if (ksr_tls_parse_hostport(&type, &ip, &port, &t, st) < 0) return -1; + + ret = cfg_get_token(&t, st, 0); + if (ret < 0) return -1; + if (ret > 0) { + LM_ERR("%s:%d:%d: Closing ']' missing\n", + st->file, st->line, st->col); + return -1; + } + if (t.type != ']') { + LM_ERR("%s:%d:%d: Syntax error, ']' expected\n", + st->file, t.start.line, t.start.col); + return -1; + } + + if (cfg_eat_eol(st, flags)) return -1; + + if ((_ksr_tls_domain = tls_new_domain(opt->val | type, &ip, port)) == NULL) { + LM_ERR("%s:%d: Cannot create TLS domain structure\n", st->file, st->line); + return -1; + } + + ret = tls_add_domain(_ksr_tls_cfg, _ksr_tls_domain); + if (ret < 0) { + LM_ERR("%s:%d: Error while creating TLS domain structure\n", st->file, + st->line); + tls_free_domain(_ksr_tls_domain); + _ksr_tls_domain = NULL; + return -1; + } else if (ret == 1) { + LM_ERR("%s:%d: Duplicate TLS domain (appears earlier in the config file)\n", + st->file, st->line); + tls_free_domain(_ksr_tls_domain); + _ksr_tls_domain = NULL; + return -1; + } + + update_opt_variables(); + cfg_set_options(st, options); + + return 0; +} + + +/* + * Create configuration structures from configuration file + */ +tls_domains_cfg_t* tls_load_config(str* filename) +{ + cfg_parser_t* parser; + str empty; + struct stat file_status; + char tmp_name[13] = "configXXXXXX"; + str filename_str; + DIR *dir; + struct dirent *ent; + int out_fd, in_fd, filename_is_directory; + char *file_path, ch; + + parser = NULL; + memset(&file_status, 0, sizeof(struct stat)); + dir = (DIR *)NULL; + in_fd = out_fd = filename_is_directory = 0; + file_path = (char *)0; + + if ((_ksr_tls_cfg = tls_new_cfg()) == NULL) goto error; + + if (stat(filename->s, &file_status) != 0) { + LM_ERR("cannot stat config file %s\n", filename->s); + goto error; + } + if (S_ISDIR(file_status.st_mode)) { + filename_is_directory = 1; + dir = opendir(filename->s); + if (dir == NULL) { + LM_ERR("cannot open directory file %s\n", filename->s); + goto error; + } + out_fd = mkstemp(&(tmp_name[0])); + if (out_fd == -1) { + LM_ERR("cannot make tmp file %s\n", &(tmp_name[0])); + goto error; + } + while ((ent = readdir(dir)) != NULL) { + if(file_path) pkg_free(file_path); + file_path = pkg_malloc(filename->len + 1 + 256); + memcpy(file_path, filename->s, filename->len); + file_path[filename->len] = '/'; + strcpy(file_path + filename->len + 1, ent->d_name); + if (stat(file_path, &file_status) != 0) { + LM_ERR("cannot get status of config file %s\n", + file_path); + goto error; + } + if (S_ISREG(file_status.st_mode)) { + in_fd = open(file_path, O_RDONLY); + if (in_fd == -1) { + LM_ERR("cannot open config file %s\n", + file_path); + goto error; + } + pkg_free(file_path); + file_path = NULL; + while (read(in_fd, &ch, 1)) { + if (write(out_fd, &ch, 1)<0) { + LM_ERR("write error: %s\n", strerror(errno)); + } + } + close(in_fd); + in_fd = 0; + ch = '\n'; + if (write(out_fd, &ch, 1)<0) { + LM_ERR("write error: %s\n", strerror(errno)); + } + } + } + closedir(dir); + close(out_fd); + dir = (DIR *)NULL; + out_fd = 0; + } + + empty.s = 0; + empty.len = 0; + if (filename_is_directory) { + filename_str.s = &(tmp_name[0]); + filename_str.len = strlen(&(tmp_name[0])); + if ((parser = cfg_parser_init(&empty, &filename_str)) == NULL) { + LM_ERR("Error while initializing configuration file parser.\n"); + unlink(&(tmp_name[0])); + goto error; + } + unlink(&(tmp_name[0])); + } else { + if ((parser = cfg_parser_init(&empty, filename)) == NULL) { + LM_ERR("Error while initializing configuration file parser.\n"); + goto error; + } + } + + cfg_section_parser(parser, ksr_tls_parse_domain, NULL); + if (sr_cfg_parse(parser)) goto error; + cfg_parser_close(parser); + if (file_path) pkg_free(file_path); + return _ksr_tls_cfg; + +error: + if (dir) closedir(dir); + if (out_fd > 0) { + close(out_fd); + unlink(&(tmp_name[0])); + } + if (file_path) pkg_free(file_path); + if (parser) cfg_parser_close(parser); + if (_ksr_tls_cfg) { + tls_free_cfg(_ksr_tls_cfg); + _ksr_tls_cfg = NULL; + } + return 0; +} + + +/* + * Convert TLS method string to integer + */ +int tls_parse_method(str* method) +{ + cfg_option_t* opt; + + if (!method) { + LM_BUG("Invalid parameter value\n"); + return -1; + } + + opt = cfg_lookup_token(methods, method); + if (!opt) return -1; + + return opt->val; +} + +/* + * Convert TLS verify_client string to integer + */ +int tls_parse_verify_client(str* verify_client) +{ + cfg_option_t* opt; + + if (!verify_client) { + LM_BUG("Invalid parameter value\n"); + return -1; + } + + opt = cfg_lookup_token(verify_client_params, verify_client); + if (!opt) return -1; + + return opt->val; +} diff --git a/src/modules/tls_wolfssl/tls_config.h b/src/modules/tls_wolfssl/tls_config.h new file mode 100644 index 00000000000..ac235d5af4b --- /dev/null +++ b/src/modules/tls_wolfssl/tls_config.h @@ -0,0 +1,47 @@ +/* + * TLS module + * + * Copyright (C) 2005,2006 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/*! + * \file + * \brief Kamailio TLS support :: Configuration file parser + * \ingroup tls + * Module: \ref tls + */ + + +#ifndef _TLS_CONFIG_H +#define _TLS_CONFIG_H + +#include "../../core/str.h" +#include "tls_domain.h" + +#include +#include + +tls_domains_cfg_t* tls_load_config(str* filename); + +/* + * Convert TLS method string to integer + */ +int tls_parse_method(str* method); + +/* + * Convert TLS verify_client string to integer + */ +int tls_parse_verify_client(str* verify_client); + +#endif /* _TLS_CONFIG_H */ diff --git a/src/modules/tls_wolfssl/tls_ct_q.h b/src/modules/tls_wolfssl/tls_ct_q.h new file mode 100644 index 00000000000..7e7703ec7ac --- /dev/null +++ b/src/modules/tls_wolfssl/tls_ct_q.h @@ -0,0 +1,133 @@ +/* + * TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * TLS clear text queue (wrappers over sbufq) + * (e.g. queue clear text when SSL_write() cannot write it immediately due to + * re-keying). + * @file + * @ingroup tls + * Module: @ref tls + */ + +#ifndef __tls_ct_q_h +#define __tls_ct_q_h + +#include "sbufq.h" +#include "../../core/compiler_opt.h" + +typedef struct sbuffer_queue tls_ct_q; + + +#define tls_ct_q_empty(bq) ((bq)==0 || (bq)->first==0) +#define tls_ct_q_non_empty(bq) ((bq) && (bq)->first!=0) + + +/** + * @brief Adds/appends data to a tls clear text buffer queue + * @warning it does no attempt to synchronize access/lock. If needed it should + * be called under lock. + * @param **ct_q - double pointer to the buffer queue + * @param data + * @param size + * @param min_buf_size - min size to allocate for new buffer elements + * @return 0 on success, -1 on error (mem. allocation) + */ +inline static int tls_ct_q_add(tls_ct_q** ct_q, const void* data, + unsigned int size, unsigned int min_buf_size) +{ + tls_ct_q* q; + + q = *ct_q; + if (likely(q == 0)){ + q=shm_malloc(sizeof(tls_ct_q)); + if (unlikely(q==0)) + goto error; + memset(q, 0, sizeof(tls_ct_q)); + *ct_q = q; + } + return sbufq_add(q, data, size, min_buf_size); +error: + return -1; +} + + + +/** + * @brief Destroy a buffer queue + * + * Everything is destroyed from a buffer queue (shm_free()'d), included the queue head. + * @warning it does no attempt to synchronize access/lock. If needed it should + * be called under lock. + * @param **ct_q - double pointer to the queue + * @return - number of bytes that used to be queued (>=0). + */ +inline static unsigned int tls_ct_q_destroy(tls_ct_q** ct_q) +{ + unsigned int ret; + + ret = 0; + if (likely(ct_q && *ct_q)) { + ret = sbufq_destroy(*ct_q); + shm_free(*ct_q); + *ct_q = 0; + } + return ret; +} + + + +/** + * @brief Tries to flush the tls clear text queue + * + * Tries to flush as much as possible from the given queue, using the + * given callback. + * @warning it does no attempt to synchronize access/lock. If needed it should + * be called under lock. + * @param tc_q - buffer queue + * @param *flags - set to: + * F_BUFQ_EMPTY if the queued is completely flushed + * F_BUFQ_FLUSH_ERR if the flush_f callback returned error. + * @param flush_f - flush function (callback). modeled after write(): + * flush_f(flush_p, const void* buf, unsigned size). + * It should return the number of bytes "flushed" on + * success, or <0 on error. If the number of bytes + * "flushed" is smaller then the requested size, it + * would be assumed that no more bytes can be flushed + * and sbufq_flush will exit. + * @param flush_p1 - parameter for the flush function callback. + * @param flush_p2 - parameter for the flush function callback. + * @return -1 on internal error, or the number of bytes flushed on + * success (>=0). Note that the flags param is + * always set and it should be used to check for errors, since + * a flush_f() failure will not result in a negative return. + */ +inline static int tls_ct_q_flush(tls_ct_q** tc_q, int* flags, + int (*flush_f)(void* p1, void* p2, + const void* buf, + unsigned size), + void* flush_p1, void* flush_p2) +{ + return *tc_q?sbufq_flush(*tc_q, flags, flush_f, flush_p1, flush_p2):0; +} + + + +#endif /*__tls_ct_q_h*/ + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_ct_wrq.c b/src/modules/tls_wolfssl/tls_ct_wrq.c new file mode 100644 index 00000000000..e07c8787bb7 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_ct_wrq.c @@ -0,0 +1,203 @@ +/* + * TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * tls clear text write queue. + * (queue clear text when SSL_write() cannot write it immediately due to + * re-keying). + * @file + * @ingroup tls + * Module: @ref tls + */ + +#include "tls_ct_wrq.h" +#include "tls_cfg.h" +#include "tls_server.h" +#include "../../core/atomic_ops.h" +#include "../../core/mem/shm_mem.h" + + +atomic_t* tls_total_ct_wq; /* total clear text bytes queued for a future + SSL_write() (due to renegotiations/ + SSL_WRITE_WANTS_READ ).*/ + + + +/** + * @brief Init clear text write queues support + * @return 0 on success, < 0 on error. + */ +int tls_ct_wq_init() +{ + tls_total_ct_wq = shm_malloc(sizeof(*tls_total_ct_wq)); + if (unlikely(tls_total_ct_wq == 0)) + return -1; + atomic_set(tls_total_ct_wq, 0); + return 0; +} + + + +/** + * @brief Destroy clear text write queues support + */ +void tls_ct_wq_destroy() +{ + if (tls_total_ct_wq) { + shm_free(tls_total_ct_wq); + tls_total_ct_wq = 0; + } +} + + + +/** + * @brief Total number of written queued bytes in all the SSL connections + * @return total number of written queued bytes in all SSL connections + */ +unsigned int tls_ct_wq_total_bytes() +{ + return (unsigned)atomic_get(tls_total_ct_wq); +} + + + +/** + * @brief Callback for tls_ct_q_flush() + * + * @param tcp_c TCP connection containing the SSL context + * @param error error reason (set on exit) + * @param buf buffer + * @param size buffer size + * @return >0 on success (bytes written), <=0 on ssl error (should be + * handled outside) + * @warning the SSL context must have the wbio and rbio previously set! + */ +static int ssl_flush(void* tcp_c, void* error, const void* buf, unsigned size) +{ + int n; + int ssl_error; + struct tls_extra_data* tls_c; + SSL* ssl; + + tls_c = ((struct tcp_connection*)tcp_c)->extra_data; + ssl = tls_c->ssl; + ssl_error = SSL_ERROR_NONE; + if (unlikely(tls_c->state == S_TLS_CONNECTING)) { + n = tls_connect(tcp_c, &ssl_error); + if (unlikely(n>=1)) { + n = wolfSSL_write(ssl, buf, size); + if (unlikely(n <= 0)) + ssl_error = wolfSSL_get_error(ssl, n); + } + } else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) { + n = tls_accept(tcp_c, &ssl_error); + if (unlikely(n>=1)) { + n = wolfSSL_write(ssl, buf, size); + if (unlikely(n <= 0)) + ssl_error = wolfSSL_get_error(ssl, n); + } + } else { + n = wolfSSL_write(ssl, buf, size); + if (unlikely(n <= 0)) + ssl_error = wolfSSL_get_error(ssl, n); + } + + *(long*)error = ssl_error; + return n; +} + + + +/** + * @brief Wrapper over tls_ct_q_flush() + * + * Wrapper over tls_ct_q_flush(), besides doing a tls_ct_q_add it + * also keeps track of queue size and total queued bytes. + * @param c TCP connection + * @param ct_q clear text queue + * @param flags filled, @see tls_ct_q_add() for more details. + * @param ssl_err set to the SSL err (SSL_ERROR_NONE on full success). + * @return -1 on internal error, or the number of bytes flushed on success + * (>=0). + */ +int tls_ct_wq_flush(struct tcp_connection* c, tls_ct_q** ct_q, + int* flags, int* ssl_err) +{ + int ret; + long error; + + error = SSL_ERROR_NONE; + ret = tls_ct_q_flush(ct_q, flags, ssl_flush, c, &error); + *ssl_err = (int)error; + if (likely(ret > 0)) + atomic_add(tls_total_ct_wq, -ret); + return ret; +} + + + +/** + * @brief Wrapper over tls_ct_q_add() + * + * Wrapper over tls_ct_q_add(), besides doing a tls_ct_q_add it + * also keeps track of queue size and total queued bytes. + * If the maximum queue size is exceeded => error. + * @param ct_q clear text queue + * @param data data + * @param size data size + * @return 0 on success, < 0 on error (-1 memory allocation, -2 queue size + * too big). + */ +int tls_ct_wq_add(tls_ct_q** ct_q, const void* data, unsigned int size) +{ + int ret; + + if (unlikely( (*ct_q && (((*ct_q)->queued + size) > + cfg_get(tls, tls_cfg, con_ct_wq_max))) || + (atomic_get(tls_total_ct_wq) + size) > + cfg_get(tls, tls_cfg, ct_wq_max))) { + return -2; + } + ret = tls_ct_q_add(ct_q, data, size, + cfg_get(tls, tls_cfg, ct_wq_blk_size)); + if (likely(ret >= 0)) + atomic_add(tls_total_ct_wq, size); + return ret; +} + + + +/** + * @brief Wrapper over tls_ct_q_destroy() + * Wrapper over tls_ct_q_destroy(), besides doing a tls_ct_q_destroy it + * also keeps track of the total queued bytes. + * @param ct_q clear text queue + * @return number of bytes that used to be queued (>=0), + */ +unsigned int tls_ct_wq_free(tls_ct_q** ct_q) +{ + unsigned int ret; + + if (likely((ret = tls_ct_q_destroy(ct_q)) > 0)) + atomic_add(tls_total_ct_wq, -ret); + return ret; +} + + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_ct_wrq.h b/src/modules/tls_wolfssl/tls_ct_wrq.h new file mode 100644 index 00000000000..e27ec492947 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_ct_wrq.h @@ -0,0 +1,98 @@ +/* + * TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * tls clear text write queue. + * (queue clear text when SSL_write() cannot write it immediately due to + * re-keying). + * @file + * @ingroup tls + * Module: @ref tls + */ + +#ifndef __tls_ct_wrq_h +#define __tls_ct_wrq_h + +#include "tls_ct_q.h" +#include "tls_server.h" +#include "../../core/tcp_conn.h" + + + +/** + * @brief Init clear text write queues support + * @return 0 on success, < 0 on error. + */ +int tls_ct_wq_init(); + +/** + * @brief Destroy clear text write queues support + */ +void tls_ct_wq_destroy(); + + +/** + * @brief Total number of written queued bytes in all the SSL connections + * @return total number of written queued bytes in all SSL connections + */ +unsigned int tls_ct_wq_total_bytes(); + +#define tls_ct_wq_empty(tc_q) (*(tc_q)==0 || (*(tc_q))->first==0) +#define tls_ct_wq_non_empty(bq) (*(tc_q) && (*(tc_q))->first!=0) + +/** + * @brief Wrapper over tls_ct_q_flush() + * + * Wrapper over tls_ct_q_flush(), besides doing a tls_ct_q_add it + * also keeps track of queue size and total queued bytes. + * @param c TCP connection + * @param ct_q clear text queue + * @param flags filled, @see tls_ct_q_add() for more details. + * @param ssl_err set to the SSL err (SSL_ERROR_NONE on full success). + * @return -1 on internal error, or the number of bytes flushed on success + * (>=0). + */ +int tls_ct_wq_flush(struct tcp_connection* c, tls_ct_q** tc_q, + int* flags, int* ssl_err); + +/** + * @brief Wrapper over tls_ct_q_add() + * + * Wrapper over tls_ct_q_add(), besides doing a tls_ct_q_add it + * also keeps track of queue size and total queued bytes. + * If the maximum queue size is exceeded => error. + * @param ct_q clear text queue + * @param data data + * @param size data size + * @return 0 on success, < 0 on error (-1 memory allocation, -2 queue size + * too big). + */ +int tls_ct_wq_add(tls_ct_q** ct_q, const void* data, unsigned int size); + +/** + * @brief Wrapper over tls_ct_q_destroy() + * Wrapper over tls_ct_q_destroy(), besides doing a tls_ct_q_destroy it + * also keeps track of the total queued bytes. + * @param ct_q clear text queue + * @return number of bytes that used to be queued (>=0), + */ +unsigned int tls_ct_wq_free(tls_ct_q** ct_q); + +#endif /*__tls_ct_wrq_h*/ + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_domain.c b/src/modules/tls_wolfssl/tls_domain.c new file mode 100644 index 00000000000..9de2f25ca6f --- /dev/null +++ b/src/modules/tls_wolfssl/tls_domain.c @@ -0,0 +1,1433 @@ +/* + * TLS module + * + * Copyright (C) 2005,2006 iptelorg GmbH + * Copyright (C) 2013 Motorola Solutions, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * Kamailio TLS support :: Virtual domain configuration support + * @file + * @ingroup tls + * Module: @ref tls + */ + +#include + +#include +#include + +// # include +#include "../../core/ut.h" +#include "../../core/mem/shm_mem.h" +#include "../../core/pt.h" +#include "../../core/cfg/cfg.h" +#include "../../core/dprint.h" +#include "tls_config.h" +#include "tls_server.h" +#include "tls_util.h" +#include "tls_mod.h" +#include "tls_init.h" +#include "tls_domain.h" +#include "tls_cfg.h" +#include "tls_verify.h" + +/* + * ECDHE is enabled only on OpenSSL 1.0.0e and later. + * See http://www.openssl.org/news/secadv_20110906.txt + * for details. + * Also, copied from _ssl.c of Python for correct initialization. + * Allow automatic ECDH curve selection (on OpenSSL 1.0.2+), or use + * prime256v1 by default. This is Apache mod_ssl's initialization + * policy, so we should be safe. OpenSSL 1.1 has it enabled by default. + */ + +#ifndef OPENSSL_NO_DH + +/* + * not needed for OpenSSL 1.1.0+ and LibreSSL + */ +#if !defined(SSL_CTX_set_dh_auto) +static unsigned char dh3072_p[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, + 0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2, + 0xEC,0x07,0xA2,0x8F,0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9, + 0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,0x39,0x95,0x49,0x7C, + 0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, + 0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D, + 0x04,0x50,0x7A,0x33,0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64, + 0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A,0x8A,0xEA,0x71,0x57, + 0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, + 0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0, + 0x4A,0x25,0x61,0x9D,0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B, + 0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64,0xD8,0x76,0x02,0x73, + 0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, + 0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0, + 0xBA,0xD9,0x46,0xE2,0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31, + 0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E,0x4B,0x82,0xD1,0x20, + 0xA9,0x3A,0xD2,0xCA,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + +}; + +static unsigned char dh3072_g[] = { 0x02 }; +#endif + +static void setup_dh(WOLFSSL_CTX *ctx) +{ +/* + * not needed for OpenSSL 1.1.0+ and LibreSSL + * DH_new() is deprecated in OpenSSL 3 + */ +#if !defined(SSL_CTX_set_dh_auto) + DH *dh; + BIGNUM *p; + BIGNUM *g; + + dh = DH_new(); + if (dh == NULL) { + return; + } + + p = BN_bin2bn(dh3072_p, sizeof(dh3072_p), NULL); + g = BN_bin2bn(dh3072_g, sizeof(dh3072_g), NULL); + + if (p == NULL || g == NULL) { + DH_free(dh); + return; + } + + dh->p = p; + dh->g = g; + + + wolfSSL_CTX_set_options(ctx, WOLFSSL_OP_SINGLE_DH_USE); + wolfSSL_CTX_set_tmp_dh(ctx, dh); + + DH_free(dh); +#else + SSL_CTX_set_dh_auto(ctx, 1); +#endif +} +#endif + + +/** + * @brief Create a new TLS domain structure + * + * Create a new domain structure in new allocated shared memory. + * @param type domain Type + * @param ip domain IP + * @param port domain port + * @return new domain + */ +tls_domain_t* tls_new_domain(int type, struct ip_addr *ip, unsigned short port) +{ + tls_domain_t* d; + + d = shm_malloc(sizeof(tls_domain_t)); + if (d == NULL) { + ERR("Memory allocation failure\n"); + return 0; + } + memset(d, '\0', sizeof(tls_domain_t)); + + d->type = type; + if (ip) memcpy(&d->ip, ip, sizeof(struct ip_addr)); + d->port = port; + d->verify_cert = -1; + d->verify_depth = -1; + d->require_cert = -1; + d->verify_client = -1; + return d; +} + + +/** + * @brief Free all memory used by TLS configuration domain + * @param d freed domain + */ +void tls_free_domain(tls_domain_t* d) +{ + int i; + int procs_no; + + if (!d) return; + if (d->ctx) { + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + if (d->ctx[i]) wolfSSL_CTX_free(d->ctx[i]); + } + shm_free(d->ctx); + } + + if (d->cipher_list.s) shm_free(d->cipher_list.s); + if (d->ca_file.s) shm_free(d->ca_file.s); + if (d->ca_path.s) shm_free(d->ca_path.s); + if (d->crl_file.s) shm_free(d->crl_file.s); + if (d->pkey_file.s) shm_free(d->pkey_file.s); + if (d->cert_file.s) shm_free(d->cert_file.s); + if (d->server_name.s) shm_free(d->server_name.s); + if (d->server_id.s) shm_free(d->server_id.s); + shm_free(d); +} + + +/** + * @brief Free TLS configuration structure + * @param cfg freed configuration + */ +void tls_free_cfg(tls_domains_cfg_t* cfg) +{ + tls_domain_t* p; + while(cfg->srv_list) { + p = cfg->srv_list; + cfg->srv_list = cfg->srv_list->next; + tls_free_domain(p); + } + while(cfg->cli_list) { + p = cfg->cli_list; + cfg->cli_list = cfg->cli_list->next; + tls_free_domain(p); + } + if (cfg->srv_default) tls_free_domain(cfg->srv_default); + if (cfg->cli_default) tls_free_domain(cfg->cli_default); + shm_free(cfg); +} + + +/** + * @brief Destroy all TLS configuration data + */ +void tls_destroy_cfg(void) +{ + tls_domains_cfg_t* ptr; + + if (tls_domains_cfg_lock) { + lock_destroy(tls_domains_cfg_lock); + lock_dealloc(tls_domains_cfg_lock); + tls_domains_cfg_lock = 0; + } + + if (tls_domains_cfg) { + while(*tls_domains_cfg) { + ptr = *tls_domains_cfg; + *tls_domains_cfg = (*tls_domains_cfg)->next; + tls_free_cfg(ptr); + } + + shm_free(tls_domains_cfg); + tls_domains_cfg = 0; + } +} + + + +/** + * @brief Generate TLS domain identifier + * @param d printed domain + * @return printed domain, with zero termination + */ +char* tls_domain_str(tls_domain_t* d) +{ + static char buf[1024]; + char* p; + + buf[0] = '\0'; + p = buf; + p = strcat(p, d->type & TLS_DOMAIN_SRV ? "TLSs<" : "TLSc<"); + if (d->type & TLS_DOMAIN_DEF) { + p = strcat(p, "default>"); + } else if (d->type & TLS_DOMAIN_ANY) { + p = strcat(p, "any:"); + if(d->server_name.s && d->server_name.len>0) { + p = strncat(p, d->server_name.s, d->server_name.len); + } + p = strcat(p, ">"); + } else { + p = strcat(p, ip_addr2a(&d->ip)); + p = strcat(p, ":"); + p = strcat(p, int2str(d->port, 0)); + p = strcat(p, ">"); + } + return buf; +} + + +/** + * @brief Initialize TLS domain parameters that have not been configured yet + * + * Initialize TLS domain parameters that have not been configured from + * parent domain (usually one of default domains) + * @param d initialized domain + * @param parent parent domain + * @return 0 on success, -1 on error + */ +static int ksr_tls_fill_missing(tls_domain_t* d, tls_domain_t* parent) +{ + if (d->method == TLS_METHOD_UNSPEC) d->method = parent->method; + LOG(L_INFO, "%s: tls_method=%d\n", tls_domain_str(d), d->method); + + if (d->method < 1 || d->method >= TLS_METHOD_MAX) { + ERR("%s: Invalid TLS method value\n", tls_domain_str(d)); + return -1; + } + + if (!d->cert_file.s) { + if (shm_asciiz_dup(&d->cert_file.s, parent->cert_file.s) < 0) + return -1; + d->cert_file.len = parent->cert_file.len; + } + LOG(L_INFO, "%s: certificate='%s'\n", tls_domain_str(d), d->cert_file.s); + + if (!d->ca_file.s){ + if (shm_asciiz_dup(&d->ca_file.s, parent->ca_file.s) < 0) + return -1; + d->ca_file.len = parent->ca_file.len; + } + LOG(L_INFO, "%s: ca_list='%s'\n", tls_domain_str(d), d->ca_file.s); + + if (!d->ca_path.s){ + if (shm_asciiz_dup(&d->ca_path.s, parent->ca_path.s) < 0) + return -1; + d->ca_path.len = parent->ca_path.len; + } + LOG(L_INFO, "%s: ca_path='%s'\n", tls_domain_str(d), d->ca_path.s); + + if (!d->crl_file.s) { + if (shm_asciiz_dup(&d->crl_file.s, parent->crl_file.s) < 0) + return -1; + d->crl_file.len = parent->crl_file.len; + } + LOG(L_INFO, "%s: crl='%s'\n", tls_domain_str(d), d->crl_file.s); + + if (d->require_cert == -1) d->require_cert = parent->require_cert; + LOG(L_INFO, "%s: require_certificate=%d\n", tls_domain_str(d), + d->require_cert); + + if (!d->cipher_list.s) { + if ( shm_asciiz_dup(&d->cipher_list.s, parent->cipher_list.s) < 0) + return -1; + d->cipher_list.len = parent->cipher_list.len; + } + LOG(L_INFO, "%s: cipher_list='%s'\n", tls_domain_str(d), d->cipher_list.s); + + if (!d->pkey_file.s) { + if (shm_asciiz_dup(&d->pkey_file.s, parent->pkey_file.s) < 0) + return -1; + d->pkey_file.len = parent->pkey_file.len; + } + LOG(L_INFO, "%s: private_key='%s'\n", tls_domain_str(d), d->pkey_file.s); + + if (d->verify_cert == -1) d->verify_cert = parent->verify_cert; + LOG(L_INFO, "%s: verify_certificate=%d\n", tls_domain_str(d), + d->verify_cert); + + if (d->verify_depth == -1) d->verify_depth = parent->verify_depth; + LOG(L_INFO, "%s: verify_depth=%d\n", tls_domain_str(d), d->verify_depth); + + if (d->verify_client == -1) d->verify_client = parent->verify_client; + LOG(L_INFO, "%s: verify_client=%d\n", tls_domain_str(d), d->verify_client); + + return 0; +} + + +/** + * @brief Called for ctx, with 2 args + * @param ctx SSL context + * @param larg ? + * @param parg ? + * @return return 0 on succes, <0 on critical error + */ +typedef int (*per_ctx_cbk_f)(WOLFSSL_CTX* ctx, long larg, void* parg); + + +/** + * @brief Execute callback on all the CTX'es on a domain + * @param d domain + * @param ctx_cbk callback function + * @param l1 parameter passed to the callback + * @param p2 parameter passed to the callback + * @return 0 on success, <0 on error + */ +static int tls_domain_foreach_CTX(tls_domain_t* d, per_ctx_cbk_f ctx_cbk, + long l1, void* p2) +{ + int i,ret; + int procs_no; + + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + if ((ret=ctx_cbk(d->ctx[i], l1, p2))<0) + return ret; + } + return 0; +} + + +/** + * @brief Execute callback on all the CTX'es on in a domain list + * @param d domain + * @param ctx_cbk callback function + * @param l1 parameter passed to the callback + * @param p2 parameter passed to the callback + * @return 0 on success, <0 on error + */ +static int tls_foreach_CTX_in_domain_lst(tls_domain_t* d, + per_ctx_cbk_f ctx_cbk, + long l1, void* p2) +{ + int ret; + for (; d; d=d->next) + if ((ret=tls_domain_foreach_CTX(d, ctx_cbk, l1, p2))<0) + return ret; + return 0; +} + + +/** + * @brief Execute callback on all the CTX'es in all the srv domains in a tls cfg + * @param cfg tls cfg. + * @param ctx_cbk callback function + * @param l1 parameter passed to the callback + * @param p2 parameter passed to the callback + * @return 0 on success, <0 on error + */ +static int tls_foreach_CTX_in_srv_domains(tls_domains_cfg_t* cfg, + per_ctx_cbk_f ctx_cbk, + long l1, void* p2) +{ + int ret; + if ((ret = tls_domain_foreach_CTX(cfg->srv_default, ctx_cbk, l1, p2)) < 0) + return ret; + if ((ret = tls_foreach_CTX_in_domain_lst(cfg->srv_list, ctx_cbk, l1, p2)) + < 0) + return ret; + return 0; +} + + +/** + * @brief Execute callback on all the CTX'es in all the client domains in a tls cfg + * @param cfg tls cfg. + * @param ctx_cbk callback function + * @param l1 parameter passed to the callback + * @param p2 parameter passed to the callback + * @return 0 on success, <0 on error. + */ +static int tls_foreach_CTX_in_cli_domains(tls_domains_cfg_t* cfg, + per_ctx_cbk_f ctx_cbk, + long l1, void* p2) +{ + int ret; + if ((ret = tls_domain_foreach_CTX(cfg->cli_default, ctx_cbk, l1, p2)) < 0) + return ret; + if ((ret = tls_foreach_CTX_in_domain_lst(cfg->cli_list, ctx_cbk, l1, p2)) + < 0) + return ret; + return 0; +} + + +/** + * @brief Execute callback on all the CTX'es in all the domains in a tls cfg + * @param cfg tls cfg + * @param ctx_cbk callback function + * @param l1 parameter passed to the callback + * @param p2 parameter passed to the callback + * @return 0 on success, <0 on error + */ +static int tls_foreach_CTX_in_cfg(tls_domains_cfg_t* cfg, + per_ctx_cbk_f ctx_cbk, + long l1, void* p2) +{ + int ret; + + if ((ret = tls_foreach_CTX_in_srv_domains(cfg, ctx_cbk, l1, p2)) < 0) + return ret; + if ((ret = tls_foreach_CTX_in_cli_domains(cfg, ctx_cbk, l1, p2)) < 0) + return ret; + return 0; +} + + + + +/** + * @brief Fix pathnames when loading domain keys or other list + * + * Fix pathnames, to be used when loading the domain key, cert, ca list a.s.o. + * It will replace path with a fixed shm allocated version. Assumes path->s + * was shm allocated. + * @param path path to be fixed. If it starts with '.' or '/' is left alone + * (forced "relative" or "absolute" path). Otherwise the path is considered + * to be relative to the main config file directory + * (e.g. for /etc/ser/ser.cfg => /etc/ser/\). + * @return 0 on success, -1 on error + */ +int fix_shm_pathname(str* path) +{ + str new_path; + char *abs_path; + + if(path->s && path->len && *path->s != '.' && *path->s != '/') { + abs_path = get_abs_pathname(0, path); + if(abs_path == 0) { + LM_ERR("get abs pathname failed\n"); + return -1; + } + new_path.len = strlen(abs_path); + new_path.s = shm_malloc(new_path.len + 1); + if(new_path.s == 0) { + LM_ERR("no more shm memory\n"); + pkg_free(abs_path); + return -1; + } + memcpy(new_path.s, abs_path, new_path.len); + new_path.s[new_path.len] = 0; + shm_free(path->s); + pkg_free(abs_path); + *path = new_path; + } + return 0; +} + + + +/** + * @brief Load certificate from file + * @param d domain + * @return 0 if not configured or on success, -1 on error + */ +static int load_cert(tls_domain_t* d) +{ + int i; + int procs_no; + + if (!d->cert_file.s || !d->cert_file.len) { + DBG("%s: No certificate configured\n", tls_domain_str(d)); + return 0; + } + if (fix_shm_pathname(&d->cert_file) < 0) + return -1; + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + if (!wolfSSL_CTX_use_certificate_chain_file(d->ctx[i], d->cert_file.s)) { + ERR("%s: Unable to load certificate file '%s'\n", + tls_domain_str(d), d->cert_file.s); + TLS_ERR("load_cert:"); + return -1; + } + + } + return 0; +} + + +/** + * @brief Load CA list from file + * @param d domain + * @return 0 if not configured or on success, -1 on error + */ +static int load_ca_list(tls_domain_t* d) +{ + int i; + int procs_no; + + if ((!d->ca_file.s || !d->ca_file.len) && (!d->ca_path.s || !d->ca_path.len)) { + DBG("%s: No CA list configured\n", tls_domain_str(d)); + return 0; + } + if (d->ca_file.s && d->ca_file.len>0 && fix_shm_pathname(&d->ca_file) < 0) + return -1; + if (d->ca_path.s && d->ca_path.len>0 && fix_shm_pathname(&d->ca_path) < 0) + return -1; + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + if (wolfSSL_CTX_load_verify_locations(d->ctx[i], d->ca_file.s, + d->ca_path.s) != 1) { + ERR("%s: Unable to load CA list file '%s' dir '%s'\n", + tls_domain_str(d), (d->ca_file.s)?d->ca_file.s:"", + (d->ca_path.s)?d->ca_path.s:""); + TLS_ERR("load_ca_list:"); + return -1; + } + if(d->ca_file.s && d->ca_file.len>0) { + wolfSSL_CTX_set_client_CA_list(d->ctx[i], + wolfSSL_load_client_CA_file(d->ca_file.s)); + if (wolfSSL_CTX_get_client_CA_list(d->ctx[i]) == 0) { + ERR("%s: Error while setting client CA list file [%.*s/%d]\n", + tls_domain_str(d), (d->ca_file.s)?d->ca_file.len:0, + (d->ca_file.s)?d->ca_file.s:"", + d->ca_file.len); + TLS_ERR("load_ca_list:"); + return -1; + } + } + } + return 0; +} + + +/** + * @brief Load CRL from file + * @param d domain + * @return 0 if not configured or on success, -1 on error + */ +static int load_crl(tls_domain_t* d) +{ + int i; + int procs_no; + X509_STORE* store; + + if (!d->crl_file.s) { + DBG("%s: No CRL configured\n", tls_domain_str(d)); + return 0; + } + if (fix_shm_pathname(&d->crl_file) < 0) + return -1; + LOG(L_INFO, "%s: Certificate revocation lists will be checked (%.*s)\n", + tls_domain_str(d), d->crl_file.len, d->crl_file.s); + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + if (wolfSSL_CTX_load_verify_locations(d->ctx[i], d->crl_file.s, 0) != 1) { + ERR("%s: Unable to load certificate revocation list '%s'\n", + tls_domain_str(d), d->crl_file.s); + TLS_ERR("load_crl:"); + return -1; + } + store = wolfSSL_CTX_get_cert_store(d->ctx[i]); + X509_STORE_set_flags(store, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + } + return 0; +} + + +#define C_DEF_NO_KRB5 "DEFAULT:!KRB5" +#define C_DEF_NO_KRB5_LEN (sizeof(C_DEF_NO_KRB5)-1) +#define C_NO_KRB5_SUFFIX ":!KRB5" +#define C_NO_KRB5_SUFFIX_LEN (sizeof(C_NO_KRB5_SUFFIX)-1) + +/** + * @brief Configure cipher list + * @param d domain + * @return 0 on success, -1 on error + */ +static int set_cipher_list(tls_domain_t*) __attribute__((unused)); +static int set_cipher_list(tls_domain_t* d) +{ + int i; + int procs_no; + char* cipher_list; + + cipher_list=d->cipher_list.s; +#ifdef TLS_KSSL_WORKARROUND + if (openssl_kssl_malloc_bug) { /* is openssl bug #1467 present ? */ + if (d->cipher_list.s==0) { + /* use "DEFAULT:!KRB5" */ + cipher_list="DEFAULT:!KRB5"; + } else { + /* append ":!KRB5" */ + cipher_list=shm_malloc(d->cipher_list.len+C_NO_KRB5_SUFFIX_LEN+1); + if (cipher_list) { + memcpy(cipher_list, d->cipher_list.s, d->cipher_list.len); + memcpy(cipher_list+d->cipher_list.len, C_NO_KRB5_SUFFIX, + C_NO_KRB5_SUFFIX_LEN); + cipher_list[d->cipher_list.len+C_NO_KRB5_SUFFIX_LEN]=0; + shm_free(d->cipher_list.s); + d->cipher_list.s=cipher_list; + d->cipher_list.len+=C_NO_KRB5_SUFFIX_LEN; + } + } + } +#endif /* TLS_KSSL_WORKARROUND */ + if (!cipher_list) return 0; + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + if (wolfSSL_CTX_set_cipher_list(d->ctx[i], cipher_list) == 0 ) { + ERR("%s: Failure to set SSL context cipher list \"%s\"\n", + tls_domain_str(d), cipher_list); + return -1; + } +#ifndef OPENSSL_NO_DH + setup_dh(d->ctx[i]); +#endif + } + return 0; +} + + +/** + * @brief Enable/disable TLS certificate verification + * @param d domain + * @return 0 + */ +static int set_verification(tls_domain_t* d) +{ + int verify_mode, i; + int procs_no; + + if (d->require_cert || d->verify_client == TLS_VERIFY_CLIENT_ON) { + verify_mode = WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT; + LOG(L_INFO, "%s: %s MUST present valid certificate\n", + tls_domain_str(d), d->type & TLS_DOMAIN_SRV ? "Client" : "Server"); + } else { + if (d->verify_cert || d->verify_client >= TLS_VERIFY_CLIENT_OPTIONAL) { + verify_mode = WOLFSSL_VERIFY_PEER; + if (d->type & TLS_DOMAIN_SRV) { + LOG(L_INFO, "%s: IF client provides certificate then it" + " MUST be valid\n", tls_domain_str(d)); + } else { + LOG(L_INFO, "%s: Server MUST present valid certificate\n", + tls_domain_str(d)); + } + } else { + verify_mode = WOLFSSL_VERIFY_NONE; + if (d->type & TLS_DOMAIN_SRV) { + LOG(L_INFO, "%s: No client certificate required and no checks" + " performed\n", tls_domain_str(d)); + } else { + LOG(L_INFO, "%s: Server MAY present invalid certificate\n", + tls_domain_str(d)); + } + } + } + + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + if (d->verify_client >= TLS_VERIFY_CLIENT_OPTIONAL_NO_CA) { + /* Note that actual verification result is available in $tls_peer_verified */ + wolfSSL_CTX_set_verify(d->ctx[i], verify_mode, verify_callback_unconditional_success); + } else { + wolfSSL_CTX_set_verify(d->ctx[i], verify_mode, 0); + } + wolfSSL_CTX_set_verify_depth(d->ctx[i], d->verify_depth); + + } + return 0; +} + + +/* This callback function is executed when libssl processes the SSL + * handshake and does SSL record layer stuff. It's used to trap + * client-initiated renegotiations. + */ + +static void sr_ssl_ctx_info_callback(const SSL *ssl, int event, int ret) +{ + struct tls_extra_data* data = 0; + int tls_dbg; + + if (event & SSL_CB_HANDSHAKE_START) { + tls_dbg = cfg_get(tls, tls_cfg, debug); + LOG(tls_dbg, "SSL handshake started\n"); + if(data==0) + data = (struct tls_extra_data*)wolfSSL_get_app_data(ssl); + if(data->flags & F_TLS_CON_HANDSHAKED) { + LOG(tls_dbg, "SSL renegotiation initiated by client\n"); + data->flags |= F_TLS_CON_RENEGOTIATION; + } + } + if (event & SSL_CB_HANDSHAKE_DONE) { + tls_dbg = cfg_get(tls, tls_cfg, debug); + if(data==0) + data = (struct tls_extra_data*)wolfSSL_get_app_data(ssl); + LOG(tls_dbg, "SSL handshake done\n"); + data->flags |= F_TLS_CON_HANDSHAKED; + } +} + +/** + * @brief Configure generic SSL parameters + * @param d domain + * @return 0 + */ +static int set_ssl_options(tls_domain_t* d) +{ + int i; + int procs_no; + long options; + + + procs_no=get_max_procs(); + options=SSL_OP_ALL; /* all the bug workarrounds by default */ + options|=SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_CIPHER_SERVER_PREFERENCE; + + for(i = 0; i < procs_no; i++) { + wolfSSL_CTX_set_options(d->ctx[i], options); + if(sr_tls_renegotiation==0) + wolfSSL_CTX_set_info_callback(d->ctx[i], sr_ssl_ctx_info_callback); + } + return 0; +} + + +/** + * @brief Configure TLS session cache parameters + * @param d domain + * @return 0 + */ +static int set_session_cache(tls_domain_t* d) +{ + int i; + int procs_no; + str tls_session_id; + + procs_no=get_max_procs(); + tls_session_id=cfg_get(tls, tls_cfg, session_id); + for(i = 0; i < procs_no; i++) { + /* janakj: I am not sure if session cache makes sense in ser, session + * cache is stored in SSL_CTX and we have one SSL_CTX per process, + * thus sessions among processes will not be reused + */ + wolfSSL_CTX_set_session_cache_mode(d->ctx[i], + cfg_get(tls, tls_cfg, session_cache) ? SSL_SESS_CACHE_SERVER : + SSL_SESS_CACHE_OFF); + /* not really needed is SSL_SESS_CACHE_OFF */ + wolfSSL_CTX_set_session_id_context(d->ctx[i], + (unsigned char*)tls_session_id.s, tls_session_id.len); + } + return 0; +} + + + +/** + * @brief TLS SSL_CTX_set_mode and SSL_CTX_clear_mode wrapper + * @param ctx SSL context + * @param mode WOLFSSL_MODE_* + * @param clear if set to !=0 will do a clear, else (==0) a set + * @return 0 (always succeeds) + */ +static int tls_ssl_ctx_mode(WOLFSSL_CTX* ctx, long mode, void* clear) +{ + if (clear) + wolfSSL_CTX_clear_mode(ctx, mode); + + else + wolfSSL_CTX_set_mode(ctx, mode); + return 0; +} + + + +/** + * @brief TLS set ctx->free_list_max_len + * @param ctx TLS context + * @param val value (<0 ignored) + * @param unused unused + * @return 0 (always succeeds) + */ +static int tls_ssl_ctx_set_freelist(WOLFSSL_CTX* ctx, long val, void* unused) +{ + return 0; +} + +/** + * @brief TLS SSL_CTX_set_max_send_fragment wrapper + * @param ctx TLS context + * @param val value (<0 ignored). Should be between 512 and 16k + * @param unused unused + * @return 0 on success, < 0 on failure (invalid value) + */ +static int tls_ssl_ctx_set_max_send_fragment(WOLFSSL_CTX* ctx, long val, void* unused) +{ + /* WOLFFIX if (val >= 0) + return SSL_CTX_set_max_send_fragment(ctx, val) -1; + */ + + return 0; +} + + + +/** + * @brief TLS SSL_CTX_set_read_ahead wrapper + * @param ctx TLS context + * @param val value (<0 ignored, 0 or >0) + * @param unused unused + * @return 0 (always success). + */ +static int tls_ssl_ctx_set_read_ahead(WOLFSSL_CTX* ctx, long val, void* unused) +{ + if (val >= 0) + wolfSSL_CTX_set_read_ahead(ctx, val); + return 0; +} + + +#ifndef OPENSSL_NO_TLSEXT + +/** + * @brief SNI callback function + * + * callback on server_name -> trigger context switch if a TLS domain + * for the server_name is found (checks socket too) */ +static int tls_server_name_cb(SSL *ssl, int *ad, void *private) +{ + tls_domain_t *orig_domain, *new_domain; + str server_name; + + orig_domain = (tls_domain_t*)private; + server_name.s = (char*)wolfSSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name.s) { + LM_DBG("received server_name (TLS extension): '%s'\n", server_name.s); + } else { + LM_DBG("SSL_get_servername returned NULL: return SSL_TLSEXT_ERR_NOACK\n"); + return SSL_TLSEXT_ERR_NOACK; + } + + server_name.len = strlen(server_name.s); + + new_domain = tls_lookup_cfg(*tls_domains_cfg, TLS_DOMAIN_SRV, + &orig_domain->ip, orig_domain->port, &server_name, 0); + if (new_domain==NULL) { + LM_DBG("TLS domain for socket [%s:%d] and server_name='%s' " + "not found\n", ip_addr2a(&orig_domain->ip), + orig_domain->port, server_name.s); + /* we do not perform SSL_CTX switching, thus the default server domain + for this socket (or the default server domain) will be used. */ + return SSL_TLSEXT_ERR_ALERT_WARNING; + } + + LM_DBG("TLS cfg domain selected for received server name [%s]:" + " socket [%s:%d] server name='%s' -" + " switching SSL CTX to %p dom %p%s\n", + server_name.s, ip_addr2a(&new_domain->ip), + new_domain->port, ZSW(new_domain->server_name.s), + new_domain->ctx[process_no], new_domain, + (new_domain->type & TLS_DOMAIN_DEF)?" (default)":""); + wolfSSL_set_SSL_CTX(ssl, new_domain->ctx[process_no]); + /* SSL_set_SSL_CTX only sets the correct certificate parameters, but does + set the proper verify options. Thus this will be done manually! */ + + wolfSSL_set_options(ssl, wolfSSL_CTX_get_options(wolfSSL_get_SSL_CTX(ssl))); + if ((wolfSSL_get_verify_mode(ssl) == WOLFSSL_VERIFY_NONE) || + (wolfSSL_num_renegotiations(ssl) == 0)) { + /* + * Only initialize the verification settings from the ctx + * if they are not yet set, or if we're called when a new + * SSL connection is set up (num_renegotiations == 0). + * Otherwise, we would possibly reset a per-directory + * configuration which was put into effect by ssl_hook_access. + */ + wolfSSL_set_verify(ssl, wolfSSL_CTX_get_verify_mode(wolfSSL_get_SSL_CTX(ssl)), + wolfSSL_CTX_get_verify_callback(wolfSSL_get_SSL_CTX(ssl))); + + } + + LM_DBG("tls_server_name_cb return SSL_TLSEXT_ERR_OK"); + return SSL_TLSEXT_ERR_OK; +} +#endif + + +/** + * @brief Initialize all domain attributes from default domains if necessary + * @param d initialized TLS domain + * @param def default TLS domains + */ +static int ksr_tls_fix_domain(tls_domain_t* d, tls_domain_t* def) +{ + int i; + int procs_no; + + if (ksr_tls_fill_missing(d, def) < 0) return -1; + + if(d->type & TLS_DOMAIN_ANY) { + if(d->server_name.s==NULL || d->server_name.len<0) { + LM_ERR("%s: tls domain for any address but no server name\n", + tls_domain_str(d)); + return -1; + } + } + + procs_no=get_max_procs(); + d->ctx = (WOLFSSL_CTX**)shm_malloc(sizeof(WOLFSSL_CTX*) * procs_no); + if (!d->ctx) { + ERR("%s: Cannot allocate shared memory\n", tls_domain_str(d)); + return -1; + } + if(d->method>TLS_USE_TLSvRANGE) { + LM_DBG("using tls methods range: %d\n", d->method); + } else { + LM_DBG("using one tls method version: %d\n", d->method); + } + memset(d->ctx, 0, sizeof(WOLFSSL_CTX*) * procs_no); + for(i = 0; i < procs_no; i++) { + + /* libssl >= 1.1.0 */ + //d->ctx[i] = SSL_CTX_new(sr_tls_methods[d->method - 1].TLSMethod); + d->ctx[i] = wolfSSL_CTX_new(wolfSSLv23_method()); + if (d->ctx[i] == NULL) { + unsigned long e = 0; + e = ERR_peek_last_error(); + ERR("%s: Cannot create SSL context [%d] (%lu: %s / %s)\n", + tls_domain_str(d), i, e, ERR_error_string(e, NULL), + ERR_reason_error_string(e)); + return -1; + } + wolfSSL_CTX_set_min_proto_version(d->ctx[i], TLS1_2_VERSION); +#if 0 + if(d->method>TLS_USE_TLSvRANGE) { + if(sr_tls_methods[d->method - 1].TLSMethodMin) { + SSL_CTX_set_min_proto_version(d->ctx[i], + sr_tls_methods[d->method - 1].TLSMethodMin); + } + } else { + if(sr_tls_methods[d->method - 1].TLSMethodMin) { + SSL_CTX_set_min_proto_version(d->ctx[i], + sr_tls_methods[d->method - 1].TLSMethodMin); + } + if(sr_tls_methods[d->method - 1].TLSMethodMax) { + SSL_CTX_set_max_proto_version(d->ctx[i], + sr_tls_methods[d->method - 1].TLSMethodMax); + } + } +#endif + +#ifndef OPENSSL_NO_TLSEXT + /* + * check server domains for server_name extension and register + * callback function + */ + if ((d->type & TLS_DOMAIN_SRV) + && (d->server_name.len>0 || (d->type & TLS_DOMAIN_DEF))) { + if (!wolfSSL_CTX_set_tlsext_servername_callback(d->ctx[i], tls_server_name_cb)) { + LM_ERR("register server_name callback handler for socket " + "[%s:%d], server_name='%s' failed for proc %d\n", + ip_addr2a(&d->ip), d->port, + (d->server_name.s)?d->server_name.s:"", i); + return -1; + } + if (!wolfSSL_CTX_set_servername_arg(d->ctx[i], d)) { + LM_ERR("register server_name callback handler data for socket " + "[%s:%d], server_name='%s' failed for proc %d\n", + ip_addr2a(&d->ip), d->port, + (d->server_name.s)?d->server_name.s:"", i); + return -1; + } + } +#endif + } + +#ifndef OPENSSL_NO_TLSEXT + if ((d->type & TLS_DOMAIN_SRV) + && (d->server_name.len>0 || (d->type & TLS_DOMAIN_DEF))) { + LM_NOTICE("registered server_name callback handler for socket " + "[%s:%d], server_name='%s' ...\n", ip_addr2a(&d->ip), d->port, + (d->server_name.s)?d->server_name.s:""); + } +#endif + + if (load_cert(d) < 0) return -1; + if (load_ca_list(d) < 0) return -1; + if (load_crl(d) < 0) return -1; + // if (set_cipher_list(d) < 0) return -1; + if (set_verification(d) < 0) return -1; + if (set_ssl_options(d) < 0) return -1; + if (set_session_cache(d) < 0) return -1; + return 0; +} + +/** + * @brief Load a private key from a file + * @param d TLS domain + * @return 0 on success, -1 on error + */ +static int load_private_key(tls_domain_t* d) +{ + int idx, ret_pwd, i; + int procs_no; + + if (!d->pkey_file.s || !d->pkey_file.len) { + DBG("%s: No private key specified\n", tls_domain_str(d)); + return 0; + } + if (fix_shm_pathname(&d->pkey_file) < 0) + return -1; + + procs_no=get_max_procs(); + for(i = 0; i < procs_no; i++) { + + for(idx = 0, ret_pwd = 0; idx < 3; idx++) { + ret_pwd = wolfSSL_CTX_use_PrivateKey_file(d->ctx[i], d->pkey_file.s, + SSL_FILETYPE_PEM); + if (ret_pwd) { + break; + } else { + ERR("%s: Unable to load private key '%s'\n", + tls_domain_str(d), d->pkey_file.s); + TLS_ERR("load_private_key:"); + continue; + } + } + + if (!ret_pwd) { + ERR("%s: Unable to load private key file '%s'\n", + tls_domain_str(d), d->pkey_file.s); + TLS_ERR("load_private_key:"); + return -1; + } + if (!wolfSSL_CTX_check_private_key(d->ctx[i])) { + ERR("%s: Key '%s' does not match the public key of the" + " certificate\n", tls_domain_str(d), d->pkey_file.s); + TLS_ERR("load_private_key:"); + return -1; + } + } + + DBG("%s: Key '%s' successfully loaded\n", + tls_domain_str(d), d->pkey_file.s); + return 0; +} + +/** + * @brief Initialize attributes of all domains from default domains if necessary + * + * Initialize attributes of all domains from default domains if necessary, + * fill in missing parameters. + * @param cfg initialized domain + * @param srv_defaults server defaults + * @param cli_defaults command line interface defaults + * @return 0 on success, -1 on error + */ +int tls_fix_domains_cfg(tls_domains_cfg_t* cfg, tls_domain_t* srv_defaults, + tls_domain_t* cli_defaults) +{ + tls_domain_t* d; + int ssl_mode_release_buffers; + int ssl_freelist_max_len; + int ssl_max_send_fragment; + int ssl_read_ahead; + + if (!cfg->cli_default) { + cfg->cli_default = tls_new_domain(TLS_DOMAIN_DEF | TLS_DOMAIN_CLI, + 0, 0); + } + + if (!cfg->srv_default) { + cfg->srv_default = tls_new_domain(TLS_DOMAIN_DEF | TLS_DOMAIN_SRV, + 0, 0); + } + + if (ksr_tls_fix_domain(cfg->srv_default, srv_defaults) < 0) return -1; + if (ksr_tls_fix_domain(cfg->cli_default, cli_defaults) < 0) return -1; + + d = cfg->srv_list; + while (d) { + if (ksr_tls_fix_domain(d, srv_defaults) < 0) return -1; + d = d->next; + } + + d = cfg->cli_list; + while (d) { + if (ksr_tls_fix_domain(d, cli_defaults) < 0) return -1; + d = d->next; + } + + /* Ask for passwords as the last step */ + d = cfg->srv_list; + while(d) { + if (load_private_key(d) < 0) return -1; + d = d->next; + } + + d = cfg->cli_list; + while(d) { + if (load_private_key(d) < 0) return -1; + d = d->next; + } + + if (load_private_key(cfg->srv_default) < 0) return -1; + if (load_private_key(cfg->cli_default) < 0) return -1; + + /* set various global per CTX options + * (done here to show possible missing features messages only once) + */ + ssl_mode_release_buffers = cfg_get(tls, tls_cfg, ssl_release_buffers); + ssl_freelist_max_len = cfg_get(tls, tls_cfg, ssl_freelist_max); + ssl_max_send_fragment = cfg_get(tls, tls_cfg, ssl_max_send_fragment); + ssl_read_ahead = cfg_get(tls, tls_cfg, ssl_read_ahead); + + /* set SSL_MODE_RELEASE_BUFFERS if ssl_mode_release_buffers !=0, + reset if == 0 and ignore if < 0 */ + /* only in >= 1.0.0 */ + if (ssl_mode_release_buffers >= 0 && + tls_foreach_CTX_in_cfg(cfg, tls_ssl_ctx_mode, SSL_MODE_RELEASE_BUFFERS, + (void*)(long)(ssl_mode_release_buffers==0)) + < 0) { + ERR("invalid ssl_release_buffers value (%d)\n", + ssl_mode_release_buffers); + return -1; + } + /* only in >= 1.0.0 */ + +#ifndef OPENSSL_NO_BUF_FREELISTS + if (tls_foreach_CTX_in_cfg(cfg, tls_ssl_ctx_set_freelist, + ssl_freelist_max_len, 0) < 0) { + ERR("invalid ssl_freelist_max_len value (%d)\n", + ssl_freelist_max_len); + return -1; + } +#endif + +#if defined (OPENSSL_NO_BUF_FREELISTS) + if (ssl_freelist_max_len >= 0) + ERR("cannot change openssl freelist_max_len, openssl too old" + "(needed at least 1.0.0) or compiled without freelist support" + " (OPENSSL_NO_BUF_FREELIST)\n"); +#endif + + /* only in >= 0.9.9 */ + if (tls_foreach_CTX_in_cfg(cfg, tls_ssl_ctx_set_max_send_fragment, + ssl_max_send_fragment, 0) < 0) { + ERR("invalid ssl_max_send_fragment value (%d)\n", + ssl_max_send_fragment); + return -1; + } + if (tls_foreach_CTX_in_cfg(cfg, tls_ssl_ctx_set_read_ahead, + ssl_read_ahead, 0) < 0) { + ERR("invalid ssl_read_ahead value (%d)\n", ssl_read_ahead); + return -1; + } + /* set options for SSL_write: + WOLFSSL_MODE_ACCEPT_MOVING_WRITE_BUFFER - needed when queueing + clear text for a future write (WANTS_READ). In this case the + buffer address will change for the repeated SSL_write() and + without this option it will trigger the openssl sanity checks. + SSL_MODE_ENABLE_PARTIAL_WRITE - needed to deal with potentially + huge multi-record writes that don't fit in the default buffer + (the default buffer must have space for at least 1 record) */ + if (tls_foreach_CTX_in_cfg(cfg, tls_ssl_ctx_mode, + WOLFSSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_ENABLE_PARTIAL_WRITE, + 0) < 0) { + ERR("could not set WOLFSSL_MODE_ACCEPT_MOVING_WRITE_BUFFER and" + " SSL_MODE_ENABLE_PARTIAL_WRITE\n"); + return -1; + } + + return 0; +} + + +/** + * @brief Create new configuration structure + * + * Create new configuration structure in new allocated shared memory + * @return configuration structure or zero on error + */ +tls_domains_cfg_t* tls_new_cfg(void) +{ + tls_domains_cfg_t* r; + + r = (tls_domains_cfg_t*)shm_malloc(sizeof(tls_domains_cfg_t)); + if (!r) { + ERR("No memory left\n"); + return 0; + } + memset(r, 0, sizeof(tls_domains_cfg_t)); + atomic_set(&r->ref_count, 0); + return r; +} + + +/** + * @brief Lookup TLS configuration based on type, ip, and port + * @param cfg configuration set + * @param type type of configuration + * @param ip IP for configuration + * @param port port for configuration + * @return found configuration or default, if not found + */ +tls_domain_t* tls_lookup_cfg(tls_domains_cfg_t* cfg, int type, + struct ip_addr* ip, unsigned short port, str *sname, str *srvid) +{ + tls_domain_t *p; + int dotpos; + + if (type & TLS_DOMAIN_DEF) { + if (type & TLS_DOMAIN_SRV) return cfg->srv_default; + else return cfg->cli_default; + } else { + if (type & TLS_DOMAIN_SRV) p = cfg->srv_list; + else p = cfg->cli_list; + } + + while (p) { + if(srvid && srvid->len>0) { + LM_DBG("comparing addr: [%s:%d] [%s:%d] -- id: [%.*s] [%.*s]\n", + ip_addr2a(&p->ip), p->port, ip_addr2a(ip), port, + p->server_id.len, ZSW(p->server_id.s), + srvid->len, ZSW(srvid->s)); + if(p->server_id.s && p->server_id.len==srvid->len + && strncasecmp(p->server_id.s, srvid->s, srvid->len)==0) { + LM_DBG("TLS config found by server id\n"); + return p; + } + + } + if(sname) { + LM_DBG("comparing addr: l[%s:%d] r[%s:%d] -- sni: l[%.*s] r[%.*s] %d" + " -- type: %d\n", + ip_addr2a(&p->ip), p->port, ip_addr2a(ip), port, + p->server_name.len, ZSW(p->server_name.s), + sname->len, ZSW(sname->s), p->server_name_mode, p->type); + } + if ((p->type & TLS_DOMAIN_ANY) + || ((p->port==0 || p->port == port) + && ip_addr_cmp(&p->ip, ip))) { + if(sname && sname->s && sname->len>0 + && p->server_name.s && p->server_name.len>0) { + if (p->server_name_mode!=KSR_TLS_SNM_SUBDOM) { + /* match sni domain */ + if(p->server_name.len==sname->len + && strncasecmp(p->server_name.s, sname->s, + sname->len)==0) { + LM_DBG("socket+server_name based TLS server domain found\n"); + return p; + } + } + if ((p->server_name_mode==KSR_TLS_SNM_INCDOM + || p->server_name_mode==KSR_TLS_SNM_SUBDOM) + && (p->server_name.lenlen)) { + dotpos = sname->len - p->server_name.len - 1; + if(sname->s[dotpos] == '.' + && strncasecmp(p->server_name.s, + sname->s + dotpos + 1, + p->server_name.len)==0) { + LM_DBG("socket+server_name based TLS server sub-domain found\n"); + return p; + } + } + } else { + if (!(p->type & TLS_DOMAIN_ANY)) { + LM_DBG("socket based TLS server domain found\n"); + return p; + } + } + } + p = p->next; + } + + /* No matching domain found, return default */ + if (type & TLS_DOMAIN_SRV) return cfg->srv_default; + else return cfg->cli_default; +} + + +/** + * @brief Check whether configuration domain is duplicated + * @param cfg configuration set + * @param d checked domain + * @return 1 if domain is duplicated, 0 if it's not + */ +int ksr_tls_domain_duplicated(tls_domains_cfg_t* cfg, tls_domain_t* d) +{ + tls_domain_t *p; + + if (d->type & TLS_DOMAIN_DEF) { + if (d->type & TLS_DOMAIN_SRV) { + if(cfg->srv_default==d) { + return 0; + } + return cfg->srv_default != NULL; + } else { + if(cfg->cli_default==d) { + return 0; + } + return cfg->cli_default != NULL; + } + } else { + if (d->type & TLS_DOMAIN_SRV) p = cfg->srv_list; + else p = cfg->cli_list; + } + + if(d->type & TLS_DOMAIN_ANY) { + /* any address, it must have server_name for SNI */ + if(d->server_name.len==0) { + LM_WARN("duplicate definition for a tls profile (same address)" + " and no server name provided\n"); + return 1; + } else { + return 0; + } + } + + while (p) { + if(p!=d) { + if ((p->port == d->port) && ip_addr_cmp(&p->ip, &d->ip)) { + if(d->server_name.len==0 || p->server_name.len==0) { + LM_WARN("duplicate definition for a tls profile (same address)" + " and no server name provided\n"); + return 1; + } + } + } + p = p->next; + } + + return 0; +} + + +/** + * @brief Add a domain to the configuration set + * @param cfg configuration set + * @param d TLS domain + * @return 1 if domain already exists, 0 after addition, -1 on error + */ +int tls_add_domain(tls_domains_cfg_t* cfg, tls_domain_t* d) +{ + if (!cfg) { + ERR("TLS configuration structure missing\n"); + return -1; + } + + if (d->type & TLS_DOMAIN_DEF) { + if (d->type & TLS_DOMAIN_CLI) { + cfg->cli_default = d; + } else { + cfg->srv_default = d; + } + } else { + if (d->type & TLS_DOMAIN_SRV) { + d->next = cfg->srv_list; + cfg->srv_list = d; + } else { + d->next = cfg->cli_list; + cfg->cli_list = d; + } + } + return 0; +} diff --git a/src/modules/tls_wolfssl/tls_domain.h b/src/modules/tls_wolfssl/tls_domain.h new file mode 100644 index 00000000000..f027b869d6e --- /dev/null +++ b/src/modules/tls_wolfssl/tls_domain.h @@ -0,0 +1,238 @@ +/* + * TLS module + * + * Copyright (C) 2005,2006 iptelorg GmbH + * Copyright (C) 2013 Motorola Solutions, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * Kamailio TLS support :: virtual domain configuration support + * @file + * @ingroup tls + * Module: @ref tls + */ + + +#ifndef _TLS_DOMAIN_H +#define _TLS_DOMAIN_H + +#include "../../core/str.h" +#include "../../core/ip_addr.h" +#include "../../core/atomic_ops.h" +#include +#include + + +#define TLS_OP_SSLv2_PLUS 0 +#define TLS_OP_SSLv3_PLUS (TLS_OP_SSLv2_PLUS | SSL_OP_NO_SSLv2) +#define TLS_OP_TLSv1_PLUS (TLS_OP_SSLv3_PLUS | SSL_OP_NO_SSLv3) + +#ifdef SSL_OP_NO_TLSv1 +# define TLS_OP_TLSv1_1_PLUS (TLS_OP_TLSv1_PLUS | SSL_OP_NO_TLSv1) + +# ifdef SSL_OP_NO_TLSv1_1 +# define TLS_OP_TLSv1_2_PLUS (TLS_OP_TLSv1_1_PLUS | SSL_OP_NO_TLSv1_1) +# endif /*SSL_OP_NO_TLSv1_1*/ + +#endif /*SSL_OP_NO_TLSv1*/ + +/** + * Available TLS methods + */ +enum tls_method { + TLS_METHOD_UNSPEC = 0, + TLS_USE_SSLv23_cli, + TLS_USE_SSLv23_srv, + TLS_USE_SSLv23, /* any SSL/TLS version */ + TLS_USE_SSLv2_cli, + TLS_USE_SSLv2_srv, + TLS_USE_SSLv2, /* only SSLv2 (deprecated) */ + TLS_USE_SSLv3_cli, + TLS_USE_SSLv3_srv, + TLS_USE_SSLv3, /* only SSLv3 (insecure) */ + TLS_USE_TLSv1_cli, + TLS_USE_TLSv1_srv, + TLS_USE_TLSv1, /* only TLSv1.0 */ + TLS_USE_TLSv1_1_cli, + TLS_USE_TLSv1_1_srv, + TLS_USE_TLSv1_1, /* only TLSv1.1 */ + TLS_USE_TLSv1_2_cli, + TLS_USE_TLSv1_2_srv, + TLS_USE_TLSv1_2, /* only TLSv1.2 */ + TLS_USE_TLSvRANGE, /* placeholder - TLSvX ranges must be after it */ + TLS_USE_TLSv1_PLUS, /* TLSv1.0 or greater */ + TLS_USE_TLSv1_1_PLUS, /* TLSv1.1 or greater */ + TLS_USE_TLSv1_2_PLUS, /* TLSv1.2 or greater */ + TLS_METHOD_MAX +}; + + +/** + * TLS configuration domain type + */ +enum tls_domain_type { + TLS_DOMAIN_DEF = (1 << 0), /**< Default domain */ + TLS_DOMAIN_SRV = (1 << 1), /**< Server domain */ + TLS_DOMAIN_CLI = (1 << 2), /**< Client domain */ + TLS_DOMAIN_ANY = (1 << 3) /**< Any address */ +}; + +#define KSR_TLS_SNM_STRICT 0 /**< Match server_name only */ +#define KSR_TLS_SNM_INCDOM 1 /**< Match server_name and subdomains */ +#define KSR_TLS_SNM_SUBDOM 2 /**< Match subdomains only */ + + +/** + * TLS "verify_client" options + */ +enum tls_verify_client_options { + TLS_VERIFY_CLIENT_OFF = 0, + TLS_VERIFY_CLIENT_ON = 1, + TLS_VERIFY_CLIENT_OPTIONAL = 2, + TLS_VERIFY_CLIENT_OPTIONAL_NO_CA = 3 +}; + + +/** + * separate configuration per ip:port + */ +typedef struct tls_domain { + int type; + struct ip_addr ip; + unsigned short port; + WOLFSSL_CTX** ctx; + str cert_file; + str pkey_file; + int verify_cert; + int verify_depth; + str ca_file; + str ca_path; + int require_cert; + str cipher_list; + enum tls_method method; + str crl_file; + str server_name; + int server_name_mode; + str server_id; + int verify_client; + struct tls_domain* next; +} tls_domain_t; + + +/** + * TLS configuration structures + */ +typedef struct tls_domains_cfg { + tls_domain_t* srv_default; /**< Default server domain */ + tls_domain_t* cli_default; /**< Default client domain */ + tls_domain_t* srv_list; /**< Server domain list */ + tls_domain_t* cli_list; /**< Client domain list */ + struct tls_domains_cfg* next; /**< Next element in the garbage list */ + atomic_t ref_count; /**< How many connections use this configuration */ +} tls_domains_cfg_t; + + +/** + * @brief Create a new TLS domain structure + * + * Create a new domain structure in new allocated shared memory. + * @param type domain Type + * @param ip domain IP + * @param port domain port + * @return new domain + */ +tls_domain_t *tls_new_domain(int type, struct ip_addr *ip, + unsigned short port); + + +/** + * @brief Free all memory used by TLS configuration domain + * @param d freed domain + */ +void tls_free_domain(tls_domain_t* d); + + +/** + * @brief Generate TLS domain identifier + * @param d printed domain + * @return printed domain, with zero termination + */ +char* tls_domain_str(tls_domain_t* d); + + + +/** + * @brief Create new TLS configuration structure + * + * Create new configuration structure in new allocated shared memory. + * @return configuration structure or zero on error + */ +tls_domains_cfg_t* tls_new_cfg(void); + + +/** + * @brief Add a domain to the configuration set + * @param cfg configuration set + * @param d TLS domain + * @return 1 if domain already exists, 0 after addition, -1 on error + */ +int tls_add_domain(tls_domains_cfg_t* cfg, tls_domain_t* d); + + +/** + * @brief Initialize attributes of all domains from default domains if necessary + * + * Initialize attributes of all domains from default domains if necessary, + * fill in missing parameters. + * @param cfg initialized domain + * @param srv_defaults server defaults + * @param cli_defaults command line interface defaults + * @return 0 on success, -1 on error + */ +int tls_fix_domains_cfg(tls_domains_cfg_t* cfg, tls_domain_t* srv_defaults, + tls_domain_t* cli_defaults); + + +/** + * @brief Lookup TLS configuration based on type, ip, and port + * @param cfg configuration set + * @param type type of configuration + * @param ip IP for configuration + * @param port port for configuration + * @param sname server name + * @return found configuration or default, if not found + */ +tls_domain_t* tls_lookup_cfg(tls_domains_cfg_t* cfg, int type, + struct ip_addr* ip, unsigned short port, str *sname, str *srvid); + + +/** + * @brief Free TLS configuration structure + * @param cfg freed configuration + */ +void tls_free_cfg(tls_domains_cfg_t* cfg); + + +/** + * @brief Destroy all TLS configuration data + */ +void tls_destroy_cfg(void); + +/** + * @brief Check if a TLS configuration domain exists + */ +int ksr_tls_domain_duplicated(tls_domains_cfg_t* cfg, tls_domain_t* d); + +#endif /* _TLS_DOMAIN_H */ diff --git a/src/modules/tls_wolfssl/tls_dump_vf.c b/src/modules/tls_wolfssl/tls_dump_vf.c new file mode 100644 index 00000000000..24da280e21b --- /dev/null +++ b/src/modules/tls_wolfssl/tls_dump_vf.c @@ -0,0 +1,150 @@ +/* + * TLS module + * + * Copyright (C) 2006 enum.at + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Exception: permission to copy, modify, propagate, and distribute a work + * formed by combining OpenSSL toolkit software and the code in this file, + * such as linking with software components and libraries released under + * OpenSSL project license. + */ + +/** log the verification failure reason. + * @file tls_dump_vf.c + * @ingroup: tls + * Module: @ref tls + */ + +#include "tls_dump_vf.h" + +#include +#include +#include "../../core/dprint.h" +#include "tls_mod.h" +#include "tls_cfg.h" + +/** log the verification failure reason. + */ +void tls_dump_verification_failure(long verification_result) +{ + int tls_log; + + tls_log = cfg_get(tls, tls_cfg, log); + switch(verification_result) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + LOG(tls_log, "verification failure: unable to get issuer certificate\n"); + break; + case X509_V_ERR_UNABLE_TO_GET_CRL: + LOG(tls_log, "verification failure: unable to get certificate CRL\n"); + break; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + LOG(tls_log, "verification failure: unable to decrypt certificate's signature\n"); + break; + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + LOG(tls_log, "verification failure: unable to decrypt CRL's signature\n"); + break; + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + LOG(tls_log, "verification failure: unable to decode issuer public key\n"); + break; + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + LOG(tls_log, "verification failure: certificate signature failure\n"); + break; + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + LOG(tls_log, "verification failure: CRL signature failure\n"); + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + LOG(tls_log, "verification failure: certificate is not yet valid\n"); + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + LOG(tls_log, "verification failure: certificate has expired\n"); + break; + case X509_V_ERR_CRL_NOT_YET_VALID: + LOG(tls_log, "verification failure: CRL is not yet valid\n"); + break; + case X509_V_ERR_CRL_HAS_EXPIRED: + LOG(tls_log, "verification failure: CRL has expired\n"); + break; + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + LOG(tls_log, "verification failure: format error in certificate's notBefore field\n"); + break; + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + LOG(tls_log, "verification failure: format error in certificate's notAfter field\n"); + break; + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + LOG(tls_log, "verification failure: format error in CRL's lastUpdate field\n"); + break; + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + LOG(tls_log, "verification failure: format error in CRL's nextUpdate field\n"); + break; + case X509_V_ERR_OUT_OF_MEM: + LOG(tls_log, "verification failure: out of memory\n"); + break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + LOG(tls_log, "verification failure: self signed certificate\n"); + break; + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + LOG(tls_log, "verification failure: self signed certificate in certificate chain\n"); + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + LOG(tls_log, "verification failure: unable to get local issuer certificate\n"); + break; + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + LOG(tls_log, "verification failure: unable to verify the first certificate\n"); + break; + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + LOG(tls_log, "verification failure: certificate chain too long\n"); + break; + case X509_V_ERR_CERT_REVOKED: + LOG(tls_log, "verification failure: certificate revoked\n"); + break; + case X509_V_ERR_INVALID_CA: + LOG(tls_log, "verification failure: invalid CA certificate\n"); + break; + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + LOG(tls_log, "verification failure: path length constraint exceeded\n"); + break; + case X509_V_ERR_INVALID_PURPOSE: + LOG(tls_log, "verification failure: unsupported certificate purpose\n"); + break; + case X509_V_ERR_CERT_UNTRUSTED: + LOG(tls_log, "verification failure: certificate not trusted\n"); + break; + case X509_V_ERR_CERT_REJECTED: + LOG(tls_log, "verification failure: certificate rejected\n"); + break; + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + LOG(tls_log, "verification failure: subject issuer mismatch\n"); + break; + case X509_V_ERR_AKID_SKID_MISMATCH: + LOG(tls_log, "verification failure: authority and subject key identifier mismatch\n"); + break; + case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: + LOG(tls_log, "verification failure: authority and issuer serial number mismatch\n"); + break; + case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + LOG(tls_log, "verification failure: key usage does not include certificate signing\n"); + break; + case X509_V_ERR_APPLICATION_VERIFICATION: + LOG(tls_log, "verification failure: application verification failure\n"); + break; + } +} + + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_dump_vf.h b/src/modules/tls_wolfssl/tls_dump_vf.h new file mode 100644 index 00000000000..aa2a836d61e --- /dev/null +++ b/src/modules/tls_wolfssl/tls_dump_vf.h @@ -0,0 +1,41 @@ +/* + * TLS module + * + * Copyright (C) 2006 enum.at + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Exception: permission to copy, modify, propagate, and distribute a work + * formed by combining OpenSSL toolkit software and the code in this file, + * such as linking with software components and libraries released under + * OpenSSL project license. + */ +/** log the verification failure reason. + * @file tls_dump_vf.h + * @ingroup: tls + * Module: @ref tls + */ + +#ifndef __tls_dump_vf_h +#define __tls_dump_vf_h + + +void tls_dump_verification_failure(long verification_result); + +#endif /*__tls_dump_vf_h*/ + +/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ diff --git a/src/modules/tls_wolfssl/tls_init.c b/src/modules/tls_wolfssl/tls_init.c new file mode 100644 index 00000000000..8094895fd5a --- /dev/null +++ b/src/modules/tls_wolfssl/tls_init.c @@ -0,0 +1,583 @@ +/* + * TLS module + * + * Copyright (C) 2005,2006 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \defgroup tls Kamailio TLS support + * + * This modules implements SIP over TCP with TLS encryption. + * Make sure you read the README file that describes configuration + * of TLS for single servers and servers hosting multiple domains, + * and thus using multiple SSL/TLS certificates. + * + * + */ +/*! + * \file + * \brief Kamailio TLS support :: Initialization + * \ingroup tls + * Module: \ref tls + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../core/dprint.h" +#include "../../core/mem/shm_mem.h" +#include "../../core/tcp_init.h" +#include "../../core/socket_info.h" +#include "../../core/pt.h" +#include "../../core/cfg/cfg.h" +#include "../../core/cfg/cfg_ctx.h" +#include "tls_verify.h" +#include "tls_domain.h" +#include "tls_util.h" +#include "tls_mod.h" +#include "tls_init.h" +#include "tls_ct_wrq.h" +#include "tls_cfg.h" + +/* will be set to 1 when the TLS env is initialized to make destroy safe */ +static int tls_mod_preinitialized = 0; +static int tls_mod_initialized = 0; + + +/* replace openssl zlib compression with our version if necessary + * (the openssl zlib compression uses the wrong malloc, see + * openssl #1468): 0.9.8-dev < version <0.9.8e-beta1 */ + +#ifdef TLS_KSSL_WORKARROUND +#endif /* TLS_KSSL_WORKARROUND */ + +/* openssl < 1. 0 */ + + + +#ifndef OPENSSL_NO_COMP +#define TLS_COMP_SUPPORT +#else +#undef TLS_COMP_SUPPORT +#endif + +#ifndef OPENSSL_NO_KRB5 +#define TLS_KERBEROS_SUPPORT +#else +#undef TLS_KERBEROS_SUPPORT +#endif + + +#ifdef TLS_KSSL_WORKARROUND +int openssl_kssl_malloc_bug=0; /* is openssl bug #1467 present ? */ +#endif + +sr_tls_methods_t sr_tls_methods[TLS_METHOD_MAX]; + +#ifdef NO_TLS_MALLOC_DBG +#undef TLS_MALLOC_DBG /* extra malloc debug info from openssl */ +#endif /* NO_TLS_MALLOC_DBG */ + +/* + * Wrappers around SER shared memory functions + * (which can be macros) + */ +#ifdef TLS_MALLOC_DBG +#warning "tls module compiled with malloc debugging info (extra overhead)" +#include + +/* +#define RAND_NULL_MALLOC (1024) +#define NULL_GRACE_PERIOD 10U +*/ + + + +inline static char* buf_append(char* buf, char* end, char* str, int str_len) +{ + if ( (buf+str_len)16) bt_size=16;*/ /* go up only 12 entries */ + for (i=3; i< bt_size; i++){ + /* try to isolate only the function name*/ + s=strchr(bt_strs[i], '('); + if (s && ((e=strchr(s, ')'))!=0)){ + s++; + }else if ((s=strchr(bt_strs[i], '['))!=0){ + e=s+strlen(s); + }else{ + s=bt_strs[i]; e=s+strlen(s); /* add thw whole string */ + } + next=buf_append(p, end, s, (int)(long)(e-s)); + if (next==0) break; + else p=next; + if (pfunc= + p+size; + } +#ifdef RAND_NULL_MALLOC + }else{ + p=0; + backtrace2str(bt_buf, sizeof(bt_buf)); + LM_CRIT("tls - random ser_malloc(%d)[%s:%d] returning null - bt: %s\n", + size, file, line, bt_buf); + } +#endif + return p; +} + + +static void* ser_realloc(void *ptr, size_t size, const char* file, int line) +{ + void *p; + char bt_buf[1024]; + int s; +#ifdef RAND_NULL_MALLOC + static ticks_t st=0; + + /* start random null returns only after + * NULL_GRACE_PERIOD from first call */ + if (st==0) st=get_ticks(); + if (((get_ticks()-st)func= + p+size; + } +#ifdef RAND_NULL_MALLOC + }else{ + p=0; + backtrace2str(bt_buf, sizeof(bt_buf)); + LM_CRIT("tls - random ser_realloc(%p, %d)[%s:%d]" + " returning null - bt: %s\n", ptr, size, file, line, + bt_buf); + } +#endif + return p; +} + +#else /*TLS_MALLOC_DBG */ + + +static void* ser_malloc(size_t size, const char *fname, int fline) +{ + return shm_malloc(size); +} + + +static void* ser_realloc(void *ptr, size_t size, const char *fname, int fline) +{ + return shm_realloc(ptr, size); +} + +static void ser_free(void *ptr, const char *fname, int fline) +{ + if (ptr) { + shm_free(ptr); + } +} + +#endif /* LIBRESSL_VERSION_NUMBER */ + +/* + * Initialize TLS socket + */ +int tls_h_init_si_f(struct socket_info *si) +{ + int ret; + /* + * reuse tcp initialization + */ + ret = tcp_init(si); + if (ret != 0) { + LM_ERR("Error while initializing TCP part of TLS socket %.*s:%d\n", + si->address_str.len, si->address_str.s, si->port_no); + goto error; + } + + si->proto = PROTO_TLS; + return 0; + +error: + if (si->socket != -1) { + close(si->socket); + si->socket = -1; + } + return ret; +} + + + +/* + * initialize ssl methods + */ +static void init_ssl_methods(void) +{ + /* openssl 1.1.0+ */ + memset(sr_tls_methods, 0, sizeof(sr_tls_methods)); + + /* any SSL/TLS version */ + sr_tls_methods[TLS_USE_SSLv23_cli - 1].TLSMethod = TLS_client_method(); + sr_tls_methods[TLS_USE_SSLv23_srv - 1].TLSMethod = TLS_server_method(); + sr_tls_methods[TLS_USE_SSLv23 - 1].TLSMethod = TLS_method(); + +#ifndef OPENSSL_NO_SSL3_METHOD + sr_tls_methods[TLS_USE_SSLv3_cli - 1].TLSMethod = TLS_client_method(); + sr_tls_methods[TLS_USE_SSLv3_cli - 1].TLSMethodMin = SSL3_VERSION; + sr_tls_methods[TLS_USE_SSLv3_cli - 1].TLSMethodMax = SSL3_VERSION; + sr_tls_methods[TLS_USE_SSLv3_srv - 1].TLSMethod = TLS_server_method(); + sr_tls_methods[TLS_USE_SSLv3_srv - 1].TLSMethodMin = SSL3_VERSION; + sr_tls_methods[TLS_USE_SSLv3_srv - 1].TLSMethodMax = SSL3_VERSION; + sr_tls_methods[TLS_USE_SSLv3 - 1].TLSMethod = TLS_method(); + sr_tls_methods[TLS_USE_SSLv3 - 1].TLSMethodMin = SSL3_VERSION; + sr_tls_methods[TLS_USE_SSLv3 - 1].TLSMethodMax = SSL3_VERSION; +#endif + + sr_tls_methods[TLS_USE_TLSv1_cli - 1].TLSMethod = TLS_client_method(); + sr_tls_methods[TLS_USE_TLSv1_cli - 1].TLSMethodMin = TLS1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_cli - 1].TLSMethodMax = TLS1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_srv - 1].TLSMethod = TLS_server_method(); + sr_tls_methods[TLS_USE_TLSv1_srv - 1].TLSMethodMin = TLS1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_srv - 1].TLSMethodMax = TLS1_VERSION; + sr_tls_methods[TLS_USE_TLSv1 - 1].TLSMethod = TLS_method(); + sr_tls_methods[TLS_USE_TLSv1 - 1].TLSMethodMin = TLS1_VERSION; + sr_tls_methods[TLS_USE_TLSv1 - 1].TLSMethodMax = TLS1_VERSION; + + sr_tls_methods[TLS_USE_TLSv1_1_cli - 1].TLSMethod = TLS_client_method(); + sr_tls_methods[TLS_USE_TLSv1_1_cli - 1].TLSMethodMin = TLS1_1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_1_cli - 1].TLSMethodMax = TLS1_1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_1_srv - 1].TLSMethod = TLS_server_method(); + sr_tls_methods[TLS_USE_TLSv1_1_srv - 1].TLSMethodMin = TLS1_1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_1_srv - 1].TLSMethodMax = TLS1_1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_1 - 1].TLSMethod = TLS_method(); + sr_tls_methods[TLS_USE_TLSv1_1 - 1].TLSMethodMin = TLS1_1_VERSION; + sr_tls_methods[TLS_USE_TLSv1_1 - 1].TLSMethodMax = TLS1_1_VERSION; + + sr_tls_methods[TLS_USE_TLSv1_2_cli - 1].TLSMethod = TLS_client_method(); + sr_tls_methods[TLS_USE_TLSv1_2_cli - 1].TLSMethodMin = TLS1_2_VERSION; + sr_tls_methods[TLS_USE_TLSv1_2_cli - 1].TLSMethodMax = TLS1_2_VERSION; + sr_tls_methods[TLS_USE_TLSv1_2_srv - 1].TLSMethod = TLS_server_method(); + sr_tls_methods[TLS_USE_TLSv1_2_srv - 1].TLSMethodMin = TLS1_2_VERSION; + sr_tls_methods[TLS_USE_TLSv1_2_srv - 1].TLSMethodMax = TLS1_2_VERSION; + sr_tls_methods[TLS_USE_TLSv1_2 - 1].TLSMethod = TLS_method(); + sr_tls_methods[TLS_USE_TLSv1_2 - 1].TLSMethodMin = TLS1_2_VERSION; + sr_tls_methods[TLS_USE_TLSv1_2 - 1].TLSMethodMax = TLS1_2_VERSION; + + /* ranges of TLS versions (require a minimum TLS version) */ + sr_tls_methods[TLS_USE_TLSv1_PLUS - 1].TLSMethod = TLS_method(); + sr_tls_methods[TLS_USE_TLSv1_PLUS - 1].TLSMethodMin = TLS1_VERSION; + + sr_tls_methods[TLS_USE_TLSv1_1_PLUS - 1].TLSMethod = TLS_method(); + sr_tls_methods[TLS_USE_TLSv1_1_PLUS - 1].TLSMethodMin = TLS1_1_VERSION; + + sr_tls_methods[TLS_USE_TLSv1_2_PLUS - 1].TLSMethod = TLS_method(); + sr_tls_methods[TLS_USE_TLSv1_2_PLUS - 1].TLSMethodMin = TLS1_2_VERSION; +} + + +/* + * Fix openssl compression bugs if necessary + */ +static int init_tls_compression(void) +{ + return 0; +} + + +/** + * tls pre-init function + * - executed when module is loaded + */ +int tls_pre_init(void) +{ + void *(*mf)(size_t, const char *, int) = NULL; + void *(*rf)(void *, size_t, const char *, int) = NULL; + void (*ff)(void *, const char *, int) = NULL; + +#ifdef KSR_LIBSSL_STATIC + LM_INFO("libssl linked mode: static\n"); +#endif + + /* + * this has to be called before any function calling CRYPTO_malloc, + * CRYPTO_malloc will set allow_customize in openssl to 0 + */ + // CRYPTO_get_mem_functions(&mf, &rf, &ff); + LM_DBG("initial memory functions - malloc: %p realloc: %p free: %p\n", + mf, rf, ff); + mf = NULL; + rf = NULL; + ff = NULL; +#ifdef TLS_MALLOC_DBG + if (!CRYPTO_set_mem_ex_functions(ser_malloc, ser_realloc, ser_free)) { +#else + if (!CRYPTO_set_mem_functions(ser_malloc, ser_realloc, ser_free)) { +#endif + LM_ERR("Unable to set the memory allocation functions\n"); + // CRYPTO_get_mem_functions(&mf, &rf, &ff); + LM_ERR("libssl current mem functions - m: %p r: %p f: %p\n", + mf, rf, ff); + LM_ERR("module mem functions - m: %p r: %p f: %p\n", + ser_malloc, ser_realloc, ser_free); + LM_ERR("Be sure tls module is loaded before any other module using" + " libssl (can be loaded first to be safe)\n"); + return -1; + } + LM_DBG("updated memory functions - malloc: %p realloc: %p free: %p\n", + ser_malloc, ser_realloc, ser_free); + + init_tls_compression(); + return 0; +} + +/** + * tls mod pre-init function + * - executed before any mod_init() + */ +int tls_h_mod_pre_init_f(void) +{ + if(tls_mod_preinitialized==1) { + LM_DBG("already mod pre-initialized\n"); + return 0; + } + LM_DBG("preparing tls env for modules initialization\n"); + + LM_DBG("preparing tls env for modules initialization (libssl >=1.1)\n"); + wolfSSL_OPENSSL_init_ssl(0, NULL); + wolfSSL_load_error_strings(); + tls_mod_preinitialized=1; + return 0; +} + +/* + * First step of TLS initialization + */ +int tls_h_mod_init_f(void) +{ + /*struct socket_info* si;*/ + long ssl_version; + const char *ssl_version_txt; + int low_mem_threshold1; + int low_mem_threshold2; + str tls_grp; + str s; + cfg_ctx_t* cfg_ctx; + + if(tls_mod_initialized == 1) { + LM_DBG("already initialized\n"); + return 0; + } + LM_DBG("initializing tls system\n"); + + ssl_version=wolfSSL_OpenSSL_version_num(); + ssl_version_txt=wolfSSL_OpenSSL_version(OPENSSL_VERSION); + + /* check if version have the same major minor and fix level + * (e.g. 0.9.8a & 0.9.8c are ok, but 0.9.8 and 0.9.9x are not) + * - values is represented as 0xMMNNFFPPS: major minor fix patch status + * 0x00090705f == 0.9.7e release */ + if ((ssl_version>>12)!=(OPENSSL_VERSION_NUMBER>>12)){ + LM_CRIT("installed openssl library" + " version is too different from the library the " NAME " tls" + " module was compiled with: installed \"%s\" (0x%08lx)," + " compiled \"%s\" (0x%08lx).\n" + " Please make sure a compatible version is used" + " (tls_force_run in kamailio.cfg will override this check)\n", + ssl_version_txt, ssl_version, + OPENSSL_VERSION_TEXT, (long)OPENSSL_VERSION_NUMBER); + if (cfg_get(tls, tls_cfg, force_run)) + LM_WARN("tls_force_run turned on, ignoring " + " openssl version mismatch\n"); + else + return -1; /* safer to exit */ + } + + /* check kerberos support using compile flags only for version < 1.1.0 */ + + /* set free memory threshold for openssl bug #1491 workaround */ + low_mem_threshold1 = cfg_get(tls, tls_cfg, low_mem_threshold1); + low_mem_threshold2 = cfg_get(tls, tls_cfg, low_mem_threshold2); + if (low_mem_threshold1<0){ + /* default */ + low_mem_threshold1=512*1024*get_max_procs(); + }else + low_mem_threshold1*=1024; /* KB */ + if (low_mem_threshold2<0){ + /* default */ + low_mem_threshold2=256*1024*get_max_procs(); + }else + low_mem_threshold2*=1024; /* KB */ + if ((low_mem_threshold1==0) || (low_mem_threshold2==0)) + LM_WARN("tls: openssl bug #1491 (crash/mem leaks on low memory)" + " workaround disabled\n"); + else + LM_WARN("openssl bug #1491 (crash/mem leaks on low memory)" + " workaround enabled (on low memory tls operations will fail" + " preemptively) with free memory thresholds %d and %d bytes\n", + low_mem_threshold1, low_mem_threshold2); + + if (shm_available()==(unsigned long)(-1)){ + LM_WARN(NAME " is compiled without MALLOC_STATS support:" + " the workaround for low mem. openssl bugs will _not_ " + "work\n"); + low_mem_threshold1=0; + low_mem_threshold2=0; + } + if ((low_mem_threshold1 != cfg_get(tls, tls_cfg, low_mem_threshold1)) + || (low_mem_threshold2 + != cfg_get(tls, tls_cfg, low_mem_threshold2))) { + /* ugly hack to set the initial values for the mem tresholds */ + if (cfg_register_ctx(&cfg_ctx, 0)) { + LM_ERR("failed to register cfg context\n"); + return -1; + } + tls_grp.s = "tls"; + tls_grp.len = strlen(tls_grp.s); + s.s = "low_mem_threshold1"; + s.len = strlen(s.s); + if (low_mem_threshold1 != cfg_get(tls, tls_cfg, low_mem_threshold1) && + cfg_set_now_int(cfg_ctx, &tls_grp, NULL /* group id */, &s, + low_mem_threshold1)) { + LM_ERR("failed to set tls.low_mem_threshold1 to %d\n", + low_mem_threshold1); + return -1; + } + s.s = "low_mem_threshold2"; + s.len = strlen(s.s); + if (low_mem_threshold2 != cfg_get(tls, tls_cfg, low_mem_threshold2) && + cfg_set_now_int(cfg_ctx, &tls_grp, NULL /* group id */, &s, + low_mem_threshold2)) { + LM_ERR("failed to set tls.low_mem_threshold1 to %d\n", + low_mem_threshold2); + return -1; + } + } + + init_ssl_methods(); + tls_mod_initialized = 1; + return 0; +} + + +/* + * Make sure that all server domains in the configuration have corresponding + * listening socket in SER + */ +int tls_check_sockets(tls_domains_cfg_t* cfg) +{ + tls_domain_t* d; + + if (!cfg) return 0; + + d = cfg->srv_list; + while(d) { + if (d->ip.len && !find_si(&d->ip, d->port, PROTO_TLS)) { + LM_ERR("%s: No listening socket found\n", tls_domain_str(d)); + return -1; + } + d = d->next; + } + return 0; +} + + +/* + * TLS cleanup when application exits + */ +void tls_h_mod_destroy_f(void) +{ + LM_DBG("tls module final tls destroy\n"); + if(tls_mod_preinitialized > 0) + ERR_free_strings(); + /* TODO: free all the ctx'es */ + tls_destroy_cfg(); + tls_ct_wq_destroy(); + /* explicit execution of libssl cleanup to avoid being executed again + * by atexit(), when shm is gone */ + LM_DBG("executing openssl v1.1+ cleanup\n"); + OPENSSL_cleanup(); +} diff --git a/src/modules/tls_wolfssl/tls_init.h b/src/modules/tls_wolfssl/tls_init.h new file mode 100644 index 00000000000..582bd5c2f76 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_init.h @@ -0,0 +1,85 @@ +/* + * TLS module + * + * Copyright (C) 2005,2006 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! + * \file + * \brief Kamailio TLS support :: OpenSSL initialization funtions + * \ingroup tls + * Module: \ref tls + */ + + +#ifndef _TLS_INIT_H +#define _TLS_INIT_H + +#include +#include +#include "../../core/ip_addr.h" +#include "tls_domain.h" + +/* openssl < 1. 0 */ +#ifndef OPENSSL_NO_KRB5 +/* enable workarround for openssl kerberos wrong malloc bug + * (kssl code uses libc malloc/free/calloc instead of OPENSSL_malloc & + * friends)*/ +#define TLS_KSSL_WORKARROUND +extern int openssl_kssl_malloc_bug; /* is openssl bug #1467 present ? */ +#endif + + +typedef struct sr_tls_methods_s { + const SSL_METHOD* TLSMethod; + int TLSMethodMin; + int TLSMethodMax; +} sr_tls_methods_t; +extern sr_tls_methods_t sr_tls_methods[]; + +/* + * just once, pre-initialize the tls subsystem + */ +int tls_pre_init(void); + +/** + * just once, prepare for init of all modules + */ +int tls_h_mod_pre_init_f(void); + +/* + * just once, initialize the tls subsystem after all mod inits + */ +int tls_h_mod_init_f(void); + + +/* + * just once before final cleanup + */ +void tls_h_mod_destroy_f(void); + + +/* + * for each socket + */ +int tls_h_init_si_f(struct socket_info *si); + +/* + * Make sure that all server domains in the configuration have corresponding + * listening socket in SER + */ +int tls_check_sockets(tls_domains_cfg_t* cfg); + +#endif /* _TLS_INIT_H */ diff --git a/src/modules/tls_wolfssl/tls_map.c b/src/modules/tls_wolfssl/tls_map.c new file mode 100644 index 00000000000..ad9846ab8a0 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_map.c @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2014 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include +#include + +#include "../../core/mem/mem.h" +#include "tls_map.h" + +struct map_node_t { + unsigned hash; + void *value; + map_node_t *next; + /* char key[]; */ + /* char value[]; */ +}; + + +static unsigned map_hash(const char *str) { + unsigned hash = 5381; + while (*str) { + hash = ((hash << 5) + hash) ^ *str++; + } + return hash; +} + + +static map_node_t *map_newnode(const char *key, void *value, int vsize) { + map_node_t *node; + int ksize = strlen(key) + 1; + int voffset = ksize + ((sizeof(void*) - ksize) % sizeof(void*)); + node = pkg_malloc(sizeof(*node) + voffset + vsize); + if (!node) return NULL; + memcpy(node + 1, key, ksize); + node->hash = map_hash(key); + node->value = ((char*) (node + 1)) + voffset; + memcpy(node->value, value, vsize); + return node; +} + + +static int map_bucketidx(map_base_t *m, unsigned hash) { + /* If the implementation is changed to allow a non-power-of-2 bucket count, + * the line below should be changed to use mod instead of AND */ + return hash & (m->nbuckets - 1); +} + + +static void map_addnode(map_base_t *m, map_node_t *node) { + int n = map_bucketidx(m, node->hash); + node->next = m->buckets[n]; + m->buckets[n] = node; +} + + +static int map_resize(map_base_t *m, int nbuckets) { + map_node_t *nodes, *node, *next; + map_node_t **buckets; + int i; + /* Chain all nodes together */ + nodes = NULL; + i = m->nbuckets; + while (i--) { + node = (m->buckets)[i]; + while (node) { + next = node->next; + node->next = nodes; + nodes = node; + node = next; + } + } + /* Reset buckets */ + buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets); + if (buckets != NULL) { + m->buckets = buckets; + m->nbuckets = nbuckets; + } + if (m->buckets) { + memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets); + /* Re-add nodes to buckets */ + node = nodes; + while (node) { + next = node->next; + map_addnode(m, node); + node = next; + } + } + /* Return error code if realloc() failed */ + return (buckets == NULL) ? -1 : 0; +} + + +static map_node_t **map_getref(map_base_t *m, const char *key) { + unsigned hash = map_hash(key); + map_node_t **next; + if (m->nbuckets > 0) { + next = &m->buckets[map_bucketidx(m, hash)]; + while (*next) { + if ((*next)->hash == hash && !strcmp((char*) (*next + 1), key)) { + return next; + } + next = &(*next)->next; + } + } + return NULL; +} + + +void map_deinit_(map_base_t *m) { + map_node_t *next, *node; + int i; + i = m->nbuckets; + while (i--) { + node = m->buckets[i]; + while (node) { + next = node->next; + pkg_free(node); + node = next; + } + } + pkg_free(m->buckets); +} + + +void *map_get_(map_base_t *m, const char *key) { + map_node_t **next = map_getref(m, key); + return next ? (*next)->value : NULL; +} + + +int map_set_(map_base_t *m, const char *key, void *value, int vsize) { + int n, err; + map_node_t **next, *node; + /* Find & replace existing node */ + next = map_getref(m, key); + if (next) { + memcpy((*next)->value, value, vsize); + return 0; + } + /* Add new node */ + node = map_newnode(key, value, vsize); + if (node == NULL) goto fail; + if (m->nnodes >= m->nbuckets) { + n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1; + err = map_resize(m, n); + if (err) goto fail; + } + map_addnode(m, node); + m->nnodes++; + return 0; + fail: + if (node) pkg_free(node); + return -1; +} + + +void map_remove_(map_base_t *m, const char *key) { + map_node_t *node; + map_node_t **next = map_getref(m, key); + if (next) { + node = *next; + *next = (*next)->next; + pkg_free(node); + m->nnodes--; + } +} + + +map_iter_t map_iter_(void) { + map_iter_t iter; + iter.bucketidx = -1; + iter.node = NULL; + return iter; +} + + +const char *map_next_(map_base_t *m, map_iter_t *iter) { + if (iter->node) { + iter->node = iter->node->next; + if (iter->node == NULL) goto nextBucket; + } else { + nextBucket: + do { + if (++iter->bucketidx >= m->nbuckets) { + return NULL; + } + iter->node = m->buckets[iter->bucketidx]; + } while (iter->node == NULL); + } + return (char*) (iter->node + 1); +} diff --git a/src/modules/tls_wolfssl/tls_map.h b/src/modules/tls_wolfssl/tls_map.h new file mode 100644 index 00000000000..682ec08a2fb --- /dev/null +++ b/src/modules/tls_wolfssl/tls_map.h @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2014 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef _TLS_MAP_H +#define _TLS_MAP_H + +#include + +#define MAP_VERSION "0.1.0" + +struct map_node_t; +typedef struct map_node_t map_node_t; + +typedef struct { + map_node_t **buckets; + unsigned nbuckets, nnodes; +} map_base_t; + +typedef struct { + unsigned bucketidx; + map_node_t *node; +} map_iter_t; + + +#define map_t(T)\ + struct { map_base_t base; T *ref; T tmp; } + + +#define map_init(m)\ + memset(m, 0, sizeof(*(m))) + + +#define map_deinit(m)\ + map_deinit_(&(m)->base) + + +#define map_get(m, key)\ + ( (m)->ref = map_get_(&(m)->base, key) ) + + +#define map_set(m, key, value)\ + ( (m)->tmp = (value),\ + map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) ) + + +#define map_remove(m, key)\ + map_remove_(&(m)->base, key) + + +#define map_iter(m)\ + map_iter_() + + +#define map_next(m, iter)\ + map_next_(&(m)->base, iter) + + +void map_deinit_(map_base_t *m); +void *map_get_(map_base_t *m, const char *key); +int map_set_(map_base_t *m, const char *key, void *value, int vsize); +void map_remove_(map_base_t *m, const char *key); +map_iter_t map_iter_(void); +const char *map_next_(map_base_t *m, map_iter_t *iter); + + +typedef map_t(void*) map_void_t; +typedef map_t(char*) map_str_t; +typedef map_t(int) map_int_t; +typedef map_t(char) map_char_t; +typedef map_t(float) map_float_t; +typedef map_t(double) map_double_t; + +#endif /* _TLS_MAP_H */ diff --git a/src/modules/tls_wolfssl/tls_mod.c b/src/modules/tls_wolfssl/tls_mod.c new file mode 100644 index 00000000000..47a466692c2 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_mod.c @@ -0,0 +1,602 @@ +/* + * TLS module + * + * Copyright (C) 2007 iptelorg GmbH + * Copyright (C) Motorola Solutions, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** Kamailio TLS support :: Module interface. + * @file + * @ingroup tls + * Module: @ref tls + */ + +#include +#include +#include +#include +#include "../../core/locking.h" + +#define PARSE_DEF_H /* WOLFFIX class PARSE_ERROR enum */ +#include "../../core/sr_module.h" +#include "../../core/ip_addr.h" +#include "../../core/trim.h" +#include "../../core/globals.h" +#include "../../core/timer_ticks.h" +#include "../../core/timer.h" /* ticks_t */ +#include "../../core/tls_hooks.h" +#include "../../core/ut.h" +#include "../../core/shm_init.h" +#include "../../core/rpc_lookup.h" +#include "../../core/cfg/cfg.h" +#include "../../core/dprint.h" +#include "../../core/mod_fix.h" +#include "../../core/kemi.h" +#include "tls_init.h" +#include "tls_server.h" +#include "tls_domain.h" +#include "tls_select.h" +#include "tls_config.h" +#include "tls_rpc.h" +#include "tls_util.h" +#include "tls_mod.h" +#include "tls_cfg.h" +#include "tls_rand.h" + +#ifndef TLS_HOOKS + #error "TLS_HOOKS must be defined, or the tls module won't work" +#endif +#ifdef CORE_TLS + #error "conflict: CORE_TLS must _not_ be defined" +#endif + +/* + * FIXME: + * - How do we ask for secret key password ? Mod_init is called after + * daemonize and thus has no console access + * - forward_tls and t_relay_to_tls should be here + * add tls_log + * - Currently it is not possible to reset certificate in a domain, + * for example if you specify client certificate in the default client + * domain then there is no way to define another client domain which would + * have no client certificate configured + */ + + +/* + * Module management function prototypes + */ +static int mod_init(void); +static int mod_child(int rank); +static void destroy(void); + +static int w_is_peer_verified(struct sip_msg* msg, char* p1, char* p2); +static int w_tls_set_connect_server_id(sip_msg_t* msg, char* psrvid, char* p2); + +int ksr_rand_engine_param(modparam_t type, void* val); + +MODULE_VERSION + + +extern str sr_tls_event_callback; +str sr_tls_xavp_cfg = {0, 0}; +/* + * Default settings when modparams are used + */ +static tls_domain_t mod_params = { + TLS_DOMAIN_DEF | TLS_DOMAIN_SRV, /* Domain Type */ + {}, /* IP address */ + 0, /* Port number */ + 0, /* SSL ctx */ + STR_STATIC_INIT(TLS_CERT_FILE), /* Certificate file */ + STR_STATIC_INIT(TLS_PKEY_FILE), /* Private key file */ + 0, /* Verify certificate */ + 9, /* Verify depth */ + STR_STATIC_INIT(TLS_CA_FILE), /* CA file */ + STR_STATIC_INIT(TLS_CA_PATH), /* CA path */ + 0, /* Require certificate */ + {0, 0}, /* Cipher list */ + TLS_USE_TLSv1_PLUS, /* TLS method */ + STR_STATIC_INIT(TLS_CRL_FILE), /* Certificate revocation list */ + {0, 0}, /* Server name (SNI) */ + 0, /* Server name (SNI) mode */ + {0, 0}, /* Server id */ + TLS_VERIFY_CLIENT_OFF, /* Verify client */ + 0 /* next */ +}; + + +/* + * Default settings for server domains when using external config file + */ +tls_domain_t srv_defaults = { + TLS_DOMAIN_DEF | TLS_DOMAIN_SRV, /* Domain Type */ + {}, /* IP address */ + 0, /* Port number */ + 0, /* SSL ctx */ + STR_STATIC_INIT(TLS_CERT_FILE), /* Certificate file */ + STR_STATIC_INIT(TLS_PKEY_FILE), /* Private key file */ + 0, /* Verify certificate */ + 9, /* Verify depth */ + STR_STATIC_INIT(TLS_CA_FILE), /* CA file */ + STR_STATIC_INIT(TLS_CA_PATH), /* CA path */ + 0, /* Require certificate */ + {0, 0}, /* Cipher list */ + TLS_USE_TLSv1_PLUS, /* TLS method */ + STR_STATIC_INIT(TLS_CRL_FILE), /* Certificate revocation list */ + {0, 0}, /* Server name (SNI) */ + 0, /* Server name (SNI) mode */ + {0, 0}, /* Server id */ + TLS_VERIFY_CLIENT_OFF, /* Verify client */ + 0 /* next */ +}; + + +/* + * Default settings for client domains when using external config file + */ +tls_domain_t cli_defaults = { + TLS_DOMAIN_DEF | TLS_DOMAIN_CLI, /* Domain Type */ + {}, /* IP address */ + 0, /* Port number */ + 0, /* SSL ctx */ + {0, 0}, /* Certificate file */ + {0, 0}, /* Private key file */ + 0, /* Verify certificate */ + 9, /* Verify depth */ + STR_STATIC_INIT(TLS_CA_FILE), /* CA file */ + STR_STATIC_INIT(TLS_CA_PATH), /* CA path */ + 0, /* Require certificate */ + {0, 0}, /* Cipher list */ + TLS_USE_TLSv1_PLUS, /* TLS method */ + {0, 0}, /* Certificate revocation list */ + {0, 0}, /* Server name (SNI) */ + 0, /* Server name (SNI) mode */ + {0, 0}, /* Server id */ + TLS_VERIFY_CLIENT_OFF, /* Verify client */ + 0 /* next */ +}; + + + +/* Current TLS configuration */ +tls_domains_cfg_t** tls_domains_cfg = NULL; + +/* List lock, used by garbage collector */ +gen_lock_t* tls_domains_cfg_lock = NULL; + + +int sr_tls_renegotiation = 0; + +/* + * Exported functions + */ +static cmd_export_t cmds[] = { + {"is_peer_verified", (cmd_function)w_is_peer_verified, 0, 0, 0, + REQUEST_ROUTE}, + {"tls_set_connect_server_id", (cmd_function)w_tls_set_connect_server_id, + 1, fixup_spve_null, fixup_free_spve_null, ANY_ROUTE}, + {0,0,0,0,0,0} +}; + + +/* + * Exported parameters + */ +static param_export_t params[] = { + {"tls_method", PARAM_STR, &default_tls_cfg.method }, + {"server_name", PARAM_STR, &default_tls_cfg.server_name }, + {"verify_certificate", PARAM_INT, &default_tls_cfg.verify_cert }, + {"verify_depth", PARAM_INT, &default_tls_cfg.verify_depth }, + {"require_certificate", PARAM_INT, &default_tls_cfg.require_cert }, + {"verify_client", PARAM_STR, &default_tls_cfg.verify_client}, + {"private_key", PARAM_STR, &default_tls_cfg.private_key }, + {"ca_list", PARAM_STR, &default_tls_cfg.ca_list }, + {"ca_path", PARAM_STR, &default_tls_cfg.ca_path }, + {"certificate", PARAM_STR, &default_tls_cfg.certificate }, + {"crl", PARAM_STR, &default_tls_cfg.crl }, + {"cipher_list", PARAM_STR, &default_tls_cfg.cipher_list }, + {"connection_timeout", PARAM_INT, &default_tls_cfg.con_lifetime }, + {"tls_log", PARAM_INT, &default_tls_cfg.log }, + {"tls_debug", PARAM_INT, &default_tls_cfg.debug }, + {"session_cache", PARAM_INT, &default_tls_cfg.session_cache}, + {"session_id", PARAM_STR, &default_tls_cfg.session_id }, + {"config", PARAM_STR, &default_tls_cfg.config_file }, + {"tls_disable_compression", PARAM_INT, + &default_tls_cfg.disable_compression}, + {"ssl_release_buffers", PARAM_INT, &default_tls_cfg.ssl_release_buffers}, + {"ssl_freelist_max_len", PARAM_INT, &default_tls_cfg.ssl_freelist_max}, + {"ssl_max_send_fragment", PARAM_INT, + &default_tls_cfg.ssl_max_send_fragment}, + {"ssl_read_ahead", PARAM_INT, &default_tls_cfg.ssl_read_ahead}, + {"send_close_notify", PARAM_INT, &default_tls_cfg.send_close_notify}, + {"con_ct_wq_max", PARAM_INT, &default_tls_cfg.con_ct_wq_max}, + {"ct_wq_max", PARAM_INT, &default_tls_cfg.ct_wq_max}, + {"ct_wq_blk_size", PARAM_INT, &default_tls_cfg.ct_wq_blk_size}, + {"tls_force_run", PARAM_INT, &default_tls_cfg.force_run}, + {"low_mem_threshold1", PARAM_INT, &default_tls_cfg.low_mem_threshold1}, + {"low_mem_threshold2", PARAM_INT, &default_tls_cfg.low_mem_threshold2}, + {"renegotiation", PARAM_INT, &sr_tls_renegotiation}, + {"xavp_cfg", PARAM_STR, &sr_tls_xavp_cfg}, + {"event_callback", PARAM_STR, &sr_tls_event_callback}, + {"rand_engine", PARAM_STR|USE_FUNC_PARAM, (void*)ksr_rand_engine_param}, + + {0, 0, 0} +}; + +#ifndef MOD_NAME +#define MOD_NAME "tls_wolfssl" +#endif + +/* + * Module interface + */ +struct module_exports exports = { + MOD_NAME, /* module name */ + DEFAULT_DLFLAGS, /* dlopen flags */ + cmds, /* exported functions */ + params, /* exported parameters */ + 0, /* exported rpc command */ + tls_pv, /* exported pseudo-variables */ + 0, /* response handling function */ + mod_init, /* module init function */ + mod_child, /* child initi function */ + destroy /* destroy function */ +}; + + + +static struct tls_hooks tls_h = { + tls_h_read_f, + tls_h_encode_f, + tls_h_tcpconn_init_f, + tls_h_tcpconn_clean_f, + tls_h_tcpconn_close_f, + tls_h_init_si_f, + tls_h_mod_init_f, + tls_h_mod_destroy_f, + tls_h_mod_pre_init_f, +}; + + + +#if 0 +/* + * Create TLS configuration from modparams + */ +static tls_domains_cfg_t* tls_use_modparams(void) +{ + tls_domains_cfg_t* ret; + + ret = tls_new_cfg(); + if (!ret) return; + + +} +#endif + + +static int mod_init(void) +{ + int method; + int verify_client; + + if (tls_disable){ + LM_WARN("tls support is disabled " + "(set enable_tls=1 in the config to enable it)\n"); + return 0; + } + if (fix_tls_cfg(&default_tls_cfg) < 0 ) { + LM_ERR("initial tls configuration fixup failed\n"); + return -1; + } + /* declare configuration */ + if (cfg_declare("tls", tls_cfg_def, &default_tls_cfg, + cfg_sizeof(tls), (void **)&tls_cfg)) { + LM_ERR("failed to register the configuration\n"); + return -1; + } + /* Convert tls_method parameter to integer */ + method = tls_parse_method(&cfg_get(tls, tls_cfg, method)); + if (method < 0) { + LM_ERR("Invalid tls_method parameter value\n"); + return -1; + } + /* fill mod_params */ + mod_params.method = method; + mod_params.verify_cert = cfg_get(tls, tls_cfg, verify_cert); + mod_params.verify_depth = cfg_get(tls, tls_cfg, verify_depth); + mod_params.require_cert = cfg_get(tls, tls_cfg, require_cert); + mod_params.pkey_file = cfg_get(tls, tls_cfg, private_key); + mod_params.ca_file = cfg_get(tls, tls_cfg, ca_list); + mod_params.crl_file = cfg_get(tls, tls_cfg, crl); + mod_params.cert_file = cfg_get(tls, tls_cfg, certificate); + mod_params.cipher_list = cfg_get(tls, tls_cfg, cipher_list); + mod_params.server_name = cfg_get(tls, tls_cfg, server_name); + /* Convert verify_client parameter to integer */ + verify_client = tls_parse_verify_client(&cfg_get(tls, tls_cfg, verify_client)); + if (verify_client < 0) { + LM_ERR("Invalid tls_method parameter value\n"); + return -1; + } + mod_params.verify_client = verify_client; + + tls_domains_cfg = + (tls_domains_cfg_t**)shm_malloc(sizeof(tls_domains_cfg_t*)); + if (!tls_domains_cfg) { + LM_ERR("Not enough shared memory left\n"); + goto error; + } + *tls_domains_cfg = NULL; + + register_select_table(tls_sel); + /* register the rpc interface */ + if (rpc_register_array(tls_rpc)!=0) { + LM_ERR("failed to register RPC commands\n"); + goto error; + } + + /* if (init_tls() < 0) return -1; */ + + tls_domains_cfg_lock = lock_alloc(); + if (tls_domains_cfg_lock == 0) { + LM_ERR("Unable to create TLS configuration lock\n"); + goto error; + } + if (lock_init(tls_domains_cfg_lock) == 0) { + lock_dealloc(tls_domains_cfg_lock); + ERR("Unable to initialize TLS configuration lock\n"); + goto error; + } + if (tls_ct_wq_init() < 0) { + LM_ERR("Unable to initialize TLS buffering\n"); + goto error; + } + if (cfg_get(tls, tls_cfg, config_file).s) { + *tls_domains_cfg = + tls_load_config(&cfg_get(tls, tls_cfg, config_file)); + if (!(*tls_domains_cfg)) goto error; + } else { + *tls_domains_cfg = tls_new_cfg(); + if (!(*tls_domains_cfg)) goto error; + } + + if (tls_check_sockets(*tls_domains_cfg) < 0) + goto error; + + LM_INFO("use OpenSSL version: %08x\n", (uint32_t)(OPENSSL_VERSION_NUMBER)); +#ifndef OPENSSL_NO_ECDH + LM_INFO("With ECDH-Support!\n"); +#endif +#ifndef OPENSSL_NO_DH + LM_INFO("With Diffie Hellman\n"); +#endif + if(sr_tls_event_callback.s==NULL || sr_tls_event_callback.len<=0) { + tls_lookup_event_routes(); + } + return 0; +error: + tls_h_mod_destroy_f(); + return -1; +} + + +static int mod_child(int rank) +{ + if (tls_disable || (tls_domains_cfg==0)) + return 0; + + /* fix tls config only from the main proc/PROC_INIT., when we know + * the exact process number and before any other process starts*/ + if (rank == PROC_INIT){ + if (cfg_get(tls, tls_cfg, config_file).s){ + if (tls_fix_domains_cfg(*tls_domains_cfg, + &srv_defaults, &cli_defaults) < 0) + return -1; + }else{ + if (tls_fix_domains_cfg(*tls_domains_cfg, + &mod_params, &mod_params) < 0) + return -1; + } + } + return 0; +} + + +static void destroy(void) +{ + /* tls is destroyed via the registered destroy_tls_h callback + * => nothing to do here */ +} + + +int ksr_rand_engine_param(modparam_t type, void* val) +{ + str *reng; + + if(val==NULL) { + return -1; + } + reng = (str*)val; + LM_DBG("random engine: %.*s\n", reng->len, reng->s); + if(reng->len == 5 && strncasecmp(reng->s, "krand", 5) == 0) { + LM_DBG("setting krand random engine\n"); + wolfSSL_RAND_set_rand_method(RAND_ksr_krand_method()); + } else if(reng->len == 8 && strncasecmp(reng->s, "fastrand", 8) == 0) { + LM_DBG("setting fastrand random engine\n"); + wolfSSL_RAND_set_rand_method(RAND_ksr_fastrand_method()); + } else if (reng->len == 10 && strncasecmp(reng->s, "cryptorand", 10) == 0) { + LM_DBG("setting cryptorand random engine\n"); + wolfSSL_RAND_set_rand_method(RAND_ksr_cryptorand_method()); + } + + /* WOLFFIX else if (reng->len == 8 && strncasecmp(reng->s, "kxlibssl", 8) == 0) { + LM_DBG("setting kxlibssl random engine\n"); + wolfSSL_RAND_set_rand_method(RAND_ksr_kxlibssl_method()); + } */ + + return 0; +} + +static int ki_is_peer_verified(sip_msg_t* msg) +{ + struct tcp_connection *c; + SSL *ssl; + long ssl_verify; + X509 *x509_cert; + + LM_DBG("started...\n"); + if (msg->rcv.proto != PROTO_TLS) { + LM_ERR("proto != TLS --> peer can't be verified, return -1\n"); + return -1; + } + + LM_DBG("trying to find TCP connection of received message...\n"); + + c = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, + cfg_get(tls, tls_cfg, con_lifetime)); + if (!c) { + LM_ERR("connection no longer exists\n"); + return -1; + } + + if(c->type != PROTO_TLS) { + LM_ERR("Connection found but is not TLS\n"); + tcpconn_put(c); + return -1; + } + + if (!c->extra_data) { + LM_ERR("no extra_data specified in TLS/TCP connection found." + " This should not happen... return -1\n"); + tcpconn_put(c); + return -1; + } + + ssl = ((struct tls_extra_data*)c->extra_data)->ssl; + + ssl_verify = wolfSSL_get_verify_result(ssl); + if ( ssl_verify != X509_V_OK ) { + LM_WARN("verification of presented certificate failed... return -1\n"); + tcpconn_put(c); + return -1; + } + + /* now, we have only valid peer certificates or peers without certificates. + * Thus we have to check for the existence of a peer certificate + */ + x509_cert = wolfSSL_get_peer_certificate(ssl); + if ( x509_cert == NULL ) { + LM_INFO("tlsops:is_peer_verified: WARNING: peer did not present " + "a certificate. Thus it could not be verified... return -1\n"); + tcpconn_put(c); + return -1; + } + + X509_free(x509_cert); + + tcpconn_put(c); + + LM_DBG("tlsops:is_peer_verified: peer is successfully verified" + "...done\n"); + return 1; +} + +static int w_is_peer_verified(struct sip_msg* msg, char* foo, char* foo2) +{ + return ki_is_peer_verified(msg); +} + +static int ki_tls_set_connect_server_id(sip_msg_t* msg, str* srvid) +{ + if(ksr_tls_set_connect_server_id(srvid)<0) { + return -1; + } + + return 1; +} + +static int w_tls_set_connect_server_id(sip_msg_t* msg, char* psrvid, char* p2) +{ + str ssrvid = STR_NULL; + + if(fixup_get_svalue(msg, (gparam_t*)psrvid, &ssrvid)<0) { + LM_ERR("failed to get server id parameter\n"); + return -1; + } + + return ki_tls_set_connect_server_id(msg, &ssrvid); +} + +/** + * + */ +static sr_kemi_xval_t* ki_tls_cget(sip_msg_t *msg, str *aname) +{ + return ki_tls_cget_attr(msg, aname); +} + +/** + * + */ +/* clang-format off */ +static sr_kemi_t sr_kemi_tls_exports[] = { + { str_init("tls"), str_init("is_peer_verified"), + SR_KEMIP_INT, ki_is_peer_verified, + { SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + { str_init("tls"), str_init("set_connect_server_id"), + SR_KEMIP_INT, ki_tls_set_connect_server_id, + { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + { str_init("tls"), str_init("cget"), + SR_KEMIP_XVAL, ki_tls_cget, + { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, + SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } + }, + + { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } } +}; +/* clang-format on */ + +/** + * + */ +int mod_register(char *path, int *dlflags, void *p1, void *p2) +{ + if (tls_disable) { + LM_WARN("tls support is disabled " + "(set enable_tls=1 in the config to enable it)\n"); + return 0; + } + + /* shm is used, be sure it is initialized */ + if(!shm_initialized() && init_shm()<0) + return -1; + + if(tls_pre_init()<0) + return -1; + + register_tls_hooks(&tls_h); + + LM_DBG("setting cryptorand random engine\n"); + wolfSSL_RAND_set_rand_method(RAND_ksr_cryptorand_method()); + + sr_kemi_modules_add(sr_kemi_tls_exports); + + return 0; +} diff --git a/src/modules/tls_wolfssl/tls_mod.h b/src/modules/tls_wolfssl/tls_mod.h new file mode 100644 index 00000000000..ad68009694e --- /dev/null +++ b/src/modules/tls_wolfssl/tls_mod.h @@ -0,0 +1,45 @@ +/* + * TLS module - module interface + * + * Copyright (C) 2005 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/** Kamailio TLS support :: module interface. + * @file + * @ingroup tls + * Module: @ref tls + */ + + + +#ifndef _TLS_MOD_H +#define _TLS_MOD_H + +#include "../../core/str.h" +#include "../../core/locking.h" +#include "tls_domain.h" + + +/* Current TLS configuration */ +extern tls_domains_cfg_t** tls_domains_cfg; +extern gen_lock_t* tls_domains_cfg_lock; + +extern tls_domain_t cli_defaults; +extern tls_domain_t srv_defaults; + +extern str tls_domains_cfg_file; + +extern int sr_tls_renegotiation; + +#endif /* _TLS_MOD_H */ diff --git a/src/modules/tls_wolfssl/tls_rand.c b/src/modules/tls_wolfssl/tls_rand.c new file mode 100644 index 00000000000..fbb9a83fd5a --- /dev/null +++ b/src/modules/tls_wolfssl/tls_rand.c @@ -0,0 +1,187 @@ +/* + * TLS module + * + * Copyright (C) 2019 Asipto GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * OpenSSL docs: + * https://www.openssl.org/docs/man1.1.1/man7/RAND.html + * https://www.openssl.org/docs/man1.1.1/man3/RAND_set_rand_method.html + */ + +#include +#include +#include +#include + +#include "tls_rand.h" + + +#include "../../core/dprint.h" +#include "../../core/locking.h" +#include "../../core/mem/shm.h" +#include "../../core/rand/kam_rand.h" +#include "../../core/rand/fastrand.h" +#include "../../core/rand/fortuna/random.h" + +/* + * Implementation for tests with system library PNRG, + * do not use this in production. + */ +static int ksr_krand_bytes(unsigned char *outdata, int size) +{ + int r; + + if (size < 0) { + return 0; + } else if (size == 0) { + return 1; + } + + while(size >= sizeof(int)) { + r = kam_rand(); + memcpy(outdata, &r, sizeof(int)); + size -= sizeof(int); + outdata += sizeof(int); + } + if(size>0) { + r = kam_rand(); + memcpy(outdata, &r, size); + } + return 1; +} + +static int ksr_krand_pseudorand(unsigned char *outdata, int size) +{ + return ksr_krand_bytes(outdata, size); +} + +static int ksr_krand_status(void) +{ + return 1; +} + +const WOLFSSL_RAND_METHOD _ksr_krand_method = { + NULL, + ksr_krand_bytes, + NULL, + NULL, + ksr_krand_pseudorand, + ksr_krand_status +}; + +const WOLFSSL_RAND_METHOD *RAND_ksr_krand_method(void) +{ + return &_ksr_krand_method; +} + +/* + * Implementation for tests with fastrand implementation, + * better as system library but still not secure enough. + * Do not use this in production.y + */ +static int ksr_fastrand_bytes(unsigned char *outdata, int size) +{ + int r; + + if (size < 0) { + return 0; + } else if (size == 0) { + return 1; + } + + while(size >= sizeof(int)) { + r = fastrand(); + memcpy(outdata, &r, sizeof(int)); + size -= sizeof(int); + outdata += sizeof(int); + } + if(size>0) { + r = fastrand(); + memcpy(outdata, &r, size); + } + return 1; +} + +static int ksr_fastrand_pseudorand(unsigned char *outdata, int size) +{ + return ksr_fastrand_bytes(outdata, size); +} + +static int ksr_fastrand_status(void) +{ + return 1; +} + +const WOLFSSL_RAND_METHOD _ksr_fastrand_method = { + NULL, + ksr_fastrand_bytes, + NULL, + NULL, + ksr_fastrand_pseudorand, + ksr_fastrand_status +}; + +const WOLFSSL_RAND_METHOD *RAND_ksr_fastrand_method(void) +{ + return &_ksr_fastrand_method; +} + +/* + * Implementation with Fortuna cryptographic PRNG. + * We are not strictly implementing the OpenSSL API here - we will + * not return an error if the PRNG has not been seeded with enough + * randomness to ensure an unpredictable byte sequence. + */ +static int ksr_cryptorand_bytes(unsigned char *outdata, int size) +{ + if (size < 0) { + return 0; + } else if (size == 0) { + return 1; + } + + sr_get_pseudo_random_bytes(outdata, size); + return 1; +} + +static int ksr_cryptorand_status(void) +{ + return 1; +} + +/* + * We don't have a dedicated function for pseudo-random + * bytes, just use the secure version as well for it. + */ +const WOLFSSL_RAND_METHOD _ksr_cryptorand_method = { + NULL, + ksr_cryptorand_bytes, + NULL, + NULL, + ksr_cryptorand_bytes, + ksr_cryptorand_status +}; + +const WOLFSSL_RAND_METHOD *RAND_ksr_cryptorand_method(void) +{ + return &_ksr_cryptorand_method; +} + +/** + * PRNG by using default libssl random engine protected by lock + */ +// WOLFFIX no libssl rand method diff --git a/src/modules/tls_wolfssl/tls_rand.h b/src/modules/tls_wolfssl/tls_rand.h new file mode 100644 index 00000000000..b9d97f4b494 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_rand.h @@ -0,0 +1,30 @@ +/* + * TLS module + * + * Copyright (C) 2019 Asipto GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef _TLS_RAND_H_ +#define _TLS_RAND_H_ +#include +#include + +const WOLFSSL_RAND_METHOD *RAND_ksr_krand_method(void); +const WOLFSSL_RAND_METHOD *RAND_ksr_fastrand_method(void); +const WOLFSSL_RAND_METHOD *RAND_ksr_cryptorand_method(void); +// WOLFFIX const WOLFSSL_RAND_METHOD *RAND_ksr_kxlibssl_method(void); + +#endif diff --git a/src/modules/tls_wolfssl/tls_rpc.c b/src/modules/tls_wolfssl/tls_rpc.c new file mode 100644 index 00000000000..a4a1056f18f --- /dev/null +++ b/src/modules/tls_wolfssl/tls_rpc.c @@ -0,0 +1,263 @@ +/* + * TLS module - management interface + * + * Copyright (C) 2005 iptelorg GmbH + * Copyright (C) 2013 Motorola Solutions, Inc. + * + * This file is part of Kamailio, a free SIP server. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/** tls module management interface (rpc). + * @file tls_rpc.c + * @ingroup tls + * Module: @ref tls + */ + + +#include "../../core/rpc.h" +#include "../../core/tcp_conn.h" +#include "../../core/tcp_info.h" +#include "../../core/timer.h" +#include "../../core/cfg/cfg.h" +#include "../../core/dprint.h" +#include "tls_init.h" +#include "tls_mod.h" +#include "tls_domain.h" +#include "tls_config.h" +#include "tls_util.h" +#include "tls_server.h" +#include "tls_ct_wrq.h" +#include "tls_rpc.h" +#include "tls_cfg.h" + +static const char* tls_reload_doc[2] = { + "Reload TLS configuration file", + 0 +}; + +static void tls_reload(rpc_t* rpc, void* ctx) +{ + tls_domains_cfg_t* cfg; + str tls_domains_cfg_file; + + tls_domains_cfg_file = cfg_get(tls, tls_cfg, config_file); + if (!tls_domains_cfg_file.s) { + rpc->fault(ctx, 500, "No TLS configuration file configured"); + return; + } + + /* Try to delete old configurations first */ + collect_garbage(); + + cfg = tls_load_config(&tls_domains_cfg_file); + + if (!cfg) { + rpc->fault(ctx, 500, "Error while loading TLS configuration file" + " (consult server log)"); + return; + } + + if (tls_fix_domains_cfg(cfg, &srv_defaults, &cli_defaults) < 0) { + rpc->fault(ctx, 500, "Error while fixing TLS configuration" + " (consult server log)"); + goto error; + } + if (tls_check_sockets(cfg) < 0) { + rpc->fault(ctx, 500, "No server listening socket found for one of" + " TLS domains (consult server log)"); + goto error; + } + + DBG("TLS configuration successfully loaded"); + + lock_get(tls_domains_cfg_lock); + + cfg->next = (*tls_domains_cfg); + *tls_domains_cfg = cfg; + + lock_release(tls_domains_cfg_lock); + rpc->rpl_printf(ctx, "Ok. TLS configuration reloaded."); + return; + + error: + tls_free_cfg(cfg); + +} + + +static const char* tls_list_doc[2] = { + "List currently open TLS connections", + 0 +}; + +extern gen_lock_t* tcpconn_lock; +extern struct tcp_connection** tcpconn_id_hash; + +static void tls_list(rpc_t* rpc, void* c) +{ + char buf[128]; + char src_ip[IP_ADDR_MAX_STR_SIZE]; + char dst_ip[IP_ADDR_MAX_STR_SIZE]; + void* handle; + char* tls_info; + char* state; + struct tls_extra_data* tls_d; + struct tcp_connection* con; + int i, len, timeout; + + TCPCONN_LOCK; + for(i = 0; i < TCP_ID_HASH_SIZE; i++) { + for (con = tcpconn_id_hash[i]; con; con = con->id_next) { + if (con->rcv.proto != PROTO_TLS) continue; + tls_d = con->extra_data; + rpc->add(c, "{", &handle); + /* tcp data */ + if ((len = ip_addr2sbuf(&con->rcv.src_ip, src_ip, sizeof(src_ip))) + == 0) + BUG("failed to convert source ip"); + src_ip[len] = 0; + if ((len = ip_addr2sbuf(&con->rcv.dst_ip, dst_ip, sizeof(dst_ip))) + == 0) + BUG("failed to convert destination ip"); + dst_ip[len] = 0; + timeout = TICKS_TO_S(con->timeout - get_ticks_raw()); + rpc->struct_add(handle, "ddsdsd", + "id", con->id, + "timeout", timeout, + "src_ip", src_ip, + "src_port", con->rcv.src_port, + "dst_ip", dst_ip, + "dst_port", con->rcv.dst_port); + if (tls_d) { + if(wolfSSL_get_current_cipher(tls_d->ssl)) { + tls_info = wolfSSL_CIPHER_description( + wolfSSL_get_current_cipher(tls_d->ssl), + buf, sizeof(buf)); + len = strlen(buf); + if (len && buf[len - 1] == '\n') buf[len - 1] = '\0'; + } else { + tls_info = "unknown"; + } + /* tls data */ + state = "unknown/error"; + lock_get(&con->write_lock); + switch(tls_d->state) { + case S_TLS_NONE: + state = "none/init"; + break; + case S_TLS_ACCEPTING: + state = "tls_accept"; + break; + case S_TLS_CONNECTING: + state = "tls_connect"; + break; + case S_TLS_ESTABLISHED: + state = "established"; + break; + } + rpc->struct_add(handle, "sddds", + "cipher", tls_info, + "ct_wq_size", tls_d->ct_wq? + tls_d->ct_wq->queued:0, + "enc_rd_buf", tls_d->enc_rd_buf? + tls_d->enc_rd_buf->size:0, + "flags", tls_d->flags, + "state", state + ); + lock_release(&con->write_lock); + } else { + rpc->struct_add(handle, "sddds", + "cipher", "unknown", + "ct_wq_size", 0, + "enc_rd_buf", 0, + "flags", 0, + "state", "pre-init" + ); + } + } + } + TCPCONN_UNLOCK; +} + + + +static const char* tls_info_doc[2] = { + "Returns internal tls related info.", + 0 }; + +static void tls_info(rpc_t* rpc, void* c) +{ + struct tcp_gen_info ti; + void* handle; + + tcp_get_info(&ti); + rpc->add(c, "{", &handle); + rpc->struct_add(handle, "ddd", + "max_connections", ti.tls_max_connections, + "opened_connections", ti.tls_connections_no, + "clear_text_write_queued_bytes", tls_ct_wq_total_bytes()); +} + + + +static const char* tls_options_doc[2] = { + "Dumps all the tls config options.", + 0 }; + +static void tls_options(rpc_t* rpc, void* c) +{ + void* handle; + rpc->add(c, "{", &handle); + rpc->struct_add(handle, "dSdddSSSSSdSSdddddddddddddd", + "force_run", cfg_get(tls, tls_cfg, force_run), + "method", &cfg_get(tls, tls_cfg, method), + "verify_certificate", cfg_get(tls, tls_cfg, verify_cert), + + "verify_depth", cfg_get(tls, tls_cfg, verify_depth), + "require_certificate", cfg_get(tls, tls_cfg, require_cert), + "verify_client", &cfg_get(tls, tls_cfg, verify_client), + "private_key", &cfg_get(tls, tls_cfg, private_key), + "ca_list", &cfg_get(tls, tls_cfg, ca_list), + "certificate", &cfg_get(tls, tls_cfg, certificate), + "cipher_list", &cfg_get(tls, tls_cfg, cipher_list), + "session_cache", cfg_get(tls, tls_cfg, session_cache), + "session_id", &cfg_get(tls, tls_cfg, session_id), + "config", &cfg_get(tls, tls_cfg, config_file), + "log", cfg_get(tls, tls_cfg, log), + "debug", cfg_get(tls, tls_cfg, debug), + "connection_timeout", TICKS_TO_S(cfg_get(tls, tls_cfg, con_lifetime)), + "disable_compression", cfg_get(tls, tls_cfg, disable_compression), + "ssl_release_buffers", cfg_get(tls, tls_cfg, ssl_release_buffers), + "ssl_freelist_max", cfg_get(tls, tls_cfg, ssl_freelist_max), + "ssl_max_send_fragment", cfg_get(tls, tls_cfg, ssl_max_send_fragment), + "ssl_read_ahead", cfg_get(tls, tls_cfg, ssl_read_ahead), + "send_close_notify", cfg_get(tls, tls_cfg, send_close_notify), + "low_mem_threshold1", cfg_get(tls, tls_cfg, low_mem_threshold1), + "low_mem_threshold2", cfg_get(tls, tls_cfg, low_mem_threshold2), + "ct_wq_max", cfg_get(tls, tls_cfg, ct_wq_max), + "con_ct_wq_max", cfg_get(tls, tls_cfg, con_ct_wq_max), + "ct_wq_blk_size", cfg_get(tls, tls_cfg, ct_wq_blk_size) + ); +} + + + + +rpc_export_t tls_rpc[] = { + {"tls.reload", tls_reload, tls_reload_doc, 0}, + {"tls.list", tls_list, tls_list_doc, RET_ARRAY}, + {"tls.info", tls_info, tls_info_doc, 0}, + {"tls.options",tls_options, tls_options_doc, 0}, + {0, 0, 0, 0} +}; diff --git a/src/modules/tls_wolfssl/tls_rpc.h b/src/modules/tls_wolfssl/tls_rpc.h new file mode 100644 index 00000000000..c248f77575a --- /dev/null +++ b/src/modules/tls_wolfssl/tls_rpc.h @@ -0,0 +1,33 @@ +/* + * TLS module + * + * Copyright (C) 2005 iptelorg GmbH + * + * This file is part of Kamailio, a free SIP server. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/** tls module management interface (rpc). + * @file tls_rpc.h + * @ingroup tls + * Module: @ref tls + */ + +#ifndef _TLS_RPC_H +#define _TLS_RPC_H + +#include "../../core/rpc.h" + +extern rpc_export_t tls_rpc[]; + +#endif /* _TLS_RPC_H */ diff --git a/src/modules/tls_wolfssl/tls_select.c b/src/modules/tls_wolfssl/tls_select.c new file mode 100644 index 00000000000..a7b2c27ea35 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_select.c @@ -0,0 +1,1707 @@ +/* + * TLS module - select interface + * + * Copyright (C) 2005 iptelorg GmbH + * Copyright (C) 2006 enum.at + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Exception: permission to copy, modify, propagate, and distribute a work + * formed by combining OpenSSL toolkit software and the code in this file, + * such as linking with software components and libraries released under + * OpenSSL project license. + */ + +/** Kamailio TLS support :: Select interface. + * @file + * @ingroup tls + * Module: @ref tls + */ + + +#include +#include +#include +#include "../../core/globals.h" +#include "../../core/tcp_server.h" +#include "../../core/tcp_conn.h" +#include "../../core/ut.h" +#include "../../core/pvapi.h" +#include "../../core/cfg/cfg.h" +#include "../../core/dprint.h" +#include "../../core/strutils.h" +#include "tls_server.h" +#include "tls_select.h" +#include "tls_mod.h" +#include "tls_init.h" /* features macros */ +#include "tls_cfg.h" + +// CERT_REVOKED is already defined by wolfSSL +enum { + CERT_LOCAL = 1, /* Select local certificate */ + CERT_PEER, /* Select peer certificate */ + CERT_SUBJECT, /* Select subject part of certificate */ + CERT_ISSUER, /* Select issuer part of certificate */ + CERT_VERIFIED, /* Test for verified certificate */ + CERT_REVOKED2, /* Test for revoked certificate */ + CERT_EXPIRED, /* Expiration certificate test */ + CERT_SELFSIGNED, /* self-signed certificate test */ + CERT_NOTBEFORE, /* Select validity end from certificate */ + CERT_NOTAFTER, /* Select validity start from certificate */ + CERT_RAW, /* Select raw PEM-encoded certificate */ + CERT_URLENCODED, /* Select urlencoded PEM-encoded certificate */ + COMP_CN, /* Common name */ + COMP_O, /* Organization name */ + COMP_OU, /* Organization unit */ + COMP_C, /* Country name */ + COMP_ST, /* State */ + COMP_L, /* Locality/town */ + COMP_HOST, /* hostname from subject/alternative */ + COMP_URI, /* URI from subject/alternative */ + COMP_E, /* Email address */ + COMP_IP, /* IP from subject/alternative */ + COMP_UID, /* UserID*/ + TLSEXT_SN /* Server name of the peer */ +}; + + +enum { + PV_CERT_LOCAL = 1<<0, /* Select local certificate */ + PV_CERT_PEER = 1<<1, /* Select peer certificate */ + PV_CERT_SUBJECT = 1<<2, /* Select subject part of certificate */ + PV_CERT_ISSUER = 1<<3, /* Select issuer part of certificate */ + + PV_CERT_VERIFIED = 1<<4, /* Test for verified certificate */ + PV_CERT_REVOKED = 1<<5, /* Test for revoked certificate */ + PV_CERT_EXPIRED = 1<<6, /* Expiration certificate test */ + PV_CERT_SELFSIGNED = 1<<7, /* self-signed certificate test */ + PV_CERT_NOTBEFORE = 1<<8, /* Select validity end from certificate */ + PV_CERT_NOTAFTER = 1<<9, /* Select validity start from certificate */ + PV_CERT_RAW = 1<<10, /* Select raw PEM-encoded certificate */ + PV_CERT_URLENCODED = 1<<11, /* Select urlencoded PEM-encoded certificate */ + + PV_COMP_CN = 1<<12, /* Common name */ + PV_COMP_O = 1<<13, /* Organization name */ + PV_COMP_OU = 1<<14, /* Organization unit */ + PV_COMP_C = 1<<15, /* Country name */ + PV_COMP_ST = 1<<16, /* State */ + PV_COMP_L = 1<<17, /* Locality/town */ + + PV_COMP_HOST = 1<<18, /* hostname from subject/alternative */ + PV_COMP_URI = 1<<19, /* URI from subject/alternative */ + PV_COMP_E = 1<<20, /* Email address */ + PV_COMP_IP = 1<<21, /* IP from subject/alternative */ + PV_COMP_UID = 1<<22, /* UserID*/ + + PV_TLSEXT_SNI = 1<<23, /* Peer's server name (TLS extension) */ +}; + + + +static struct tcp_connection* _tls_pv_con = 0; + + +void tls_set_pv_con(struct tcp_connection *c) +{ + _tls_pv_con = c; +} + +struct tcp_connection* get_cur_connection(struct sip_msg* msg) +{ + struct tcp_connection* c; + + if(_tls_pv_con != 0) + return _tls_pv_con; + + if (msg->rcv.proto != PROTO_TLS) { + ERR("Transport protocol is not TLS (bug in config)\n"); + return 0; + } + + c = tcpconn_get(msg->rcv.proto_reserved1, 0, 0, 0, + cfg_get(tls, tls_cfg, con_lifetime)); + if (c && c->type != PROTO_TLS) { + ERR("Connection found but is not TLS\n"); + tcpconn_put(c); + return 0; + } + return c; +} + + +static SSL* get_ssl(struct tcp_connection* c) +{ + struct tls_extra_data* extra; + + if (!c || !c->extra_data) { + ERR("Unable to extract SSL data from TLS connection\n"); + return 0; + } + extra = (struct tls_extra_data*)c->extra_data; + return extra->ssl; +} + + +static int get_cert(X509** cert, struct tcp_connection** c, struct sip_msg* msg, int my) +{ + SSL* ssl; + + *cert = 0; + *c = get_cur_connection(msg); + if (!(*c)) { + INFO("TLS connection not found\n"); + return -1; + } + ssl = get_ssl(*c); + if (!ssl) goto err; + *cert = my ? wolfSSL_get_certificate(ssl) : wolfSSL_get_peer_certificate(ssl); + if (!*cert) { + if (my) { + ERR("Unable to retrieve my TLS certificate from SSL structure\n"); + } else { + ERR("Unable to retrieve peer TLS certificate from SSL structure\n"); + } + goto err; + } + + return 0; + + err: + tcpconn_put(*c); + return -1; +} + + +static int get_cipher(str* res, sip_msg_t* msg) +{ + str cipher; + static char buf[1024]; + + struct tcp_connection* c; + SSL* ssl; + + c = get_cur_connection(msg); + if (!c) { + INFO("TLS connection not found in select_cipher\n"); + goto err; + } + ssl = get_ssl(c); + if (!ssl) goto err; + + cipher.s = (char*)wolfSSL_CIPHER_get_name(wolfSSL_get_current_cipher(ssl)); + cipher.len = cipher.s ? strlen(cipher.s) : 0; + if (cipher.len >= 1024) { + ERR("Cipher name too long\n"); + goto err; + } + if(cipher.s!=NULL && cipher.len>0) { + memcpy(buf, cipher.s, cipher.len); + } else { + buf[0] = '\0'; + } + res->s = buf; + res->len = cipher.len; + tcpconn_put(c); + return 0; + + err: + if (c) tcpconn_put(c); + return -1; +} + +static int sel_cipher(str* res, select_t* s, sip_msg_t* msg) +{ + return get_cipher(res, msg); +} + + +static int pv_cipher(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + if (get_cipher(&res->rs, msg) < 0) { + return pv_get_null(msg, param, res); + } + res->flags = PV_VAL_STR; + return 0; +} + + +static int get_bits(str* res, int* i, sip_msg_t* msg) +{ + str bits; + int b; + static char buf[1024]; + + struct tcp_connection* c; + SSL* ssl; + + c = get_cur_connection(msg); + if (!c) { + INFO("TLS connection not found in select_bits\n"); + goto err; + } + ssl = get_ssl(c); + if (!ssl) goto err; + + b = wolfSSL_CIPHER_get_bits(wolfSSL_get_current_cipher(ssl), 0); + bits.s = int2str(b, &bits.len); + if (bits.len >= 1024) { + ERR("Bits string too long\n"); + goto err; + } + memcpy(buf, bits.s, bits.len); + res->s = buf; + res->len = bits.len; + if (i) *i = b; + tcpconn_put(c); + return 0; + + err: + if (c) tcpconn_put(c); + return -1; +} + + +static int sel_bits(str* res, select_t* s, sip_msg_t* msg) +{ + return get_bits(res, NULL, msg); +} + +static int pv_bits(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + if (get_bits(&res->rs, &res->ri, msg) < 0) { + return pv_get_null(msg, param, res); + } + res->flags = PV_VAL_STR | PV_VAL_INT; + return 0; +} + + +static int get_version(str* res, sip_msg_t* msg) +{ + str version; + static char buf[1024]; + + struct tcp_connection* c; + SSL* ssl; + + c = get_cur_connection(msg); + if (!c) { + INFO("TLS connection not found in select_version\n"); + goto err; + } + ssl = get_ssl(c); + if (!ssl) goto err; + + version.s = (char*)wolfSSL_get_version(ssl); + version.len = version.s ? strlen(version.s) : 0; + if (version.len >= 1024) { + ERR("Version string too long\n"); + goto err; + } + if(version.s!=NULL && version.len>0) { + memcpy(buf, version.s, version.len); + } else { + buf[0] = '\0'; + } + res->s = buf; + res->len = version.len; + tcpconn_put(c); + return 0; + + err: + if (c) tcpconn_put(c); + return -1; +} + + +static int sel_version(str* res, select_t* s, sip_msg_t* msg) +{ + return get_version(res, msg); +} + + +static int pv_version(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + if (get_version(&res->rs, msg) < 0) { + return pv_get_null(msg, param, res); + } + res->flags = PV_VAL_STR; + return 0; +} + + + +static int get_desc(str* res, sip_msg_t* msg) +{ + static char buf[128]; + + struct tcp_connection* c; + SSL* ssl; + + c = get_cur_connection(msg); + if (!c) { + INFO("TLS connection not found in select_desc\n"); + goto err; + } + ssl = get_ssl(c); + if (!ssl) goto err; + + buf[0] = '\0'; + wolfSSL_CIPHER_description(SSL_get_current_cipher(ssl), buf, 128); + res->s = buf; + res->len = strlen(buf); + tcpconn_put(c); + return 0; + + err: + if (c) tcpconn_put(c); + return -1; +} + + +static int sel_desc(str* res, select_t* s, sip_msg_t* msg) +{ + return get_desc(res, msg); +} + +static int pv_desc(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + if (get_desc(&res->rs, msg) < 0) { + return pv_get_null(msg, param, res); + } + res->flags = PV_VAL_STR; + return 0; +} + + + +static int get_cert_version(str* res, int local, sip_msg_t* msg) +{ + static char buf[INT2STR_MAX_LEN]; + X509* cert; + struct tcp_connection* c; + char* version; + + if (get_cert(&cert, &c, msg, local) < 0) return -1; + version = int2str(X509_get_version(cert), &res->len); + memcpy(buf, version, res->len); + res->s = buf; + if (!local) X509_free(cert); + tcpconn_put(c); + return 0; +} + +static int sel_cert_version(str* res, select_t* s, sip_msg_t* msg) +{ + int local; + + switch(s->params[s->n - 2].v.i) { + case CERT_PEER: local = 0; break; + case CERT_LOCAL: local = 1; break; + default: + BUG("Bug in call to sel_cert_version\n"); + return -1; + } + + return get_cert_version(res, local, msg); +} + +static int pv_cert_version(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + int local; + + if (param->pvn.u.isname.name.n & PV_CERT_PEER) { + local = 0; + } else if (param->pvn.u.isname.name.n & PV_CERT_LOCAL) { + local = 1; + } else { + BUG("bug in call to pv_cert_version\n"); + return pv_get_null(msg, param, res); + } + + if (get_cert_version(&res->rs, local, msg) < 0) { + return pv_get_null(msg, param, res); + } + res->flags = PV_VAL_STR; + return 0; +} + + + +/* + * Check whether peer certificate exists and verify the result + * of certificate verification + */ +static int check_cert(str* res, int* ires, int local, int err, sip_msg_t* msg) +{ + static str succ = STR_STATIC_INIT("1"); + static str fail = STR_STATIC_INIT("0"); + + struct tcp_connection* c; + SSL* ssl; + X509* cert = 0; + + c = get_cur_connection(msg); + if (!c) return -1; + + ssl = get_ssl(c); + if (!ssl) goto error; + + if (local) { + DBG("Verification of local certificates not supported\n"); + goto error; + } else { + if ((cert = wolfSSL_get_peer_certificate(ssl)) && SSL_get_verify_result(ssl) == err) { + *res = succ; + if (ires) *ires = 1; + } else { + *res = fail; + if (ires) *ires = 0; + } + } + + if (cert) X509_free(cert); + tcpconn_put(c); + return 0; + + error: + if (c) tcpconn_put(c); + return -1; +} + + +static int sel_check_cert(str* res, select_t* s, sip_msg_t* msg) +{ + int local, err; + + switch(s->params[s->n - 2].v.i) { + case CERT_PEER: local = 0; break; + case CERT_LOCAL: local = 1; break; + default: + BUG("Bug in call to sel_cert_version\n"); + return -1; + } + + switch (s->params[s->n - 1].v.i) { + case CERT_VERIFIED: err = X509_V_OK; break; + case CERT_REVOKED2: err = X509_V_ERR_CERT_REVOKED; break; + case CERT_EXPIRED: err = X509_V_ERR_CERT_HAS_EXPIRED; break; + case CERT_SELFSIGNED: err = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; break; + default: + BUG("Unexpected parameter value \"%d\"\n", s->params[s->n - 1].v.i); + return -1; + } + + return check_cert(res, NULL, local, err, msg); +} + +static int pv_check_cert(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + int err; + + switch (param->pvn.u.isname.name.n) { + case PV_CERT_VERIFIED: err = X509_V_OK; break; + case PV_CERT_REVOKED: err = X509_V_ERR_CERT_REVOKED; break; + case PV_CERT_EXPIRED: err = X509_V_ERR_CERT_HAS_EXPIRED; break; + case PV_CERT_SELFSIGNED: err = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; break; + default: + BUG("unexpected parameter value \"%d\"\n", param->pvn.u.isname.name.n); + return pv_get_null(msg, param, res); + } + + + if (check_cert(&res->rs, &res->ri, 0, err, msg) < 0) { + return pv_get_null(msg, param, res); + } + + res->flags = PV_VAL_STR | PV_VAL_INT; + return 0; +} + + + + +static int get_validity(str* res, int local, int bound, sip_msg_t* msg) +{ +#define NOT_BEFORE 0 +#define NOT_AFTER 1 + static char buf[1024]; + X509* cert; + struct tcp_connection* c; + BUF_MEM* p; + BIO* mem = 0; + ASN1_TIME* date; + + if (get_cert(&cert, &c, msg, local) < 0) return -1; + + switch (bound) { + case NOT_BEFORE: date = X509_get_notBefore(cert); break; + case NOT_AFTER: date = X509_get_notAfter(cert); break; + default: + BUG("Unexpected parameter value \"%d\"\n", bound); + goto err; + } + + mem = BIO_new(BIO_s_mem()); + if (!mem) { + ERR("Error while creating memory BIO\n"); + goto err; + } + + if (!ASN1_TIME_print(mem, date)) { + ERR("Error while printing certificate date/time\n"); + goto err; + } + + BIO_get_mem_ptr(mem, &p); + if (p->length >= 1024) { + ERR("Date/time too long\n"); + goto err; + } + memcpy(buf, p->data, p->length); + res->s = buf; + res->len = p->length; + + BIO_free(mem); + if (!local) X509_free(cert); + tcpconn_put(c); + return 0; + err: + if (mem) BIO_free(mem); + if (!local) X509_free(cert); + tcpconn_put(c); + return -1; +} + +static int sel_validity(str* res, select_t* s, sip_msg_t* msg) +{ + int local, bound; + + switch(s->params[s->n - 2].v.i) { + case CERT_PEER: local = 0; break; + case CERT_LOCAL: local = 1; break; + default: + BUG("Could not determine certificate\n"); + return -1; + } + + switch (s->params[s->n - 1].v.i) { + case CERT_NOTBEFORE: bound = NOT_BEFORE; break; + case CERT_NOTAFTER: bound = NOT_AFTER; break; + default: + BUG("Unexpected parameter value \"%d\"\n", s->params[s->n - 1].v.i); + return -1; + } + + return get_validity(res, local, bound, msg); +} + + +static int pv_validity(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + int bound; + + switch (param->pvn.u.isname.name.n) { + case PV_CERT_NOTBEFORE: bound = NOT_BEFORE; break; + case PV_CERT_NOTAFTER: bound = NOT_AFTER; break; + default: + BUG("unexpected parameter value \"%d\"\n", param->pvn.u.isname.name.n); + return pv_get_null(msg, param, res); + } + + if (get_validity(&res->rs, 0, bound, msg) < 0) { + return pv_get_null(msg, param, res); + } + + res->flags = PV_VAL_STR; + return 0; +} + + +static int get_sn(str* res, int* ires, int local, sip_msg_t* msg) +{ + static char buf[INT2STR_MAX_LEN]; + X509* cert; + struct tcp_connection* c; + char* sn; + int num; + + if (get_cert(&cert, &c, msg, local) < 0) return -1; + + num = ASN1_INTEGER_get(X509_get_serialNumber(cert)); + sn = int2str(num, &res->len); + memcpy(buf, sn, res->len); + res->s = buf; + if (ires) *ires = num; + if (!local) X509_free(cert); + tcpconn_put(c); + return 0; +} + +static int sel_sn(str* res, select_t* s, sip_msg_t* msg) +{ + int local; + + switch(s->params[s->n - 2].v.i) { + case CERT_PEER: local = 0; break; + case CERT_LOCAL: local = 1; break; + default: + BUG("Could not determine certificate\n"); + return -1; + } + + return get_sn(res, NULL, local, msg); +} + + +static int pv_sn(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + int local; + + if (param->pvn.u.isname.name.n & PV_CERT_PEER) { + local = 0; + } else if (param->pvn.u.isname.name.n & PV_CERT_LOCAL) { + local = 1; + } else { + BUG("could not determine certificate\n"); + return pv_get_null(msg, param, res); + } + + if (get_sn(&res->rs, &res->ri, local, msg) < 0) { + return pv_get_null(msg, param, res); + } + + res->flags = PV_VAL_STR | PV_VAL_INT; + return 0; +} + + +static int cert_to_buf(X509 *cert, char **bufptr, size_t *len) +{ +#define MAX_CERT_SIZE 16384 + static char buf[MAX_CERT_SIZE]; + BIO *mem = NULL; + + mem = BIO_new(BIO_s_mem()); + if (!mem) { + ERR("Error while creating memory BIO\n"); + goto err; + } + + /* Write a certificate to a BIO */ + if (!PEM_write_bio_X509(mem, cert)) { + goto err; + } + + *len = BIO_pending(mem); + if (*len > MAX_CERT_SIZE) { + ERR("certificate is too long\n"); + goto err; + } + + if (BIO_read(mem, buf, *len) <= 0) { + ERR("problem reading data out of BIO"); + goto err; + } + + *bufptr = buf; + + BIO_free(mem); + return 0; +err: + + if (mem) BIO_free(mem); + return -1; +} + + +static int get_ssl_cert(str* res, int local, int urlencoded, sip_msg_t* msg) +{ + char *buf = NULL; + /* buf2 holds the urlencoded version of buf, which can be up to 3 times its size */ + static char buf2[MAX_CERT_SIZE*3+1]; + X509* cert; + struct tcp_connection* c; + size_t len; + str temp_str; + + if (get_cert(&cert, &c, msg, local) < 0) return -1; + + if (cert_to_buf(cert, &buf, &len) < 0) { + ERR("cert to buf failed\n"); + goto err; + } + + if (urlencoded) + { + temp_str.len = len; + temp_str.s = buf; + res->s = buf2; + res->len = MAX_CERT_SIZE*3+1; + + if (urlencode(&temp_str, res) < 0) { + ERR("Problem with urlencode()\n"); + goto err; + } + } + else + { + res->s = buf; + res->len = len; + } + + if (!local) X509_free(cert); + tcpconn_put(c); + return 0; + + err: + if (!local) X509_free(cert); + tcpconn_put(c); + return -1; +} + + +static int sel_ssl_cert(str* res, select_t* s, sip_msg_t* msg) +{ + int i, local = 0, urlencoded = 0; + + for(i = 1; i <= s->n - 1; i++) { + switch(s->params[i].v.i) { + case CERT_PEER: local = 0; break; + case CERT_LOCAL: local = 1; break; + case CERT_RAW: urlencoded = 0; break; + case CERT_URLENCODED: urlencoded = 1; break; + default: + BUG("Bug in call to sel_ssl_cert\n"); + return -1; + } + } + + return get_ssl_cert(res, local, urlencoded, msg); +} + + +static int pv_ssl_cert(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + int local, urlencoded; + + if (param->pvn.u.isname.name.n & PV_CERT_PEER) { + local = 0; + } else if (param->pvn.u.isname.name.n & PV_CERT_LOCAL) { + local = 1; + } else { + BUG("bug in call to pv_ssl_cert\n"); + return pv_get_null(msg, param, res); + } + + if (param->pvn.u.isname.name.n & PV_CERT_RAW) { + urlencoded = 0; + } else if (param->pvn.u.isname.name.n & PV_CERT_URLENCODED) { + urlencoded = 1; + } else { + BUG("bug in call to pv_ssl_cert\n"); + return pv_get_null(msg, param, res); + } + + if (get_ssl_cert(&res->rs, local, urlencoded, msg) < 0) { + return pv_get_null(msg, param, res); + } + res->flags = PV_VAL_STR; + return 0; +} + + +/* NB: SSL_get0_verified_chain() was introduced in OpenSSL 1.1.0 */ +static int get_verified_cert_chain(STACK_OF(X509)** chain, struct tcp_connection** c, struct sip_msg* msg) +{ + SSL* ssl; + + *chain = 0; + *c = get_cur_connection(msg); + if (!(*c)) { + INFO("TLS connection not found\n"); + return -1; + } + ssl = get_ssl(*c); + if (!ssl) goto err; + *chain = wolfSSL_get0_verified_chain(ssl); + if (!*chain) { + ERR("Unable to retrieve peer TLS verified chain from SSL structure\n"); + goto err; + } + + return 0; +err: + tcpconn_put(*c); + return -1; +} + + +static int sel_ssl_verified_cert_chain(str* res, select_t* s, sip_msg_t* msg) +{ + char *buf = NULL; + struct tcp_connection* c; + size_t len; + STACK_OF(X509)* chain; + X509* cert; + int i; + + if (get_verified_cert_chain(&chain, &c, msg) < 0) return -1; + + if (s->params[s->n-1].type == SEL_PARAM_INT) { + i = s->params[s->n-1].v.i; + } else + return -1; + + if (i < 0 || i >= sk_X509_num(chain)) + return -1; + + cert = sk_X509_value(chain, i); + if (!cert) + return -1; + + if (cert_to_buf(cert, &buf, &len) < 0) { + ERR("cert to buf failed\n"); + goto err; + } + + res->s = buf; + res->len = len; + + tcpconn_put(c); + return 0; + +err: + tcpconn_put(c); + return -1; +} + + +static int get_comp(str* res, int local, int issuer, int nid, sip_msg_t* msg) +{ + static char buf[1024]; + X509* cert; + struct tcp_connection* c; + X509_NAME* name; + X509_NAME_ENTRY* e; + ASN1_STRING* asn1; + int index, text_len; + char* elem; + unsigned char* text_s; + + text_s = 0; + + if (get_cert(&cert, &c, msg, local) < 0) return -1; + + name = issuer ? X509_get_issuer_name(cert) : X509_get_subject_name(cert); + if (!name) { + ERR("Cannot extract subject or issuer name from peer certificate\n"); + goto err; + } + + index = X509_NAME_get_index_by_NID(name, nid, -1); + if (index == -1) { + switch(nid) { + case NID_commonName: elem = "CommonName"; break; + case NID_organizationName: elem = "OrganizationName"; break; + case NID_organizationalUnitName: elem = "OrganizationalUnitUname"; break; + case NID_countryName: elem = "CountryName"; break; + case NID_stateOrProvinceName: elem = "StateOrProvinceName"; break; + case NID_localityName: elem = "LocalityName"; break; + case NID_userId: elem = "UserID"; break; + default: elem = "Unknown"; break; + } + DBG("Element %s not found in certificate subject/issuer\n", elem); + goto err; + } + + e = X509_NAME_get_entry(name, index); + asn1 = X509_NAME_ENTRY_get_data(e); + text_len = ASN1_STRING_to_UTF8(&text_s, asn1); + if (text_len < 0 || text_len >= 1024) { + ERR("Error converting ASN1 string\n"); + goto err; + } + memcpy(buf, text_s, text_len); + res->s = buf; + res->len = text_len; + + OPENSSL_free(text_s); + if (!local) X509_free(cert); + tcpconn_put(c); + return 0; + + err: + if (text_s) OPENSSL_free(text_s); + if (!local) X509_free(cert); + tcpconn_put(c); + return -1; +} + + +static int sel_comp(str* res, select_t* s, sip_msg_t* msg) +{ + int i, local = 0, issuer = 0; + int nid = NID_commonName; + + for(i = 1; i <= s->n - 1; i++) { + switch(s->params[i].v.i) { + case CERT_LOCAL: local = 1; break; + case CERT_PEER: local = 0; break; + case CERT_SUBJECT: issuer = 0; break; + case CERT_ISSUER: issuer = 1; break; + case COMP_CN: nid = NID_commonName; break; + case COMP_O: nid = NID_organizationName; break; + case COMP_OU: nid = NID_organizationalUnitName; break; + case COMP_C: nid = NID_countryName; break; + case COMP_ST: nid = NID_stateOrProvinceName; break; + case COMP_L: nid = NID_localityName; break; + case COMP_UID: nid = NID_userId; break; + default: + BUG("Bug in sel_comp: %d\n", s->params[s->n - 1].v.i); + return -1; + } + } + + return get_comp(res, local, issuer, nid, msg); +} + + +static int pv_comp(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + int ind_local, local = 0, issuer = 0, nid = NID_commonName; + + /* copy callback value as we modify it */ + ind_local = param->pvn.u.isname.name.n; + DBG("ind_local = %x", ind_local); + + if (ind_local & PV_CERT_PEER) { + local = 0; + ind_local = ind_local ^ PV_CERT_PEER; + } else if (ind_local & PV_CERT_LOCAL) { + local = 1; + ind_local = ind_local ^ PV_CERT_LOCAL; + } else { + BUG("could not determine certificate\n"); + return pv_get_null(msg, param, res); + } + + if (ind_local & PV_CERT_SUBJECT) { + issuer = 0; + ind_local = ind_local ^ PV_CERT_SUBJECT; + } else if (ind_local & PV_CERT_ISSUER) { + issuer = 1; + ind_local = ind_local ^ PV_CERT_ISSUER; + } else { + BUG("could not determine subject or issuer\n"); + return pv_get_null(msg, param, res); + } + + switch(ind_local) { + case PV_COMP_CN: nid = NID_commonName; break; + case PV_COMP_O: nid = NID_organizationName; break; + case PV_COMP_OU: nid = NID_organizationalUnitName; break; + case PV_COMP_C: nid = NID_countryName; break; + case PV_COMP_ST: nid = NID_stateOrProvinceName; break; + case PV_COMP_L: nid = NID_localityName; break; + case PV_COMP_UID: nid = NID_userId; break; + default: nid = NID_undef; + } + + if (get_comp(&res->rs, local, issuer, nid, msg) < 0) { + return pv_get_null(msg, param, res); + } + + res->flags = PV_VAL_STR; + return 0; +} + + +static int get_alt(str* res, int local, int type, sip_msg_t* msg) +{ + static char buf[1024]; + int n, found = 0; + STACK_OF(GENERAL_NAME)* names = 0; + GENERAL_NAME* nm; + X509* cert; + struct tcp_connection* c; + str text; + struct ip_addr ip; + + if (get_cert(&cert, &c, msg, local) < 0) return -1; + + names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if (!names) { + DBG("Cannot get certificate alternative subject\n"); + goto err; + + } + + for (n = 0; n < sk_GENERAL_NAME_num(names); n++) { + nm = sk_GENERAL_NAME_value(names, n); + if (nm->type != type) continue; + switch(type) { + case GEN_EMAIL: + case GEN_DNS: + case GEN_URI: + text.s = (char*)nm->d.ia5->data; + text.len = nm->d.ia5->length; + if (text.len >= 1024) { + ERR("Alternative subject text too long\n"); + goto err; + } + memcpy(buf, text.s, text.len); + res->s = buf; + res->len = text.len; + found = 1; + break; + + case GEN_IPADD: + ip.len = nm->d.iPAddress->length; + ip.af = (ip.len == 16) ? AF_INET6 : AF_INET; + memcpy(ip.u.addr, nm->d.iPAddress->data, ip.len); + text.s = ip_addr2a(&ip); + text.len = strlen(text.s); + memcpy(buf, text.s, text.len); + res->s = buf; + res->len = text.len; + found = 1; + break; + } + break; + } + if (!found) goto err; + + if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + if (!local) X509_free(cert); + tcpconn_put(c); + return 0; + err: + if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + if (!local) X509_free(cert); + tcpconn_put(c); + return -1; +} + +static int sel_alt(str* res, select_t* s, sip_msg_t* msg) +{ + int type = GEN_URI, local = 0, i; + + for(i = 1; i <= s->n - 1; i++) { + switch(s->params[i].v.i) { + case CERT_LOCAL: local = 1; break; + case CERT_PEER: local = 0; break; + case COMP_E: type = GEN_EMAIL; break; + case COMP_HOST: type = GEN_DNS; break; + case COMP_URI: type = GEN_URI; break; + case COMP_IP: type = GEN_IPADD; break; + default: + BUG("Bug in sel_alt: %d\n", s->params[s->n - 1].v.i); + return -1; + } + } + + return get_alt(res, local, type, msg); +} + + +static int pv_alt(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + int ind_local, local = 0, type = GEN_URI; + + ind_local = param->pvn.u.isname.name.n; + + if (ind_local & PV_CERT_PEER) { + local = 0; + ind_local = ind_local ^ PV_CERT_PEER; + } else if (ind_local & PV_CERT_LOCAL) { + local = 1; + ind_local = ind_local ^ PV_CERT_LOCAL; + } else { + BUG("could not determine certificate\n"); + return pv_get_null(msg, param, res); + } + + switch(ind_local) { + case PV_COMP_E: type = GEN_EMAIL; break; + case PV_COMP_HOST: type = GEN_DNS; break; + case PV_COMP_URI: type = GEN_URI; break; + case PV_COMP_IP: type = GEN_IPADD; break; + default: + BUG("ind_local=%d\n", ind_local); + return pv_get_null(msg, param, res); + } + + if (get_alt(&res->rs, local, type, msg) < 0) { + return pv_get_null(msg, param, res); + } + + res->flags = PV_VAL_STR; + return 0; +} + + +static int sel_tls(str* res, select_t* s, struct sip_msg* msg) +{ + return sel_desc(res, s, msg); +} + + +static int sel_name(str* res, select_t* s, struct sip_msg* msg) +{ + return sel_comp(res, s, msg); +} + + +static int sel_cert(str* res, select_t* s, struct sip_msg* msg) +{ + return sel_comp(res, s, msg); +} + + +#ifdef OPENSSL_NO_TLSEXT +static int get_tlsext_sn(str* res, sip_msg_t* msg) +{ + ERR("TLS extension 'server name' is not available! " + "please install openssl with TLS extension support and recompile " + "the server\n"); + return -1; +} +#else +static int get_tlsext_sn(str* res, sip_msg_t* msg) +{ + static char buf[1024]; + struct tcp_connection* c; + str server_name; + SSL* ssl; + + c = get_cur_connection(msg); + if (!c) { + INFO("TLS connection not found in select_desc\n"); + goto error; + } + ssl = get_ssl(c); + if (!ssl) goto error; + + buf[0] = '\0'; + + server_name.s = (char*)wolfSSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name.s) { + server_name.len = strlen(server_name.s); + DBG("received server_name (TLS extension): '%.*s'\n", + STR_FMT(&server_name)); + } else { + DBG("SSL_get_servername returned NULL\n"); + goto error; + } + + /* copy server_name into the buffer. If the buffer is too small copy only + * the last bytes as these are the more important ones and prefix with + * '+' */ + if (server_name.len > sizeof(buf)) { + ERR("server_name to big for buffer\n"); + buf[0] = '+'; + memcpy(buf + 1, server_name.s + 1 + server_name.len - sizeof(buf), + sizeof(buf) - 1); + res->len = sizeof(buf); + } else { + memcpy(buf, server_name.s, server_name.len); + res->len = server_name.len; + } + res->s = buf; + + tcpconn_put(c); + return 0; + +error: + if (c) tcpconn_put(c); + return -1; +} +#endif + + +static int sel_tlsext_sn(str* res, select_t* s, sip_msg_t* msg) +{ + return get_tlsext_sn(res, msg); +} + + +static int pv_tlsext_sn(sip_msg_t* msg, pv_param_t* param, pv_value_t* res) +{ + if (param->pvn.u.isname.name.n != PV_TLSEXT_SNI) { + BUG("unexpected parameter value \"%d\"\n", + param->pvn.u.isname.name.n); + return pv_get_null(msg, param, res); + } + + if (get_tlsext_sn(&res->rs, msg) < 0) { + return pv_get_null(msg, param, res); + } + + res->flags = PV_VAL_STR; + return 0; +} + + +int pv_parse_tls_name(pv_spec_p sp, str *in) +{ + if(sp==NULL || in==NULL || in->len<=0) + return -1; + + switch(in->len) { + case 13: + if(strncmp(in->s, "m_issuer_line", 13)==0) + sp->pvp.pvn.u.isname.name.n = 1001; + else if(strncmp(in->s, "p_issuer_line", 13)==0) + sp->pvp.pvn.u.isname.name.n = 5001; + else goto error; + break; + case 14: + if(strncmp(in->s, "m_subject_line", 14)==0) + sp->pvp.pvn.u.isname.name.n = 1000; + else if(strncmp(in->s, "p_subject_line", 14)==0) + sp->pvp.pvn.u.isname.name.n = 5000; + else goto error; + break; + default: + goto error; + } + sp->pvp.pvn.type = PV_NAME_INTSTR; + sp->pvp.pvn.u.isname.type = 0; + + return 0; + +error: + LM_ERR("unknown PV tls name %.*s\n", in->len, in->s); + return -1; +} + + +int pv_get_tls(struct sip_msg *msg, pv_param_t *param, + pv_value_t *res) +{ + SSL *ssl = NULL; + tcp_connection_t *c = NULL; + X509 *cert = NULL; + str sv = STR_NULL; + + if(msg==NULL || param==NULL) { + return -1; + } + + c = get_cur_connection(msg); + if (c == NULL) { + LM_DBG("TLS connection not found\n"); + return pv_get_null(msg, param, res); + } + ssl = get_ssl(c); + if (ssl == NULL) { + goto error; + } + cert = (param->pvn.u.isname.name.n < 5000) ? wolfSSL_get_certificate(ssl) + : wolfSSL_get_peer_certificate(ssl); + if (cert == NULL) { + if (param->pvn.u.isname.name.n < 5000) { + LM_ERR("failed to retrieve my TLS certificate from SSL structure\n"); + } else { + LM_ERR("failed to retrieve peer TLS certificate from SSL structure\n"); + } + goto error; + } + + switch(param->pvn.u.isname.name.n) + { + case 1000: + case 5000: + sv.s = pv_get_buffer(); + sv.len = pv_get_buffer_size() - 1; + if(X509_NAME_oneline(X509_get_subject_name(cert), sv.s, sv.len)==NULL) { + goto error; + } + tcpconn_put(c); + return pv_get_strzval(msg, param, res, sv.s); + break; + + case 1001: + case 5001: + sv.s = pv_get_buffer(); + sv.len = pv_get_buffer_size() - 1; + if(X509_NAME_oneline(X509_get_issuer_name(cert), sv.s, sv.len)==NULL) { + goto error; + } + tcpconn_put(c); + return pv_get_strzval(msg, param, res, sv.s); + break; + + default: + goto error; + } + +error: + tcpconn_put(c); + return pv_get_null(msg, param, res); +} + +select_row_t tls_sel[] = { + /* Current cipher parameters */ + { NULL, SEL_PARAM_STR, STR_STATIC_INIT("tls"), sel_tls, 0}, + + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("version"), sel_version, 0}, + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("desc"), sel_desc, 0}, + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("description"), sel_desc, 0}, + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("cipher"), sel_cipher, 0}, + + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("serverName"), sel_tlsext_sn, 0}, + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("server_name"), sel_tlsext_sn, 0}, + + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("peer"), sel_cert, DIVERSION | CERT_PEER}, + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("my"), sel_cert, DIVERSION | CERT_LOCAL}, + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("me"), sel_cert, DIVERSION | CERT_LOCAL}, + { sel_tls, SEL_PARAM_STR, STR_STATIC_INIT("myself"), sel_cert, DIVERSION | CERT_LOCAL}, + + { sel_cipher, SEL_PARAM_STR, STR_STATIC_INIT("bits"), sel_bits, 0}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("subject"), sel_name, DIVERSION | CERT_SUBJECT}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("subj"), sel_name, DIVERSION | CERT_SUBJECT}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("issuer"), sel_name, DIVERSION | CERT_ISSUER}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("verified"), sel_check_cert, DIVERSION | CERT_VERIFIED}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("revoked"), sel_check_cert, DIVERSION | CERT_REVOKED2}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("expired"), sel_check_cert, DIVERSION | CERT_EXPIRED}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("self_signed"), sel_check_cert, DIVERSION | CERT_SELFSIGNED}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("version"), sel_cert_version, 0}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("rawCert"), sel_ssl_cert, DIVERSION | CERT_RAW}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("raw_cert"), sel_ssl_cert, DIVERSION | CERT_RAW}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("URLEncodedCert"), sel_ssl_cert, DIVERSION | CERT_URLENCODED}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("urlencoded_cert"), sel_ssl_cert, DIVERSION | CERT_URLENCODED}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("verified_cert_chain"), sel_ssl_verified_cert_chain, CONSUME_NEXT_INT}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("sn"), sel_sn, 0}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("serialNumber"), sel_sn, 0}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("serial_number"), sel_sn, 0}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("notBefore"), sel_validity, DIVERSION | CERT_NOTBEFORE}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("not_before"), sel_validity, DIVERSION | CERT_NOTBEFORE}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("notAfter"), sel_validity, DIVERSION | CERT_NOTAFTER}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("not_after"), sel_validity, DIVERSION | CERT_NOTAFTER}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("email"), sel_alt, DIVERSION | COMP_E}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("emailAddress"), sel_alt, DIVERSION | COMP_E}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("email_address"), sel_alt, DIVERSION | COMP_E}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("host"), sel_alt, DIVERSION | COMP_HOST}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("hostname"), sel_alt, DIVERSION | COMP_HOST}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("dns"), sel_alt, DIVERSION | COMP_HOST}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("uri"), sel_alt, DIVERSION | COMP_URI}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("url"), sel_alt, DIVERSION | COMP_URI}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("urn"), sel_alt, DIVERSION | COMP_URI}, + + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("ip"), sel_alt, DIVERSION | COMP_IP}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("IPAddress"), sel_alt, DIVERSION | COMP_IP}, + { sel_cert, SEL_PARAM_STR, STR_STATIC_INIT("ip_address"), sel_alt, DIVERSION | COMP_IP}, + + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("cn"), sel_comp, DIVERSION | COMP_CN}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("commonName"), sel_comp, DIVERSION | COMP_CN}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("common_name"), sel_comp, DIVERSION | COMP_CN}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("name"), sel_comp, DIVERSION | COMP_CN}, + + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("l"), sel_comp, DIVERSION | COMP_L}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("localityName"), sel_comp, DIVERSION | COMP_L}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("locality_name"), sel_comp, DIVERSION | COMP_L}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("locality"), sel_comp, DIVERSION | COMP_L}, + + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("c"), sel_comp, DIVERSION | COMP_C}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("countryName"), sel_comp, DIVERSION | COMP_C}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("country_name"), sel_comp, DIVERSION | COMP_C}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("country"), sel_comp, DIVERSION | COMP_C}, + + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("st"), sel_comp, DIVERSION | COMP_ST}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("stateOrProvinceName"), sel_comp, DIVERSION | COMP_ST}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("state_or_province_name"), sel_comp, DIVERSION | COMP_ST}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("state"), sel_comp, DIVERSION | COMP_ST}, + + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("o"), sel_comp, DIVERSION | COMP_O}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("organizationName"), sel_comp, DIVERSION | COMP_O}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("organization_name"), sel_comp, DIVERSION | COMP_O}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("organization"), sel_comp, DIVERSION | COMP_O}, + + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("ou"), sel_comp, DIVERSION | COMP_OU}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("organizationalUnitName"), sel_comp, DIVERSION | COMP_OU}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("organizational_unit_name"), sel_comp, DIVERSION | COMP_OU}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("unit"), sel_comp, DIVERSION | COMP_OU}, + + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("uid"), sel_comp, DIVERSION | COMP_UID}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("uniqueIdentifier"), sel_comp, DIVERSION | COMP_UID}, + { sel_name, SEL_PARAM_STR, STR_STATIC_INIT("unique_identifier"), sel_comp, DIVERSION | COMP_UID}, + + { NULL, SEL_PARAM_INT, STR_NULL, NULL, 0} +}; + + +/* + * pseudo variables + */ +pv_export_t tls_pv[] = { + /* TLS session parameters */ + {{"tls_version", sizeof("tls_version")-1}, + PVT_OTHER, pv_version, 0, + 0, 0, 0, 0 }, + {{"tls_description", sizeof("tls_description")-1}, + PVT_OTHER, pv_desc, 0, + 0, 0, 0, 0 }, + {{"tls_cipher_info", sizeof("tls_cipher_info")-1}, + PVT_OTHER, pv_cipher, 0, + 0, 0, 0, 0 }, + {{"tls_cipher_bits", sizeof("tls_cipher_bits")-1}, + PVT_OTHER, pv_bits, 0, + 0, 0, 0, 0 }, + /* raw and urlencoded versions of peer and local certificates */ + {{"tls_peer_raw_cert", sizeof("tls_peer_raw_cert")-1}, + PVT_OTHER, pv_ssl_cert, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_RAW}, + {{"tls_my_raw_cert", sizeof("tls_my_raw_cert")-1}, + PVT_OTHER, pv_ssl_cert, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_RAW}, + {{"tls_peer_urlencoded_cert", sizeof("tls_peer_urlencoded_cert")-1}, + PVT_OTHER, pv_ssl_cert, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_URLENCODED}, + {{"tls_my_urlencoded_cert", sizeof("tls_my_urlencoded_cert")-1}, + PVT_OTHER, pv_ssl_cert, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_URLENCODED}, + /* general certificate parameters for peer and local */ + {{"tls_peer_version", sizeof("tls_peer_version")-1}, + PVT_OTHER, pv_cert_version, 0, + 0, 0, pv_init_iname, PV_CERT_PEER }, + {{"tls_my_version", sizeof("tls_my_version")-1}, + PVT_OTHER, pv_cert_version, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL }, + {{"tls_peer_serial", sizeof("tls_peer_serial")-1}, + PVT_OTHER, pv_sn, 0, + 0, 0, pv_init_iname, PV_CERT_PEER }, + {{"tls_my_serial", sizeof("tls_my_serial")-1}, + PVT_OTHER, pv_sn,0, + 0, 0, pv_init_iname, PV_CERT_LOCAL }, + /* certificate parameters for peer and local, for subject and issuer*/ + {{"tls_peer_subject", sizeof("tls_peer_subject")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT }, + {{"tls_peer_issuer", sizeof("tls_peer_issuer")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_ISSUER }, + {{"tls_my_subject", sizeof("tls_my_subject")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT }, + {{"tls_my_issuer", sizeof("tls_my_issuer")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_ISSUER }, + {{"tls_peer_subject_cn", sizeof("tls_peer_subject_cn")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT | PV_COMP_CN }, + {{"tls_peer_issuer_cn", sizeof("tls_peer_issuer_cn")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_ISSUER | PV_COMP_CN }, + {{"tls_my_subject_cn", sizeof("tls_my_subject_cn")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT | PV_COMP_CN }, + {{"tls_my_issuer_cn", sizeof("tls_my_issuer_cn")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_ISSUER | PV_COMP_CN }, + {{"tls_peer_subject_locality", sizeof("tls_peer_subject_locality")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT | PV_COMP_L }, + {{"tls_peer_issuer_locality", sizeof("tls_peer_issuer_locality")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_ISSUER | PV_COMP_L }, + {{"tls_my_subject_locality", sizeof("tls_my_subject_locality")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT | PV_COMP_L }, + {{"tls_my_issuer_locality", sizeof("tls_my_issuer_locality")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_ISSUER | PV_COMP_L }, + {{"tls_peer_subject_country", sizeof("tls_peer_subject_country")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT | PV_COMP_C }, + {{"tls_peer_issuer_country", sizeof("tls_peer_issuer_country")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_ISSUER | PV_COMP_C }, + {{"tls_my_subject_country", sizeof("tls_my_subject_country")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT | PV_COMP_C }, + {{"tls_my_issuer_country", sizeof("tls_my_issuer_country")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_ISSUER | PV_COMP_C }, + {{"tls_peer_subject_state", sizeof("tls_peer_subject_state")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT | PV_COMP_ST }, + {{"tls_peer_issuer_state", sizeof("tls_peer_issuer_state")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_ISSUER | PV_COMP_ST }, + {{"tls_my_subject_state", sizeof("tls_my_subject_state")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT | PV_COMP_ST }, + {{"tls_my_issuer_state", sizeof("tls_my_issuer_state")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_ISSUER | PV_COMP_ST }, + {{"tls_peer_subject_organization", sizeof("tls_peer_subject_organization")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT | PV_COMP_O }, + {{"tls_peer_issuer_organization", sizeof("tls_peer_issuer_organization")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_ISSUER | PV_COMP_O }, + {{"tls_my_subject_organization", sizeof("tls_my_subject_organization")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT | PV_COMP_O }, + {{"tls_my_issuer_organization", sizeof("tls_my_issuer_organization")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_ISSUER | PV_COMP_O }, + {{"tls_peer_subject_unit", sizeof("tls_peer_subject_unit")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT | PV_COMP_OU }, + {{"tls_peer_issuer_unit", sizeof("tls_peer_issuer_unit")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_ISSUER | PV_COMP_OU }, + {{"tls_my_subject_unit", sizeof("tls_my_subject_unit")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT | PV_COMP_OU }, + {{"tls_my_issuer_unit", sizeof("tls_my_issuer_unit")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_ISSUER | PV_COMP_OU }, + /* unique identifier for peer and local */ + {{"tls_peer_subject_uid", sizeof("tls_peer_subject_uid")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_CERT_SUBJECT | PV_COMP_UID }, + {{"tls_my_subject_uid", sizeof("tls_my_subject_uid")-1}, + PVT_OTHER, pv_comp, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_CERT_SUBJECT | PV_COMP_UID }, + /* subject alternative name parameters for peer and local */ + {{"tls_peer_san_email", sizeof("tls_peer_san_email")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_COMP_E }, + {{"tls_my_san_email", sizeof("tls_my_san_email")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_COMP_E }, + {{"tls_peer_san_hostname", sizeof("tls_peer_san_hostname")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_COMP_HOST }, + {{"tls_my_san_hostname", sizeof("tls_my_san_hostname")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_COMP_HOST }, + {{"tls_peer_san_uri", sizeof("tls_peer_san_uri")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_COMP_URI }, + {{"tls_my_san_uri", sizeof("tls_my_san_uri")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_COMP_URI }, + {{"tls_peer_san_ip", sizeof("tls_peer_san_ip")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_PEER | PV_COMP_IP }, + {{"tls_my_san_ip", sizeof("tls_my_san_ip")-1}, + PVT_OTHER, pv_alt, 0, + 0, 0, pv_init_iname, PV_CERT_LOCAL | PV_COMP_IP }, + /* peer certificate validation parameters */ + {{"tls_peer_verified", sizeof("tls_peer_verified")-1}, + PVT_OTHER, pv_check_cert, 0, + 0, 0, pv_init_iname, PV_CERT_VERIFIED }, + {{"tls_peer_revoked", sizeof("tls_peer_revoked")-1}, + PVT_OTHER, pv_check_cert, 0, + 0, 0, pv_init_iname, PV_CERT_REVOKED }, + {{"tls_peer_expired", sizeof("tls_peer_expired")-1}, + PVT_OTHER, pv_check_cert, 0, + 0, 0, pv_init_iname, PV_CERT_EXPIRED }, + {{"tls_peer_selfsigned", sizeof("tls_peer_selfsigned")-1}, + PVT_OTHER, pv_check_cert, 0, + 0, 0, pv_init_iname, PV_CERT_SELFSIGNED }, + {{"tls_peer_notBefore", sizeof("tls_peer_notBefore")-1}, + PVT_OTHER, pv_validity, 0, + 0, 0, pv_init_iname, PV_CERT_NOTBEFORE }, + {{"tls_peer_notAfter", sizeof("tls_peer_notAfter")-1}, + PVT_OTHER, pv_validity, 0, + 0, 0, pv_init_iname, PV_CERT_NOTAFTER }, + /* peer certificate validation parameters */ + {{"tls_peer_server_name", sizeof("tls_peer_server_name")-1}, + PVT_OTHER, pv_tlsext_sn, 0, + 0, 0, pv_init_iname, PV_TLSEXT_SNI }, + { {"tls", (sizeof("tls")-1)}, PVT_OTHER, pv_get_tls, + 0, pv_parse_tls_name, 0, 0, 0}, + + { {0, 0}, 0, 0, 0, 0, 0, 0, 0 } + +}; + + +/** + * + */ +static sr_kemi_xval_t _ksr_kemi_tls_xval = {0}; + + +/** + * + */ +sr_kemi_xval_t* ki_tls_cget_attr(sip_msg_t* msg, str *aname) +{ + pv_param_t param; + pv_value_t value; + int i; + + memset(&_ksr_kemi_tls_xval, 0, sizeof(sr_kemi_xval_t)); + for(i=0; tls_pv[i].name.s != NULL; i++) { + if((tls_pv[i].name.len == aname->len) + && strncmp(tls_pv[i].name.s, aname->s, aname->len) == 0) { + break; + } + } + if(tls_pv[i].name.s==NULL) { + LM_WARN("unknown attribute: %.*s\n", aname->len, aname->s); + sr_kemi_xval_null(&_ksr_kemi_tls_xval, SR_KEMI_XVAL_NULL_EMPTY); + return &_ksr_kemi_tls_xval; + } + if(tls_pv[i].parse_name!=NULL || tls_pv[i].parse_index!=NULL) { + LM_WARN("unsupported attribute: %.*s\n", aname->len, aname->s); + sr_kemi_xval_null(&_ksr_kemi_tls_xval, SR_KEMI_XVAL_NULL_EMPTY); + return &_ksr_kemi_tls_xval; + } + memset(¶m, 0, sizeof(pv_param_t)); + memset(&value, 0, sizeof(pv_value_t)); + + if(tls_pv[i].getf(msg, ¶m, &value) != 0) { + sr_kemi_xval_null(&_ksr_kemi_tls_xval, SR_KEMI_XVAL_NULL_EMPTY); + return &_ksr_kemi_tls_xval; + } + if(value.flags & PV_VAL_NULL) { + sr_kemi_xval_null(&_ksr_kemi_tls_xval, SR_KEMI_XVAL_NULL_EMPTY); + return &_ksr_kemi_tls_xval; + } + if(value.flags & PV_TYPE_INT) { + _ksr_kemi_tls_xval.vtype = SR_KEMIP_INT; + _ksr_kemi_tls_xval.v.n = value.ri; + return &_ksr_kemi_tls_xval; + } + if(value.flags & PV_VAL_STR) { + _ksr_kemi_tls_xval.vtype = SR_KEMIP_STR; + _ksr_kemi_tls_xval.v.s = value.rs; + return &_ksr_kemi_tls_xval; + } + + LM_WARN("unsupported value for attribute: %.*s\n", aname->len, aname->s); + sr_kemi_xval_null(&_ksr_kemi_tls_xval, SR_KEMI_XVAL_NULL_EMPTY); + return &_ksr_kemi_tls_xval; +} diff --git a/src/modules/tls_wolfssl/tls_select.h b/src/modules/tls_wolfssl/tls_select.h new file mode 100644 index 00000000000..b30e445806d --- /dev/null +++ b/src/modules/tls_wolfssl/tls_select.h @@ -0,0 +1,52 @@ +/* + * TLS module - select interface + * + * Copyright (C) 2005,2006 iptelorg GmbH + * Copyright (C) 2006 enum.at + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Exception: permission to copy, modify, propagate, and distribute a work + * formed by combining OpenSSL toolkit software and the code in this file, + * such as linking with software components and libraries released under + * OpenSSL project license. + */ +/*! + * \file + * \brief Kamailio TLS support :: select interface + * \ingroup tls + * Module: \ref tls + */ + + +#ifndef _TLS_SELECT_H +#define _TLS_SELECT_H + +#include "../../core/select.h" +#include "../../core/pvar.h" +#include "../../core/kemi.h" +#include "../../core/tcp_conn.h" + +extern select_row_t tls_sel[]; + +extern pv_export_t tls_pv[]; + +void tls_set_pv_con(struct tcp_connection *c); + +sr_kemi_xval_t* ki_tls_cget_attr(sip_msg_t* msg, str *aname); + +#endif /* _TLS_SELECT_H */ diff --git a/src/modules/tls_wolfssl/tls_server.c b/src/modules/tls_wolfssl/tls_server.c new file mode 100644 index 00000000000..db78e7f13ab --- /dev/null +++ b/src/modules/tls_wolfssl/tls_server.c @@ -0,0 +1,1539 @@ +/* + * TLS module - main server part + * + * Copyright (C) 2005-2010 iptelorg GmbH + * Copyright (C) 2013 Motorola Solutions, Inc. + * + * This file is part of Kamailio, a free SIP server. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** main tls part (implements the tls hooks that are called from the tcp code). + * @file tls_server.c + * @ingroup tls + * Module: @ref tls + */ + + +#include +#include +#include + +// WOLFFIX +# define SSL_get_cipher_version(s) \ + wolfSSL_CIPHER_get_version(wolfSSL_get_current_cipher(s)) + + + +#include "../../core/dprint.h" +#include "../../core/ip_addr.h" +#include "../../core/mem/shm_mem.h" +#include "../../core/pt.h" +#include "../../core/timer.h" +#include "../../core/globals.h" +#include "../../core/pt.h" +#include "../../core/tcp_int_send.h" +#include "../../core/tcp_read.h" +#include "../../core/cfg/cfg.h" +#include "../../core/route.h" +#include "../../core/forward.h" +#include "../../core/onsend.h" +#include "../../core/xavp.h" +#include "../../core/fmsg.h" +#include "../../core/kemi.h" + +#include "tls_init.h" +#include "tls_domain.h" +#include "tls_util.h" +#include "tls_mod.h" +#include "tls_server.h" +#include "tls_select.h" +#include "tls_bio.h" +#include "tls_dump_vf.h" +#include "tls_cfg.h" + +int tls_run_event_routes(struct tcp_connection *c); + +/* low memory treshold for openssl bug #1491 workaround */ +#define LOW_MEM_NEW_CONNECTION_TEST() \ + (cfg_get(tls, tls_cfg, low_mem_threshold1) && \ + (shm_available_safe() < cfg_get(tls, tls_cfg, low_mem_threshold1))) +#define LOW_MEM_CONNECTED_TEST() \ + (cfg_get(tls, tls_cfg, low_mem_threshold2) && \ + (shm_available_safe() < cfg_get(tls, tls_cfg, low_mem_threshold2))) + +#define TLS_RD_MBUF_SZ 65536 +#define TLS_WR_MBUF_SZ 65536 + + +/* debugging */ +#ifdef NO_TLS_RD_DEBUG +#undef TLS_RD_DEBUG +#endif + +#ifdef NO_TLS_WR_DEBUG +#undef TLS_WR_DEBUG +#endif +#if defined TLS_RD_DEBUG || defined TLS_WR_DEBUG +#define TLS_F_DEBUG +#endif + +/* if NO_TLS_F_DEBUG or NO_TLS_DEBUG => no debug code */ +#if defined NO_TLS_F_DEBUG || defined NO_TLS_DEBUG +#undef TLS_F_DEBUG +#endif + +#ifdef TLS_F_DEBUG + #ifdef __SUNPRO_C + #define TLS_F_TRACE(fmt, ...) \ + LOG_FP(DEFAULT_FACILITY, cfg_get(tls, tls_cfg, debug),\ + "TLS_TRACE: " LOC_INFO, " %s" fmt,\ + _FUNC_NAME_, __VA_ARGS__) + #else + #define TLS_F_TRACE(fmt, args...) \ + LOG_FP(DEFAULT_FACILITY, cfg_get(tls, tls_cfg, debug),\ + "TLS_TRACE: " LOC_INFO, " %s" fmt,\ + _FUNC_NAME_, ## args) + #endif /* __SUNPRO_c */ +#else /* TLS_F_DEBUG */ + #ifdef __SUNPRO_C + #define TLS_F_TRACE(...) + #else + #define TLS_F_TRACE(fmt, args...) + #endif /* __SUNPRO_c */ +#endif /* TLS_F_DEBUG */ + +/* tls_read debugging */ +#ifdef TLS_RD_DEBUG + #define TLS_RD_TRACE TLS_F_TRACE +#else /* TLS_RD_DEBUG */ + #ifdef __SUNPRO_C + #define TLS_RD_TRACE(...) + #else + #define TLS_RD_TRACE(fmt, args...) + #endif /* __SUNPRO_c */ +#endif /* TLS_RD_DEBUG */ + +/* tls_write debugging */ +#ifdef TLS_WR_DEBUG + #define TLS_WR_TRACE TLS_F_TRACE +#else /* TLS_RD_DEBUG */ + #ifdef __SUNPRO_C + #define TLS_WR_TRACE(...) + #else + #define TLS_WR_TRACE(fmt, args...) + #endif /* __SUNPRO_c */ +#endif /* TLS_RD_DEBUG */ + + +extern str sr_tls_xavp_cfg; + +static str _ksr_tls_connect_server_id = STR_NULL; + +int ksr_tls_set_connect_server_id(str *srvid) +{ + if(srvid==NULL || srvid->len<=0) { + if(_ksr_tls_connect_server_id.s) { + pkg_free(_ksr_tls_connect_server_id.s); + } + _ksr_tls_connect_server_id.s = NULL; + _ksr_tls_connect_server_id.len = 0; + return 0; + } + + if(_ksr_tls_connect_server_id.len>=srvid->len) { + memcpy(_ksr_tls_connect_server_id.s, srvid->s, srvid->len); + _ksr_tls_connect_server_id.len = srvid->len; + return 0; + } + + if(_ksr_tls_connect_server_id.s) { + pkg_free(_ksr_tls_connect_server_id.s); + } + _ksr_tls_connect_server_id.len = 0; + + _ksr_tls_connect_server_id.s = (char*)pkg_mallocxz(srvid->len + 1); + if(_ksr_tls_connect_server_id.s==NULL) { + PKG_MEM_ERROR; + return -1; + } + + memcpy(_ksr_tls_connect_server_id.s, srvid->s, srvid->len); + _ksr_tls_connect_server_id.len = srvid->len; + + return 0; +} + +static str *tls_get_connect_server_id(void) +{ + sr_xavp_t *vavp = NULL; + str sid = {"server_id", 9}; + + if(sr_tls_xavp_cfg.s!=NULL) { + vavp = xavp_get_child_with_sval(&sr_tls_xavp_cfg, &sid); + } + if(vavp==NULL || vavp->val.v.s.len<=0) { + LM_DBG("xavp with outbound server id not found\n"); + if(_ksr_tls_connect_server_id.len>0) { + LM_DBG("found global outbound server id: %.*s\n", + _ksr_tls_connect_server_id.len, + _ksr_tls_connect_server_id.s); + return &_ksr_tls_connect_server_id; + } + LM_DBG("outbound server id not set\n"); + return NULL; + } + LM_DBG("found xavp with outbound server id: %s\n", vavp->val.v.s.s); + return &vavp->val.v.s; + +} + +/** + * get the server name (sni) for outbound connections from xavp + */ +static str *tls_get_connect_server_name(void) +{ +#ifndef OPENSSL_NO_TLSEXT + sr_xavp_t *vavp = NULL; + str sname = {"server_name", 11}; + + if(sr_tls_xavp_cfg.s!=NULL) + vavp = xavp_get_child_with_sval(&sr_tls_xavp_cfg, &sname); + if(vavp==NULL || vavp->val.v.s.len<=0) { + LM_DBG("xavp with outbound server name not found\n"); + return NULL; + } + LM_DBG("found xavp with outbound server name: %s\n", vavp->val.v.s.s); + return &vavp->val.v.s; +#else + return NULL; +#endif +} + +/** finish the ssl init. + * Creates the SSL context + internal tls_extra_data and sets + * extra_data to it. + * Separated from tls_tcpconn_init to allow delayed ssl context + * init (from the "child" process and not from the main one). + * WARNING: the connection should be already locked. + * @return 0 on success, -1 on errror. + */ +static int tls_complete_init(struct tcp_connection* c) +{ + tls_domain_t* dom; + struct tls_extra_data* data = 0; + tls_domains_cfg_t* cfg; + enum tls_conn_states state; + str *sname = NULL; + str *srvid = NULL; + + if (LOW_MEM_NEW_CONNECTION_TEST()){ + ERR("tls: ssl bug #1491 workaround: not enough memory for safe" + " operation: shm=%lu threshold1=%d\n", shm_available_safe(), + cfg_get(tls, tls_cfg, low_mem_threshold1)); + goto error2; + } + /* Get current TLS configuration and increase reference + * count immediately. + */ + + LM_DBG("completing tls connection initialization\n"); + + lock_get(tls_domains_cfg_lock); + cfg = *tls_domains_cfg; + + /* Increment the reference count in the configuration structure, this + * is to ensure that, while on the garbage queue, the configuration does + * not get deleted if there are still connection referencing its SSL_CTX + */ + atomic_inc(&cfg->ref_count); + lock_release(tls_domains_cfg_lock); + + if (c->flags & F_CONN_PASSIVE) { + state=S_TLS_ACCEPTING; + dom = tls_lookup_cfg(cfg, TLS_DOMAIN_SRV, + &c->rcv.dst_ip, c->rcv.dst_port, 0, 0); + } else { + state=S_TLS_CONNECTING; + sname = tls_get_connect_server_name(); + srvid = tls_get_connect_server_id(); + dom = tls_lookup_cfg(cfg, TLS_DOMAIN_CLI, + &c->rcv.dst_ip, c->rcv.dst_port, sname, srvid); + ksr_tls_set_connect_server_id(NULL); + } + if (unlikely(c->state<0)) { + BUG("Invalid connection (state %d)\n", c->state); + goto error; + } + DBG("Using initial TLS domain %s (dom %p ctx %p sn [%s])\n", + tls_domain_str(dom), dom, dom->ctx[process_no], + ZSW(dom->server_name.s)); + + data = (struct tls_extra_data*)shm_malloc(sizeof(struct tls_extra_data)); + if (!data) { + ERR("Not enough shared memory left\n"); + goto error; + } + memset(data, '\0', sizeof(struct tls_extra_data)); + data->ssl = wolfSSL_new(dom->ctx[process_no]); + data->rwbio = tls_BIO_new_mbuf(0, 0); + data->cfg = cfg; + data->state = state; + + if (unlikely(data->ssl == 0 || data->rwbio == 0)) { + TLS_ERR_SSL("Failed to create SSL or BIO structure:", data->ssl); + if (data->ssl) + wolfSSL_free(data->ssl); + if (data->rwbio) + BIO_free(data->rwbio); + goto error; + } + +#ifndef OPENSSL_NO_TLSEXT + if (sname!=NULL) { + if(!wolfSSL_set_tlsext_host_name(data->ssl, sname->s)) { + if (data->ssl) + wolfSSL_free(data->ssl); + if (data->rwbio) + BIO_free(data->rwbio); + goto error; + } + LM_DBG("outbound TLS server name set to: %s\n", sname->s); + } +#endif + + wolfSSL_set_bio(data->ssl, data->rwbio, data->rwbio); + c->extra_data = data; + + /* link the extra data struct inside ssl connection*/ + wolfSSL_set_app_data(data->ssl, data); + return 0; + +error: + atomic_dec(&cfg->ref_count); + if (data) shm_free(data); +error2: + return -1; +} + + + +/** completes tls init if needed and checks if tls can be used (unsafe). + * It will check for low memory. + * If it returns success, c->extra_data is guaranteed to be !=0. + * WARNING: must be called with c->write_lock held. + * @return 0 on success, < 0 on error (complete init failed or out of memory). + */ +static int tls_fix_connection_unsafe(struct tcp_connection* c) +{ + if (unlikely(!c->extra_data)) { + if (unlikely(tls_complete_init(c) < 0)) { + return -1; + } + }else if (unlikely(LOW_MEM_CONNECTED_TEST())){ + ERR("tls: ssl bug #1491 workaround: not enough memory for safe" + " operation: shm=%lu threshold2=%d\n", shm_available_safe(), + cfg_get(tls, tls_cfg, low_mem_threshold2)); + return -1; + } + return 0; +} + + + +/** completes tls init if needed and checks if tls can be used, (safe). + * It will check for low memory. + * If it returns success, c->extra_data is guaranteed to be !=0. + * WARNING: must _not_ be called with c->write_lock held (it will + * lock/unlock internally), see also tls_fix_connection_unsafe(). + * @return 0 on success, < 0 on error (complete init failed or out of memory). + */ +static int tls_fix_connection(struct tcp_connection* c) +{ + int ret; + + if (unlikely(c->extra_data == 0)) { + lock_get(&c->write_lock); + if (unlikely(c->extra_data == 0)) { + ret = tls_complete_init(c); + lock_release(&c->write_lock); + return ret; + } + lock_release(&c->write_lock); + } + if (unlikely(LOW_MEM_CONNECTED_TEST())){ + ERR("tls: ssl bug #1491 workaround: not enough memory for safe" + " operation: shm=%lu threshold2=%d\n", shm_available_safe(), + cfg_get(tls, tls_cfg, low_mem_threshold2)); + return -1; + } + return 0; +} + + + +/** sets an mbuf pair for the bio used by the tls connection. + * WARNING: must be called with c->write_lock held. + * @return 0 on success, -1 on error. + */ +static int tls_set_mbufs(struct tcp_connection *c, + struct tls_mbuf* rd, + struct tls_mbuf* wr) +{ + BIO *rwbio; + + rwbio = ((struct tls_extra_data*)c->extra_data)->rwbio; + if (unlikely(tls_BIO_mbuf_set(rwbio, rd, wr)<=0)) { + /* it should be always 1 */ + ERR("failed to set mbufs"); + return -1; + } + return 0; +} + + +static void tls_dump_cert_info(char* s, X509* cert) +{ + char* subj; + char* issuer; + + subj=issuer=0; + subj = X509_NAME_oneline(X509_get_subject_name(cert), 0 , 0); + issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0 , 0); + + if (subj){ + LOG(cfg_get(tls, tls_cfg, log), "%s subject:%s\n", s ? s : "", subj); + wolfSSL_OPENSSL_free(subj); + } + if (issuer){ + LOG(cfg_get(tls, tls_cfg, log), "%s issuer:%s\n", s ? s : "", issuer); + wolfSSL_OPENSSL_free(issuer); + } +} + + +/** wrapper around SSL_accept, usin SSL return convention. + * It will also log critical errors and certificate debugging info. + * @param c - tcp connection with tls (extra_data must be a filled + * tcp_extra_data structure). The state must be S_TLS_ACCEPTING. + * @param error set to the error reason (SSL_ERROR_*). + * Note that it can be SSL_ERROR_NONE while the return is < 0 + * ("internal" error, not at the SSL level, see below). + * @return >=1 on success, 0 and <0 on error. 0 means the underlying SSL + * connection was closed/shutdown. < 0 is returned for any + * SSL_ERROR (including WANT_READ or WANT_WRITE), but also + * for internal non SSL related errors (in this case -2 is + * returned and error==SSL_ERROR_NONE). + * + */ +int tls_accept(struct tcp_connection *c, int* error) +{ + int ret; + SSL *ssl = NULL; + X509* cert; + struct tls_extra_data* tls_c; + int tls_log; + + *error = SSL_ERROR_NONE; + tls_c=(struct tls_extra_data*)c->extra_data; + ssl=tls_c->ssl; + + if (unlikely(tls_c->state != S_TLS_ACCEPTING)) { + BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state); + goto err; + } + ret = wolfSSL_accept(ssl); + if (unlikely(ret == 1)) { + DBG("TLS accept successful\n"); + tls_c->state = S_TLS_ESTABLISHED; + tls_log = cfg_get(tls, tls_cfg, log); + LOG(tls_log, "tls_accept: new connection from %s:%d using %s %s %d\n", + ip_addr2a(&c->rcv.src_ip), c->rcv.src_port, + SSL_get_cipher_version(ssl), wolfSSL_get_cipher_name(ssl), + SSL_get_cipher_bits(ssl, 0) + ); + LOG(tls_log, "tls_accept: local socket: %s:%d\n", + ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port + ); + cert = wolfSSL_get_peer_certificate(ssl); + if (cert != 0) { + tls_dump_cert_info("tls_accept: client certificate", cert); + if (wolfSSL_get_verify_result(ssl) != X509_V_OK) { + LOG(tls_log, "WARNING: tls_accept: client certificate " + "verification failed!!!\n"); + tls_dump_verification_failure(wolfSSL_get_verify_result(ssl)); + } + X509_free(cert); + } else { + LOG(tls_log, "tls_accept: client did not present a certificate\n"); + } + } else { /* ret == 0 or < 0 */ + *error = wolfSSL_get_error(ssl, ret); + } + return ret; +err: + /* internal non openssl related errors */ + return -2; +} + + +/** wrapper around SSL_connect, using SSL return convention. + * It will also log critical errors and certificate debugging info. + * @param c - tcp connection with tls (extra_data must be a filled + * tcp_extra_data structure). The state must be S_TLS_CONNECTING. + * @param error set to the error reason (SSL_ERROR_*). + * Note that it can be SSL_ERROR_NONE while the return is < 0 + * ("internal" error, not at the SSL level, see below). + * @return >=1 on success, 0 and <0 on error. 0 means the underlying SSL + * connection was closed/shutdown. < 0 is returned for any + * SSL_ERROR (including WANT_READ or WANT_WRITE), but also + * for internal non SSL related errors (in this case -2 is + * returned and error==SSL_ERROR_NONE). + * + */ +int tls_connect(struct tcp_connection *c, int* error) +{ + SSL *ssl; + int ret; + X509* cert; + struct tls_extra_data* tls_c; + int tls_log; + + *error = SSL_ERROR_NONE; + tls_c=(struct tls_extra_data*)c->extra_data; + ssl=tls_c->ssl; + + if (unlikely(tls_c->state != S_TLS_CONNECTING)) { + BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state); + goto err; + } + ret = wolfSSL_connect(ssl); + if (unlikely(ret == 1)) { + DBG("TLS connect successful\n"); + tls_c->state = S_TLS_ESTABLISHED; + tls_log = cfg_get(tls, tls_cfg, log); + LOG(tls_log, "tls_connect: new connection to %s:%d using %s %s %d\n", + ip_addr2a(&c->rcv.src_ip), c->rcv.src_port, + SSL_get_cipher_version(ssl), wolfSSL_get_cipher_name(ssl), + SSL_get_cipher_bits(ssl, 0) + ); + LOG(tls_log, "tls_connect: sending socket: %s:%d \n", + ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port + ); + cert = wolfSSL_get_peer_certificate(ssl); + if (cert != 0) { + tls_dump_cert_info("tls_connect: server certificate", cert); + if (wolfSSL_get_verify_result(ssl) != X509_V_OK) { + LOG(tls_log, "WARNING: tls_connect: server certificate " + "verification failed!!!\n"); + tls_dump_verification_failure(wolfSSL_get_verify_result(ssl)); + } + X509_free(cert); + } else { + /* this should not happen, servers always present a cert */ + LOG(tls_log, "tls_connect: server did not " + "present a certificate\n"); + } + tls_run_event_routes(c); + } else { /* 0 or < 0 */ + *error = wolfSSL_get_error(ssl, ret); + } + return ret; +err: + /* internal non openssl related errors */ + return -2; +} + + +/* + * wrapper around SSL_shutdown, returns -1 on error, 0 on success. + */ +static int tls_shutdown(struct tcp_connection *c) +{ + int ret, err, ssl_err; + struct tls_extra_data* tls_c; + SSL *ssl; + + tls_c=(struct tls_extra_data*)c->extra_data; + if (unlikely(tls_c == 0 || tls_c->ssl == 0)) { + ERR("No SSL data to perform tls_shutdown\n"); + return -1; + } + ssl = tls_c->ssl; + /* it doesn't make sense to try a TLS level shutdown + * if the connection is not fully initialized */ + if (unlikely(tls_c->state != S_TLS_ESTABLISHED)) + return 0; + if (unlikely(LOW_MEM_CONNECTED_TEST())){ + ERR("tls: ssl bug #1491 workaround: not enough memory for safe" + " operation: shm=%lu threshold2=%d\n", shm_available_safe(), + cfg_get(tls, tls_cfg, low_mem_threshold2)); + goto err; + } + + ret = wolfSSL_shutdown(ssl); + if (ret == 1) { + DBG("TLS shutdown successful\n"); + return 0; + } else if (ret == 0) { + DBG("First phase of 2-way handshake completed succesfuly\n"); + return 0; + } else { + err = wolfSSL_get_error(ssl, ret); + switch (err) { + case SSL_ERROR_ZERO_RETURN: + DBG("TLS shutdown failed cleanly\n"); + goto err; + + case SSL_ERROR_WANT_READ: + DBG("Need to get more data to finish TLS shutdown\n"); + break; + + case SSL_ERROR_WANT_WRITE: + DBG("Need to send more data to finish TLS shutdown\n"); + break; + + case SSL_ERROR_WANT_CONNECT: + DBG("Need to retry connect\n"); + break; + + case SSL_ERROR_WANT_ACCEPT: + DBG("Need to retry accept\n"); + break; + + case SSL_ERROR_WANT_X509_LOOKUP: + DBG("Application callback asked to be called again\n"); + break; + + case SSL_ERROR_SYSCALL: + TLS_ERR_RET(ssl_err, "TLS shutdown"); + if (!ssl_err) { + if (ret == 0) { + WARN("Unexpected EOF occurred while performing TLS shutdown\n"); + } else { + ERR("IO error: (%d) %s\n", errno, strerror(errno)); + } + } + goto err; + + case SSL_ERROR_SSL: + default: + TLS_ERR("SSL error:"); + goto err; + } + } + + return 0; +err: + return -1; +} + + + +/** init tls specific data in a tcp connection. + * Called when a new tcp connection is accepted or connected. + * It completes the tcp connection initialisation by setting the tls + * specific parts. + * Note that ssl context creation and other expensive operation are left + * out (they are delayed until the first read/write). + * No locking is needed (when the connection is created no other process + * can access it). + * @param c - tcp connection. + * @param sock - socket (unused for now). + * @return 0 on success, < 0 on error. + */ +int tls_h_tcpconn_init_f(struct tcp_connection *c, int sock) +{ + c->type = PROTO_TLS; + c->rcv.proto = PROTO_TLS; + c->timeout = get_ticks_raw() + cfg_get(tls, tls_cfg, con_lifetime); + c->lifetime = cfg_get(tls, tls_cfg, con_lifetime); + c->extra_data = 0; + return 0; +} + + +/** clean the extra data upon connection shut down. + */ +void tls_h_tcpconn_clean_f(struct tcp_connection *c) +{ + struct tls_extra_data* extra; + /* + * runs within global tcp lock + */ + if ((c->type != PROTO_TLS) && (c->type != PROTO_WSS)) { + BUG("Bad connection structure\n"); + abort(); + } + if (c->extra_data) { + extra = (struct tls_extra_data*)c->extra_data; + wolfSSL_free(extra->ssl); + atomic_dec(&extra->cfg->ref_count); + if (extra->ct_wq) + tls_ct_wq_free(&extra->ct_wq); + if (extra->enc_rd_buf) { + shm_free(extra->enc_rd_buf); + extra->enc_rd_buf = 0; + } + shm_free(c->extra_data); + c->extra_data = 0; + } +} + + +/** perform one-way shutdown, do not wait for notify from the remote peer. + */ +void tls_h_tcpconn_close_f(struct tcp_connection *c, int fd) +{ + unsigned char wr_buf[TLS_WR_MBUF_SZ]; + struct tls_mbuf rd, wr; + + /* + * runs either within global tcp lock or after the connection has + * been "detached" and is unreachable from any other process. + * Unfortunately when called via + * tcpconn_put_destroy()+tcpconn_close_main_fd() the connection might + * still be in a writer, so in this case locking is needed. + */ + DBG("Closing SSL connection %p\n", c->extra_data); + if (unlikely(cfg_get(tls, tls_cfg, send_close_notify) && c->extra_data)) { + lock_get(&c->write_lock); + if (unlikely(c->extra_data == 0)) { + /* changed in the meanwhile */ + lock_release(&c->write_lock); + return; + } + tls_mbuf_init(&rd, 0, 0); /* no read */ + tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf)); + if (tls_set_mbufs(c, &rd, &wr)==0) { + tls_shutdown(c); /* shutdown only on successful set fd */ + /* write as much as possible and update wr. + * Since this is a close, we don't want to queue the write + * (if it can't write immediately, just fail silently) + */ + if (wr.used) + _tcpconn_write_nb(fd, c, (char*)wr.buf, wr.used); + /* we don't bother reading anything (we don't want to wait + on close) */ + } + lock_release(&c->write_lock); + } +} + + + +/* generic tcpconn_{do,1st}_send() function pointer type */ +typedef int (*tcp_low_level_send_t)(int fd, struct tcp_connection *c, + char* buf, unsigned len, + snd_flags_t send_flags, + long* resp, int locked); + + + +/** tls encrypt before sending function. + * It is a callback that will be called by the tcp code, before a send + * on TLS would be attempted. It should replace the input buffer with a + * new static buffer containing the TLS processed data. + * If the input buffer could not be fully encoded (e.g. run out of space + * in the internal static buffer), it should set rest_buf and rest_len to + * the remaining part, so that it could be called again once the output has + * been used (sent). The send_flags used are also passed and they can be + * changed (e.g. to disallow a close() after a partial encode). + * WARNING: it must always be called with c->write_lock held! + * @param c - tcp connection + * @param pbuf - pointer to buffer (value/result, on success it will be + * replaced with a static buffer). + * @param plen - pointer to buffer size (value/result, on success it will be + * replaced with the size of the replacement buffer. + * @param rest_buf - (result) should be filled with a pointer to the + * remaining unencoded part of the original buffer if any, + * 0 otherwise. + * @param rest_len - (result) should be filled with the length of the + * remaining unencoded part of the original buffer (0 if + * the original buffer was fully encoded). + * @param send_flags - pointer to the send_flags that will be used for sending + * the message. + * @return *plen on success (>=0), < 0 on error. + */ +int tls_h_encode_f(struct tcp_connection *c, + const char** pbuf, unsigned int* plen, + const char** rest_buf, unsigned int* rest_len, + snd_flags_t* send_flags) +{ + int n, offs; + SSL* ssl = NULL; + struct tls_extra_data* tls_c; + static unsigned char wr_buf[TLS_WR_MBUF_SZ]; + struct tls_mbuf rd, wr; + int ssl_error; + char* err_src; + char ip_buf[64]; + const char* buf; + unsigned int len; + int x; + + buf = *pbuf; + len = *plen; + *rest_buf = 0; + *rest_len = 0; + TLS_WR_TRACE("(%p, %p, %d, ... 0x%0x) start (%s:%d* -> %s)\n", + c, buf, len, send_flags->f, + ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port, + su2a(&c->rcv.src_su, sizeof(c->rcv.src_su))); + n = 0; + offs = 0; + ssl_error = SSL_ERROR_NONE; + err_src = "TLS write:"; + if (unlikely(tls_fix_connection_unsafe(c) < 0)) { + /* c->extra_data might be null => exit immediately */ + TLS_WR_TRACE("(%p) end: tls_fix_connection_unsafe failed =>" + " immediate error exit\n", c); + return -1; + } + tls_c = (struct tls_extra_data*)c->extra_data; + ssl = tls_c->ssl; + tls_mbuf_init(&rd, 0, 0); /* no read */ + tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf)); + /* clear text already queued (WANTS_READ) queue directly*/ + if (unlikely(tls_write_wants_read(tls_c))) { + TLS_WR_TRACE("(%p) WANTS_READ queue present => queueing" + " (%d bytes, %p + %d)\n", c, len - offs, buf, offs); + if (unlikely(tls_ct_wq_add(&tls_c->ct_wq, buf+offs, len -offs) < 0)) { + ERR("ct write buffer full for %p (%d bytes)\n", + c, tls_c->ct_wq?tls_c->ct_wq->queued:0); + goto error_wq_full; + } + /* buffer queued for a future send attempt, after first reading + * some data (key exchange) => don't allow immediate closing of + * the connection */ + send_flags->f &= ~SND_F_CON_CLOSE; + goto end; + } + if (unlikely(tls_set_mbufs(c, &rd, &wr) < 0)) { + ERR("tls_set_mbufs failed\n"); + goto error; + } +redo_wr: + if (unlikely(tls_c->state == S_TLS_CONNECTING)) { + n = tls_connect(c, &ssl_error); + TLS_WR_TRACE("(%p) tls_connect() => %d (err=%d)\n", c, n, ssl_error); + if (unlikely(n>=1)) { + n = wolfSSL_write(ssl, buf + offs, len - offs); + if (unlikely(n <= 0)) + ssl_error = wolfSSL_get_error(ssl, n); + } else { + /* tls_connect failed/needs more IO */ + if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) + goto error; + err_src = "TLS connect:"; + } + } else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) { + n = tls_accept(c, &ssl_error); + TLS_WR_TRACE("(%p) tls_accept() => %d (err=%d)\n", c, n, ssl_error); + if (unlikely(n>=1)) { + n = wolfSSL_write(ssl, buf + offs, len - offs); + if (unlikely(n <= 0)) + ssl_error = wolfSSL_get_error(ssl, n); + } else { + /* tls_accept failed/needs more IO */ + if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) + goto error; + err_src = "TLS accept:"; + } + } else { + n = SSL_write(ssl, buf + offs, len - offs); + if (unlikely(n <= 0)) + ssl_error = SSL_get_error(ssl, n); + } + TLS_WR_TRACE("(%p) SSL_write(%p + %d, %d) => %d (err=%d)\n", + c, buf, offs, len - offs, n, ssl_error); + /* check for possible ssl errors */ + if (unlikely(n <= 0)){ + switch(ssl_error) { + case SSL_ERROR_NONE: + BUG("unexpected SSL_ERROR_NONE for n=%d\n", n); + goto error; + break; + case SSL_ERROR_ZERO_RETURN: + /* SSL EOF */ + ERR("ssl level EOF\n"); + goto ssl_eof; + case SSL_ERROR_WANT_READ: + /* queue write buffer */ + TLS_WR_TRACE("(%p) SSL_ERROR_WANT_READ => queueing for read" + " (%p + %d, %d)\n", c, buf, offs, len -offs); + if (unlikely(tls_ct_wq_add(&tls_c->ct_wq, buf+offs, len -offs) + < 0)) { + ERR("ct write buffer full (%d bytes)\n", + tls_c->ct_wq?tls_c->ct_wq->queued:0); + goto error_wq_full; + } + tls_c->flags |= F_TLS_CON_WR_WANTS_RD; + /* buffer queued for a future send attempt, after first + * reading some data (key exchange) => don't allow immediate + * closing of the connection */ + send_flags->f &= ~SND_F_CON_CLOSE; + break; /* or goto end */ + case SSL_ERROR_WANT_WRITE: + if (unlikely(offs == 0)) { + /* error, no record fits in the buffer or + * no partial write enabled and buffer to small to fit + * all the records */ + BUG("write buffer too small (%d/%d bytes)\n", + wr.used, wr.size); + goto bug; + } else { + /* offs != 0 => something was "written" */ + *rest_buf = buf + offs; + *rest_len = len - offs; + /* this function should be called again => disallow + * immediate closing of the connection */ + send_flags->f &= ~SND_F_CON_CLOSE; + TLS_WR_TRACE("(%p) SSL_ERROR_WANT_WRITE partial write" + " (written %p , %d, rest_buf=%p" + " rest_len=%d))\n", c, buf, offs, + *rest_buf, *rest_len); + } + break; /* or goto end */ + case SSL_ERROR_SSL: + /* protocol level error */ + ERR("protocol level error\n"); + TLS_ERR_SSL(err_src, ssl); + memset(ip_buf, 0, sizeof(buf)); + ip_addr2sbuf(&(c->rcv.src_ip), ip_buf, sizeof(ip_buf)); + ERR("source IP: %s\n", ip_buf); + memset(ip_buf, 0, sizeof(buf)); + ip_addr2sbuf(&(c->rcv.dst_ip), ip_buf, sizeof(ip_buf)); + ERR("destination IP: %s\n", ip_buf); + + goto error; + + case SSL_ERROR_WANT_CONNECT: + /* only if the underlying BIO is not yet connected + * and the call would block in connect(). + * (not possible in our case) */ + BUG("unexpected SSL_ERROR_WANT_CONNECT\n"); + break; + case SSL_ERROR_WANT_ACCEPT: + /* only if the underlying BIO is not yet connected + * and call would block in accept() + * (not possible in our case) */ + BUG("unexpected SSL_ERROR_WANT_ACCEPT\n"); + break; + + case SSL_ERROR_WANT_X509_LOOKUP: + /* can only appear on client application and it indicates that + * an installed client cert. callback should be called again + * (it returned < 0 indicated that it wants to be called + * later). Not possible in our case */ + BUG("unsupported SSL_ERROR_WANT_X509_LOOKUP"); + goto bug; + case SSL_ERROR_SYSCALL: + TLS_ERR_RET(x, err_src); + if (!x) { + if (n == 0) { + WARN("Unexpected EOF\n"); + } else + /* should never happen */ + BUG("IO error (%d) %s\n", errno, strerror(errno)); + } + goto error; + default: + TLS_ERR_SSL(err_src, ssl); + BUG("unexpected SSL error %d\n", ssl_error); + goto bug; + } + } else if (unlikely(n < (len - offs))) { + /* partial ssl write (possible if SSL_MODE_ENABLE_PARTIAL_WRITE) => + * retry with the rest */ + TLS_WR_TRACE("(%p) partial write (%d < %d, offset %d), retry\n", + c, n, len - offs, offs); + offs += n; + goto redo_wr; + } + tls_set_mbufs(c, 0, 0); +end: + *pbuf = (const char*)wr.buf; + *plen = wr.used; + TLS_WR_TRACE("(%p) end (offs %d, rest_buf=%p rest_len=%d 0x%0x) => %d \n", + c, offs, *rest_buf, *rest_len, send_flags->f, *plen); + return *plen; +error: +/*error_send:*/ +error_wq_full: +bug: + tls_set_mbufs(c, 0, 0); + TLS_WR_TRACE("(%p) end error (offs %d, %d encoded) => -1\n", + c, offs, wr.used); + return -1; +ssl_eof: + c->state = S_CONN_EOF; + c->flags |= F_CONN_FORCE_EOF; + *pbuf = (const char*)wr.buf; + *plen = wr.used; + DBG("TLS connection has been closed\n"); + TLS_WR_TRACE("(%p) end EOF (offs %d) => (%d\n", + c, offs, *plen); + return *plen; +} + + + +/** tls read. + * Each modification of ssl data structures has to be protected, another process + * might ask for the same connection and attempt write to it which would + * result in updating the ssl structures. + * WARNING: must be called whic c->write_lock _unlocked_. + * @param c - tcp connection pointer. The following flags might be set: + * @param flags - value/result: + * input: RD_CONN_FORCE_EOF - force EOF after the first + * successful read (bytes_read >=0 ) + * output: RD_CONN_SHORT_READ if the read exhausted + * all the bytes in the socket read buffer. + * RD_CONN_EOF if EOF detected (0 bytes read) + * or forced via RD_CONN_FORCE_EOF. + * RD_CONN_REPEAT_READ if this function should + * be called again (e.g. has some data + * buffered internally that didn't fit in + * tcp_req). + * Note: RD_CONN_SHORT_READ & RD_CONN_EOF should be cleared + * before calling this function when there is new + * data (e.g. POLLIN), but not if the called is + * retried because of RD_CONN_REPEAT_READ and there + * is no information about the socket having more + * read data available. + * @return bytes decrypted on success, -1 on error (it also sets some + * tcp connection flags and might set c->state and r->error on + * EOF or error). + */ +int tls_h_read_f(struct tcp_connection* c, rd_conn_flags_t* flags) +{ + struct tcp_req* r; + int bytes_free, bytes_read, read_size, ssl_error, ssl_read; + SSL* ssl; + unsigned char rd_buf[TLS_RD_MBUF_SZ]; + unsigned char wr_buf[TLS_WR_MBUF_SZ]; + struct tls_mbuf rd, wr; + struct tls_extra_data* tls_c; + struct tls_rd_buf* enc_rd_buf; + int n, flush_flags; + char* err_src; + char ip_buf[64]; + int x; + int tls_dbg; + + ssl = NULL; + TLS_RD_TRACE("(%p, %p (%d)) start (%s -> %s:%d*)\n", + c, flags, *flags, + su2a(&c->rcv.src_su, sizeof(c->rcv.src_su)), + ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port); + ssl_read = 0; + r = &c->req; + enc_rd_buf = 0; + *flags &= ~RD_CONN_REPEAT_READ; + if (unlikely(tls_fix_connection(c) < 0)) { + TLS_RD_TRACE("(%p, %p) end: tls_fix_connection failed =>" + " immediate error exit\n", c, flags); + return -1; + } + /* here it's safe to use c->extra_data in read-only mode. + * If it's != 0 is changed only on destroy. It's not possible to have + * parallel reads.*/ + tls_c = c->extra_data; + bytes_free = c->req.b_size - (unsigned int)(r->pos - r->buf); + if (unlikely(bytes_free <= 0)) { + ERR("Buffer overrun, dropping\n"); + r->error = TCP_REQ_OVERRUN; + return -1; + } +redo_read: + /* if data queued from a previous read(), use it (don't perform + * a real read()). + */ + if (unlikely(tls_c->enc_rd_buf)) { + /* use queued data */ + /* safe to use without locks, because only read changes it and + * there can't be parallel reads on the same connection */ + enc_rd_buf = tls_c->enc_rd_buf; + tls_c->enc_rd_buf = 0; + TLS_RD_TRACE("(%p, %p) using queued data (%p: %p %d bytes)\n", c, + flags, enc_rd_buf, enc_rd_buf->buf + enc_rd_buf->pos, + enc_rd_buf->size - enc_rd_buf->pos); + tls_mbuf_init(&rd, enc_rd_buf->buf + enc_rd_buf->pos, + enc_rd_buf->size - enc_rd_buf->pos); + rd.used = enc_rd_buf->size - enc_rd_buf->pos; + } else { + /* if we were using using queued data before, free & reset the + the queued read data before performing the real read() */ + if (unlikely(enc_rd_buf)) { + TLS_RD_TRACE("(%p, %p) reset prev. used enc_rd_buf (%p)\n", c, + flags, enc_rd_buf); + shm_free(enc_rd_buf); + enc_rd_buf = 0; + } + /* real read() */ + tls_mbuf_init(&rd, rd_buf, sizeof(rd_buf)); + /* read() only if no previously detected EOF, or previous + * short read (which means the socket buffer was emptied) */ + if (likely(!(*flags & (RD_CONN_EOF|RD_CONN_SHORT_READ)))) { + /* don't read more then the free bytes in the tcp req buffer */ + read_size = MIN_unsigned(rd.size, bytes_free); + bytes_read = tcp_read_data(c->fd, c, (char*)rd.buf, read_size, + flags); + TLS_RD_TRACE("(%p, %p) tcp_read_data(..., %d, *%d) => %d bytes\n", + c, flags, read_size, *flags, bytes_read); + /* try SSL_read even on 0 bytes read, it might have + * internally buffered data */ + if (unlikely(bytes_read < 0)) { + goto error; + } + rd.used = bytes_read; + } + } + +continue_ssl_read: + tls_mbuf_init(&wr, wr_buf, sizeof(wr_buf)); + ssl_error = SSL_ERROR_NONE; + err_src = "TLS read:"; + /* we have to avoid to run in the same time + * with a tls_write because of the + * update bio stuff (we don't want a write + * stealing the wbio or rbio under us or vice versa) + * => lock on con->write_lock (ugly hack) */ + lock_get(&c->write_lock); + tls_set_mbufs(c, &rd, &wr); + ssl = tls_c->ssl; + n = 0; + if (unlikely(tls_write_wants_read(tls_c) && + !(*flags & RD_CONN_EOF))) { + n = tls_ct_wq_flush(c, &tls_c->ct_wq, &flush_flags, + &ssl_error); + TLS_RD_TRACE("(%p, %p) tls write on read (WRITE_WANTS_READ):" + " ct_wq_flush()=> %d (ff=%d ssl_error=%d))\n", + c, flags, n, flush_flags, ssl_error); + if (unlikely(n < 0 )) { + tls_set_mbufs(c, 0, 0); + lock_release(&c->write_lock); + ERR("write flush error (%d)\n", n); + goto error; + } + if (likely(flush_flags & F_BUFQ_EMPTY)) + tls_c->flags &= ~F_TLS_CON_WR_WANTS_RD; + if (unlikely(flush_flags & F_BUFQ_ERROR_FLUSH)) + err_src = "TLS write:"; + } + if (likely(ssl_error == SSL_ERROR_NONE)) { + if (unlikely(tls_c->state == S_TLS_CONNECTING)) { + n = tls_connect(c, &ssl_error); + TLS_RD_TRACE("(%p, %p) tls_connect() => %d (err=%d)\n", + c, flags, n, ssl_error); + if (unlikely(n>=1)) { + n = wolfSSL_read(ssl, r->pos, bytes_free); + } else { + /* tls_connect failed/needs more IO */ + if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) { + lock_release(&c->write_lock); + goto error; + } + err_src = "TLS connect:"; + goto ssl_read_skipped; + } + } else if (unlikely(tls_c->state == S_TLS_ACCEPTING)) { + n = tls_accept(c, &ssl_error); + TLS_RD_TRACE("(%p, %p) tls_accept() => %d (err=%d)\n", + c, flags, n, ssl_error); + if (unlikely(n>=1)) { + n = wolfSSL_read(ssl, r->pos, bytes_free); + } else { + /* tls_accept failed/needs more IO */ + if (unlikely(n < 0 && ssl_error == SSL_ERROR_NONE)) { + lock_release(&c->write_lock); + goto error; + } + err_src = "TLS accept:"; + goto ssl_read_skipped; + } + } else { + /* if bytes in then decrypt read buffer into tcpconn req. + * buffer */ + n = wolfSSL_read(ssl, r->pos, bytes_free); + } + /** handle SSL_read() return. + * There are 3 main cases, each with several sub-cases, depending + * on whether or not the output buffer was filled, if there + * is still unconsumed input data in the input buffer (rd) + * and if there is "cached" data in the internal openssl + * buffers. + * 0. error (n<=0): + * SSL_ERROR_WANT_READ - input data fully + * consumed, no more returnable cached data inside openssl + * => exit. + * SSL_ERROR_WANT_WRITE - should never happen (the write + * buffer is big enough to handle any re-negociation). + * SSL_ERROR_ZERO_RETURN - ssl level shutdown => exit. + * other errors are unexpected. + * 1. output buffer filled (n == bytes_free): + * 1i. - still unconsumed input, nothing buffered by openssl + * 1ip. - unconsumed input + buffered data by openssl (pending + * on the next SSL_read). + * 1p. - completely consumed input, buffered data internally + * by openssl (pending). + * Likely to happen, about the only case when + * SSL_pending() could be used (but only if readahead=0). + * 1f. - consumed input, no buffered data. + * 2. output buffer not fully filled (n < bytes_free): + * 2i. - still unconsumed input, nothing buffered by openssl. + * This can appear if SSL readahead is 0 (SSL_read() + * tries to get only 1 record from the input). + * 2ip. - unconsumed input and buffered data by openssl. + * Unlikely to happen (e.g. readahead is 1, more + * records are buffered internally by openssl, but + * there was not enough space for buffering the whole + * input). + * 2p - consumed input, but buffered data by openssl. + * It happens especially when readahead is 1. + * 2f. - consumed input, no buffered data. + * + * One should repeat SSL_read() until and error is detected + * (0*) or the input and internal ssl buffers are fully consumed + * (1f or 2f). However in general is not possible to see if + * SSL_read() could return more data. SSL_pending() has very + * limited usability (basically it would return !=0 only if there + * was no enough space in the output buffer and only if this did + * not happen at a record boundary). + * The solution is to repeat SSL_read() until error or until + * the output buffer is filled (0* or 1*). + * In the later case, this whole function should be called again + * once there is more output space (set RD_CONN_REPEAT_READ). + */ + + if (unlikely(tls_c->flags & F_TLS_CON_RENEGOTIATION)) { + /* Fix CVE-2009-3555 - disable renegotiation if started by client + * - simulate SSL EOF to force close connection*/ + tls_dbg = cfg_get(tls, tls_cfg, debug); + LOG(tls_dbg, "Reading on a renegotiation of connection (n:%d) (%d)\n", + n, wolfSSL_get_error(ssl, n)); + err_src = "TLS R-N read:"; + ssl_error = SSL_ERROR_ZERO_RETURN; + } else { + if (unlikely(n <= 0)) { + ssl_error = wolfSSL_get_error(ssl, n); + err_src = "TLS read:"; + /* errors handled below, outside the lock */ + } else { + ssl_error = SSL_ERROR_NONE; + r->pos += n; + ssl_read += n; + bytes_free -=n; + } + } + TLS_RD_TRACE("(%p, %p) SSL_read() => %d (err=%d) ssl_read=%d" + " *flags=%d tls_c->flags=%d\n", + c, flags, n, ssl_error, ssl_read, *flags, + tls_c->flags); +ssl_read_skipped: + ; + } + if (unlikely(wr.used != 0 && ssl_error != SSL_ERROR_ZERO_RETURN)) { + TLS_RD_TRACE("(%p, %p) tcpconn_send_unsafe %d bytes\n", + c, flags, wr.used); + /* something was written and it's not ssl EOF*/ + if (unlikely(tcpconn_send_unsafe(c->fd, c, (char*)wr.buf, + wr.used, c->send_flags) < 0)) { + tls_set_mbufs(c, 0, 0); + lock_release(&c->write_lock); + TLS_RD_TRACE("(%p, %p) tcpconn_send_unsafe error\n", c, flags); + goto error_send; + } + } + /* quickly catch bugs: segfault if accessed and not set */ + tls_set_mbufs(c, 0, 0); + lock_release(&c->write_lock); + switch(ssl_error) { + case SSL_ERROR_NONE: + if (unlikely(n < 0)) { + BUG("unexpected SSL_ERROR_NONE for n=%d\n", n); + goto error; + } + break; + case SSL_ERROR_ZERO_RETURN: + /* SSL EOF */ + TLS_RD_TRACE("(%p, %p) SSL EOF (fd=%d)\n", c, flags, c->fd); + goto ssl_eof; + case SSL_ERROR_WANT_READ: + TLS_RD_TRACE("(%p, %p) SSL_ERROR_WANT_READ *flags=%d\n", + c, flags, *flags); + /* needs to read more data */ + if (unlikely(rd.pos != rd.used)) { + /* data still in the read buffer */ + BUG("SSL_ERROR_WANT_READ but data still in" + " the rbio (%p, %d bytes at %d)\n", rd.buf, + rd.used - rd.pos, rd.pos); + goto bug; + } + if (unlikely((*flags & (RD_CONN_EOF | RD_CONN_SHORT_READ)) == 0) && + bytes_free){ + /* there might still be data to read and there is space + * to decrypt it in tcp_req (no byte has been written into + * tcp_req in this case) */ + TLS_RD_TRACE("(%p, %p) redo read *flags=%d bytes_free=%d\n", + c, flags, *flags, bytes_free); + goto redo_read; + } + goto end; /* no more data to read */ + case SSL_ERROR_WANT_WRITE: + if (wr.used) { + /* something was written => buffer not big enough to hold + * everything => reset buffer & retry (the tcp_write already + * happened if we are here) */ + TLS_RD_TRACE("(%p) SSL_ERROR_WANT_WRITE partial write" + " (written %d), retrying\n", c, wr.used); + goto continue_ssl_read; + } + /* else write buffer too small, nothing written */ + BUG("write buffer too small (%d/%d bytes)\n", + wr.used, wr.size); + goto bug; + case SSL_ERROR_SSL: + /* protocol level error */ + ERR("protocol level error\n"); + TLS_ERR_SSL(err_src, ssl); + memset(ip_buf, 0, sizeof(ip_buf)); + ip_addr2sbuf(&(c->rcv.src_ip), ip_buf, sizeof(ip_buf)); + ERR("src addr: %s:%d\n", ip_buf, c->rcv.src_port); + memset(ip_buf, 0, sizeof(ip_buf)); + ip_addr2sbuf(&(c->rcv.dst_ip), ip_buf, sizeof(ip_buf)); + ERR("dst addr: %s:%d\n", ip_buf, c->rcv.dst_port); + + goto error; + + case SSL_ERROR_WANT_CONNECT: + /* only if the underlying BIO is not yet connected + * and the call would block in connect(). + * (not possible in our case) */ + BUG("unexpected SSL_ERROR_WANT_CONNECT\n"); + goto bug; + case SSL_ERROR_WANT_ACCEPT: + /* only if the underlying BIO is not yet connected + * and call would block in accept() + * (not possible in our case) */ + BUG("unexpected SSL_ERROR_WANT_ACCEPT\n"); + goto bug; + + case SSL_ERROR_WANT_X509_LOOKUP: + /* can only appear on client application and it indicates that + * an installed client cert. callback should be called again + * (it returned < 0 indicated that it wants to be called + * later). Not possible in our case */ + BUG("unsupported SSL_ERROR_WANT_X509_LOOKUP"); + goto bug; + case SSL_ERROR_SYSCALL: + TLS_ERR_RET(x, err_src); + if (!x) { + if (n == 0) { + WARN("Unexpected EOF\n"); + } else + /* should never happen */ + BUG("IO error (%d) %s\n", errno, strerror(errno)); + } + goto error; + default: + TLS_ERR_SSL(err_src, ssl); + BUG("unexpected SSL error %d\n", ssl_error); + goto bug; + } + if (unlikely(n < 0)) { + /* here n should always be >= 0 */ + BUG("unexpected value (n = %d)\n", n); + goto bug; + } + if (unlikely(rd.pos != rd.used)) { + /* encrypted data still in the read buffer (SSL_read() did not + * consume all of it) */ + if (unlikely(n < 0)) + /* here n should always be >= 0 */ + BUG("unexpected value (n = %d)\n", n); + else { + if (unlikely(bytes_free != 0)) { + /* 2i or 2ip: unconsumed input and output buffer not filled => + * retry ssl read (SSL_read() will read will stop at + * record boundaries, unless readahead==1). + * No tcp_read() is attempted, since that would reset the + * current no-yet-consumed input data. + */ + TLS_RD_TRACE("(%p, %p) input not fully consumed =>" + " retry SSL_read" + " (pos: %d, remaining %d, output free %d)\n", + c, flags, rd.pos, rd.used-rd.pos, bytes_free); + goto continue_ssl_read; + } + /* 1i or 1ip: bytes_free == 0 + * (unconsumed input, but filled output buffer) => + * queue read data, and exit asking for repeating the call + * once there is some space in the output buffer. + */ + if (likely(!enc_rd_buf)) { + TLS_RD_TRACE("(%p, %p) creating enc_rd_buf (for %d bytes)\n", + c, flags, rd.used - rd.pos); + enc_rd_buf = shm_malloc(sizeof(*enc_rd_buf) - + sizeof(enc_rd_buf->buf) + + rd.used - rd.pos); + if (unlikely(enc_rd_buf == 0)) { + ERR("memory allocation error (%d bytes requested)\n", + (int)(sizeof(*enc_rd_buf) + sizeof(enc_rd_buf->buf) + + rd.used - rd.pos)); + goto error; + } + enc_rd_buf->pos = 0; + enc_rd_buf->size = rd.used - rd.pos; + memcpy(enc_rd_buf->buf, rd.buf + rd.pos, + enc_rd_buf->size); + } else if ((enc_rd_buf->buf + enc_rd_buf->pos) == rd.buf) { + TLS_RD_TRACE("(%p, %p) enc_rd_buf already in use," + " updating pos %d\n", + c, flags, enc_rd_buf->pos); + enc_rd_buf->pos += rd.pos; + } else { + BUG("enc_rd_buf->buf = %p, pos = %d, rd_buf.buf = %p\n", + enc_rd_buf->buf, enc_rd_buf->pos, rd.buf); + goto bug; + } + if (unlikely(tls_c->enc_rd_buf)) + BUG("tls_c->enc_rd_buf!=0 (%p)\n", tls_c->enc_rd_buf); + /* there can't be 2 reads in parallel, so no locking is needed + * here */ + tls_c->enc_rd_buf = enc_rd_buf; + enc_rd_buf = 0; + *flags |= RD_CONN_REPEAT_READ; + } + } else if (bytes_free != 0) { + /* 2f or 2p: input fully consumed (rd.pos == rd.used), + * output buffer not filled, still possible to have pending + * data buffered by openssl */ + if (unlikely((*flags & (RD_CONN_EOF|RD_CONN_SHORT_READ)) == 0)) { + /* still space in the tcp unenc. req. buffer, no SSL_read error, + * not a short read and not an EOF (possible more data in + * the socket buffer) => try a new tcp read too */ + TLS_RD_TRACE("(%p, %p) retry read (still space and no short" + " tcp read: %d)\n", c, flags, *flags); + goto redo_read; + } else { + /* don't tcp_read() anymore, but there might still be data + * buffered internally by openssl (e.g. if readahead==1) => + * retry SSL_read() with the current full input buffer + * (if no more internally SSL buffered data => WANT_READ => exit). + */ + TLS_RD_TRACE("(%p, %p) retry SSL_read only (*flags =%d)\n", + c, flags, *flags); + goto continue_ssl_read; + } + } else { + /* 1p or 1f: rd.pos == rd.used && bytes_free == 0 + * (input fully consumed && output buffer filled) */ + /* ask for a repeat when there is more buffer space + * (there is no definitive way to know if ssl doesn't still have + * some internal buffered data until we get WANT_READ, see + * SSL_read() comment above) */ + *flags |= RD_CONN_REPEAT_READ; + TLS_RD_TRACE("(%p, %p) output filled, exit asking to be called again" + " (*flags =%d)\n", c, flags, *flags); + } + +end: + if (enc_rd_buf) + shm_free(enc_rd_buf); + TLS_RD_TRACE("(%p, %p) end => %d (*flags=%d)\n", + c, flags, ssl_read, *flags); + return ssl_read; +ssl_eof: + /* behave as an EOF would have been received at the tcp level */ + if (enc_rd_buf) + shm_free(enc_rd_buf); + c->state = S_CONN_EOF; + *flags |= RD_CONN_EOF; + TLS_RD_TRACE("(%p, %p) end EOF => %d (*flags=%d)\n", + c, flags, ssl_read, *flags); + return ssl_read; +error_send: +error: +bug: + if (enc_rd_buf) + shm_free(enc_rd_buf); + r->error=TCP_READ_ERROR; + TLS_RD_TRACE("(%p, %p) end error => %d (*flags=%d)\n", + c, flags, ssl_read, *flags); + return -1; +} + + +static int _tls_evrt_connection_out = -1; /* default disabled */ +str sr_tls_event_callback = STR_NULL; + +/*! + * lookup tls event routes + */ +void tls_lookup_event_routes(void) +{ + _tls_evrt_connection_out=route_lookup(&event_rt, "tls:connection-out"); + if (_tls_evrt_connection_out>=0 && event_rt.rlist[_tls_evrt_connection_out]==0) + _tls_evrt_connection_out=-1; /* disable */ + if(_tls_evrt_connection_out!=-1) + forward_set_send_info(1); +} + +/** + * + */ +int tls_run_event_routes(struct tcp_connection *c) +{ + int backup_rt; + struct run_act_ctx ctx; + sip_msg_t *fmsg = NULL; + str evname = str_init("tls:connection-out"); + sr_kemi_eng_t *keng = NULL; + + if(_tls_evrt_connection_out<0 && sr_tls_event_callback.len<=0) + return 0; + + if(p_onsend==0 || p_onsend->msg==0) + return 0; + + if(faked_msg_init()<0) + return -1; + fmsg = faked_msg_next(); + + backup_rt = get_route_type(); + set_route_type(LOCAL_ROUTE); + init_run_actions_ctx(&ctx); + tls_set_pv_con(c); + if(_tls_evrt_connection_out>=0) { + run_top_route(event_rt.rlist[_tls_evrt_connection_out], fmsg, 0); + } else { + keng = sr_kemi_eng_get(); + if(keng!=NULL) { + if(sr_kemi_ctx_route(keng, &ctx, fmsg, EVENT_ROUTE, + &sr_tls_event_callback, &evname)<0) { + LM_ERR("error running event route kemi callback\n"); + return -1; + } + } + } + /* drop() executed in the event route - set nosend flag */ + if (unlikely(ctx.run_flags&DROP_R_F)) { + c->flags|=F_CONN_NOSEND; + } + tls_set_pv_con(0); + set_route_type(backup_rt); + return 0; +} diff --git a/src/modules/tls_wolfssl/tls_server.h b/src/modules/tls_wolfssl/tls_server.h new file mode 100644 index 00000000000..5e7784c4d03 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_server.h @@ -0,0 +1,101 @@ +/* + * TLS module - main server part + * + * Copyright (C) 2005-2010 iptelorg GmbH + * + * This file is part of Kamailio, a free SIP server. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** main tls part (implements the tls hooks that are called from the tcp code). + * @file tls_server.h + * @ingroup tls + * Module: @ref tls + */ + + +#ifndef _TLS_SERVER_H +#define _TLS_SERVER_H + +#include +#include "../../core/tcp_conn.h" +#include "../../core/tcp_read.h" +#include "tls_domain.h" +#include "tls_ct_wrq.h" + +typedef enum tls_conn_states { + S_TLS_NONE = 0, + S_TLS_ACCEPTING, + S_TLS_CONNECTING, + S_TLS_ESTABLISHED + } tls_conn_states_t; + +typedef struct tls_rd_buf { + unsigned int pos; /* current position */ + unsigned int size; /* total size (buf) */ + unsigned char buf[1]; +} tls_rd_buf_t; + +/* tls conn flags */ +#define F_TLS_CON_WR_WANTS_RD 1 /* write wants read */ +#define F_TLS_CON_HANDSHAKED 2 /* connection is handshaked */ +#define F_TLS_CON_RENEGOTIATION 4 /* renegotiation by clinet */ + +typedef struct tls_extra_data { + tls_domains_cfg_t* cfg; /* Configuration used for this connection */ + SSL* ssl; /* SSL context used for the connection */ + BIO* rwbio; /* bio used for read/write + * (openssl code might add buffering BIOs so + * it's better to remember our original BIO) */ + tls_ct_q* ct_wq; + struct tls_rd_buf* enc_rd_buf; + unsigned int flags; + enum tls_conn_states state; +} tls_extra_data_t; + + +/* return true if write wants read */ +#define tls_write_wants_read(tls_ed) (tls_ed->flags & F_TLS_CON_WR_WANTS_RD) + + +/* + * Called when new tcp connection is accepted + */ +int tls_h_tcpconn_init_f(struct tcp_connection *c, int sock); + +/* + * clean the extra data upon connection shut down + */ +void tls_h_tcpconn_clean_f(struct tcp_connection *c); + +/* + * shut down the TLS connection + */ +void tls_h_tcpconn_close_f(struct tcp_connection *c, int fd); + +int tls_h_encode_f(struct tcp_connection *c, const char ** pbuf, + unsigned int* plen, const char** rest_buf, unsigned int* rest_len, + snd_flags_t* send_flags) ; + +int tls_h_read_f(struct tcp_connection *c, rd_conn_flags_t* flags); + +int tls_h_fix_read_conn(struct tcp_connection *c); + +int tls_connect(struct tcp_connection *c, int* error); +int tls_accept(struct tcp_connection *c, int* error); + +void tls_lookup_event_routes(void); +int ksr_tls_set_connect_server_id(str *srvid); + +#endif /* _TLS_SERVER_H */ diff --git a/src/modules/tls_wolfssl/tls_util.c b/src/modules/tls_wolfssl/tls_util.c new file mode 100644 index 00000000000..b129900f05b --- /dev/null +++ b/src/modules/tls_wolfssl/tls_util.c @@ -0,0 +1,99 @@ +/* + * TLS module + * + * Copyright (C) 2005 iptelorg GmbH + * Copyright (C) 2013 Motorola Solutions, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! + * \file + * \brief Kamailio TLS support :: Common functions + * \ingroup tls + * Module: \ref tls + */ + + +#define _GNU_SOURCE 1 /* Needed for strndup */ + +#include +#include +#include "../../core/mem/shm_mem.h" +#include "../../core/globals.h" +#include "../../core/dprint.h" +#include "tls_mod.h" +#include "tls_util.h" + + +/* + * Make a shared memory copy of ASCII zero terminated string + * Return value: -1 on error + * 0 on success + */ +int shm_asciiz_dup(char** dest, char* val) +{ + char* ret; + int len; + + if (!val) { + *dest = NULL; + return 0; + } + + len = strlen(val); + ret = shm_malloc(len + 1); + if (!ret) { + ERR("No memory left\n"); + return -1; + } + memcpy(ret, val, len + 1); + *dest = ret; + return 0; +} + + +/* + * Delete old TLS configuration that is not needed anymore + */ +void collect_garbage(void) +{ + tls_domains_cfg_t *prev, *cur, *next; + + /* Make sure we do not run two garbage collectors + * at the same time + */ + lock_get(tls_domains_cfg_lock); + + /* Skip the current configuration, garbage starts + * with the 2nd element on the list + */ + prev = *tls_domains_cfg; + cur = (*tls_domains_cfg)->next; + + while(cur) { + next = cur->next; + if (atomic_get(&cur->ref_count) == 0) { + /* Not referenced by any existing connection */ + prev->next = cur->next; + tls_free_cfg(cur); + } else { + /* Only update prev if we didn't remove cur */ + prev = cur; + } + cur = next; + } + + lock_release(tls_domains_cfg_lock); +} + diff --git a/src/modules/tls_wolfssl/tls_util.h b/src/modules/tls_wolfssl/tls_util.h new file mode 100644 index 00000000000..86a09e1f79b --- /dev/null +++ b/src/modules/tls_wolfssl/tls_util.h @@ -0,0 +1,87 @@ +/* + * TLS module + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/*! + * \file + * \brief Kamailio TLS support :: Common functions + * \ingroup tls + * Module: \ref tls + */ + + +#ifndef _TLS_UTIL_H +#define _TLS_UTIL_H + +#include +#include + +#include "../../core/dprint.h" +#include "../../core/str.h" +#include "tls_domain.h" + +static inline int tls_err_ret(char *s, SSL* ssl, + tls_domains_cfg_t **tls_domains_cfg) +{ + long err; + int ret = 0; + const char *sn = NULL; + + if ((*tls_domains_cfg)->srv_default->ctx && + (*tls_domains_cfg)->srv_default->ctx[0]) + { + if(ssl) { + sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + } + while((err = ERR_get_error())) { + ret = 1; + ERR("%s%s (sni: %s)\n", s ? s : "", ERR_error_string(err, 0), + (sn) ? sn : "unknown"); + } + } + return ret; +} + +#define TLS_ERR_RET(r, s) \ +do { \ + (r) = tls_err_ret((s), NULL, tls_domains_cfg); \ +} while(0) + + +#define TLS_ERR(s) \ +do { \ + tls_err_ret((s), NULL, tls_domains_cfg); \ +} while(0) + +#define TLS_ERR_SSL(s, ssl) \ +do { \ + tls_err_ret((s), (ssl), tls_domains_cfg); \ +} while(0) + +/* + * Make a shared memory copy of ASCII zero terminated string + * Return value: -1 on error + * 0 on success + */ +int shm_asciiz_dup(char** dest, char* val); + + +/* + * Delete old TLS configuration that is not needed anymore + */ +void collect_garbage(void); + +#endif /* _TLS_UTIL_H */ diff --git a/src/modules/tls_wolfssl/tls_verify.c b/src/modules/tls_wolfssl/tls_verify.c new file mode 100644 index 00000000000..cea7fb3a946 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_verify.c @@ -0,0 +1,135 @@ +/* + * TLS module + * + * Copyright (C) 2005 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../../core/dprint.h" +#include "tls_verify.h" + +/*! + * \file + * \brief Kamailio TLS support :: Certificate verification + * \ingroup tls + * Module: \ref tls + */ + + +/* FIXME: remove this and use the value in domains instead */ +#define VERIFY_DEPTH_S 3 + +/* This callback is called during each verification process, + at each step during the chain of certificates (this function + is not the certificate_verification one!). */ +int verify_callback(int pre_verify_ok, X509_STORE_CTX *ctx) { + char buf[256]; + X509 *err_cert; + int err, depth; + + depth = X509_STORE_CTX_get_error_depth(ctx); + LM_DBG("tls init - depth = %d\n",depth); + if ( depth > VERIFY_DEPTH_S ) { + LM_NOTICE("tls init - cert chain too long ( depth > VERIFY_DEPTH_S)\n"); + pre_verify_ok=0; + } + + if( pre_verify_ok ) { + LM_NOTICE("tls init - preverify is good: verify return: %d\n", + pre_verify_ok); + return pre_verify_ok; + } + + err_cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + X509_NAME_oneline(X509_get_subject_name(err_cert),buf,sizeof buf); + + LM_NOTICE("tls init - subject = %s\n", buf); + LM_NOTICE("tls init - verify error - num=%d:%s\n", err, + X509_verify_cert_error_string(err)); + LM_NOTICE("tls init - error code is %d (depth: %d)\n", + err, depth); + + switch (err) { + case X509_V_OK: + LM_NOTICE("tls init - all ok\n"); + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + X509_NAME_oneline(X509_get_issuer_name(err_cert),buf,sizeof buf); + LM_NOTICE("tls init - issuer= %s\n",buf); + break; + + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_CERT_NOT_YET_VALID: + LM_NOTICE("tls init - notBefore\n"); + break; + + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_CERT_HAS_EXPIRED: + LM_NOTICE("tls init - notAfter\n"); + break; + + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + LM_NOTICE("tls init - unable to decrypt cert signature\n"); + break; + + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + LM_NOTICE("tls init - unable to decode issuer public key\n"); + break; + + case X509_V_ERR_OUT_OF_MEM: + LM_ERR("tls init - Out of memory \n"); + break; + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + LM_NOTICE("tls init - Self signed certificate issue\n"); + break; + + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + LM_NOTICE("tls init - certificate chain too long\n"); + break; + case X509_V_ERR_INVALID_CA: + LM_NOTICE("tls init - invalid CA\n"); + break; + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + LM_NOTICE("tls init - path length exceeded\n"); + break; + case X509_V_ERR_INVALID_PURPOSE: + LM_NOTICE("tls init - invalid purpose\n"); + break; + case X509_V_ERR_CERT_UNTRUSTED: + LM_NOTICE("tls init - certificate untrusted\n"); + break; + case X509_V_ERR_CERT_REJECTED: + LM_NOTICE("tls init - certificate rejected\n"); + break; + + default: + LM_NOTICE("tls init - something wrong with the" + " cert ... error code is %d (check x509_vfy.h)\n", + err); + break; + } + + LM_NOTICE("tls init - verify return: %d\n", pre_verify_ok); + return(pre_verify_ok); +} + + +int verify_callback_unconditional_success(int pre_verify_ok, X509_STORE_CTX *ctx) { + LM_NOTICE("Post-verification callback: unconditional success\n"); + return 1; +} diff --git a/src/modules/tls_wolfssl/tls_verify.h b/src/modules/tls_wolfssl/tls_verify.h new file mode 100644 index 00000000000..8ff646d9f13 --- /dev/null +++ b/src/modules/tls_wolfssl/tls_verify.h @@ -0,0 +1,42 @@ +/* + * TLS module - certificate verification function + * + * Copyright (C) 2005 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/*! + * \file + * \brief Kamailio TLS support :: Certificate verification function + * \ingroup tls + * Module: \ref tls + */ + + +#ifndef _TLS_VERIFY_H +#define _TLS_VERIFY_H + +#include +#include + +/* This callback is called during each verification process, +at each step during the chain of certificates (this function +is not the certificate_verification one!). */ +int verify_callback(int pre_verify_ok, X509_STORE_CTX *ctx); + +/* Post-verification callback handler which unconditionally returns 1 (success) + Note that actual verification result can be retrieved through TLS PVs after-the-fact + */ +int verify_callback_unconditional_success(int pre_verify_ok, X509_STORE_CTX *ctx); + +#endif /* _TLS_VERIFY_H */ diff --git a/src/modules/tls_wolfssl/todo.txt b/src/modules/tls_wolfssl/todo.txt new file mode 100644 index 00000000000..7c81f71fd90 --- /dev/null +++ b/src/modules/tls_wolfssl/todo.txt @@ -0,0 +1,4 @@ +* After the parser change relative pathnames are not handled + properly (they are not expanded to absolute pathnames with + respect to the configuration file). +