* include/hbznet.h
* src/rtl/hbinet.c
+ added new C function hb_znetInetTimeout()
* minor cleanup (local variables localization)
* contrib/hbssl/hbssl.hbm
* contrib/hbssl/hbssl.hbx
+ contrib/hbssl/ssl_inet.c
+ added support for SSL/TLS encryption in hb_inet*() sockets.
To enable SSL/TLS encryption on such socket it's enough to
call hb_inetSSL_connect() or hb_inetSSL_accept() passing as
1-st parameter hb_inet socket item with already established
connection and in in the 2-nd parameter SSL item. The peer
should call second function. In general hb_inetSSL_connect()
should be called by client and hb_inetSSL_accept() by server.
To use hb_inetSSL_accept() it's necessary to also set
certificated (at least self ;-)) encryption keys. See the
example I committed to test directory.
The exact syntax of new functions is:
hb_inetSSL_connect( <pSocket>, <pSSL> [, <nTimeout> ] )
hb_inetSSL_accept( <pSocket>, <pSSL> [, <nTimeout> ] )
To use hb_inet*() functions to connect with SSL/TLS server
Harbour users only have to call hb_inetSSL_connect() after
setting connection, i.e.:
IF !Empty( sock := hb_inetConnect( cServer, nPort ) )
ssl_ctx := SSL_CTX_new()
IF hb_inetSSL_connect( sock, SSL_new( ssl_ctx ) ) == 1
// SSL connection established
// now user can use all hb_inet*() functions is
// the same way as for raw TCP connections and
// all parameters like timeouts are fully supported
// but transmission is encrypted.
[...]
ENDIF
ENDIF
It's not longer necessary to use SSL_set_fd() + SSL_connect()
and then SSL_read() / SSL_write() / hb_SSL_read_line() /
hb_SSL_read_all().
BTW hb_SSL_read_line() and hb_SSL_read_all() in HBSSL library
are broken and have to be fixed.
TODO: Now HBTIP library can be nicely simplified and additional
code for SSL/TLS read/write operations removed. It's
enough to once call hb_inetSSL_connect() if SSL/TLS
encryption is needed.
+ contrib/hbssl/tests/inetssl.prg
+ added test code for hb_inet*() SSL/TLS connections.
It's client and server example which also generates self
certificated encryption keys running openssl command.
If this code is linked with non console GT then user
should generated certificates himself (see comment in
LoadCertificates() function for more information).
201 lines
5.6 KiB
Plaintext
201 lines
5.6 KiB
Plaintext
/*
|
|
* Copyright 2015 Przemyslaw Czerpak (druzus/at/poczta.onet.pl)
|
|
* www - http://harbour-project.org
|
|
*/
|
|
|
|
#require "hbssl"
|
|
|
|
#define N_PORT 4001
|
|
#define EOL e"\r\n"
|
|
#define PEM_CERT_FILE "inetssl.pem"
|
|
|
|
STATIC s_lReady := .f.
|
|
STATIC s_lStop := .f.
|
|
|
|
STATIC s_lDelaySrv := .f.
|
|
STATIC s_lDelayCli := .f.
|
|
|
|
REQUEST HB_MT
|
|
|
|
PROCEDURE Main( delay )
|
|
LOCAL thrd
|
|
|
|
if !empty( delay )
|
|
s_lDelayCli := "C" $ upper( delay )
|
|
s_lDelaySrv := "S" $ upper( delay )
|
|
endif
|
|
|
|
/* initialize SSL library */
|
|
SSL_init()
|
|
RAND_seed( Time() + hb_tsToStr( hb_dateTime() ) + hb_DirBase() + NetName() )
|
|
|
|
/* start server thread */
|
|
thrd := hb_threadStart( @Server() )
|
|
IF Empty( thrd )
|
|
? "Cannot start thread."
|
|
RETURN
|
|
ENDIF
|
|
|
|
/* wait for server being ready to accept incoming connections */
|
|
DO WHILE ! s_lReady
|
|
hb_idleSleep( 0.01 )
|
|
ENDDO
|
|
|
|
/* start client */
|
|
Client()
|
|
|
|
/* inform server it should finish and wait for it */
|
|
s_lStop := .t.
|
|
hb_threadJoin( thrd )
|
|
?
|
|
|
|
RETURN
|
|
|
|
|
|
FUNCTION Client()
|
|
LOCAL sock, ssl_ctx, ssl, nResult, nErr, cLine
|
|
|
|
ssl_ctx := SSL_CTX_new()
|
|
ssl := SSL_new( ssl_ctx )
|
|
|
|
sock := hb_inetCreate()
|
|
hb_inetTimeout( sock, 5000 )
|
|
|
|
? "CLIENT: connecting..."
|
|
IF empty( hb_inetConnectIP( "127.0.0.1", N_PORT, sock ) )
|
|
? "CLIENT: cannot connect to server."
|
|
ELSE
|
|
? "CLIENT: connected to the server."
|
|
hb_inetTimeout( sock, 3000 )
|
|
|
|
IF s_lDelayCli
|
|
? "CLIENT: waiting..."
|
|
hb_idleSleep( 1 )
|
|
ENDIF
|
|
|
|
? "CLIENT: SSL CONNECT..."
|
|
nResult := hb_inetSSL_CONNECT( sock, ssl )
|
|
nErr := ERR_get_error()
|
|
?? hb_strFormat( e"\nCLIENT: hb_inetSSL_CONNECT()=>%d (%d), '%s'\n", ;
|
|
nResult, nErr, ;
|
|
ERR_error_string( nErr ) )
|
|
IF nResult == 1
|
|
? "CLIENT: connected with " + SSL_get_cipher( ssl ) + " encryption."
|
|
DipsCertInfo( ssl, "CLIENT: " )
|
|
|
|
hb_inetSendAll( sock, hb_tsToStr( hb_dateTime() ) + EOL )
|
|
DO WHILE ! empty( cLine := hb_inetRecvLine( sock ) )
|
|
? "CLIENT: RECV:", hb_valToExp( cLine )
|
|
ENDDO
|
|
ENDIF
|
|
|
|
ENDIF
|
|
hb_inetClose( sock )
|
|
|
|
RETURN NIL
|
|
|
|
|
|
FUNCTION Server()
|
|
LOCAL sockSrv, sockConn, ssl_ctx, ssl, nResult, nErr, cLine
|
|
|
|
? "SERVER: create listen socekt..."
|
|
IF Empty( sockSrv := hb_inetServer( N_PORT ) )
|
|
? "SERVER: cannot create listen socket."
|
|
ELSE
|
|
? "SERVER: loading certificates..."
|
|
ssl_ctx := SSL_CTX_new()
|
|
LoadCertificates( ssl_ctx, PEM_CERT_FILE, PEM_CERT_FILE )
|
|
ssl := SSL_new( ssl_ctx )
|
|
|
|
? "SERVER: waiting for connecitons..."
|
|
hb_inetTimeout( sockSrv, 100 )
|
|
s_lReady := .t.
|
|
DO WHILE ! s_lStop
|
|
IF !Empty( sockConn := hb_inetAccept( sockSrv ) )
|
|
? "SERVER: accepted new connection."
|
|
hb_inetTimeout( sockConn, 3000 )
|
|
|
|
IF s_lDelaySrv
|
|
? "SERVER: waiting..."
|
|
hb_idleSleep( 1 )
|
|
ENDIF
|
|
|
|
? "SERVER: SSL ACCEPT..."
|
|
nResult := hb_inetSSL_ACCEPT( sockConn, ssl )
|
|
nErr := ERR_get_error()
|
|
?? hb_strFormat( e"\nSERVER: hb_inetSSL_ACCEPT()=>%d (%d), '%s'\n", ;
|
|
nResult, nErr, ;
|
|
ERR_error_string( nErr ) )
|
|
|
|
IF nResult == 1
|
|
cLine := hb_inetRecvLine( sockConn )
|
|
? "SERVER: RECV:", hb_valToExp( cLine )
|
|
hb_inetSendAll( sockConn, ;
|
|
"ECHO[ " + cLine + " ]" + EOL + ;
|
|
hb_tsToStr( hb_dateTime() ) + EOL + ;
|
|
OS() + EOL + ;
|
|
VERSION() + EOL + ;
|
|
EOL )
|
|
ENDIF
|
|
|
|
hb_inetClose( sockConn )
|
|
sockConn := nil
|
|
ENDIF
|
|
ENDDO
|
|
|
|
s_lReady := .f.
|
|
|
|
ENDIF
|
|
|
|
RETURN NIL
|
|
|
|
|
|
FUNCTION LoadCertificates( ssl_ctx, cCertFile, cKeyFile )
|
|
|
|
/* Server using hb_inetSSL_ACCEPT() needs certificates,
|
|
they can be generated using the following command:
|
|
openssl req -x509 -nodes -days 365 -newkey rsa:1024 \
|
|
-out <cCertFile> -keyout <cKeyFile>
|
|
*/
|
|
IF ! hb_fileExists( cCertFile ) .AND. ! hb_fileExists( cKeyFile )
|
|
? "SERVER: generating certificates..."
|
|
hb_run( "openssl req -x509 -nodes -days 365 -newkey rsa:1024 " + ;
|
|
"-out " + cCertFile + " -keyout " + cKeyFile )
|
|
ENDIF
|
|
|
|
/* set the local certificate from CertFile */
|
|
IF SSL_CTX_use_certificate_file( ssl_ctx, cCertFile, HB_SSL_FILETYPE_PEM ) <= 0
|
|
OutErr( hb_strFormat( e"SERVER: SSL_CTX_use_certificate_file()=> '%s'\n", ;
|
|
ERR_error_string( ERR_get_error() ) ) )
|
|
QUIT
|
|
ENDIF
|
|
|
|
/* set the private key from KeyFile (may be the same as CertFile) */
|
|
IF SSL_CTX_use_PrivateKey_file( ssl_ctx, cKeyFile, HB_SSL_FILETYPE_PEM ) <= 0
|
|
OutErr( hb_strFormat( e"SERVER: SSL_CTX_use_PrivateKey_file()=> '%s'\n", ;
|
|
ERR_error_string( ERR_get_error() ) ) )
|
|
QUIT
|
|
ENDIF
|
|
|
|
/* verify private key */
|
|
IF ! SSL_CTX_check_private_key( ssl_ctx ) == 1
|
|
OutErr( e"SERVER: Private key does not match the public certificate\n" )
|
|
QUIT
|
|
ENDIF
|
|
|
|
RETURN NIL
|
|
|
|
|
|
FUNCTION DipsCertInfo( ssl, cWho )
|
|
LOCAL cert
|
|
|
|
IF !Empty( cert := SSL_get_peer_certificate( ssl ) )
|
|
? cWho + "Server certificates:"
|
|
? cWho + "Subject:", X509_NAME_oneline( X509_get_subject_name( cert ), 0, 0 )
|
|
? cWho + "Issuer:", X509_NAME_oneline( X509_get_issuer_name( cert ), 0, 0 )
|
|
ELSE
|
|
? cWho + "No certificates."
|
|
ENDIF
|
|
|
|
RETURN NIL
|