#include "cache.h"
#include "exec_cmd.h"
#include "run-command.h"
+#include "prompt.h"
#ifdef NO_OPENSSL
typedef void *SSL;
#else
#include <openssl/evp.h>
#include <openssl/hmac.h>
+#include <openssl/x509v3.h>
#endif
struct store_conf {
unsigned trash_remote_new:1, trash_only_new:1;
};
-struct string_list {
- struct string_list *next;
- char string[1];
-};
-
-struct channel_conf {
- struct channel_conf *next;
- char *name;
- struct store_conf *master, *slave;
- char *master_name, *slave_name;
- char *sync_state;
- struct string_list *patterns;
- int mops, sops;
- unsigned max_messages; /* for slave only */
-};
-
-struct group_conf {
- struct group_conf *next;
- char *name;
- struct string_list *channels;
-};
-
/* For message->status */
#define M_RECENT (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
#define M_DEAD (1<<1) /* expunged */
struct message {
struct message *next;
- /* struct string_list *keywords; */
size_t size; /* zero implies "not fetched" */
int uid;
unsigned char flags, status;
struct imap_store_conf {
struct store_conf gen;
struct imap_server_conf *server;
- unsigned use_namespace:1;
};
#define NIL (void *)0x1
}
}
+#ifdef NO_OPENSSL
static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
{
-#ifdef NO_OPENSSL
fprintf(stderr, "SSL requested but SSL support not compiled in\n");
return -1;
+}
+
#else
+
+static int host_matches(const char *host, const char *pattern)
+{
+ if (pattern[0] == '*' && pattern[1] == '.') {
+ pattern += 2;
+ if (!(host = strchr(host, '.')))
+ return 0;
+ host++;
+ }
+
+ return *host && *pattern && !strcasecmp(host, pattern);
+}
+
+static int verify_hostname(X509 *cert, const char *hostname)
+{
+ int len;
+ X509_NAME *subj;
+ char cname[1000];
+ int i, found;
+ STACK_OF(GENERAL_NAME) *subj_alt_names;
+
+ /* try the DNS subjectAltNames */
+ found = 0;
+ if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {
+ int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);
+ for (i = 0; !found && i < num_subj_alt_names; i++) {
+ GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
+ if (subj_alt_name->type == GEN_DNS &&
+ strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&
+ host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))
+ found = 1;
+ }
+ sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
+ }
+ if (found)
+ return 0;
+
+ /* try the common name */
+ if (!(subj = X509_get_subject_name(cert)))
+ return error("cannot get certificate subject");
+ if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
+ return error("cannot get certificate common name");
+ if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
+ return 0;
+ return error("certificate owner '%s' does not match hostname '%s'",
+ cname, hostname);
+}
+
+static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+{
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
const SSL_METHOD *meth;
#else
#endif
SSL_CTX *ctx;
int ret;
+ X509 *cert;
SSL_library_init();
SSL_load_error_strings();
return -1;
}
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ /*
+ * SNI (RFC4366)
+ * OpenSSL does not document this function, but the implementation
+ * returns 1 on success, 0 on failure after calling SSLerr().
+ */
+ ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+ if (ret != 1)
+ warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+#endif
+
ret = SSL_connect(sock->ssl);
if (ret <= 0) {
socket_perror("SSL_connect", sock, ret);
return -1;
}
+ if (verify) {
+ /* make sure the hostname matches that of the certificate */
+ cert = SSL_get_peer_certificate(sock->ssl);
+ if (!cert)
+ return error("unable to get peer certificate.");
+ if (verify_hostname(cert, server.host) < 0)
+ return -1;
+ }
+
return 0;
-#endif
}
+#endif
static int socket_read(struct imap_socket *sock, char *buf, int len)
{
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response))
- return error("IMAP error: sending response failed\n");
+ return error("IMAP error: sending response failed");
free(response);
if (!preauth) {
#ifndef NO_OPENSSL
if (!srvc->use_ssl && CAP(STARTTLS)) {
- if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
+ if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
goto bail;
if (ssl_socket_connect(&imap->buf.sock, 1,
srvc->ssl_verify))
goto bail;
/* capabilities may have changed, so get the new capabilities */
- if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
+ if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
goto bail;
}
#endif
goto bail;
}
if (!srvc->pass) {
- char prompt[80];
- sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
- arg = git_getpass(prompt);
- if (!arg) {
- perror("getpass");
- exit(1);
- }
+ struct strbuf prompt = STRBUF_INIT;
+ strbuf_addf(&prompt, "Password (%s@%s): ", srvc->user, srvc->host);
+ arg = git_getpass(prompt.buf);
+ strbuf_release(&prompt);
if (!*arg) {
fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host);
goto bail;
git_extract_argv0_path(argv[0]);
+ git_setup_gettext();
+
if (argc != 1)
usage(imap_send_usage);