diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 5065fad46..311cb7b73 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -39,7 +39,6 @@ jobs: - name: gcov run: | - cd ../re gcov build/**/*.o - name: install gcovr @@ -48,7 +47,18 @@ jobs: - name: coverage check run: | - min_cov="64.4" - cov=$(~/.local/bin/gcovr -r . -s | grep lines | awk '{ print $2 }' | sed 's/%//') + min_cov="64.6" + mkdir html + cov=$(~/.local/bin/gcovr -r . --html-details html/index.html --json-summary | jq .line_percent) echo "Coverage: ${cov}% (min $min_cov%)" exit $(echo "$cov < $min_cov" | bc -l) + + - name: coverage zip + run: | + zip -r coverage.zip html + + - uses: actions/upload-artifact@v4 + with: + name: coverage + path: coverage.zip + retention-days: 7 diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 6415e43dc..6c0fde21b 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -17,5 +17,5 @@ jobs: - name: build run: | - cmake -B build -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON -DUSE_OPENSSL=OFF -DCMAKE_C_FLAGS="-Werror" + cmake -B build -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 -DCMAKE_DISABLE_FIND_PACKAGE_OpenSSL=ON -DUSE_OPENSSL=OFF -DCMAKE_C_FLAGS="-Werror" cmake --build build -- CODE_SIGNING_ALLOWED=NO diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index d444c1e00..5acf6fe44 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -15,11 +15,11 @@ jobs: strategy: matrix: os: [ubuntu-22.04] - sanitizer: [thread, address] + sanitizer: [thread, address, undefined] env: - CC: clang + CC: clang-17 CMAKE_GENERATOR: Ninja - CFLAGS: "-fsanitize=${{ matrix.sanitizer }}" + CFLAGS: "-fsanitize=${{ matrix.sanitizer }} -fno-sanitize-recover=all -fno-sanitize=function" ASAN_OPTIONS: fast_unwind_on_malloc=0 steps: @@ -38,6 +38,12 @@ jobs: run: | sudo apt-get update && sudo apt-get install -y ninja-build + - name: Install clang-tools + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main" + sudo apt-get update && sudo apt-get install -y clang-17 + - name: make info run: | echo "OS: ${{ matrix.os }}" diff --git a/README.md b/README.md index bac0db318..5514f7440 100644 --- a/README.md +++ b/README.md @@ -223,13 +223,14 @@ legend: | System | Support type | Supported versions | Notes | |---|---|---|---| -| Linux | Tier 1 | glibc >= 2.27 (Ubuntu 18.04) | | +| Linux | Tier 1 | glibc >= 2.27 | | +| Linux | Tier 1 | musl >= 1.2 | | | macOS | Tier 1 | macOS >= 10.10 | | | Windows | Tier 1 | >= Windows 8 | MinGW-w64, >= VS 2019 | | Android | Tier 2 | | | | iOS | Tier 2 | | | -| FreeBSD | Tier 2 | >= 11 | | -| OpenBSD | Tier 2 | >= 6.7 | | +| FreeBSD | Tier 2 | >= 12 | | +| OpenBSD | Tier 2 | >= 7.4 | | | Linux | Tier 2 | uClibc | | @@ -253,6 +254,7 @@ legend: * GNU C Library (glibc) * Windows C Run-Time Libraries (CRT) * uClibc +* musl ### Supported compilers: diff --git a/include/re_http.h b/include/re_http.h index 322eb1abf..1ac3eafba 100644 --- a/include/re_http.h +++ b/include/re_http.h @@ -169,6 +169,9 @@ int http_client_set_cert(struct http_cli *cli, const char *path); int http_client_set_certpem(struct http_cli *cli, const char *pem); int http_client_set_key(struct http_cli *cli, const char *path); int http_client_set_keypem(struct http_cli *cli, const char *pem); +int http_client_use_chain(struct http_cli *cli, const char *path); +int http_client_use_chainpem(struct http_cli *cli, const char *chain, + int len_chain); int http_client_set_session_reuse(struct http_cli *cli, bool enabled); int http_client_set_tls_min_version(struct http_cli *cli, int version); diff --git a/include/re_net.h b/include/re_net.h index 8f1d7cc39..841f67460 100644 --- a/include/re_net.h +++ b/include/re_net.h @@ -26,12 +26,6 @@ #define INET6_ADDRSTRLEN 46 #endif -/** Length of IPv4/v6 address string */ -#ifdef HAVE_INET6 -#define NET_ADDRSTRLEN INET6_ADDRSTRLEN -#else -#define NET_ADDRSTRLEN INET_ADDRSTRLEN -#endif /* forward declarations */ struct sa; diff --git a/include/re_sip.h b/include/re_sip.h index 5feb216bb..c4c013c1d 100644 --- a/include/re_sip.h +++ b/include/re_sip.h @@ -303,7 +303,7 @@ int sip_debug(struct re_printf *pf, const struct sip *sip); int sip_send(struct sip *sip, void *sock, enum sip_transp tp, const struct sa *dst, struct mbuf *mb); int sip_send_conn(struct sip *sip, void *sock, enum sip_transp tp, - const struct sa *dst, struct mbuf *mb, + const struct sa *dst, char *host, struct mbuf *mb, sip_conn_h *connh, void *arg); void sip_set_trace_handler(struct sip *sip, sip_trace_h *traceh); @@ -402,6 +402,7 @@ void sip_dialog_set_srcport(struct sip_dialog *dlg, uint16_t srcport); uint16_t sip_dialog_srcport(struct sip_dialog *dlg); const char *sip_dialog_uri(const struct sip_dialog *dlg); uint32_t sip_dialog_lseq(const struct sip_dialog *dlg); +uint32_t sip_dialog_lseqinv(const struct sip_dialog *dlg); enum sip_transp sip_dialog_tp(const struct sip_dialog *dlg); bool sip_dialog_established(const struct sip_dialog *dlg); bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg); diff --git a/include/re_tls.h b/include/re_tls.h index eb944e57e..dc5539af5 100644 --- a/include/re_tls.h +++ b/include/re_tls.h @@ -56,6 +56,9 @@ int tls_set_certificate_der(struct tls *tls, enum tls_keytype keytype, const uint8_t *cert, size_t len_cert, const uint8_t *key, size_t len_key); int tls_set_certificate(struct tls *tls, const char *cert, size_t len); +int tls_set_certificate_chain_pem(struct tls *tls, const char *chain, + size_t len_chain); +int tls_set_certificate_chain(struct tls *tls, const char *path); void tls_set_verify_client(struct tls *tls); void tls_set_verify_client_trust_all(struct tls *tls); int tls_set_verify_client_handler(struct tls_conn *tc, int depth, diff --git a/src/http/client.c b/src/http/client.c index c97623bc5..06186f269 100644 --- a/src/http/client.c +++ b/src/http/client.c @@ -1260,6 +1260,58 @@ int http_client_disable_verify_server(struct http_cli *cli) return 0; } + + +/** + * Change used certificate+key of TLS connection + * + * @param cli HTTP Client + * @param chain Cert (chain) + Key (PEM format) + * @param len_chain Length of certificate + key PEM string + * + * @return int 0 if success, otherwise errorcode + */ +int http_client_use_chainpem(struct http_cli *cli, const char *chain, + int len_chain) +{ + if (!cli || !cli->tls) + return EINVAL; + + int err = tls_set_certificate_chain_pem(cli->tls, chain, len_chain); + if (err) + return err; + + cli->cert = mem_deref(cli->cert); + cli->key = mem_deref(cli->key); + + return 0; +} + + +/** + * Change used certificate+key of TLS connection + * + * @param cli HTTP Client + * @param path Path to Cert (chain) + Key file (PEM format) + * + * @return int 0 if success, otherwise errorcode + */ +int http_client_use_chain(struct http_cli *cli, const char *path) +{ + int err; + + if (!cli || !cli->tls) + return EINVAL; + + err = tls_set_certificate_chain(cli->tls, path); + if (err) + return err; + + cli->cert = mem_deref(cli->cert); + cli->key = mem_deref(cli->key); + + return 0; +} #endif /* USE_TLS */ diff --git a/src/sip/dialog.c b/src/sip/dialog.c index 79f9bcfa1..9bab1e9b7 100644 --- a/src/sip/dialog.c +++ b/src/sip/dialog.c @@ -34,6 +34,7 @@ struct sip_dialog { uint32_t hash; uint32_t lseq; uint32_t rseq; + uint32_t lseqinv; size_t cpos; size_t rpos; enum sip_transp tp; @@ -395,12 +396,13 @@ int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg, if (!dlg) return ENOMEM; - dlg->callid = mem_ref(odlg->callid); - dlg->ltag = mem_ref(odlg->ltag); - dlg->hash = odlg->hash; - dlg->lseq = odlg->lseq; - dlg->rseq = msg->req ? msg->cseq.num : 0; - dlg->tp = msg->tp; + dlg->callid = mem_ref(odlg->callid); + dlg->ltag = mem_ref(odlg->ltag); + dlg->hash = odlg->hash; + dlg->lseq = odlg->lseq; + dlg->lseqinv = odlg->lseqinv; + dlg->rseq = msg->req ? msg->cseq.num : 0; + dlg->tp = msg->tp; err = pl_strdup(&dlg->uri, &addr.auri); if (err) @@ -570,6 +572,9 @@ int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq, if (!mb || !dlg || !met) return EINVAL; + if (!strcmp(met, "INVITE")) + dlg->lseqinv = dlg->lseq; + err |= mbuf_write_mem(mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb)); err |= mbuf_printf(mb, "Call-ID: %s\r\n", dlg->callid); err |= mbuf_printf(mb, "CSeq: %u %s\r\n", strcmp(met, "ACK") ? @@ -661,6 +666,19 @@ uint32_t sip_dialog_lseq(const struct sip_dialog *dlg) } +/** + * Get the local sequence number of the last sent INVITE + * + * @param dlg SIP Dialog + * + * @return Local sequence number + */ +uint32_t sip_dialog_lseqinv(const struct sip_dialog *dlg) +{ + return dlg ? dlg->lseqinv : 0; +} + + /** * Check if a SIP Dialog is established * diff --git a/src/sip/request.c b/src/sip/request.c index 02b45d740..a7eec96e6 100644 --- a/src/sip/request.c +++ b/src/sip/request.c @@ -233,7 +233,7 @@ static int request(struct sip_request *req, enum sip_transp tp, } if (!req->stateful) { - err = sip_send_conn(req->sip, NULL, tp, dst, mb, + err = sip_send_conn(req->sip, NULL, tp, dst, req->host, mb, connect_handler, req); } else { diff --git a/src/sip/sip.c b/src/sip/sip.c index 987d8b257..7c22307a4 100644 --- a/src/sip/sip.c +++ b/src/sip/sip.c @@ -210,10 +210,10 @@ void sip_close(struct sip *sip, bool force) * @return 0 if success, otherwise errorcode */ int sip_send_conn(struct sip *sip, void *sock, enum sip_transp tp, - const struct sa *dst, struct mbuf *mb, + const struct sa *dst, char *host, struct mbuf *mb, sip_conn_h *connh, void *arg) { - return sip_transp_send(NULL, sip, sock, tp, dst, NULL, mb, connh, NULL, + return sip_transp_send(NULL, sip, sock, tp, dst, host, mb, connh, NULL, arg); } diff --git a/src/sipsess/connect.c b/src/sipsess/connect.c index 6280ed005..7f3c8a950 100644 --- a/src/sipsess/connect.c +++ b/src/sipsess/connect.c @@ -91,6 +91,10 @@ static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg) if (!msg || err || sip_request_loops(&sess->ls, msg->scode)) goto out; + if (!sip_dialog_cmp_half(sess->dlg, msg) + || sip_dialog_lseqinv(sess->dlg) != msg->cseq.num) + goto out; + sdp = mbuf_get_left(msg->mb) > 0; if (msg->scode < 200) { @@ -148,15 +152,15 @@ static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg) } else if (msg->scode < 300) { + sess->established = true; + sess->hdrs = mem_deref(sess->hdrs); err = sip_dialog_established(sess->dlg) ? sip_dialog_update(sess->dlg, msg) : sip_dialog_create(sess->dlg, msg); - if (err) - goto out; - if (sdp) { + if (sdp && !err) { if (sess->neg_state == SDP_NEG_LOCAL_OFFER) { sess->neg_state = SDP_NEG_DONE; err = sess->answerh(msg, sess->arg); @@ -179,7 +183,6 @@ static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg) && mbuf_get_left(desc)) sess->neg_state = SDP_NEG_DONE; - sess->established = true; mem_deref(desc); if (err || sess->terminated) diff --git a/src/tls/openssl/sni.c b/src/tls/openssl/sni.c index 1f02f707b..8298e40fd 100644 --- a/src/tls/openssl/sni.c +++ b/src/tls/openssl/sni.c @@ -19,12 +19,10 @@ #include "tls.h" -#define DEBUG_MODULE "tls" +#define DEBUG_MODULE "tls/sni" #define DEBUG_LEVEL 5 #include -#if !defined(LIBRESSL_VERSION_NUMBER) - struct tls_conn; @@ -161,48 +159,33 @@ static int ssl_set_verify_client(SSL *ssl, const char *host) } -static int ssl_use_cert(SSL *ssl, struct tls_cert *uc) -{ - int err; - long r; - - SSL_certs_clear(ssl); - r = SSL_clear_chain_certs(ssl); - if (r != 1) - return EINVAL; - - r = SSL_use_cert_and_key(ssl, tls_cert_x509(uc), tls_cert_pkey(uc), - tls_cert_chain(uc), 1); - if (r != 1) { - ERR_clear_error(); - return EINVAL; - } - - err = ssl_set_verify_client(ssl, tls_cert_host(uc)); - return err; -} - - static int ssl_servername_handler(SSL *ssl, int *al, void *arg) { - struct tls *tls = arg; + struct tls *tls = arg; struct tls_cert *uc = NULL; const char *sni; - (void)al; sni = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (!str_isset(sni)) - goto out; + if (!str_isset(sni)) { + *al = SSL_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } /* find and apply matching certificate */ uc = tls_cert_for_sni(tls, sni); - if (!uc) - goto out; + if (!uc) { + *al = SSL_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } DEBUG_INFO("found cert for sni %s\n", sni); - (void)ssl_use_cert(ssl, uc); + if (SSL_set_SSL_CTX(ssl, tls_cert_ctx(uc)) == NULL) { + *al = SSL_AD_INTERNAL_ERROR; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + (void)ssl_set_verify_client(ssl, tls_cert_host(uc)); -out: return SSL_TLSEXT_ERR_OK; } @@ -218,5 +201,3 @@ void tls_enable_sni(struct tls *tls) ssl_servername_handler); SSL_CTX_set_tlsext_servername_arg(tls_ssl_ctx(tls), tls); } - -#endif /* !defined(LIBRESSL_VERSION_NUMBER) */ diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c index f3d111ceb..b182b74ef 100644 --- a/src/tls/openssl/tls.c +++ b/src/tls/openssl/tls.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -57,9 +58,7 @@ struct tls { */ struct tls_cert { struct le le; - X509 *x509; - EVP_PKEY *pkey; - STACK_OF(X509) *chain; + SSL_CTX *ctx; char *host; }; @@ -163,7 +162,6 @@ static int keytype2int(enum tls_keytype type) } -#if !defined(LIBRESSL_VERSION_NUMBER) /** * OpenSSL verify handler for debugging purposes. Prints only warnings in the * default build @@ -206,7 +204,6 @@ int tls_verify_handler(int ok, X509_STORE_CTX *ctx) return ok; } -#endif static int tls_verify_idx = -1; @@ -222,95 +219,112 @@ static void tls_init_verify_idx(void) } -/** - * Allocate a new TLS context - * - * @param tlsp Pointer to allocated TLS context - * @param method TLS method - * @param keyfile Optional private key file - * @param pwd Optional password - * - * @return 0 if success, otherwise errorcode - */ -int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, - const char *pwd) +static int tls_ctx_alloc(SSL_CTX **ctxp, enum tls_method method, + const char *certf, const char *pwd, struct tls *tls) { - struct tls *tls; - int r, err; + int err = 0; + int r; + SSL_CTX *ctx; int min_proto = 0; - if (!tlsp) - return EINVAL; - - tls = mem_zalloc(sizeof(*tls), destructor); - if (!tls) - return ENOMEM; - - tls->verify_server = true; switch (method) { case TLS_METHOD_TLS: case TLS_METHOD_SSLV23: - tls->ctx = SSL_CTX_new(TLS_method()); + ctx = SSL_CTX_new(TLS_method()); min_proto = TLS1_2_VERSION; break; case TLS_METHOD_DTLS: case TLS_METHOD_DTLSV1: case TLS_METHOD_DTLSV1_2: - tls->ctx = SSL_CTX_new(DTLS_method()); + ctx = SSL_CTX_new(DTLS_method()); break; default: DEBUG_WARNING("tls method %d not supported\n", method); - err = ENOSYS; + return ENOSYS; + } + + if (!ctx) { + ERR_clear_error(); + return ENOMEM; + } + + SSL_CTX_set_min_proto_version(ctx, min_proto); + + if (!certf) goto out; + + /* Load our keys and certificates */ + if (pwd && tls) { + err = str_dup(&tls->pass, pwd); + if (err) + goto out; + + SSL_CTX_set_default_passwd_cb(ctx, password_cb); + SSL_CTX_set_default_passwd_cb_userdata(ctx, tls); } - if (!tls->ctx) { + r = SSL_CTX_use_certificate_chain_file(ctx, certf); + if (r <= 0) { + DEBUG_WARNING("Can't read certificate file: %s (%d)\n", certf, + r); ERR_clear_error(); - err = ENOMEM; + err = EINVAL; goto out; } - err = tls_set_min_proto_version(tls, min_proto); - if (err) + r = SSL_CTX_use_PrivateKey_file(ctx, certf, SSL_FILETYPE_PEM); + if (r <= 0) { + DEBUG_WARNING("Can't read key file: %s (%d)\n", certf, r); + ERR_clear_error(); + err = EINVAL; goto out; + } -#if defined(TRACE_SSL) - SSL_CTX_set_keylog_callback(tls->ctx, tls_keylogger_cb); -#endif +out: + if (err) + SSL_CTX_free(ctx); + else + *ctxp = ctx; - /* Load our keys and certificates */ - if (keyfile) { - if (pwd) { - err = str_dup(&tls->pass, pwd); - if (err) - goto out; + return err; +} - SSL_CTX_set_default_passwd_cb(tls->ctx, password_cb); - SSL_CTX_set_default_passwd_cb_userdata(tls->ctx, tls); - } - r = SSL_CTX_use_certificate_chain_file(tls->ctx, keyfile); - if (r <= 0) { - DEBUG_WARNING("Can't read certificate file: %s (%d)\n", - keyfile, r); - ERR_clear_error(); - err = EINVAL; - goto out; - } +/** + * Allocate a new TLS context + * + * @param tlsp Pointer to allocated TLS context + * @param method TLS method + * @param keyfile Optional private key file + * @param pwd Optional password + * + * @return 0 if success, otherwise errorcode + */ +int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, + const char *pwd) +{ + struct tls *tls; + int err; - r = SSL_CTX_use_PrivateKey_file(tls->ctx, keyfile, - SSL_FILETYPE_PEM); - if (r <= 0) { - DEBUG_WARNING("Can't read key file: %s (%d)\n", - keyfile, r); - ERR_clear_error(); - err = EINVAL; - goto out; - } - } + if (!tlsp) + return EINVAL; + + tls = mem_zalloc(sizeof(*tls), destructor); + if (!tls) + return ENOMEM; + + err = tls_ctx_alloc(&tls->ctx, method, keyfile, pwd, tls); + if (err) + goto out; + + tls->verify_server = true; + +#if defined(TRACE_SSL) + SSL_CTX_set_keylog_callback(tls->ctx, tls_keylogger_cb); +#endif err = hash_alloc(&tls->reuse.ht_sessions, 256); if (err) @@ -1404,7 +1418,6 @@ int tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count) */ int tls_set_verify_server(struct tls_conn *tc, const char *host) { -#if !defined(LIBRESSL_VERSION_NUMBER) struct sa sa; if (!tc || !host) @@ -1433,12 +1446,6 @@ int tls_set_verify_server(struct tls_conn *tc, const char *host) SSL_set_verify(tc->ssl, SSL_VERIFY_PEER, tls_verify_handler); return 0; -#else - (void)tc; - (void)host; - - return ENOSYS; -#endif } @@ -1946,17 +1953,14 @@ SSL_CTX *tls_ssl_ctx(const struct tls *tls) } -#if !defined(LIBRESSL_VERSION_NUMBER) static void tls_cert_destructor(void *arg) { struct tls_cert *uc = arg; mem_deref(uc->host); - X509_free(uc->x509); - EVP_PKEY_free(uc->pkey); - sk_X509_pop_free(uc->chain, X509_free); + if (uc->ctx) + SSL_CTX_free(uc->ctx); } -#endif /** @@ -1972,11 +1976,8 @@ static void tls_cert_destructor(void *arg) */ int tls_add_certf(struct tls *tls, const char *certf, const char *host) { -#if !defined(LIBRESSL_VERSION_NUMBER) struct tls_cert *uc; - BIO *bio = NULL; int err = 0; - int ret; if (!tls || !certf) return EINVAL; @@ -1988,73 +1989,30 @@ int tls_add_certf(struct tls *tls, const char *certf, const char *host) if (str_isset(host)) { err = str_dup(&uc->host, host); if (err) - goto out; + goto err; } - bio = BIO_new_file(certf, "r"); - if (!bio) { - err = EIO; - goto out; - } + err = tls_ctx_alloc(&uc->ctx, TLS_METHOD_TLS, certf, NULL, NULL); + if (err) + goto err; - uc->x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (!uc->x509) { - DEBUG_WARNING("Can't read certificate from file: %s\n", certf); - err = ENOTSUP; - goto out; + X509_STORE *ca = SSL_CTX_get_cert_store(tls->ctx); + if (ca) { + X509_STORE_up_ref(ca); + SSL_CTX_set_cert_store(uc->ctx, ca); } - while (1) { - X509 *ca = PEM_read_bio_X509(bio, NULL, 0, NULL); - if (!ca) - break; + list_append(&tls->certs, &uc->le, uc); + if (list_count(&tls->certs) == 1) + tls_enable_sni(tls); - if (!uc->chain) - uc->chain = sk_X509_new_null(); - - if (!uc->chain) { - err = ENOMEM; - goto out; - } - - if (!sk_X509_push(uc->chain, ca)) { - err = ENOMEM; - goto out; - } - } - - ret = BIO_reset(bio); - if (ret < 0 || !bio) { - err = EIO; - goto out; - } - - uc->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - if (!uc->pkey) { - DEBUG_WARNING("Can't read private key from file: %s\n", certf); - err = ENOTSUP; - goto out; - } + return 0; -out: - BIO_free(bio); - if (err) { - ERR_clear_error(); - mem_deref(uc); - } - else { - list_append(&tls->certs, &uc->le, uc); - if (list_count(&tls->certs) == 1) - tls_enable_sni(tls); - } +err: + ERR_clear_error(); + mem_deref(uc); return err; -#else - (void)tls; - (void)certf; - (void)host; - return ENOSYS; -#endif } @@ -2067,36 +2025,15 @@ int tls_add_certf(struct tls *tls, const char *certf, const char *host) */ X509 *tls_cert_x509(struct tls_cert *hc) { - return hc ? hc->x509 : NULL; + return hc ? SSL_CTX_get0_certificate(hc->ctx) : NULL; } -/** - * Returns the private key of the TLS certificate - * - * @param hc TLS certificate - * - * @return The OpenSSL EVP_PKEY - */ -EVP_PKEY *tls_cert_pkey(struct tls_cert *hc) -{ - return hc ? hc->pkey : NULL; -} +SSL_CTX *tls_cert_ctx(struct tls_cert *hc) { - -/* - * Returns the certificate chain of the TLS certificate - * - * @param hc TLS certificate - * - * @return The OpenSSL stack of X509 - */ -struct stack_st_X509 *tls_cert_chain(struct tls_cert *hc) -{ - return hc ? hc->chain : NULL; + return hc ? hc->ctx : NULL; } - /** * Returns the host name of the TLS certificate * @@ -2214,3 +2151,154 @@ int tls_set_resumption(struct tls *tls, const enum tls_resume_mode mode) return 0; } + + +/** + * Change used certificate+key of an existing SSL object + * + * @param tls TLS Object + * @param chain Cert (chain) + Key in PEM format + * @param len_chain Length of certificate + key PEM string + * + * @return int 0 if success, otherwise errorcode + */ +int tls_set_certificate_chain_pem(struct tls *tls, const char *chain, + size_t len_chain) +{ + STACK_OF(X509) *cert_stack = NULL; + BIO *bio_mem = NULL; + EVP_PKEY *pkey = NULL; + X509 *leaf_cert = NULL; + int err = ENOMEM; + + if (!tls || !chain || !len_chain) + return EINVAL; + + bio_mem = BIO_new_mem_buf(chain, (int)len_chain); + cert_stack = sk_X509_new_null(); + if (!bio_mem || !cert_stack) + goto out; + + X509 *cert; + while ((cert = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL)) != NULL) { + int n = sk_X509_push(cert_stack, cert); + if (n < 1) { + X509_free(cert); + goto out; + } + } + + err = EINVAL; + + if (sk_X509_num(cert_stack) == 0) + goto out; + + leaf_cert = sk_X509_shift(cert_stack); + long ok = SSL_CTX_use_certificate(tls->ctx, leaf_cert); + if (ok <= 0) { + X509_free(leaf_cert); + goto out; + } + + if (sk_X509_num(cert_stack)) { + ok = SSL_CTX_clear_chain_certs(tls->ctx); + if (!ok) + goto out; + + while((cert = sk_X509_shift(cert_stack)) != NULL){ + ok = SSL_CTX_add0_chain_cert(tls->ctx, cert); + if (!ok) { + X509_free(cert); + goto out; + } + } + } + + BIO_free(bio_mem); + bio_mem = BIO_new_mem_buf(chain, (int)len_chain); + if (!bio_mem) { + err = ENOMEM; + goto out; + } + + pkey = PEM_read_bio_PrivateKey(bio_mem, NULL, NULL, NULL); + if (!pkey) + goto out; + + ok = SSL_CTX_use_PrivateKey(tls->ctx, pkey); + if (ok <= 0) { + err = EKEYREJECTED; + goto out; + } + + ok = SSL_CTX_check_private_key(tls->ctx); + if (ok <= 0) + goto out; + + if (tls->cert) + X509_free(tls->cert); + + tls->cert = leaf_cert; + leaf_cert = NULL; + + err = 0; + +out: + if (bio_mem) + BIO_free(bio_mem); + if (leaf_cert) + X509_free(leaf_cert); + if (cert_stack) + sk_X509_pop_free(cert_stack, X509_free); + if (pkey) + EVP_PKEY_free(pkey); + if (err) + ERR_clear_error(); + + return err; +} + + +/** + * Change used certificate+key of an existing SSL object + * + * @param tls TLS Object + * @param path Path to Cert (chain) + Key file (PEM format) + * + * @return int 0 if success, otherwise errorcode + */ +int tls_set_certificate_chain(struct tls *tls, const char *path) +{ + X509 *cert; + int ok = 0; + + if (!tls || !path) + return EINVAL; + + ok = SSL_CTX_use_certificate_chain_file(tls->ctx, path); + if (ok <= 0) { + ERR_clear_error(); + return ENOENT; + } + + ok = SSL_CTX_use_PrivateKey_file(tls->ctx, path, SSL_FILETYPE_PEM); + if (ok <= 0) { + ERR_clear_error(); + return EKEYREJECTED; + } + + cert = SSL_CTX_get0_certificate(tls->ctx); + if (!cert) { + ERR_clear_error(); + return ENOENT; + } + + X509_up_ref(cert); + + if (tls->cert) + X509_free(tls->cert); + + tls->cert = cert; + + return 0; +} diff --git a/src/tls/openssl/tls.h b/src/tls/openssl/tls.h index 7094c9d69..ac780f0ae 100644 --- a/src/tls/openssl/tls.h +++ b/src/tls/openssl/tls.h @@ -27,14 +27,11 @@ struct tls_cert; void tls_flush_error(void); SSL_CTX *tls_ssl_ctx(const struct tls *tls); X509 *tls_cert_x509(struct tls_cert *hc); -EVP_PKEY *tls_cert_pkey(struct tls_cert *hc); +SSL_CTX *tls_cert_ctx(struct tls_cert *hc); -struct stack_st_X509 *tls_cert_chain(struct tls_cert *hc); const char *tls_cert_host(struct tls_cert *hc); const struct list *tls_certs(const struct tls *tls); struct tls_cert *tls_cert_for_sni(const struct tls *tls, const char *sni); -#if !defined(LIBRESSL_VERSION_NUMBER) int tls_verify_handler(int ok, X509_STORE_CTX *ctx); void tls_enable_sni(struct tls *tls); -#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7e2610395..8a1bbf043 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -161,7 +161,7 @@ endif() set(LINKLIBS re ${OPENSSL_LIBRARIES}) if(WIN32) - list(APPEND LINKLIBS qwave iphlpapi wsock32 ws2_32) + list(APPEND LINKLIBS qwave iphlpapi wsock32 ws2_32 crypt32) else() list(APPEND LINKLIBS m ${RESOLV_LIBRARY}) endif() diff --git a/test/http.c b/test/http.c index a8417e438..2da471cee 100644 --- a/test/http.c +++ b/test/http.c @@ -344,9 +344,9 @@ static void http_resp_handler(int err, const struct http_msg *msg, void *arg) /* verify HTTP response */ TEST_STRCMP("1.1", 3, msg->ver.p, msg->ver.l); - TEST_STRCMP("", 0, msg->met.p, msg->met.l); - TEST_STRCMP("", 0, msg->path.p, msg->path.l); - TEST_STRCMP("", 0, msg->prm.p, msg->prm.l); + TEST_ASSERT(!msg->met.p); + TEST_ASSERT(!msg->path.p); + TEST_ASSERT(!msg->prm.p); TEST_EQUALS(200, msg->scode); TEST_STRCMP("OK", 2, msg->reason.p, msg->reason.l); TEST_EQUALS(t->clen, msg->clen); diff --git a/test/rtcp.c b/test/rtcp.c index fb2175120..1641866f3 100644 --- a/test/rtcp.c +++ b/test/rtcp.c @@ -21,7 +21,7 @@ */ -#define GENERATOR_SSRC 0x00000001 +static const uint32_t GENERATOR_SSRC = 0x00000001; struct fixture { @@ -191,3 +191,110 @@ int test_rtcp_packetloss(void) return err; } + + +struct agent { + struct rtp_sock *rtp_sock; + struct sa laddr_rtcp; + unsigned rtp_count; + unsigned psfb_count; +}; + + +static void rtp_recv_handler(const struct sa *src, + const struct rtp_header *hdr, + struct mbuf *mb, void *arg) +{ + struct agent *ag = arg; + (void)src; + (void)hdr; + (void)mb; + + ++ag->rtp_count; +} + + +static void rtcp_recv_handler(const struct sa *src, struct rtcp_msg *msg, + void *arg) +{ + struct agent *ag = arg; + (void)src; + + switch (msg->hdr.pt) { + + case RTCP_PSFB: + ++ag->psfb_count; + re_cancel(); + break; + + default: + break; + } +} + + +static int agent_init(struct agent *ag, bool mux) +{ + struct sa laddr; + + sa_set_str(&laddr, "127.0.0.1", 0); + + int err = rtp_listen(&ag->rtp_sock, IPPROTO_UDP, + &laddr, 1024, 65535, true, + rtp_recv_handler, rtcp_recv_handler, ag); + if (err) + return err; + + rtcp_enable_mux(ag->rtp_sock, mux); + + udp_local_get(rtcp_sock(ag->rtp_sock), &ag->laddr_rtcp); + + return 0; +} + + +static int test_rtcp_loop_base(bool mux) +{ + struct agent a = {0}, b = {0}; + int err; + + err = agent_init(&a, mux); + TEST_ERR(err); + err = agent_init(&b, mux); + TEST_ERR(err); + + rtcp_start(a.rtp_sock, "cname", &b.laddr_rtcp); + rtcp_start(b.rtp_sock, "cname", &a.laddr_rtcp); + + err = rtcp_send_pli(a.rtp_sock, rtp_sess_ssrc(b.rtp_sock)); + TEST_ERR(err); + + err = re_main_timeout(1000); + TEST_ERR(err); + + ASSERT_EQ(0, a.rtp_count); + ASSERT_EQ(0, a.psfb_count); + ASSERT_EQ(0, b.rtp_count); + ASSERT_EQ(1, b.psfb_count); + + out: + mem_deref(b.rtp_sock); + mem_deref(a.rtp_sock); + + return err; +} + + +int test_rtcp_loop(void) +{ + int err; + + err = test_rtcp_loop_base(false); + TEST_ERR(err); + + err = test_rtcp_loop_base(true); + TEST_ERR(err); + + out: + return err; +} diff --git a/test/test.c b/test/test.c index 6bfc024f9..967f5dca5 100644 --- a/test/test.c +++ b/test/test.c @@ -162,6 +162,7 @@ static const struct test tests[] = { TEST(test_rtcp_decode_badmsg), TEST(test_rtcp_packetloss), TEST(test_rtcp_twcc), + TEST(test_rtcp_loop), TEST(test_sa_class), TEST(test_sa_cmp), TEST(test_sa_decode), diff --git a/test/test.h b/test/test.h index cee4a3401..04b2639f2 100644 --- a/test/test.h +++ b/test/test.h @@ -272,6 +272,7 @@ int test_rtcp_decode(void); int test_rtcp_decode_badmsg(void); int test_rtcp_packetloss(void); int test_rtcp_twcc(void); +int test_rtcp_loop(void); int test_sa_class(void); int test_sa_cmp(void); int test_sa_decode(void); diff --git a/test/vidconv.c b/test/vidconv.c index c34a88125..d4bf8bf01 100644 --- a/test/vidconv.c +++ b/test/vidconv.c @@ -348,6 +348,9 @@ int test_vidconv_pixel_formats(void) size_t size = test->dst_planev[p].sz; + if (!test->dst_planev[p].data) + continue; + TEST_MEMCMP(test->dst_planev[p].data, test->dst_planev[p].sz, fdst->data[p],