Repository: profanity-im/profanity (c)

Issue:
/sendfile does not work with self signed cert
<!--- Provide a general summary of the issue in the Title above -->
When using /sendfile to upload a file to an xmpp server using tls with a self signed certificate, the operation fails, stating "SSL peer certificate or SSH remote key was not OK". This fails even if the account is set to trust the server tls cert with tls trust in the account settings. 
As a sidenote, if the http upload module on the xmpp server is changed to use http for the upload, it is successful. However, when attempting to download the file from the server with /url the same "SSL peer certificate or SSH remote key was not OK" error occurs (because the server returns a https url).
<!--- More than 50 issues open? Please don't file any new feature requests -->
<!--- Help us reduce the work first :-) -->

## Expected Behavior
The file should upload properly without an error.
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->

## Current Behavior
The file is not uploaded properly and returns an error instead.
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->

## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->

## Steps to Reproduce (for bugs)
<!--- Describe, in detail, what needs to happen to reproduce this bug -->
<!--- Give us a screenshot (if it's helpful for this particular bug) -->
1. Login to xmpp server using self signed cert
2. Attempt to upload file using /sendfile
3. Upload fails

## Context
Attempting to create an xmpp server using a self signed cert
<!--- How has this issue affected you? What are you trying to accomplish? -->

## Environment
* Give us the version and build information output generated by `profanity -v`
* If you could not yet build profanity, mention the revision you try to build from
* Operating System/Distribution
* glib version

Profanity, version 0.11.1
Build information:
XMPP library: libmesode
Desktop notification support: Enabled
OTR support: Enabled (libotr 4.1.1)
PGP support: Enabled (libgpgme 1.16.0)
OMEMO support: Enabled
C plugins: Enabled
Python plugins: Enabled (3.10.1)
GTK icons/clipboard: Disabled

Using Arch Linux 5.15.11-arch2-1
ldd (GNU libc) 2.33
/sendfile does not work with self signed cert
<!--- Provide a general summary of the issue in the Title above -->
When using /sendfile to upload a file to an xmpp server using tls with a self signed certificate, the operation fails, stating "SSL peer certificate or SSH remote key was not OK". This fails even if the account is set to trust the server tls cert with tls trust in the account settings. 
As a sidenote, if the http upload module on the xmpp server is changed to use http for the upload, it is successful. However, when attempting to download the file from the server with /url the same "SSL peer certificate or SSH remote key was not OK" error occurs (because the server returns a https url).
<!--- More than 50 issues open? Please don't file any new feature requests -->
<!--- Help us reduce the work first :-) -->

## Expected Behavior
The file should upload properly without an error.
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->

## Current Behavior
The file is not uploaded properly and returns an error instead.
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->

## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->

## Steps to Reproduce (for bugs)
<!--- Describe, in detail, what needs to happen to reproduce this bug -->
<!--- Give us a screenshot (if it's helpful for this particular bug) -->
1. Login to xmpp server using self signed cert
2. Attempt to upload file using /sendfile
3. Upload fails

## Context
Attempting to create an xmpp server using a self signed cert
<!--- How has this issue affected you? What are you trying to accomplish? -->

## Environment
* Give us the version and build information output generated by `profanity -v`
* If you could not yet build profanity, mention the revision you try to build from
* Operating System/Distribution
* glib version

Profanity, version 0.11.1
Build information:
XMPP library: libmesode
Desktop notification support: Enabled
OTR support: Enabled (libotr 4.1.1)
PGP support: Enabled (libgpgme 1.16.0)
OMEMO support: Enabled
C plugins: Enabled
Python plugins: Enabled (3.10.1)
GTK icons/clipboard: Disabled

Using Arch Linux 5.15.11-arch2-1
ldd (GNU libc) 2.33


Discussion:
Seems I've found the culprit. [Looks like it's curl that's throwing the error](https://stackoverflow.com/questions/14192837/ssl-peer-certificate-or-ssh-remote-key-was-not-ok), so perhaps it'd be a good idea to run curl without verifying the peer when uploading and downloading files if the tls option for the account is set to trust?
Seems I've found the culprit. [Looks like it's curl that's throwing the error](https://stackoverflow.com/questions/14192837/ssl-peer-certificate-or-ssh-remote-key-was-not-ok), so perhaps it'd be a good idea to run curl without verifying the peer when uploading and downloading files if the tls option for the account is set to trust?

Patch:
diff --git a/Makefile.am b/Makefile.am
--- a/Makefile.am
+++ b/Makefile.am
@@ -61,6 +61,7 @@ core_sources = \
 	src/config/theme.c src/config/theme.h \
 	src/config/color.c src/config/color.h \
 	src/config/scripts.c src/config/scripts.h \
+	src/config/cafile.c src/config/cafile.h \
 	src/plugins/plugins.h src/plugins/plugins.c \
 	src/plugins/api.h src/plugins/api.c \
 	src/plugins/callbacks.h src/plugins/callbacks.c \
@@ -124,6 +125,7 @@ unittest_sources = \
 	tests/unittests/log/stub_log.c \
 	tests/unittests/database/stub_database.c \
 	tests/unittests/config/stub_accounts.c \
+	tests/unittests/config/stub_cafile.c \
 	tests/unittests/tools/stub_http_upload.c \
 	tests/unittests/tools/stub_http_download.c \
 	tests/unittests/tools/stub_aesgcm_download.c \
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -67,6 +67,7 @@
 #include "config/files.h"
 #include "config/accounts.h"
 #include "config/account.h"
+#include "config/cafile.h"
 #include "config/preferences.h"
 #include "config/theme.h"
 #include "config/tlscerts.h"
@@ -231,6 +232,7 @@ cmd_tls_trust(ProfWin* window, const char* const command, gchar** args)
         cons_show("Error getting TLS certificate.");
         return TRUE;
     }
+    cafile_add(cert);
     if (tlscerts_exists(cert->fingerprint)) {
         cons_show("Certificate %s already trusted.", cert->fingerprint);
         tlscerts_free(cert);
diff --git a/src/config/cafile.c b/src/config/cafile.c
new file mode 100644
--- /dev/null
+++ b/src/config/cafile.c
@@ -0,0 +1,106 @@
+/*
+ * cafile.c
+ * vim: expandtab:ts=4:sts=4:sw=4
+ *
+ * Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#include <fcntl.h>
+#include <glib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "common.h"
+#include "config/files.h"
+#include "log.h"
+
+static gchar*
+_cafile_name(void)
+{
+    gchar* certs_dir = files_get_data_path(DIR_CERTS);
+    if (!create_dir(certs_dir)) {
+        g_free(certs_dir);
+        return NULL;
+    }
+    gchar* filename = g_strdup_printf("%s/CAfile.pem", certs_dir);
+    g_free(certs_dir);
+    return filename;
+}
+
+void
+cafile_add(const TLSCertificate* cert)
+{
+    if (!cert->pem) {
+        log_error("[CAfile] can't store cert with fingerprint %s: PEM is empty", cert->fingerprint);
+        return;
+    }
+    gchar* cafile = _cafile_name();
+    if (!cafile)
+        return;
+    gchar *contents = NULL, *new_contents = NULL;
+    gsize length;
+    GError* glib_error = NULL;
+    if (g_file_test(cafile, G_FILE_TEST_EXISTS)) {
+        if (!g_file_get_contents(cafile, &contents, &length, &glib_error)) {
+            log_error("[CAfile] could not read from %s: %s", cafile, glib_error ? glib_error->message : "No GLib error given");
+            goto out;
+        }
+        if (strstr(contents, cert->fingerprint)) {
+            log_debug("[CAfile] fingerprint %s already stored", cert->fingerprint);
+            goto out;
+        }
+    }
+    const char* header = "# Profanity CAfile\n# DO NOT EDIT - this file is automatically generated";
+    new_contents = g_strdup_printf("%s\n\n# %s\n%s", contents ? contents : header, cert->fingerprint, cert->pem);
+    if (!g_file_set_contents(cafile, new_contents, -1, &glib_error))
+        log_error("[CAfile] could not write to %s: %s", cafile, glib_error ? glib_error->message : "No GLib error given");
+out:
+    g_free(new_contents);
+    g_free(contents);
+    g_free(cafile);
+}
+
+gchar*
+cafile_get_name(void)
+{
+    gchar* cafile = _cafile_name();
+    if (!g_file_test(cafile, G_FILE_TEST_EXISTS)) {
+        /* That's no problem!
+         * There's no need to have a profanity-specific CAfile if all CA's
+         * of servers you're trying to connect to are in your OS trust-store
+         */
+        log_debug("[CAfile] file %s not created yet", cafile);
+        g_free(cafile);
+        cafile = NULL;
+    }
+    return cafile;
+}
diff --git a/src/config/cafile.h b/src/config/cafile.h
new file mode 100644
--- /dev/null
+++ b/src/config/cafile.h
@@ -0,0 +1,45 @@
+/*
+ * cafile.h
+ * vim: expandtab:ts=4:sts=4:sw=4
+ *
+ * Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#ifndef CONFIG_CAFILE_H
+#define CONFIG_CAFILE_H
+
+#include <glib.h>
+#include "tlscerts.h"
+
+void cafile_add(const TLSCertificate* cert);
+gchar* cafile_get_name(void);
+
+#endif
diff --git a/src/config/files.h b/src/config/files.h
--- a/src/config/files.h
+++ b/src/config/files.h
@@ -59,6 +59,7 @@
 #define DIR_DATABASE  "database"
 #define DIR_DOWNLOADS "downloads"
 #define DIR_EDITOR    "editor"
+#define DIR_CERTS     "certs"
 
 void files_create_directories(void);
 
diff --git a/src/config/tlscerts.c b/src/config/tlscerts.c
--- a/src/config/tlscerts.c
+++ b/src/config/tlscerts.c
@@ -130,7 +130,7 @@ tlscerts_list(void)
         char* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
 
         TLSCertificate* cert = tlscerts_new(fingerprint, version, serialnumber, subjectname, issuername, notbefore,
-                                            notafter, keyalg, signaturealg);
+                                            notafter, keyalg, signaturealg, NULL);
 
         free(fingerprint);
         free(serialnumber);
@@ -154,60 +154,39 @@ tlscerts_list(void)
 TLSCertificate*
 tlscerts_new(const char* const fingerprint, int version, const char* const serialnumber, const char* const subjectname,
              const char* const issuername, const char* const notbefore, const char* const notafter,
-             const char* const key_alg, const char* const signature_alg)
+             const char* const key_alg, const char* const signature_alg, const char* const pem)
 {
-    TLSCertificate* cert = malloc(sizeof(TLSCertificate));
+    TLSCertificate* cert = calloc(1, sizeof(TLSCertificate));
 
     if (fingerprint) {
         cert->fingerprint = strdup(fingerprint);
-    } else {
-        cert->fingerprint = NULL;
     }
     cert->version = version;
     if (serialnumber) {
         cert->serialnumber = strdup(serialnumber);
-    } else {
-        cert->serialnumber = NULL;
     }
     if (subjectname) {
         cert->subjectname = strdup(subjectname);
-    } else {
-        cert->subjectname = NULL;
     }
     if (issuername) {
         cert->issuername = strdup(issuername);
-    } else {
-        cert->issuername = NULL;
     }
     if (notbefore) {
         cert->notbefore = strdup(notbefore);
-    } else {
-        cert->notbefore = NULL;
     }
     if (notafter) {
         cert->notafter = strdup(notafter);
-    } else {
-        cert->notafter = NULL;
     }
     if (key_alg) {
         cert->key_alg = strdup(key_alg);
-    } else {
-        cert->key_alg = NULL;
     }
     if (signature_alg) {
         cert->signature_alg = strdup(signature_alg);
-    } else {
-        cert->signature_alg = NULL;
+    }
+    if (pem) {
+        cert->pem = strdup(pem);
     }
 
-    cert->subject_country = NULL;
-    cert->subject_state = NULL;
-    cert->subject_distinguishedname = NULL;
-    cert->subject_serialnumber = NULL;
-    cert->subject_commonname = NULL;
-    cert->subject_organisation = NULL;
-    cert->subject_organisation_unit = NULL;
-    cert->subject_email = NULL;
     gchar** fields = g_strsplit(subjectname, "/", 0);
     for (int i = 0; i < g_strv_length(fields); i++) {
         gchar** keyval = g_strsplit(fields[i], "=", 2);
@@ -241,14 +220,6 @@ tlscerts_new(const char* const fingerprint, int version, const char* const seria
     }
     g_strfreev(fields);
 
-    cert->issuer_country = NULL;
-    cert->issuer_state = NULL;
-    cert->issuer_distinguishedname = NULL;
-    cert->issuer_serialnumber = NULL;
-    cert->issuer_commonname = NULL;
-    cert->issuer_organisation = NULL;
-    cert->issuer_organisation_unit = NULL;
-    cert->issuer_email = NULL;
     fields = g_strsplit(issuername, "/", 0);
     for (int i = 0; i < g_strv_length(fields); i++) {
         gchar** keyval = g_strsplit(fields[i], "=", 2);
@@ -286,7 +257,7 @@ tlscerts_new(const char* const fingerprint, int version, const char* const seria
 }
 
 void
-tlscerts_add(TLSCertificate* cert)
+tlscerts_add(const TLSCertificate* cert)
 {
     if (!cert) {
         return;
@@ -354,7 +325,7 @@ tlscerts_get_trusted(const char* const fingerprint)
     char* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
 
     TLSCertificate* cert = tlscerts_new(fingerprint, version, serialnumber, subjectname, issuername, notbefore,
-                                        notafter, keyalg, signaturealg);
+                                        notafter, keyalg, signaturealg, NULL);
 
     free(serialnumber);
     free(subjectname);
@@ -412,6 +383,8 @@ tlscerts_free(TLSCertificate* cert)
         free(cert->key_alg);
         free(cert->signature_alg);
 
+        free(cert->pem);
+
         free(cert);
     }
 }
diff --git a/src/config/tlscerts.h b/src/config/tlscerts.h
--- a/src/config/tlscerts.h
+++ b/src/config/tlscerts.h
@@ -65,13 +65,14 @@ typedef struct tls_cert_t
     char* fingerprint;
     char* key_alg;
     char* signature_alg;
+    char* pem;
 } TLSCertificate;
 
 void tlscerts_init(void);
 
 TLSCertificate* tlscerts_new(const char* const fingerprint, int version, const char* const serialnumber, const char* const subjectname,
                              const char* const issuername, const char* const notbefore, const char* const notafter,
-                             const char* const key_alg, const char* const signature_alg);
+                             const char* const key_alg, const char* const signature_alg, const char* const pem);
 
 void tlscerts_set_current(const char* const fp);
 
@@ -81,7 +82,7 @@ void tlscerts_clear_current(void);
 
 gboolean tlscerts_exists(const char* const fingerprint);
 
-void tlscerts_add(TLSCertificate* cert);
+void tlscerts_add(const TLSCertificate* cert);
 
 gboolean tlscerts_revoke(const char* const fingerprint);
 
diff --git a/src/event/server_events.c b/src/event/server_events.c
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -47,6 +47,7 @@
 #include "config/preferences.h"
 #include "config/tlscerts.h"
 #include "config/account.h"
+#include "config/cafile.h"
 #include "config/scripts.h"
 #include "event/client_events.h"
 #include "event/common.h"
@@ -1134,10 +1135,11 @@ sv_ev_muc_occupant_online(const char* const room, const char* const nick, const
 }
 
 int
-sv_ev_certfail(const char* const errormsg, TLSCertificate* cert)
+sv_ev_certfail(const char* const errormsg, const TLSCertificate* cert)
 {
     // check profanity trusted certs
     if (tlscerts_exists(cert->fingerprint)) {
+        cafile_add(cert);
         return 1;
     }
 
@@ -1181,6 +1183,7 @@ sv_ev_certfail(const char* const errormsg, TLSCertificate* cert)
         cons_show("Adding %s to trusted certificates.", cert->fingerprint);
         if (!tlscerts_exists(cert->fingerprint)) {
             tlscerts_add(cert);
+            cafile_add(cert);
         }
         free(cmd);
         return 1;
diff --git a/src/event/server_events.h b/src/event/server_events.h
--- a/src/event/server_events.h
+++ b/src/event/server_events.h
@@ -86,7 +86,7 @@ void sv_ev_roster_update(const char* const barejid, const char* const name,
                          GSList* groups, const char* const subscription, gboolean pending_out);
 void sv_ev_roster_received(void);
 void sv_ev_connection_features_received(void);
-int sv_ev_certfail(const char* const errormsg, TLSCertificate* cert);
+int sv_ev_certfail(const char* const errormsg, const TLSCertificate* cert);
 void sv_ev_lastactivity_response(const char* const from, const int seconds, const char* const msg);
 void sv_ev_bookmark_autojoin(Bookmark* bookmark);
 
diff --git a/src/tools/http_download.c b/src/tools/http_download.c
--- a/src/tools/http_download.c
+++ b/src/tools/http_download.c
@@ -50,6 +50,7 @@
 #include "profanity.h"
 #include "event/client_events.h"
 #include "tools/http_download.h"
+#include "config/cafile.h"
 #include "config/preferences.h"
 #include "ui/ui.h"
 #include "ui/window.h"
@@ -125,6 +126,10 @@ http_file_get(void* userdata)
     }
 
     char* cert_path = prefs_get_string(PREF_TLS_CERTPATH);
+    gchar* cafile = cafile_get_name();
+    ProfAccount* account = accounts_get_account(session_get_account_name());
+    gboolean insecure = strcmp(account->tls_policy, "trust") == 0;
+    account_free(account);
     pthread_mutex_unlock(&lock);
 
     curl_global_init(CURL_GLOBAL_ALL);
@@ -145,9 +150,16 @@ http_file_get(void* userdata)
 
     curl_easy_setopt(curl, CURLOPT_USERAGENT, "profanity");
 
+    if (cafile) {
+        curl_easy_setopt(curl, CURLOPT_CAINFO, cafile);
+    }
     if (cert_path) {
         curl_easy_setopt(curl, CURLOPT_CAPATH, cert_path);
     }
+    if (insecure) {
+        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
+        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+    }
 
     if ((res = curl_easy_perform(curl)) != CURLE_OK) {
         err = strdup(curl_easy_strerror(res));
@@ -161,6 +173,7 @@ http_file_get(void* userdata)
     }
 
     pthread_mutex_lock(&lock);
+    g_free(cafile);
     g_free(cert_path);
     if (err) {
         if (download->cancel) {
diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c
--- a/src/tools/http_upload.c
+++ b/src/tools/http_upload.c
@@ -48,6 +48,7 @@
 #include "profanity.h"
 #include "event/client_events.h"
 #include "tools/http_upload.h"
+#include "config/cafile.h"
 #include "config/preferences.h"
 #include "ui/ui.h"
 #include "ui/window.h"
@@ -184,6 +185,10 @@ http_file_put(void* userdata)
     g_free(msg);
 
     char* cert_path = prefs_get_string(PREF_TLS_CERTPATH);
+    gchar* cafile = cafile_get_name();
+    ProfAccount* account = accounts_get_account(session_get_account_name());
+    gboolean insecure = strcmp(account->tls_policy, "trust") == 0;
+    account_free(account);
     pthread_mutex_unlock(&lock);
 
     curl_global_init(CURL_GLOBAL_ALL);
@@ -244,9 +249,16 @@ http_file_put(void* userdata)
 
     fh = upload->filehandle;
 
+    if (cafile) {
+        curl_easy_setopt(curl, CURLOPT_CAINFO, cafile);
+    }
     if (cert_path) {
         curl_easy_setopt(curl, CURLOPT_CAPATH, cert_path);
     }
+    if (insecure) {
+        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
+        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+    }
 
     curl_easy_setopt(curl, CURLOPT_READDATA, fh);
     curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)(upload->filesize));
@@ -288,6 +300,7 @@ http_file_put(void* userdata)
     g_free(expires_header);
 
     pthread_mutex_lock(&lock);
+    g_free(cafile);
     g_free(cert_path);
 
     if (err) {
diff --git a/src/ui/console.c b/src/ui/console.c
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -181,7 +181,7 @@ cons_show_error(const char* const msg, ...)
 }
 
 void
-cons_show_tlscert_summary(TLSCertificate* cert)
+cons_show_tlscert_summary(const TLSCertificate* cert)
 {
     if (!cert) {
         return;
@@ -193,7 +193,7 @@ cons_show_tlscert_summary(TLSCertificate* cert)
 }
 
 void
-cons_show_tlscert(TLSCertificate* cert)
+cons_show_tlscert(const TLSCertificate* cert)
 {
     if (!cert) {
         return;
diff --git a/src/ui/ui.h b/src/ui/ui.h
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -333,8 +333,8 @@ void cons_show_contact_online(PContact contact, Resource* resource, GDateTime* l
 void cons_show_contact_offline(PContact contact, char* resource, char* status);
 void cons_theme_properties(void);
 void cons_theme_colours(void);
-void cons_show_tlscert(TLSCertificate* cert);
-void cons_show_tlscert_summary(TLSCertificate* cert);
+void cons_show_tlscert(const TLSCertificate* cert);
+void cons_show_tlscert_summary(const TLSCertificate* cert);
 
 void cons_alert(ProfWin* alert_origin_window);
 void cons_remove_alert(ProfWin* window);
diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c
--- a/src/xmpp/connection.c
+++ b/src/xmpp/connection.c
@@ -1100,7 +1100,8 @@ _xmppcert_to_profcert(const xmpp_tlscert_t* xmpptlscert)
         xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_NOTBEFORE),
         xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_NOTAFTER),
         xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_KEYALG),
-        xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_SIGALG));
+        xmpp_tlscert_get_string(xmpptlscert, XMPP_CERT_SIGALG),
+        xmpp_tlscert_get_pem(xmpptlscert));
 }
 
 static xmpp_log_t*


Test patch:
diff --git a/tests/unittests/config/stub_cafile.c b/tests/unittests/config/stub_cafile.c
new file mode 100644
--- /dev/null
+++ b/tests/unittests/config/stub_cafile.c
@@ -0,0 +1,55 @@
+/*
+ * stub_cafile.c
+ * vim: expandtab:ts=4:sts=4:sw=4
+ *
+ * Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity 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 Profanity.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two.
+ *
+ * You must obey the GNU General Public License in all respects for all of the
+ * code used other than OpenSSL. If you modify file(s) with this exception, you
+ * may extend this exception to your version of the file(s), but you are not
+ * obligated to do so. If you do not wish to do so, delete this exception
+ * statement from your version. If you delete this exception statement from all
+ * source files in the program, then also delete it here.
+ *
+ */
+
+#include <fcntl.h>
+#include <glib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "common.h"
+#include "config/files.h"
+#include "log.h"
+
+void
+cafile_add(const TLSCertificate* cert)
+{
+}
+
+gchar*
+cafile_get_name(void)
+{
+    return NULL;
+}
diff --git a/tests/unittests/ui/stub_ui.c b/tests/unittests/ui/stub_ui.c
--- a/tests/unittests/ui/stub_ui.c
+++ b/tests/unittests/ui/stub_ui.c
@@ -649,11 +649,11 @@ jabber_get_tls_peer_cert(void)
     return NULL;
 }
 void
-cons_show_tlscert(TLSCertificate* cert)
+cons_show_tlscert(const TLSCertificate* cert)
 {
 }
 void
-cons_show_tlscert_summary(TLSCertificate* cert)
+cons_show_tlscert_summary(const TLSCertificate* cert)
 {
 }