#include "alertpanel.h"
#include "utils.h"
#include "intl.h"
+#include "prefs_common.h"
+#include "socket.h"
-static void ssl_certificate_destroy(SSLCertificate *cert);
+static char *ssl_certificate_check_signer (X509 *cert);
+static SSLCertificate *ssl_certificate_new_lookup(X509 *x509_cert, gchar *host, gushort port, gboolean lookup);
-static char * convert_fingerprint(char *src)
+static char * get_fqdn(char *host)
+{
+ struct hostent *hp;
+
+ if (host == NULL || strlen(host) == 0)
+ return g_strdup("");
+
+ hp = my_gethostbyname(host);
+ if (hp == NULL)
+ return g_strdup(host); /*caller should free*/
+ else
+ return g_strdup(hp->h_name);
+}
+
+static char * readable_fingerprint(unsigned char *src, int len)
{
int i=0;
char * ret;
if (src == NULL)
return NULL;
ret = g_strdup("");
- while (src[i] != '\0') {
+ while (i < len) {
char *tmp2;
if(i>0)
tmp2 = g_strdup_printf("%s:%02X", ret, src[i]);
else
tmp2 = g_strdup_printf("%02X", src[i]);
+ g_free(ret);
ret = g_strdup(tmp2);
g_free(tmp2);
i++;
return ret;
}
-SSLCertificate *ssl_certificate_new(gchar *host, gchar *issuer, gchar *subject, gchar *md)
+SSLCertificate *ssl_certificate_new(X509 *x509_cert, gchar *host, gushort port)
+{
+ return ssl_certificate_new_lookup(x509_cert, host, port, TRUE);
+}
+
+static SSLCertificate *ssl_certificate_new_lookup(X509 *x509_cert, gchar *host, gushort port, gboolean lookup)
{
SSLCertificate *cert = g_new0(SSLCertificate, 1);
- if (host == NULL || issuer == NULL || subject == NULL || md == NULL) {
+ if (host == NULL || x509_cert == NULL) {
ssl_certificate_destroy(cert);
return NULL;
}
-
- cert->host = g_strdup(host);
- cert->issuer = g_strdup(issuer);
- cert->subject = g_strdup(subject);
- cert->fingerprint = g_strdup(md);
+ cert->x509_cert = X509_dup(x509_cert);
+ if (lookup)
+ cert->host = get_fqdn(host);
+ else
+ cert->host = g_strdup(host);
+ cert->port = port;
return cert;
}
static void ssl_certificate_save (SSLCertificate *cert)
{
- gchar *file;
+ gchar *file, *port;
FILE *fp;
+
file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
"certs", G_DIR_SEPARATOR_S, NULL);
make_dir_hier(file);
g_free(file);
+ port = g_strdup_printf("%d", cert->port);
file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
"certs", G_DIR_SEPARATOR_S,
- cert->host, ".cert", NULL);
+ cert->host, ".", port, ".cert", NULL);
- fp = fopen(file, "w");
+ g_free(port);
+ fp = fopen(file, "wb");
if (fp == NULL) {
g_free(file);
alertpanel_error(_("Can't save certificate !"));
return;
}
- fputs("issuer=", fp);
- fputs(cert->issuer, fp);
- fputs("\nsubject=", fp);
- fputs(cert->subject, fp);
- fputs("\nfingerprint=", fp);
- fputs(cert->fingerprint, fp);
- fputs("\n", fp);
+ i2d_X509_fp(fp, cert->x509_cert);
+ g_free(file);
fclose(fp);
+
}
-static char* ssl_certificate_to_string(SSLCertificate *cert)
+char* ssl_certificate_to_string(SSLCertificate *cert)
{
- char *ret;
- ret = g_strdup_printf(" Issuer: %s\n Subject: %s\n Fingerprint: %s",
- cert->issuer,
- cert->subject,
- cert->fingerprint);
+ char *ret, buf[100];
+ char *issuer_commonname, *issuer_location, *issuer_organization;
+ char *subject_commonname, *subject_location, *subject_organization;
+ char *fingerprint, *sig_status;
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
+
+ /* issuer */
+ if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
+ NID_commonName, buf, 100) >= 0)
+ issuer_commonname = g_strdup(buf);
+ else
+ issuer_commonname = g_strdup(_("<not in certificate>"));
+ if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
+ NID_localityName, buf, 100) >= 0) {
+ issuer_location = g_strdup(buf);
+ if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
+ NID_countryName, buf, 100) >= 0)
+ issuer_location = g_strconcat(issuer_location,", ",buf, NULL);
+ } else if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
+ NID_countryName, buf, 100) >= 0)
+ issuer_location = g_strdup(buf);
+ else
+ issuer_location = g_strdup(_("<not in certificate>"));
+
+ if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
+ NID_organizationName, buf, 100) >= 0)
+ issuer_organization = g_strdup(buf);
+ else
+ issuer_organization = g_strdup(_("<not in certificate>"));
+
+ /* subject */
+ if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
+ NID_commonName, buf, 100) >= 0)
+ subject_commonname = g_strdup(buf);
+ else
+ subject_commonname = g_strdup(_("<not in certificate>"));
+ if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
+ NID_localityName, buf, 100) >= 0) {
+ subject_location = g_strdup(buf);
+ if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
+ NID_countryName, buf, 100) >= 0)
+ subject_location = g_strconcat(subject_location,", ",buf, NULL);
+ } else if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
+ NID_countryName, buf, 100) >= 0)
+ subject_location = g_strdup(buf);
+ else
+ subject_location = g_strdup(_("<not in certificate>"));
+
+ if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
+ NID_organizationName, buf, 100) >= 0)
+ subject_organization = g_strdup(buf);
+ else
+ subject_organization = g_strdup(_("<not in certificate>"));
+
+ /* fingerprint */
+ X509_digest(cert->x509_cert, EVP_md5(), md, &n);
+ fingerprint = readable_fingerprint(md, (int)n);
+
+ /* signature */
+ sig_status = ssl_certificate_check_signer(cert->x509_cert);
+
+ ret = g_strdup_printf(_(" Owner: %s (%s) in %s\n Signed by: %s (%s) in %s\n Fingerprint: %s\n Signature status: %s"),
+ subject_commonname, subject_organization, subject_location,
+ issuer_commonname, issuer_organization, issuer_location,
+ fingerprint,
+ (sig_status==NULL ? "correct":sig_status));
+
+ if (issuer_commonname)
+ g_free(issuer_commonname);
+ if (issuer_location)
+ g_free(issuer_location);
+ if (issuer_organization)
+ g_free(issuer_organization);
+ if (subject_commonname)
+ g_free(subject_commonname);
+ if (subject_location)
+ g_free(subject_location);
+ if (subject_organization)
+ g_free(subject_organization);
+ if (fingerprint)
+ g_free(fingerprint);
+ if (sig_status)
+ g_free(sig_status);
return ret;
}
void ssl_certificate_destroy(SSLCertificate *cert)
{
- g_return_if_fail(cert != NULL);
+ if (cert == NULL)
+ return;
+
+ if (cert->x509_cert)
+ X509_free(cert->x509_cert);
if (cert->host)
g_free(cert->host);
- if (cert->issuer)
- g_free(cert->issuer);
- if (cert->subject)
- g_free(cert->subject);
- if (cert->fingerprint)
- g_free(cert->fingerprint);
g_free(cert);
cert = NULL;
}
-static SSLCertificate *ssl_certificate_find (gchar *host)
+void ssl_certificate_delete_from_disk(SSLCertificate *cert)
{
+ gchar *buf;
gchar *file;
- gchar buf[1024], *subject, *issuer, *fingerprint;
- SSLCertificate *cert;
+ buf = g_strdup_printf("%d", cert->port);
+ file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ "certs", G_DIR_SEPARATOR_S,
+ cert->host, ".", buf, ".cert", NULL);
+ unlink (file);
+ g_free(buf);
+ g_free(file);
+}
+
+SSLCertificate *ssl_certificate_find (gchar *host, gushort port)
+{
+ return ssl_certificate_find_lookup (host, port, TRUE);
+}
+
+SSLCertificate *ssl_certificate_find_lookup (gchar *host, gushort port, gboolean lookup)
+{
+ gchar *file;
+ gchar *buf;
+ gchar *fqdn_host;
+ SSLCertificate *cert = NULL;
+ X509 *tmp_x509;
FILE *fp;
-
+
+ if (lookup)
+ fqdn_host = get_fqdn(host);
+ else
+ fqdn_host = g_strdup(host);
+
+ buf = g_strdup_printf("%d", port);
file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
"certs", G_DIR_SEPARATOR_S,
- host, ".cert", NULL);
+ fqdn_host, ".", buf, ".cert", NULL);
- fp = fopen(file, "r");
+ g_free(buf);
+ fp = fopen(file, "rb");
if (fp == NULL) {
g_free(file);
+ g_free(fqdn_host);
return NULL;
}
- while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
- if (!strncmp(buf, "subject=", 8)) {
- subject = g_strdup((char *)buf +8);
- g_strdelimit(subject, "\r\n", '\0');
- }
- else if (!strncmp(buf, "issuer=", 7)) {
- issuer = g_strdup((char *)buf +7);
- g_strdelimit(issuer, "\r\n", '\0');
- }
- else if (!strncmp(buf, "fingerprint=", 12)) {
- fingerprint = g_strdup((char *)buf +12);
- g_strdelimit(fingerprint, "\r\n", '\0');
- }
- }
- fclose (fp);
- if (subject && issuer && fingerprint) {
- cert = ssl_certificate_new(host, issuer, subject, fingerprint);
- }
+ if ((tmp_x509 = d2i_X509_fp(fp, 0)) != NULL) {
+ cert = ssl_certificate_new_lookup(tmp_x509, fqdn_host, port, lookup);
+ X509_free(tmp_x509);
+ }
+ fclose(fp);
g_free(file);
+ g_free(fqdn_host);
- if (subject)
- g_free(subject);
- if (issuer)
- g_free(issuer);
- if (fingerprint)
- g_free(fingerprint);
-
return cert;
}
static gboolean ssl_certificate_compare (SSLCertificate *cert_a, SSLCertificate *cert_b)
{
- if (!strcmp(cert_a->issuer, cert_b->issuer)
- && !strcmp(cert_a->subject, cert_b->subject)
- && !strcmp(cert_a->fingerprint, cert_b->fingerprint))
+ if (cert_a == NULL || cert_b == NULL)
+ return FALSE;
+ else if (!X509_cmp(cert_a->x509_cert, cert_b->x509_cert))
return TRUE;
else
return FALSE;
store = X509_STORE_new();
if (store == NULL) {
printf("Can't create X509_STORE\n");
- return FALSE;
+ return NULL;
}
if (X509_STORE_set_default_paths(store))
ok++;
return NULL;
}
-gboolean ssl_certificate_check (X509 *x509_cert, gchar *host, gchar *issuer,
- gchar *subject, gchar *md)
+gboolean ssl_certificate_check (X509 *x509_cert, gchar *host, gushort port)
{
- char *readable_md = convert_fingerprint(md);
- SSLCertificate *current_cert = ssl_certificate_new(host, issuer, subject, readable_md);
+ SSLCertificate *current_cert = ssl_certificate_new(x509_cert, host, port);
SSLCertificate *known_cert;
if (current_cert == NULL) {
debug_print("Buggy certificate !\n");
- debug_print("host: %s\n", host);
- debug_print("issuer: %s\n", issuer);
- debug_print("subject: %s\n", subject);
- debug_print("md: %s\n", readable_md);
- if (readable_md)
- g_free(readable_md);
return FALSE;
}
- if (readable_md)
- g_free(readable_md);
-
- known_cert = ssl_certificate_find (host);
+ known_cert = ssl_certificate_find (host, port);
if (known_cert == NULL) {
gint val;
- gchar *err_msg, *cur_cert_str;
- gchar *sig_status = NULL;
-
- cur_cert_str = ssl_certificate_to_string(current_cert);
+ gchar *err_msg, *cur_cert_str, *sig_status;
sig_status = ssl_certificate_check_signer(x509_cert);
- err_msg = g_strdup_printf(_("The SSL certificate presented by %s is unknown.\nPresented certificate is:\n%s\n\n%s%s"),
- host,
- cur_cert_str,
- (sig_status == NULL)?"The presented certificate signature is correct.":"The presented certificate signature is not correct: ",
- (sig_status == NULL)?"":sig_status);
+ if (sig_status == NULL && !prefs_common.ssl_ask_unknown_valid) {
+ /* trust and accept silently if hostnames match */
+ char *buf; /* don't free buf ! */
+ if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509_cert),
+ NID_commonName, buf, 100) >= 0)
+ if (!strcmp(buf, current_cert->host)) {
+ g_free(sig_status);
+ ssl_certificate_save(current_cert);
+ ssl_certificate_destroy(current_cert);
+ return TRUE;
+ }
+ }
+
+ g_free(sig_status);
+
+ cur_cert_str = ssl_certificate_to_string(current_cert);
+
+ err_msg = g_strdup_printf(_("%s presented an unknown SSL certificate:\n%s"),
+ current_cert->host,
+ cur_cert_str);
g_free (cur_cert_str);
- g_free (sig_status);
-
+
+ if (prefs_common.no_recv_err_panel) {
+ log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
+ err_msg,
+ _("Don't popup error dialog on receive error"));
+ g_free(err_msg);
+ return FALSE;
+ }
+
val = alertpanel(_("Warning"),
err_msg,
_("Accept and save"), _("Cancel connection"), NULL);
}
else if (!ssl_certificate_compare (current_cert, known_cert)) {
gint val;
- gchar *err_msg, *known_cert_str, *cur_cert_str, *sig_status;
+ gchar *err_msg, *known_cert_str, *cur_cert_str;
- sig_status = ssl_certificate_check_signer(x509_cert);
-
known_cert_str = ssl_certificate_to_string(known_cert);
cur_cert_str = ssl_certificate_to_string(current_cert);
- err_msg = g_strdup_printf(_("The SSL certificate presented by %s differs from the known one.\nKnown certificate is:\n%s\nPresented certificate is:\n%s\n\n%s%s"),
- host,
+ err_msg = g_strdup_printf(_("%s's SSL certificate changed !\nWe have saved this one:\n%s\n\nIt is now:\n%s\n\nThis could mean the server answering is not the known one."),
+ current_cert->host,
known_cert_str,
- cur_cert_str,
- (sig_status == NULL)?"The presented certificate signature is correct.":"The presented certificate signature is not correct: ",
- (sig_status == NULL)?"":sig_status);
+ cur_cert_str);
g_free (cur_cert_str);
g_free (known_cert_str);
- g_free (sig_status);
+
+ if (prefs_common.no_recv_err_panel) {
+ log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
+ err_msg,
+ _("Don't popup error dialog on receive error"));
+ g_free(err_msg);
+ return FALSE;
+ }
val = alertpanel(_("Warning"),
err_msg,