/** * mutt_regex_new - Create an Regex from a string * @param str Regular expression * @param flags Type flags, e.g. #DT_REGEX_MATCH_CASE * @param err Buffer for error messages * @retval ptr New Regex object * @retval NULL Error */ struct Regex *mutt_regex_new(const char *str, int flags, struct Buffer *err) { if (!str) return NULL; int rflags = 0; struct Regex *reg = mutt_mem_calloc(1, sizeof(struct Regex)); reg->regex = mutt_mem_calloc(1, sizeof(regex_t)); reg->pattern = mutt_str_strdup(str); /* Should we use smart case matching? */ if (((flags & DT_REGEX_MATCH_CASE) == 0) && mutt_mb_is_lower(str)) rflags |= REG_ICASE; /* Is a prefix of '!' allowed? */ if (((flags & DT_REGEX_ALLOW_NOT) != 0) && (str[0] == '!')) { reg->not = true; str++; } int rc = REGCOMP(reg->regex, str, rflags); if ((rc != 0) && err) { regerror(rc, reg->regex, err->data, err->dsize); mutt_regex_free(®); return NULL; } return reg; }
/** * mutt_regex_compile - Create an Regex from a string * @param str Regular expression * @param flags Type flags, e.g. REG_ICASE * @retval ptr New Regex object * @retval NULL Error */ struct Regex *mutt_regex_compile(const char *str, int flags) { struct Regex *rx = mutt_mem_calloc(1, sizeof(struct Regex)); rx->pattern = mutt_str_strdup(str); rx->regex = mutt_mem_calloc(1, sizeof(regex_t)); if (REGCOMP(rx->regex, NONULL(str), flags) != 0) mutt_regex_free(&rx); return rx; }
/** * mutt_socket_new - allocate and initialise a new connection * @param type Type of the new Connection * @retval ptr New Connection */ struct Connection *mutt_socket_new(enum ConnectionType type) { struct Connection *conn = mutt_mem_calloc(1, sizeof(struct Connection)); conn->fd = -1; if (type == MUTT_CONNECTION_TUNNEL) { mutt_tunnel_socket_setup(conn); } else if (type == MUTT_CONNECTION_SSL) { int ret = mutt_ssl_socket_setup(conn); if (ret < 0) FREE(&conn); } else { conn->conn_read = raw_socket_read; conn->conn_write = raw_socket_write; conn->conn_open = raw_socket_open; conn->conn_close = raw_socket_close; conn->conn_poll = raw_socket_poll; } return conn; }
/** * address_new - Create an Address from a string * @param addr Email address to parse * @retval ptr New Address object */ struct Address *address_new(const char *addr) { struct Address *a = mutt_mem_calloc(1, sizeof(*a)); // a->personal = mutt_str_strdup(addr); a->mailbox = mutt_str_strdup(addr); return a; }
/** * mutt_list_insert_after - Insert a string after a given ListNode * @param h Head of the List * @param n ListNode after which the string will be inserted * @param s String to insert * @retval ptr Newly created ListNode containing the string */ struct ListNode *mutt_list_insert_after(struct ListHead *h, struct ListNode *n, char *s) { struct ListNode *np = mutt_mem_calloc(1, sizeof(struct ListNode)); np->data = s; STAILQ_INSERT_AFTER(h, n, np, entries); return np; }
/** * cs_inherit_variable - Create in inherited config item * @param cs Config items * @param parent HashElem of parent config item * @param name Name of account-specific config item * @retval ptr New HashElem representing the inherited config item */ struct HashElem *cs_inherit_variable(const struct ConfigSet *cs, struct HashElem *parent, const char *name) { if (!cs || !parent) return NULL; /* LCOV_EXCL_LINE */ struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = calloc(1, err.dsize); struct Inheritance *i = mutt_mem_calloc(1, sizeof(*i)); i->parent = parent; i->name = mutt_str_strdup(name); struct HashElem *he = mutt_hash_typed_insert(cs->hash, i->name, DT_INHERITED, i); if (!he) { FREE(&i->name); FREE(&i); } FREE(&err.data); return he; }
/** * mutt_list_insert_tail - Append a string to the end of a List * @param h Head of the List * @param s String to insert * @retval ptr Newly appended ListNode containing the string */ struct ListNode *mutt_list_insert_tail(struct ListHead *h, char *s) { struct ListNode *np = mutt_mem_calloc(1, sizeof(struct ListNode)); np->data = s; STAILQ_INSERT_TAIL(h, np, entries); return np; }
/** * set_compress_info - Find the compress hooks for a mailbox * @param m Mailbox to examine * @retval ptr CompressInfo Hook info for the mailbox's path * @retval NULL Error * * When a mailbox is opened, we check if there are any matching hooks. */ static struct CompressInfo *set_compress_info(struct Mailbox *m) { if (!m) return NULL; if (m->compress_info) return m->compress_info; /* Open is compulsory */ const char *o = find_hook(MUTT_OPEN_HOOK, m->path); if (!o) return NULL; const char *c = find_hook(MUTT_CLOSE_HOOK, m->path); const char *a = find_hook(MUTT_APPEND_HOOK, m->path); struct CompressInfo *ci = mutt_mem_calloc(1, sizeof(struct CompressInfo)); m->compress_info = ci; ci->cmd_open = mutt_str_strdup(o); ci->cmd_close = mutt_str_strdup(c); ci->cmd_append = mutt_str_strdup(a); return ci; }
void config_address(void) { struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = mutt_mem_calloc(1, err.dsize); mutt_buffer_reset(&err); struct ConfigSet *cs = cs_new(30); address_init(cs); dont_fail = true; if (!cs_register_variables(cs, Vars, 0)) return; dont_fail = false; cs_add_listener(cs, log_listener); set_list(cs); TEST_CHECK(test_initial_values(cs, &err)); TEST_CHECK(test_string_set(cs, &err)); TEST_CHECK(test_string_get(cs, &err)); TEST_CHECK(test_native_set(cs, &err)); TEST_CHECK(test_native_get(cs, &err)); TEST_CHECK(test_reset(cs, &err)); TEST_CHECK(test_validator(cs, &err)); TEST_CHECK(test_inherit(cs, &err)); cs_free(&cs); FREE(&err.data); }
/** * address_dup - Create a copy of an Address object * @param addr Address to duplicate * @retval ptr New Address object */ static struct Address *address_dup(struct Address *addr) { if (!addr) return NULL; /* LCOV_EXCL_LINE */ struct Address *a = mutt_mem_calloc(1, sizeof(*a)); a->personal = mutt_str_strdup(addr->personal); a->mailbox = mutt_str_strdup(addr->mailbox); return a; }
/** * tls_get_client_cert - Get the client certificate for a TLS connection * @param conn Connection to a server */ static void tls_get_client_cert(struct Connection *conn) { struct TlsSockData *data = conn->sockdata; gnutls_x509_crt_t clientcrt; char *dn = NULL; char *cn = NULL; char *cnend = NULL; size_t dnlen; /* get our cert CN if we have one */ const gnutls_datum_t *crtdata = gnutls_certificate_get_ours(data->state); if (!crtdata) return; if (gnutls_x509_crt_init(&clientcrt) < 0) { mutt_debug(LL_DEBUG1, "Failed to init gnutls crt\n"); return; } if (gnutls_x509_crt_import(clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0) { mutt_debug(LL_DEBUG1, "Failed to import gnutls client crt\n"); goto err_crt; } /* get length of DN */ dnlen = 0; gnutls_x509_crt_get_dn(clientcrt, NULL, &dnlen); dn = mutt_mem_calloc(1, dnlen); gnutls_x509_crt_get_dn(clientcrt, dn, &dnlen); mutt_debug(LL_DEBUG2, "client certificate DN: %s\n", dn); /* extract CN to use as external user name */ cn = strstr(dn, "CN="); if (!cn) { mutt_debug(LL_DEBUG1, "no CN found in DN\n"); goto err_dn; } cnend = strstr(dn, ",EMAIL="); if (cnend) *cnend = '\0'; /* if we are using a client cert, SASL may expect an external auth name */ if (mutt_account_getuser(&conn->account) < 0) mutt_debug(LL_DEBUG1, "Couldn't get user info\n"); err_dn: FREE(&dn); err_crt: gnutls_x509_crt_deinit(clientcrt); }
/** * comp_ac_add - Add a Mailbox to a Account */ int comp_ac_add(struct Account *a, struct Mailbox *m) { if (!a || !m) return -1; if (m->magic != MUTT_COMPRESSED) return -1; m->account = a; struct MailboxNode *np = mutt_mem_calloc(1, sizeof(*np)); np->mailbox = m; STAILQ_INSERT_TAIL(&a->mailboxes, np, entries); return 0; }
/** * mutt_envlist_init - Create a copy of the environment * @param envp Environment variables */ void mutt_envlist_init(char *envp[]) { if (EnvList) mutt_envlist_free(); if (!envp) return; char **src, **dst; int count = 0; for (src = envp; src && *src; src++) count++; EnvList = mutt_mem_calloc(count + 1, sizeof(char *)); for (src = envp, dst = EnvList; src && *src; src++, dst++) *dst = mutt_str_strdup(*src); }
/** * cs_inherit_variable - Create in inherited config item * @param cs Config items * @param parent HashElem of parent config item * @param name Name of account-specific config item * @retval ptr New HashElem representing the inherited config item */ struct HashElem *cs_inherit_variable(const struct ConfigSet *cs, struct HashElem *parent, const char *name) { if (!cs || !parent) return NULL; struct Inheritance *i = mutt_mem_calloc(1, sizeof(*i)); i->parent = parent; i->name = mutt_str_strdup(name); struct HashElem *he = mutt_hash_typed_insert(cs->hash, i->name, DT_INHERITED, i); if (!he) { FREE(&i->name); FREE(&i); } return he; }
/** * tls_check_one_certificate - Check a GnuTLS certificate * @param certdata List of GnuTLS certificates * @param certstat GnuTLS certificate status * @param hostname Hostname * @param idx Index into certificate list * @param len Length of certificate list * @retval 0 Failure * @retval >0 Success */ static int tls_check_one_certificate(const gnutls_datum_t *certdata, gnutls_certificate_status_t certstat, const char *hostname, int idx, size_t len) { int certerr, savedcert; gnutls_x509_crt_t cert; char buf[128]; char fpbuf[128]; size_t buflen; char dn_common_name[128]; char dn_email[128]; char dn_organization[128]; char dn_organizational_unit[128]; char dn_locality[128]; char dn_province[128]; char dn_country[128]; time_t t; char datestr[30]; struct Menu *menu = NULL; char helpstr[1024]; char title[256]; FILE *fp = NULL; gnutls_datum_t pemdata; int row, done, ret; if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0) return 1; /* interactive check from user */ if (gnutls_x509_crt_init(&cert) < 0) { mutt_error(_("Error initialising gnutls certificate data")); return 0; } if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0) { mutt_error(_("Error processing certificate data")); gnutls_x509_crt_deinit(cert); return 0; } menu = mutt_menu_new(MENU_GENERIC); menu->max = 27; menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *)); for (int i = 0; i < menu->max; i++) menu->dialog[i] = mutt_mem_calloc(1, dialog_row_len * sizeof(char)); mutt_menu_push_current(menu); row = 0; mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), dialog_row_len); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) { dn_common_name[0] = '\0'; } buflen = sizeof(dn_email); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) dn_email[0] = '\0'; buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) { dn_organization[0] = '\0'; } buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) { dn_organizational_unit[0] = '\0'; } buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) { dn_locality[0] = '\0'; } buflen = sizeof(dn_province); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) { dn_province[0] = '\0'; } buflen = sizeof(dn_country); if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) { dn_country[0] = '\0'; } snprintf(menu->dialog[row++], dialog_row_len, " %s %s", dn_common_name, dn_email); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organization); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organizational_unit); snprintf(menu->dialog[row++], dialog_row_len, " %s %s %s", dn_locality, dn_province, dn_country); row++; mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), dialog_row_len); row++; buflen = sizeof(dn_common_name); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn_common_name, &buflen) != 0) { dn_common_name[0] = '\0'; } buflen = sizeof(dn_email); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0) { dn_email[0] = '\0'; } buflen = sizeof(dn_organization); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, dn_organization, &buflen) != 0) { dn_organization[0] = '\0'; } buflen = sizeof(dn_organizational_unit); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, dn_organizational_unit, &buflen) != 0) { dn_organizational_unit[0] = '\0'; } buflen = sizeof(dn_locality); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, dn_locality, &buflen) != 0) { dn_locality[0] = '\0'; } buflen = sizeof(dn_province); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, dn_province, &buflen) != 0) { dn_province[0] = '\0'; } buflen = sizeof(dn_country); if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, dn_country, &buflen) != 0) { dn_country[0] = '\0'; } snprintf(menu->dialog[row++], dialog_row_len, " %s %s", dn_common_name, dn_email); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organization); snprintf(menu->dialog[row++], dialog_row_len, " %s", dn_organizational_unit); snprintf(menu->dialog[row++], dialog_row_len, " %s %s %s", dn_locality, dn_province, dn_country); row++; snprintf(menu->dialog[row++], dialog_row_len, _("This certificate is valid")); t = gnutls_x509_crt_get_activation_time(cert); mutt_date_make_tls(datestr, sizeof(datestr), t); snprintf(menu->dialog[row++], dialog_row_len, _(" from %s"), datestr); t = gnutls_x509_crt_get_expiration_time(cert); mutt_date_make_tls(datestr, sizeof(datestr), t); snprintf(menu->dialog[row++], dialog_row_len, _(" to %s"), datestr); fpbuf[0] = '\0'; tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata); snprintf(menu->dialog[row++], dialog_row_len, _("SHA1 Fingerprint: %s"), fpbuf); fpbuf[0] = '\0'; fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */ tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata); fpbuf[39] = '\0'; /* Divide into two lines of output */ snprintf(menu->dialog[row++], dialog_row_len, "%s%s", _("SHA256 Fingerprint: "), fpbuf); snprintf(menu->dialog[row++], dialog_row_len, "%*s%s", (int) mutt_str_strlen(_("SHA256 Fingerprint: ")), "", fpbuf + 40); if (certerr & CERTERR_NOTYETVALID) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate is not yet valid"), dialog_row_len); } if (certerr & CERTERR_EXPIRED) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate has expired"), dialog_row_len); } if (certerr & CERTERR_REVOKED) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server certificate has been revoked"), dialog_row_len); } if (certerr & CERTERR_HOSTNAME) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Server hostname does not match certificate"), dialog_row_len); } if (certerr & CERTERR_SIGNERNOTCA) { row++; mutt_str_strfcpy(menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), dialog_row_len); } snprintf(title, sizeof(title), _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len); menu->title = title; /* certificates with bad dates, or that are revoked, must be * accepted manually each and every time */ if (C_CertificateFile && !savedcert && !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED))) { menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always"); /* L10N: These three letters correspond to the choices in the string: (r)eject, accept (o)nce, (a)ccept always. This is an interactive certificate confirmation prompt for a GNUTLS connection. */ menu->keys = _("roa"); } else { menu->prompt = _("(r)eject, accept (o)nce"); /* L10N: These two letters correspond to the choices in the string: (r)eject, accept (o)nce. These is an interactive certificate confirmation prompt for a GNUTLS connection. */ menu->keys = _("ro"); } helpstr[0] = '\0'; mutt_make_help(buf, sizeof(buf), _("Exit "), MENU_GENERIC, OP_EXIT); mutt_str_strcat(helpstr, sizeof(helpstr), buf); mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP); mutt_str_strcat(helpstr, sizeof(helpstr), buf); menu->help = helpstr; done = 0; OptIgnoreMacroEvents = true; while (done == 0) { switch (mutt_menu_loop(menu)) { case -1: /* abort */ case OP_MAX + 1: /* reject */ case OP_EXIT: done = 1; break; case OP_MAX + 3: /* accept always */ done = 0; fp = mutt_file_fopen(C_CertificateFile, "a"); if (fp) { /* save hostname if necessary */ if (certerr & CERTERR_HOSTNAME) { fpbuf[0] = '\0'; tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata); fprintf(fp, "#H %s %s\n", hostname, fpbuf); done = 1; } /* Save the cert for all other errors */ if (certerr ^ CERTERR_HOSTNAME) { done = 0; ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata); if (ret == 0) { if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1) { done = 1; } gnutls_free(pemdata.data); } } mutt_file_fclose(&fp); } if (done == 0) { mutt_error(_("Warning: Couldn't save certificate")); } else { mutt_message(_("Certificate saved")); mutt_sleep(0); } /* fallthrough */ case OP_MAX + 2: /* accept once */ done = 2; break; } } OptIgnoreMacroEvents = false; mutt_menu_pop_current(menu); mutt_menu_destroy(&menu); gnutls_x509_crt_deinit(cert); return done == 2; }
void config_set(void) { log_line(__func__); struct Buffer err; mutt_buffer_init(&err); err.dsize = 256; err.data = mutt_mem_calloc(1, err.dsize); mutt_buffer_reset(&err); struct ConfigSet *cs = cs_new(30); if (!TEST_CHECK(cs != NULL)) return; cs_add_listener(cs, log_listener); cs_add_listener(cs, log_listener); /* dupe */ cs_remove_listener(cs, log_listener); cs_remove_listener(cs, log_listener); /* non-existant */ const struct ConfigSetType cst_dummy = { "dummy", NULL, NULL, NULL, NULL, NULL, NULL, }; if (TEST_CHECK(!cs_register_type(cs, DT_STRING, &cst_dummy))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } const struct ConfigSetType cst_dummy2 = { "dummy2", dummy_string_set, dummy_string_get, dummy_native_set, dummy_native_get, dummy_reset, dummy_destroy, }; if (TEST_CHECK(!cs_register_type(cs, 25, &cst_dummy2))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } bool_init(cs); bool_init(cs); /* second one should fail */ if (TEST_CHECK(!cs_register_variables(cs, Vars, 0))) { TEST_MSG("Expected error\n"); } else { TEST_MSG("This test should have failed\n"); return; } const char *name = "Unknown"; int result = cs_str_string_set(cs, name, "hello", &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 1\n"); return; } result = cs_str_string_get(cs, name, &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 2\n"); return; } result = cs_str_native_set(cs, name, IP "hello", &err); if (TEST_CHECK(CSR_RESULT(result) == CSR_ERR_UNKNOWN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 3\n"); return; } intptr_t native = cs_str_native_get(cs, name, &err); if (TEST_CHECK(native == INT_MIN)) { TEST_MSG("Expected error: Unknown var '%s'\n", name); } else { TEST_MSG("This should have failed 4\n"); return; } struct HashElem *he = cs_get_elem(cs, "Banana"); if (!TEST_CHECK(he != NULL)) return; set_list(cs); const struct ConfigSetType *cst = cs_get_type_def(cs, 15); if (!TEST_CHECK(!cst)) return; cs_free(&cs); FREE(&err.data); log_line(__func__); }
/** * ssl_setup - Set up SSL on the Connection * @param conn Connection * @retval 0 Success * @retval -1 Failure */ static int ssl_setup(struct Connection *conn) { struct SslSockData *ssldata = NULL; int maxbits; ssldata = mutt_mem_calloc(1, sizeof(struct SslSockData)); conn->sockdata = ssldata; ssldata->sctx = SSL_CTX_new(SSLv23_client_method()); if (!ssldata->sctx) { /* L10N: an SSL context is a data structure returned by the OpenSSL function SSL_CTX_new(). In this case it returned NULL: an error condition. */ mutt_error(_("Unable to create SSL context")); ssl_dprint_err_stack(); goto free_sasldata; } /* disable SSL protocols as needed */ #ifdef SSL_OP_NO_TLSv1_2 if (!C_SslUseTlsv12) SSL_CTX_set_options(ssldata->sctx, SSL_OP_NO_TLSv1_2); #endif #ifdef SSL_OP_NO_TLSv1_1 if (!C_SslUseTlsv11) SSL_CTX_set_options(ssldata->sctx, SSL_OP_NO_TLSv1_1); #endif #ifdef SSL_OP_NO_TLSv1 if (!C_SslUseTlsv1) SSL_CTX_set_options(ssldata->sctx, SSL_OP_NO_TLSv1); #endif if (!C_SslUseSslv3) SSL_CTX_set_options(ssldata->sctx, SSL_OP_NO_SSLv3); if (!C_SslUseSslv2) SSL_CTX_set_options(ssldata->sctx, SSL_OP_NO_SSLv2); if (C_SslUsesystemcerts) { if (!SSL_CTX_set_default_verify_paths(ssldata->sctx)) { mutt_debug(LL_DEBUG1, "Error setting default verify paths\n"); goto free_ctx; } } if (C_CertificateFile && !ssl_load_certificates(ssldata->sctx)) mutt_debug(LL_DEBUG1, "Error loading trusted certificates\n"); ssl_get_client_cert(ssldata, conn); if (C_SslCiphers) { SSL_CTX_set_cipher_list(ssldata->sctx, C_SslCiphers); } if (ssl_set_verify_partial(ssldata->sctx)) { mutt_error(_("Warning: error enabling ssl_verify_partial_chains")); } ssldata->ssl = SSL_new(ssldata->sctx); SSL_set_fd(ssldata->ssl, conn->fd); if (ssl_negotiate(conn, ssldata)) goto free_ssl; ssldata->isopen = 1; conn->ssf = SSL_CIPHER_get_bits(SSL_get_current_cipher(ssldata->ssl), &maxbits); return 0; free_ssl: SSL_free(ssldata->ssl); ssldata->ssl = 0; free_ctx: SSL_CTX_free(ssldata->sctx); ssldata->sctx = 0; free_sasldata: FREE(&ssldata); return -1; }
/** * interactive_check_cert - Ask the user if a certificate is valid * @param cert Certificate * @param idx Place of certificate in the chain * @param len Length of the certificate chain * @param ssl SSL state * @param allow_always If certificate may be always allowed * @retval true User selected 'skip' * @retval false Otherwise */ static bool interactive_check_cert(X509 *cert, int idx, size_t len, SSL *ssl, bool allow_always) { static const int part[] = { NID_commonName, /* CN */ NID_pkcs9_emailAddress, /* Email */ NID_organizationName, /* O */ NID_organizationalUnitName, /* OU */ NID_localityName, /* L */ NID_stateOrProvinceName, /* ST */ NID_countryName, /* C */ }; X509_NAME *x509_subject = NULL; X509_NAME *x509_issuer = NULL; char helpstr[1024]; char buf[256]; char title[256]; struct Menu *menu = mutt_menu_new(MENU_GENERIC); int done, row; FILE *fp = NULL; int ALLOW_SKIP = 0; /* All caps tells Coverity that this is effectively a preproc condition */ mutt_menu_push_current(menu); menu->max = mutt_array_size(part) * 2 + 11; menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *)); for (int i = 0; i < menu->max; i++) menu->dialog[i] = mutt_mem_calloc(1, dialog_row_len * sizeof(char)); row = 0; mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), dialog_row_len); row++; x509_subject = X509_get_subject_name(cert); for (unsigned int u = 0; u < mutt_array_size(part); u++) { snprintf(menu->dialog[row++], dialog_row_len, " %s", x509_get_part(x509_subject, part[u])); } row++; mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), dialog_row_len); row++; x509_issuer = X509_get_issuer_name(cert); for (unsigned int u = 0; u < mutt_array_size(part); u++) { snprintf(menu->dialog[row++], dialog_row_len, " %s", x509_get_part(x509_issuer, part[u])); } row++; snprintf(menu->dialog[row++], dialog_row_len, "%s", _("This certificate is valid")); snprintf(menu->dialog[row++], dialog_row_len, _(" from %s"), asn1time_to_string(X509_getm_notBefore(cert))); snprintf(menu->dialog[row++], dialog_row_len, _(" to %s"), asn1time_to_string(X509_getm_notAfter(cert))); row++; buf[0] = '\0'; x509_fingerprint(buf, sizeof(buf), cert, EVP_sha1); snprintf(menu->dialog[row++], dialog_row_len, _("SHA1 Fingerprint: %s"), buf); buf[0] = '\0'; buf[40] = '\0'; /* Ensure the second printed line is null terminated */ x509_fingerprint(buf, sizeof(buf), cert, EVP_sha256); buf[39] = '\0'; /* Divide into two lines of output */ snprintf(menu->dialog[row++], dialog_row_len, "%s%s", _("SHA256 Fingerprint: "), buf); snprintf(menu->dialog[row++], dialog_row_len, "%*s%s", (int) mutt_str_strlen(_("SHA256 Fingerprint: ")), "", buf + 40); snprintf(title, sizeof(title), _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len); menu->title = title; /* The leaf/host certificate can't be skipped. */ #ifdef HAVE_SSL_PARTIAL_CHAIN if ((idx != 0) && C_SslVerifyPartialChains) ALLOW_SKIP = 1; #endif /* Inside ssl_verify_callback(), this function is guarded by a call to * check_certificate_by_digest(). This means if check_certificate_expiration() is * true, then check_certificate_file() must be false. Therefore we don't need * to also scan the certificate file here. */ allow_always = allow_always && C_CertificateFile && check_certificate_expiration(cert, true); /* L10N: These four letters correspond to the choices in the next four strings: (r)eject, accept (o)nce, (a)ccept always, (s)kip. These prompts are the interactive certificate confirmation prompts for an OpenSSL connection. */ menu->keys = _("roas"); if (allow_always) { if (ALLOW_SKIP) menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always, (s)kip"); else menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always"); } else { if (ALLOW_SKIP) menu->prompt = _("(r)eject, accept (o)nce, (s)kip"); else menu->prompt = _("(r)eject, accept (o)nce"); } helpstr[0] = '\0'; mutt_make_help(buf, sizeof(buf), _("Exit "), MENU_GENERIC, OP_EXIT); mutt_str_strcat(helpstr, sizeof(helpstr), buf); mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP); mutt_str_strcat(helpstr, sizeof(helpstr), buf); menu->help = helpstr; done = 0; OptIgnoreMacroEvents = true; while (done == 0) { switch (mutt_menu_loop(menu)) { case -1: /* abort */ case OP_MAX + 1: /* reject */ case OP_EXIT: done = 1; break; case OP_MAX + 3: /* accept always */ if (!allow_always) break; done = 0; fp = fopen(C_CertificateFile, "a"); if (fp) { if (PEM_write_X509(fp, cert)) done = 1; mutt_file_fclose(&fp); } if (done == 0) { mutt_error(_("Warning: Couldn't save certificate")); } else { mutt_message(_("Certificate saved")); mutt_sleep(0); } /* fallthrough */ case OP_MAX + 2: /* accept once */ done = 2; SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL); ssl_cache_trusted_cert(cert); break; case OP_MAX + 4: /* skip */ if (!ALLOW_SKIP) break; done = 2; SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex); break; } } OptIgnoreMacroEvents = false; mutt_menu_pop_current(menu); mutt_menu_destroy(&menu); mutt_debug(LL_DEBUG2, "done=%d\n", done); return done == 2; }
/** * mbox_mbox_sync - Implements MxOps::mbox_sync() */ static int mbox_mbox_sync(struct Mailbox *m, int *index_hint) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; char tempfile[PATH_MAX]; char buf[32]; int i, j; enum SortType save_sort = SORT_ORDER; int rc = -1; int need_sort = 0; /* flag to resort mailbox if new mail arrives */ int first = -1; /* first message to be written */ LOFF_T offset; /* location in mailbox to write changed messages */ struct stat statbuf; struct MUpdate *new_offset = NULL; struct MUpdate *old_offset = NULL; FILE *fp = NULL; struct Progress progress; char msgbuf[PATH_MAX + 64]; /* sort message by their position in the mailbox on disk */ if (C_Sort != SORT_ORDER) { save_sort = C_Sort; C_Sort = SORT_ORDER; mutt_mailbox_changed(m, MBN_RESORT); C_Sort = save_sort; need_sort = 1; } /* need to open the file for writing in such a way that it does not truncate * the file, so use read-write mode. */ adata->fp = freopen(m->path, "r+", adata->fp); if (!adata->fp) { mx_fastclose_mailbox(m); mutt_error(_("Fatal error! Could not reopen mailbox!")); return -1; } mutt_sig_block(); if (mbox_lock_mailbox(m, true, true) == -1) { mutt_sig_unblock(); mutt_error(_("Unable to lock mailbox")); goto bail; } /* Check to make sure that the file hasn't changed on disk */ i = mbox_mbox_check(m, index_hint); if ((i == MUTT_NEW_MAIL) || (i == MUTT_REOPENED)) { /* new mail arrived, or mailbox reopened */ rc = i; goto bail; } else if (i < 0) { /* fatal error */ return -1; } /* Create a temporary file to write the new version of the mailbox in. */ mutt_mktemp(tempfile, sizeof(tempfile)); int fd = open(tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600); if ((fd == -1) || !(fp = fdopen(fd, "w"))) { if (fd != -1) { close(fd); unlink(tempfile); } mutt_error(_("Could not create temporary file")); goto bail; } /* find the first deleted/changed message. we save a lot of time by only * rewriting the mailbox from the point where it has actually changed. */ for (i = 0; (i < m->msg_count) && !m->emails[i]->deleted && !m->emails[i]->changed && !m->emails[i]->attach_del; i++) { } if (i == m->msg_count) { /* this means ctx->changed or m->msg_deleted was set, but no * messages were found to be changed or deleted. This should * never happen, is we presume it is a bug in neomutt. */ mutt_error( _("sync: mbox modified, but no modified messages (report this bug)")); mutt_debug(LL_DEBUG1, "no modified messages\n"); unlink(tempfile); goto bail; } /* save the index of the first changed/deleted message */ first = i; /* where to start overwriting */ offset = m->emails[i]->offset; /* the offset stored in the header does not include the MMDF_SEP, so make * sure we seek to the correct location */ if (m->magic == MUTT_MMDF) offset -= (sizeof(MMDF_SEP) - 1); /* allocate space for the new offsets */ new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate)); old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate)); if (!m->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Writing %s..."), m->path); mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, C_WriteInc, m->msg_count); } for (i = first, j = 0; i < m->msg_count; i++) { if (!m->quiet) mutt_progress_update(&progress, i, (int) (ftello(adata->fp) / (m->size / 100 + 1))); /* back up some information which is needed to restore offsets when * something fails. */ old_offset[i - first].valid = true; old_offset[i - first].hdr = m->emails[i]->offset; old_offset[i - first].body = m->emails[i]->content->offset; old_offset[i - first].lines = m->emails[i]->lines; old_offset[i - first].length = m->emails[i]->content->length; if (!m->emails[i]->deleted) { j++; if (m->magic == MUTT_MMDF) { if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } } /* save the new offset for this message. we add 'offset' because the * temporary file only contains saved message which are located after * 'offset' in the real mailbox */ new_offset[i - first].hdr = ftello(fp) + offset; if (mutt_copy_message_ctx(fp, m, m->emails[i], MUTT_CM_UPDATE, CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0) { mutt_perror(tempfile); unlink(tempfile); goto bail; } /* Since messages could have been deleted, the offsets stored in memory * will be wrong, so update what we can, which is the offset of this * message, and the offset of the body. If this is a multipart message, * we just flush the in memory cache so that the message will be reparsed * if the user accesses it later. */ new_offset[i - first].body = ftello(fp) - m->emails[i]->content->length + offset; mutt_body_free(&m->emails[i]->content->parts); switch (m->magic) { case MUTT_MMDF: if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } break; default: if (fputs("\n", fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } } } } if (fclose(fp) != 0) { fp = NULL; mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n"); unlink(tempfile); mutt_perror(tempfile); goto bail; } fp = NULL; /* Save the state of this folder. */ if (stat(m->path, &statbuf) == -1) { mutt_perror(m->path); unlink(tempfile); goto bail; } fp = fopen(tempfile, "r"); if (!fp) { mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n"); mutt_perror(tempfile); FREE(&new_offset); FREE(&old_offset); return -1; } if ((fseeko(adata->fp, offset, SEEK_SET) != 0) || /* seek the append location */ /* do a sanity check to make sure the mailbox looks ok */ !fgets(buf, sizeof(buf), adata->fp) || ((m->magic == MUTT_MBOX) && !mutt_str_startswith(buf, "From ", CASE_MATCH)) || ((m->magic == MUTT_MMDF) && (mutt_str_strcmp(MMDF_SEP, buf) != 0))) { mutt_debug(LL_DEBUG1, "message not in expected position\n"); mutt_debug(LL_DEBUG1, "\tLINE: %s\n", buf); i = -1; } else { if (fseeko(adata->fp, offset, SEEK_SET) != 0) /* return to proper offset */ { i = -1; mutt_debug(LL_DEBUG1, "fseek() failed\n"); } else { /* copy the temp mailbox back into place starting at the first * change/deleted message */ if (!m->quiet) mutt_message(_("Committing changes...")); i = mutt_file_copy_stream(fp, adata->fp); if (ferror(adata->fp)) i = -1; } if (i == 0) { m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */ if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0)) { i = -1; mutt_debug(LL_DEBUG1, "ftruncate() failed\n"); } } } mutt_file_fclose(&fp); fp = NULL; mbox_unlock_mailbox(m); if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1)) { /* error occurred while writing the mailbox back, so keep the temp copy around */ char savefile[PATH_MAX]; snprintf(savefile, sizeof(savefile), "%s/neomutt.%s-%s-%u", NONULL(C_Tmpdir), NONULL(Username), NONULL(ShortHostname), (unsigned int) getpid()); rename(tempfile, savefile); mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_pretty_mailbox(savefile, sizeof(savefile)); mutt_error(_("Write failed! Saved partial mailbox to %s"), savefile); FREE(&new_offset); FREE(&old_offset); return -1; } /* Restore the previous access/modification times */ mbox_reset_atime(m, &statbuf); /* reopen the mailbox in read-only mode */ adata->fp = fopen(m->path, "r"); if (!adata->fp) { unlink(tempfile); mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_error(_("Fatal error! Could not reopen mailbox!")); FREE(&new_offset); FREE(&old_offset); return -1; } /* update the offsets of the rewritten messages */ for (i = first, j = first; i < m->msg_count; i++) { if (!m->emails[i]->deleted) { m->emails[i]->offset = new_offset[i - first].hdr; m->emails[i]->content->hdr_offset = new_offset[i - first].hdr; m->emails[i]->content->offset = new_offset[i - first].body; m->emails[i]->index = j++; } } FREE(&new_offset); FREE(&old_offset); unlink(tempfile); /* remove partial copy of the mailbox */ mutt_sig_unblock(); if (C_CheckMboxSize) { struct Mailbox *tmp = mutt_find_mailbox(m->path); if (tmp && !tmp->has_new) mutt_update_mailbox(tmp); } return 0; /* signal success */ bail: /* Come here in case of disaster */ mutt_file_fclose(&fp); /* restore offsets, as far as they are valid */ if ((first >= 0) && old_offset) { for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++) { m->emails[i]->offset = old_offset[i - first].hdr; m->emails[i]->content->hdr_offset = old_offset[i - first].hdr; m->emails[i]->content->offset = old_offset[i - first].body; m->emails[i]->lines = old_offset[i - first].lines; m->emails[i]->content->length = old_offset[i - first].length; } } /* this is ok to call even if we haven't locked anything */ mbox_unlock_mailbox(m); mutt_sig_unblock(); FREE(&new_offset); FREE(&old_offset); adata->fp = freopen(m->path, "r", adata->fp); if (!adata->fp) { mutt_error(_("Could not reopen mailbox")); mx_fastclose_mailbox(m); return -1; } if (need_sort) { /* if the mailbox was reopened, the thread tree will be invalid so make * sure to start threading from scratch. */ mutt_mailbox_changed(m, MBN_RESORT); } return rc; }
/** * mbox_adata_new - Create a new MboxAccountData struct * @retval ptr New MboxAccountData */ static struct MboxAccountData *mbox_adata_new(void) { return mutt_mem_calloc(1, sizeof(struct MboxAccountData)); }
static bool test_initial_values(struct ConfigSet *cs, struct Buffer *err) { log_line(__func__); TEST_MSG("Apple = '%s'\n", VarApple->mailbox); TEST_MSG("Banana = '%s'\n", VarBanana->mailbox); const char *apple_orig = "*****@*****.**"; const char *banana_orig = "*****@*****.**"; if (!TEST_CHECK(mutt_str_strcmp(VarApple->mailbox, apple_orig) == 0)) { TEST_MSG("Error: initial values were wrong\n"); return false; } if (!TEST_CHECK(mutt_str_strcmp(VarBanana->mailbox, banana_orig) == 0)) { TEST_MSG("Error: initial values were wrong\n"); return false; } cs_str_string_set(cs, "Apple", "*****@*****.**", err); cs_str_string_set(cs, "Banana", NULL, err); struct Buffer value; mutt_buffer_init(&value); value.dsize = 256; value.data = mutt_mem_calloc(1, value.dsize); mutt_buffer_reset(&value); int rc; mutt_buffer_reset(&value); rc = cs_str_initial_get(cs, "Apple", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } if (!TEST_CHECK(mutt_str_strcmp(value.data, apple_orig) == 0)) { TEST_MSG("Apple's initial value is wrong: '%s'\n", value.data); FREE(&value.data); return false; } TEST_MSG("Apple = '%s'\n", VarApple->mailbox); TEST_MSG("Apple's initial value is '%s'\n", value.data); mutt_buffer_reset(&value); rc = cs_str_initial_get(cs, "Banana", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } if (!TEST_CHECK(mutt_str_strcmp(value.data, banana_orig) == 0)) { TEST_MSG("Banana's initial value is wrong: '%s'\n", value.data); FREE(&value.data); return false; } TEST_MSG("Banana = '%s'\n", VarBanana ? VarBanana->mailbox : ""); TEST_MSG("Banana's initial value is '%s'\n", NONULL(value.data)); mutt_buffer_reset(&value); rc = cs_str_initial_set(cs, "Cherry", "*****@*****.**", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } mutt_buffer_reset(&value); rc = cs_str_initial_set(cs, "Cherry", "*****@*****.**", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } mutt_buffer_reset(&value); rc = cs_str_initial_get(cs, "Cherry", &value); if (!TEST_CHECK(CSR_RESULT(rc) == CSR_SUCCESS)) { TEST_MSG("%s\n", value.data); FREE(&value.data); return false; } TEST_MSG("Cherry = '%s'\n", VarCherry ? VarCherry->mailbox : ""); TEST_MSG("Cherry's initial value is '%s'\n", NONULL(value.data)); FREE(&value.data); log_line(__func__); return true; }
/** * tls_compare_certificates - Compare certificates against #C_CertificateFile * @param peercert Certificate * @retval 1 Certificate matches file * @retval 0 Error, or no match */ static int tls_compare_certificates(const gnutls_datum_t *peercert) { gnutls_datum_t cert; unsigned char *ptr = NULL; gnutls_datum_t b64_data; unsigned char *b64_data_data = NULL; struct stat filestat; if (stat(C_CertificateFile, &filestat) == -1) return 0; b64_data.size = filestat.st_size + 1; b64_data_data = mutt_mem_calloc(1, b64_data.size); b64_data_data[b64_data.size - 1] = '\0'; b64_data.data = b64_data_data; FILE *fp = fopen(C_CertificateFile, "r"); if (!fp) return 0; b64_data.size = fread(b64_data.data, 1, b64_data.size, fp); mutt_file_fclose(&fp); do { const int ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert); if (ret != 0) { FREE(&b64_data_data); return 0; } /* find start of cert, skipping junk */ ptr = (unsigned char *) strstr((char *) b64_data.data, CERT_SEP); if (!ptr) { gnutls_free(cert.data); FREE(&b64_data_data); return 0; } /* find start of next cert */ ptr = (unsigned char *) strstr((char *) ptr + 1, CERT_SEP); b64_data.size = b64_data.size - (ptr - b64_data.data); b64_data.data = ptr; if (cert.size == peercert->size) { if (memcmp(cert.data, peercert->data, cert.size) == 0) { /* match found */ gnutls_free(cert.data); FREE(&b64_data_data); return 1; } } gnutls_free(cert.data); } while (ptr); /* no match found */ FREE(&b64_data_data); return 0; }
/** * tls_negotiate - Negotiate TLS connection * @param conn Connection to a server * @retval 0 Success * @retval -1 Error * * After TLS state has been initialized, attempt to negotiate TLS over the * wire, including certificate checks. */ static int tls_negotiate(struct Connection *conn) { struct TlsSockData *data = mutt_mem_calloc(1, sizeof(struct TlsSockData)); conn->sockdata = data; int err = gnutls_certificate_allocate_credentials(&data->xcred); if (err < 0) { FREE(&conn->sockdata); mutt_error("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err)); return -1; } gnutls_certificate_set_x509_trust_file(data->xcred, C_CertificateFile, GNUTLS_X509_FMT_PEM); /* ignore errors, maybe file doesn't exist yet */ if (C_SslCaCertificatesFile) { gnutls_certificate_set_x509_trust_file(data->xcred, C_SslCaCertificatesFile, GNUTLS_X509_FMT_PEM); } if (C_SslClientCert) { mutt_debug(LL_DEBUG2, "Using client certificate %s\n", C_SslClientCert); gnutls_certificate_set_x509_key_file(data->xcred, C_SslClientCert, C_SslClientCert, GNUTLS_X509_FMT_PEM); } #ifdef HAVE_DECL_GNUTLS_VERIFY_DISABLE_TIME_CHECKS /* disable checking certificate activation/expiration times * in gnutls, we do the checks ourselves */ gnutls_certificate_set_verify_flags(data->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); #endif err = gnutls_init(&data->state, GNUTLS_CLIENT); if (err) { mutt_error("gnutls_handshake: %s", gnutls_strerror(err)); goto fail; } /* set socket */ gnutls_transport_set_ptr(data->state, (gnutls_transport_ptr_t)(long) conn->fd); if (gnutls_server_name_set(data->state, GNUTLS_NAME_DNS, conn->account.host, mutt_str_strlen(conn->account.host))) { mutt_error(_("Warning: unable to set TLS SNI host name")); } if (tls_set_priority(data) < 0) { goto fail; } if (C_SslMinDhPrimeBits > 0) { gnutls_dh_set_prime_bits(data->state, C_SslMinDhPrimeBits); } /* gnutls_set_cred (data->state, GNUTLS_ANON, NULL); */ gnutls_credentials_set(data->state, GNUTLS_CRD_CERTIFICATE, data->xcred); err = gnutls_handshake(data->state); while (err == GNUTLS_E_AGAIN) { err = gnutls_handshake(data->state); } if (err < 0) { if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) { mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err), gnutls_alert_get_name(gnutls_alert_get(data->state))); } else { mutt_error("gnutls_handshake: %s", gnutls_strerror(err)); } goto fail; } if (tls_check_certificate(conn) == 0) goto fail; /* set Security Strength Factor (SSF) for SASL */ /* NB: gnutls_cipher_get_key_size() returns key length in bytes */ conn->ssf = gnutls_cipher_get_key_size(gnutls_cipher_get(data->state)) * 8; tls_get_client_cert(conn); if (!OptNoCurses) { mutt_message(_("SSL/TLS connection using %s (%s/%s/%s)"), gnutls_protocol_get_name(gnutls_protocol_get_version(data->state)), gnutls_kx_get_name(gnutls_kx_get(data->state)), gnutls_cipher_get_name(gnutls_cipher_get(data->state)), gnutls_mac_get_name(gnutls_mac_get(data->state))); mutt_sleep(0); } return 0; fail: gnutls_certificate_free_credentials(data->xcred); gnutls_deinit(data->state); FREE(&conn->sockdata); return -1; }