c64ab570a1
work. please note, that I would do quite some things differently nowadays. I home to get those things in via refactoring
172 lines
6.8 KiB
C++
172 lines
6.8 KiB
C++
/***************************************************************************
|
||
* Copyright (C) 2005 by Christof Donat *
|
||
* cdonat@gmx.de *
|
||
* *
|
||
* This program is free software; you can redistribute it and/or modify *
|
||
* it under the terms of the GNU General Public License as published by *
|
||
* the Free Software Foundation; either version 2 of the License, or *
|
||
* (at your option) any later version. *
|
||
* *
|
||
* This program is distributed in the hope that it will be useful, *
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||
* GNU General Public License for more details. *
|
||
* *
|
||
* You should have received a copy of the GNU General Public License *
|
||
* along with this program; if not, write to the *
|
||
* Free Software Foundation, Inc., *
|
||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||
***************************************************************************/
|
||
|
||
#include "sslclient.h"
|
||
#include "logger.h"
|
||
|
||
#include <sys/types.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <netdb.h>
|
||
|
||
#include <boost/filesystem/operations.hpp>
|
||
#include <boost/lexical_cast.hpp>
|
||
|
||
#include <gnutls/x509.h>
|
||
|
||
using std::string;
|
||
using std::streamsize;
|
||
using boost::lexical_cast;
|
||
using boost::bad_lexical_cast;
|
||
|
||
int SSLDevice::countInstances = 0;
|
||
|
||
SSLDevice::SSLDevice(const string& address, const short int port): m_session(0) {
|
||
this->init(address.c_str(),port);
|
||
}
|
||
SSLDevice::SSLDevice(const char* address, const short int port): m_session(0) {
|
||
this->init(address,port);
|
||
}
|
||
|
||
void SSLDevice::init(const char* address, const short int port) {
|
||
struct sockaddr_in addr;
|
||
bzero(&addr,sizeof(addr));
|
||
addr.sin_family = AF_INET;
|
||
if( (addr.sin_addr.s_addr = inet_addr(address)) == INADDR_NONE ) {
|
||
struct hostent* hp = gethostbyname(address);
|
||
if( hp == 0 ) throw("can't resolve hostname '"+string(address)+"'");
|
||
bcopy(hp->h_addr, (char*) &addr.sin_addr, hp->h_length);
|
||
}
|
||
addr.sin_port = htons(port);
|
||
|
||
if( (m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ) throw(string("can't open socket"));
|
||
if( ::connect(m_socket, (struct sockaddr*) &addr, sizeof(addr)) < 0 ) {
|
||
::close(m_socket);
|
||
throw("can't connect to '"+string(address)+"' port "+(lexical_cast<string>(port)));
|
||
}
|
||
|
||
const int cert_type_priority[3] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
|
||
|
||
m_session = new gnutls_session;
|
||
|
||
if( countInstances == 0 ) gnutls_global_init();
|
||
countInstances++;
|
||
|
||
gnutls_certificate_allocate_credentials(&m_xcred);
|
||
gnutls_certificate_set_x509_trust_file(m_xcred, "ca.pem", GNUTLS_X509_FMT_PEM);
|
||
|
||
gnutls_init(m_session,GNUTLS_CLIENT);
|
||
|
||
gnutls_set_default_priority(*m_session);
|
||
|
||
gnutls_credentials_set(*m_session, GNUTLS_CRD_CERTIFICATE, m_xcred);
|
||
|
||
gnutls_transport_set_ptr(*m_session, (gnutls_transport_ptr)&m_socket);
|
||
|
||
if( gnutls_handshake(*m_session) < 0 )
|
||
throw(string(gnutls_protocol_get_name(gnutls_protocol_get_version(*m_session)))+" Handshake failed");
|
||
|
||
verify_certificate( *m_session, address);
|
||
}
|
||
SSLDevice::SSLDevice(const SSLDevice& other) : m_session(other.m_session), m_socket(other.m_socket), m_xcred(other.m_xcred) { }
|
||
SSLDevice::~SSLDevice() { }
|
||
|
||
void SSLDevice::closeSocket() {
|
||
gnutls_bye(*m_session, GNUTLS_SHUT_RDWR);
|
||
gnutls_certificate_free_credentials(m_xcred);
|
||
::close(m_socket);
|
||
gnutls_deinit(*m_session);
|
||
delete(m_session);
|
||
|
||
countInstances--;
|
||
if( countInstances == 0 ) gnutls_global_deinit();
|
||
}
|
||
|
||
streamsize SSLDevice::read(char* s, streamsize n) { return (streamsize)gnutls_record_recv(*m_session,(void*)s,(size_t)n); }
|
||
streamsize SSLDevice::write(const char* s, streamsize n) {
|
||
/*streamsize i = (streamsize)gnutls_record_send(*m_session,(const void*)s,(size_t)n);
|
||
Logger::log(Logger::DEBUG,string("sending ")+boost::lexical_cast<string>(i)+" von "+boost::lexical_cast<string>(n)+" bytes: \n---\n"+s+"\n---\n");*/
|
||
return (streamsize)gnutls_record_send(*m_session,(const void*)s,(size_t)n); //i;
|
||
}
|
||
|
||
void SSLDevice::verify_certificate( gnutls_session session, string hostname) {
|
||
boost::filesystem::path general("/etc/hsadminc.cert");
|
||
if( boost::filesystem::exists(general) && !boost::filesystem::is_directory(general) )
|
||
gnutls_certificate_set_x509_trust_file (m_xcred, general.native().c_str(), GNUTLS_X509_FMT_PEM);
|
||
//string privfilename = getenv("HOME");
|
||
string priv = getenv("HOME");
|
||
boost::filesystem::path p( priv + "/.hsadmin.cert" );
|
||
//p /= ".hsadminc.conf";
|
||
|
||
if( boost::filesystem::exists(p) && !boost::filesystem::is_directory(p) )
|
||
gnutls_certificate_set_x509_trust_file (m_xcred, p.native().c_str(), GNUTLS_X509_FMT_PEM);
|
||
//gnutls_certificate_set_x509_trust_file (m_xcred, const char * cafile, PEM)
|
||
|
||
unsigned int status = 0;//gnutls_certificate_verify_peers(*m_session);
|
||
/*if( gnutls_certificate_verify_peers2(*m_session, &status) < 0 )
|
||
throw(string("Error while verifying certificate"));*/
|
||
|
||
string tval = "Certificate Verification failed: ";
|
||
if( status & GNUTLS_CERT_INVALID ) { throw(tval+"The certificate is not trustet"); }
|
||
if( status & GNUTLS_CERT_SIGNER_NOT_FOUND ) { throw(tval+"The certificate has no known Issuer"); }
|
||
if( status & GNUTLS_CERT_REVOKED ) { throw(tval+"The certificate has been revoked"); }
|
||
|
||
// the further checks are only valid for X509 certificates
|
||
if ( gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) return;
|
||
|
||
const gnutls_datum* cert_list;
|
||
unsigned int cert_list_size;
|
||
gnutls_x509_crt cert;
|
||
|
||
if ( gnutls_x509_crt_init(&cert) < 0) throw(tval+"error in initialization");
|
||
|
||
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
|
||
if ( cert_list == NULL ) throw(tval+"no certificate found");
|
||
|
||
bool failed = false;
|
||
string tReason = "";
|
||
for( int i = 0; i < cert_list_size-1; i++ ) {
|
||
bool failed = false;
|
||
|
||
if ( gnutls_x509_crt_import( cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
|
||
throw(tval+"error parsing certificate Number "+lexical_cast<string>(i));
|
||
|
||
if ( gnutls_x509_crt_get_expiration_time(cert) < time(0) ) {
|
||
tReason = "The certificate has expired";
|
||
failed = true;
|
||
}
|
||
|
||
if ( gnutls_x509_crt_get_activation_time(cert) > time(0) ) {
|
||
tReason = "The certificate is not yet activated";
|
||
failed = true;
|
||
}
|
||
if ( !gnutls_x509_crt_check_hostname(cert,hostname.c_str()) ) {
|
||
tReason = "The certificate’s owner does not match hostname ’";
|
||
tReason += hostname +"'";
|
||
failed = true;
|
||
}
|
||
gnutls_x509_crt_deinit(cert);
|
||
|
||
if( !failed ) break;
|
||
}
|
||
if( failed ) throw(tval+tReason);
|
||
}
|