コード例 #1
0
ファイル: curs_lib.c プロジェクト: darnir/neomutt
/**
 * format_s_x - Format a string like snprintf()
 * @param[out] buf      Buffer in which to save string
 * @param[in]  buflen   Buffer length
 * @param[in]  prec     Field precision, e.g. "-3.4"
 * @param[in]  s        String to format
 * @param[in]  arboreal  If true, string contains graphical tree characters
 *
 * This formats a string rather like:
 * - snprintf(fmt, sizeof(fmt), "%%%ss", prec);
 * - snprintf(buf, buflen, fmt, s);
 * except that the numbers in the conversion specification refer to
 * the number of character cells when printed.
 */
static void format_s_x(char *buf, size_t buflen, const char *prec, const char *s, int arboreal)
{
  int justify = FMT_RIGHT;
  char *p = NULL;
  int min_width;
  int max_width = INT_MAX;

  if (*prec == '-')
  {
    prec++;
    justify = FMT_LEFT;
  }
  else if (*prec == '=')
  {
    prec++;
    justify = FMT_CENTER;
  }
  min_width = strtol(prec, &p, 10);
  if (*p == '.')
  {
    prec = p + 1;
    max_width = strtol(prec, &p, 10);
    if (p <= prec)
      max_width = INT_MAX;
  }

  mutt_simple_format(buf, buflen, min_width, max_width, justify, ' ', s,
                     mutt_str_strlen(s), arboreal);
}
コード例 #2
0
ファイル: curs_lib.c プロジェクト: darnir/neomutt
/**
 * mutt_paddstr - Display a string on screen, padded if necessary
 * @param n Final width of field
 * @param s String to display
 */
void mutt_paddstr(int n, const char *s)
{
  wchar_t wc;
  size_t k;
  size_t len = mutt_str_strlen(s);
  mbstate_t mbstate;

  memset(&mbstate, 0, sizeof(mbstate));
  for (; len && (k = mbrtowc(&wc, s, len, &mbstate)); s += k, len -= k)
  {
    if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
    {
      if (k == (size_t)(-1))
        memset(&mbstate, 0, sizeof(mbstate));
      k = (k == (size_t)(-1)) ? 1 : len;
      wc = ReplacementChar;
    }
    if (!IsWPrint(wc))
      wc = '?';
    const int w = wcwidth(wc);
    if (w >= 0)
    {
      if (w > n)
        break;
      addnstr((char *) s, k);
      n -= w;
    }
  }
  while (n-- > 0)
    addch(' ');
}
コード例 #3
0
ファイル: curs_lib.c プロジェクト: darnir/neomutt
/**
 * mutt_strwidth - Measure a string's width in screen cells
 * @param s String to be measured
 * @retval num Screen cells string would use
 */
int mutt_strwidth(const char *s)
{
  wchar_t wc;
  int w;
  size_t k, n;
  mbstate_t mbstate;

  if (!s)
    return 0;

  n = mutt_str_strlen(s);

  memset(&mbstate, 0, sizeof(mbstate));
  for (w = 0; n && (k = mbrtowc(&wc, s, n, &mbstate)); s += k, n -= k)
  {
    if (*s == MUTT_SPECIAL_INDEX)
    {
      s += 2; /* skip the index coloring sequence */
      k = 0;
      continue;
    }

    if ((k == (size_t)(-1)) || (k == (size_t)(-2)))
    {
      if (k == (size_t)(-1))
        memset(&mbstate, 0, sizeof(mbstate));
      k = (k == (size_t)(-1)) ? 1 : n;
      wc = ReplacementChar;
    }
    if (!IsWPrint(wc))
      wc = '?';
    w += wcwidth(wc);
  }
  return w;
}
コード例 #4
0
ファイル: complete.c プロジェクト: darnir/neomutt
/**
 * nntp_complete - Auto-complete NNTP newsgroups
 * @param buf    Buffer containing pathname
 * @param buflen Length of buffer
 * @retval  0 Match found
 * @retval -1 No matches
 *
 * XXX rules
 */
int nntp_complete(char *buf, size_t buflen)
{
  struct NntpAccountData *adata = CurrentNewsSrv;
  size_t n = 0;
  char filepart[PATH_MAX];
  bool init = false;

  mutt_str_strfcpy(filepart, buf, sizeof(filepart));

  /* special case to handle when there is no filepart yet
   * find the first subscribed newsgroup */
  int len = mutt_str_strlen(filepart);
  if (len == 0)
  {
    for (; n < adata->groups_num; n++)
    {
      struct NntpMboxData *mdata = adata->groups_list[n];

      if (mdata && mdata->subscribed)
      {
        mutt_str_strfcpy(filepart, mdata->group, sizeof(filepart));
        init = true;
        n++;
        break;
      }
    }
  }

  for (; n < adata->groups_num; n++)
  {
    struct NntpMboxData *mdata = adata->groups_list[n];

    if (mdata && mdata->subscribed && (mutt_str_strncmp(mdata->group, filepart, len) == 0))
    {
      if (init)
      {
        size_t i;
        for (i = 0; filepart[i] && mdata->group[i]; i++)
        {
          if (filepart[i] != mdata->group[i])
          {
            filepart[i] = '\0';
            break;
          }
        }
        filepart[i] = '\0';
      }
      else
      {
        mutt_str_strfcpy(filepart, mdata->group, sizeof(filepart));
        init = true;
      }
    }
  }

  mutt_str_strfcpy(buf, filepart, buflen);
  return init ? 0 : -1;
}
コード例 #5
0
ファイル: curs_lib.c プロジェクト: darnir/neomutt
/**
 * mutt_unget_string - Return a string to the input buffer
 * @param s String to return
 *
 * This puts events into the `UngetKeyEvents` buffer
 */
void mutt_unget_string(const char *s)
{
  const char *p = s + mutt_str_strlen(s) - 1;

  while (p >= s)
  {
    mutt_unget_event((unsigned char) *p--, 0);
  }
}
コード例 #6
0
ファイル: ssl_gnutls.c プロジェクト: darnir/neomutt
/**
 * tls_set_priority - Set TLS algorithm priorities
 * @param data TLS socket data
 * @retval  0 Success
 * @retval -1 Error
 */
static int tls_set_priority(struct TlsSockData *data)
{
  size_t nproto = 4;
  size_t priority_size;

  priority_size = mutt_str_strlen(C_SslCiphers) + 128;
  char *priority = mutt_mem_malloc(priority_size);

  priority[0] = '\0';
  if (C_SslCiphers)
    mutt_str_strcat(priority, priority_size, C_SslCiphers);
  else
    mutt_str_strcat(priority, priority_size, "NORMAL");

  if (!C_SslUseTlsv12)
  {
    nproto--;
    mutt_str_strcat(priority, priority_size, ":-VERS-TLS1.2");
  }
  if (!C_SslUseTlsv11)
  {
    nproto--;
    mutt_str_strcat(priority, priority_size, ":-VERS-TLS1.1");
  }
  if (!C_SslUseTlsv1)
  {
    nproto--;
    mutt_str_strcat(priority, priority_size, ":-VERS-TLS1.0");
  }
  if (!C_SslUseSslv3)
  {
    nproto--;
    mutt_str_strcat(priority, priority_size, ":-VERS-SSL3.0");
  }

  if (nproto == 0)
  {
    mutt_error(_("All available protocols for TLS/SSL connection disabled"));
    FREE(&priority);
    return -1;
  }

  int err = gnutls_priority_set_direct(data->state, priority, NULL);
  if (err < 0)
  {
    mutt_error("gnutls_priority_set_direct(%s): %s", priority, gnutls_strerror(err));
    FREE(&priority);
    return -1;
  }

  FREE(&priority);
  return 0;
}
コード例 #7
0
ファイル: auth_oauth.c プロジェクト: kdave/neomutt
/**
 * imap_auth_oauth - Authenticate an IMAP connection using OAUTHBEARER
 * @param adata Imap Account data
 * @param method Name of this authentication method (UNUSED)
 * @retval num Result, e.g. #IMAP_AUTH_SUCCESS
 */
enum ImapAuthRes imap_auth_oauth(struct ImapAccountData *adata, const char *method)
{
  char *ibuf = NULL;
  char *oauthbearer = NULL;
  int ilen;
  int rc;

  /* For now, we only support SASL_IR also and over TLS */
  if (!(adata->capabilities & IMAP_CAP_AUTH_OAUTHBEARER) ||
      !(adata->capabilities & IMAP_CAP_SASL_IR) || !adata->conn->ssf)
  {
    return IMAP_AUTH_UNAVAIL;
  }

  /* If they did not explicitly request or configure oauth then fail quietly */
  if (!(method || (C_ImapOauthRefreshCommand && *C_ImapOauthRefreshCommand)))
    return IMAP_AUTH_UNAVAIL;

  mutt_message(_("Authenticating (OAUTHBEARER)..."));

  /* We get the access token from the imap_oauth_refresh_command */
  oauthbearer = mutt_account_getoauthbearer(&adata->conn->account);
  if (!oauthbearer)
    return IMAP_AUTH_FAILURE;

  ilen = mutt_str_strlen(oauthbearer) + 30;
  ibuf = mutt_mem_malloc(ilen);
  snprintf(ibuf, ilen, "AUTHENTICATE OAUTHBEARER %s", oauthbearer);

  /* This doesn't really contain a password, but the token is good for
   * an hour, so suppress it anyways.  */
  rc = imap_exec(adata, ibuf, IMAP_CMD_PASS);

  FREE(&oauthbearer);
  FREE(&ibuf);

  if (rc != IMAP_EXEC_SUCCESS)
  {
    /* The error response was in SASL continuation, so continue the SASL
     * to cause a failure and exit SASL input.  See RFC 7628 3.2.3 */
    mutt_socket_send(adata->conn, "\001");
    rc = imap_exec(adata, ibuf, IMAP_CMD_NO_FLAGS);
  }

  if (rc == IMAP_EXEC_SUCCESS)
  {
    mutt_clear_error();
    return IMAP_AUTH_SUCCESS;
  }

  mutt_error(_("OAUTHBEARER authentication failed."));
  return IMAP_AUTH_FAILURE;
}
コード例 #8
0
ファイル: curs_lib.c プロジェクト: darnir/neomutt
/**
 * mutt_enter_fname_full - Ask the user to select a file
 * @param[in]  prompt   Prompt
 * @param[in]  buf      Buffer for the result
 * @param[in]  buflen   Length of the buffer
 * @param[in]  mailbox  If true, select mailboxes
 * @param[in]  multiple Allow multiple selections
 * @param[out] files    List of files selected
 * @param[out] numfiles Number of files selected
 * @param[in]  flags    Flags, see #SelectFileFlags
 * @retval  0 Success
 * @retval -1 Error
 */
int mutt_enter_fname_full(const char *prompt, char *buf, size_t buflen, bool mailbox,
                          bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
{
  struct Event ch;

  SETCOLOR(MT_COLOR_PROMPT);
  mutt_window_mvaddstr(MuttMessageWindow, 0, 0, (char *) prompt);
  addstr(_(" ('?' for list): "));
  NORMAL_COLOR;
  if (buf[0] != '\0')
    addstr(buf);
  mutt_window_clrtoeol(MuttMessageWindow);
  mutt_refresh();

  do
  {
    ch = mutt_getch();
  } while (ch.ch == -2);
  if (ch.ch < 0)
  {
    mutt_window_clearline(MuttMessageWindow, 0);
    return -1;
  }
  else if (ch.ch == '?')
  {
    mutt_refresh();
    buf[0] = '\0';

    if (!flags)
      flags = MUTT_SEL_FOLDER;
    if (multiple)
      flags |= MUTT_SEL_MULTI;
    if (mailbox)
      flags |= MUTT_SEL_MAILBOX;
    mutt_select_file(buf, buflen, flags, files, numfiles);
  }
  else
  {
    char *pc = mutt_mem_malloc(mutt_str_strlen(prompt) + 3);

    sprintf(pc, "%s: ", prompt);
    mutt_unget_event(ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
    if (mutt_get_field_full(pc, buf, buflen, (mailbox ? MUTT_EFILE : MUTT_FILE) | MUTT_CLEAR,
                            multiple, files, numfiles) != 0)
    {
      buf[0] = '\0';
    }
    FREE(&pc);
  }

  return 0;
}
コード例 #9
0
ファイル: curs_lib.c プロジェクト: darnir/neomutt
/**
 * mutt_wstr_trunc - Work out how to truncate a widechar string
 * @param[in]  src    String to measure
 * @param[in]  maxlen Maximum length of string in bytes
 * @param[in]  maxwid Maximum width in screen columns
 * @param[out] width  Save the truncated screen column width
 * @retval num Bytes to use
 *
 * See how many bytes to copy from string so it's at most maxlen bytes long and
 * maxwid columns wide
 */
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
{
  wchar_t wc;
  size_t n, w = 0, l = 0, cl;
  int cw;
  mbstate_t mbstate;

  if (!src)
    goto out;

  n = mutt_str_strlen(src);

  memset(&mbstate, 0, sizeof(mbstate));
  for (w = 0; n && (cl = mbrtowc(&wc, src, n, &mbstate)); src += cl, n -= cl)
  {
    if ((cl == (size_t)(-1)) || (cl == (size_t)(-2)))
    {
      if (cl == (size_t)(-1))
        memset(&mbstate, 0, sizeof(mbstate));
      cl = (cl == (size_t)(-1)) ? 1 : n;
      wc = ReplacementChar;
    }
    cw = wcwidth(wc);
    /* hack because MUTT_TREE symbols aren't turned into characters
     * until rendered by print_enriched_string (#3364) */
    if ((cw < 0) && (src[0] == MUTT_SPECIAL_INDEX))
    {
      cl = 2; /* skip the index coloring sequence */
      cw = 0;
    }
    else if ((cw < 0) && (cl == 1) && (src[0] != '\0') && (src[0] < MUTT_TREE_MAX))
      cw = 1;
    else if (cw < 0)
      cw = 0; /* unprintable wchar */
    if ((cl + l > maxlen) || (cw + w > maxwid))
      break;
    l += cl;
    w += cw;
  }
out:
  if (width)
    *width = w;
  return l;
}
コード例 #10
0
ファイル: icommands.c プロジェクト: darnir/neomutt
/**
 * mutt_parse_icommand - Parse an informational command
 * @param line Command to execute
 * @param err  Buffer for error messages
 * @retval #MUTT_CMD_SUCCESS Success
 * @retval #MUTT_CMD_ERROR   Error (no message): command not found
 * @retval #MUTT_CMD_ERROR   Error with message: command failed
 * @retval #MUTT_CMD_WARNING Warning with message: command failed
 */
enum CommandResult mutt_parse_icommand(/* const */ char *line, struct Buffer *err)
{
  if (!line || !*line || !err)
    return MUTT_CMD_ERROR;

  enum CommandResult rc = MUTT_CMD_ERROR;

  struct Buffer expn, token;

  mutt_buffer_init(&expn);
  mutt_buffer_init(&token);

  expn.data = expn.dptr = line;
  expn.dsize = mutt_str_strlen(line);

  mutt_buffer_reset(err);

  SKIPWS(expn.dptr);
  while (*expn.dptr)
  {
    mutt_extract_token(&token, &expn, 0);
    for (size_t i = 0; ICommandList[i].name; i++)
    {
      if (mutt_str_strcmp(token.data, ICommandList[i].name) != 0)
        continue;

      rc = ICommandList[i].func(&token, &expn, ICommandList[i].data, err);
      if (rc != 0)
        goto finish;

      break; /* Continue with next command */
    }
  }

finish:
  if (expn.destroy)
    FREE(&expn.data);
  return rc;
}
コード例 #11
0
ファイル: ssl_gnutls.c プロジェクト: darnir/neomutt
/**
 * 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;
}
コード例 #12
0
ファイル: ssl.c プロジェクト: darnir/neomutt
/**
 * check_host - Check the host on the certificate
 * @param x509cert Certificate
 * @param hostname Hostname
 * @param err      Buffer for error message
 * @param errlen   Length of buffer
 * @retval 1 Hostname matches the certificate
 * @retval 0 Error
 */
static int check_host(X509 *x509cert, const char *hostname, char *err, size_t errlen)
{
  int rc = 0;
  /* hostname in ASCII format: */
  char *hostname_ascii = NULL;
  /* needed to get the common name: */
  X509_NAME *x509_subject = NULL;
  char *buf = NULL;
  int bufsize;
  /* needed to get the DNS subjectAltNames: */
  STACK_OF(GENERAL_NAME) * subj_alt_names;
  int subj_alt_names_count;
  GENERAL_NAME *subj_alt_name = NULL;
  /* did we find a name matching hostname? */
  bool match_found;

  /* Check if 'hostname' matches the one of the subjectAltName extensions of
   * type DNS or the Common Name (CN). */

#ifdef HAVE_LIBIDN
  if (mutt_idna_to_ascii_lz(hostname, &hostname_ascii, 0) != 0)
  {
    hostname_ascii = mutt_str_strdup(hostname);
  }
#else
  hostname_ascii = mutt_str_strdup(hostname);
#endif

  /* Try the DNS subjectAltNames. */
  match_found = false;
  subj_alt_names = X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL);
  if (subj_alt_names)
  {
    subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
    for (int i = 0; i < subj_alt_names_count; i++)
    {
      subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
      if (subj_alt_name->type == GEN_DNS)
      {
        if ((subj_alt_name->d.ia5->length >= 0) &&
            (mutt_str_strlen((char *) subj_alt_name->d.ia5->data) ==
             (size_t) subj_alt_name->d.ia5->length) &&
            (match_found = hostname_match(hostname_ascii,
                                          (char *) (subj_alt_name->d.ia5->data))))
        {
          break;
        }
      }
    }
    GENERAL_NAMES_free(subj_alt_names);
  }

  if (!match_found)
  {
    /* Try the common name */
    x509_subject = X509_get_subject_name(x509cert);
    if (!x509_subject)
    {
      if (err && errlen)
        mutt_str_strfcpy(err, _("cannot get certificate subject"), errlen);
      goto out;
    }

    /* first get the space requirements */
    bufsize = X509_NAME_get_text_by_NID(x509_subject, NID_commonName, NULL, 0);
    if (bufsize == -1)
    {
      if (err && errlen)
        mutt_str_strfcpy(err, _("cannot get certificate common name"), errlen);
      goto out;
    }
    bufsize++; /* space for the terminal nul char */
    buf = mutt_mem_malloc((size_t) bufsize);
    if (X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, bufsize) == -1)
    {
      if (err && errlen)
        mutt_str_strfcpy(err, _("cannot get certificate common name"), errlen);
      goto out;
    }
    /* cast is safe since bufsize is incremented above, so bufsize-1 is always
     * zero or greater.  */
    if (mutt_str_strlen(buf) == (size_t) bufsize - 1)
    {
      match_found = hostname_match(hostname_ascii, buf);
    }
  }

  if (!match_found)
  {
    if (err && errlen)
      snprintf(err, errlen, _("certificate owner does not match hostname %s"), hostname);
    goto out;
  }

  rc = 1;

out:
  FREE(&buf);
  FREE(&hostname_ascii);

  return rc;
}
コード例 #13
0
ファイル: ssl.c プロジェクト: darnir/neomutt
/**
 * 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;
}
コード例 #14
0
ファイル: curs_lib.c プロジェクト: darnir/neomutt
/**
 * mutt_multi_choice - Offer the user a multiple choice question
 * @param prompt  Message prompt
 * @param letters Allowable selection keys
 * @retval >=0 0-based user selection
 * @retval  -1 Selection aborted
 */
int mutt_multi_choice(const char *prompt, const char *letters)
{
  struct Event ch;
  int choice;
  bool redraw = true;
  int prompt_lines = 1;
  char *p = NULL;

  while (true)
  {
    if (redraw || SigWinch)
    {
      redraw = false;
      if (SigWinch)
      {
        SigWinch = 0;
        mutt_resize_screen();
        clearok(stdscr, TRUE);
        mutt_menu_current_redraw();
      }
      if (MuttMessageWindow->cols)
      {
        prompt_lines = (mutt_strwidth(prompt) + MuttMessageWindow->cols - 1) /
                       MuttMessageWindow->cols;
        prompt_lines = MAX(1, MIN(3, prompt_lines));
      }
      if (prompt_lines != MuttMessageWindow->rows)
      {
        mutt_window_reflow_message_rows(prompt_lines);
        mutt_menu_current_redraw();
      }

      SETCOLOR(MT_COLOR_PROMPT);
      mutt_window_mvaddstr(MuttMessageWindow, 0, 0, prompt);
      NORMAL_COLOR;
      mutt_window_clrtoeol(MuttMessageWindow);
    }

    mutt_refresh();
    /* SigWinch is not processed unless timeout is set */
    mutt_getch_timeout(30 * 1000);
    ch = mutt_getch();
    mutt_getch_timeout(-1);
    if (ch.ch == -2)
      continue;
    /* (ch.ch == 0) is technically possible.  Treat the same as < 0 (abort) */
    if ((ch.ch <= 0) || CI_is_return(ch.ch))
    {
      choice = -1;
      break;
    }
    else
    {
      p = strchr(letters, ch.ch);
      if (p)
      {
        choice = p - letters + 1;
        break;
      }
      else if ((ch.ch <= '9') && (ch.ch > '0'))
      {
        choice = ch.ch - '0';
        if (choice <= mutt_str_strlen(letters))
          break;
      }
    }
    BEEP();
  }
  if (MuttMessageWindow->rows != 1)
  {
    mutt_window_reflow_message_rows(1);
    mutt_menu_current_redraw();
  }
  else
    mutt_window_clearline(MuttMessageWindow, 0);
  mutt_refresh();
  return choice;
}
コード例 #15
0
ファイル: crypt.c プロジェクト: darnir/neomutt
/**
 * crypt_get_fingerprint_or_id - Get the fingerprint or long key ID
 * @param[in]  p       String to examine
 * @param[out] pphint  Start of string to be passed to pgp_add_string_to_hints() or crypt_add_string_to_hints()
 * @param[out] ppl     Start of long key ID if detected, else NULL
 * @param[out] pps     Start of short key ID if detected, else NULL
 * @retval ptr  Copy of fingerprint, if any, stripped of all spaces.  Must be FREE'd by caller
 * @retval NULL Otherwise
 *
 * Obtain pointers to fingerprint or short or long key ID, if any.
 *
 * Upon return, at most one of return, *ppl and *pps pointers is non-NULL,
 * indicating the longest fingerprint or ID found, if any.
 */
const char *crypt_get_fingerprint_or_id(char *p, const char **pphint,
                                        const char **ppl, const char **pps)
{
  const char *ps = NULL, *pl = NULL, *phint = NULL;
  char *pfcopy = NULL, *s1 = NULL, *s2 = NULL;
  char c;
  int isid;
  size_t hexdigits;

  /* User input may be partial name, fingerprint or short or long key ID,
   * independent of C_PgpLongIds.
   * Fingerprint without spaces is 40 hex digits (SHA-1) or 32 hex digits (MD5).
   * Strip leading "0x" for key ID detection and prepare pl and ps to indicate
   * if an ID was found and to simplify logic in the key loop's inner
   * condition of the caller. */

  char *pf = mutt_str_skip_whitespace(p);
  if (mutt_str_startswith(pf, "0x", CASE_IGNORE))
    pf += 2;

  /* Check if a fingerprint is given, must be hex digits only, blanks
   * separating groups of 4 hex digits are allowed. Also pre-check for ID. */
  isid = 2; /* unknown */
  hexdigits = 0;
  s1 = pf;
  do
  {
    c = *(s1++);
    if ((('0' <= c) && (c <= '9')) || (('A' <= c) && (c <= 'F')) ||
        (('a' <= c) && (c <= 'f')))
    {
      hexdigits++;
      if (isid == 2)
        isid = 1; /* it is an ID so far */
    }
    else if (c)
    {
      isid = 0; /* not an ID */
      if ((c == ' ') && ((hexdigits % 4) == 0))
        ; /* skip blank before or after 4 hex digits */
      else
        break; /* any other character or position */
    }
  } while (c);

  /* If at end of input, check for correct fingerprint length and copy if. */
  pfcopy = (!c && ((hexdigits == 40) || (hexdigits == 32)) ? mutt_str_strdup(pf) : NULL);

  if (pfcopy)
  {
    /* Use pfcopy to strip all spaces from fingerprint and as hint. */
    s1 = pfcopy;
    s2 = pfcopy;
    do
    {
      *(s1++) = *(s2 = mutt_str_skip_whitespace(s2));
    } while (*(s2++));

    phint = pfcopy;
    ps = NULL;
    pl = NULL;
  }
  else
  {
    phint = p;
    ps = NULL;
    pl = NULL;
    if (isid == 1)
    {
      if (mutt_str_strlen(pf) == 16)
        pl = pf; /* long key ID */
      else if (mutt_str_strlen(pf) == 8)
        ps = pf; /* short key ID */
    }
  }

  *pphint = phint;
  *ppl = pl;
  *pps = ps;
  return pfcopy;
}
コード例 #16
0
ファイル: crypt.c プロジェクト: darnir/neomutt
/**
 * mutt_is_application_smime - Does the message use S/MIME?
 * @param m Body of email
 * @retval >0 Message uses S/MIME, e.g. #SMIME_ENCRYPT
 * @retval  0 Message doesn't use S/MIME, (#SEC_NO_FLAGS)
 */
SecurityFlags mutt_is_application_smime(struct Body *m)
{
  if (!m)
    return SEC_NO_FLAGS;

  if (((m->type & TYPE_APPLICATION) == 0) || !m->subtype)
    return SEC_NO_FLAGS;

  char *t = NULL;
  bool complain = false;
  /* S/MIME MIME types don't need x- anymore, see RFC2311 */
  if ((mutt_str_strcasecmp(m->subtype, "x-pkcs7-mime") == 0) ||
      (mutt_str_strcasecmp(m->subtype, "pkcs7-mime") == 0))
  {
    t = mutt_param_get(&m->parameter, "smime-type");
    if (t)
    {
      if (mutt_str_strcasecmp(t, "enveloped-data") == 0)
        return SMIME_ENCRYPT;
      else if (mutt_str_strcasecmp(t, "signed-data") == 0)
        return SMIME_SIGN | SMIME_OPAQUE;
      else
        return SEC_NO_FLAGS;
    }
    /* Netscape 4.7 uses
     * Content-Description: S/MIME Encrypted Message
     * instead of Content-Type parameter */
    if (mutt_str_strcasecmp(m->description, "S/MIME Encrypted Message") == 0)
      return SMIME_ENCRYPT;
    complain = true;
  }
  else if (mutt_str_strcasecmp(m->subtype, "octet-stream") != 0)
    return SEC_NO_FLAGS;

  t = mutt_param_get(&m->parameter, "name");

  if (!t)
    t = m->d_filename;
  if (!t)
    t = m->filename;
  if (!t)
  {
    if (complain)
    {
      mutt_message(
          _("S/MIME messages with no hints on content are unsupported"));
    }
    return SEC_NO_FLAGS;
  }

  /* no .p7c, .p10 support yet. */

  int len = mutt_str_strlen(t) - 4;
  if ((len > 0) && (*(t + len) == '.'))
  {
    len++;
    if (mutt_str_strcasecmp((t + len), "p7m") == 0)
    {
      /* Not sure if this is the correct thing to do, but
       * it's required for compatibility with Outlook */
      return SMIME_SIGN | SMIME_OPAQUE;
    }
    else if (mutt_str_strcasecmp((t + len), "p7s") == 0)
      return SMIME_SIGN | SMIME_OPAQUE;
  }

  return SEC_NO_FLAGS;
}
コード例 #17
0
ファイル: crypt.c プロジェクト: darnir/neomutt
/**
 * crypt_get_keys - Check we have all the keys we need
 * @param[in]  msg         Message with addresses to match
 * @param[out] keylist     Keys needed
 * @param[in]  oppenc_mode If true, use opportunistic encryption
 * @retval  0 Success
 * @retval -1 Error
 *
 * Do a quick check to make sure that we can find all of the
 * encryption keys if the user has requested this service.
 * Return the list of keys in KEYLIST.
 * If oppenc_mode is true, only keys that can be determined without
 * prompting will be used.
 */
int crypt_get_keys(struct Email *msg, char **keylist, bool oppenc_mode)
{
  struct Address *addrlist = NULL, *last = NULL;
  const char *fqdn = mutt_fqdn(true);
  char *self_encrypt = NULL;

  /* Do a quick check to make sure that we can find all of the encryption
   * keys if the user has requested this service.  */

  if (!WithCrypto)
    return 0;

  if (WithCrypto & APPLICATION_PGP)
    OptPgpCheckTrust = true;

  last = mutt_addr_append(&addrlist, msg->env->to, false);
  last = mutt_addr_append(last ? &last : &addrlist, msg->env->cc, false);
  mutt_addr_append(last ? &last : &addrlist, msg->env->bcc, false);

  if (fqdn)
    mutt_addr_qualify(addrlist, fqdn);
  addrlist = mutt_addrlist_dedupe(addrlist);

  *keylist = NULL;

  if (oppenc_mode || (msg->security & SEC_ENCRYPT))
  {
    if (((WithCrypto & APPLICATION_PGP) != 0) && (msg->security & APPLICATION_PGP))
    {
      *keylist = crypt_pgp_find_keys(addrlist, oppenc_mode);
      if (!*keylist)
      {
        mutt_addr_free(&addrlist);
        return -1;
      }
      OptPgpCheckTrust = false;
      if (C_PgpSelfEncrypt || (C_PgpEncryptSelf == MUTT_YES))
        self_encrypt = C_PgpDefaultKey;
    }
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (msg->security & APPLICATION_SMIME))
    {
      *keylist = crypt_smime_find_keys(addrlist, oppenc_mode);
      if (!*keylist)
      {
        mutt_addr_free(&addrlist);
        return -1;
      }
      if (C_SmimeSelfEncrypt || (C_SmimeEncryptSelf == MUTT_YES))
        self_encrypt = C_SmimeDefaultKey;
    }
  }

  if (!oppenc_mode && self_encrypt && *self_encrypt)
  {
    const size_t keylist_size = mutt_str_strlen(*keylist);
    mutt_mem_realloc(keylist, keylist_size + mutt_str_strlen(self_encrypt) + 2);
    sprintf(*keylist + keylist_size, " %s", self_encrypt);
  }

  mutt_addr_free(&addrlist);

  return 0;
}
コード例 #18
0
ファイル: ssl_gnutls.c プロジェクト: darnir/neomutt
/**
 * 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;
}