Merge branch 'ob/imap-send-ssl-verify' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Feb 2013 05:54:15 +0000 (21:54 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Feb 2013 05:54:15 +0000 (21:54 -0800)
* ob/imap-send-ssl-verify:
imap-send: support subjectAltName as well
imap-send: the subject of SSL certificate must match the host
imap-send: move #ifdef around

1  2 
imap-send.c
diff --combined imap-send.c
index d42e4712972794f055aec6630ba86797d7e5343c,171c887076b115f8f53707c2c5845c8f6d3e69bd..ef500111ec0bca6514e519d39e7a269a7342cddf
  #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 {
@@@ -139,6 -139,7 +140,6 @@@ static struct imap_server_conf server 
  struct imap_store_conf {
        struct store_conf gen;
        struct imap_server_conf *server;
 -      unsigned use_namespace:1;
  };
  
  #define NIL   (void *)0x1
@@@ -266,12 -267,64 +267,64 @@@ static void socket_perror(const char *f
        }
  }
  
+ #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;
        }
  
+       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)
  {
@@@ -1022,7 -1085,7 +1085,7 @@@ static int auth_cram_md5(struct imap_st
  
        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);
  
@@@ -1186,10 -1249,13 +1249,10 @@@ static struct store *imap_open_store(st
                        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;
@@@ -1513,8 -1579,6 +1576,8 @@@ int main(int argc, char **argv
  
        git_extract_argv0_path(argv[0]);
  
 +      git_setup_gettext();
 +
        if (argc != 1)
                usage(imap_send_usage);