Blame src/lftp_ssl.cc

Packit Service a2489d
/*
Packit Service a2489d
 * lftp - file transfer program
Packit Service a2489d
 *
Packit Service a2489d
 * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
Packit Service a2489d
 *
Packit Service a2489d
 * This program is free software; you can redistribute it and/or modify
Packit Service a2489d
 * it under the terms of the GNU General Public License as published by
Packit Service a2489d
 * the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
 * (at your option) any later version.
Packit Service a2489d
 *
Packit Service a2489d
 * This program is distributed in the hope that it will be useful,
Packit Service a2489d
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
 * GNU General Public License for more details.
Packit Service a2489d
 *
Packit Service a2489d
 * You should have received a copy of the GNU General Public License
Packit Service a2489d
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service a2489d
 */
Packit Service a2489d
Packit Service a2489d
#include <config.h>
Packit Service a2489d
Packit Service a2489d
#if USE_SSL
Packit Service a2489d
#include <sys/types.h>
Packit Service a2489d
#include <sys/mman.h>
Packit Service a2489d
#include <sys/stat.h>
Packit Service a2489d
#include <unistd.h>
Packit Service a2489d
#include <fcntl.h>
Packit Service a2489d
#include <errno.h>
Packit Service a2489d
#include <string.h>
Packit Service a2489d
#include "lftp_ssl.h"
Packit Service a2489d
#include "xmalloc.h"
Packit Service a2489d
#include "ResMgr.h"
Packit Service a2489d
#include "log.h"
Packit Service a2489d
#include "misc.h"
Packit Service a2489d
#include "network.h"
Packit Service a2489d
#include "buffer.h"
Packit Service a2489d
extern "C" {
Packit Service a2489d
#include "c-ctype.h"
Packit Service a2489d
#include "quotearg.h"
Packit Service a2489d
#include "quote.h"
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
lftp_ssl_base::lftp_ssl_base(int fd1,handshake_mode_t m,const char *h)
Packit Service a2489d
   : hostname(h)
Packit Service a2489d
{
Packit Service a2489d
   fd=fd1;
Packit Service a2489d
   handshake_done=false;
Packit Service a2489d
   handshake_mode=m;
Packit Service a2489d
   fatal=false;
Packit Service a2489d
   cert_error=false;
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_base::set_error(const char *s1,const char *s2)
Packit Service a2489d
{
Packit Service a2489d
   if(s2)
Packit Service a2489d
      error.vset(s1,": ",s2,NULL);
Packit Service a2489d
   else
Packit Service a2489d
      error.set(s1);
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_base::set_cert_error(const char *s,const xstring& fp)
Packit Service a2489d
{
Packit Service a2489d
   bool verify_default=ResMgr::QueryBool("ssl:verify-certificate",hostname);
Packit Service a2489d
   bool verify=ResMgr::QueryBool("ssl:verify-certificate",hostname);
Packit Service a2489d
   xstring fp_hex;
Packit Service a2489d
   if(fp) {
Packit Service a2489d
      for(unsigned i=0; i
Packit Service a2489d
	 fp_hex.appendf("%02X:",(unsigned char)fp[i]);
Packit Service a2489d
      fp_hex.chomp(':');
Packit Service a2489d
      if(verify && verify_default)
Packit Service a2489d
	 verify=ResMgr::QueryBool("ssl:verify-certificate",fp_hex);
Packit Service a2489d
      s=xstring::format("%s (%s)",s,fp_hex.get());
Packit Service a2489d
   }
Packit Service a2489d
   const char *const warn=verify?"ERROR":"WARNING";
Packit Service a2489d
   Log::global->Format(0,"%s: Certificate verification: %s\n",warn,s);
Packit Service a2489d
   if(verify && !error)
Packit Service a2489d
   {
Packit Service a2489d
      set_error("Certificate verification",s);
Packit Service a2489d
      fatal=true;
Packit Service a2489d
      cert_error=true;
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#if USE_GNUTLS
Packit Service a2489d
Packit Service a2489d
/* Helper functions to load a certificate and key
Packit Service a2489d
 * files into memory. They use mmap for simplicity.
Packit Service a2489d
 */
Packit Service a2489d
static gnutls_datum_t mmap_file(const char *file)
Packit Service a2489d
{
Packit Service a2489d
    int fd;
Packit Service a2489d
    gnutls_datum_t mmaped_file = { NULL, 0 };
Packit Service a2489d
    struct stat stat_st;
Packit Service a2489d
    void *ptr;
Packit Service a2489d
Packit Service a2489d
    fd = open(file, 0);
Packit Service a2489d
    if (fd == -1)
Packit Service a2489d
	return mmaped_file;
Packit Service a2489d
Packit Service a2489d
    fstat(fd, &stat_st);
Packit Service a2489d
Packit Service a2489d
    if ((ptr =
Packit Service a2489d
	 mmap(NULL, stat_st.st_size, PROT_READ, MAP_SHARED, fd,
Packit Service a2489d
	      0)) == MAP_FAILED)
Packit Service a2489d
    {
Packit Service a2489d
      close(fd);
Packit Service a2489d
	return mmaped_file;
Packit Service a2489d
    }
Packit Service a2489d
   close(fd);
Packit Service a2489d
Packit Service a2489d
    mmaped_file.data = (unsigned char*)ptr;
Packit Service a2489d
    mmaped_file.size = stat_st.st_size;
Packit Service a2489d
Packit Service a2489d
    return mmaped_file;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
static void munmap_file(gnutls_datum_t data)
Packit Service a2489d
{
Packit Service a2489d
    munmap(data.data, data.size);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#if LFTP_LIBGNUTLS_VERSION_CODE < 0x010201
Packit Service a2489d
#define gnutls_x509_crt_list_import lftp_gnutls_x509_crt_list_import
Packit Service a2489d
#define GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED 1
Packit Service a2489d
static
Packit Service a2489d
int gnutls_x509_crt_list_import(gnutls_x509_crt_t *certs, unsigned int* cert_max,
Packit Service a2489d
    const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format, unsigned int flags);
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
void lftp_ssl_gnutls_instance::LoadCA()
Packit Service a2489d
{
Packit Service a2489d
   // free CA first
Packit Service a2489d
   for(unsigned i=0; i
Packit Service a2489d
      gnutls_x509_crt_deinit(ca_list[i]);
Packit Service a2489d
   xfree(ca_list);
Packit Service a2489d
   ca_list=0;
Packit Service a2489d
   ca_list_size=0;
Packit Service a2489d
Packit Service a2489d
   const char *ca_file=ResMgr::Query("ssl:ca-file",0);
Packit Service a2489d
   if(!ca_file || !ca_file[0])
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   gnutls_datum_t ca_pem=mmap_file(ca_file);
Packit Service a2489d
   if(!ca_pem.data)
Packit Service a2489d
   {
Packit Service a2489d
      Log::global->Format(0,"%s: %s\n",ca_file,strerror(errno));
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   ca_list_size=64;
Packit Service a2489d
   ca_list=(gnutls_x509_crt_t*)xmalloc(ca_list_size*sizeof(gnutls_x509_crl_t));
Packit Service a2489d
   int res=gnutls_x509_crt_list_import(ca_list,&ca_list_size,&ca_pem,GNUTLS_X509_FMT_PEM,GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
Packit Service a2489d
   if(res==GNUTLS_E_SHORT_MEMORY_BUFFER)
Packit Service a2489d
   {
Packit Service a2489d
      ca_list=(gnutls_x509_crt_t*)xrealloc(ca_list,ca_list_size*sizeof(gnutls_x509_crl_t));
Packit Service a2489d
      res=gnutls_x509_crt_list_import(ca_list,&ca_list_size,&ca_pem,GNUTLS_X509_FMT_PEM,0);
Packit Service a2489d
   }
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      Log::global->Format(0,"gnutls_x509_crt_list_import: %s\n",gnutls_strerror(res));
Packit Service a2489d
      xfree(ca_list);
Packit Service a2489d
      ca_list=0;
Packit Service a2489d
      ca_list_size=0;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   munmap_file(ca_pem);
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_gnutls_instance::LoadCRL()
Packit Service a2489d
{
Packit Service a2489d
   // free CRL first
Packit Service a2489d
   for(unsigned i=0; i
Packit Service a2489d
      gnutls_x509_crl_deinit(crl_list[i]);
Packit Service a2489d
   xfree(crl_list);
Packit Service a2489d
   crl_list=0;
Packit Service a2489d
   crl_list_size=0;
Packit Service a2489d
Packit Service a2489d
   const char *crl_file=ResMgr::Query("ssl:crl-file",0);
Packit Service a2489d
   if(!crl_file || !crl_file[0])
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   gnutls_datum_t crl_pem=mmap_file(crl_file);
Packit Service a2489d
   if(!crl_pem.data)
Packit Service a2489d
   {
Packit Service a2489d
      Log::global->Format(0,"%s: %s\n",crl_file,strerror(errno));
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   crl_list_size=1;
Packit Service a2489d
   crl_list=(gnutls_x509_crl_t*)xmalloc(crl_list_size*sizeof(gnutls_x509_crl_t));
Packit Service a2489d
   int res=gnutls_x509_crl_import(crl_list[0],&crl_pem,GNUTLS_X509_FMT_PEM);
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      Log::global->Format(0,"gnutls_x509_crl_import: %s\n",gnutls_strerror(res));
Packit Service a2489d
      xfree(crl_list);
Packit Service a2489d
      crl_list=0;
Packit Service a2489d
      crl_list_size=0;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   munmap_file(crl_pem);
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_gnutls_instance::Reconfig(const char *name)
Packit Service a2489d
{
Packit Service a2489d
   if(!name || !strcmp(name,"ssl:ca-file"))
Packit Service a2489d
      LoadCA();
Packit Service a2489d
   if(!name || !strcmp(name,"ssl:crl-file"))
Packit Service a2489d
      LoadCRL();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
static const char *lftp_ssl_find_ca_file()
Packit Service a2489d
{
Packit Service a2489d
   // a few possible locations of ca-bundle.crt
Packit Service a2489d
   static const char *const ca_file_location[]={
Packit Service a2489d
      "/etc/pki/tls/certs/ca-bundle.crt",
Packit Service a2489d
      "/etc/certs/ca-bundle.crt",
Packit Service a2489d
      "/usr/share/ssl/certs/ca-bundle.crt",
Packit Service a2489d
      "/etc/ssl/certs/ca-certificates.crt",
Packit Service a2489d
      "/usr/local/ssl/certs/ca-bundle.crt",
Packit Service a2489d
      "/etc/apache/ssl.crt/ca-bundle.crt",
Packit Service a2489d
      "/usr/share/curl/curl-ca-bundle.crt",
Packit Service a2489d
      0};
Packit Service a2489d
   for(int i=0; ca_file_location[i]; i++)
Packit Service a2489d
   {
Packit Service a2489d
      if(access(ca_file_location[i], R_OK)==0)
Packit Service a2489d
	 return ca_file_location[i];
Packit Service a2489d
   }
Packit Service a2489d
   return 0;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
static void lftp_ssl_gnutls_log_func(int level, const char *msg)
Packit Service a2489d
{
Packit Service a2489d
   if(!strncmp(msg,"ASSERT",6)
Packit Service a2489d
   || !strncmp(msg,"READ",4)
Packit Service a2489d
   || !strncmp(msg,"WRITE",5))
Packit Service a2489d
      level+=10;
Packit Service a2489d
   Log::global->Format(9+level,"GNUTLS: %s",msg);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
lftp_ssl_gnutls_instance::lftp_ssl_gnutls_instance()
Packit Service a2489d
{
Packit Service a2489d
   ca_list=0;
Packit Service a2489d
   ca_list_size=0;
Packit Service a2489d
   crl_list=0;
Packit Service a2489d
   crl_list_size=0;
Packit Service a2489d
Packit Service a2489d
   gnutls_global_init();
Packit Service a2489d
   gnutls_global_set_log_function(lftp_ssl_gnutls_log_func);
Packit Service a2489d
   gnutls_global_set_log_level(9);
Packit Service a2489d
Packit Service a2489d
   const char *ca_file=ResMgr::Query("ssl:ca-file",0);
Packit Service a2489d
   if(!ca_file || !ca_file[0])
Packit Service a2489d
      ResMgr::Set("ssl:ca-file",0,lftp_ssl_find_ca_file());
Packit Service a2489d
Packit Service a2489d
   Reconfig(0);
Packit Service a2489d
}
Packit Service a2489d
lftp_ssl_gnutls_instance::~lftp_ssl_gnutls_instance()
Packit Service a2489d
{
Packit Service a2489d
   gnutls_global_deinit();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
Packit Service a2489d
Ref<lftp_ssl_gnutls_instance> lftp_ssl_gnutls::instance;
Packit Service a2489d
Packit Service a2489d
void lftp_ssl_gnutls::global_init()
Packit Service a2489d
{
Packit Service a2489d
   if(!instance)
Packit Service a2489d
      instance=new lftp_ssl_gnutls_instance();
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_gnutls::global_deinit()
Packit Service a2489d
{
Packit Service a2489d
   instance=0;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#ifndef GNUTLS_NONBLOCK // for gnutls < 3.0
Packit Service a2489d
#  define add_GNUTLS_NONBLOCK
Packit Service a2489d
# else
Packit Service a2489d
#  define add_GNUTLS_NONBLOCK |GNUTLS_NONBLOCK
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
lftp_ssl_gnutls::lftp_ssl_gnutls(int fd1,handshake_mode_t m,const char *h)
Packit Service a2489d
   : lftp_ssl_base(fd1,m,h)
Packit Service a2489d
{
Packit Service a2489d
   global_init();
Packit Service a2489d
Packit Service a2489d
   cred=0;
Packit Service a2489d
Packit Service a2489d
   gnutls_init(&session,(m==CLIENT?GNUTLS_CLIENT:GNUTLS_SERVER)add_GNUTLS_NONBLOCK);
Packit Service a2489d
   gnutls_set_default_priority(session);
Packit Service a2489d
Packit Service a2489d
   gnutls_transport_set_ptr(session,(gnutls_transport_ptr_t)fd);
Packit Service a2489d
Packit Service a2489d
   const char *priority=ResMgr::Query("ssl:priority", 0);
Packit Service a2489d
   if(!priority || !*priority)
Packit Service a2489d
   {
Packit Service a2489d
      // hack for some ftp servers
Packit Service a2489d
      const char *auth=ResMgr::Query("ftp:ssl-auth", hostname);
Packit Service a2489d
      if(auth && !strncmp(auth, "SSL", 3))
Packit Service a2489d
         priority="NORMAL:+VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2";
Packit Service a2489d
   }
Packit Service a2489d
   if(priority && *priority)
Packit Service a2489d
   {
Packit Service a2489d
      int res = gnutls_priority_set_direct(session, priority, 0);
Packit Service a2489d
      if(res != GNUTLS_E_SUCCESS)
Packit Service a2489d
	 Log::global->Format(0,"gnutls_priority_set_direct(`%s'): %s\n",priority,gnutls_strerror(res));
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(h && ResMgr::QueryBool("ssl:use-sni",h)) {
Packit Service a2489d
      if(gnutls_server_name_set(session, GNUTLS_NAME_DNS, h, xstrlen(h)) < 0)
Packit Service a2489d
	 fprintf(stderr,"WARNING: failed to configure server name indication (SNI) TLS extension\n");
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_gnutls::load_keys()
Packit Service a2489d
{
Packit Service a2489d
   gnutls_certificate_allocate_credentials(&cred);
Packit Service a2489d
   int res;
Packit Service a2489d
#if 0
Packit Service a2489d
   const char *ca_file=ResMgr::Query("ssl:ca-file",hostname);
Packit Service a2489d
   const char *ca_file0=ResMgr::Query("ssl:ca-file",0);
Packit Service a2489d
   if(ca_file && ca_file[0] && xstrcmp(ca_file,ca_file0))
Packit Service a2489d
   {
Packit Service a2489d
      res=gnutls_certificate_set_x509_trust_file(cred,ca_file,GNUTLS_X509_FMT_PEM);
Packit Service a2489d
      if(res<0)
Packit Service a2489d
	 Log::global->Format(0,"gnutls_certificate_set_x509_trust_file(%s): %s\n",ca_file,gnutls_strerror(res));
Packit Service a2489d
   }
Packit Service a2489d
   const char *crl_file=ResMgr::Query("ssl:crl-file",hostname);
Packit Service a2489d
   const char *crl_file0=ResMgr::Query("ssl:crl-file",0);
Packit Service a2489d
   if(crl_file && crl_file[0] && xstrcmp(crl_file,crl_file0))
Packit Service a2489d
   {
Packit Service a2489d
      res=gnutls_certificate_set_x509_crl_file(cred,crl_file,GNUTLS_X509_FMT_PEM);
Packit Service a2489d
      if(res<0)
Packit Service a2489d
	 Log::global->Format(0,"gnutls_certificate_set_x509_crl_file(%s): %s\n",crl_file,gnutls_strerror(res));
Packit Service a2489d
   }
Packit Service a2489d
#endif
Packit Service a2489d
   const char *key_file =ResMgr::Query("ssl:key-file",hostname);
Packit Service a2489d
   const char *cert_file=ResMgr::Query("ssl:cert-file",hostname);
Packit Service a2489d
   if(key_file && key_file[0] && cert_file && cert_file[0])
Packit Service a2489d
   {
Packit Service a2489d
      res=gnutls_certificate_set_x509_key_file(cred,cert_file,key_file,GNUTLS_X509_FMT_PEM);
Packit Service a2489d
      if(res<0)
Packit Service a2489d
	 Log::global->Format(0,"gnutls_certificate_set_x509_key_file(%s,%s): %s\n",cert_file,key_file,gnutls_strerror(res));
Packit Service a2489d
   }
Packit Service a2489d
   gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_gnutls::shutdown()
Packit Service a2489d
{
Packit Service a2489d
   if(handshake_done)
Packit Service a2489d
      gnutls_bye(session,GNUTLS_SHUT_RDWR);  // FIXME - E_AGAIN
Packit Service a2489d
}
Packit Service a2489d
lftp_ssl_gnutls::~lftp_ssl_gnutls()
Packit Service a2489d
{
Packit Service a2489d
   if(cred)
Packit Service a2489d
      gnutls_certificate_free_credentials(cred);
Packit Service a2489d
   gnutls_deinit(session);
Packit Service a2489d
   session=0;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* This function will try to verify the peer's certificate chain, and
Packit Service a2489d
 * also check if the hostname matches, and the activation, expiration dates.
Packit Service a2489d
 */
Packit Service a2489d
void lftp_ssl_gnutls::verify_certificate_chain(const gnutls_datum_t *cert_chain,int cert_chain_length)
Packit Service a2489d
{
Packit Service a2489d
   int i;
Packit Service a2489d
   gnutls_x509_crt_t *cert=(gnutls_x509_crt_t*)alloca(cert_chain_length*sizeof(gnutls_x509_crt_t));
Packit Service a2489d
Packit Service a2489d
   /* Import all the certificates in the chain to
Packit Service a2489d
    * native certificate format.
Packit Service a2489d
    */
Packit Service a2489d
   for (i = 0; i < cert_chain_length; i++)
Packit Service a2489d
   {
Packit Service a2489d
      gnutls_x509_crt_init(&cert[i]);
Packit Service a2489d
      gnutls_x509_crt_import(cert[i],&cert_chain[i],GNUTLS_X509_FMT_DER);
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   /* Now verify the certificates against their issuers
Packit Service a2489d
    * in the chain.
Packit Service a2489d
    */
Packit Service a2489d
   for (i = 1; i < cert_chain_length; i++)
Packit Service a2489d
      verify_cert2(cert[i - 1], cert[i]);
Packit Service a2489d
Packit Service a2489d
    /* Here we must verify the last certificate in the chain against
Packit Service a2489d
     * our trusted CA list.
Packit Service a2489d
     */
Packit Service a2489d
   verify_last_cert(cert[cert_chain_length - 1]);
Packit Service a2489d
Packit Service a2489d
   /* Check if the name in the first certificate matches our destination!
Packit Service a2489d
    */
Packit Service a2489d
   bool check_hostname = ResMgr::QueryBool("ssl:check-hostname", hostname);
Packit Service a2489d
   if(check_hostname) {
Packit Service a2489d
      if(!gnutls_x509_crt_check_hostname(cert[0], hostname))
Packit Service a2489d
	 set_cert_error(xstring::format("certificate common name doesn't match requested host name %s",quote(hostname)),get_fp(cert[0]));
Packit Service a2489d
   } else {
Packit Service a2489d
      Log::global->Format(0, "WARNING: Certificate verification: hostname checking disabled\n");
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   for (i = 0; i < cert_chain_length; i++)
Packit Service a2489d
      gnutls_x509_crt_deinit(cert[i]);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
Packit Service a2489d
/* Verifies a certificate against an other certificate
Packit Service a2489d
 * which is supposed to be it's issuer. Also checks the
Packit Service a2489d
 * crl_list if the certificate is revoked.
Packit Service a2489d
 */
Packit Service a2489d
void lftp_ssl_gnutls::verify_cert2(gnutls_x509_crt_t crt,gnutls_x509_crt_t issuer)
Packit Service a2489d
{
Packit Service a2489d
   int ret;
Packit Service a2489d
   time_t now = SMTask::now;
Packit Service a2489d
   size_t name_size;
Packit Service a2489d
   char name[256];
Packit Service a2489d
Packit Service a2489d
   /* Print information about the certificates to
Packit Service a2489d
    * be checked.
Packit Service a2489d
    */
Packit Service a2489d
   name_size = sizeof(name);
Packit Service a2489d
   gnutls_x509_crt_get_dn(crt, name, &name_size);
Packit Service a2489d
Packit Service a2489d
   Log::global->Format(9, "Certificate: %s\n", name);
Packit Service a2489d
Packit Service a2489d
   name_size = sizeof(name);
Packit Service a2489d
   gnutls_x509_crt_get_issuer_dn(crt, name, &name_size);
Packit Service a2489d
Packit Service a2489d
   Log::global->Format(9, " Issued by:        %s\n", name);
Packit Service a2489d
Packit Service a2489d
   /* Get the DN of the issuer cert.
Packit Service a2489d
    */
Packit Service a2489d
   name_size = sizeof(name);
Packit Service a2489d
   gnutls_x509_crt_get_dn(issuer, name, &name_size);
Packit Service a2489d
Packit Service a2489d
   Log::global->Format(9, " Checking against: %s\n", name);
Packit Service a2489d
Packit Service a2489d
   /* Do the actual verification.
Packit Service a2489d
    */
Packit Service a2489d
   unsigned crt_status=0;
Packit Service a2489d
   unsigned issuer_status=0;
Packit Service a2489d
   gnutls_x509_crt_verify(crt, &issuer, 1, 0, &crt_status);
Packit Service a2489d
   if(crt_status&GNUTLS_CERT_SIGNER_NOT_CA)
Packit Service a2489d
   {
Packit Service a2489d
      // recheck the issuer certificate against CA
Packit Service a2489d
      gnutls_x509_crt_verify(issuer, instance->ca_list, instance->ca_list_size, 0, &issuer_status);
Packit Service a2489d
      if(issuer_status==0)
Packit Service a2489d
	 crt_status&=~GNUTLS_CERT_SIGNER_NOT_CA;
Packit Service a2489d
      if(crt_status==GNUTLS_CERT_INVALID)
Packit Service a2489d
	 crt_status=0;
Packit Service a2489d
   }
Packit Service a2489d
   if (crt_status & GNUTLS_CERT_INVALID)
Packit Service a2489d
   {
Packit Service a2489d
      char msg[256];
Packit Service a2489d
      strcpy(msg,"Not trusted");
Packit Service a2489d
      if(crt_status & GNUTLS_CERT_SIGNER_NOT_FOUND)
Packit Service a2489d
	 strcat(msg,": no issuer was found");
Packit Service a2489d
      if(crt_status & GNUTLS_CERT_SIGNER_NOT_CA)
Packit Service a2489d
	 strcat(msg,": issuer is not a CA");
Packit Service a2489d
      set_cert_error(msg,get_fp(crt));
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
      Log::global->Format(9, "  Trusted\n");
Packit Service a2489d
Packit Service a2489d
Packit Service a2489d
    /* Now check the expiration dates.
Packit Service a2489d
     */
Packit Service a2489d
    if (gnutls_x509_crt_get_activation_time(crt) > now)
Packit Service a2489d
	set_cert_error("Not yet activated",get_fp(crt));
Packit Service a2489d
Packit Service a2489d
    if (gnutls_x509_crt_get_expiration_time(crt) < now)
Packit Service a2489d
	set_cert_error("Expired",get_fp(crt));
Packit Service a2489d
Packit Service a2489d
    /* Check if the certificate is revoked.
Packit Service a2489d
     */
Packit Service a2489d
    ret = gnutls_x509_crt_check_revocation(crt, instance->crl_list, instance->crl_list_size);
Packit Service a2489d
    if (ret == 1) {		/* revoked */
Packit Service a2489d
	set_cert_error("Revoked",get_fp(crt));
Packit Service a2489d
    }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
Packit Service a2489d
/* Verifies a certificate against the trusted CA list.
Packit Service a2489d
 * Also checks the crl_list if the certificate is revoked.
Packit Service a2489d
 */
Packit Service a2489d
void lftp_ssl_gnutls::verify_last_cert(gnutls_x509_crt_t crt)
Packit Service a2489d
{
Packit Service a2489d
   unsigned int crt_status;
Packit Service a2489d
   int ret;
Packit Service a2489d
   time_t now = SMTask::now;
Packit Service a2489d
   size_t name_size;
Packit Service a2489d
   char name[256];
Packit Service a2489d
Packit Service a2489d
   /* Print information about the certificates to
Packit Service a2489d
    * be checked.
Packit Service a2489d
    */
Packit Service a2489d
   name_size = sizeof(name);
Packit Service a2489d
   gnutls_x509_crt_get_dn(crt, name, &name_size);
Packit Service a2489d
Packit Service a2489d
   Log::global->Format(9, "Certificate: %s\n", name);
Packit Service a2489d
Packit Service a2489d
   name_size = sizeof(name);
Packit Service a2489d
   gnutls_x509_crt_get_issuer_dn(crt, name, &name_size);
Packit Service a2489d
Packit Service a2489d
   Log::global->Format(9, " Issued by: %s\n", name);
Packit Service a2489d
Packit Service a2489d
   /* Do the actual verification.
Packit Service a2489d
    */
Packit Service a2489d
   gnutls_x509_crt_verify(crt, instance->ca_list, instance->ca_list_size, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &crt_status);
Packit Service a2489d
Packit Service a2489d
   if (crt_status & GNUTLS_CERT_INVALID)
Packit Service a2489d
   {
Packit Service a2489d
      char msg[256];
Packit Service a2489d
      strcpy(msg,"Not trusted");
Packit Service a2489d
      if (crt_status & GNUTLS_CERT_SIGNER_NOT_CA)
Packit Service a2489d
	 strcat(msg,": Issuer is not a CA");
Packit Service a2489d
      set_cert_error(msg,get_fp(crt));
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
      Log::global->Format(9, "  Trusted\n");
Packit Service a2489d
Packit Service a2489d
Packit Service a2489d
   /* Now check the expiration dates.
Packit Service a2489d
    */
Packit Service a2489d
   if(gnutls_x509_crt_get_activation_time(crt) > now)
Packit Service a2489d
      set_cert_error("Not yet activated",get_fp(crt));
Packit Service a2489d
Packit Service a2489d
   if(gnutls_x509_crt_get_expiration_time(crt) < now)
Packit Service a2489d
      set_cert_error("Expired",get_fp(crt));
Packit Service a2489d
Packit Service a2489d
   /* Check if the certificate is revoked.
Packit Service a2489d
    */
Packit Service a2489d
   ret = gnutls_x509_crt_check_revocation(crt, instance->crl_list, instance->crl_list_size);
Packit Service a2489d
   if (ret == 1) {		/* revoked */
Packit Service a2489d
      set_cert_error("Revoked",get_fp(crt));
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool lftp_ssl_gnutls::check_fatal(int res)
Packit Service a2489d
{
Packit Service a2489d
   if(!gnutls_error_is_fatal(res))
Packit Service a2489d
      return false;
Packit Service a2489d
   if((res==GNUTLS_E_UNEXPECTED_PACKET_LENGTH
Packit Service a2489d
       || res==GNUTLS_E_PUSH_ERROR || res==GNUTLS_E_PULL_ERROR
Packit Service a2489d
       || res==GNUTLS_E_DECRYPTION_FAILED)
Packit Service a2489d
   && (!errno || temporary_network_error(errno)))
Packit Service a2489d
      return false;
Packit Service a2489d
   return true;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
int lftp_ssl_gnutls::do_handshake()
Packit Service a2489d
{
Packit Service a2489d
   if(handshake_done)
Packit Service a2489d
      return DONE;
Packit Service a2489d
   errno=0;
Packit Service a2489d
   int res=gnutls_handshake(session);
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      if(res==GNUTLS_E_AGAIN || res==GNUTLS_E_INTERRUPTED)
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else // error
Packit Service a2489d
      {
Packit Service a2489d
	 fatal=check_fatal(res);
Packit Service a2489d
	 set_error("gnutls_handshake",gnutls_strerror(res));
Packit Service a2489d
	 return ERROR;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   handshake_done=true;
Packit Service a2489d
   SMTask::current->Timeout(0);
Packit Service a2489d
Packit Service a2489d
   if(gnutls_certificate_type_get(session)!=GNUTLS_CRT_X509)
Packit Service a2489d
   {
Packit Service a2489d
      set_cert_error("Unsupported certificate type",xstring::null);
Packit Service a2489d
      return DONE; // FIXME: handle openpgp as well
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   unsigned cert_list_size=0;
Packit Service a2489d
   const gnutls_datum_t *cert_list=gnutls_certificate_get_peers(session,&cert_list_size);
Packit Service a2489d
   if(cert_list==NULL || cert_list_size==0)
Packit Service a2489d
      set_cert_error("No certificate was found!",xstring::null);
Packit Service a2489d
   else
Packit Service a2489d
      verify_certificate_chain(cert_list,cert_list_size);
Packit Service a2489d
Packit Service a2489d
   return DONE;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#ifndef GNUTLS_E_PREMATURE_TERMINATION // for gnutls < 3.0
Packit Service a2489d
# define GNUTLS_E_PREMATURE_TERMINATION GNUTLS_E_UNEXPECTED_PACKET_LENGTH
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
int lftp_ssl_gnutls::read(char *buf,int size)
Packit Service a2489d
{
Packit Service a2489d
   if(error)
Packit Service a2489d
      return ERROR;
Packit Service a2489d
   int res=do_handshake();
Packit Service a2489d
   if(res!=DONE)
Packit Service a2489d
      return res;
Packit Service a2489d
   errno=0;
Packit Service a2489d
   res=gnutls_record_recv(session,buf,size);
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      if(res==GNUTLS_E_AGAIN || res==GNUTLS_E_INTERRUPTED)
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else if(res==GNUTLS_E_UNEXPECTED_PACKET_LENGTH || res==GNUTLS_E_PREMATURE_TERMINATION)
Packit Service a2489d
      {
Packit Service a2489d
	 Log::global->Format(7,"gnutls_record_recv: %s Assuming EOF.\n",gnutls_strerror(res));
Packit Service a2489d
	 return 0;
Packit Service a2489d
      }
Packit Service a2489d
      else // error
Packit Service a2489d
      {
Packit Service a2489d
	 fatal=check_fatal(res);
Packit Service a2489d
	 set_error("gnutls_record_recv",gnutls_strerror(res));
Packit Service a2489d
	 return ERROR;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   return res;
Packit Service a2489d
}
Packit Service a2489d
int lftp_ssl_gnutls::write(const char *buf,int size)
Packit Service a2489d
{
Packit Service a2489d
   if(error)
Packit Service a2489d
      return ERROR;
Packit Service a2489d
   int res=do_handshake();
Packit Service a2489d
   if(res!=DONE)
Packit Service a2489d
      return res;
Packit Service a2489d
   if(size==0)
Packit Service a2489d
      return 0;
Packit Service a2489d
   errno=0;
Packit Service a2489d
   res=gnutls_record_send(session,buf,size);
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      if(res==GNUTLS_E_AGAIN || res==GNUTLS_E_INTERRUPTED)
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else // error
Packit Service a2489d
      {
Packit Service a2489d
	 fatal=check_fatal(res);
Packit Service a2489d
	 set_error("gnutls_record_send",gnutls_strerror(res));
Packit Service a2489d
	 return ERROR;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   return res;
Packit Service a2489d
}
Packit Service a2489d
bool lftp_ssl_gnutls::want_in()
Packit Service a2489d
{
Packit Service a2489d
   return gnutls_record_get_direction(session)==0;
Packit Service a2489d
}
Packit Service a2489d
bool lftp_ssl_gnutls::want_out()
Packit Service a2489d
{
Packit Service a2489d
   return gnutls_record_get_direction(session)==1;
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_gnutls::copy_sid(const lftp_ssl_gnutls *o)
Packit Service a2489d
{
Packit Service a2489d
   size_t session_data_size=0;
Packit Service a2489d
   void *session_data;
Packit Service a2489d
   int res=gnutls_session_get_data(o->session,NULL,&session_data_size);
Packit Service a2489d
   if(res!=GNUTLS_E_SUCCESS && res!=GNUTLS_E_SHORT_MEMORY_BUFFER)
Packit Service a2489d
      return;
Packit Service a2489d
   session_data=xmalloc(session_data_size);
Packit Service a2489d
   if(gnutls_session_get_data(o->session,session_data,&session_data_size)!=GNUTLS_E_SUCCESS)
Packit Service a2489d
      return;
Packit Service a2489d
   gnutls_session_set_data(session,session_data,session_data_size);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#include <sha1.h>
Packit Service a2489d
const xstring& lftp_ssl_gnutls::get_fp(gnutls_x509_crt_t cert)
Packit Service a2489d
{
Packit Service a2489d
   static xstring fp;
Packit Service a2489d
   fp.truncate();
Packit Service a2489d
   size_t fp_len=SHA1_DIGEST_SIZE;
Packit Service a2489d
   if(gnutls_x509_crt_get_fingerprint(cert,GNUTLS_DIG_SHA1,fp.add_space(fp_len),&fp_len))
Packit Service a2489d
      return xstring::null;
Packit Service a2489d
   fp.add_commit(fp_len);
Packit Service a2489d
   return fp;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#if LFTP_LIBGNUTLS_VERSION_CODE < 0x010201
Packit Service a2489d
#define PEM_CERT_SEP2 "-----BEGIN X509 CERTIFICATE"
Packit Service a2489d
#define PEM_CERT_SEP "-----BEGIN CERTIFICATE"
Packit Service a2489d
#define CLEAR_CERTS \
Packit Service a2489d
    for(j=0;j
Packit Service a2489d
/**
Packit Service a2489d
  * gnutls_x509_crt_list_import - This function will import a PEM encoded certificate list
Packit Service a2489d
  * @certs: The structures to store the parsed certificate. Must not be initialized.
Packit Service a2489d
  * @cert_max: Initially must hold the maximum number of certs. It will be updated with the number of certs available.
Packit Service a2489d
  * @data: The PEM encoded certificate.
Packit Service a2489d
  * @format: One of DER or PEM. Only PEM is supported for now.
Packit Service a2489d
  * @flags: must be zero or an OR'd sequence of gnutls_certificate_import_flags.
Packit Service a2489d
  *
Packit Service a2489d
  * This function will convert the given PEM encoded certificate list
Packit Service a2489d
  * to the native gnutls_x509_crt_t format. The output will be stored in @certs.
Packit Service a2489d
  * They will be automatically initialized.
Packit Service a2489d
  *
Packit Service a2489d
  * If the Certificate is PEM encoded it should have a header of "X509 CERTIFICATE", or
Packit Service a2489d
  * "CERTIFICATE".
Packit Service a2489d
  *
Packit Service a2489d
  * Returns the number of certificates read or a negative error value.
Packit Service a2489d
  *
Packit Service a2489d
  */
Packit Service a2489d
static
Packit Service a2489d
int gnutls_x509_crt_list_import(gnutls_x509_crt_t *certs, unsigned int* cert_max,
Packit Service a2489d
    const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format, unsigned int flags)
Packit Service a2489d
{
Packit Service a2489d
    int size;
Packit Service a2489d
    const char *ptr;
Packit Service a2489d
    gnutls_datum_t tmp;
Packit Service a2489d
    int ret, nocopy=0;
Packit Service a2489d
    unsigned int count=0,j;
Packit Service a2489d
Packit Service a2489d
    /* move to the certificate
Packit Service a2489d
     */
Packit Service a2489d
    ptr = (const char *)memmem(data->data, data->size,
Packit Service a2489d
		 PEM_CERT_SEP, sizeof(PEM_CERT_SEP) - 1);
Packit Service a2489d
    if (ptr == NULL)
Packit Service a2489d
	ptr = (const char *)memmem(data->data, data->size,
Packit Service a2489d
		     PEM_CERT_SEP2, sizeof(PEM_CERT_SEP2) - 1);
Packit Service a2489d
Packit Service a2489d
    if (ptr == NULL) {
Packit Service a2489d
	return GNUTLS_E_BASE64_DECODING_ERROR;
Packit Service a2489d
    }
Packit Service a2489d
    size = data->size - (ptr - (char*)data->data);
Packit Service a2489d
Packit Service a2489d
    count = 0;
Packit Service a2489d
Packit Service a2489d
    do {
Packit Service a2489d
        if (count >= *cert_max) {
Packit Service a2489d
            if (!(flags & GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED))
Packit Service a2489d
                break;
Packit Service a2489d
            else
Packit Service a2489d
                nocopy = 1;
Packit Service a2489d
        }
Packit Service a2489d
Packit Service a2489d
	if (!nocopy) {
Packit Service a2489d
	    ret = gnutls_x509_crt_init( &certs[count]);
Packit Service a2489d
	    if (ret < 0) {
Packit Service a2489d
                goto error;
Packit Service a2489d
            }
Packit Service a2489d
Packit Service a2489d
	    tmp.data = (unsigned char*)ptr;
Packit Service a2489d
	    tmp.size = size;
Packit Service a2489d
Packit Service a2489d
	    ret = gnutls_x509_crt_import( certs[count], &tmp, GNUTLS_X509_FMT_PEM);
Packit Service a2489d
	    if (ret < 0) {
Packit Service a2489d
                goto error;
Packit Service a2489d
            }
Packit Service a2489d
        }
Packit Service a2489d
Packit Service a2489d
	/* now we move ptr after the pem header
Packit Service a2489d
	 */
Packit Service a2489d
	ptr++;
Packit Service a2489d
	/* find the next certificate (if any)
Packit Service a2489d
	 */
Packit Service a2489d
	size = data->size - (ptr - (char*)data->data);
Packit Service a2489d
Packit Service a2489d
	if (size > 0) {
Packit Service a2489d
	    const char *ptr2;
Packit Service a2489d
Packit Service a2489d
	    ptr2 =
Packit Service a2489d
		(const char *)memmem(ptr, size, PEM_CERT_SEP, sizeof(PEM_CERT_SEP) - 1);
Packit Service a2489d
	    if (ptr2 == NULL)
Packit Service a2489d
		ptr2 = (const char *)memmem(ptr, size, PEM_CERT_SEP2,
Packit Service a2489d
			      sizeof(PEM_CERT_SEP2) - 1);
Packit Service a2489d
Packit Service a2489d
	    ptr = ptr2;
Packit Service a2489d
	} else
Packit Service a2489d
	    ptr = NULL;
Packit Service a2489d
Packit Service a2489d
	count++;
Packit Service a2489d
    } while (ptr != NULL);
Packit Service a2489d
Packit Service a2489d
    *cert_max = count;
Packit Service a2489d
Packit Service a2489d
    if (nocopy==0)
Packit Service a2489d
        return count;
Packit Service a2489d
    else
Packit Service a2489d
        return GNUTLS_E_SHORT_MEMORY_BUFFER;
Packit Service a2489d
Packit Service a2489d
error:
Packit Service a2489d
    CLEAR_CERTS;
Packit Service a2489d
    return ret;
Packit Service a2489d
}
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
/*=============================== OpenSSL ====================================*/
Packit Service a2489d
#elif USE_OPENSSL
Packit Service a2489d
//static int lftp_ssl_passwd_callback(char *buf,int size,int rwflag,void *userdata);
Packit Service a2489d
Packit Service a2489d
#if OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER
Packit Service a2489d
// for compatibility with older versions
Packit Service a2489d
X509_OBJECT *X509_OBJECT_new()
Packit Service a2489d
{
Packit Service a2489d
    X509_OBJECT *ret;
Packit Service a2489d
    return (X509_OBJECT*)OPENSSL_malloc(sizeof(*ret));
Packit Service a2489d
}
Packit Service a2489d
void X509_OBJECT_free(X509_OBJECT *a)
Packit Service a2489d
{
Packit Service a2489d
    if (a == NULL)
Packit Service a2489d
        return;
Packit Service a2489d
    X509_OBJECT_free_contents(a);
Packit Service a2489d
    OPENSSL_free(a);
Packit Service a2489d
}
Packit Service a2489d
X509_CRL *X509_OBJECT_get0_X509_CRL(X509_OBJECT *a)
Packit Service a2489d
{
Packit Service a2489d
    if (a == NULL) return NULL;
Packit Service a2489d
    if (a->type != X509_LU_CRL) return NULL;
Packit Service a2489d
    return a->data.crl;
Packit Service a2489d
}
Packit Service a2489d
# define X509_CRL_get0_nextUpdate(x) X509_CRL_get_nextUpdate(x)
Packit Service a2489d
# define ASN1_STRING_get0_data(x) ASN1_STRING_data(x)
Packit Service a2489d
# define X509_REVOKED_get0_serialNumber(x) (x->serialNumber)
Packit Service a2489d
#endif // OPENSSL_VERSION_NUMBER < 0x10100000L
Packit Service a2489d
Packit Service a2489d
Ref<lftp_ssl_openssl_instance> lftp_ssl_openssl::instance;
Packit Service a2489d
Packit Service a2489d
static char file[256];
Packit Service a2489d
static void lftp_ssl_write_rnd()
Packit Service a2489d
{
Packit Service a2489d
   RAND_write_file(file);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void lftp_ssl_openssl::global_init()
Packit Service a2489d
{
Packit Service a2489d
   if(!instance)
Packit Service a2489d
      instance=new lftp_ssl_openssl_instance();
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_openssl::global_deinit()
Packit Service a2489d
{
Packit Service a2489d
   instance=0;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#ifndef SSL_OP_NO_TICKET
Packit Service a2489d
# define SSL_OP_NO_TICKET 0
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
lftp_ssl_openssl_instance::lftp_ssl_openssl_instance()
Packit Service a2489d
{
Packit Service a2489d
   crl_store=0;
Packit Service a2489d
   ssl_ctx=0;
Packit Service a2489d
Packit Service a2489d
#ifdef WINDOWS
Packit Service a2489d
   RAND_screen();
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
   RAND_file_name(file,sizeof(file));
Packit Service a2489d
Packit Service a2489d
   if(RAND_load_file(file,-1) && RAND_status()!=0)
Packit Service a2489d
      atexit(lftp_ssl_write_rnd);
Packit Service a2489d
Packit Service a2489d
#if SSLEAY_VERSION_NUMBER < 0x0800
Packit Service a2489d
   ssl_ctx=SSL_CTX_new();
Packit Service a2489d
   X509_set_default_verify_paths(ssl_ctx->cert);
Packit Service a2489d
#else
Packit Service a2489d
   SSLeay_add_ssl_algorithms();
Packit Service a2489d
   ssl_ctx=SSL_CTX_new(SSLv23_client_method());
Packit Service a2489d
   long options=SSL_OP_ALL|SSL_OP_NO_TICKET|SSL_OP_NO_SSLv2;
Packit Service a2489d
   const char *priority=ResMgr::Query("ssl:priority", 0);
Packit Service a2489d
   if(priority && *priority)
Packit Service a2489d
   {
Packit Service a2489d
      static const struct ssl_option {
Packit Service a2489d
	 const char name[8];
Packit Service a2489d
	 long option;
Packit Service a2489d
      } opt_table[]={
Packit Service a2489d
	 {"-SSL3.0",SSL_OP_NO_SSLv3},
Packit Service a2489d
	 {"-TLS1.0",SSL_OP_NO_TLSv1},
Packit Service a2489d
	 {"-TLS1.1",SSL_OP_NO_TLSv1_1},
Packit Service a2489d
	 {"-TLS1.2",SSL_OP_NO_TLSv1_2},
Packit Service a2489d
	 {"",0}
Packit Service a2489d
      };
Packit Service a2489d
      char *to_parse=alloca_strdup(priority);
Packit Service a2489d
      for(char *ptr=strtok(to_parse,":"); ptr; ptr=strtok(NULL,":")) {
Packit Service a2489d
	 if(*ptr && !strncmp(ptr+1,"VERS-",5)) {
Packit Service a2489d
	    ptr[5]=ptr[0];
Packit Service a2489d
	    ptr+=5;
Packit Service a2489d
	 }
Packit Service a2489d
	 for(const ssl_option *opt=opt_table; opt->name[0]; opt++) {
Packit Service a2489d
	    if(!strcmp(ptr,opt->name)) {
Packit Service a2489d
	       options|=opt->option;
Packit Service a2489d
	       Log::global->Format(9,"ssl: applied %s option\n",ptr);
Packit Service a2489d
	       break;
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   SSL_CTX_set_options(ssl_ctx, options);
Packit Service a2489d
   SSL_CTX_set_cipher_list(ssl_ctx, "ALL:!aNULL:!eNULL:!SSLv2:!LOW:!EXP:!MD5:@STRENGTH");
Packit Service a2489d
   SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,lftp_ssl_openssl::verify_callback);
Packit Service a2489d
//    SSL_CTX_set_default_passwd_cb(ssl_ctx,lftp_ssl_passwd_callback);
Packit Service a2489d
Packit Service a2489d
   const char *ca_file=ResMgr::Query("ssl:ca-file",0);
Packit Service a2489d
   const char *ca_path=ResMgr::Query("ssl:ca-path",0);
Packit Service a2489d
   if(ca_file && !*ca_file)
Packit Service a2489d
      ca_file=0;
Packit Service a2489d
   if(ca_path && !*ca_path)
Packit Service a2489d
      ca_path=0;
Packit Service a2489d
   if(ca_file || ca_path)
Packit Service a2489d
   {
Packit Service a2489d
      if(!SSL_CTX_load_verify_locations(ssl_ctx,ca_file,ca_path))
Packit Service a2489d
      {
Packit Service a2489d
	 fprintf(stderr,"WARNING: SSL_CTX_load_verify_locations(%s,%s) failed\n",
Packit Service a2489d
	    ca_file?ca_file:"NULL",
Packit Service a2489d
	    ca_path?ca_path:"NULL");
Packit Service a2489d
	 SSL_CTX_set_default_verify_paths(ssl_ctx);
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
   {
Packit Service a2489d
      SSL_CTX_set_default_verify_paths(ssl_ctx);
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   const char *crl_file=ResMgr::Query("ssl:crl-file",0);
Packit Service a2489d
   const char *crl_path=ResMgr::Query("ssl:crl-path",0);
Packit Service a2489d
   if(crl_file && !*crl_file)
Packit Service a2489d
      crl_file=0;
Packit Service a2489d
   if(crl_path && !*crl_path)
Packit Service a2489d
      crl_path=0;
Packit Service a2489d
   if(crl_file || crl_path)
Packit Service a2489d
   {
Packit Service a2489d
      crl_store=X509_STORE_new();
Packit Service a2489d
      if(!X509_STORE_load_locations(crl_store,crl_file,crl_path))
Packit Service a2489d
      {
Packit Service a2489d
	 fprintf(stderr,"WARNING: X509_STORE_load_locations(%s,%s) failed\n",
Packit Service a2489d
	    crl_file?crl_file:"NULL",
Packit Service a2489d
	    crl_path?crl_path:"NULL");
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
#endif /* SSLEAY_VERSION_NUMBER < 0x0800 */
Packit Service a2489d
}
Packit Service a2489d
lftp_ssl_openssl_instance::~lftp_ssl_openssl_instance()
Packit Service a2489d
{
Packit Service a2489d
   SSL_CTX_free(ssl_ctx);
Packit Service a2489d
   X509_STORE_free(crl_store);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
lftp_ssl_openssl::lftp_ssl_openssl(int fd1,handshake_mode_t m,const char *h)
Packit Service a2489d
   : lftp_ssl_base(fd1,m,h)
Packit Service a2489d
{
Packit Service a2489d
   if(!instance)
Packit Service a2489d
      global_init();
Packit Service a2489d
Packit Service a2489d
   ssl=SSL_new(instance->ssl_ctx);
Packit Service a2489d
   SSL_set_fd(ssl,fd);
Packit Service a2489d
   SSL_ctrl(ssl,SSL_CTRL_MODE,SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER,0);
Packit Service a2489d
Packit Service a2489d
   if(h && ResMgr::QueryBool("ssl:use-sni",h)) {
Packit Service a2489d
      if(!SSL_set_tlsext_host_name(ssl, h))
Packit Service a2489d
	 fprintf(stderr,"WARNING: failed to configure server name indication (SNI) TLS extension\n");
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_openssl::load_keys()
Packit Service a2489d
{
Packit Service a2489d
   const char *key_file =ResMgr::Query("ssl:key-file",hostname);
Packit Service a2489d
   const char *cert_file=ResMgr::Query("ssl:cert-file",hostname);
Packit Service a2489d
   if(key_file && !*key_file)
Packit Service a2489d
      key_file=0;
Packit Service a2489d
   if(cert_file && !*cert_file)
Packit Service a2489d
      cert_file=0;
Packit Service a2489d
Packit Service a2489d
   if(cert_file)
Packit Service a2489d
   {
Packit Service a2489d
      if(!key_file)
Packit Service a2489d
	 key_file=cert_file;
Packit Service a2489d
      if(SSL_use_certificate_file(ssl,cert_file,SSL_FILETYPE_PEM)<=0)
Packit Service a2489d
      {
Packit Service a2489d
	 // FIXME
Packit Service a2489d
      }
Packit Service a2489d
      if(SSL_use_PrivateKey_file(ssl,key_file,SSL_FILETYPE_PEM)<=0)
Packit Service a2489d
      {
Packit Service a2489d
	 // FIXME
Packit Service a2489d
      }
Packit Service a2489d
      if(!SSL_check_private_key(ssl))
Packit Service a2489d
      {
Packit Service a2489d
	 // FIXME
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_openssl::shutdown()
Packit Service a2489d
{
Packit Service a2489d
   if(handshake_done)
Packit Service a2489d
      SSL_shutdown(ssl);
Packit Service a2489d
}
Packit Service a2489d
lftp_ssl_openssl::~lftp_ssl_openssl()
Packit Service a2489d
{
Packit Service a2489d
   SSL_free(ssl);
Packit Service a2489d
   ssl=0;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
static lftp_ssl_openssl *verify_callback_ssl;
Packit Service a2489d
Packit Service a2489d
bool lftp_ssl_openssl::check_fatal(int res)
Packit Service a2489d
{
Packit Service a2489d
   return !(SSL_get_error(ssl,res)==SSL_ERROR_SYSCALL
Packit Service a2489d
	    && (ERR_get_error()==0 || temporary_network_error(errno)));
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
int lftp_ssl_openssl::do_handshake()
Packit Service a2489d
{
Packit Service a2489d
   if(handshake_done)
Packit Service a2489d
      return DONE;
Packit Service a2489d
   if(handshake_mode==SERVER)
Packit Service a2489d
   {
Packit Service a2489d
      // FIXME: SSL_accept
Packit Service a2489d
      return RETRY;
Packit Service a2489d
   }
Packit Service a2489d
   errno=0;
Packit Service a2489d
   verify_callback_ssl=this;
Packit Service a2489d
   int res=SSL_connect(ssl);
Packit Service a2489d
   verify_callback_ssl=0;
Packit Service a2489d
   if(res<=0)
Packit Service a2489d
   {
Packit Service a2489d
      if(BIO_sock_should_retry(res))
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else if (SSL_want_x509_lookup(ssl))
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else // error
Packit Service a2489d
      {
Packit Service a2489d
	 fatal=check_fatal(res);
Packit Service a2489d
	 set_error("SSL_connect",strerror());
Packit Service a2489d
	 return ERROR;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   handshake_done=true;
Packit Service a2489d
   check_certificate();
Packit Service a2489d
   SMTask::current->Timeout(0);
Packit Service a2489d
   return DONE;
Packit Service a2489d
}
Packit Service a2489d
int lftp_ssl_openssl::read(char *buf,int size)
Packit Service a2489d
{
Packit Service a2489d
   if(error)
Packit Service a2489d
      return ERROR;
Packit Service a2489d
   int res=do_handshake();
Packit Service a2489d
   if(res!=DONE)
Packit Service a2489d
      return res;
Packit Service a2489d
   errno=0;
Packit Service a2489d
   res=SSL_read(ssl,buf,size);
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      if(BIO_sock_should_retry(res))
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else if (SSL_want_x509_lookup(ssl))
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else // error
Packit Service a2489d
      {
Packit Service a2489d
	 fatal=check_fatal(res);
Packit Service a2489d
	 set_error("SSL_read",strerror());
Packit Service a2489d
	 return ERROR;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   return res;
Packit Service a2489d
}
Packit Service a2489d
int lftp_ssl_openssl::write(const char *buf,int size)
Packit Service a2489d
{
Packit Service a2489d
   if(error)
Packit Service a2489d
      return ERROR;
Packit Service a2489d
   int res=do_handshake();
Packit Service a2489d
   if(res!=DONE)
Packit Service a2489d
      return res;
Packit Service a2489d
   if(size==0)
Packit Service a2489d
      return 0;
Packit Service a2489d
   errno=0;
Packit Service a2489d
   res=SSL_write(ssl,buf,size);
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      if(BIO_sock_should_retry(res))
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else if (SSL_want_x509_lookup(ssl))
Packit Service a2489d
	 return RETRY;
Packit Service a2489d
      else // error
Packit Service a2489d
      {
Packit Service a2489d
	 fatal=check_fatal(res);
Packit Service a2489d
	 set_error("SSL_write",strerror());
Packit Service a2489d
	 return ERROR;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   return res;
Packit Service a2489d
}
Packit Service a2489d
bool lftp_ssl_openssl::want_in()
Packit Service a2489d
{
Packit Service a2489d
   return SSL_want_read(ssl);
Packit Service a2489d
}
Packit Service a2489d
bool lftp_ssl_openssl::want_out()
Packit Service a2489d
{
Packit Service a2489d
   return SSL_want_write(ssl);
Packit Service a2489d
}
Packit Service a2489d
void lftp_ssl_openssl::copy_sid(const lftp_ssl_openssl *o)
Packit Service a2489d
{
Packit Service a2489d
   SSL_copy_session_id(ssl,o->ssl);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const char *lftp_ssl_openssl::strerror()
Packit Service a2489d
{
Packit Service a2489d
   SSL_load_error_strings();
Packit Service a2489d
   int error=ERR_get_error();
Packit Service a2489d
   const char *ssl_error=0;
Packit Service a2489d
   if(ERR_GET_LIB(error)==ERR_LIB_SSL)
Packit Service a2489d
      ssl_error=ERR_reason_error_string(error);
Packit Service a2489d
   else
Packit Service a2489d
      ssl_error=ERR_error_string(error,NULL);
Packit Service a2489d
   if(!ssl_error)
Packit Service a2489d
      ssl_error="error";
Packit Service a2489d
   return ssl_error;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* This one is (very much!) based on work by Ralf S. Engelschall <rse@engelschall.com>.
Packit Service a2489d
 * Comments by Ralf. */
Packit Service a2489d
int lftp_ssl_openssl::verify_crl(X509_STORE_CTX *ctx)
Packit Service a2489d
{
Packit Service a2489d
    X509_OBJECT *obj=0;
Packit Service a2489d
    X509_NAME *subject=0;
Packit Service a2489d
    X509_NAME *issuer=0;
Packit Service a2489d
    X509 *xs=0;
Packit Service a2489d
    X509_CRL *crl=0;
Packit Service a2489d
    X509_REVOKED *revoked=0;
Packit Service a2489d
    X509_STORE_CTX *store_ctx=0;
Packit Service a2489d
    long serial;
Packit Service a2489d
    int i, n, rc;
Packit Service a2489d
    char *cp=0;
Packit Service a2489d
Packit Service a2489d
    /*
Packit Service a2489d
     * Unless a revocation store for CRLs was created we
Packit Service a2489d
     * cannot do any CRL-based verification, of course.
Packit Service a2489d
     */
Packit Service a2489d
    if (!instance->crl_store)
Packit Service a2489d
        return 1;
Packit Service a2489d
Packit Service a2489d
    /*
Packit Service a2489d
     * Determine certificate ingredients in advance
Packit Service a2489d
     */
Packit Service a2489d
    xs      = X509_STORE_CTX_get_current_cert(ctx);
Packit Service a2489d
    subject = X509_get_subject_name(xs);
Packit Service a2489d
    issuer  = X509_get_issuer_name(xs);
Packit Service a2489d
Packit Service a2489d
    /*
Packit Service a2489d
     * OpenSSL provides the general mechanism to deal with CRLs but does not
Packit Service a2489d
     * use them automatically when verifying certificates, so we do it
Packit Service a2489d
     * explicitly here. We will check the CRL for the currently checked
Packit Service a2489d
     * certificate, if there is such a CRL in the store.
Packit Service a2489d
     *
Packit Service a2489d
     * We come through this procedure for each certificate in the certificate
Packit Service a2489d
     * chain, starting with the root-CA's certificate. At each step we've to
Packit Service a2489d
     * both verify the signature on the CRL (to make sure it's a valid CRL)
Packit Service a2489d
     * and it's revocation list (to make sure the current certificate isn't
Packit Service a2489d
     * revoked).  But because to check the signature on the CRL we need the
Packit Service a2489d
     * public key of the issuing CA certificate (which was already processed
Packit Service a2489d
     * one round before), we've a little problem. But we can both solve it and
Packit Service a2489d
     * at the same time optimize the processing by using the following
Packit Service a2489d
     * verification scheme (idea and code snippets borrowed from the GLOBUS
Packit Service a2489d
     * project):
Packit Service a2489d
     *
Packit Service a2489d
     * 1. We'll check the signature of a CRL in each step when we find a CRL
Packit Service a2489d
     *    through the _subject_ name of the current certificate. This CRL
Packit Service a2489d
     *    itself will be needed the first time in the next round, of course.
Packit Service a2489d
     *    But we do the signature processing one round before this where the
Packit Service a2489d
     *    public key of the CA is available.
Packit Service a2489d
     *
Packit Service a2489d
     * 2. We'll check the revocation list of a CRL in each step when
Packit Service a2489d
     *    we find a CRL through the _issuer_ name of the current certificate.
Packit Service a2489d
     *    This CRLs signature was then already verified one round before.
Packit Service a2489d
     *
Packit Service a2489d
     * This verification scheme allows a CA to revoke its own certificate as
Packit Service a2489d
     * well, of course.
Packit Service a2489d
     */
Packit Service a2489d
Packit Service a2489d
    /*
Packit Service a2489d
     * Try to retrieve a CRL corresponding to the _subject_ of
Packit Service a2489d
     * the current certificate in order to verify it's integrity.
Packit Service a2489d
     */
Packit Service a2489d
    obj = X509_OBJECT_new();
Packit Service a2489d
    store_ctx = X509_STORE_CTX_new();
Packit Service a2489d
    X509_STORE_CTX_init(store_ctx, instance->crl_store, NULL, NULL);
Packit Service a2489d
    rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
Packit Service a2489d
    X509_STORE_CTX_free(store_ctx); store_ctx=0;
Packit Service a2489d
    crl = X509_OBJECT_get0_X509_CRL(obj);
Packit Service a2489d
    if (rc > 0 && crl != NULL) {
Packit Service a2489d
        /*
Packit Service a2489d
         * Verify the signature on this CRL
Packit Service a2489d
         */
Packit Service a2489d
        if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) {
Packit Service a2489d
            Log::global->Format(0,"Invalid signature on CRL!\n");
Packit Service a2489d
            X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
Packit Service a2489d
            X509_OBJECT_free(obj); obj=0;
Packit Service a2489d
            return 0;
Packit Service a2489d
        }
Packit Service a2489d
Packit Service a2489d
        /*
Packit Service a2489d
         * Check date of CRL to make sure it's not expired
Packit Service a2489d
         */
Packit Service a2489d
        i = X509_cmp_current_time(X509_CRL_get0_nextUpdate(crl));
Packit Service a2489d
        if (i == 0) {
Packit Service a2489d
            Log::global->Format(0,"Found CRL has invalid nextUpdate field.\n");
Packit Service a2489d
            X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
Packit Service a2489d
            X509_OBJECT_free(obj); obj=0;
Packit Service a2489d
            return 0;
Packit Service a2489d
        }
Packit Service a2489d
        if (i < 0) {
Packit Service a2489d
            Log::global->Format(0,"Found CRL is expired - revoking all certificates until you get updated CRL.\n");
Packit Service a2489d
            X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
Packit Service a2489d
            X509_OBJECT_free(obj); obj=0;
Packit Service a2489d
            return 0;
Packit Service a2489d
        }
Packit Service a2489d
        X509_OBJECT_free(obj); obj=0;
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
    /*
Packit Service a2489d
     * Try to retrieve a CRL corresponding to the _issuer_ of
Packit Service a2489d
     * the current certificate in order to check for revocation.
Packit Service a2489d
     */
Packit Service a2489d
    obj = X509_OBJECT_new();
Packit Service a2489d
    store_ctx = X509_STORE_CTX_new();
Packit Service a2489d
    X509_STORE_CTX_init(store_ctx, instance->crl_store, NULL, NULL);
Packit Service a2489d
    rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
Packit Service a2489d
    X509_STORE_CTX_free(store_ctx); store_ctx=0;
Packit Service a2489d
    crl = X509_OBJECT_get0_X509_CRL(obj);
Packit Service a2489d
    if (rc > 0 && crl != NULL) {
Packit Service a2489d
        /*
Packit Service a2489d
         * Check if the current certificate is revoked by this CRL
Packit Service a2489d
         */
Packit Service a2489d
        n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
Packit Service a2489d
        for (i = 0; i < n; i++) {
Packit Service a2489d
            revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
Packit Service a2489d
	    const ASN1_INTEGER *revoked_serial = X509_REVOKED_get0_serialNumber(revoked);
Packit Service a2489d
            if (ASN1_INTEGER_cmp(revoked_serial, X509_get_serialNumber(xs)) == 0) {
Packit Service a2489d
                serial = ASN1_INTEGER_get(revoked_serial);
Packit Service a2489d
                cp = X509_NAME_oneline(issuer, NULL, 0);
Packit Service a2489d
                Log::global->Format(0,
Packit Service a2489d
		    "Certificate with serial %ld (0x%lX) revoked per CRL from issuer %s\n",
Packit Service a2489d
                        serial, serial, cp ? cp : "(ERROR)");
Packit Service a2489d
                free(cp); cp=0;
Packit Service a2489d
Packit Service a2489d
                X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
Packit Service a2489d
                X509_OBJECT_free(obj); obj=0;
Packit Service a2489d
                return 0;
Packit Service a2489d
            }
Packit Service a2489d
        }
Packit Service a2489d
        X509_OBJECT_free(obj); obj=0;
Packit Service a2489d
    }
Packit Service a2489d
    return 1;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
static bool convert_from_utf8(char *str,int len)
Packit Service a2489d
{
Packit Service a2489d
   DirectedBuffer translate(DirectedBuffer::GET);
Packit Service a2489d
   translate.SetTranslation("UTF-8",false);
Packit Service a2489d
   translate.PutTranslated(str,len);
Packit Service a2489d
   const char *str1,*str2;
Packit Service a2489d
   int len1,len2;
Packit Service a2489d
   translate.Get(&str1,&len1);
Packit Service a2489d
   if(len1>len)
Packit Service a2489d
      return false;  // no room to store expanded string
Packit Service a2489d
Packit Service a2489d
   // be safe and try to convert back to UTF-8
Packit Service a2489d
   DirectedBuffer translate_back(DirectedBuffer::PUT);
Packit Service a2489d
   translate_back.SetTranslation("UTF-8",false);
Packit Service a2489d
   translate_back.PutTranslated(str1,len1);
Packit Service a2489d
   translate_back.Get(&str2,&len2);
Packit Service a2489d
   if(len2!=len || memcmp(str2,str,len))
Packit Service a2489d
      return false;  // conversion error
Packit Service a2489d
Packit Service a2489d
   memcpy(str,str1,len1);
Packit Service a2489d
   str[len1]=0;
Packit Service a2489d
   return true;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* begin curl code */
Packit Service a2489d
/* Copyright (c) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se> */
Packit Service a2489d
#define Curl_raw_toupper c_toupper
Packit Service a2489d
#define Curl_raw_equal !strcmp
Packit Service a2489d
#define Curl_raw_nequal !strncmp
Packit Service a2489d
#define Curl_inet_pton inet_pton
Packit Service a2489d
#if INET6
Packit Service a2489d
# define ENABLE_IPV6 1
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
/*
Packit Service a2489d
 * Match a hostname against a wildcard pattern.
Packit Service a2489d
 * E.g.
Packit Service a2489d
 *  "foo.host.com" matches "*.host.com".
Packit Service a2489d
 *
Packit Service a2489d
 * We use the matching rule described in RFC6125, section 6.4.3.
Packit Service a2489d
 * http://tools.ietf.org/html/rfc6125#section-6.4.3
Packit Service a2489d
 *
Packit Service a2489d
 * In addition: ignore trailing dots in the host names and wildcards, so that
Packit Service a2489d
 * the names are used normalized. This is what the browsers do.
Packit Service a2489d
 *
Packit Service a2489d
 * Do not allow wildcard matching on IP numbers. There are apparently
Packit Service a2489d
 * certificates being used with an IP address in the CN field, thus making no
Packit Service a2489d
 * apparent distinction between a name and an IP. We need to detect the use of
Packit Service a2489d
 * an IP address and not wildcard match on such names.
Packit Service a2489d
 *
Packit Service a2489d
 * NOTE: hostmatch() gets called with copied buffers so that it can modify the
Packit Service a2489d
 * contents at will.
Packit Service a2489d
 */
Packit Service a2489d
#define CURL_HOST_NOMATCH 0
Packit Service a2489d
#define CURL_HOST_MATCH   1
Packit Service a2489d
Packit Service a2489d
static int hostmatch(char *hostname, char *pattern)
Packit Service a2489d
{
Packit Service a2489d
  const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
Packit Service a2489d
  int wildcard_enabled;
Packit Service a2489d
  size_t prefixlen, suffixlen;
Packit Service a2489d
  struct in_addr ignored;
Packit Service a2489d
#ifdef ENABLE_IPV6
Packit Service a2489d
  struct sockaddr_in6 si6;
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
  /* normalize pattern and hostname by stripping off trailing dots */
Packit Service a2489d
  size_t len = strlen(hostname);
Packit Service a2489d
  if(hostname[len-1]=='.')
Packit Service a2489d
    hostname[len-1]=0;
Packit Service a2489d
  len = strlen(pattern);
Packit Service a2489d
  if(pattern[len-1]=='.')
Packit Service a2489d
    pattern[len-1]=0;
Packit Service a2489d
Packit Service a2489d
  pattern_wildcard = strchr(pattern, '*');
Packit Service a2489d
  if(pattern_wildcard == NULL)
Packit Service a2489d
    return Curl_raw_equal(pattern, hostname) ?
Packit Service a2489d
      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
Packit Service a2489d
Packit Service a2489d
  /* detect IP address as hostname and fail the match if so */
Packit Service a2489d
  if(Curl_inet_pton(AF_INET, hostname, &ignored) > 0)
Packit Service a2489d
    return CURL_HOST_NOMATCH;
Packit Service a2489d
#ifdef ENABLE_IPV6
Packit Service a2489d
  else if(Curl_inet_pton(AF_INET6, hostname, &si6.sin6_addr) > 0)
Packit Service a2489d
    return CURL_HOST_NOMATCH;
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
  /* We require at least 2 dots in pattern to avoid too wide wildcard
Packit Service a2489d
     match. */
Packit Service a2489d
  wildcard_enabled = 1;
Packit Service a2489d
  pattern_label_end = strchr(pattern, '.');
Packit Service a2489d
  if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL ||
Packit Service a2489d
     pattern_wildcard > pattern_label_end ||
Packit Service a2489d
     Curl_raw_nequal(pattern, "xn--", 4)) {
Packit Service a2489d
    wildcard_enabled = 0;
Packit Service a2489d
  }
Packit Service a2489d
  if(!wildcard_enabled)
Packit Service a2489d
    return Curl_raw_equal(pattern, hostname) ?
Packit Service a2489d
      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
Packit Service a2489d
Packit Service a2489d
  hostname_label_end = strchr(hostname, '.');
Packit Service a2489d
  if(hostname_label_end == NULL ||
Packit Service a2489d
     !Curl_raw_equal(pattern_label_end, hostname_label_end))
Packit Service a2489d
    return CURL_HOST_NOMATCH;
Packit Service a2489d
Packit Service a2489d
  /* The wildcard must match at least one character, so the left-most
Packit Service a2489d
     label of the hostname is at least as large as the left-most label
Packit Service a2489d
     of the pattern. */
Packit Service a2489d
  if(hostname_label_end - hostname < pattern_label_end - pattern)
Packit Service a2489d
    return CURL_HOST_NOMATCH;
Packit Service a2489d
Packit Service a2489d
  prefixlen = pattern_wildcard - pattern;
Packit Service a2489d
  suffixlen = pattern_label_end - (pattern_wildcard+1);
Packit Service a2489d
  return Curl_raw_nequal(pattern, hostname, prefixlen) &&
Packit Service a2489d
    Curl_raw_nequal(pattern_wildcard+1, hostname_label_end - suffixlen,
Packit Service a2489d
                    suffixlen) ?
Packit Service a2489d
    CURL_HOST_MATCH : CURL_HOST_NOMATCH;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
static int cert_hostcheck(const char *match_pattern, const char *hostname)
Packit Service a2489d
{
Packit Service a2489d
  char *matchp;
Packit Service a2489d
  char *hostp;
Packit Service a2489d
  int res = 0;
Packit Service a2489d
  if(!match_pattern || !*match_pattern ||
Packit Service a2489d
      !hostname || !*hostname) /* sanity check */
Packit Service a2489d
    ;
Packit Service a2489d
  else {
Packit Service a2489d
    matchp = strdup(match_pattern);
Packit Service a2489d
    if(matchp) {
Packit Service a2489d
      hostp = strdup(hostname);
Packit Service a2489d
      if(hostp) {
Packit Service a2489d
        if(hostmatch(hostp, matchp) == CURL_HOST_MATCH)
Packit Service a2489d
          res= 1;
Packit Service a2489d
        free(hostp);
Packit Service a2489d
      }
Packit Service a2489d
      free(matchp);
Packit Service a2489d
    }
Packit Service a2489d
  }
Packit Service a2489d
Packit Service a2489d
  return res;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* Quote from RFC2818 section 3.1 "Server Identity"
Packit Service a2489d
Packit Service a2489d
   If a subjectAltName extension of type dNSName is present, that MUST
Packit Service a2489d
   be used as the identity. Otherwise, the (most specific) Common Name
Packit Service a2489d
   field in the Subject field of the certificate MUST be used. Although
Packit Service a2489d
   the use of the Common Name is existing practice, it is deprecated and
Packit Service a2489d
   Certification Authorities are encouraged to use the dNSName instead.
Packit Service a2489d
Packit Service a2489d
   Matching is performed using the matching rules specified by
Packit Service a2489d
   [RFC2459].  If more than one identity of a given type is present in
Packit Service a2489d
   the certificate (e.g., more than one dNSName name, a match in any one
Packit Service a2489d
   of the set is considered acceptable.) Names may contain the wildcard
Packit Service a2489d
   character * which is considered to match any single domain name
Packit Service a2489d
   component or component fragment. E.g., *.a.com matches foo.a.com but
Packit Service a2489d
   not bar.foo.a.com. f*.com matches foo.com but not bar.com.
Packit Service a2489d
Packit Service a2489d
   In some cases, the URI is specified as an IP address rather than a
Packit Service a2489d
   hostname. In this case, the iPAddress subjectAltName must be present
Packit Service a2489d
   in the certificate and must exactly match the IP in the URI.
Packit Service a2489d
Packit Service a2489d
*/
Packit Service a2489d
void lftp_ssl_openssl::check_certificate()
Packit Service a2489d
{
Packit Service a2489d
  X509 *server_cert = SSL_get_peer_certificate (ssl);
Packit Service a2489d
  if (!server_cert)
Packit Service a2489d
    {
Packit Service a2489d
      set_cert_error(xstring::format(_("No certificate presented by %s.\n"),
Packit Service a2489d
                 quotearg_style (escape_quoting_style, hostname)),xstring::null);
Packit Service a2489d
      return;
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
  bool check_hostname = ResMgr::QueryBool("ssl:check-hostname", hostname);
Packit Service a2489d
  if(!check_hostname) {
Packit Service a2489d
    Log::global->Format(0, "WARNING: Certificate verification: hostname checking disabled\n");
Packit Service a2489d
    return;
Packit Service a2489d
  }
Packit Service a2489d
Packit Service a2489d
  int matched = -1; /* -1 is no alternative match yet, 1 means match and 0
Packit Service a2489d
                       means mismatch */
Packit Service a2489d
  int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
Packit Service a2489d
  size_t addrlen = 0;
Packit Service a2489d
  STACK_OF(GENERAL_NAME) *altnames;
Packit Service a2489d
#ifdef ENABLE_IPV6
Packit Service a2489d
  struct in6_addr addr;
Packit Service a2489d
#else
Packit Service a2489d
  struct in_addr addr;
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
  sockaddr_u fd_addr;
Packit Service a2489d
  socklen_t fd_addr_len = sizeof(fd_addr);
Packit Service a2489d
  getsockname(fd,&fd_addr.sa,&fd_addr_len);
Packit Service a2489d
Packit Service a2489d
#ifdef ENABLE_IPV6
Packit Service a2489d
  if(fd_addr.sa.sa_family==AF_INET6 &&
Packit Service a2489d
     Curl_inet_pton(AF_INET6, hostname, &addr)) {
Packit Service a2489d
    target = GEN_IPADD;
Packit Service a2489d
    addrlen = sizeof(struct in6_addr);
Packit Service a2489d
  }
Packit Service a2489d
  else
Packit Service a2489d
#endif
Packit Service a2489d
    if(Curl_inet_pton(AF_INET, hostname, &addr)) {
Packit Service a2489d
      target = GEN_IPADD;
Packit Service a2489d
      addrlen = sizeof(struct in_addr);
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
  /* get a "list" of alternative names */
Packit Service a2489d
  altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
Packit Service a2489d
Packit Service a2489d
  if(altnames) {
Packit Service a2489d
    int numalts;
Packit Service a2489d
    int i;
Packit Service a2489d
Packit Service a2489d
    /* get amount of alternatives, RFC2459 claims there MUST be at least
Packit Service a2489d
       one, but we don't depend on it... */
Packit Service a2489d
    numalts = sk_GENERAL_NAME_num(altnames);
Packit Service a2489d
Packit Service a2489d
    /* loop through all alternatives while none has matched */
Packit Service a2489d
    for (i=0; (i
Packit Service a2489d
      /* get a handle to alternative name number i */
Packit Service a2489d
      const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
Packit Service a2489d
Packit Service a2489d
      /* only check alternatives of the same type the target is */
Packit Service a2489d
      if(check->type == target) {
Packit Service a2489d
        /* get data and length */
Packit Service a2489d
        const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5);
Packit Service a2489d
        size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
Packit Service a2489d
Packit Service a2489d
        switch(target) {
Packit Service a2489d
        case GEN_DNS: /* name/pattern comparison */
Packit Service a2489d
          /* The OpenSSL man page explicitly says: "In general it cannot be
Packit Service a2489d
             assumed that the data returned by ASN1_STRING_get0_data() is null
Packit Service a2489d
             terminated or does not contain embedded nulls." But also that
Packit Service a2489d
             "The actual format of the data will depend on the actual string
Packit Service a2489d
             type itself: for example for and IA5String the data will be ASCII"
Packit Service a2489d
Packit Service a2489d
             Gisle researched the OpenSSL sources:
Packit Service a2489d
             "I checked the 0.9.6 and 0.9.8 sources before my patch and
Packit Service a2489d
             it always 0-terminates an IA5String."
Packit Service a2489d
          */
Packit Service a2489d
          if((altlen == strlen(altptr)) &&
Packit Service a2489d
             /* if this isn't true, there was an embedded zero in the name
Packit Service a2489d
                string and we cannot match it. */
Packit Service a2489d
             cert_hostcheck(altptr, hostname))
Packit Service a2489d
            matched = 1;
Packit Service a2489d
          else
Packit Service a2489d
            matched = 0;
Packit Service a2489d
          break;
Packit Service a2489d
Packit Service a2489d
        case GEN_IPADD: /* IP address comparison */
Packit Service a2489d
          /* compare alternative IP address if the data chunk is the same size
Packit Service a2489d
             our server IP address is */
Packit Service a2489d
          if((altlen == addrlen) && !memcmp(altptr, &addr, altlen))
Packit Service a2489d
            matched = 1;
Packit Service a2489d
          else
Packit Service a2489d
            matched = 0;
Packit Service a2489d
          break;
Packit Service a2489d
        }
Packit Service a2489d
      }
Packit Service a2489d
    }
Packit Service a2489d
    GENERAL_NAMES_free(altnames);
Packit Service a2489d
  }
Packit Service a2489d
Packit Service a2489d
  if(matched == 1)
Packit Service a2489d
    /* an alternative name matched the server hostname */
Packit Service a2489d
    Log::global->Format(9, "Certificate verification: subjectAltName: %s matched\n", quote(hostname));
Packit Service a2489d
  else if(matched == 0) {
Packit Service a2489d
    /* an alternative name field existed, but didn't match and then
Packit Service a2489d
       we MUST fail */
Packit Service a2489d
    set_cert_error(xstring::format("subjectAltName does not match %s", quote(hostname)),get_fp(server_cert));
Packit Service a2489d
  }
Packit Service a2489d
  else {
Packit Service a2489d
    /* we have to look to the last occurence of a commonName in the
Packit Service a2489d
       distinguished one to get the most significant one. */
Packit Service a2489d
    int j,i=-1 ;
Packit Service a2489d
Packit Service a2489d
/* The following is done because of a bug in 0.9.6b */
Packit Service a2489d
Packit Service a2489d
    unsigned char *nulstr = (unsigned char *)"";
Packit Service a2489d
    unsigned char *peer_CN = nulstr;
Packit Service a2489d
Packit Service a2489d
    X509_NAME *name = X509_get_subject_name(server_cert) ;
Packit Service a2489d
    if(name)
Packit Service a2489d
      while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0)
Packit Service a2489d
        i=j;
Packit Service a2489d
Packit Service a2489d
    /* we have the name entry and we will now convert this to a string
Packit Service a2489d
       that we can use for comparison. Doing this we support BMPstring,
Packit Service a2489d
       UTF8 etc. */
Packit Service a2489d
Packit Service a2489d
    if(i>=0) {
Packit Service a2489d
      ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));
Packit Service a2489d
Packit Service a2489d
      /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
Packit Service a2489d
         is already UTF-8 encoded. We check for this case and copy the raw
Packit Service a2489d
         string manually to avoid the problem. This code can be made
Packit Service a2489d
         conditional in the future when OpenSSL has been fixed. Work-around
Packit Service a2489d
         brought by Alexis S. L. Carvalho. */
Packit Service a2489d
      if(tmp) {
Packit Service a2489d
        if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
Packit Service a2489d
          j = ASN1_STRING_length(tmp);
Packit Service a2489d
          if(j >= 0) {
Packit Service a2489d
            peer_CN = (unsigned char*)OPENSSL_malloc(j+1);
Packit Service a2489d
            if(peer_CN) {
Packit Service a2489d
              memcpy(peer_CN, ASN1_STRING_get0_data(tmp), j);
Packit Service a2489d
              peer_CN[j] = '\0';
Packit Service a2489d
            }
Packit Service a2489d
          }
Packit Service a2489d
        }
Packit Service a2489d
        else /* not a UTF8 name */
Packit Service a2489d
          j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
Packit Service a2489d
Packit Service a2489d
        if(peer_CN && ((int)strlen((char *)peer_CN) != j)) {
Packit Service a2489d
          /* there was a terminating zero before the end of string, this
Packit Service a2489d
             cannot match and we return failure! */
Packit Service a2489d
          set_cert_error("illegal cert name field (contains NUL character)",get_fp(server_cert));
Packit Service a2489d
        }
Packit Service a2489d
      }
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
    if(peer_CN == nulstr)
Packit Service a2489d
       peer_CN = NULL;
Packit Service a2489d
    else {
Packit Service a2489d
      /* convert peer_CN from UTF8 */
Packit Service a2489d
      if(!convert_from_utf8((char*)peer_CN, strlen((char*)peer_CN)))
Packit Service a2489d
	 set_cert_error("invalid cert name field (cannot convert from UTF8)",get_fp(server_cert));
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
    if(cert_error)
Packit Service a2489d
      /* error already detected, pass through */
Packit Service a2489d
      ;
Packit Service a2489d
    else if(!peer_CN) {
Packit Service a2489d
      set_cert_error("unable to obtain common name from peer certificate",get_fp(server_cert));
Packit Service a2489d
    }
Packit Service a2489d
    else if(!cert_hostcheck((const char *)peer_CN, hostname)) {
Packit Service a2489d
        set_cert_error(xstring::format("certificate subject name %s does not match "
Packit Service a2489d
              "target host name %s", quote_n(0,(const char *)peer_CN), quote_n(1,hostname)),get_fp(server_cert));
Packit Service a2489d
    }
Packit Service a2489d
    else {
Packit Service a2489d
      Log::global->Format(9, "Certificate verification: common name: %s matched\n", quote((char*)peer_CN));
Packit Service a2489d
    }
Packit Service a2489d
    if(peer_CN)
Packit Service a2489d
      OPENSSL_free(peer_CN);
Packit Service a2489d
  }
Packit Service a2489d
}
Packit Service a2489d
/* end curl code */
Packit Service a2489d
Packit Service a2489d
#include <sha1.h>
Packit Service a2489d
const xstring&  lftp_ssl_openssl::get_fp(X509 *cert)
Packit Service a2489d
{
Packit Service a2489d
   static xstring fp;
Packit Service a2489d
   fp.truncate();
Packit Service a2489d
   unsigned fp_len=SHA1_DIGEST_SIZE;
Packit Service a2489d
   if(!X509_digest(cert, EVP_sha1(), (unsigned char*)fp.add_space(fp_len), &fp_len))
Packit Service a2489d
      return xstring::null;
Packit Service a2489d
   fp.add_commit(fp_len);
Packit Service a2489d
   return fp;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
int lftp_ssl_openssl::verify_callback(int ok,X509_STORE_CTX *ctx)
Packit Service a2489d
{
Packit Service a2489d
   static X509 *prev_cert=0;
Packit Service a2489d
   X509 *cert=X509_STORE_CTX_get_current_cert(ctx);
Packit Service a2489d
Packit Service a2489d
   if(cert!=prev_cert)
Packit Service a2489d
   {
Packit Service a2489d
      int depth          = X509_STORE_CTX_get_error_depth(ctx);
Packit Service a2489d
      X509_NAME *subject = X509_get_subject_name(cert);
Packit Service a2489d
      X509_NAME *issuer  = X509_get_issuer_name(cert);
Packit Service a2489d
      char *subject_line = X509_NAME_oneline(subject, NULL, 0);
Packit Service a2489d
      char *issuer_line  = X509_NAME_oneline(issuer, NULL, 0);
Packit Service a2489d
      Log::global->Format(3,"Certificate depth: %d; subject: %s; issuer: %s\n",
Packit Service a2489d
			  depth,subject_line,issuer_line);
Packit Service a2489d
      free(subject_line);
Packit Service a2489d
      free(issuer_line);
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(ok && !verify_crl(ctx))
Packit Service a2489d
      ok=0;
Packit Service a2489d
Packit Service a2489d
   int error=X509_STORE_CTX_get_error(ctx);
Packit Service a2489d
   if(!ok)
Packit Service a2489d
      verify_callback_ssl->set_cert_error(X509_verify_cert_error_string(error),get_fp(cert));
Packit Service a2489d
Packit Service a2489d
   prev_cert=cert;
Packit Service a2489d
   return 1;
Packit Service a2489d
}
Packit Service a2489d
#endif // USE_OPENSSL
Packit Service a2489d
Packit Service a2489d
#endif // USE_SSL