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 */