Ejemplo n.º 1
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * check_certificate_expiration - Check if a certificate has expired
 * @param peercert Certificate to check
 * @param silent   If true, don't notify the user if the certificate has expired
 * @retval true  Certificate is valid
 * @retval false Certificate has expired (or hasn't yet become valid)
 */
static bool check_certificate_expiration(X509 *peercert, bool silent)
{
  if (C_SslVerifyDates == MUTT_NO)
    return true;

  if (X509_cmp_current_time(X509_get0_notBefore(peercert)) >= 0)
  {
    if (!silent)
    {
      mutt_debug(LL_DEBUG2, "Server certificate is not yet valid\n");
      mutt_error(_("Server certificate is not yet valid"));
    }
    return false;
  }

  if (X509_cmp_current_time(X509_get0_notAfter(peercert)) <= 0)
  {
    if (!silent)
    {
      mutt_debug(LL_DEBUG2, "Server certificate has expired\n");
      mutt_error(_("Server certificate has expired"));
    }
    return false;
  }

  return true;
}
Ejemplo n.º 2
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * ssl_set_verify_partial - Allow verification using partial chains (with no root)
 * @param ctx SSL context
 * @retval  0 Success
 * @retval -1 Error
 */
static int ssl_set_verify_partial(SSL_CTX *ctx)
{
  int rc = 0;
#ifdef HAVE_SSL_PARTIAL_CHAIN
  X509_VERIFY_PARAM *param = NULL;

  if (C_SslVerifyPartialChains)
  {
    param = X509_VERIFY_PARAM_new();
    if (param)
    {
      X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
      if (SSL_CTX_set1_param(ctx, param) == 0)
      {
        mutt_debug(LL_DEBUG2, "SSL_CTX_set1_param() failed.\n");
        rc = -1;
      }
      X509_VERIFY_PARAM_free(param);
    }
    else
    {
      mutt_debug(LL_DEBUG2, "X509_VERIFY_PARAM_new() failed.\n");
      rc = -1;
    }
  }
#endif
  return rc;
}
Ejemplo n.º 3
0
Archivo: set.c Proyecto: kdave/neomutt
/**
 * cs_he_initial_set - Set the initial value of a config item
 * @param cs    Config items
 * @param he    HashElem representing config item
 * @param value Value to set
 * @param err   Buffer for error messages
 * @retval int Result, e.g. #CSR_SUCCESS
 */
int cs_he_initial_set(const struct ConfigSet *cs, struct HashElem *he,
                      const char *value, struct Buffer *err)
{
  if (!cs || !he)
    return CSR_ERR_CODE;

  struct ConfigDef *cdef = NULL;
  const struct ConfigSetType *cst = NULL;

  if (he->type & DT_INHERITED)
  {
    struct Inheritance *i = he->data;
    cdef = i->parent->data;
    mutt_debug(LL_DEBUG1, "Variable '%s' is inherited type\n", cdef->name);
    return CSR_ERR_CODE;
  }

  cdef = he->data;
  cst = cs_get_type_def(cs, he->type);
  if (!cst)
  {
    mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
    return CSR_ERR_CODE;
  }

  int rc = cst->string_set(cs, NULL, cdef, value, err);
  if (CSR_RESULT(rc) != CSR_SUCCESS)
    return rc;

  cs_notify_listeners(cs, he, he->key.strkey, CE_INITIAL_SET);
  return CSR_SUCCESS;
}
Ejemplo n.º 4
0
/**
 * mutt_socket_write_d - Write data to a socket
 * @param conn Connection to a server
 * @param buf Buffer with data to write
 * @param len Length of data to write
 * @param dbg Debug level for logging
 * @retval >0 Number of bytes written
 * @retval -1 Error
 */
int mutt_socket_write_d(struct Connection *conn, const char *buf, int len, int dbg)
{
  int sent = 0;

  mutt_debug(dbg, "%d> %s", conn->fd, buf);

  if (conn->fd < 0)
  {
    mutt_debug(LL_DEBUG1, "attempt to write to closed connection\n");
    return -1;
  }

  while (sent < len)
  {
    const int rc = conn->conn_write(conn, buf + sent, len - sent);
    if (rc < 0)
    {
      mutt_debug(LL_DEBUG1, "error writing (%s), closing socket\n", strerror(errno));
      mutt_socket_close(conn);

      return -1;
    }

    if (rc < len - sent)
      mutt_debug(LL_DEBUG3, "short write (%d of %d bytes)\n", rc, len - sent);

    sent += rc;
  }

  return sent;
}
Ejemplo n.º 5
0
/**
 * 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);
}
Ejemplo n.º 6
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * ssl_get_client_cert - Get the client certificate for an SSL connection
 * @param ssldata SSL socket data
 * @param conn    Connection to a server
 */
static void ssl_get_client_cert(struct SslSockData *ssldata, struct Connection *conn)
{
  if (!C_SslClientCert)
    return;

  mutt_debug(LL_DEBUG2, "Using client certificate %s\n", C_SslClientCert);
  SSL_CTX_set_default_passwd_cb_userdata(ssldata->sctx, &conn->account);
  SSL_CTX_set_default_passwd_cb(ssldata->sctx, ssl_passwd_cb);
  SSL_CTX_use_certificate_file(ssldata->sctx, C_SslClientCert, SSL_FILETYPE_PEM);
  SSL_CTX_use_PrivateKey_file(ssldata->sctx, C_SslClientCert, SSL_FILETYPE_PEM);

  /* 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");
}
Ejemplo n.º 7
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * ssl_cache_trusted_cert - Cache a trusted certificate
 * @param c Certificate
 * @retval >0 Number of elements in the cache
 * @retval  0 Error
 */
static int ssl_cache_trusted_cert(X509 *c)
{
  mutt_debug(LL_DEBUG1, "trusted\n");
  if (!SslSessionCerts)
    SslSessionCerts = sk_X509_new_null();
  return sk_X509_push(SslSessionCerts, X509_dup(c));
}
Ejemplo n.º 8
0
/**
 * mutt_perror_debug - Show the user an 'errno' message
 * @param s Additional text to show
 */
void mutt_perror_debug(const char *s)
{
  char *p = strerror(errno);

  mutt_debug(LL_DEBUG1, "%s: %s (errno = %d)\n", s, p ? p : "unknown error", errno);
  mutt_error("%s: %s (errno = %d)", s, p ? p : _("unknown error"), errno);
}
Ejemplo n.º 9
0
Archivo: set.c Proyecto: kdave/neomutt
/**
 * cs_he_initial_get - Get the initial, or parent, value of a config item
 * @param cs     Config items
 * @param he     HashElem representing config item
 * @param result Buffer for results or error messages
 * @retval int Result, e.g. #CSR_SUCCESS
 *
 * If a config item is inherited from another, then this will get the parent's
 * value.  Otherwise, it will get the config item's initial value.
 */
int cs_he_initial_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
{
  if (!cs || !he)
    return CSR_ERR_CODE;

  struct Inheritance *i = NULL;
  const struct ConfigDef *cdef = NULL;
  const struct ConfigSetType *cst = NULL;

  if (he->type & DT_INHERITED)
  {
    i = he->data;
    cdef = i->parent->data;
    cst = cs_get_type_def(cs, i->parent->type);
  }
  else
  {
    cdef = he->data;
    cst = cs_get_type_def(cs, he->type);
  }

  if (!cst)
  {
    mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name,
               DTYPE(he->type));
    return CSR_ERR_CODE;
  }

  return cst->string_get(cs, NULL, cdef, result);
}
Ejemplo n.º 10
0
/**
 * mutt_socket_readln_d - Read a line from a socket
 * @param buf    Buffer to store the line
 * @param buflen Length of data to write
 * @param conn   Connection to a server
 * @param dbg    Debug level for logging
 * @retval >0 Success, number of bytes read
 * @retval -1 Error
 */
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
{
  char ch;
  int i;

  for (i = 0; i < buflen - 1; i++)
  {
    if (mutt_socket_readchar(conn, &ch) != 1)
    {
      buf[i] = '\0';
      return -1;
    }

    if (ch == '\n')
      break;
    buf[i] = ch;
  }

  /* strip \r from \r\n termination */
  if (i && (buf[i - 1] == '\r'))
    i--;
  buf[i] = '\0';

  mutt_debug(dbg, "%d< %s\n", conn->fd, buf);

  /* number of bytes read, not strlen */
  return i + 1;
}
Ejemplo n.º 11
0
/**
 * mutt_socket_readchar - simple read buffering to speed things up
 * @param[in]  conn Connection to a server
 * @param[out] c    Character that was read
 * @retval  1 Success
 * @retval -1 Error
 */
int mutt_socket_readchar(struct Connection *conn, char *c)
{
  if (conn->bufpos >= conn->available)
  {
    if (conn->fd >= 0)
      conn->available = conn->conn_read(conn, conn->inbuf, sizeof(conn->inbuf));
    else
    {
      mutt_debug(LL_DEBUG1, "attempt to read from closed connection.\n");
      return -1;
    }
    conn->bufpos = 0;
    if (conn->available == 0)
    {
      mutt_error(_("Connection to %s closed"), conn->account.host);
    }
    if (conn->available <= 0)
    {
      mutt_socket_close(conn);
      return -1;
    }
  }
  *c = conn->inbuf[conn->bufpos];
  conn->bufpos++;
  return 1;
}
Ejemplo n.º 12
0
/**
 * socket_connect - set up to connect to a socket fd
 * @param fd File descriptor to connect with
 * @param sa Address info
 * @retval  0 Success
 * @retval >0 An errno, e.g. EPERM
 * @retval -1 Error
 */
static int socket_connect(int fd, struct sockaddr *sa)
{
  int sa_size;
  int save_errno;
  sigset_t set;

  if (sa->sa_family == AF_INET)
    sa_size = sizeof(struct sockaddr_in);
#ifdef HAVE_GETADDRINFO
  else if (sa->sa_family == AF_INET6)
    sa_size = sizeof(struct sockaddr_in6);
#endif
  else
  {
    mutt_debug(LL_DEBUG1, "Unknown address family!\n");
    return -1;
  }

  if (C_ConnectTimeout > 0)
    alarm(C_ConnectTimeout);

  mutt_sig_allow_interrupt(1);

  /* FreeBSD's connect() does not respect SA_RESTART, meaning
   * a SIGWINCH will cause the connect to fail. */
  sigemptyset(&set);
  sigaddset(&set, SIGWINCH);
  sigprocmask(SIG_BLOCK, &set, NULL);

  save_errno = 0;

  if (connect(fd, sa, sa_size) < 0)
  {
    save_errno = errno;
    mutt_debug(LL_DEBUG2, "Connection failed. errno: %d\n", errno);
    SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
  }

  if (C_ConnectTimeout > 0)
    alarm(0);
  mutt_sig_allow_interrupt(0);
  sigprocmask(SIG_UNBLOCK, &set, NULL);

  return save_errno;
}
Ejemplo n.º 13
0
/**
 * socket_preconnect - Execute a command before opening a socket
 * @retval 0  Success
 * @retval >0 An errno, e.g. EPERM
 */
static int socket_preconnect(void)
{
  if (!C_Preconnect)
    return 0;

  mutt_debug(LL_DEBUG2, "Executing preconnect: %s\n", C_Preconnect);
  const int rc = mutt_system(C_Preconnect);
  mutt_debug(LL_DEBUG2, "Preconnect result: %d\n", rc);
  if (rc != 0)
  {
    const int save_errno = errno;
    mutt_perror(_("Preconnect command failed"));

    return save_errno;
  }

  return 0;
}
Ejemplo n.º 14
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * ssl_load_certificates - Load certificates and filter out the expired ones
 * @param ctx SSL context
 * @retval 1 Success
 * @retval 0 Error
 *
 * ssl certificate verification can behave strangely if there are expired certs
 * loaded into the trusted store.  This function filters out expired certs.
 *
 * Previously the code used this form:
 *     SSL_CTX_load_verify_locations (ssldata->ctx, #C_CertificateFile, NULL);
 */
static int ssl_load_certificates(SSL_CTX *ctx)
{
  FILE *fp = NULL;
  X509 *cert = NULL;
  X509_STORE *store = NULL;
  int rc = 1;
  char buf[256];

  mutt_debug(LL_DEBUG2, "loading trusted certificates\n");
  store = SSL_CTX_get_cert_store(ctx);
  if (!store)
  {
    store = X509_STORE_new();
    SSL_CTX_set_cert_store(ctx, store);
  }

  fp = fopen(C_CertificateFile, "rt");
  if (!fp)
    return 0;

  while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
  {
    if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
        (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
    {
      mutt_debug(LL_DEBUG2, "filtering expired cert: %s\n",
                 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)));
    }
    else
    {
      X509_STORE_add_cert(store, cert);
    }
  }
  /* PEM_read_X509 sets the error NO_START_LINE on eof */
  if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
    rc = 0;
  ERR_clear_error();

  X509_free(cert);
  mutt_file_fclose(&fp);

  return rc;
}
Ejemplo n.º 15
0
/**
 * mutt_socket_open - Simple wrapper
 * @param conn Connection to a server
 * @retval  0 Success
 * @retval -1 Error
 */
int mutt_socket_open(struct Connection *conn)
{
  int rc;

  if (socket_preconnect())
    return -1;

  rc = conn->conn_open(conn);

  mutt_debug(LL_DEBUG2, "Connected to %s:%d on fd=%d\n", conn->account.host,
             conn->account.port, conn->fd);

  return rc;
}
Ejemplo n.º 16
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * ssl_passwd_cb - Callback to get a password
 * @param buf      Buffer for the password
 * @param buflen   Length of the buffer
 * @param rwflag   0 if writing, 1 if reading (UNUSED)
 * @param userdata ConnAccount whose password is requested
 * @retval >0 Success, number of chars written to buf
 * @retval  0 Error
 */
static int ssl_passwd_cb(char *buf, int buflen, int rwflag, void *userdata)
{
  struct ConnAccount *account = userdata;

  if (mutt_account_getuser(account) < 0)
    return 0;

  mutt_debug(LL_DEBUG2, "getting password for %s@%s:%u\n", account->user,
             account->host, account->port);

  if (mutt_account_getpass(account) < 0)
    return 0;

  return snprintf(buf, buflen, "%s", account->pass);
}
Ejemplo n.º 17
0
Archivo: set.c Proyecto: kdave/neomutt
/**
 * cs_remove_listener - Remove a listener (callback function)
 * @param cs Config items
 * @param fn Listener callback function
 */
void cs_remove_listener(struct ConfigSet *cs, cs_listener fn)
{
  if (!cs || !fn)
    return;

  for (size_t i = 0; i < mutt_array_size(cs->listeners); i++)
  {
    if (cs->listeners[i] == fn)
    {
      cs->listeners[i] = NULL;
      return;
    }
  }
  mutt_debug(LL_DEBUG1, "Listener wasn't registered\n");
}
Ejemplo n.º 18
0
Archivo: set.c Proyecto: darnir/neomutt
/**
 * cs_str_native_set - Natively set the value of a string config item
 * @param cs    Config items
 * @param name  Name of config item
 * @param value Native pointer/value to set
 * @param err   Buffer for error messages
 * @retval int Result, e.g. #CSR_SUCCESS
 */
int cs_str_native_set(const struct ConfigSet *cs, const char *name,
                      intptr_t value, struct Buffer *err)
{
  if (!cs || !name)
    return CSR_ERR_CODE; /* LCOV_EXCL_LINE */

  struct HashElem *he = cs_get_elem(cs, name);
  if (!he)
  {
    mutt_buffer_printf(err, "Unknown var '%s'", name);
    return CSR_ERR_UNKNOWN;
  }

  const struct ConfigDef *cdef = NULL;
  const struct ConfigSetType *cst = NULL;
  void *var = NULL;

  if (he->type & DT_INHERITED)
  {
    struct Inheritance *i = he->data;
    cdef = i->parent->data;
    var = &i->var;
    cst = cs_get_type_def(cs, i->parent->type);
  }
  else
  {
    cdef = he->data;
    var = cdef->var;
    cst = cs_get_type_def(cs, he->type);
  }

  if (!cst)
  {
    mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
    return CSR_ERR_CODE;
  }

  int rc = cst->native_set(cs, var, cdef, value, err);
  if (CSR_RESULT(rc) == CSR_SUCCESS)
  {
    if (he->type & DT_INHERITED)
      he->type = cdef->type | DT_INHERITED;
    if (!(rc & CSR_SUC_NO_CHANGE))
      cs_notify_listeners(cs, he, cdef->name, CE_SET);
  }

  return rc;
}
Ejemplo n.º 19
0
/**
 * mutt_socket_close - Close a socket
 * @param conn Connection to a server
 * @retval  0 Success
 * @retval -1 Error
 */
int mutt_socket_close(struct Connection *conn)
{
  if (!conn)
    return 0;

  int rc = -1;

  if (conn->fd < 0)
    mutt_debug(LL_DEBUG1, "Attempt to close closed connection.\n");
  else
    rc = conn->conn_close(conn);

  conn->fd = -1;
  conn->ssf = 0;

  return rc;
}
Ejemplo n.º 20
0
Archivo: set.c Proyecto: kdave/neomutt
/**
 * cs_he_string_set - Set a config item by string
 * @param cs    Config items
 * @param he    HashElem representing config item
 * @param value Value to set
 * @param err   Buffer for error messages
 * @retval int Result, e.g. #CSR_SUCCESS
 */
int cs_he_string_set(const struct ConfigSet *cs, struct HashElem *he,
                     const char *value, struct Buffer *err)
{
  if (!cs || !he)
    return CSR_ERR_CODE;

  struct ConfigDef *cdef = NULL;
  const struct ConfigSetType *cst = NULL;
  void *var = NULL;

  if (he->type & DT_INHERITED)
  {
    struct Inheritance *i = he->data;
    cdef = i->parent->data;
    var = &i->var;
    cst = cs_get_type_def(cs, i->parent->type);
  }
  else
  {
    cdef = he->data;
    var = cdef->var;
    cst = cs_get_type_def(cs, he->type);
  }

  if (!cst)
  {
    mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
    return CSR_ERR_CODE;
  }

  if (!var)
    return CSR_ERR_CODE; /* LCOV_EXCL_LINE */

  int rc = cst->string_set(cs, var, cdef, value, err);
  if (CSR_RESULT(rc) != CSR_SUCCESS)
    return rc;

  if (he->type & DT_INHERITED)
  {
    struct Inheritance *i = he->data;
    he->type = i->parent->type | DT_INHERITED;
  }
  if (!(rc & CSR_SUC_NO_CHANGE))
    cs_notify_listeners(cs, he, he->key.strkey, CE_SET);
  return rc;
}
Ejemplo n.º 21
0
/**
 * mutt_progress_init - Set up a progress bar
 * @param progress Progress bar
 * @param msg      Message to display
 * @param flags    Flags, e.g. #MUTT_PROGRESS_SIZE
 * @param inc      Increments to display (0 disables updates)
 * @param size     Total size of expected file / traffic
 */
void mutt_progress_init(struct Progress *progress, const char *msg,
                        unsigned short flags, unsigned short inc, size_t size)
{
  struct timeval tv = { 0, 0 };

  if (!progress)
    return;
  if (OptNoCurses)
    return;

  memset(progress, 0, sizeof(struct Progress));
  progress->inc = inc;
  progress->flags = flags;
  progress->msg = msg;
  progress->size = size;
  if (progress->size != 0)
  {
    if (progress->flags & MUTT_PROGRESS_SIZE)
    {
      mutt_str_pretty_size(progress->sizestr, sizeof(progress->sizestr),
                           progress->size);
    }
    else
      snprintf(progress->sizestr, sizeof(progress->sizestr), "%zu", progress->size);
  }
  if (inc == 0)
  {
    if (size != 0)
      mutt_message("%s (%s)", msg, progress->sizestr);
    else
      mutt_message(msg);
    return;
  }
  if (gettimeofday(&tv, NULL) < 0)
    mutt_debug(LL_DEBUG1, "gettimeofday failed: %d\n", errno);
  /* if timestamp is 0 no time-based suppression is done */
  if (C_TimeInc != 0)
  {
    progress->timestamp =
        ((unsigned int) tv.tv_sec * 1000) + (unsigned int) (tv.tv_usec / 1000);
  }
  mutt_progress_update(progress, 0, 0);
}
Ejemplo n.º 22
0
Archivo: set.c Proyecto: kdave/neomutt
/**
 * cs_register_variables - Register a set of config items
 * @param cs    Config items
 * @param vars  Variable definition
 * @param flags Flags, e.g. #CS_REG_DISABLED
 * @retval bool True, if all variables were registered successfully
 */
bool cs_register_variables(const struct ConfigSet *cs, struct ConfigDef vars[], int flags)
{
  if (!cs || !vars)
    return false;

  struct Buffer *err = mutt_buffer_pool_get();

  bool rc = true;

  for (size_t i = 0; vars[i].name; i++)
  {
    if (!reg_one_var(cs, &vars[i], err))
    {
      mutt_debug(LL_DEBUG1, "%s\n", mutt_b2s(err));
      rc = false;
    }
  }

  mutt_buffer_pool_release(&err);
  return rc;
}
Ejemplo n.º 23
0
Archivo: set.c Proyecto: kdave/neomutt
/**
 * cs_he_string_get - Get a config item as a string
 * @param cs     Config items
 * @param he     HashElem representing config item
 * @param result Buffer for results or error messages
 * @retval int Result, e.g. #CSR_SUCCESS
 */
int cs_he_string_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
{
  if (!cs || !he)
    return CSR_ERR_CODE;

  struct Inheritance *i = NULL;
  const struct ConfigDef *cdef = NULL;
  const struct ConfigSetType *cst = NULL;
  void *var = NULL;

  if (he->type & DT_INHERITED)
  {
    i = he->data;
    cdef = i->parent->data;
    cst = cs_get_type_def(cs, i->parent->type);
  }
  else
  {
    cdef = he->data;
    cst = cs_get_type_def(cs, he->type);
  }

  if ((he->type & DT_INHERITED) && (DTYPE(he->type) != 0))
  {
    var = &i->var; /* Local value */
  }
  else
  {
    var = cdef->var; /* Normal var */
  }

  if (!cst)
  {
    mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name,
               DTYPE(he->type));
    return CSR_ERR_CODE;
  }

  return cst->string_get(cs, var, cdef, result);
}
Ejemplo n.º 24
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * ssl_dprint_err_stack - Dump the SSL error stack
 */
static void ssl_dprint_err_stack(void)
{
  BIO *bio = NULL;
  char *buf = NULL;
  long buflen;
  char *output = NULL;

  bio = BIO_new(BIO_s_mem());
  if (!bio)
    return;
  ERR_print_errors(bio);
  buflen = BIO_get_mem_data(bio, &buf);
  if (buflen > 0)
  {
    output = mutt_mem_malloc(buflen + 1);
    memcpy(output, buf, buflen);
    output[buflen] = '\0';
    mutt_debug(LL_DEBUG1, "SSL error stack: %s\n", output);
    FREE(&output);
  }
  BIO_free(bio);
}
Ejemplo n.º 25
0
Archivo: set.c Proyecto: kdave/neomutt
/**
 * cs_add_listener - Add a listener (callback function)
 * @param cs Config items
 * @param fn Listener callback function
 */
void cs_add_listener(struct ConfigSet *cs, cs_listener fn)
{
  if (!cs || !fn)
    return;

  for (size_t i = 0; i < mutt_array_size(cs->listeners); i++)
  {
    if (cs->listeners[i] == fn)
    {
      mutt_debug(LL_DEBUG1, "Listener was already registered\n");
      return;
    }
  }

  for (size_t i = 0; i < mutt_array_size(cs->listeners); i++)
  {
    if (!cs->listeners[i])
    {
      cs->listeners[i] = fn;
      return;
    }
  }
}
Ejemplo n.º 26
0
Archivo: set.c Proyecto: darnir/neomutt
/**
 * cs_register_variables - Register a set of config items
 * @param cs    Config items
 * @param vars  Variable definition
 * @param flags Flags, e.g. #CS_REG_DISABLED
 * @retval bool True, if all variables were registered successfully
 */
bool cs_register_variables(const struct ConfigSet *cs, struct ConfigDef vars[], int flags)
{
  if (!cs || !vars)
    return CSR_ERR_CODE; /* LCOV_EXCL_LINE */

  struct Buffer err;
  mutt_buffer_init(&err);
  err.dsize = 256;
  err.data = calloc(1, err.dsize);

  bool rc = true;

  for (size_t i = 0; vars[i].name; i++)
  {
    if (!reg_one_var(cs, &vars[i], &err))
    {
      mutt_debug(LL_DEBUG1, "%s\n", err.data);
      rc = false;
    }
  }

  FREE(&err.data);
  return rc;
}
Ejemplo n.º 27
0
/**
 * print_gss_error - Print detailed error message to the debug log
 * @param err_maj Error's major number
 * @param err_min Error's minor number
 */
static void print_gss_error(OM_uint32 err_maj, OM_uint32 err_min)
{
  OM_uint32 maj_stat, min_stat;
  OM_uint32 msg_ctx = 0;
  gss_buffer_desc status_string;
  char buf_maj[512];
  char buf_min[512];

  do
  {
    maj_stat = gss_display_status(&min_stat, err_maj, GSS_C_GSS_CODE,
                                  GSS_C_NO_OID, &msg_ctx, &status_string);
    if (GSS_ERROR(maj_stat))
      break;
    size_t status_len = status_string.length;
    if (status_len >= sizeof(buf_maj))
      status_len = sizeof(buf_maj) - 1;
    strncpy(buf_maj, (char *) status_string.value, status_len);
    buf_maj[status_len] = '\0';
    gss_release_buffer(&min_stat, &status_string);

    maj_stat = gss_display_status(&min_stat, err_min, GSS_C_MECH_CODE,
                                  GSS_C_NULL_OID, &msg_ctx, &status_string);
    if (!GSS_ERROR(maj_stat))
    {
      status_len = status_string.length;
      if (status_len >= sizeof(buf_min))
        status_len = sizeof(buf_min) - 1;
      strncpy(buf_min, (char *) status_string.value, status_len);
      buf_min[status_len] = '\0';
      gss_release_buffer(&min_stat, &status_string);
    }
  } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);

  mutt_debug(LL_DEBUG2, "((%s:%d )(%s:%d))\n", buf_maj, err_maj, buf_min, err_min);
}
Ejemplo n.º 28
0
Archivo: ssl.c Proyecto: darnir/neomutt
/**
 * ssl_err - Display an SSL error message
 * @param data SSL socket data
 * @param err  SSL error code
 */
static void ssl_err(struct SslSockData *data, int err)
{
  int e = SSL_get_error(data->ssl, err);
  switch (e)
  {
    case SSL_ERROR_NONE:
      return;
    case SSL_ERROR_ZERO_RETURN:
      data->isopen = 0;
      break;
    case SSL_ERROR_SYSCALL:
      data->isopen = 0;
      break;
  }

  const char *errmsg = NULL;
  unsigned long sslerr;

  switch (e)
  {
    case SSL_ERROR_SYSCALL:
      errmsg = "I/O error";
      break;
    case SSL_ERROR_WANT_ACCEPT:
      errmsg = "retry accept";
      break;
    case SSL_ERROR_WANT_CONNECT:
      errmsg = "retry connect";
      break;
    case SSL_ERROR_WANT_READ:
      errmsg = "retry read";
      break;
    case SSL_ERROR_WANT_WRITE:
      errmsg = "retry write";
      break;
    case SSL_ERROR_WANT_X509_LOOKUP:
      errmsg = "retry x509 lookup";
      break;
    case SSL_ERROR_ZERO_RETURN:
      errmsg = "SSL connection closed";
      break;
    case SSL_ERROR_SSL:
      sslerr = ERR_get_error();
      switch (sslerr)
      {
        case 0:
          switch (err)
          {
            case 0:
              errmsg = "EOF";
              break;
            default:
              errmsg = strerror(errno);
          }
          break;
        default:
          errmsg = ERR_error_string(sslerr, NULL);
      }
      break;
    default:
      errmsg = "unknown error";
  }

  mutt_debug(LL_DEBUG1, "SSL error: %s\n", errmsg);
}
Ejemplo n.º 29
0
Archivo: ssl.c Proyecto: 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;
}
Ejemplo n.º 30
0
/**
 * imap_auth_gss - GSS Authentication support
 * @param adata Imap Account data
 * @param method Name of this authentication method
 * @retval enum Result, e.g. #IMAP_AUTH_SUCCESS
 */
enum ImapAuthRes imap_auth_gss(struct ImapAccountData *adata, const char *method)
{
  gss_buffer_desc request_buf, send_token;
  gss_buffer_t sec_token;
  gss_name_t target_name;
  gss_ctx_id_t context;
  gss_OID mech_name;
  char server_conf_flags;
  gss_qop_t quality;
  int cflags;
  OM_uint32 maj_stat, min_stat;
  unsigned long buf_size;
  int rc, retval = IMAP_AUTH_FAILURE;

  if (!(adata->capabilities & IMAP_CAP_AUTH_GSSAPI))
    return IMAP_AUTH_UNAVAIL;

  if (mutt_account_getuser(&adata->conn->account) < 0)
    return IMAP_AUTH_FAILURE;

  struct Buffer *buf1 = mutt_buffer_pool_get();
  struct Buffer *buf2 = mutt_buffer_pool_get();

  /* get an IMAP service ticket for the server */
  mutt_buffer_printf(buf1, "imap@%s", adata->conn->account.host);
  request_buf.value = buf1->data;
  request_buf.length = mutt_buffer_len(buf1);

  maj_stat = gss_import_name(&min_stat, &request_buf, gss_nt_service_name, &target_name);
  if (maj_stat != GSS_S_COMPLETE)
  {
    mutt_debug(LL_DEBUG2, "Couldn't get service name for [%s]\n", buf1);
    retval = IMAP_AUTH_UNAVAIL;
    goto cleanup;
  }
  else if (C_DebugLevel >= 2)
  {
    gss_display_name(&min_stat, target_name, &request_buf, &mech_name);
    mutt_debug(LL_DEBUG2, "Using service name [%s]\n", (char *) request_buf.value);
    gss_release_buffer(&min_stat, &request_buf);
  }
  /* Acquire initial credentials - without a TGT GSSAPI is UNAVAIL */
  sec_token = GSS_C_NO_BUFFER;
  context = GSS_C_NO_CONTEXT;

  /* build token */
  maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &context, target_name,
                                  GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
                                  0, GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL,
                                  &send_token, (unsigned int *) &cflags, NULL);
  if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
  {
    print_gss_error(maj_stat, min_stat);
    mutt_debug(LL_DEBUG1, "Error acquiring credentials - no TGT?\n");
    gss_release_name(&min_stat, &target_name);

    retval = IMAP_AUTH_UNAVAIL;
    goto cleanup;
  }

  /* now begin login */
  mutt_message(_("Authenticating (GSSAPI)..."));

  imap_cmd_start(adata, "AUTHENTICATE GSSAPI");

  /* expect a null continuation response ("+") */
  do
    rc = imap_cmd_step(adata);
  while (rc == IMAP_CMD_CONTINUE);

  if (rc != IMAP_CMD_RESPOND)
  {
    mutt_debug(LL_DEBUG2, "Invalid response from server: %s\n", buf1);
    gss_release_name(&min_stat, &target_name);
    goto bail;
  }

  /* now start the security context initialisation loop... */
  mutt_debug(LL_DEBUG2, "Sending credentials\n");
  mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
  gss_release_buffer(&min_stat, &send_token);
  mutt_buffer_addstr(buf1, "\r\n");
  mutt_socket_send(adata->conn, mutt_b2s(buf1));

  while (maj_stat == GSS_S_CONTINUE_NEEDED)
  {
    /* Read server data */
    do
      rc = imap_cmd_step(adata);
    while (rc == IMAP_CMD_CONTINUE);

    if (rc != IMAP_CMD_RESPOND)
    {
      mutt_debug(LL_DEBUG1, "#1 Error receiving server response\n");
      gss_release_name(&min_stat, &target_name);
      goto bail;
    }

    if (mutt_b64_buffer_decode(buf2, adata->buf + 2) < 0)
    {
      mutt_debug(LL_DEBUG1, "Invalid base64 server response\n");
      gss_release_name(&min_stat, &target_name);
      goto err_abort_cmd;
    }
    request_buf.value = buf2->data;
    request_buf.length = mutt_buffer_len(buf2);
    sec_token = &request_buf;

    /* Write client data */
    maj_stat = gss_init_sec_context(
        &min_stat, GSS_C_NO_CREDENTIAL, &context, target_name, GSS_C_NO_OID,
        GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS,
        sec_token, NULL, &send_token, (unsigned int *) &cflags, NULL);
    if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
    {
      print_gss_error(maj_stat, min_stat);
      mutt_debug(LL_DEBUG1, "Error exchanging credentials\n");
      gss_release_name(&min_stat, &target_name);

      goto err_abort_cmd;
    }
    mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
    gss_release_buffer(&min_stat, &send_token);
    mutt_buffer_addstr(buf1, "\r\n");
    mutt_socket_send(adata->conn, mutt_b2s(buf1));
  }

  gss_release_name(&min_stat, &target_name);

  /* get security flags and buffer size */
  do
    rc = imap_cmd_step(adata);
  while (rc == IMAP_CMD_CONTINUE);

  if (rc != IMAP_CMD_RESPOND)
  {
    mutt_debug(LL_DEBUG1, "#2 Error receiving server response\n");
    goto bail;
  }
  if (mutt_b64_buffer_decode(buf2, adata->buf + 2) < 0)
  {
    mutt_debug(LL_DEBUG1, "Invalid base64 server response\n");
    goto err_abort_cmd;
  }
  request_buf.value = buf2->data;
  request_buf.length = mutt_buffer_len(buf2);

  maj_stat = gss_unwrap(&min_stat, context, &request_buf, &send_token, &cflags, &quality);
  if (maj_stat != GSS_S_COMPLETE)
  {
    print_gss_error(maj_stat, min_stat);
    mutt_debug(LL_DEBUG2, "Couldn't unwrap security level data\n");
    gss_release_buffer(&min_stat, &send_token);
    goto err_abort_cmd;
  }
  mutt_debug(LL_DEBUG2, "Credential exchange complete\n");

  /* first octet is security levels supported. We want NONE */
  server_conf_flags = ((char *) send_token.value)[0];
  if (!(((char *) send_token.value)[0] & GSS_AUTH_P_NONE))
  {
    mutt_debug(LL_DEBUG2, "Server requires integrity or privacy\n");
    gss_release_buffer(&min_stat, &send_token);
    goto err_abort_cmd;
  }

  /* we don't care about buffer size if we don't wrap content. But here it is */
  ((char *) send_token.value)[0] = '\0';
  buf_size = ntohl(*((long *) send_token.value));
  gss_release_buffer(&min_stat, &send_token);
  mutt_debug(LL_DEBUG2, "Unwrapped security level flags: %c%c%c\n",
             (server_conf_flags & GSS_AUTH_P_NONE) ? 'N' : '-',
             (server_conf_flags & GSS_AUTH_P_INTEGRITY) ? 'I' : '-',
             (server_conf_flags & GSS_AUTH_P_PRIVACY) ? 'P' : '-');
  mutt_debug(LL_DEBUG2, "Maximum GSS token size is %ld\n", buf_size);

  /* agree to terms (hack!) */
  buf_size = htonl(buf_size); /* not relevant without integrity/privacy */
  mutt_buffer_reset(buf1);
  mutt_buffer_addch(buf1, GSS_AUTH_P_NONE);
  mutt_buffer_addstr_n(buf1, ((char *) &buf_size) + 1, 3);
  /* server decides if principal can log in as user */
  mutt_buffer_addstr(buf1, adata->conn->account.user);
  request_buf.value = buf1->data;
  request_buf.length = mutt_buffer_len(buf1);
  maj_stat = gss_wrap(&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
                      &cflags, &send_token);
  if (maj_stat != GSS_S_COMPLETE)
  {
    mutt_debug(LL_DEBUG2, "Error creating login request\n");
    goto err_abort_cmd;
  }

  mutt_b64_buffer_encode(buf1, send_token.value, send_token.length);
  mutt_debug(LL_DEBUG2, "Requesting authorisation as %s\n", adata->conn->account.user);
  mutt_buffer_addstr(buf1, "\r\n");
  mutt_socket_send(adata->conn, mutt_b2s(buf1));

  /* Joy of victory or agony of defeat? */
  do
    rc = imap_cmd_step(adata);
  while (rc == IMAP_CMD_CONTINUE);
  if (rc == IMAP_CMD_RESPOND)
  {
    mutt_debug(LL_DEBUG1, "Unexpected server continuation request\n");
    goto err_abort_cmd;
  }
  if (imap_code(adata->buf))
  {
    /* flush the security context */
    mutt_debug(LL_DEBUG2, "Releasing GSS credentials\n");
    maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token);
    if (maj_stat != GSS_S_COMPLETE)
      mutt_debug(LL_DEBUG1, "Error releasing credentials\n");

    /* send_token may contain a notification to the server to flush
     * credentials. RFC1731 doesn't specify what to do, and since this
     * support is only for authentication, we'll assume the server knows
     * enough to flush its own credentials */
    gss_release_buffer(&min_stat, &send_token);

    retval = IMAP_AUTH_SUCCESS;
    goto cleanup;
  }
  else
    goto bail;

err_abort_cmd:
  mutt_socket_send(adata->conn, "*\r\n");
  do
    rc = imap_cmd_step(adata);
  while (rc == IMAP_CMD_CONTINUE);

bail:
  mutt_error(_("GSSAPI authentication failed"));
  retval = IMAP_AUTH_FAILURE;

cleanup:
  mutt_buffer_pool_release(&buf1);
  mutt_buffer_pool_release(&buf2);

  return retval;
}