File Annotation
Not logged in
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Copyright (c) 2009 D. Richard Hipp
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** This program is free software; you can redistribute it and/or
16f6fd904a 2009-11-09    dmitry: ** modify it under the terms of the GNU General Public
16f6fd904a 2009-11-09    dmitry: ** License version 2 as published by the Free Software Foundation.
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** This program is distributed in the hope that it will be useful,
16f6fd904a 2009-11-09    dmitry: ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16f6fd904a 2009-11-09    dmitry: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16f6fd904a 2009-11-09    dmitry: ** General Public License for more details.
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** You should have received a copy of the GNU General Public
16f6fd904a 2009-11-09    dmitry: ** License along with this library; if not, write to the
16f6fd904a 2009-11-09    dmitry: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16f6fd904a 2009-11-09    dmitry: ** Boston, MA  02111-1307, USA.
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** Author contact information:
16f6fd904a 2009-11-09    dmitry: **   drh@hwaci.com
16f6fd904a 2009-11-09    dmitry: **   http://www.hwaci.com/drh/
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: *******************************************************************************
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** This file manages low-level SSL communications.
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** This file implements a singleton.  A single SSL connection may be active
16f6fd904a 2009-11-09    dmitry: ** at a time.  State information is stored in static variables.  The identity
16f6fd904a 2009-11-09    dmitry: ** of the server is held in global variables that are set by url_parse().
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** SSL support is abstracted out into this module because Fossil can
16f6fd904a 2009-11-09    dmitry: ** be compiled without SSL support (which requires OpenSSL library)
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: #include "config.h"
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: #ifdef FOSSIL_ENABLE_SSL
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: #include <openssl/bio.h>
16f6fd904a 2009-11-09    dmitry: #include <openssl/ssl.h>
16f6fd904a 2009-11-09    dmitry: #include <openssl/err.h>
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: #include "http_ssl.h"
16f6fd904a 2009-11-09    dmitry: #include <assert.h>
16f6fd904a 2009-11-09    dmitry: #include <sys/types.h>
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** There can only be a single OpenSSL IO connection open at a time.
16f6fd904a 2009-11-09    dmitry: ** State information about that IO is stored in the following
16f6fd904a 2009-11-09    dmitry: ** local variables:
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: static int sslIsInit = 0;    /* True after global initialization */
16f6fd904a 2009-11-09    dmitry: static BIO *iBio;            /* OpenSSL I/O abstraction */
16f6fd904a 2009-11-09    dmitry: static char *sslErrMsg = 0;  /* Text of most recent OpenSSL error */
16f6fd904a 2009-11-09    dmitry: static SSL_CTX *sslCtx;      /* SSL context */
16f6fd904a 2009-11-09    dmitry: static SSL *ssl;
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Clear the SSL error message
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: static void ssl_clear_errmsg(void){
16f6fd904a 2009-11-09    dmitry:   free(sslErrMsg);
16f6fd904a 2009-11-09    dmitry:   sslErrMsg = 0;
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Set the SSL error message.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: void ssl_set_errmsg(char *zFormat, ...){
16f6fd904a 2009-11-09    dmitry:   va_list ap;
16f6fd904a 2009-11-09    dmitry:   ssl_clear_errmsg();
16f6fd904a 2009-11-09    dmitry:   va_start(ap, zFormat);
16f6fd904a 2009-11-09    dmitry:   sslErrMsg = vmprintf(zFormat, ap);
16f6fd904a 2009-11-09    dmitry:   va_end(ap);
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Return the current SSL error message
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: const char *ssl_errmsg(void){
16f6fd904a 2009-11-09    dmitry:   return sslErrMsg;
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Call this routine once before any other use of the SSL interface.
16f6fd904a 2009-11-09    dmitry: ** This routine does initial configuration of the SSL module.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: void ssl_global_init(void){
16f6fd904a 2009-11-09    dmitry:   if( sslIsInit==0 ){
16f6fd904a 2009-11-09    dmitry:     SSL_library_init();
16f6fd904a 2009-11-09    dmitry:     SSL_load_error_strings();
16f6fd904a 2009-11-09    dmitry:     ERR_load_BIO_strings();
16f6fd904a 2009-11-09    dmitry:     OpenSSL_add_all_algorithms();
16f6fd904a 2009-11-09    dmitry:     sslCtx = SSL_CTX_new(SSLv23_client_method());
16f6fd904a 2009-11-09    dmitry:     sslIsInit = 1;
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Call this routine to shutdown the SSL module prior to program exit.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: void ssl_global_shutdown(void){
16f6fd904a 2009-11-09    dmitry:   if( sslIsInit ){
16f6fd904a 2009-11-09    dmitry:     SSL_CTX_free(sslCtx);
16f6fd904a 2009-11-09    dmitry:     ssl_clear_errmsg();
16f6fd904a 2009-11-09    dmitry:     sslIsInit = 0;
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Close the currently open SSL connection.  If no connection is open,
16f6fd904a 2009-11-09    dmitry: ** this routine is a no-op.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: void ssl_close(void){
16f6fd904a 2009-11-09    dmitry:   if( iBio!=NULL ){
16f6fd904a 2009-11-09    dmitry:     BIO_reset(iBio);
16f6fd904a 2009-11-09    dmitry:     BIO_free_all(iBio);
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Open an SSL connection.  The identify of the server is determined
16f6fd904a 2009-11-09    dmitry: ** by global varibles that are set using url_parse():
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: **    g.urlName       Name of the server.  Ex: www.fossil-scm.org
16f6fd904a 2009-11-09    dmitry: **    g.urlPort       TCP/IP port to use.  Ex: 80
16f6fd904a 2009-11-09    dmitry: **
16f6fd904a 2009-11-09    dmitry: ** Return the number of errors.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: int ssl_open(void){
16f6fd904a 2009-11-09    dmitry:   X509 *cert;
16f6fd904a 2009-11-09    dmitry:   int hasSavedCertificate = 0;
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   ssl_global_init();
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   /* Get certificate for current server from global config and
16f6fd904a 2009-11-09    dmitry:    * (if we have it in config) add it to certificate store.
16f6fd904a 2009-11-09    dmitry:    */
16f6fd904a 2009-11-09    dmitry:   cert = ssl_get_certificate();
16f6fd904a 2009-11-09    dmitry:   if ( cert != NULL ){
16f6fd904a 2009-11-09    dmitry:     X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
16f6fd904a 2009-11-09    dmitry:     X509_free(cert);
16f6fd904a 2009-11-09    dmitry:     hasSavedCertificate = 1;
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   iBio = BIO_new_ssl_connect(sslCtx);
16f6fd904a 2009-11-09    dmitry:   BIO_get_ssl(iBio, &ssl);
16f6fd904a 2009-11-09    dmitry:   SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
16f6fd904a 2009-11-09    dmitry:   if( iBio==NULL ) {
16f6fd904a 2009-11-09    dmitry:     ssl_set_errmsg("SSL: cannot open SSL (%s)",
16f6fd904a 2009-11-09    dmitry:                     ERR_reason_error_string(ERR_get_error()));
16f6fd904a 2009-11-09    dmitry:     return 1;
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   char *connStr = mprintf("%s:%d", g.urlName, g.urlPort);
16f6fd904a 2009-11-09    dmitry:   BIO_set_conn_hostname(iBio, connStr);
16f6fd904a 2009-11-09    dmitry:   free(connStr);
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   if( BIO_do_connect(iBio)<=0 ){
16f6fd904a 2009-11-09    dmitry:     ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
16f6fd904a 2009-11-09    dmitry:         g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error()));
16f6fd904a 2009-11-09    dmitry:     ssl_close();
16f6fd904a 2009-11-09    dmitry:     return 1;
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   if( BIO_do_handshake(iBio)<=0 ) {
16f6fd904a 2009-11-09    dmitry:     ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)",
16f6fd904a 2009-11-09    dmitry:         g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error()));
16f6fd904a 2009-11-09    dmitry:     ssl_close();
16f6fd904a 2009-11-09    dmitry:     return 1;
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry:   /* Check if certificate is valid */
16f6fd904a 2009-11-09    dmitry:   cert = SSL_get_peer_certificate(ssl);
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   if ( cert == NULL ){
16f6fd904a 2009-11-09    dmitry:     ssl_set_errmsg("No SSL certificate was presented by the peer");
16f6fd904a 2009-11-09    dmitry:     ssl_close();
16f6fd904a 2009-11-09    dmitry:     return 1;
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   if( SSL_get_verify_result(ssl) != X509_V_OK ){
16f6fd904a 2009-11-09    dmitry:     char *desc, *prompt;
16f6fd904a 2009-11-09    dmitry:     BIO *mem;
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:     mem = BIO_new(BIO_s_mem());
16f6fd904a 2009-11-09    dmitry:     X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
16f6fd904a 2009-11-09    dmitry:     BIO_puts(mem, "\n\nIssued By:\n\n");
16f6fd904a 2009-11-09    dmitry:     X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
16f6fd904a 2009-11-09    dmitry:     BIO_write(mem, "", 1); // null-terminate mem buffer
16f6fd904a 2009-11-09    dmitry:     BIO_get_mem_data(mem, &desc);
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:     char *warning = "";
16f6fd904a 2009-11-09    dmitry:     if( hasSavedCertificate ){
16f6fd904a 2009-11-09    dmitry:       warning = "WARNING: Certificate doesn't match the "
16f6fd904a 2009-11-09    dmitry:                 "saved certificate for this host!";
16f6fd904a 2009-11-09    dmitry:     }
16f6fd904a 2009-11-09    dmitry:     prompt = mprintf("\nUnknown SSL certificate:\n\n%s\n\n%s\n"
16f6fd904a 2009-11-09    dmitry:                      "Accept certificate [a=always/y/N]? ", desc, warning);
16f6fd904a 2009-11-09    dmitry:     BIO_free(mem);
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:     Blob ans;
16f6fd904a 2009-11-09    dmitry:     blob_zero(&ans);
16f6fd904a 2009-11-09    dmitry:     prompt_user(prompt, &ans);
16f6fd904a 2009-11-09    dmitry:     free( prompt );
16f6fd904a 2009-11-09    dmitry:     if( blob_str(&ans)[0]!='y' && blob_str(&ans)[0]!='a' ) {
16f6fd904a 2009-11-09    dmitry:       X509_free(cert);
16f6fd904a 2009-11-09    dmitry:       ssl_set_errmsg("SSL certificate declined");
16f6fd904a 2009-11-09    dmitry:       ssl_close();
16f6fd904a 2009-11-09    dmitry:       return 1;
16f6fd904a 2009-11-09    dmitry:     }
16f6fd904a 2009-11-09    dmitry:     if( blob_str(&ans)[0]=='a' ) {
16f6fd904a 2009-11-09    dmitry:       ssl_save_certificate(cert);
16f6fd904a 2009-11-09    dmitry:     }
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry:   X509_free(cert);
16f6fd904a 2009-11-09    dmitry:   return 0;
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Save certificate to global config.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: void ssl_save_certificate(X509 *cert)
16f6fd904a 2009-11-09    dmitry: {
16f6fd904a 2009-11-09    dmitry:   BIO *mem;
16f6fd904a 2009-11-09    dmitry:   char *zCert, *zHost;
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   mem = BIO_new(BIO_s_mem());
16f6fd904a 2009-11-09    dmitry:   PEM_write_bio_X509(mem, cert);
16f6fd904a 2009-11-09    dmitry:   BIO_write(mem, "", 1); // null-terminate mem buffer
16f6fd904a 2009-11-09    dmitry:   BIO_get_mem_data(mem, &zCert);
16f6fd904a 2009-11-09    dmitry:   zHost = mprintf("cert:%s", g.urlName);
16f6fd904a 2009-11-09    dmitry:   db_set(zHost, zCert, 1);
16f6fd904a 2009-11-09    dmitry:   free(zHost);
16f6fd904a 2009-11-09    dmitry:   BIO_free(mem);
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Get certificate for g.urlName from global config.
16f6fd904a 2009-11-09    dmitry: ** Return NULL if no certificate found.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: X509 *ssl_get_certificate()
16f6fd904a 2009-11-09    dmitry: {
16f6fd904a 2009-11-09    dmitry:   char *zHost, *zCert;
16f6fd904a 2009-11-09    dmitry:   BIO *mem;
16f6fd904a 2009-11-09    dmitry:   X509 *cert;
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry:   zHost = mprintf("cert:%s", g.urlName);
16f6fd904a 2009-11-09    dmitry:   zCert = db_get(zHost, NULL);
16f6fd904a 2009-11-09    dmitry:   free(zHost);
16f6fd904a 2009-11-09    dmitry:   if ( zCert==NULL )
16f6fd904a 2009-11-09    dmitry:     return NULL;
16f6fd904a 2009-11-09    dmitry:   mem = BIO_new(BIO_s_mem());
16f6fd904a 2009-11-09    dmitry:   BIO_puts(mem, zCert);
16f6fd904a 2009-11-09    dmitry:   cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
16f6fd904a 2009-11-09    dmitry:   free(zCert);
16f6fd904a 2009-11-09    dmitry:   BIO_free(mem);
16f6fd904a 2009-11-09    dmitry:   return cert;
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Send content out over the SSL connection.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: size_t ssl_send(void *NotUsed, void *pContent, size_t N){
16f6fd904a 2009-11-09    dmitry:   size_t sent;
16f6fd904a 2009-11-09    dmitry:   size_t total = 0;
16f6fd904a 2009-11-09    dmitry:   while( N>0 ){
16f6fd904a 2009-11-09    dmitry:     sent = BIO_write(iBio, pContent, N);
16f6fd904a 2009-11-09    dmitry:     if( sent<=0 ) break;
16f6fd904a 2009-11-09    dmitry:     total += sent;
16f6fd904a 2009-11-09    dmitry:     N -= sent;
16f6fd904a 2009-11-09    dmitry:     pContent = (void*)&((char*)pContent)[sent];
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry:   return total;
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: /*
16f6fd904a 2009-11-09    dmitry: ** Receive content back from the SSL connection.
16f6fd904a 2009-11-09    dmitry: */
16f6fd904a 2009-11-09    dmitry: size_t ssl_receive(void *NotUsed, void *pContent, size_t N){
16f6fd904a 2009-11-09    dmitry:   size_t got;
16f6fd904a 2009-11-09    dmitry:   size_t total = 0;
16f6fd904a 2009-11-09    dmitry:   while( N>0 ){
16f6fd904a 2009-11-09    dmitry:     got = BIO_read(iBio, pContent, N);
16f6fd904a 2009-11-09    dmitry:     if( got<=0 ) break;
16f6fd904a 2009-11-09    dmitry:     total += got;
16f6fd904a 2009-11-09    dmitry:     N -= got;
16f6fd904a 2009-11-09    dmitry:     pContent = (void*)&((char*)pContent)[got];
16f6fd904a 2009-11-09    dmitry:   }
16f6fd904a 2009-11-09    dmitry:   return total;
16f6fd904a 2009-11-09    dmitry: }
16f6fd904a 2009-11-09    dmitry: 
16f6fd904a 2009-11-09    dmitry: #endif /* FOSSIL_ENABLE_SSL */