Blame src/lftp_ssl.cc

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